// Author: Jaakko Salli
// Modified by:
// Created: Apr-30-2006
-// RCS-ID: $Id$
// Copyright: (c) 2005 Jaakko Salli
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#pragma hdrstop
#endif
-#if wxUSE_COMBOCTRL
+#include "wx/combo.h"
+
+#ifdef __WXMSW__
+#include "wx/msw/private.h"
+#endif
+#if wxUSE_COMBOBOX
#include "wx/combobox.h"
+extern WXDLLEXPORT_DATA(const char) wxComboBoxNameStr[] = "comboBox";
+
+// ----------------------------------------------------------------------------
+// XTI
+// ----------------------------------------------------------------------------
+
+wxDEFINE_FLAGS( wxComboBoxStyle )
+wxBEGIN_FLAGS( wxComboBoxStyle )
+// new style border flags, we put them first to
+// use them for streaming out
+wxFLAGS_MEMBER(wxBORDER_SIMPLE)
+wxFLAGS_MEMBER(wxBORDER_SUNKEN)
+wxFLAGS_MEMBER(wxBORDER_DOUBLE)
+wxFLAGS_MEMBER(wxBORDER_RAISED)
+wxFLAGS_MEMBER(wxBORDER_STATIC)
+wxFLAGS_MEMBER(wxBORDER_NONE)
+
+// old style border flags
+wxFLAGS_MEMBER(wxSIMPLE_BORDER)
+wxFLAGS_MEMBER(wxSUNKEN_BORDER)
+wxFLAGS_MEMBER(wxDOUBLE_BORDER)
+wxFLAGS_MEMBER(wxRAISED_BORDER)
+wxFLAGS_MEMBER(wxSTATIC_BORDER)
+wxFLAGS_MEMBER(wxBORDER)
+
+// standard window styles
+wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
+wxFLAGS_MEMBER(wxCLIP_CHILDREN)
+wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
+wxFLAGS_MEMBER(wxWANTS_CHARS)
+wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
+wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
+wxFLAGS_MEMBER(wxVSCROLL)
+wxFLAGS_MEMBER(wxHSCROLL)
+
+wxFLAGS_MEMBER(wxCB_SIMPLE)
+wxFLAGS_MEMBER(wxCB_SORT)
+wxFLAGS_MEMBER(wxCB_READONLY)
+wxFLAGS_MEMBER(wxCB_DROPDOWN)
+
+wxEND_FLAGS( wxComboBoxStyle )
+
+wxIMPLEMENT_DYNAMIC_CLASS_XTI(wxComboBox, wxControl, "wx/combobox.h")
+
+wxBEGIN_PROPERTIES_TABLE(wxComboBox)
+wxEVENT_PROPERTY( Select, wxEVT_COMBOBOX, wxCommandEvent )
+wxEVENT_PROPERTY( TextEnter, wxEVT_TEXT_ENTER, wxCommandEvent )
+
+// TODO DELEGATES
+wxPROPERTY( Font, wxFont, SetFont, GetFont, wxEMPTY_PARAMETER_VALUE, \
+ 0 /*flags*/, wxT("Helpstring"), wxT("group"))
+wxPROPERTY_COLLECTION( Choices, wxArrayString, wxString, AppendString, \
+ GetStrings, 0 /*flags*/, wxT("Helpstring"), wxT("group"))
+wxPROPERTY( Value,wxString, SetValue, GetValue, wxEMPTY_PARAMETER_VALUE, \
+ 0 /*flags*/, wxT("Helpstring"), wxT("group"))
+wxPROPERTY( Selection,int, SetSelection, GetSelection, wxEMPTY_PARAMETER_VALUE, \
+ 0 /*flags*/, wxT("Helpstring"), wxT("group"))
+
+wxPROPERTY_FLAGS( WindowStyle, wxComboBoxStyle, long, SetWindowStyleFlag, \
+ GetWindowStyleFlag, wxEMPTY_PARAMETER_VALUE, 0 /*flags*/, \
+ wxT("Helpstring"), wxT("group")) // style
+wxEND_PROPERTIES_TABLE()
+
+wxEMPTY_HANDLERS_TABLE(wxComboBox)
+
+wxCONSTRUCTOR_5( wxComboBox, wxWindow*, Parent, wxWindowID, Id, \
+ wxString, Value, wxPoint, Position, wxSize, Size )
+
+#endif // wxUSE_COMBOBOX
+
+#if wxUSE_COMBOCTRL
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/tooltip.h"
-#include "wx/combo.h"
-
-
-
// constants
// ----------------------------------------------------------------------------
#define wxCC_GENERIC_TLW_IS_DIALOG
#define wxComboCtrlGenericTLW wxDialog
-#include "wx/gtk/private.h"
+#if defined(__WXGTK20__)
+# include "wx/gtk/private.h"
+#else
+# include "wx/gtk1/private.h"
+#endif
// NB: Let's not be afraid to use wxGTK's wxPopupTransientWindow as a
-// 'perfect' popup, as it can succesfully host child controls even in
+// 'perfect' popup, as it can successfully host child controls even in
// popups that are shown in modal dialogs.
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
#endif
+// Returns true if given popup window type can be classified as perfect
+// on this platform.
+static inline bool IsPopupWinTypePerfect( wxByte popupWinType )
+{
+#if POPUPWIN_IS_PERFECT && TRANSIENT_POPUPWIN_IS_PERFECT
+ wxUnusedVar(popupWinType);
+ return true;
+#else
+ return ( popupWinType == POPUPWIN_GENERICTLW
+ #if POPUPWIN_IS_PERFECT
+ || popupWinType == POPUPWIN_WXPOPUPWINDOW
+ #endif
+ #if TRANSIENT_POPUPWIN_IS_PERFECT
+ || popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW
+ #endif
+ );
+#endif
+}
+
+
//
// ** TODO **
// * wxComboPopupWindow for external use (ie. replace old wxUniv wxPopupComboWindow)
wxWindow* popup = m_combo->GetPopupControl()->GetControl();
wxWindow* winpopup = m_combo->GetPopupWindow();
- if (
+ if ( !winFocused || (
winFocused != m_focusStart &&
winFocused != popup &&
winFocused->GetParent() != popup &&
winFocused != m_combo &&
winFocused != m_combo->GetButton() // GTK (atleast) requires this
)
+ )
{
m_combo->HidePopup(true);
}
m_inShow++;
- wxASSERT( IsKindOf(CLASSINFO(wxPopupTransientWindow)) );
-
- wxPopupTransientWindow* ptw = (wxPopupTransientWindow*) this;
+ wxPopupTransientWindow* const
+ ptw = static_cast<wxPopupTransientWindow*>(this);
if ( show != ptw->IsShown() )
{
void wxComboPopupWindow::OnDismiss()
{
wxComboCtrlBase* combo = (wxComboCtrlBase*) GetParent();
- wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxComboCtrlBase)),
+ wxASSERT_MSG( wxDynamicCast(combo, wxComboCtrlBase),
wxT("parent might not be wxComboCtrl, but check IMPLEMENT_DYNAMIC_CLASS(2) macro for correctness") );
combo->OnPopupDismiss(true);
{
}
+bool wxComboPopup::FindItem(const wxString& WXUNUSED(item),
+ wxString* WXUNUSED(trueItem))
+{
+ return true;
+}
+
bool wxComboPopup::LazyCreate()
{
return false;
m_combo->HidePopup(true);
}
+void wxComboPopup::DestroyPopup()
+{
+ // Here we make sure that the popup control's Destroy() gets called.
+ // This is necessary for the wxPersistentWindow to work properly.
+ wxWindow* popupCtrl = GetControl();
+ if ( popupCtrl )
+ {
+ // While all wxComboCtrl examples have m_popupInterface and
+ // popupCtrl as the same class (that will be deleted via the
+ // Destroy() call below), it is technically still possible to
+ // have implementations where they are in fact not same
+ // multiple-inherited class. Here we use C++ RTTI to check for
+ // this rare case.
+ #ifndef wxNO_RTTI
+ // It is probably better to delete m_popupInterface first, so
+ // that it retains access to its popup control window.
+ if ( dynamic_cast<void*>(this) !=
+ dynamic_cast<void*>(popupCtrl) )
+ delete this;
+ #endif
+ popupCtrl->Destroy();
+ }
+ else
+ {
+ delete this;
+ }
+}
+
// ----------------------------------------------------------------------------
// input handling
// ----------------------------------------------------------------------------
if ( m_combo->GetTextCtrl() )
m_combo->GetTextCtrl()->SelectAll();
else
- m_combo->SetSelection(-1,-1);
+ m_combo->SelectAll();
}
// Send focus indication to parent.
// wxEVT_SET_FOCUSes (since m_text->SetFocus is called
// from combo's focus event handler), they should be quite
// harmless.
- wxFocusEvent evt2(event.GetEventType(),m_combo->GetId());
+ wxFocusEvent evt2(event);
+ evt2.SetId(m_combo->GetId());
evt2.SetEventObject(m_combo);
m_combo->GetEventHandler()->ProcessEvent(evt2);
// This is pushed to the event handler queue of the control in popup.
//
-class wxComboPopupExtraEventHandler : public wxEvtHandler
+class wxComboPopupEvtHandler : public wxEvtHandler
{
public:
- wxComboPopupExtraEventHandler( wxComboCtrlBase* combo )
+ wxComboPopupEvtHandler( wxComboCtrlBase* combo )
: wxEvtHandler()
{
m_combo = combo;
// events until mouse left button has been up.
m_blockEventsToPopup = true;
}
- virtual ~wxComboPopupExtraEventHandler() { }
+ virtual ~wxComboPopupEvtHandler() { }
void OnMouseEvent( wxMouseEvent& event );
};
-BEGIN_EVENT_TABLE(wxComboPopupExtraEventHandler, wxEvtHandler)
- EVT_MOUSE_EVENTS(wxComboPopupExtraEventHandler::OnMouseEvent)
+BEGIN_EVENT_TABLE(wxComboPopupEvtHandler, wxEvtHandler)
+ EVT_MOUSE_EVENTS(wxComboPopupEvtHandler::OnMouseEvent)
END_EVENT_TABLE()
-void wxComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event )
+void wxComboPopupEvtHandler::OnMouseEvent( wxMouseEvent& event )
{
wxPoint pt = event.GetPosition();
wxSize sz = m_combo->GetPopupControl()->GetControl()->GetClientSize();
// block this one)
m_blockEventsToPopup = false;
event.Skip(false);
+
+ // Also, this button press was (probably) used to display
+ // the popup, so relay it back to the drop-down button
+ // (which supposedly originated it). This is necessary to
+ // refresh it properly.
+ relayToButton = true;
}
}
else if ( m_blockEventsToPopup )
if ( relayToButton )
{
- wxWindow* eventSink = m_combo;
wxWindow* btn = m_combo->GetButton();
if ( btn )
- eventSink = btn;
-
- eventSink->GetEventHandler()->ProcessEvent(event);
+ btn->GetEventHandler()->ProcessEvent(event);
+ else
+ // Bypass the event handling mechanism. Using it would be
+ // confusing for the platform-specific wxComboCtrl
+ // implementations.
+ m_combo->HandleButtonMouseEvent(event, 0);
}
}
BEGIN_EVENT_TABLE(wxComboCtrlBase, wxControl)
- EVT_TEXT(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
EVT_SIZE(wxComboCtrlBase::OnSizeEvent)
EVT_SET_FOCUS(wxComboCtrlBase::OnFocusEvent)
EVT_KILL_FOCUS(wxComboCtrlBase::OnFocusEvent)
//EVT_BUTTON(wxID_ANY,wxComboCtrlBase::OnButtonClickEvent)
EVT_KEY_DOWN(wxComboCtrlBase::OnKeyEvent)
EVT_CHAR(wxComboCtrlBase::OnCharEvent)
- EVT_TEXT_ENTER(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged)
END_EVENT_TABLE()
m_text = NULL;
m_popupInterface = NULL;
- m_popupExtraHandler = NULL;
+ m_popupEvtHandler = NULL;
m_textEvtHandler = NULL;
#if INSTALL_TOPLEV_HANDLER
m_extRight = 0;
m_marginLeft = -1;
m_iFlags = 0;
+ m_textCtrlStyle = 0;
m_timeCanAcceptClick = 0;
m_resetFocus = false;
+ m_hasTcBgCol = false;
}
bool wxComboCtrlBase::Create(wxWindow *parent,
m_iFlags |= wxCC_IFLAG_CREATED;
// If x and y indicate valid size, wxSizeEvent won't be
- // emitted automatically, so we need to add artifical one.
+ // emitted automatically, so we need to add artificial one.
if ( size.x > 0 && size.y > 0 )
{
wxSizeEvent evt(size,GetId());
+ evt.SetEventObject(this);
GetEventHandler()->AddPendingEvent(evt);
}
}
void
-wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator)
+wxComboCtrlBase::CreateTextCtrl(int style)
{
if ( !(m_windowStyle & wxCB_READONLY) )
{
// 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.
- style |= wxTE_PROCESS_TAB;
+ style |= wxTE_PROCESS_TAB | m_textCtrlStyle;
if ( HasFlag(wxTE_PROCESS_ENTER) )
style |= wxTE_PROCESS_ENTER;
m_text = new wxComboCtrlTextCtrl();
m_text->Create(this, wxID_ANY, m_valueString,
wxDefaultPosition, wxSize(10,-1),
- style, validator);
+ style);
+
+ // Connecting the events is currently the most reliable way
+ wxWindowID id = m_text->GetId();
+ m_text->Connect(id, wxEVT_TEXT,
+ wxCommandEventHandler(wxComboCtrlBase::OnTextCtrlEvent),
+ NULL, this);
+ m_text->Connect(id, wxEVT_TEXT_ENTER,
+ wxCommandEventHandler(wxComboCtrlBase::OnTextCtrlEvent),
+ NULL, this);
+
m_text->SetHint(m_hintText);
}
}
void wxComboCtrlBase::OnThemeChange()
{
- // Leave the default bg on the Mac so the area used by the focus ring will
- // be the correct colour and themed brush. Instead we'll use
- // wxSYS_COLOUR_WINDOW in the EVT_PAINT handler as needed.
-#ifndef __WXMAC__
- #if defined(__WXMSW__) || defined(__WXGTK__)
+ // Because wxComboCtrl has transparent parts on most platforms, we
+ // don't want to touch the actual background colour. Instead, we just
+ // usually re-obtain m_tcBgCol here.
+
+#if defined(__WXMSW__) || defined(__WXGTK__)
wxVisualAttributes vattrs = wxComboBox::GetClassDefaultAttributes();
- #else
+#else
wxVisualAttributes vattrs;
vattrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
vattrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
- #endif
+#endif
+
+ if ( !m_hasTcBgCol )
+ m_tcBgCol = vattrs.colBg;
+#ifndef __WXMAC__
// Only change the colours if application has not specified
// custom ones.
if ( !m_hasFgCol )
{
SetOwnForegroundColour(vattrs.colFg);
- m_hasFgCol = false;
}
- if ( !m_hasBgCol )
+ if ( !HasTransparentBackground() )
{
- SetOwnBackgroundColour(vattrs.colBg);
- m_hasBgCol = false;
+ SetOwnBackgroundColour(GetParent()->GetBackgroundColour());
}
#endif // !__WXMAC__
}
// its platform default or bitmap+pushbutton background is used, but not if
// there is vertical size adjustment or horizontal spacing.
if ( ( (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) ||
- (m_bmpNormal.Ok() && m_blankButtonBg) ) &&
+ (m_bmpNormal.IsOk() && m_blankButtonBg) ) &&
m_btnSpacingX == 0 &&
m_btnHei <= 0 )
{
btnBorder = 0;
}
else if ( (m_iFlags & wxCC_BUTTON_COVERS_BORDER) &&
- m_btnSpacingX == 0 && !m_bmpNormal.Ok() )
+ m_btnSpacingX == 0 && !m_bmpNormal.IsOk() )
{
m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE);
btnBorder = 0;
// It is larger
// OR
// button width is set to default and blank button bg is not drawn
- if ( m_bmpNormal.Ok() )
+ if ( m_bmpNormal.IsOk() )
{
int bmpReqWidth = m_bmpNormal.GetWidth();
int bmpReqHeight = m_bmpNormal.GetHeight();
{
int newY = butHeight+(customBorder*2);
SetClientSize(wxDefaultCoord,newY);
- if ( m_bmpNormal.Ok() || m_btnArea.width != butWidth || m_btnArea.height != butHeight )
+ if ( m_bmpNormal.IsOk() || m_btnArea.width != butWidth || m_btnArea.height != butHeight )
m_iFlags |= wxCC_IFLAG_HAS_NONSTANDARD_BUTTON;
else
m_iFlags &= ~wxCC_IFLAG_HAS_NONSTANDARD_BUTTON;
// There is special custom paint area - it is better to
// use some margin with the wxTextCtrl.
m_text->SetMargins(m_marginLeft);
- x = m_tcArea.x + m_widthCustomPaint +
+ x = m_tcArea.x + m_widthCustomPaint +
m_marginLeft + textCtrlXAdjust;
}
wxSize wxComboCtrlBase::DoGetBestSize() const
{
- wxSize sizeText(150,0);
+ int width = m_text ? m_text->GetBestSize().x : 80;
- if ( m_text )
- sizeText = m_text->GetBestSize();
+ return GetSizeFromTextSize(width);
+}
- // TODO: Better method to calculate close-to-native control height.
+wxSize wxComboCtrlBase::DoGetSizeFromTextSize(int xlen, int ylen) const
+{
+ // Calculate close-to-native control height
int fhei;
- if ( m_font.Ok() )
+
+#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
+ fhei = EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight());
+#elif defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
+ // Control creation is not entirely cheap, so cache the heights to
+ // avoid repeatedly creating dummy controls:
+ static wxString s_last_font;
+ static int s_last_fhei = -1;
+ wxString fontdesc;
+ if ( m_font.IsOk() )
+ fontdesc = m_font.GetNativeFontInfoDesc();
+ if ( s_last_fhei != -1 && fontdesc == s_last_font )
+ {
+ fhei = s_last_fhei;
+ }
+ else
+ {
+ wxComboBox* cb = new wxComboBox;
+ cb->Hide();
+ cb->Create(const_cast<wxComboCtrlBase*>(this), wxID_ANY);
+ if ( m_font.IsOk() )
+ cb->SetFont(m_font);
+ s_last_font = fontdesc;
+ s_last_fhei = fhei = cb->GetBestSize().y;
+ cb->Destroy();
+ }
+#else
+ if ( m_font.IsOk() )
fhei = (m_font.GetPointSize()*2) + 5;
- else if ( wxNORMAL_FONT->Ok() )
+ else if ( wxNORMAL_FONT->IsOk() )
fhei = (wxNORMAL_FONT->GetPointSize()*2) + 5;
else
- fhei = sizeText.y + 4;
+ fhei = 22;
+#endif // only for wxComboBox on MSW or GTK
- // Need to force height to accomodate bitmap?
+ // Need to force height to accommodate bitmap?
int btnSizeY = m_btnSize.y;
- if ( m_bmpNormal.Ok() && fhei < btnSizeY )
+ if ( m_bmpNormal.IsOk() && fhei < btnSizeY )
fhei = btnSizeY;
// Control height doesn't depend on border
fhei += 4;
*/
- // Final adjustments
-#ifdef __WXGTK__
- fhei += 1;
-#endif
-
#ifdef __WXMAC__
// these are the numbers from the HIG:
switch ( m_windowVariant )
#endif
fhei += 2 * FOCUS_RING;
- int width = sizeText.x + FOCUS_RING + COMBO_MARGIN + DEFAULT_DROPBUTTON_WIDTH;
- wxSize ret(width, fhei);
- CacheBestSize(ret);
- return ret;
+ // Calculate width
+ int fwid = xlen + FOCUS_RING + COMBO_MARGIN + DEFAULT_DROPBUTTON_WIDTH;
+
+ // Add the margins we have previously set
+ wxPoint marg( GetMargins() );
+ fwid += wxMax(0, marg.x);
+ fhei += wxMax(0, marg.y);
+
+ if ( ylen > 0 )
+ fhei += ylen - GetCharHeight();
+
+ return wxSize(fwid, fhei);
}
void wxComboCtrlBase::OnSizeEvent( wxSizeEvent& event )
}
#endif // wxUSE_TOOLTIPS
-#if wxUSE_VALIDATORS
-void wxComboCtrlBase::SetValidator(const wxValidator& validator)
-{
- wxTextCtrl* textCtrl = GetTextCtrl();
-
- if ( textCtrl )
- textCtrl->SetValidator( validator );
- else
- wxControl::SetValidator( validator );
-}
-
-wxValidator* wxComboCtrlBase::GetValidator()
-{
- wxTextCtrl* textCtrl = GetTextCtrl();
-
- return textCtrl ? textCtrl->GetValidator() : wxControl::GetValidator();
-}
-#endif // wxUSE_VALIDATORS
-
bool wxComboCtrlBase::SetForegroundColour(const wxColour& colour)
{
if ( wxControl::SetForegroundColour(colour) )
bool wxComboCtrlBase::SetBackgroundColour(const wxColour& colour)
{
- if ( wxControl::SetBackgroundColour(colour) )
- {
- if ( m_text )
- m_text->SetBackgroundColour(colour);
- return true;
- }
- return false;
+ if ( m_text )
+ m_text->SetBackgroundColour(colour);
+ m_tcBgCol = colour;
+ m_hasTcBgCol = true;
+ return true;
}
+
+wxColour wxComboCtrlBase::GetBackgroundColour() const
+{
+ if ( m_text )
+ return m_text->GetBackgroundColour();
+ return m_tcBgCol;
+}
+
// ----------------------------------------------------------------------------
// painting
// ----------------------------------------------------------------------------
{
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
}
- else if ( m_hasBgCol )
+ else if ( m_hasTcBgCol )
{
// Honour the custom background colour
- bgCol = GetBackgroundColour();
+ bgCol = m_tcBgCol;
}
else
{
if ( !enabled )
drawState |= wxCONTROL_DISABLED;
- if ( !m_bmpNormal.Ok() )
+ // Need to clear button background even if m_btn is present
+ // and also when using custom bitmap for the button
+ if ( (flags & Button_PaintBackground) &&
+ (!HasTransparentBackground() ||
+ !(m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE)) )
{
- if ( flags & Button_BitmapOnly )
- return;
+ wxColour bgCol;
- // Need to clear button background even if m_btn is present
- if ( flags & Button_PaintBackground )
- {
- wxColour bgCol;
+ if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
+ bgCol = GetParent()->GetBackgroundColour();
+ else
+ bgCol = GetBackgroundColour();
- if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
- bgCol = GetParent()->GetBackgroundColour();
- else
- bgCol = GetBackgroundColour();
+ dc.SetBrush(bgCol);
+ dc.SetPen(bgCol);
+ dc.DrawRectangle(rect);
+ }
- dc.SetBrush(bgCol);
- dc.SetPen(bgCol);
- dc.DrawRectangle(rect);
- }
+ if ( !m_bmpNormal.IsOk() )
+ {
+ if ( flags & Button_BitmapOnly )
+ return;
// Draw standard button
wxRendererNative::Get().DrawComboBoxDropButton(this,
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,
drawState);
}
}
- else
-
- {
- // 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,
if ( IsCreated() )
{
wxSizeEvent evt(GetSize(),GetId());
+ evt.SetEventObject(this);
GetEventHandler()->ProcessEvent(evt);
Refresh();
}
void wxComboCtrlBase::OnTextCtrlEvent(wxCommandEvent& event)
{
- if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED )
+ // Avoid infinite recursion
+ if ( event.GetEventObject() == this )
+ {
+ event.Skip();
+ return;
+ }
+
+ if ( event.GetEventType() == wxEVT_TEXT )
{
if ( m_ignoreEvtText > 0 )
{
}
}
- // Change event id, object and string before relaying it forward
- event.SetId(GetId());
- wxString s = event.GetString();
- event.SetEventObject(this);
- event.SetString(s);
- event.Skip();
+ // For safety, completely re-create a new wxCommandEvent
+ wxCommandEvent evt2(event);
+ evt2.SetId(GetId());
+ evt2.SetEventObject(this);
+ HandleWindowEvent(evt2);
+
+ event.StopPropagation();
}
// call if cursor is on button area or mouse is captured for the button
void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& event )
{
+ // On Mac, setting focus here leads to infinite recursion and eventually
+ // a crash due to the SetFocus call producing another event.
+ // Handle Mac in OnIdleEvent using m_resetFocus.
+
if ( event.GetEventType() == wxEVT_SET_FOCUS )
{
- wxWindow* tc = GetTextCtrl();
- if ( tc && tc != DoFindFocus() )
+ if ( GetTextCtrl() && !GetTextCtrl()->HasFocus() )
{
- tc->SetFocus();
+#ifdef __WXMAC__
+ m_resetFocus = true;
+#else
+ GetTextCtrl()->SetFocus();
+#endif
}
}
-
+
Refresh();
}
if ( m_resetFocus )
{
m_resetFocus = false;
- wxWindow* tc = GetTextCtrl();
- if ( tc )
- tc->SetFocus();
+ if ( GetTextCtrl() )
+ GetTextCtrl()->SetFocus();
}
}
popupInterface->Create(m_winPopup);
m_popup = popup = popupInterface->GetControl();
- m_popupExtraHandler = new wxComboPopupExtraEventHandler(this);
- popup->PushEventHandler( m_popupExtraHandler );
+ m_popupEvtHandler = new wxComboPopupEvtHandler(this);
+ popup->PushEventHandler( m_popupEvtHandler );
// This may be helpful on some platforms
// (eg. it bypasses a wxGTK popupwindow bug where
HidePopup(true);
if ( m_popup )
- m_popup->RemoveEventHandler(m_popupExtraHandler);
+ m_popup->RemoveEventHandler(m_popupEvtHandler);
- delete m_popupExtraHandler;
+ wxDELETE(m_popupEvtHandler);
- delete m_popupInterface;
+ if ( m_popupInterface )
+ {
+ // NB: DestroyPopup() performs 'delete this'.
+ m_popupInterface->DestroyPopup();
+ m_popupInterface = NULL;
+ }
if ( m_winPopup )
{
m_winPopup->RemoveEventHandler(m_popupWinEvtHandler);
- delete m_popupWinEvtHandler;
- m_popupWinEvtHandler = NULL;
+ wxDELETE(m_popupWinEvtHandler);
m_winPopup->Destroy();
+ m_winPopup = NULL;
}
- m_popupExtraHandler = NULL;
- m_popupInterface = NULL;
- m_winPopup = NULL;
m_popup = NULL;
}
}
// This must be done after creation
- if ( m_valueString.length() )
+ if ( !m_valueString.empty() )
{
iface->SetStringValue(m_valueString);
//Refresh();
{
// Derived classes can override this method for totally custom
// popup action
- if ( !IsPopupWindowState(Visible) )
+ switch ( GetPopupWindowState() )
{
- wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_DROPDOWN, GetId());
- event.SetEventObject(this);
- HandleWindowEvent(event);
+ case Hidden:
+ {
+ Popup();
+ break;
+ }
- ShowPopup();
- }
- else
- {
- HidePopup(true);
+ case Animating:
+ case Visible:
+ {
+ HidePopup(true);
+ break;
+ }
}
}
+void wxComboCtrlBase::Popup()
+{
+ wxCommandEvent event(wxEVT_COMBOBOX_DROPDOWN, GetId());
+ event.SetEventObject(this);
+ HandleWindowEvent(event);
+
+ ShowPopup();
+}
+
void wxComboCtrlBase::ShowPopup()
{
EnsurePopupControl();
winPopup->Show();
m_popupWinState = Visible;
+
+ // If popup window was a generic top-level window, or the
+ // wxPopupWindow implemenation on this platform is classified as
+ // perfect, then we should be able to safely set focus to the popup
+ // control.
+ if ( IsPopupWinTypePerfect(m_popupWinType) )
+ m_popup->SetFocus();
}
else if ( IsPopupWindowState(Hidden) )
{
// Inform popup control itself
m_popupInterface->OnDismiss();
- if ( m_popupExtraHandler )
- ((wxComboPopupExtraEventHandler*)m_popupExtraHandler)->OnPopupDismiss();
+ if ( m_popupEvtHandler )
+ ((wxComboPopupEvtHandler*)m_popupEvtHandler)->OnPopupDismiss();
#if INSTALL_TOPLEV_HANDLER
// Remove top level window event handler
if ( generateEvent )
{
- wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_CLOSEUP, GetId());
+ wxCommandEvent event(wxEVT_COMBOBOX_CLOSEUP, GetId());
event.SetEventObject(this);
HandleWindowEvent(event);
}
// transfer value and show it in textctrl, if any
if ( !IsPopupWindowState(Animating) )
- SetValue( m_popupInterface->GetStringValue() );
+ SetValueByUser( m_popupInterface->GetStringValue() );
m_winPopup->Hide();
m_bmpNormal = bmpNormal;
m_blankButtonBg = blankButtonBg;
- if ( bmpPressed.Ok() )
+ if ( bmpPressed.IsOk() )
m_bmpPressed = bmpPressed;
else
m_bmpPressed = bmpNormal;
- if ( bmpHover.Ok() )
+ if ( bmpHover.IsOk() )
m_bmpHover = bmpHover;
else
m_bmpHover = bmpNormal;
- if ( bmpDisabled.Ok() )
+ if ( bmpDisabled.IsOk() )
m_bmpDisabled = bmpDisabled;
else
m_bmpDisabled = bmpNormal;
return DEFAULT_TEXT_INDENT;
}
+void wxComboCtrlBase::SetTextCtrlStyle( int style )
+{
+ m_textCtrlStyle = style;
+
+ if ( m_text )
+ m_text->SetWindowStyle(style);
+}
+
// ----------------------------------------------------------------------------
-// methods forwarded to wxTextCtrl
+// wxTextEntry interface
// ----------------------------------------------------------------------------
-wxString wxComboCtrlBase::GetValue() const
+wxString wxComboCtrlBase::DoGetValue() const
{
if ( m_text )
return m_text->GetValue();
return m_valueString;
}
-void wxComboCtrlBase::SetValueWithEvent(const wxString& value, bool withEvent)
+void wxComboCtrlBase::SetValueWithEvent(const wxString& value,
+ bool withEvent)
{
- if ( m_text )
- {
- if ( !withEvent )
- m_ignoreEvtText++;
-
- m_text->SetValue(value);
+ DoSetValue(value, withEvent ? SetValue_SendEvent : 0);
+}
- if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
- m_text->SelectAll();
- }
+void wxComboCtrlBase::OnSetValue(const wxString& value)
+{
+ // Note: before wxComboCtrl inherited from wxTextEntry,
+ // this code used to be in SetValueWithEvent().
// Since wxComboPopup 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_valueString = value;
+ bool found = true;
+ wxString trueValue = value;
+
+ // Conform to wxComboBox behaviour: read-only control can only accept
+ // valid list items and empty string
+ if ( m_popupInterface && HasFlag(wxCB_READONLY) && value.length() )
+ {
+ found = m_popupInterface->FindItem(value,
+ &trueValue);
+ }
+
+ if ( found )
+ {
+ m_valueString = trueValue;
- EnsurePopupControl();
+ EnsurePopupControl();
- if (m_popupInterface)
- m_popupInterface->SetStringValue(value);
+ if ( m_popupInterface )
+ m_popupInterface->SetStringValue(trueValue);
+ }
}
Refresh();
}
-void wxComboCtrlBase::SetValue(const wxString& value)
+void wxComboCtrlBase::SetValueByUser(const wxString& value)
{
- SetValueWithEvent(value, false);
+ // NB: Order of function calls is important here. Otherwise
+ // the SelectAll() may not work.
+
+ if ( m_text )
+ {
+ m_text->SetValue(value);
+
+ if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
+ m_text->SelectAll();
+ }
+
+ OnSetValue(value);
}
// In this SetValue variant wxComboPopup::SetStringValue is not called
m_text->SetInsertionPoint(pos);
}
-void wxComboCtrlBase::SetInsertionPointEnd()
-{
- if ( m_text )
- m_text->SetInsertionPointEnd();
-}
-
long wxComboCtrlBase::GetInsertionPoint() const
{
if ( m_text )
return 0;
}
+void wxComboCtrlBase::WriteText(const wxString& text)
+{
+ if ( m_text )
+ {
+ m_text->WriteText(text);
+ OnSetValue(m_text->GetValue());
+ }
+ else
+ {
+ OnSetValue(text);
+ }
+}
+
+void wxComboCtrlBase::DoSetValue(const wxString& value, int flags)
+{
+ if ( m_text )
+ {
+ if ( flags & SetValue_SendEvent )
+ m_text->SetValue(value);
+ else
+ m_text->ChangeValue(value);
+ }
+
+ OnSetValue(value);
+}
+
void wxComboCtrlBase::Replace(long from, long to, const wxString& value)
{
if ( m_text )
+ {
m_text->Replace(from, to, value);
+ OnSetValue(m_text->GetValue());
+ }
}
void wxComboCtrlBase::Remove(long from, long to)
{
if ( m_text )
+ {
m_text->Remove(from, to);
+ OnSetValue(m_text->GetValue());
+ }
}
void wxComboCtrlBase::SetSelection(long from, long to)
m_text->SetSelection(from, to);
}
+void wxComboCtrlBase::GetSelection(long *from, long *to) const
+{
+ if ( m_text )
+ {
+ m_text->GetSelection(from, to);
+ }
+ else
+ {
+ *from = 0;
+ *to = 0;
+ }
+}
+
+bool wxComboCtrlBase::IsEditable() const
+{
+ if ( m_text )
+ return m_text->IsEditable();
+ return false;
+}
+
+void wxComboCtrlBase::SetEditable(bool editable)
+{
+ if ( m_text )
+ m_text->SetEditable(editable);
+}
+
void wxComboCtrlBase::Undo()
{
if ( m_text )
m_text->Undo();
}
+void wxComboCtrlBase::Redo()
+{
+ if ( m_text )
+ m_text->Redo();
+}
+
+bool wxComboCtrlBase::CanUndo() const
+{
+ if ( m_text )
+ return m_text->CanUndo();
+
+ return false;
+}
+
+bool wxComboCtrlBase::CanRedo() const
+{
+ if ( m_text )
+ return m_text->CanRedo();
+
+ return false;
+}
+
bool wxComboCtrlBase::SetHint(const wxString& hint)
{
m_hintText = hint;