Aegisub/assdraw/libpropgrid/odcombo.cpp
Amar Takhar dc404a954e Add UNIX (and with luck, OS X) support to assdraw. This (unfortunatly)
includes propgrid, the build system for propgrid is brutal rather than fend of
users trying to build it themselves it's worth it to just include it with
assdraw for now.

Originally committed to SVN as r2177.
2008-04-13 06:57:09 +00:00

3836 lines
103 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: odcombo.cpp
// Purpose: wxPGOwnerDrawnComboBox and related classes implementation
// Author: Jaakko Salli
// Modified by:
// Created: Jan-25-2005
// RCS-ID: $Id:
// Copyright: (c) 2005 Jaakko Salli
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "odcombo.h"
#endif
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_COMBOBOX
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/log.h"
#include "wx/button.h"
#include "wx/combobox.h"
#include "wx/textctrl.h"
#include "wx/dcclient.h"
#include "wx/settings.h"
#include "wx/dialog.h"
#endif
#include "wx/dcbuffer.h"
#include "wx/tooltip.h"
#include "wx/timer.h"
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
#include "wx/msw/uxtheme.h"
#endif
#include "wx/propgrid/odcombo.h"
//
// THESE GO TO BASE FILE
//
#define BMP_BUTTON_MARGIN 4
//#define DEFAULT_POPUP_HEIGHT -1
#define DEFAULT_POPUP_HEIGHT 300
#define DEFAULT_TEXT_INDENT 3
#if defined(__WXMSW__)
#define ALLOW_FAKE_POPUP 0 // Use only on plats with problems with wxPopupWindow
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
//#undef wxUSE_POPUPWIN
//#define wxUSE_POPUPWIN 0
#elif defined(__WXGTK__)
// Fake popup windows cause focus problems on GTK2 (but enable on GTK1.2, just in case)
#if defined(__WXGTK20__)
#define ALLOW_FAKE_POPUP 0 // Use only on plats with problems with wxPopupWindow
#else
#define ALLOW_FAKE_POPUP 1 // Use only on plats with problems with wxPopupWindow
#endif
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#elif defined(__WXMAC__)
#define ALLOW_FAKE_POPUP 1 // Use only on plats with problems with wxPopupWindow
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#else
#define ALLOW_FAKE_POPUP 1 // Use only on plats with problems with wxPopupWindow
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#endif
// Popupwin is really only supported on wxMSW (not WINCE) and wxGTK, regardless
// what the wxUSE_POPUPWIN says.
#if (!defined(__WXMSW__) && !defined(__WXGTK__)) || defined(__WXWINCE__)
#undef wxUSE_POPUPWIN
#define wxUSE_POPUPWIN 0
#endif
#if wxUSE_POPUPWIN
#include "wx/popupwin.h"
#else
#undef USE_TRANSIENT_POPUP
#define USE_TRANSIENT_POPUP 0
#endif
// For versions < 2.6.2, don't enable transient popup. There may be
// problems I don't have time to test properly.
#if !wxCHECK_VERSION(2, 6, 2)
#undef USE_TRANSIENT_POPUP
#define USE_TRANSIENT_POPUP 0
#endif
#if USE_TRANSIENT_POPUP
#undef ALLOW_FAKE_POPUP
#define ALLOW_FAKE_POPUP 0
#define wxPGComboPopupWindowBase wxPopupTransientWindow
#define INSTALL_TOPLEV_HANDLER 0
#elif wxUSE_POPUPWIN
#define wxPGComboPopupWindowBase wxPopupWindow
#define INSTALL_TOPLEV_HANDLER 1
#else
#define wxPGComboPopupWindowBase wxDialog
#if !ALLOW_FAKE_POPUP
#define INSTALL_TOPLEV_HANDLER 0 // Doesn't need since can monitor active event
#else
#define INSTALL_TOPLEV_HANDLER 1
#endif
#endif
//
// THESE GO TO GENERIC FILE
//
// Some adjustments to make the generic more bearable
#if defined(__WXUNIVERSAL__)
#define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
#define TEXTCTRLYADJUST 0
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
#define DEFAULT_DROPBUTTON_WIDTH 19
#elif defined(__WXMSW__)
#define TEXTCTRLXADJUST 2 // position adjustment for wxTextCtrl, with zero indent
#define TEXTCTRLYADJUST 3
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
#define DEFAULT_DROPBUTTON_WIDTH 17
#elif defined(__WXGTK__)
#define TEXTCTRLXADJUST -1 // position adjustment for wxTextCtrl, with zero indent
#define TEXTCTRLYADJUST 0
#define TEXTXADJUST 1 // how much is read-only text's x adjusted
#define DEFAULT_DROPBUTTON_WIDTH 23
#elif defined(__WXMAC__)
#define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
#define TEXTCTRLYADJUST 0
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
#define DEFAULT_DROPBUTTON_WIDTH 19
#else
#define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
#define TEXTCTRLYADJUST 0
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
#define DEFAULT_DROPBUTTON_WIDTH 19
#endif
// constants
// ----------------------------------------------------------------------------
// TO BASE
// the margin between the text control and the combo button
static const wxCoord g_comboMargin = 2;
// ----------------------------------------------------------------------------
// wxPGComboFrameEventHandler takes care of hiding the popup when events happen
// in its top level parent.
// ----------------------------------------------------------------------------
#if INSTALL_TOPLEV_HANDLER
//
// This will no longer be necessary after wxTransientPopupWindow
// works well on all platforms.
//
class wxPGComboFrameEventHandler : public wxEvtHandler
{
public:
wxPGComboFrameEventHandler( wxPGComboControlBase* pCb );
~wxPGComboFrameEventHandler();
void OnPopup();
void OnIdle( wxIdleEvent& event );
void OnMouseEvent( wxMouseEvent& event );
void OnActivate( wxActivateEvent& event );
void OnResize( wxSizeEvent& event );
void OnMove( wxMoveEvent& event );
void OnMenuEvent( wxMenuEvent& event );
void OnClose( wxCloseEvent& event );
protected:
wxWindow* m_focusStart;
wxPGComboControlBase* m_combo;
private:
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxPGComboFrameEventHandler, wxEvtHandler)
EVT_IDLE(wxPGComboFrameEventHandler::OnIdle)
EVT_LEFT_DOWN(wxPGComboFrameEventHandler::OnMouseEvent)
EVT_RIGHT_DOWN(wxPGComboFrameEventHandler::OnMouseEvent)
EVT_SIZE(wxPGComboFrameEventHandler::OnResize)
EVT_MOVE(wxPGComboFrameEventHandler::OnMove)
EVT_MENU_HIGHLIGHT(wxID_ANY,wxPGComboFrameEventHandler::OnMenuEvent)
EVT_MENU_OPEN(wxPGComboFrameEventHandler::OnMenuEvent)
EVT_ACTIVATE(wxPGComboFrameEventHandler::OnActivate)
EVT_CLOSE(wxPGComboFrameEventHandler::OnClose)
END_EVENT_TABLE()
wxPGComboFrameEventHandler::wxPGComboFrameEventHandler( wxPGComboControlBase* combo )
: wxEvtHandler()
{
m_combo = combo;
}
wxPGComboFrameEventHandler::~wxPGComboFrameEventHandler()
{
}
void wxPGComboFrameEventHandler::OnPopup()
{
m_focusStart = ::wxWindow::FindFocus();
}
void wxPGComboFrameEventHandler::OnIdle( wxIdleEvent& event )
{
wxWindow* winFocused = ::wxWindow::FindFocus();
wxWindow* popup = m_combo->GetPopupControl();
wxWindow* winpopup = m_combo->GetPopupWindow();
if (
winFocused != m_focusStart &&
winFocused != popup &&
winFocused->GetParent() != popup &&
winFocused != winpopup &&
winFocused->GetParent() != winpopup &&
winFocused != m_combo &&
winFocused != m_combo->GetButton() // GTK (atleast) requires this
)
{
m_combo->HidePopup();
}
event.Skip();
}
void wxPGComboFrameEventHandler::OnMenuEvent( wxMenuEvent& event )
{
m_combo->HidePopup();
event.Skip();
}
void wxPGComboFrameEventHandler::OnMouseEvent( wxMouseEvent& event )
{
m_combo->HidePopup();
event.Skip();
}
void wxPGComboFrameEventHandler::OnClose( wxCloseEvent& event )
{
m_combo->HidePopup();
event.Skip();
}
void wxPGComboFrameEventHandler::OnActivate( wxActivateEvent& event )
{
m_combo->HidePopup();
event.Skip();
}
void wxPGComboFrameEventHandler::OnResize( wxSizeEvent& event )
{
m_combo->HidePopup();
event.Skip();
}
void wxPGComboFrameEventHandler::OnMove( wxMoveEvent& event )
{
m_combo->HidePopup();
event.Skip();
}
#endif // INSTALL_TOPLEV_HANDLER
// ----------------------------------------------------------------------------
// wxPGComboPopupWindow is wxPopupWindow customized for
// wxComboControl.
// ----------------------------------------------------------------------------
class wxPGComboPopupWindow : public wxPGComboPopupWindowBase
{
public:
wxPGComboPopupWindow( wxPGComboControlBase *parent, int style = wxBORDER_NONE );
#if USE_TRANSIENT_POPUP
virtual bool ProcessLeftDown(wxMouseEvent& event);
#endif
void OnKeyEvent(wxKeyEvent& event);
void OnMouseEvent( wxMouseEvent& event );
#if !wxUSE_POPUPWIN
void OnActivate( wxActivateEvent& event );
#endif
protected:
#if USE_TRANSIENT_POPUP
virtual void OnDismiss();
#endif
private:
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxPGComboPopupWindow, wxPGComboPopupWindowBase)
EVT_MOUSE_EVENTS(wxPGComboPopupWindow::OnMouseEvent)
#if !wxUSE_POPUPWIN
EVT_ACTIVATE(wxPGComboPopupWindow::OnActivate)
#endif
EVT_KEY_DOWN(wxPGComboPopupWindow::OnKeyEvent)
EVT_KEY_UP(wxPGComboPopupWindow::OnKeyEvent)
END_EVENT_TABLE()
wxPGComboPopupWindow::wxPGComboPopupWindow( wxPGComboControlBase *parent,
int style )
#if wxUSE_POPUPWIN
: wxPGComboPopupWindowBase(parent,style)
#else
: wxPGComboPopupWindowBase(parent,
wxID_ANY,
wxEmptyString,
wxPoint(-21,-21),
wxSize(20,20),
style)
#endif
{
}
void wxPGComboPopupWindow::OnKeyEvent( wxKeyEvent& event )
{
// Relay keyboard event to the main child controls
// (just skipping may just cause the popup to close)
wxWindowList children = GetChildren();
wxWindowList::iterator node = children.begin();
wxWindow* child = (wxWindow*)*node;
child->AddPendingEvent(event);
}
void wxPGComboPopupWindow::OnMouseEvent( wxMouseEvent& event )
{
event.Skip();
}
#if !wxUSE_POPUPWIN
void wxPGComboPopupWindow::OnActivate( wxActivateEvent& event )
{
if ( !event.GetActive() )
{
// Tell combo control that we are dismissed.
wxPGComboControl* combo = (wxPGComboControl*) GetParent();
wxASSERT( combo );
wxASSERT( combo->IsKindOf(CLASSINFO(wxPGComboControl)) );
combo->HidePopup();
event.Skip();
}
}
#endif
#if USE_TRANSIENT_POPUP
bool wxPGComboPopupWindow::ProcessLeftDown(wxMouseEvent& event )
{
return wxPGComboPopupWindowBase::ProcessLeftDown(event);
}
#endif
#if USE_TRANSIENT_POPUP
// First thing that happens when a transient popup closes is that this method gets called.
void wxPGComboPopupWindow::OnDismiss()
{
wxPGComboControlBase* combo = (wxPGComboControlBase*) GetParent();
wxASSERT ( combo->IsKindOf(CLASSINFO(wxPGComboControlBase)) );
combo->OnPopupDismiss();
}
#endif
// ----------------------------------------------------------------------------
// wxPGComboPopup methods
//
// ----------------------------------------------------------------------------
wxPGComboPopup::~wxPGComboPopup()
{
}
void wxPGComboPopup::OnPopup()
{
}
void wxPGComboPopup::OnDismiss()
{
}
wxSize wxPGComboPopup::GetAdjustedSize( int minWidth,
int prefHeight,
int WXUNUSED(maxHeight) )
{
return wxSize(minWidth,prefHeight);
}
void wxPGComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
{
if ( m_combo->GetWindowStyle() & wxCB_READONLY ) // ie. no textctrl
{
m_combo->DrawFocusBackground(dc,rect,0);
dc.DrawText( GetStringValue(),
rect.x + m_combo->GetTextIndent(),
(rect.height-dc.GetCharHeight())/2 + m_combo->m_widthCustomBorder );
}
}
void wxPGComboPopup::OnComboKeyEvent( wxKeyEvent& event )
{
event.Skip();
}
void wxPGComboPopup::OnComboDoubleClick()
{
}
void wxPGComboPopup::SetStringValue( const wxString& WXUNUSED(value) )
{
}
bool wxPGComboPopup::LazyCreate()
{
return false;
}
void wxPGComboPopup::Dismiss()
{
m_combo->HidePopup();
}
// ----------------------------------------------------------------------------
// wxPGVListBoxComboPopup is a wxVListBox customized to act as a popup control
//
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxPGVListBoxComboPopup, wxVListBox)
EVT_MOTION(wxPGVListBoxComboPopup::OnMouseMove)
EVT_KEY_DOWN(wxPGVListBoxComboPopup::OnKey)
EVT_LEFT_UP(wxPGVListBoxComboPopup::OnLeftClick)
END_EVENT_TABLE()
wxPGVListBoxComboPopup::wxPGVListBoxComboPopup(wxPGComboControl* combo)
: wxVListBox(), wxPGComboPopup(combo)
{
//m_callback = callback;
m_widestWidth = 0;
m_avgCharWidth = 0;
m_baseImageWidth = 0;
m_itemHeight = 0;
m_value = -1;
m_itemHover = -1;
m_clientDataItemsType = wxClientData_None;
}
bool wxPGVListBoxComboPopup::Create(wxWindow* parent)
{
if ( !wxVListBox::Create(parent,
wxID_ANY,
wxDefaultPosition,
wxDefaultSize,
wxBORDER_SIMPLE | wxLB_INT_HEIGHT | wxWANTS_CHARS) )
return false;
m_font = m_combo->GetFont();
wxVListBox::SetItemCount(m_strings.GetCount());
// TODO: Move this to SetFont
m_itemHeight = GetCharHeight() + 0;
return true;
}
wxPGVListBoxComboPopup::~wxPGVListBoxComboPopup()
{
Clear();
}
bool wxPGVListBoxComboPopup::LazyCreate()
{
// NB: There is a bug with wxVListBox that can be avoided by creating
// it later (bug causes empty space to be shown if initial selection
// is at the end of a list longer than the control can show at once).
return true;
}
// paint the control itself
void wxPGVListBoxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
{
if ( !(m_combo->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT) )
{
m_combo->DrawFocusBackground(dc,rect,0);
if ( m_combo->OnDrawListItem(dc,rect,m_value,wxPGCC_PAINTING_CONTROL) )
return;
}
wxPGComboPopup::PaintComboControl(dc,rect);
}
void wxPGVListBoxComboPopup::OnDrawItem( wxDC& dc, const wxRect& rect, size_t n) const
{
dc.SetFont(m_font);
bool isHilited = wxVListBox::GetSelection() == (int) n;
// Set correct text colour for selected items
// (must always set the correct colour - atleast GTK may have lost it
// in between calls).
if ( isHilited )
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
else
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
if ( !m_combo->OnDrawListItem(dc,rect,(int)n,0) )
dc.DrawText( GetString(n), rect.x + 2, rect.y );
}
wxCoord wxPGVListBoxComboPopup::OnMeasureItem(size_t n) const
{
int itemHeight = m_combo->OnMeasureListItem(n);
if ( itemHeight < 0 )
itemHeight = m_itemHeight;
return itemHeight;
}
void wxPGVListBoxComboPopup::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
{
// we need to render selected and current items differently
if ( IsCurrent(n) )
{
m_combo->DrawFocusBackground( dc, rect, wxCONTROL_ISSUBMENU|wxCONTROL_SELECTED );
}
//else: do nothing for the normal items
}
void wxPGVListBoxComboPopup::DismissWithEvent()
{
int selection = wxVListBox::GetSelection();
Dismiss();
if ( selection != wxNOT_FOUND )
m_stringValue = m_strings[selection];
else
m_stringValue = wxEmptyString;
if ( m_stringValue != m_combo->GetValue() )
m_combo->SetValue(m_stringValue);
m_value = selection;
SendComboBoxEvent(selection);
}
void wxPGVListBoxComboPopup::SendComboBoxEvent( int selection )
{
wxCommandEvent evt(wxEVT_COMMAND_COMBOBOX_SELECTED,m_combo->GetId());
evt.SetEventObject(m_combo);
evt.SetInt(selection);
// Set client data, if any
if ( selection >= 0 && (int)m_clientDatas.GetCount() > selection )
{
void* clientData = m_clientDatas[selection];
if ( m_clientDataItemsType == wxClientData_Object )
evt.SetClientObject((wxClientData*)clientData);
else
evt.SetClientData(clientData);
}
m_combo->GetEventHandler()->AddPendingEvent(evt);
}
// returns true if key was consumed
bool wxPGVListBoxComboPopup::HandleKey( int keycode, bool saturate )
{
int value = m_value;
int itemCount = GetCount();
if ( keycode == WXK_DOWN || keycode == WXK_RIGHT )
{
value++;
}
else if ( keycode == WXK_UP || keycode == WXK_LEFT )
{
value--;
}
else if ( keycode == WXK_PAGEDOWN )
{
value+=10;
}
else if ( keycode == WXK_PAGEUP )
{
value-=10;
}
/*
else if ( keycode == WXK_END )
{
value = itemCount-1;
}
else if ( keycode == WXK_HOME )
{
value = 0;
}
*/
else
return false;
if ( saturate )
{
if ( value >= itemCount )
value = itemCount - 1;
else if ( value < 0 )
value = 0;
}
else
{
if ( value >= itemCount )
value -= itemCount;
else if ( value < 0 )
value += itemCount;
}
if ( value == m_value )
// Even if value was same, don't skip the event
// (good for consistency)
return true;
m_value = value;
wxString valStr;
if ( value >= 0 )
m_combo->SetValue(m_strings[value]);
SendComboBoxEvent(m_value);
return true;
}
void wxPGVListBoxComboPopup::OnComboDoubleClick()
{
// Cycle on dclick (disable saturation to allow true cycling).
if ( !::wxGetKeyState(WXK_SHIFT) )
HandleKey(WXK_DOWN,false);
else
HandleKey(WXK_UP,false);
}
void wxPGVListBoxComboPopup::OnComboKeyEvent( wxKeyEvent& event )
{
// Saturated key movement on
if ( !HandleKey(event.GetKeyCode(),true) )
event.Skip();
}
void wxPGVListBoxComboPopup::OnPopup()
{
// *must* set value after size is set (this is because of a vlbox bug)
wxVListBox::SetSelection(m_value);
}
void wxPGVListBoxComboPopup::OnMouseMove(wxMouseEvent& event)
{
// Move selection to cursor if it is inside the popup
int itemHere = GetItemAtPosition(event.GetPosition());
if ( itemHere >= 0 )
wxVListBox::SetSelection(itemHere);
event.Skip();
}
void wxPGVListBoxComboPopup::OnLeftClick(wxMouseEvent& WXUNUSED(event))
{
DismissWithEvent();
}
void wxPGVListBoxComboPopup::OnKey(wxKeyEvent& event)
{
// Select item if ENTER is pressed
if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER )
{
DismissWithEvent();
}
// Hide popup if ESC is pressed
else if ( event.GetKeyCode() == WXK_ESCAPE )
Dismiss();
else
event.Skip();
}
void wxPGVListBoxComboPopup::CheckWidth( int pos )
{
wxCoord x = m_combo->OnMeasureListItemWidth(pos);
if ( x < 0 )
{
if ( !m_font.Ok() )
m_font = m_combo->GetFont();
wxCoord y;
m_combo->GetTextExtent(m_strings[pos], &x, &y, 0, 0, &m_font);
x += 4;
}
if ( m_widestWidth < x )
{
m_widestWidth = x;
}
}
void wxPGVListBoxComboPopup::Insert( const wxString& item, int pos )
{
// Need to change selection?
wxString strValue;
if ( !(m_combo->GetWindowStyle() & wxCB_READONLY) &&
m_combo->GetValue() == item )
m_value = pos;
else if ( m_value >= pos )
m_value++;
m_strings.Insert(item,pos);
if ( IsCreated() )
wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 );
// Calculate width
CheckWidth(pos);
}
int wxPGVListBoxComboPopup::Append(const wxString& item)
{
int pos = (int)m_strings.GetCount();
if ( m_combo->GetWindowStyle() & wxCB_SORT )
{
// Find position
// TODO: Could be optimized with binary search
wxArrayString strings = m_strings;
unsigned int i;
for ( i=0; i<strings.GetCount(); i++ )
{
if ( item.Cmp(strings.Item(i)) < 0 )
{
pos = (int)i;
break;
}
}
}
Insert(item,pos);
return pos;
}
void wxPGVListBoxComboPopup::Clear()
{
wxASSERT(m_combo);
m_strings.Empty();
ClearClientDatas();
if ( IsCreated() )
wxVListBox::SetItemCount(0);
}
void wxPGVListBoxComboPopup::ClearClientDatas()
{
if ( m_clientDataItemsType == wxClientData_Object )
{
size_t i;
for ( i=0; i<m_clientDatas.GetCount(); i++ )
delete (wxClientData*) m_clientDatas[i];
}
m_clientDatas.Empty();
}
void wxPGVListBoxComboPopup::SetItemClientData( wxODCIndex n,
void* clientData,
wxClientDataType clientDataItemsType )
{
// It should be sufficient to update this variable only here
m_clientDataItemsType = clientDataItemsType;
m_clientDatas.SetCount(n+1,NULL);
m_clientDatas[n] = clientData;
}
void* wxPGVListBoxComboPopup::GetItemClientData(wxODCIndex n) const
{
if ( ((wxODCIndex)m_clientDatas.GetCount()) > n )
return m_clientDatas[n];
return NULL;
}
void wxPGVListBoxComboPopup::Delete( wxODCIndex item )
{
// Remove client data, if set
if ( m_clientDatas.GetCount() )
{
if ( m_clientDataItemsType == wxClientData_Object )
delete (wxClientData*) m_clientDatas[item];
m_clientDatas.RemoveAt(item);
}
m_strings.RemoveAt(item);
int sel = GetSelection();
if ( IsCreated() )
wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 );
if ( (int)item < sel )
SetSelection(sel-1);
else if ( (int)item == sel )
SetSelection(wxNOT_FOUND);
}
int wxPGVListBoxComboPopup::FindString(const wxString& s) const
{
return m_strings.Index(s);
}
wxODCCount wxPGVListBoxComboPopup::GetCount() const
{
return m_strings.GetCount();
}
wxString wxPGVListBoxComboPopup::GetString( int item ) const
{
return m_strings[item];
}
void wxPGVListBoxComboPopup::SetString( int item, const wxString& str )
{
m_strings[item] = str;
}
wxString wxPGVListBoxComboPopup::GetStringValue() const
{
return m_stringValue;
}
void wxPGVListBoxComboPopup::SetSelection( int item )
{
// This seems to be necessary (2.5.3 w/ MingW atleast)
if ( item < -1 || item >= (int)m_strings.GetCount() )
item = -1;
m_value = item;
if ( item >= 0 )
m_stringValue = m_strings[item];
else
m_stringValue = wxEmptyString;
if ( IsCreated() )
wxVListBox::SetSelection(item);
}
int wxPGVListBoxComboPopup::GetSelection() const
{
return m_value;
}
void wxPGVListBoxComboPopup::SetStringValue( const wxString& value )
{
int index = m_strings.Index(value);
m_stringValue = value;
if ( index >= 0 && index < (int)wxVListBox::GetItemCount() )
{
wxVListBox::SetSelection(index);
m_value = index;
}
}
wxSize wxPGVListBoxComboPopup::GetAdjustedSize( int minWidth, int prefHeight, int maxHeight )
{
int height = 250;
if ( m_strings.GetCount() )
{
if ( prefHeight > 0 )
height = prefHeight;
if ( height > maxHeight )
height = maxHeight;
int totalHeight = GetTotalHeight(); // + 3;
if ( height >= totalHeight )
{
height = totalHeight;
}
else
{
// Adjust height to a multiple of the height of the first item
// NB: Calculations that take variable height into account
// are unnecessary.
int fih = GetLineHeight(0);
int shown = height/fih;
height = shown * fih;
}
}
else
height = 50;
#if defined(__WXMAC__)
// Set a minimum height since otherwise scrollbars won't draw properly
height = wxMax(50, height);
#endif
// Take scrollbar into account in width calculations
int widestWidth = m_widestWidth + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
return wxSize(minWidth > widestWidth ? minWidth : widestWidth,
height+2);
}
void wxPGVListBoxComboPopup::Populate( int n, const wxString choices[] )
{
int i;
for ( i=0; i<n; i++ )
{
const wxString& item = choices[i];
m_strings.Add(item);
CheckWidth(i);
}
if ( IsCreated() )
wxVListBox::SetItemCount(n);
// Sort the initial choices
if ( m_combo->GetWindowStyle() & wxCB_SORT )
m_strings.Sort();
// Find initial selection
wxString strValue = m_combo->GetValue();
if ( strValue.Length() )
m_value = m_strings.Index(strValue);
}
// ----------------------------------------------------------------------------
// input handling
// ----------------------------------------------------------------------------
//
// This is pushed to the event handler queue of either combo box
// or its textctrl (latter if not readonly combo).
//
class wxPGComboBoxTextCtrlHandler : public wxEvtHandler
{
public:
wxPGComboBoxTextCtrlHandler( wxPGComboControlBase* combo )
: wxEvtHandler()
{
m_combo = combo;
}
~wxPGComboBoxTextCtrlHandler() { }
void OnKey(wxKeyEvent& event);
void OnFocus(wxFocusEvent& event);
protected:
wxPGComboControlBase* m_combo;
private:
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxPGComboBoxTextCtrlHandler, wxEvtHandler)
EVT_KEY_DOWN(wxPGComboBoxTextCtrlHandler::OnKey)
EVT_SET_FOCUS(wxPGComboBoxTextCtrlHandler::OnFocus)
END_EVENT_TABLE()
void wxPGComboBoxTextCtrlHandler::OnKey(wxKeyEvent& event)
{
// Let the wxComboCtrl event handler have a go first.
wxPGComboControlBase* combo = m_combo;
wxObject* prevObj = event.GetEventObject();
event.SetId(combo->GetId());
event.SetEventObject(combo);
combo->GetEventHandler()->ProcessEvent(event);
event.SetId(((wxWindow*)prevObj)->GetId());
event.SetEventObject(prevObj);
}
void wxPGComboBoxTextCtrlHandler::OnFocus(wxFocusEvent& event)
{
// FIXME: This code does run when control is clicked,
// yet on Windows it doesn't select all the text.
if ( !(m_combo->GetInternalFlags() & wxPGCC_NO_TEXT_AUTO_SELECT) )
{
if ( m_combo->GetTextCtrl() )
m_combo->GetTextCtrl()->SelectAll();
else
m_combo->SetSelection(-1,-1);
}
// Send focus indication to parent.
// NB: This is needed for cases where the textctrl gets focus
// instead of its parent. We'll check if the focus came from
// in order to prevent a possible infinite recursion.
if ( m_combo->ConsumingTextCtrlFocusEvent() )
{
wxFocusEvent evt2(wxEVT_SET_FOCUS,m_combo->GetId());
evt2.SetEventObject(m_combo);
m_combo->GetEventHandler()->ProcessEvent(evt2);
}
event.Skip();
}
//
// This is pushed to the event handler queue of the control in popup.
//
class wxPGComboPopupExtraEventHandler : public wxEvtHandler
{
public:
wxPGComboPopupExtraEventHandler( wxPGComboControlBase* combo )
: wxEvtHandler()
{
m_combo = combo;
m_beenInside = false;
}
~wxPGComboPopupExtraEventHandler() { }
void OnMouseEvent( wxMouseEvent& event );
// Called from wxPGComboControlBase::OnPopupDismiss
void OnPopupDismiss()
{
m_beenInside = false;
}
protected:
wxPGComboControlBase* m_combo;
bool m_beenInside;
private:
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxPGComboPopupExtraEventHandler, wxEvtHandler)
EVT_MOUSE_EVENTS(wxPGComboPopupExtraEventHandler::OnMouseEvent)
END_EVENT_TABLE()
void wxPGComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event )
{
wxPoint pt = event.GetPosition();
wxSize sz = m_combo->GetPopupControl()->GetClientSize();
int evtType = event.GetEventType();
bool isInside = pt.x >= 0 && pt.y >= 0 && pt.x < sz.x && pt.y < sz.y;
if ( evtType == wxEVT_MOTION ||
evtType == wxEVT_LEFT_DOWN ||
evtType == wxEVT_RIGHT_DOWN )
{
// Block motion and click events outside the popup
if ( !isInside )
{
event.Skip(false);
return;
}
}
else if ( evtType == wxEVT_LEFT_UP )
{
// Don't let left-down events in if outside
if ( evtType == wxEVT_LEFT_DOWN )
{
if ( !isInside )
return;
}
if ( !m_beenInside )
{
if ( isInside )
{
m_beenInside = true;
}
else
{
//
// Some mouse events to popup that happen outside it, before cursor
// has been inside the popu, need to be ignored by it but relayed to
// the dropbutton.
//
wxWindow* btn = m_combo->GetButton();
if ( btn )
btn->GetEventHandler()->AddPendingEvent(event);
else
m_combo->GetEventHandler()->AddPendingEvent(event);
return;
}
event.Skip();
}
}
event.Skip();
}
// ----------------------------------------------------------------------------
// wxPGComboControlBase
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxPGComboControlBase, wxControl)
EVT_TEXT(wxID_ANY,wxPGComboControlBase::OnTextCtrlEvent)
EVT_SIZE(wxPGComboControlBase::OnSizeEvent)
EVT_KEY_DOWN(wxPGComboControlBase::OnKeyEvent)
EVT_SET_FOCUS(wxPGComboControlBase::OnFocusEvent)
EVT_KILL_FOCUS(wxPGComboControlBase::OnFocusEvent)
//EVT_BUTTON(wxID_ANY,wxPGComboControlBase::OnButtonClickEvent)
EVT_TEXT_ENTER(wxID_ANY,wxPGComboControlBase::OnTextCtrlEvent)
EVT_SYS_COLOUR_CHANGED(wxPGComboControlBase::OnSysColourChanged)
END_EVENT_TABLE()
IMPLEMENT_ABSTRACT_CLASS(wxPGComboControlBase, wxControl)
// Have global double buffer - should be enough for multiple combos
static wxBitmap* gs_doubleBuffer = (wxBitmap*) NULL;
void wxPGComboControlBase::Init()
{
m_winPopup = (wxWindow *)NULL;
m_popup = (wxWindow *)NULL;
m_isPopupShown = false;
m_btn = (wxWindow*) NULL;
m_text = (wxTextCtrl*) NULL;
m_popupInterface = (wxPGComboPopup*) NULL;
m_popupExtraHandler = (wxEvtHandler*) NULL;
m_textEvtHandler = (wxEvtHandler*) NULL;
#if INSTALL_TOPLEV_HANDLER
m_toplevEvtHandler = (wxEvtHandler*) NULL;
#endif
m_heightPopup = -1;
m_widthMinPopup = -1;
m_widthCustomPaint = 0;
m_widthCustomBorder = 0;
m_btnState = 0;
m_btnWidDefault = 0;
m_blankButtonBg = false;
m_btnWid = m_btnHei = 0;
m_btnSide = wxRIGHT;
m_btnSpacingX = 0;
m_extLeft = 0;
m_extRight = 0;
m_absIndent = -1;
m_iFlags = 0;
m_fakePopupUsage = 0;
m_skipTextCtrlFocusEvents = 0;
m_timeCanAcceptClick = 0;
}
bool wxPGComboControlBase::Create(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
{
if ( !wxControl::Create(parent,
id,
pos,
size,
style | wxWANTS_CHARS,
validator,
name) )
return false;
m_valueString = value;
// Get colours
OnThemeChange();
m_absIndent = GetNativeTextIndent();
return true;
}
void wxPGComboControlBase::InstallInputHandlers()
{
if ( m_text )
{
m_textEvtHandler = new wxPGComboBoxTextCtrlHandler(this);
m_text->PushEventHandler(m_textEvtHandler);
}
}
void wxPGComboControlBase::CreateTextCtrl( int extraStyle, const wxValidator& validator )
{
if ( !(m_windowStyle & wxCB_READONLY) )
{
m_text = new wxTextCtrl(this,
12345,
m_valueString,
wxDefaultPosition,
wxDefaultSize,
// wxTE_PROCESS_TAB is needed because on Windows, wxTAB_TRAVERSAL is
// not used by the wxPropertyGrid and therefore the tab is
// processed by looking at ancestors to see if they have
// wxTAB_TRAVERSAL. The navigation event is then sent to
// the wrong window.
wxTE_PROCESS_TAB |
wxTE_PROCESS_ENTER |
//wxWANTS_CHARS |
extraStyle,
validator);
#if defined(__WXMSW__) && !defined(__WXWINCE__)
::SendMessage(GetHwndOf(m_text), EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
#endif
// This is required for some platforms (GTK+ atleast)
m_text->SetSizeHints(2,4);
}
}
void wxPGComboControlBase::OnThemeChange()
{
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
}
bool wxPGComboControlBase::Destroy()
{
return wxControl::Destroy();
}
wxPGComboControlBase::~wxPGComboControlBase()
{
if ( HasCapture() )
ReleaseMouse();
HidePopup();
delete gs_doubleBuffer;
gs_doubleBuffer = (wxBitmap*) NULL;
#if INSTALL_TOPLEV_HANDLER
delete ((wxPGComboFrameEventHandler*)m_toplevEvtHandler);
m_toplevEvtHandler = (wxEvtHandler*) NULL;
#endif
if ( m_popup )
m_popup->RemoveEventHandler(m_popupExtraHandler);
delete m_popupExtraHandler;
delete m_popupInterface;
delete m_winPopup;
if ( m_text )
m_text->RemoveEventHandler(m_textEvtHandler);
delete m_textEvtHandler;
}
// ----------------------------------------------------------------------------
// geometry stuff
// ----------------------------------------------------------------------------
// Recalculates button and textctrl areas
void wxPGComboControlBase::CalculateAreas( int btnWidth )
{
wxSize sz = GetClientSize();
int customBorder = m_widthCustomBorder;
bool buttonOutside;
int btnBorder; // border for button only
if ( ( (m_iFlags & wxPGCC_BUTTON_OUTSIDE_BORDER) || m_blankButtonBg ) &&
m_btnSpacingX == 0 && m_btnWid == 0 && m_btnHei == 0 &&
(!m_bmpNormal.Ok() || m_blankButtonBg) )
{
buttonOutside = true;
m_iFlags |= wxPGCC_IFLAG_BUTTON_OUTSIDE;
btnBorder = 0;
}
else
{
buttonOutside = false;
m_iFlags &= ~(wxPGCC_IFLAG_BUTTON_OUTSIDE);
btnBorder = customBorder;
}
// Defaul indentation
if ( m_absIndent < 0 )
m_absIndent = GetNativeTextIndent();
int butWidth = btnWidth;
if ( butWidth <= 0 )
butWidth = m_btnWidDefault;
else
m_btnWidDefault = butWidth;
if ( butWidth <= 0 )
return;
// Adjust button width
if ( m_btnWid < 0 )
butWidth += m_btnWid;
else if ( m_btnWid > 0 )
butWidth = m_btnWid;
int butHeight = sz.y;
butHeight -= btnBorder*2;
// Adjust button height
if ( m_btnHei < 0 )
butHeight += m_btnHei;
else if ( m_btnHei > 0 )
butHeight = m_btnHei;
// Use size of normal bitmap if...
// It is larger
// OR
// button width is set to default and blank button bg is not drawn
if ( m_bmpNormal.Ok() )
{
int bmpReqWidth = m_bmpNormal.GetWidth();
int bmpReqHeight = m_bmpNormal.GetHeight();
// If drawing blank button background, we need to add some margin.
if ( m_blankButtonBg )
{
bmpReqWidth += BMP_BUTTON_MARGIN*2;
bmpReqHeight += BMP_BUTTON_MARGIN*2;
}
if ( butWidth < bmpReqWidth || ( m_btnWid == 0 && !m_blankButtonBg ) )
butWidth = bmpReqWidth;
if ( butHeight < bmpReqHeight || ( m_btnHei == 0 && !m_blankButtonBg ) )
butHeight = bmpReqHeight;
// Need to fix height?
if ( (sz.y-(customBorder*2)) < butHeight && btnWidth == 0 )
{
int newY = butHeight+(customBorder*2);
SetClientSize(-1,newY);
sz.y = newY;
}
}
int butAreaWid = butWidth + (m_btnSpacingX*2);
m_btnSize.x = butWidth;
m_btnSize.y = butHeight;
m_btnArea.x = ( m_btnSide==wxRIGHT ? sz.x - butAreaWid - btnBorder : btnBorder );
m_btnArea.y = btnBorder;
m_btnArea.width = butAreaWid;
m_btnArea.height = sz.y - (btnBorder*2);
if ( m_bmpNormal.Ok() || m_btnArea.width != butWidth || m_btnArea.height != butHeight )
m_iFlags |= wxPGCC_IFLAG_HAS_NONSTANDARD_BUTTON;
else
m_iFlags &= ~wxPGCC_IFLAG_HAS_NONSTANDARD_BUTTON;
m_tcArea.x = ( m_btnSide==wxRIGHT ? 0 : butAreaWid ) + customBorder;
m_tcArea.y = customBorder;
m_tcArea.width = sz.x - butAreaWid - (customBorder*2);
m_tcArea.height = sz.y - (customBorder*2);
/*
if ( m_text )
{
::wxMessageBox(wxString::Format(wxT("ButtonArea (%i,%i,%i,%i)\n"),m_btnArea.x,m_btnArea.y,m_btnArea.width,m_btnArea.height) +
wxString::Format(wxT("TextCtrlArea (%i,%i,%i,%i)"),m_tcArea.x,m_tcArea.y,m_tcArea.width,m_tcArea.height));
}
*/
}
void wxPGComboControlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust )
{
if ( !m_text )
return;
wxSize sz = GetClientSize();
int customBorder = m_widthCustomBorder;
if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER )
{
// Centre textctrl
int tcSizeY = m_text->GetBestSize().y;
int diff = sz.y - tcSizeY;
int y = textCtrlYAdjust + (diff/2);
if ( y < customBorder )
y = customBorder;
m_text->SetSize( m_tcArea.x + m_widthCustomPaint + m_absIndent + textCtrlXAdjust,
y,
m_tcArea.width - g_comboMargin -
(textCtrlXAdjust + m_widthCustomPaint + m_absIndent),
-1 );
// Make sure textctrl doesn't exceed the bottom custom border
wxSize tsz = m_text->GetSize();
diff = (y + tsz.y) - (sz.y - customBorder);
if ( diff >= 0 )
{
tsz.y = tsz.y - diff - 1;
m_text->SetSize(tsz);
}
}
else
{
m_text->SetSize( m_tcArea.x,
0,
sz.x - m_btnArea.x - m_widthCustomPaint - customBorder,
sz.y );
}
}
wxSize wxPGComboControlBase::DoGetBestSize() const
{
wxSize sizeText(150,0);
if ( m_text )
sizeText = m_text->GetBestSize();
// TODO: Better method to calculate close-to-native control height.
int fhei;
if ( m_font.Ok() )
fhei = (m_font.GetPointSize()*2) + 5;
else if ( wxNORMAL_FONT->Ok() )
fhei = (wxNORMAL_FONT->GetPointSize()*2) + 5;
else
fhei = sizeText.y + 4;
// Need to force height to accomodate bitmap?
int btnSizeY = m_btnSize.y;
if ( m_bmpNormal.Ok() && fhei < btnSizeY )
fhei = btnSizeY;
// Control height doesn't depend on border
/*
// Add border
int border = m_windowStyle & wxBORDER_MASK;
if ( border == wxSIMPLE_BORDER )
fhei += 2;
else if ( border == wxNO_BORDER )
fhei += (m_widthCustomBorder*2);
else
// Sunken etc.
fhei += 4;
*/
// Final adjustments
#ifdef __WXGTK__
fhei += 1;
#endif
wxSize ret(sizeText.x + g_comboMargin + DEFAULT_DROPBUTTON_WIDTH,
fhei);
CacheBestSize(ret);
return ret;
}
void wxPGComboControlBase::DoMoveWindow(int x, int y, int width, int height)
{
// SetSize is called last in create, so it marks the end of creation
m_iFlags |= wxPGCC_IFLAG_CREATED;
wxControl::DoMoveWindow(x, y, width, height);
}
void wxPGComboControlBase::OnSizeEvent( wxSizeEvent& event )
{
if ( !IsCreated() )
return;
// defined by actual wxComboControls
OnResize();
event.Skip();
}
// ----------------------------------------------------------------------------
// standard operations
// ----------------------------------------------------------------------------
bool wxPGComboControlBase::Enable(bool enable)
{
if ( !wxControl::Enable(enable) )
return false;
if ( m_btn )
m_btn->Enable(enable);
if ( m_text )
m_text->Enable(enable);
return true;
}
bool wxPGComboControlBase::Show(bool show)
{
if ( !wxControl::Show(show) )
return false;
if (m_btn)
m_btn->Show(show);
if (m_text)
m_text->Show(show);
return true;
}
bool wxPGComboControlBase::SetFont ( const wxFont& font )
{
if ( !wxControl::SetFont(font) )
return false;
if (m_text)
m_text->SetFont(font);
return true;
}
#if wxUSE_TOOLTIPS
void wxPGComboControlBase::DoSetToolTip(wxToolTip *tooltip)
{
wxControl::DoSetToolTip(tooltip);
// Set tool tip for button and text box
if ( tooltip )
{
const wxString &tip = tooltip->GetTip();
if ( m_text ) m_text->SetToolTip(tip);
if ( m_btn ) m_btn->SetToolTip(tip);
}
else
{
if ( m_text ) m_text->SetToolTip( (wxToolTip*) NULL );
if ( m_btn ) m_btn->SetToolTip( (wxToolTip*) NULL );
}
}
#endif // wxUSE_TOOLTIPS
// ----------------------------------------------------------------------------
// painting
// ----------------------------------------------------------------------------
// draw focus background on area in a way typical on platform
void wxPGComboControlBase::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags )
{
wxSize sz = GetClientSize();
bool isEnabled;
bool doDrawFocusRect; // also selected
// For smaller size control (and for disabled background) use less spacing
int focusSpacingX;
int focusSpacingY;
if ( !(flags & wxCONTROL_ISSUBMENU) )
{
// Drawing control
isEnabled = IsEnabled();
doDrawFocusRect = ShouldDrawFocus();
// Windows-style: for smaller size control (and for disabled background) use less spacing
//focusSpacingX = isEnabled ? 2 : 1;
focusSpacingX = 1;
focusSpacingY = sz.y > (GetCharHeight()+500) && isEnabled ? 2 : 1;
}
else
{
// Drawing a list item
isEnabled = true; // they are never disabled
doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false;
focusSpacingX = 0;
focusSpacingY = 0;
}
// Set the background sub-rectangle for selection, disabled etc
wxRect selRect(rect);
selRect.y += focusSpacingY;
selRect.height -= (focusSpacingY*2);
int wcp = 0;
if ( !(flags & wxCONTROL_ISSUBMENU) )
wcp += m_widthCustomPaint;
selRect.x += wcp + focusSpacingX;
selRect.width -= wcp + (focusSpacingX*2);
wxColour bgCol;
bool doDrawSelRect = true;
if ( isEnabled )
{
// If popup is hidden and this control is focused,
// then draw the focus-indicator (selbgcolor background etc.).
if ( doDrawFocusRect )
{
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
}
else
{
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
bgCol = GetBackgroundColour();
doDrawSelRect = false;
}
}
else
{
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
bgCol = GetBackgroundColour();
}
dc.SetBrush( bgCol );
if ( doDrawSelRect )
{
dc.SetPen( bgCol );
dc.DrawRectangle( selRect );
}
}
void wxPGComboControlBase::DrawButton( wxDC& dc, const wxRect& rect, int flags )
{
int drawState = m_btnState;
if ( (m_iFlags & wxPGCC_BUTTON_STAYS_DOWN) &&
IsPopupShown() )
drawState |= wxCONTROL_PRESSED;
wxRect drawRect(rect.x+m_btnSpacingX,
rect.y+((rect.height-m_btnSize.y)/2),
m_btnSize.x,
m_btnSize.y);
// Make sure area is not larger than the control
if ( drawRect.y < rect.y )
drawRect.y = rect.y;
if ( drawRect.height > rect.height )
drawRect.height = rect.height;
bool enabled = IsEnabled();
if ( !enabled )
drawState |= wxCONTROL_DISABLED;
if ( !m_bmpNormal.Ok() )
{
if ( flags & Button_BitmapOnly )
return;
// Need to clear button background even if m_btn is present
if ( flags & Button_PaintBackground )
{
wxColour bgCol;
if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
bgCol = GetParent()->GetBackgroundColour();
else
bgCol = GetBackgroundColour();
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(rect);
}
// Draw standard button
wxRendererNative::Get().DrawComboBoxDropButton(this,
dc,
drawRect,
drawState);
}
else
{
// Draw bitmap
wxBitmap* pBmp;
if ( !enabled )
pBmp = &m_bmpDisabled;
else if ( m_btnState & wxCONTROL_PRESSED )
pBmp = &m_bmpPressed;
else if ( m_btnState & wxCONTROL_CURRENT )
pBmp = &m_bmpHover;
else
pBmp = &m_bmpNormal;
#if wxCHECK_VERSION(2, 7, 0)
if ( m_blankButtonBg )
{
// If using blank button background, we need to clear its background
// with button face colour instead of colour for rest of the control.
if ( flags & Button_PaintBackground )
{
wxColour bgCol = GetParent()->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
//wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
dc.SetPen(bgCol);
dc.SetBrush(bgCol);
dc.DrawRectangle(rect);
}
if ( !(flags & Button_BitmapOnly) )
{
wxRendererNative::Get().DrawPushButton(this,
dc,
drawRect,
drawState);
}
}
else
#endif
{
// Need to clear button background even if m_btn is present
// (assume non-button background was cleared just before this call so brushes are good)
if ( flags & Button_PaintBackground )
dc.DrawRectangle(rect);
}
// Draw bitmap centered in drawRect
dc.DrawBitmap(*pBmp,
drawRect.x + (drawRect.width-pBmp->GetWidth())/2,
drawRect.y + (drawRect.height-pBmp->GetHeight())/2,
true);
}
}
void wxPGComboControlBase::RecalcAndRefresh()
{
if ( IsCreated() )
{
wxSizeEvent evt(GetSize(),GetId());
GetEventHandler()->ProcessEvent(evt);
Refresh();
}
}
bool wxPGComboControlBase::OnDrawListItem( wxDC& WXUNUSED(dc),
const wxRect& WXUNUSED(rect),
int WXUNUSED(item),
int WXUNUSED(flags) )
{
return false; // signals caller to make default drawing
}
wxCoord wxPGComboControlBase::OnMeasureListItem( int WXUNUSED(item) )
{
return -1; // signals caller to use default
}
wxCoord wxPGComboControlBase::OnMeasureListItemWidth( int WXUNUSED(item) )
{
return -1; // signals caller to use default
}
// ----------------------------------------------------------------------------
// miscellaneous event handlers
// ----------------------------------------------------------------------------
void wxPGComboControlBase::OnTextCtrlEvent(wxCommandEvent& event)
{
// Change event id and relay it forward
event.SetId(GetId());
event.Skip();
}
// call if cursor is on button area or mouse is captured for the button
bool wxPGComboControlBase::HandleButtonMouseEvent( wxMouseEvent& event,
int flags )
{
int type = event.GetEventType();
if ( type == wxEVT_MOTION )
{
if ( flags & wxPGCC_MF_ON_BUTTON )
{
if ( !(m_btnState & wxCONTROL_CURRENT) )
{
// Mouse hover begins
m_btnState |= wxCONTROL_CURRENT;
if ( HasCapture() ) // Retain pressed state.
m_btnState |= wxCONTROL_PRESSED;
Refresh();
}
}
else if ( (m_btnState & wxCONTROL_CURRENT) )
{
// Mouse hover ends
m_btnState &= ~(wxCONTROL_CURRENT|wxCONTROL_PRESSED);
Refresh();
}
}
else if ( type == wxEVT_LEFT_DOWN )
{
// Only accept event if it wasn't right after popup dismiss
//if ( ::wxGetLocalTimeMillis() > m_timeCanClick )
{
// Need to test this, because it might be outside.
if ( flags & wxPGCC_MF_ON_BUTTON )
{
m_btnState |= wxCONTROL_PRESSED;
Refresh();
if ( !(m_iFlags & wxPGCC_POPUP_ON_MOUSE_UP) )
OnButtonClick();
else
// If showing popup now, do not capture mouse or there will be interference
CaptureMouse();
}
}
/*else
{
m_btnState = 0;
}*/
}
else if ( type == wxEVT_LEFT_UP )
{
// Only accept event if mouse was left-press was previously accepted
if ( HasCapture() )
ReleaseMouse();
if ( m_btnState & wxCONTROL_PRESSED )
{
// If mouse was inside, fire the click event.
if ( m_iFlags & wxPGCC_POPUP_ON_MOUSE_UP )
{
if ( flags & wxPGCC_MF_ON_BUTTON )
OnButtonClick();
}
m_btnState &= ~(wxCONTROL_PRESSED);
Refresh();
}
}
else if ( type == wxEVT_LEAVE_WINDOW )
{
if ( m_btnState & (wxCONTROL_CURRENT|wxCONTROL_PRESSED) )
{
m_btnState &= ~(wxCONTROL_CURRENT);
// Mouse hover ends
if ( !m_isPopupShown )
{
m_btnState &= ~(wxCONTROL_PRESSED);
Refresh();
}
}
}
else
return false;
return true;
}
// Conversion to double-clicks and some basic filtering
// returns true if event was consumed or filtered
bool wxPGComboControlBase::PreprocessMouseEvent( wxMouseEvent& event,
int WXUNUSED(flags) )
{
wxLongLong t = ::wxGetLocalTimeMillis();
int evtType = event.GetEventType();
if ( m_isPopupShown &&
( evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_RIGHT_DOWN ) )
{
HidePopup();
return true;
}
//
// Generate our own double-clicks
// (to allow on-focus dc-event on double-clicks instead of triple-clicks)
/*if ( (m_windowStyle & wxPGCC_DCLICK_CYCLES) &&
!m_isPopupShown &&
//!(handlerFlags & wxPGCC_MF_ON_BUTTON) )
!(flags & wxPGCC_MF_ON_BUTTON) )
{
if ( evtType == wxEVT_LEFT_DOWN )
{
// Set value to avoid up-events without corresponding downs
m_downReceived = true;
}
else if ( evtType == wxEVT_LEFT_DCLICK )
{
// We'll make our own double-clicks
//evtType = 0;
event.SetEventType(0);
return true;
}
else if ( evtType == wxEVT_LEFT_UP )
{
if ( m_downReceived || m_timeLastMouseUp == 1 )
{
wxLongLong timeFromLastUp = (t-m_timeLastMouseUp);
if ( timeFromLastUp < DOUBLE_CLICK_CONVERSION_TRESHOLD )
{
//type = wxEVT_LEFT_DCLICK;
event.SetEventType(wxEVT_LEFT_DCLICK);
m_timeLastMouseUp = 1;
}
else
{
m_timeLastMouseUp = t;
}
//m_downReceived = false;
}
}
}*/
// Filter out clicks on button immediately after popup dismiss (Windows like behaviour)
if ( evtType == wxEVT_LEFT_DOWN && t < m_timeCanAcceptClick )
{
event.SetEventType(0);
return true;
}
return false;
}
void wxPGComboControlBase::HandleNormalMouseEvent( wxMouseEvent& event )
{
int evtType = event.GetEventType();
if ( (evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_LEFT_DCLICK) &&
(m_windowStyle & wxCB_READONLY) )
{
if ( m_isPopupShown )
{
#if !wxUSE_POPUPWIN
// Normally do nothing - evt handler should close it for us
#if ALLOW_FAKE_POPUP
if ( m_fakePopupUsage == 2 )
HidePopup();
#endif
#elif !USE_TRANSIENT_POPUP
// Click here always hides the popup.
HidePopup();
#endif
}
else
{
if ( !(m_windowStyle & wxPGCC_DCLICK_CYCLES) )
{
// In read-only mode, clicking the text is the
// same as clicking the button.
OnButtonClick();
}
else if ( /*evtType == wxEVT_LEFT_UP || */evtType == wxEVT_LEFT_DCLICK )
{
//if ( m_popupInterface->CycleValue() )
// Refresh();
if ( m_popupInterface )
m_popupInterface->OnComboDoubleClick();
}
}
}
else
if ( m_isPopupShown )
{
// relay (some) mouse events to the popup
if ( evtType == wxEVT_MOUSEWHEEL )
m_popup->AddPendingEvent(event);
}
else if ( evtType )
event.Skip();
}
void wxPGComboControlBase::OnKeyEvent( wxKeyEvent& event )
{
int keycode = event.GetKeyCode();
if ( keycode == WXK_TAB &&
!IsPopupShown() )
{
wxNavigationKeyEvent evt;
evt.SetFlags(wxNavigationKeyEvent::FromTab|
(!event.ShiftDown()?wxNavigationKeyEvent::IsForward:
wxNavigationKeyEvent::IsBackward));
evt.SetEventObject(this);
GetParent()->GetEventHandler()->AddPendingEvent(evt);
return;
}
if ( IsPopupShown() )
{
// pass it to the popped up control
GetPopupControl()->AddPendingEvent(event);
}
else // no popup
{
int comboStyle = GetWindowStyle();
wxPGComboPopup* popupInterface = GetPopup();
if ( !popupInterface )
{
event.Skip();
return;
}
if ( (comboStyle & wxCB_READONLY) ||
( keycode != WXK_RIGHT && keycode != WXK_LEFT )
)
{
// Alternate keys: UP and DOWN show the popup instead of cycling
if ( (comboStyle & wxPGCC_ALT_KEYS) )
{
if ( keycode == WXK_UP || keycode == WXK_DOWN )
{
OnButtonClick();
return;
}
else
event.Skip();
}
else
popupInterface->OnComboKeyEvent(event);
}
else
event.Skip();
}
}
void wxPGComboControlBase::OnFocusEvent( wxFocusEvent& event )
{
if ( event.GetEventType() == wxEVT_SET_FOCUS )
{
if ( m_text && m_text != ::wxWindow::FindFocus() )
{
m_skipTextCtrlFocusEvents++;
m_text->SetFocus();
}
}
Refresh();
}
void wxPGComboControlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
{
OnThemeChange();
// indentation may also have changed
if ( !(m_iFlags & wxPGCC_IFLAG_INDENT_SET) )
m_absIndent = GetNativeTextIndent();
RecalcAndRefresh();
}
// ----------------------------------------------------------------------------
// popup handling
// ----------------------------------------------------------------------------
// Create popup window and the child control
void wxPGComboControlBase::CreatePopup()
{
wxPGComboPopup* popupInterface = m_popupInterface;
wxWindow* popup;
if ( !m_winPopup )
m_winPopup = new wxPGComboPopupWindow( this, wxNO_BORDER );
popupInterface->Create(m_winPopup);
m_popup = popup = popupInterface->GetControl();
m_popupExtraHandler = new wxPGComboPopupExtraEventHandler(this);
popup->PushEventHandler( m_popupExtraHandler );
popupInterface->m_iFlags |= wxPGCP_IFLAG_CREATED;
}
void wxPGComboControlBase::SetPopup( wxPGComboPopup* iface )
{
delete m_popupInterface;
delete m_winPopup;
m_popupInterface = iface;
#if ALLOW_FAKE_POPUP
m_fakePopupUsage = 0;
#endif
if ( !iface->LazyCreate() || m_winPopup )
{
CreatePopup();
/*
m_winPopup = new wxPGComboPopupWindow( this, wxNO_BORDER );
// Create popup right away
iface->Create(m_winPopup);
m_popup = iface->GetControl();
m_popupExtraHandler = new wxPGComboPopupExtraEventHandler(this);
m_popup->PushEventHandler( m_popupExtraHandler );
// Add interface as event handler
//m_popup->PushEventHandler( iface );
*/
// FIXME: This bypasses wxGTK popupwindow bug
// (i.e. window is not initially hidden when it should be)
m_winPopup->Hide();
#if ALLOW_FAKE_POPUP
m_fakePopupUsage = 1;
#endif
}
else
{
m_popup = (wxWindow*) NULL;
}
// This must be after creation
if ( m_valueString.length() )
iface->SetStringValue(m_valueString);
}
void wxPGComboControlBase::OnButtonClick()
{
// Derived classes can override this method for totally custom
// popup action
ShowPopup();
}
void wxPGComboControlBase::ShowPopup()
{
wxCHECK_RET( m_popupInterface, wxT("no popup interface set for wxComboControl") );
wxCHECK_RET( !IsPopupShown(), wxT("popup window already shown") );
SetFocus();
// Space above and below
int screenHeight;
wxPoint scrPos;
int spaceAbove;
int spaceBelow;
int maxHeightPopup;
wxSize ctrlSz = GetSize();
#if ALLOW_FAKE_POPUP
int existingHeight = 200;
if ( m_popup )
existingHeight = m_popup->GetSize().y;
int screenWidth;
GetParent()->GetClientSize(&screenWidth,&screenHeight);
screenWidth -= 2;
scrPos = GetPosition();
spaceAbove = scrPos.y - 2;
spaceBelow = screenHeight - spaceAbove - ctrlSz.y - 4;
maxHeightPopup = spaceBelow;
if ( spaceAbove > spaceBelow )
maxHeightPopup = spaceAbove;
if ( maxHeightPopup >= existingHeight )
{
if ( m_winPopup && m_fakePopupUsage!=2 )
{
delete m_winPopup;
m_winPopup = (wxWindow*) NULL;
m_popup = (wxWindow*) NULL;
}
m_fakePopupUsage = 2;
}
else
{
if ( m_winPopup && m_fakePopupUsage!=1 )
{
delete m_winPopup;
m_winPopup = (wxWindow*) NULL;
m_popup = (wxWindow*) NULL;
}
m_fakePopupUsage = 1;
#else
{
#endif
screenHeight = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y );
scrPos = GetParent()->ClientToScreen(GetPosition());
spaceAbove = scrPos.y;
spaceBelow = screenHeight - spaceAbove - ctrlSz.y;
maxHeightPopup = spaceBelow;
if ( spaceAbove > spaceBelow )
maxHeightPopup = spaceAbove;
}
// Width
int widthPopup = ctrlSz.x + m_extLeft + m_extRight;
if ( widthPopup < m_widthMinPopup )
widthPopup = m_widthMinPopup;
wxWindow* winPopup = m_winPopup;
wxWindow* popup;
// Need to disable tab traversal of parent
//
// NB: This is to fix a bug in wxMSW. In theory it could also be fixed
// by, for instance, adding check to window.cpp:wxWindowMSW::MSWProcessMessage
// that if transient popup is open, then tab traversal is to be ignored.
// However, I think this code would still be needed for cases where
// transient popup doesn't work yet (wxWINCE?).
wxWindow* parent = GetParent();
int parentFlags = parent->GetWindowStyle();
if ( parentFlags & wxTAB_TRAVERSAL )
{
parent->SetWindowStyle( parentFlags & ~(wxTAB_TRAVERSAL) );
m_iFlags |= wxPGCC_IFLAG_PARENT_TAB_TRAVERSAL;
}
if ( !winPopup )
{
#if ALLOW_FAKE_POPUP
if ( m_fakePopupUsage == 2 )
{
winPopup = new wxWindow();
#ifdef __WXMSW__
// Only wxMSW supports this
winPopup->Hide();
#endif
winPopup->Create( GetParent(), -1 );
m_winPopup = winPopup;
}
#endif
CreatePopup();
winPopup = m_winPopup;
popup = m_popup;
}
else
{
popup = m_popup;
}
wxASSERT( !m_popup || m_popup == popup ); // Consistency check.
wxSize adjustedSize = m_popupInterface->GetAdjustedSize(widthPopup,
m_heightPopup<=0?DEFAULT_POPUP_HEIGHT:m_heightPopup,
maxHeightPopup);
popup->SetSize(adjustedSize);
popup->Move(0,0);
m_popupInterface->OnPopup();
#if ALLOW_FAKE_POPUP
// Make sure fake popup didn't get too big
if ( m_fakePopupUsage == 2 && popup->GetSize().x > screenWidth )
{
popup->SetSize(screenWidth-2,popup->GetSize().y);
}
#endif
//
// Reposition and resize popup window
//
wxSize szp = popup->GetSize();
int popupX;
int popupY = scrPos.y + ctrlSz.y;
// Anchor popup to the side the dropbutton is on
if ( m_btnSide == wxRIGHT )
popupX = scrPos.x + ctrlSz.x + m_extRight- szp.x;
else
popupX = scrPos.x - m_extLeft;
#if ALLOW_FAKE_POPUP
if ( m_fakePopupUsage == 2 )
{
if ( spaceBelow < szp.y )
{
if ( spaceAbove > spaceBelow )
{
if ( szp.y > spaceAbove )
{
popup->SetSize(szp.x,spaceAbove);
szp.y = spaceAbove;
}
popupY = scrPos.y - szp.y;
}
else
{
if ( szp.y > spaceBelow )
{
popup->SetSize(szp.x,spaceBelow);
szp.y = spaceBelow;
}
}
}
}
else
#endif
if ( spaceBelow < szp.y )
{
popupY = scrPos.y - szp.y;
}
// Move to position
//wxLogDebug(wxT("popup scheduled position1: %i,%i"),ptp.x,ptp.y);
//wxLogDebug(wxT("popup position1: %i,%i"),winPopup->GetPosition().x,winPopup->GetPosition().y);
// Some platforms (GTK) may need these two to be separate
winPopup->SetSize( szp.x, szp.y );
winPopup->Move( popupX, popupY );
//wxLogDebug(wxT("popup position2: %i,%i"),winPopup->GetPosition().x,winPopup->GetPosition().y);
m_popup = popup;
// Set string selection (must be this way instead of SetStringSelection)
if ( m_text )
{
if ( !(m_iFlags & wxPGCC_NO_TEXT_AUTO_SELECT) )
m_text->SelectAll();
m_popupInterface->SetStringValue( m_text->GetValue() );
}
else
{
// This is neede since focus/selection indication may change when popup is shown
// FIXME: But in that case, would m_isPopupShown need to go before this?
Refresh();
}
// This must be after SetStringValue
m_isPopupShown = true;
// Show it
#if USE_TRANSIENT_POPUP
((wxPopupTransientWindow*)winPopup)->Popup(popup);
#else
winPopup->Show();
#endif
#if INSTALL_TOPLEV_HANDLER
// If our real popup is wxDialog, then only install handler
// incase of fake popup.
#if !wxUSE_POPUPWIN
if ( m_fakePopupUsage != 2 )
{
if ( m_toplevEvtHandler )
{
delete m_toplevEvtHandler;
m_toplevEvtHandler = (wxEvtHandler*) NULL;
}
}
else
#endif
{
// Put top level window event handler into place
if ( !m_toplevEvtHandler )
m_toplevEvtHandler = new wxPGComboFrameEventHandler(this);
wxWindow* toplev = ::wxGetTopLevelParent( this );
wxASSERT( toplev );
((wxPGComboFrameEventHandler*)m_toplevEvtHandler)->OnPopup();
toplev->PushEventHandler( m_toplevEvtHandler );
}
#endif
}
void wxPGComboControlBase::OnPopupDismiss()
{
// Just in case, avoid double dismiss
if ( !m_isPopupShown )
return;
// *Must* set this before focus etc.
m_isPopupShown = false;
// Inform popup control itself
m_popupInterface->OnDismiss();
//((wxComboDropButton*)m_btn)->SetPopup( (wxWindow*) NULL );
if ( m_popupExtraHandler )
((wxPGComboPopupExtraEventHandler*)m_popupExtraHandler)->OnPopupDismiss();
#if INSTALL_TOPLEV_HANDLER
// Remove top level window event handler
if ( m_toplevEvtHandler )
{
wxWindow* toplev = ::wxGetTopLevelParent( this );
if ( toplev )
toplev->RemoveEventHandler( m_toplevEvtHandler );
}
#endif
#if !wxUSE_POPUPWIN
if ( m_fakePopupUsage != 2 )
GetParent()->SetFocus();
#endif
m_timeCanAcceptClick = ::wxGetLocalTimeMillis() + 150;
// If cursor not on dropdown button, then clear its state
// (technically not required by all ports, but do it for all just in case)
if ( !m_btnArea.wxPGRectContains(ScreenToClient(::wxGetMousePosition())) )
m_btnState = 0;
// Return parent's tab traversal flag.
// See ShowPopup for notes.
if ( m_iFlags & wxPGCC_IFLAG_PARENT_TAB_TRAVERSAL )
{
wxWindow* parent = GetParent();
parent->SetWindowStyle( parent->GetWindowStyle() | wxTAB_TRAVERSAL );
m_iFlags &= ~(wxPGCC_IFLAG_PARENT_TAB_TRAVERSAL);
}
// refresh control (necessary even if m_text)
Refresh();
}
void wxPGComboControlBase::HidePopup()
{
// Should be able to call this without popup interface
//wxCHECK_RET( m_popupInterface, _T("no popup interface") );
if ( !m_isPopupShown )
return;
// transfer value and show it in textctrl, if any
SetValue( m_popupInterface->GetStringValue() );
#if USE_TRANSIENT_POPUP
((wxPopupTransientWindow*)m_winPopup)->Dismiss();
#else
m_winPopup->Hide();
#endif
OnPopupDismiss();
}
// ----------------------------------------------------------------------------
// customization methods
// ----------------------------------------------------------------------------
void wxPGComboControlBase::SetButtonPosition( int width, int height,
int side, int spacingX )
{
m_btnWid = width;
m_btnHei = height;
m_btnSide = side;
m_btnSpacingX = spacingX;
RecalcAndRefresh();
}
void wxPGComboControlBase::SetButtonBitmaps( const wxBitmap& bmpNormal,
bool blankButtonBg,
const wxBitmap& bmpPressed,
const wxBitmap& bmpHover,
const wxBitmap& bmpDisabled )
{
m_bmpNormal = bmpNormal;
m_blankButtonBg = blankButtonBg;
if ( bmpPressed.Ok() )
m_bmpPressed = bmpPressed;
else
m_bmpPressed = bmpNormal;
if ( bmpHover.Ok() )
m_bmpHover = bmpHover;
else
m_bmpHover = bmpNormal;
if ( bmpDisabled.Ok() )
m_bmpDisabled = bmpDisabled;
else
m_bmpDisabled = bmpNormal;
RecalcAndRefresh();
}
void wxPGComboControlBase::SetCustomPaintWidth( int width )
{
if ( m_text )
{
// move textctrl accordingly
wxRect r = m_text->GetRect();
int inc = width - m_widthCustomPaint;
r.x += inc;
r.width -= inc;
m_text->SetSize( r );
}
m_widthCustomPaint = width;
RecalcAndRefresh();
}
void wxPGComboControlBase::SetTextIndent( int indent )
{
if ( indent < 0 )
{
m_absIndent = GetNativeTextIndent();
m_iFlags &= ~(wxPGCC_IFLAG_INDENT_SET);
}
else
{
m_absIndent = indent;
m_iFlags |= wxPGCC_IFLAG_INDENT_SET;
}
RecalcAndRefresh();
}
wxCoord wxPGComboControlBase::GetNativeTextIndent() const
{
return DEFAULT_TEXT_INDENT;
}
// ----------------------------------------------------------------------------
// methods forwarded to wxTextCtrl
// ----------------------------------------------------------------------------
wxString wxPGComboControlBase::GetValue() const
{
if ( m_text )
return m_text->GetValue();
return m_valueString;
}
void wxPGComboControlBase::SetValue(const wxString& value)
{
if ( m_text )
{
m_text->SetValue(value);
if ( !(m_iFlags & wxPGCC_NO_TEXT_AUTO_SELECT) )
m_text->SelectAll();
}
// Since wxPGComboPopup may want to paint the combo as well, we need
// to set the string value here (as well as sometimes in ShowPopup).
if ( m_valueString != value && m_popupInterface )
{
m_popupInterface->SetStringValue(value);
}
m_valueString = value;
Refresh();
}
void wxPGComboControlBase::Copy()
{
if ( m_text )
m_text->Copy();
}
void wxPGComboControlBase::Cut()
{
if ( m_text )
m_text->Cut();
}
void wxPGComboControlBase::Paste()
{
if ( m_text )
m_text->Paste();
}
void wxPGComboControlBase::SetInsertionPoint(long pos)
{
if ( m_text )
m_text->SetInsertionPoint(pos);
}
void wxPGComboControlBase::SetInsertionPointEnd()
{
if ( m_text )
m_text->SetInsertionPointEnd();
}
long wxPGComboControlBase::GetInsertionPoint() const
{
if ( m_text )
return m_text->GetInsertionPoint();
return 0;
}
long wxPGComboControlBase::GetLastPosition() const
{
if ( m_text )
return m_text->GetLastPosition();
return 0;
}
void wxPGComboControlBase::Replace(long from, long to, const wxString& value)
{
if ( m_text )
m_text->Replace(from, to, value);
}
void wxPGComboControlBase::Remove(long from, long to)
{
if ( m_text )
m_text->Remove(from, to);
}
void wxPGComboControlBase::SetSelection(long from, long to)
{
if ( m_text )
m_text->SetSelection(from, to);
}
void wxPGComboControlBase::Undo()
{
if ( m_text )
m_text->Undo();
}
// ----------------------------------------------------------------------------
// wxPGGenericComboControl
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxPGGenericComboControl, wxPGComboControlBase)
//EVT_SIZE(wxPGGenericComboControl::OnSizeEvent)
EVT_PAINT(wxPGGenericComboControl::OnPaintEvent)
EVT_MOUSE_EVENTS(wxPGGenericComboControl::OnMouseEvent)
END_EVENT_TABLE()
IMPLEMENT_DYNAMIC_CLASS(wxPGGenericComboControl, wxPGComboControlBase)
void wxPGGenericComboControl::Init()
{
}
bool wxPGGenericComboControl::Create(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
{
// Set border
long border = style & wxBORDER_MASK;
#if defined(__WXUNIVERSAL__)
if ( !border )
border = wxBORDER_SIMPLE;
#elif defined(__WXMSW__)
if ( !border )
border = wxBORDER_SIMPLE;
#else
if ( !border )
{
border = wxBORDER_NONE;
m_widthCustomBorder = 1;
}
Customize( wxPGCC_BUTTON_OUTSIDE_BORDER |
wxPGCC_NO_TEXT_AUTO_SELECT |
wxPGCC_BUTTON_STAYS_DOWN );
#endif
style = (style & ~(wxBORDER_MASK)) | border;
// create main window
if ( !wxPGComboControlBase::Create(parent,
id,
value,
wxDefaultPosition,
wxDefaultSize,
style | wxFULL_REPAINT_ON_RESIZE,
wxDefaultValidator,
name) )
return false;
// Create textctrl, if necessary
CreateTextCtrl( wxBORDER_NONE, validator );
// Add keyboard input handlers for main control and textctrl
InstallInputHandlers();
// Set background
SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // for double-buffering
// SetSize should be called last
SetSize(pos.x,pos.y,size.x,size.y);
return true;
}
wxPGGenericComboControl::~wxPGGenericComboControl()
{
}
void wxPGGenericComboControl::OnResize()
{
// Recalculates button and textctrl areas
CalculateAreas(DEFAULT_DROPBUTTON_WIDTH);
#if 0
// Move separate button control, if any, to correct position
if ( m_btn )
{
wxSize sz = GetClientSize();
m_btn->SetSize( m_btnArea.x + m_btnSpacingX,
(sz.y-m_btnSize.y)/2,
m_btnSize.x,
m_btnSize.y );
}
#endif
// Move textctrl, if any, accordingly
PositionTextCtrl( TEXTCTRLXADJUST, TEXTCTRLYADJUST );
}
void wxPGGenericComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
{
wxSize sz = GetClientSize();
#if !wxCHECK_VERSION(2, 7, 1)
// If size is larger, recalculate double buffer bitmap
if ( !gs_doubleBuffer ||
sz.x > gs_doubleBuffer->GetWidth() ||
sz.y > gs_doubleBuffer->GetHeight() )
{
delete gs_doubleBuffer;
gs_doubleBuffer = new wxBitmap(sz.x+25,sz.y);
}
wxBufferedPaintDC dc(this,*gs_doubleBuffer);
#else
wxAutoBufferedPaintDC dc(this);
#endif
const wxRect& rectb = m_btnArea;
wxRect rect = m_tcArea;
// artificial simple border
if ( m_widthCustomBorder )
{
int customBorder = m_widthCustomBorder;
// Set border colour
wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
customBorder,
wxSOLID );
dc.SetPen( pen1 );
// area around both controls
wxRect rect2(0,0,sz.x,sz.y);
if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
{
rect2 = m_tcArea;
if ( customBorder == 1 )
{
rect2.Inflate(1);
}
else
{
#ifdef __WXGTK__
rect2.x -= 1;
rect2.y -= 1;
#else
rect2.x -= customBorder;
rect2.y -= customBorder;
#endif
rect2.width += 1 + customBorder;
rect2.height += 1 + customBorder;
}
}
dc.SetBrush( *wxTRANSPARENT_BRUSH );
dc.DrawRectangle(rect2);
}
wxColour winCol = GetBackgroundColour();
dc.SetBrush(winCol);
dc.SetPen(winCol);
//wxLogDebug(wxT("hei: %i tcy: %i tchei: %i"),GetClientSize().y,m_tcArea.y,m_tcArea.height);
//wxLogDebug(wxT("btnx: %i tcx: %i tcwid: %i"),m_btnArea.x,m_tcArea.x,m_tcArea.width);
// clear main background
dc.DrawRectangle(rect);
if ( !m_btn )
// Standard button rendering
DrawButton(dc, rectb);
// paint required portion on the control
if ( !m_text || m_widthCustomPaint )
{
wxASSERT( m_widthCustomPaint >= 0 );
// this is intentionally here to allow drawed rectangle's
// right edge to be hidden
if ( m_text )
rect.width = m_widthCustomPaint;
dc.SetFont( GetFont() );
dc.SetClippingRegion(rect);
m_popupInterface->PaintComboControl(dc, rect);
}
}
void wxPGGenericComboControl::OnMouseEvent( wxMouseEvent& event )
{
bool isOnButtonArea = m_btnArea.wxPGRectContains(event.m_x,event.m_y);
int handlerFlags = isOnButtonArea ? wxPGCC_MF_ON_BUTTON : 0;
// Preprocessing fabricates double-clicks and prevents
// (it may also do other common things in future)
if ( PreprocessMouseEvent(event,handlerFlags) )
return;
#ifdef __WXMSW__
const bool ctrlIsButton = true;
#else
const bool ctrlIsButton = false;
#endif
if ( ctrlIsButton &&
(m_windowStyle & (wxPGCC_DCLICK_CYCLES|wxCB_READONLY)) == wxCB_READONLY )
{
// if no textctrl and no special double-click, then the entire control acts
// as a button
handlerFlags |= wxPGCC_MF_ON_BUTTON;
if ( HandleButtonMouseEvent(event,handlerFlags) )
return;
}
else
{
if ( isOnButtonArea || m_btnState & wxCONTROL_PRESSED )
{
if ( HandleButtonMouseEvent(event,handlerFlags) )
return;
}
else if ( m_btnState )
{
// otherwise need to clear the hover status
m_btnState = 0;
RefreshRect(m_btnArea);
}
}
//
// This will handle left_down and left_dclick events outside button in a Windows/GTK-like manner.
// See header file for further information on this method.
HandleNormalMouseEvent(event);
}
// ----------------------------------------------------------------------------
// wxComboControl
// ----------------------------------------------------------------------------
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
// Change to #if 1 to include tmschema.h for easier testing of theme
// parameters.
#if 0
#include <tmschema.h>
#include <VSStyle.h>
#else
//----------------------------------
#define EP_EDITTEXT 1
#define ETS_NORMAL 1
#define ETS_HOT 2
#define ETS_SELECTED 3
#define ETS_DISABLED 4
#define ETS_FOCUSED 5
#define ETS_READONLY 6
#define ETS_ASSIST 7
#define TMT_FILLCOLOR 3802
#define TMT_TEXTCOLOR 3803
#define TMT_BORDERCOLOR 3801
#define TMT_EDGEFILLCOLOR 3808
#define TMT_BGTYPE 4001
#define BT_IMAGEFILE 0
#define BT_BORDERFILL 1
#define CP_DROPDOWNBUTTON 1
#define CP_BACKGROUND 2 // This and above are Vista and later only
#define CP_TRANSPARENTBACKGROUND 3
#define CP_BORDER 4
#define CP_READONLY 5
#define CP_DROPDOWNBUTTONRIGHT 6
#define CP_DROPDOWNBUTTONLEFT 7
#define CP_CUEBANNER 8
#define CBXS_NORMAL 1
#define CBXS_HOT 2
#define CBXS_PRESSED 3
#define CBXS_DISABLED 4
#define CBXSR_NORMAL 1
#define CBXSR_HOT 2
#define CBXSR_PRESSED 3
#define CBXSR_DISABLED 4
#define CBXSL_NORMAL 1
#define CBXSL_HOT 2
#define CBXSL_PRESSED 3
#define CBXSL_DISABLED 4
#define CBTBS_NORMAL 1
#define CBTBS_HOT 2
#define CBTBS_DISABLED 3
#define CBTBS_FOCUSED 4
#define CBB_NORMAL 1
#define CBB_HOT 2
#define CBB_FOCUSED 3
#define CBB_DISABLED 4
#define CBRO_NORMAL 1
#define CBRO_HOT 2
#define CBRO_PRESSED 3
#define CBRO_DISABLED 4
#define CBCB_NORMAL 1
#define CBCB_HOT 2
#define CBCB_PRESSED 3
#define CBCB_DISABLED 4
#endif
#define NATIVE_TEXT_INDENT_XP 4
#define NATIVE_TEXT_INDENT_CLASSIC 2
#define TEXTCTRLXADJUST_XP 0
#define TEXTCTRLYADJUST_XP 4
#define TEXTCTRLXADJUST_CLASSIC 0
#define TEXTCTRLYADJUST_CLASSIC 4
BEGIN_EVENT_TABLE(wxPGComboControl, wxPGComboControlBase)
EVT_PAINT(wxPGComboControl::OnPaintEvent)
EVT_MOUSE_EVENTS(wxPGComboControl::OnMouseEvent)
END_EVENT_TABLE()
IMPLEMENT_DYNAMIC_CLASS(wxPGComboControl, wxPGComboControlBase)
void wxPGComboControl::Init()
{
}
bool wxPGComboControl::Create(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
{
// Set border
long border = style & wxBORDER_MASK;
wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
if ( !border )
{
// For XP, have 1-width custom border, for older version use sunken
if ( theme )
{
border = wxBORDER_NONE;
m_widthCustomBorder = 1;
}
else
border = wxBORDER_SUNKEN;
style = (style & ~(wxBORDER_MASK)) | border;
}
//Customize( wxPGCC_BUTTON_OUTSIDE_BORDER );
// create main window
if ( !wxPGComboControlBase::Create(parent,
id,
value,
wxDefaultPosition,
wxDefaultSize,
style | wxFULL_REPAINT_ON_RESIZE,
wxDefaultValidator,
name) )
return false;
if ( theme )
{
#if wxCHECK_VERSION(2, 8, 0)
const bool isVista = (::wxGetWinVersion() >= wxWinVersion_6);
#else
int Major = 0;
int family = wxGetOsVersion(&Major, NULL);
const bool isVista = ((family == wxWINDOWS_NT) && (Major >= 6));
#endif
if ( isVista )
m_iFlags |= wxPGCC_BUTTON_STAYS_DOWN;
}
// Create textctrl, if necessary
CreateTextCtrl( wxNO_BORDER, validator );
// Add keyboard input handlers for main control and textctrl
InstallInputHandlers();
// Prepare background for double-buffering
SetBackgroundStyle( wxBG_STYLE_CUSTOM );
// SetSize should be called last
SetSize(pos.x,pos.y,size.x,size.y);
return true;
}
wxPGComboControl::~wxPGComboControl()
{
}
void wxPGComboControl::OnThemeChange()
{
wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
if ( theme )
{
wxUxThemeHandle hTheme(this, L"COMBOBOX");
COLORREF col;
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&col);
SetBackgroundColour(wxRGBToColour(col));
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&col);
SetForegroundColour(wxRGBToColour(col));
}
else
{
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
}
//void wxPGComboControl::OnSizeEvent( wxSizeEvent& event )
void wxPGComboControl::OnResize()
{
//
// Recalculates button and textctrl areas
int textCtrlXAdjust;
int textCtrlYAdjust;
if ( wxUxThemeEngine::GetIfActive() )
{
textCtrlXAdjust = TEXTCTRLXADJUST_XP;
textCtrlYAdjust = TEXTCTRLYADJUST_XP;
}
else
{
textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
}
// Technically Classic Windows style combo has more narrow button,
// but the native renderer doesn't paint it well like that.
int btnWidth = 17;
CalculateAreas(btnWidth);
// Position textctrl using standard routine
PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
}
/*
// Draws non-XP GUI dotted line around the focus area
static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
{
#if !defined(__WXWINCE__)
dc.SetLogicalFunction(wxINVERT);
wxPen pen(*wxBLACK,1,wxDOT);
pen.SetCap(wxCAP_BUTT);
dc.SetPen(pen);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect);
dc.SetLogicalFunction(wxCOPY);
#else
dc.SetLogicalFunction(wxINVERT);
dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect);
dc.SetLogicalFunction(wxCOPY);
#endif
}
*/
/*
// draw focus background on area in a way typical on platform
void wxPGComboControl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags )
{
wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
wxUxThemeHandle hTheme(this, L"COMBOBOX");
//COLORREF cref;
wxSize sz = GetClientSize();
bool isEnabled;
bool isFocused; // also selected
// For smaller size control (and for disabled background) use less spacing
int focusSpacingX;
int focusSpacingY;
if ( !(flags & wxCONTROL_ISSUBMENU) )
{
// Drawing control
isEnabled = IsEnabled();
isFocused = ShouldDrawFocus();
// Windows-style: for smaller size control (and for disabled background) use less spacing
if ( hTheme )
{
// WinXP Theme
focusSpacingX = isEnabled ? 2 : 1;
focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
}
else
{
// Classic Theme
if ( isEnabled )
{
focusSpacingX = 1;
focusSpacingY = 1;
}
else
{
focusSpacingX = 0;
focusSpacingY = 0;
}
}
}
else
{
// Drawing a list item
isEnabled = true; // they are never disabled
isFocused = flags & wxCONTROL_SELECTED ? true : false;
focusSpacingX = 0;
focusSpacingY = 0;
}
// Set the background sub-rectangle for selection, disabled etc
wxRect selRect(rect);
selRect.y += focusSpacingY;
selRect.height -= (focusSpacingY*2);
int wcp = 0;
if ( !(flags & wxCONTROL_ISSUBMENU) )
wcp += m_widthCustomPaint;
selRect.x += wcp + focusSpacingX;
selRect.width -= wcp + (focusSpacingX*2);
if ( hTheme )
theme = wxUxThemeEngine::GetIfActive();
wxColour bgCol;
bool drawDottedEdge = false;
if ( isEnabled )
{
// If popup is hidden and this control is focused,
// then draw the focus-indicator (selbgcolor background etc.).
if ( isFocused )
{
#if 0
// TODO: Proper theme color getting (JMS: I don't know which parts/colors to use,
// those below don't work)
if ( hTheme )
{
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_TEXTCOLOR,&cref);
dc.SetTextForeground( wxRGBToColour(cref) );
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_FILLCOLOR,&cref);
bgCol = wxRGBToColour(cref);
}
else
#endif
{
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
if ( m_windowStyle & wxCB_READONLY )
drawDottedEdge = true;
}
}
else
{
bgCol = GetBackgroundColour();
}
}
else
{
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
}
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(selRect);
//if ( drawDottedEdge )
// wxMSWDrawFocusRect(dc,selRect);
}
*/
void wxPGComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
{
// TODO: Convert drawing in this function to Windows API Code
wxSize sz = GetClientSize();
#if !wxCHECK_VERSION(2, 7, 1)
// If size is larger, recalculate double buffer bitmap
if ( !gs_doubleBuffer ||
sz.x > gs_doubleBuffer->GetWidth() ||
sz.y > gs_doubleBuffer->GetHeight() )
{
delete gs_doubleBuffer;
gs_doubleBuffer = new wxBitmap(sz.x+25,sz.y);
}
wxBufferedPaintDC dc(this,*gs_doubleBuffer);
#else
wxAutoBufferedPaintDC dc(this);
#endif
const wxRect& rectButton = m_btnArea;
wxRect rectTextField = m_tcArea;
const bool isEnabled = IsEnabled();
wxColour bgCol = GetBackgroundColour();
HDC hDc = GetHdcOf(dc);
HWND hWnd = GetHwndOf(this);
wxUxThemeEngine* theme = NULL;
wxUxThemeHandle hTheme(this, L"COMBOBOX");
if ( hTheme )
theme = wxUxThemeEngine::GetIfActive();
wxRect borderRect(0,0,sz.x,sz.y);
if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
{
borderRect = m_tcArea;
borderRect.Inflate(1);
}
int drawButFlags = 0;
if ( hTheme )
{
#if wxCHECK_VERSION(2, 8, 0)
const bool useVistaComboBox = (::wxGetWinVersion() >= wxWinVersion_6);
#else
int Major = 0;
int family = wxGetOsVersion(&Major, NULL);
const bool useVistaComboBox = ((family == wxWINDOWS_NT) && (Major >= 6));
#endif
RECT rFull;
wxCopyRectToRECT(borderRect, rFull);
RECT rButton;
wxCopyRectToRECT(rectButton, rButton);
RECT rBorder;
wxCopyRectToRECT(borderRect, rBorder);
bool isNonStdButton = (m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE) ||
(m_iFlags & wxPGCC_IFLAG_HAS_NONSTANDARD_BUTTON);
//
// Get some states for themed drawing
int butState;
if ( !isEnabled )
{
butState = CBXS_DISABLED;
}
// Vista will display the drop-button as depressed always
// when the popup window is visilbe
else if ( (m_btnState & wxCONTROL_PRESSED) ||
(useVistaComboBox && IsPopupShown()) )
{
butState = CBXS_PRESSED;
}
else if ( m_btnState & wxCONTROL_CURRENT )
{
butState = CBXS_HOT;
}
else
{
butState = CBXS_NORMAL;
}
int comboBoxPart = 0; // For XP, use the 'default' part
RECT* rUseForBg = &rBorder;
bool drawFullButton = false;
int bgState = butState;
const bool isFocused = IsFocused();
if ( useVistaComboBox )
{
// FIXME: Either SetBackgroundColour or GetBackgroundColour
// doesn't work under Vista, so here's a temporary
// workaround.
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
// Draw the entire control as a single button?
/*
if ( !isNonStdButton )
{
if ( HasFlag(wxCB_READONLY) )
drawFullButton = true;
}
*/
if ( drawFullButton )
{
comboBoxPart = CP_READONLY;
rUseForBg = &rFull;
// It should be safe enough to update this flag here.
m_iFlags |= wxPGCC_FULL_BUTTON;
}
else
{
comboBoxPart = CP_BORDER;
m_iFlags &= ~wxPGCC_FULL_BUTTON;
if ( isFocused )
bgState = CBB_FOCUSED;
else
bgState = CBB_NORMAL;
}
}
//
// Draw parent's background, if necessary
RECT* rUseForTb = NULL;
if ( theme->IsThemeBackgroundPartiallyTransparent( hTheme, comboBoxPart, bgState ) )
rUseForTb = &rFull;
else if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
rUseForTb = &rButton;
if ( rUseForTb )
theme->DrawThemeParentBackground( hWnd, hDc, rUseForTb );
//
// Draw the control background (including the border)
if ( m_widthCustomBorder > 0 )
{
theme->DrawThemeBackground( hTheme, hDc, comboBoxPart, bgState, rUseForBg, NULL );
}
else
{
// No border. We can't use theme, since it cannot be relied on
// to deliver borderless drawing, even with DrawThemeBackgroundEx.
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(borderRect);
}
//
// Draw the drop-button
if ( !isNonStdButton )
{
drawButFlags = Button_BitmapOnly;
int butPart = CP_DROPDOWNBUTTON;
if ( useVistaComboBox && m_widthCustomBorder > 0 )
{
if ( drawFullButton )
{
// We need to alter the button style slightly before
// drawing the actual button (but it was good above
// when background etc was done).
if ( butState == CBXS_HOT || butState == CBXS_PRESSED )
butState = CBXS_NORMAL;
}
if ( m_btnSide == wxRIGHT )
butPart = CP_DROPDOWNBUTTONRIGHT;
else
butPart = CP_DROPDOWNBUTTONLEFT;
}
theme->DrawThemeBackground( hTheme, hDc, butPart, butState, &rButton, NULL );
}
else if ( useVistaComboBox &&
(m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE) )
{
// We'll do this, because DrawThemeParentBackground
// doesn't seem to be reliable on Vista.
drawButFlags |= Button_PaintBackground;
}
}
else
{
// Windows 2000 and earlier
drawButFlags = Button_PaintBackground;
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(borderRect);
}
// Button rendering (may only do the bitmap on button, depending on the flags)
DrawButton( dc, rectButton, drawButFlags );
// Paint required portion of the custom image on the control
if ( (!m_text || m_widthCustomPaint) )
{
wxASSERT( m_widthCustomPaint >= 0 );
// this is intentionally here to allow drawed rectangle's
// right edge to be hidden
if ( m_text )
rectTextField.width = m_widthCustomPaint;
dc.SetFont( GetFont() );
dc.SetClippingRegion(rectTextField);
m_popupInterface->PaintComboControl(dc,rectTextField);
}
}
void wxPGComboControl::OnMouseEvent( wxMouseEvent& event )
{
bool isOnButtonArea = m_btnArea.wxPGRectContains(event.m_x,event.m_y);
int handlerFlags = isOnButtonArea ? wxPGCC_MF_ON_BUTTON : 0;
// Preprocessing fabricates double-clicks and prevents
// (it may also do other common things in future)
if ( PreprocessMouseEvent(event,isOnButtonArea) )
return;
if ( (m_windowStyle & (wxPGCC_DCLICK_CYCLES|wxCB_READONLY)) == wxCB_READONLY )
{
// if no textctrl and no special double-click, then the entire control acts
// as a button
handlerFlags |= wxPGCC_MF_ON_BUTTON;
if ( HandleButtonMouseEvent(event,handlerFlags) )
return;
}
else
{
if ( isOnButtonArea || m_btnState & wxCONTROL_PRESSED )
{
if ( HandleButtonMouseEvent(event,handlerFlags) )
return;
}
else if ( m_btnState )
{
// otherwise need to clear the hover status
m_btnState = 0;
RefreshRect(m_btnArea);
}
}
//
// This will handle left_down and left_dclick events outside button in a Windows-like manner.
// See header file for further information on this method.
HandleNormalMouseEvent(event);
}
wxCoord wxPGComboControl::GetNativeTextIndent() const
{
if ( wxUxThemeEngine::GetIfActive() )
return NATIVE_TEXT_INDENT_XP;
return NATIVE_TEXT_INDENT_CLASSIC;
}
#else
IMPLEMENT_DYNAMIC_CLASS(wxPGComboControl, wxPGComboControlBase)
#endif // #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
// ----------------------------------------------------------------------------
// wxPGOwnerDrawnComboBox
// ----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxPGOwnerDrawnComboBox, wxPGComboControl)
BEGIN_EVENT_TABLE(wxPGOwnerDrawnComboBox, wxPGComboControl)
END_EVENT_TABLE()
void wxPGOwnerDrawnComboBox::Init()
{
}
bool wxPGOwnerDrawnComboBox::Create(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
{
return wxPGComboControl::Create(parent,id,value,pos,size,style,validator,name);
}
wxPGOwnerDrawnComboBox::wxPGOwnerDrawnComboBox(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
const wxArrayString& choices,
long style,
const wxValidator& validator,
const wxString& name)
: wxPGComboControl()
{
Init();
Create(parent,id,value,pos,size,choices,style, validator, name);
}
bool wxPGOwnerDrawnComboBox::Create(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
const wxArrayString& choices,
long style,
const wxValidator& validator,
const wxString& name)
{
wxCArrayString chs(choices);
return Create(parent, id, value, pos, size, chs.GetCount(),
chs.GetStrings(), /*callback,*/ style, validator, name);
}
bool wxPGOwnerDrawnComboBox::Create(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
int n,
const wxString choices[],
long style,
const wxValidator& validator,
const wxString& name)
{
if ( !Create(parent, id, value, pos, size, style,
validator, name) )
{
return false;
}
wxPGVListBoxComboPopup* iface = new wxPGVListBoxComboPopup(this);
SetPopup(iface);
m_popupInterface = iface;
// Add initial choices to the interface
iface->Populate(n,choices);
return true;
}
wxPGOwnerDrawnComboBox::~wxPGOwnerDrawnComboBox()
{
if ( m_popupInterface )
m_popupInterface->ClearClientDatas();
}
// ----------------------------------------------------------------------------
// wxPGOwnerDrawnComboBox item manipulation methods
// ----------------------------------------------------------------------------
void wxPGOwnerDrawnComboBox::Clear()
{
wxASSERT( m_popupInterface );
m_popupInterface->Clear();
GetTextCtrl()->SetValue(wxEmptyString);
}
void wxPGOwnerDrawnComboBox::Delete(wxODCIndex n)
{
wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid index in wxPGOwnerDrawnComboBox::Delete") );
if ( GetSelection() == (int) n )
SetValue(wxEmptyString);
m_popupInterface->Delete(n);
}
wxODCCount wxPGOwnerDrawnComboBox::GetCount() const
{
wxASSERT( m_popupInterface );
return m_popupInterface->GetCount();
}
wxString wxPGOwnerDrawnComboBox::GetString(wxODCIndex n) const
{
wxCHECK_MSG( (n >= 0) && (n < GetCount()), wxEmptyString, _T("invalid index in wxPGOwnerDrawnComboBox::GetString") );
return m_popupInterface->GetString(n);
}
void wxPGOwnerDrawnComboBox::SetString(wxODCIndex n, const wxString& s)
{
wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid index in wxPGOwnerDrawnComboBox::SetString") );
m_popupInterface->SetString(n,s);
}
int wxPGOwnerDrawnComboBox::FindString(const wxString& s) const
{
wxASSERT( m_popupInterface );
return m_popupInterface->FindString(s);
}
void wxPGOwnerDrawnComboBox::Select(int n)
{
wxCHECK_RET( (n >= -1) && (n < (int)GetCount()), _T("invalid index in wxPGOwnerDrawnComboBox::Select") );
wxASSERT( m_popupInterface );
m_popupInterface->SetSelection(n);
wxString str;
if ( n >= 0 )
str = m_popupInterface->GetString(n);
// Refresh text portion in control
if ( m_text )
m_text->SetValue( str );
else
m_valueString = str;
Refresh();
}
int wxPGOwnerDrawnComboBox::GetSelection() const
{
wxASSERT( m_popupInterface );
return m_popupInterface->GetSelection();
}
int wxPGOwnerDrawnComboBox::DoAppend(const wxString& item)
{
wxASSERT( m_popupInterface );
return m_popupInterface->Append(item);
}
int wxPGOwnerDrawnComboBox::DoInsert(const wxString& item, wxODCIndex pos)
{
wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into sorted list"));
wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
m_popupInterface->Insert(item,pos);
return pos;
}
#if wxCHECK_VERSION(2,9,0)
int wxPGOwnerDrawnComboBox::DoInsertItems(const wxArrayStringsAdapter& items,
unsigned int pos,
void **clientData,
wxClientDataType type)
{
unsigned int i;
for ( i=0; i<items.GetCount(); i++ )
{
DoInsert(items[i], pos);
if ( clientData )
{
if ( type == wxClientData_Object )
DoSetItemClientObject(pos, (wxClientData*)clientData[i]);
else
DoSetItemClientData(pos, clientData[i]);
}
pos++;
}
return pos - 1;
}
#endif
void wxPGOwnerDrawnComboBox::DoSetItemClientData(wxODCIndex n, void* clientData)
{
wxASSERT(m_popupInterface);
m_popupInterface->SetItemClientData(n,clientData,
#if wxCHECK_VERSION(2,9,0)
GetClientDataType()
#else
m_clientDataItemsType
#endif
);
}
void* wxPGOwnerDrawnComboBox::DoGetItemClientData(wxODCIndex n) const
{
wxASSERT(m_popupInterface);
return m_popupInterface->GetItemClientData(n);
}
void wxPGOwnerDrawnComboBox::DoSetItemClientObject(wxODCIndex n, wxClientData* clientData)
{
DoSetItemClientData(n, (void*) clientData);
}
wxClientData* wxPGOwnerDrawnComboBox::DoGetItemClientObject(wxODCIndex n) const
{
return (wxClientData*) DoGetItemClientData(n);
}
#endif // wxUSE_COMBOBOX