X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/75791a9beb8b4f80d2651dbc196b2c5ae5b855d8..62795f413a7222863b4aee76c08764071f94bd87:/src/common/combocmn.cpp diff --git a/src/common/combocmn.cpp b/src/common/combocmn.cpp index 265f67dbb5..82d1657960 100644 --- a/src/common/combocmn.cpp +++ b/src/common/combocmn.cpp @@ -4,7 +4,6 @@ // Author: Jaakko Salli // Modified by: // Created: Apr-30-2006 -// RCS-ID: $Id$ // Copyright: (c) 2005 Jaakko Salli // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -23,24 +22,97 @@ #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/log.h" #include "wx/dcclient.h" #include "wx/settings.h" - #include "wx/dialog.h" #include "wx/timer.h" + #include "wx/textctrl.h" #endif #include "wx/tooltip.h" -#include "wx/combo.h" - - - // constants // ---------------------------------------------------------------------------- @@ -57,11 +129,17 @@ #if defined(__WXMSW__) +// Let's use wxFrame as a fall-back solution until wxMSW gets wxNonOwnedWindow +#include "wx/frame.h" +#define wxCC_GENERIC_TLW_IS_FRAME +#define wxComboCtrlGenericTLW wxFrame + #define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform) #define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common // native controls work on it like normal. #define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window. #define TEXTCTRL_TEXT_CENTERED 0 // 1 if text in textctrl is vertically centered +#define FOCUS_RING 0 // No focus ring on wxMSW //#undef wxUSE_POPUPWIN //#define wxUSE_POPUPWIN 0 @@ -74,27 +152,57 @@ // reflected (or something like that - atleast commenting out ->Hide() // seemed to eliminate the position change). +#include "wx/dialog.h" +#define wxCC_GENERIC_TLW_IS_DIALOG +#define wxComboCtrlGenericTLW wxDialog + +#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 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) -#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common +#define TRANSIENT_POPUPWIN_IS_PERFECT 1 // wxPopupTransientWindow works, its child can have focus, and common // native controls work on it like normal. #define POPUPWIN_IS_PERFECT 1 // Same, but for non-transient popup window. #define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered +#define FOCUS_RING 0 // No focus ring on wxGTK #elif defined(__WXMAC__) -#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform) -#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common +#include "wx/nonownedwnd.h" +#define wxCC_GENERIC_TLW_IS_NONOWNEDWINDOW +#define wxComboCtrlGenericTLW wxNonOwnedWindow + +#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform) +#define TRANSIENT_POPUPWIN_IS_PERFECT 1 // wxPopupTransientWindow works, its child can have focus, and common // native controls work on it like normal. -#define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window. +#define POPUPWIN_IS_PERFECT 1 // Same, but for non-transient popup window. #define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered +#define FOCUS_RING 3 // Reserve room for the textctrl's focus ring to display + +#undef DEFAULT_DROPBUTTON_WIDTH +#define DEFAULT_DROPBUTTON_WIDTH 22 +#undef COMBO_MARGIN +#define COMBO_MARGIN FOCUS_RING #else +#include "wx/dialog.h" +#define wxCC_GENERIC_TLW_IS_DIALOG +#define wxComboCtrlGenericTLW wxDialog + #define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform) #define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common // native controls work on it like normal. #define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window. #define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered +#define FOCUS_RING 0 #endif @@ -102,7 +210,7 @@ // Popupwin is really only supported on wxMSW (not WINCE) and wxGTK, regardless // what the wxUSE_POPUPWIN says. // FIXME: Why isn't wxUSE_POPUPWIN reliable any longer? (it was in wxW2.6.2) -#if (!defined(__WXMSW__) && !defined(__WXGTK__)) || defined(__WXWINCE__) +#if (!defined(__WXMSW__) && !defined(__WXGTK__) && !defined(__WXMAC__)) || defined(__WXWINCE__) #undef wxUSE_POPUPWIN #define wxUSE_POPUPWIN 0 #endif @@ -122,7 +230,7 @@ enum POPUPWIN_NONE = 0, POPUPWIN_WXPOPUPTRANSIENTWINDOW = 1, POPUPWIN_WXPOPUPWINDOW = 2, - POPUPWIN_WXDIALOG = 3 + POPUPWIN_GENERICTLW = 3 }; @@ -140,9 +248,9 @@ enum #define SECONDARY_POPUP_TYPE POPUPWIN_WXPOPUPWINDOW #define USES_WXPOPUPWINDOW 1 #else - #define wxComboPopupWindowBase2 wxDialog - #define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG - #define USES_WXDIALOG 1 + #define wxComboPopupWindowBase2 wxComboCtrlGenericTLW + #define SECONDARY_POPUP_TYPE POPUPWIN_GENERICTLW + #define USES_GENERICTLW 1 #endif #elif wxUSE_POPUPWIN @@ -153,17 +261,17 @@ enum #define USES_WXPOPUPWINDOW 1 #if !POPUPWIN_IS_PERFECT - #define wxComboPopupWindowBase2 wxDialog - #define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG - #define USES_WXDIALOG 1 + #define wxComboPopupWindowBase2 wxComboCtrlGenericTLW + #define SECONDARY_POPUP_TYPE POPUPWIN_GENERICTLW + #define USES_GENERICTLW 1 #endif #else // wxPopupWindow is not implemented - #define wxComboPopupWindowBase wxDialog - #define PRIMARY_POPUP_TYPE POPUPWIN_WXDIALOG - #define USES_WXDIALOG 1 + #define wxComboPopupWindowBase wxComboCtrlGenericTLW + #define PRIMARY_POPUP_TYPE POPUPWIN_GENERICTLW + #define USES_GENERICTLW 1 #endif @@ -176,8 +284,8 @@ enum #define USES_WXPOPUPWINDOW 0 #endif -#ifndef USES_WXDIALOG - #define USES_WXDIALOG 0 +#ifndef USES_GENERICTLW + #define USES_GENERICTLW 0 #endif @@ -188,6 +296,26 @@ enum #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) @@ -264,7 +392,7 @@ void wxComboFrameEventHandler::OnIdle( wxIdleEvent& event ) wxWindow* popup = m_combo->GetPopupControl()->GetControl(); wxWindow* winpopup = m_combo->GetPopupWindow(); - if ( + if ( !winFocused || ( winFocused != m_focusStart && winFocused != popup && winFocused->GetParent() != popup && @@ -273,8 +401,9 @@ void wxComboFrameEventHandler::OnIdle( wxIdleEvent& event ) winFocused != m_combo && winFocused != m_combo->GetButton() // GTK (atleast) requires this ) + ) { - m_combo->HidePopup(); + m_combo->HidePopup(true); } event.Skip(); @@ -282,37 +411,37 @@ void wxComboFrameEventHandler::OnIdle( wxIdleEvent& event ) void wxComboFrameEventHandler::OnMenuEvent( wxMenuEvent& event ) { - m_combo->HidePopup(); + m_combo->HidePopup(true); event.Skip(); } void wxComboFrameEventHandler::OnMouseEvent( wxMouseEvent& event ) { - m_combo->HidePopup(); + m_combo->HidePopup(true); event.Skip(); } void wxComboFrameEventHandler::OnClose( wxCloseEvent& event ) { - m_combo->HidePopup(); + m_combo->HidePopup(true); event.Skip(); } void wxComboFrameEventHandler::OnActivate( wxActivateEvent& event ) { - m_combo->HidePopup(); + m_combo->HidePopup(true); event.Skip(); } void wxComboFrameEventHandler::OnResize( wxSizeEvent& event ) { - m_combo->HidePopup(); + m_combo->HidePopup(true); event.Skip(); } void wxComboFrameEventHandler::OnMove( wxMoveEvent& event ) { - m_combo->HidePopup(); + m_combo->HidePopup(true); event.Skip(); } @@ -346,6 +475,7 @@ public: #if USES_WXPOPUPTRANSIENTWINDOW virtual bool Show( bool show ); virtual bool ProcessLeftDown(wxMouseEvent& event); +protected: virtual void OnDismiss(); #endif @@ -363,15 +493,16 @@ bool wxComboPopupWindow::Show( bool show ) m_inShow++; - wxASSERT( IsKindOf(CLASSINFO(wxPopupTransientWindow)) ); - - wxPopupTransientWindow* ptw = (wxPopupTransientWindow*) this; - wxComboCtrlBase* combo = (wxComboCtrlBase*) GetParent(); + wxPopupTransientWindow* const + ptw = static_cast(this); if ( show != ptw->IsShown() ) { if ( show ) - ptw->Popup(combo->GetPopupControl()->GetControl()); + // We used to do wxPopupTransientWindow::Popup here, + // but this would hide normal Show, which we are + // also going to need. + ptw->Show(); else ptw->Dismiss(); } @@ -390,10 +521,10 @@ bool wxComboPopupWindow::ProcessLeftDown(wxMouseEvent& event) 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(); + combo->OnPopupDismiss(true); } #endif // USES_WXPOPUPTRANSIENTWINDOW @@ -415,7 +546,7 @@ public: void OnSizeEvent( wxSizeEvent& event ); void OnKeyEvent(wxKeyEvent& event); -#if USES_WXDIALOG +#if USES_GENERICTLW void OnActivate( wxActivateEvent& event ); #endif @@ -429,7 +560,8 @@ private: BEGIN_EVENT_TABLE(wxComboPopupWindowEvtHandler, wxEvtHandler) EVT_KEY_DOWN(wxComboPopupWindowEvtHandler::OnKeyEvent) EVT_KEY_UP(wxComboPopupWindowEvtHandler::OnKeyEvent) -#if USES_WXDIALOG + EVT_CHAR(wxComboPopupWindowEvtHandler::OnKeyEvent) +#if USES_GENERICTLW EVT_ACTIVATE(wxComboPopupWindowEvtHandler::OnActivate) #endif EVT_SIZE(wxComboPopupWindowEvtHandler::OnSizeEvent) @@ -447,16 +579,16 @@ void wxComboPopupWindowEvtHandler::OnKeyEvent( wxKeyEvent& event ) wxWindowList children = m_combo->GetPopupWindow()->GetChildren(); wxWindowList::iterator node = children.begin(); wxWindow* child = (wxWindow*)*node; - child->AddPendingEvent(event); + child->GetEventHandler()->ProcessEvent(event); } -#if USES_WXDIALOG +#if USES_GENERICTLW void wxComboPopupWindowEvtHandler::OnActivate( wxActivateEvent& event ) { if ( !event.GetActive() ) { // Tell combo control that we are dismissed. - m_combo->HidePopup(); + m_combo->HidePopup(true); event.Skip(); } @@ -481,6 +613,11 @@ void wxComboPopup::OnDismiss() { } +wxComboCtrl* wxComboPopup::GetComboCtrl() const +{ + return wxStaticCast(m_combo, wxComboCtrl); +} + wxSize wxComboPopup::GetAdjustedSize( int minWidth, int prefHeight, int WXUNUSED(maxHeight) ) @@ -496,7 +633,7 @@ void wxComboPopup::DefaultPaintComboControl( wxComboCtrlBase* combo, combo->PrepareBackground(dc,rect,0); dc.DrawText( combo->GetValue(), - rect.x + combo->GetTextIndent(), + rect.x + combo->m_marginLeft, (rect.height-dc.GetCharHeight())/2 + rect.y ); } } @@ -511,6 +648,11 @@ void wxComboPopup::OnComboKeyEvent( wxKeyEvent& event ) event.Skip(); } +void wxComboPopup::OnComboCharEvent( wxKeyEvent& event ) +{ + event.Skip(); +} + void wxComboPopup::OnComboDoubleClick() { } @@ -519,6 +661,12 @@ void wxComboPopup::SetStringValue( const wxString& WXUNUSED(value) ) { } +bool wxComboPopup::FindItem(const wxString& WXUNUSED(item), + wxString* WXUNUSED(trueItem)) +{ + return true; +} + bool wxComboPopup::LazyCreate() { return false; @@ -526,7 +674,35 @@ bool wxComboPopup::LazyCreate() void wxComboPopup::Dismiss() { - m_combo->HidePopup(); + 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(this) != + dynamic_cast(popupCtrl) ) + delete this; + #endif + popupCtrl->Destroy(); + } + else + { + delete this; + } } // ---------------------------------------------------------------------------- @@ -559,7 +735,10 @@ private: BEGIN_EVENT_TABLE(wxComboBoxExtraInputHandler, wxEvtHandler) EVT_KEY_DOWN(wxComboBoxExtraInputHandler::OnKey) + EVT_KEY_UP(wxComboBoxExtraInputHandler::OnKey) + EVT_CHAR(wxComboBoxExtraInputHandler::OnKey) EVT_SET_FOCUS(wxComboBoxExtraInputHandler::OnFocus) + EVT_KILL_FOCUS(wxComboBoxExtraInputHandler::OnFocus) END_EVENT_TABLE() @@ -567,26 +746,30 @@ void wxComboBoxExtraInputHandler::OnKey(wxKeyEvent& event) { // Let the wxComboCtrl event handler have a go first. wxComboCtrlBase* combo = m_combo; - wxObject* prevObj = event.GetEventObject(); - event.SetId(combo->GetId()); - event.SetEventObject(combo); - combo->GetEventHandler()->ProcessEvent(event); + wxKeyEvent redirectedEvent(event); + redirectedEvent.SetId(combo->GetId()); + redirectedEvent.SetEventObject(combo); - event.SetId(((wxWindow*)prevObj)->GetId()); - event.SetEventObject(prevObj); + if ( !combo->GetEventHandler()->ProcessEvent(redirectedEvent) ) + { + // Don't let TAB through to the text ctrl - looks ugly + if ( event.GetKeyCode() != WXK_TAB ) + event.Skip(); + } } void wxComboBoxExtraInputHandler::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() & wxCC_NO_TEXT_AUTO_SELECT) ) + if ( event.GetEventType() == wxEVT_SET_FOCUS && + !(m_combo->GetInternalFlags() & wxCC_NO_TEXT_AUTO_SELECT) ) { if ( m_combo->GetTextCtrl() ) m_combo->GetTextCtrl()->SelectAll(); else - m_combo->SetSelection(-1,-1); + m_combo->SelectAll(); } // Send focus indication to parent. @@ -595,7 +778,8 @@ void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event) // wxEVT_SET_FOCUSes (since m_text->SetFocus is called // from combo's focus event handler), they should be quite // harmless. - wxFocusEvent evt2(wxEVT_SET_FOCUS,m_combo->GetId()); + wxFocusEvent evt2(event); + evt2.SetId(m_combo->GetId()); evt2.SetEventObject(m_combo); m_combo->GetEventHandler()->ProcessEvent(evt2); @@ -607,17 +791,21 @@ void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event) // 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; m_beenInside = false; + + // Let's make it so that the popup control will not receive mouse + // events until mouse left button has been up. + m_blockEventsToPopup = true; } - virtual ~wxComboPopupExtraEventHandler() { } + virtual ~wxComboPopupEvtHandler() { } void OnMouseEvent( wxMouseEvent& event ); @@ -625,91 +813,163 @@ public: void OnPopupDismiss() { m_beenInside = false; + m_blockEventsToPopup = true; } protected: wxComboCtrlBase* m_combo; - bool m_beenInside; + bool m_beenInside; + bool m_blockEventsToPopup; private: DECLARE_EVENT_TABLE() }; -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(); int evtType = event.GetEventType(); bool isInside = pt.x >= 0 && pt.y >= 0 && pt.x < sz.x && pt.y < sz.y; + bool relayToButton = false; + + event.Skip(); - if ( evtType == wxEVT_MOTION || - evtType == wxEVT_LEFT_DOWN || - evtType == wxEVT_RIGHT_DOWN ) + if ( !isInside || !m_combo->IsPopupShown() ) { - // Block motion and click events outside the popup - if ( !isInside || !m_combo->IsPopupShown() ) + // Mouse is outside the popup or popup is not actually shown (yet) + + if ( evtType == wxEVT_MOTION || + evtType == wxEVT_LEFT_DOWN || + evtType == wxEVT_LEFT_UP || + evtType == wxEVT_RIGHT_DOWN ) { + // Block motion and click events outside the popup event.Skip(false); - return; } } - else if ( evtType == wxEVT_LEFT_UP ) + else { - if ( !m_combo->IsPopupShown() ) - { - event.Skip(false); - return; - } + // Mouse is inside the popup, which is fully shown + + m_beenInside = true; - if ( !m_beenInside ) + // Do not let the popup control respond to mouse events until + // mouse press used to display the popup has been lifted. This + // is important for users with slower mouse fingers or mouse + // drivers. Note that we have some redundancy here, just in + // case the popup is some native control that does not emit all + // mouse event types. + if ( evtType == wxEVT_MOTION ) { - if ( isInside ) + if ( m_blockEventsToPopup ) { - m_beenInside = true; + if ( event.LeftIsDown() ) + event.Skip(false); + else + m_blockEventsToPopup = false; } - else + } + else if ( evtType == wxEVT_LEFT_DOWN ) + { + if ( m_blockEventsToPopup ) + m_blockEventsToPopup = false; + } + else if ( evtType == wxEVT_LEFT_UP ) + { + if ( m_blockEventsToPopup ) { - // - // 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; + // On first left up, stop blocking mouse events (but still + // 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 ) + { + event.Skip(false); + } + } - event.Skip(); + // + // Some mouse events to popup that happen outside it, before cursor + // has been inside the popup, need to be ignored by it but relayed to + // the dropbutton. + // + if ( evtType == wxEVT_LEFT_UP ) + { + if ( !m_combo->IsPopupShown() ) + { + event.Skip(false); + relayToButton = true; + } + else if ( !isInside && !m_beenInside ) + { + // Popup is shown but the cursor is not inside, nor it has been + relayToButton = true; } } - event.Skip(); + if ( relayToButton ) + { + wxWindow* btn = m_combo->GetButton(); + if ( btn ) + 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); + } } +// ---------------------------------------------------------------------------- +// wxComboCtrlTextCtrl +// ---------------------------------------------------------------------------- + +class wxComboCtrlTextCtrl : public wxTextCtrl +{ +public: + wxComboCtrlTextCtrl() : wxTextCtrl() { } + virtual ~wxComboCtrlTextCtrl() { } + + virtual wxWindow *GetMainWindowOfCompositeControl() + { + wxComboCtrl* combo = (wxComboCtrl*) GetParent(); + + // Returning this instead of just 'parent' lets FindFocus work + // correctly even when parent control is a child of a composite + // generic control (as is case with wxGenericDatePickerCtrl). + return combo->GetMainWindowOfCompositeControl(); + } +}; + // ---------------------------------------------------------------------------- // wxComboCtrlBase // ---------------------------------------------------------------------------- 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_IDLE(wxComboCtrlBase::OnIdleEvent) //EVT_BUTTON(wxID_ANY,wxComboCtrlBase::OnButtonClickEvent) EVT_KEY_DOWN(wxComboCtrlBase::OnKeyEvent) - EVT_TEXT_ENTER(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent) + EVT_CHAR(wxComboCtrlBase::OnCharEvent) EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged) END_EVENT_TABLE() @@ -718,18 +978,18 @@ IMPLEMENT_ABSTRACT_CLASS(wxComboCtrlBase, wxControl) void wxComboCtrlBase::Init() { - m_winPopup = (wxWindow *)NULL; - m_popup = (wxWindow *)NULL; + m_winPopup = NULL; + m_popup = NULL; m_popupWinState = Hidden; - m_btn = (wxWindow*) NULL; - m_text = (wxTextCtrl*) NULL; - m_popupInterface = (wxComboPopup*) NULL; + m_btn = NULL; + m_text = NULL; + m_popupInterface = NULL; - m_popupExtraHandler = (wxEvtHandler*) NULL; - m_textEvtHandler = (wxEvtHandler*) NULL; + m_popupEvtHandler = NULL; + m_textEvtHandler = NULL; #if INSTALL_TOPLEV_HANDLER - m_toplevEvtHandler = (wxEvtHandler*) NULL; + m_toplevEvtHandler = NULL; #endif m_mainCtrlWnd = this; @@ -751,9 +1011,13 @@ void wxComboCtrlBase::Init() m_extLeft = 0; m_extRight = 0; - m_absIndent = -1; + m_marginLeft = -1; m_iFlags = 0; + m_textCtrlStyle = 0; m_timeCanAcceptClick = 0; + + m_resetFocus = false; + m_hasTcBgCol = false; } bool wxComboCtrlBase::Create(wxWindow *parent, @@ -778,15 +1042,16 @@ bool wxComboCtrlBase::Create(wxWindow *parent, // Get colours OnThemeChange(); - m_absIndent = GetNativeTextIndent(); + m_marginLeft = GetNativeTextIndent(); 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); } @@ -803,7 +1068,7 @@ void wxComboCtrlBase::InstallInputHandlers() } void -wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator) +wxComboCtrlBase::CreateTextCtrl(int style) { if ( !(m_windowStyle & wxCB_READONLY) ) { @@ -814,7 +1079,7 @@ wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator) // 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; @@ -827,15 +1092,53 @@ wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator) else m_ignoreEvtText = 0; - m_text = new wxTextCtrl(this, wxID_ANY, m_valueString, - wxDefaultPosition, wxSize(-1,10) - style, validator); + m_text = new wxComboCtrlTextCtrl(); + m_text->Create(this, wxID_ANY, m_valueString, + wxDefaultPosition, wxSize(10,-1), + 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() { - SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + // 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 + wxVisualAttributes vattrs; + vattrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + vattrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); +#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); + } + if ( !HasTransparentBackground() ) + { + SetOwnBackgroundColour(GetParent()->GetBackgroundColour()); + } +#endif // !__WXMAC__ } wxComboCtrlBase::~wxComboCtrlBase() @@ -845,7 +1148,7 @@ wxComboCtrlBase::~wxComboCtrlBase() #if INSTALL_TOPLEV_HANDLER delete ((wxComboFrameEventHandler*)m_toplevEvtHandler); - m_toplevEvtHandler = (wxEvtHandler*) NULL; + m_toplevEvtHandler = NULL; #endif DestroyPopup(); @@ -872,13 +1175,19 @@ void wxComboCtrlBase::CalculateAreas( int btnWidth ) // 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 ) { m_iFlags |= wxCC_IFLAG_BUTTON_OUTSIDE; btnBorder = 0; } + else if ( (m_iFlags & wxCC_BUTTON_COVERS_BORDER) && + m_btnSpacingX == 0 && !m_bmpNormal.IsOk() ) + { + m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE); + btnBorder = 0; + } else { m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE); @@ -886,8 +1195,8 @@ void wxComboCtrlBase::CalculateAreas( int btnWidth ) } // Defaul indentation - if ( m_absIndent < 0 ) - m_absIndent = GetNativeTextIndent(); + if ( m_marginLeft < 0 ) + m_marginLeft = GetNativeTextIndent(); int butWidth = btnWidth; @@ -931,7 +1240,7 @@ void wxComboCtrlBase::CalculateAreas( int btnWidth ) // 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(); @@ -953,6 +1262,11 @@ void wxComboCtrlBase::CalculateAreas( int btnWidth ) { int newY = butHeight+(customBorder*2); SetClientSize(wxDefaultCoord,newY); + 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; + sz.y = newY; } } @@ -963,14 +1277,14 @@ void wxComboCtrlBase::CalculateAreas( int btnWidth ) m_btnSize.y = butHeight; m_btnArea.x = ( m_btnSide==wxRIGHT ? sz.x - butAreaWid - btnBorder : btnBorder ); - m_btnArea.y = btnBorder; + m_btnArea.y = btnBorder + FOCUS_RING; m_btnArea.width = butAreaWid; - m_btnArea.height = sz.y - (btnBorder*2); + m_btnArea.height = sz.y - ((btnBorder+FOCUS_RING)*2); - 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); + m_tcArea.x = ( m_btnSide==wxRIGHT ? 0 : butAreaWid ) + customBorder + FOCUS_RING; + m_tcArea.y = customBorder + FOCUS_RING; + m_tcArea.width = sz.x - butAreaWid - (customBorder*2) - (FOCUS_RING*2); + m_tcArea.height = sz.y - ((customBorder+FOCUS_RING)*2); /* if ( m_text ) @@ -987,68 +1301,116 @@ void wxComboCtrlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust return; wxSize sz = GetClientSize(); - int customBorder = m_widthCustomBorder; -#if !TEXTCTRL_TEXT_CENTERED + int customBorder = m_widthCustomBorder; if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER ) { - // Centre textctrl + int x; + + if ( !m_widthCustomPaint ) + { + // No special custom paint area - we can use 0 left margin + // with wxTextCtrl. + if ( m_text->SetMargins(0) ) + textCtrlXAdjust = 0; + x = m_tcArea.x + m_marginLeft + textCtrlXAdjust; + } + else + { + // 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 + + m_marginLeft + textCtrlXAdjust; + } + + // Centre textctrl vertically, if needed +#if !TEXTCTRL_TEXT_CENTERED int tcSizeY = m_text->GetBestSize().y; - int diff = sz.y - tcSizeY; - int y = textCtrlYAdjust + (diff/2); + int diff0 = sz.y - tcSizeY; + int y = textCtrlYAdjust + (diff0/2); +#else + wxUnusedVar(textCtrlYAdjust); + int y = 0; +#endif if ( y < customBorder ) y = customBorder; - m_text->SetSize( m_tcArea.x + m_widthCustomPaint + m_absIndent + textCtrlXAdjust, - y, - m_tcArea.width - COMBO_MARGIN - - (textCtrlXAdjust + m_widthCustomPaint + m_absIndent), - -1 ); + m_text->SetSize(x, + y, + m_tcArea.width - m_tcArea.x - x, + -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 ) + int diff1 = (y + tsz.y) - (sz.y - customBorder); + if ( diff1 >= 0 ) { - tsz.y = tsz.y - diff - 1; + tsz.y = tsz.y - diff1 - 1; m_text->SetSize(tsz); } } else -#else - wxUnusedVar(textCtrlXAdjust); - wxUnusedVar(textCtrlYAdjust); -#endif { - // If it has border, have textctrl will the entire text field. + // If it has border, have textctrl fill the entire text field. m_text->SetSize( m_tcArea.x + m_widthCustomPaint, - customBorder, - sz.x - m_btnArea.width - m_widthCustomPaint - customBorder, - sz.y-(customBorder*2) ); + m_tcArea.y, + m_tcArea.width - m_widthCustomPaint, + m_tcArea.height ); } } 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(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 @@ -1064,16 +1426,37 @@ wxSize wxComboCtrlBase::DoGetBestSize() const fhei += 4; */ - // Final adjustments -#ifdef __WXGTK__ - fhei += 1; +#ifdef __WXMAC__ + // these are the numbers from the HIG: + switch ( m_windowVariant ) + { + case wxWINDOW_VARIANT_NORMAL: + default : + fhei = 22; + break; + case wxWINDOW_VARIANT_SMALL: + fhei = 19; + break; + case wxWINDOW_VARIANT_MINI: + fhei = 15; + break; + } #endif - wxSize ret(sizeText.x + COMBO_MARGIN + DEFAULT_DROPBUTTON_WIDTH, - fhei); + fhei += 2 * FOCUS_RING; + + // Calculate width + int fwid = xlen + FOCUS_RING + COMBO_MARGIN + DEFAULT_DROPBUTTON_WIDTH; - CacheBestSize(ret); - return ret; + // 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 ) @@ -1101,6 +1484,8 @@ bool wxComboCtrlBase::Enable(bool enable) if ( m_text ) m_text->Enable(enable); + Refresh(); + return true; } @@ -1123,8 +1508,15 @@ bool wxComboCtrlBase::SetFont ( const wxFont& font ) if ( !wxControl::SetFont(font) ) return false; - if (m_text) + if ( m_text ) + { + // Without hiding the wxTextCtrl there would be some + // visible 'flicker' (at least on Windows XP). + m_text->Hide(); m_text->SetFont(font); + OnResize(); + m_text->Show(); + } return true; } @@ -1143,12 +1535,39 @@ void wxComboCtrlBase::DoSetToolTip(wxToolTip *tooltip) } else { - if ( m_text ) m_text->SetToolTip( (wxToolTip*) NULL ); - if ( m_btn ) m_btn->SetToolTip( (wxToolTip*) NULL ); + if ( m_text ) m_text->SetToolTip( NULL ); + if ( m_btn ) m_btn->SetToolTip( NULL ); } } #endif // wxUSE_TOOLTIPS +bool wxComboCtrlBase::SetForegroundColour(const wxColour& colour) +{ + if ( wxControl::SetForegroundColour(colour) ) + { + if ( m_text ) + m_text->SetForegroundColour(colour); + return true; + } + return false; +} + +bool wxComboCtrlBase::SetBackgroundColour(const wxColour& colour) +{ + 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 // ---------------------------------------------------------------------------- @@ -1159,7 +1578,7 @@ void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags { wxSize sz = GetClientSize(); bool isEnabled; - bool isFocused; // also selected + bool doDrawFocusRect; // also selected // For smaller size control (and for disabled background) use less spacing int focusSpacingX; @@ -1169,7 +1588,7 @@ void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags { // Drawing control isEnabled = IsEnabled(); - isFocused = ShouldDrawFocus(); + doDrawFocusRect = ShouldDrawFocus() && !(m_iFlags & wxCC_FULL_BUTTON); // Windows-style: for smaller size control (and for disabled background) use less spacing focusSpacingX = isEnabled ? 2 : 1; @@ -1179,7 +1598,7 @@ void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags { // Drawing a list item isEnabled = true; // they are never disabled - isFocused = flags & wxCONTROL_SELECTED ? true : false; + doDrawFocusRect = (flags & wxCONTROL_SELECTED) != 0; focusSpacingX = 0; focusSpacingY = 0; @@ -1199,31 +1618,70 @@ void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags selRect.width -= wcp + (focusSpacingX*2); wxColour bgCol; + wxColour fgCol; + + bool doDrawSelRect = true; + // Determine foreground colour if ( isEnabled ) { - // If popup is hidden and this control is focused, - // then draw the focus-indicator (selbgcolor background etc.). - if ( isFocused ) + if ( doDrawFocusRect ) + { + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + } + else if ( m_hasFgCol ) + { + // Honour the custom foreground colour + fgCol = GetForegroundColour(); + } + else + { + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + } + } + else + { + fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); + } + + // Determine background colour + if ( isEnabled ) + { + if ( doDrawFocusRect ) { - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) ); bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); } + else if ( m_hasTcBgCol ) + { + // Honour the custom background colour + bgCol = m_tcBgCol; + } else { - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) ); +#ifndef __WXMAC__ // see note in OnThemeChange + doDrawSelRect = false; bgCol = GetBackgroundColour(); +#else + bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); +#endif } } else { - dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) ); +#ifndef __WXMAC__ // see note in OnThemeChange bgCol = GetBackgroundColour(); +#else + bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); +#endif } + dc.SetTextForeground( fgCol ); dc.SetBrush( bgCol ); - dc.SetPen( bgCol ); - dc.DrawRectangle( selRect ); + if ( doDrawSelRect ) + { + dc.SetPen( bgCol ); + dc.DrawRectangle( selRect ); + } // Don't clip exactly to the selection rectangle so we can draw // to the non-selected area in front of it. @@ -1238,14 +1696,13 @@ void wxComboCtrlBase::PrepareBackground( wxDC&, const wxRect&, int ) const } #endif -void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, bool paintBg ) +void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int flags ) { int drawState = m_btnState; -#ifdef __WXGTK__ - if ( GetPopupWindowState() >= Animating ) + if ( (m_iFlags & wxCC_BUTTON_STAYS_DOWN) && + GetPopupWindowState() >= Animating ) drawState |= wxCONTROL_PRESSED; -#endif wxRect drawRect(rect.x+m_btnSpacingX, rect.y+((rect.height-m_btnSize.y)/2), @@ -1263,22 +1720,28 @@ void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, bool paintBg ) 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)) ) { - // Need to clear button background even if m_btn is present - if ( paintBg ) - { - wxColour bgCol; + 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, @@ -1303,30 +1766,13 @@ void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, bool paintBg ) 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 ( paintBg ) + if ( !(flags & Button_BitmapOnly) ) { - 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); + wxRendererNative::Get().DrawPushButton(this, + dc, + drawRect, + drawState); } - - wxRendererNative::Get().DrawPushButton(this, - dc, - drawRect, - 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 ( paintBg ) - dc.DrawRectangle(rect); } // Draw bitmap centered in drawRect @@ -1342,6 +1788,7 @@ void wxComboCtrlBase::RecalcAndRefresh() if ( IsCreated() ) { wxSizeEvent evt(GetSize(),GetId()); + evt.SetEventObject(this); GetEventHandler()->ProcessEvent(evt); Refresh(); } @@ -1353,7 +1800,14 @@ void wxComboCtrlBase::RecalcAndRefresh() 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 ) { @@ -1362,23 +1816,25 @@ void wxComboCtrlBase::OnTextCtrlEvent(wxCommandEvent& event) } } - // 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 bool wxComboCtrlBase::HandleButtonMouseEvent( wxMouseEvent& event, - int flags ) + int flags ) { int type = event.GetEventType(); if ( type == wxEVT_MOTION ) { - if ( flags & wxCC_MF_ON_BUTTON ) + if ( (flags & wxCC_MF_ON_BUTTON) && + IsPopupWindowState(Hidden) ) { if ( !(m_btnState & wxCONTROL_CURRENT) ) { @@ -1447,6 +1903,11 @@ bool wxComboCtrlBase::HandleButtonMouseEvent( wxMouseEvent& event, else return false; + // Never have 'hot' state when popup is being shown + // (this is mostly needed because of the animation). + if ( !IsPopupWindowState(Hidden) ) + m_btnState &= ~wxCONTROL_CURRENT; + return true; } @@ -1457,19 +1918,19 @@ bool wxComboCtrlBase::PreprocessMouseEvent( wxMouseEvent& event, wxLongLong t = ::wxGetLocalTimeMillis(); int evtType = event.GetEventType(); -#if USES_WXPOPUPWINDOW || USES_WXDIALOG +#if USES_WXPOPUPWINDOW || USES_GENERICTLW if ( m_popupWinType != POPUPWIN_WXPOPUPTRANSIENTWINDOW ) { if ( IsPopupWindowState(Visible) && ( evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_RIGHT_DOWN ) ) { - HidePopup(); + HidePopup(true); return true; } } #endif - // Filter out clicks on button immediately after popup dismiss (Windows like behaviour) + // Filter out clicks on button immediately after popup dismiss if ( evtType == wxEVT_LEFT_DOWN && t < m_timeCanAcceptClick ) { event.SetEventType(0); @@ -1491,7 +1952,7 @@ void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event ) #if USES_WXPOPUPWINDOW // Click here always hides the popup. if ( m_popupWinType == POPUPWIN_WXPOPUPWINDOW ) - HidePopup(); + HidePopup(true); #endif } else @@ -1511,15 +1972,35 @@ void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event ) } } } - else - if ( IsPopupShown() ) + else if ( evtType == wxEVT_MOUSEWHEEL ) { - // relay (some) mouse events to the popup - if ( evtType == wxEVT_MOUSEWHEEL ) - m_popup->AddPendingEvent(event); + if ( IsPopupShown() ) + { + // relay (some) mouse events to the popup + m_popup->GetEventHandler()->ProcessEvent(event); + } + else if ( event.GetWheelAxis() == 0 && + event.GetWheelRotation() != 0 && + event.GetModifiers() == 0 ) + { + // Translate mousewheel actions into key up/down. This is + // the simplest way of getting native behaviour: scrolling the + // wheel moves selection up/down by one item. + wxKeyEvent kevent(wxEVT_KEY_DOWN); + kevent.m_keyCode = event.GetWheelRotation() > 0 + ? WXK_UP + : WXK_DOWN; + GetEventHandler()->ProcessEvent(kevent); + } + else + { + event.Skip(); + } } else if ( evtType ) + { event.Skip(); + } } void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) @@ -1527,25 +2008,16 @@ void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) if ( IsPopupShown() ) { // pass it to the popped up control - GetPopupControl()->GetControl()->AddPendingEvent(event); + GetPopupControl()->GetControl()->GetEventHandler()->ProcessEvent(event); } else // no popup { - int keycode = event.GetKeyCode(); + wxWindow* mainCtrl = GetMainWindowOfCompositeControl(); - if ( keycode == WXK_TAB ) + if ( mainCtrl->GetParent()->HasFlag(wxTAB_TRAVERSAL) ) { - wxNavigationKeyEvent evt; - - wxWindow* mainCtrl = GetMainWindowOfCompositeControl(); - - evt.SetFlags(wxNavigationKeyEvent::FromTab| - (!event.ShiftDown() ? wxNavigationKeyEvent::IsForward - : wxNavigationKeyEvent::IsBackward)); - evt.SetEventObject(mainCtrl); - evt.SetCurrentFocus(mainCtrl); - mainCtrl->GetParent()->GetEventHandler()->AddPendingEvent(evt); - return; + if ( mainCtrl->HandleAsNavigationKey(event) ) + return; } if ( IsKeyPopupToggle(event) ) @@ -1563,6 +2035,8 @@ void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) return; } + int keycode = event.GetKeyCode(); + if ( (comboStyle & wxCB_READONLY) || (keycode != WXK_RIGHT && keycode != WXK_LEFT) ) { @@ -1573,26 +2047,64 @@ void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event) } } +void wxComboCtrlBase::OnCharEvent(wxKeyEvent& event) +{ + if ( IsPopupShown() ) + { + // pass it to the popped up control + GetPopupControl()->GetControl()->GetEventHandler()->ProcessEvent(event); + } + else // no popup + { + wxComboPopup* popupInterface = GetPopupControl(); + if ( popupInterface ) + { + popupInterface->OnComboCharEvent(event); + } + else + { + event.Skip(); + } + } +} + 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 ) { -#ifndef __WXMAC__ - wxWindow* tc = GetTextCtrl(); - if ( tc && tc != DoFindFocus() ) - tc->SetFocus(); + if ( GetTextCtrl() && !GetTextCtrl()->HasFocus() ) + { +#ifdef __WXMAC__ + m_resetFocus = true; +#else + GetTextCtrl()->SetFocus(); #endif + } } - + Refresh(); } +void wxComboCtrlBase::OnIdleEvent( wxIdleEvent& WXUNUSED(event) ) +{ + if ( m_resetFocus ) + { + m_resetFocus = false; + if ( GetTextCtrl() ) + GetTextCtrl()->SetFocus(); + } +} + void wxComboCtrlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) { OnThemeChange(); - // indentation may also have changed - if ( !(m_iFlags & wxCC_IFLAG_INDENT_SET) ) - m_absIndent = GetNativeTextIndent(); + // left margin may also have changed + if ( !(m_iFlags & wxCC_IFLAG_LEFT_MARGIN_SET) ) + m_marginLeft = GetNativeTextIndent(); RecalcAndRefresh(); } @@ -1611,17 +2123,28 @@ void wxComboCtrlBase::CreatePopup() #ifdef wxComboPopupWindowBase2 if ( m_iFlags & wxCC_IFLAG_USE_ALT_POPUP ) { - #if !USES_WXDIALOG + #if !USES_GENERICTLW m_winPopup = new wxComboPopupWindowBase2( this, wxNO_BORDER ); #else + int tlwFlags = wxNO_BORDER; + #ifdef wxCC_GENERIC_TLW_IS_FRAME + tlwFlags |= wxFRAME_NO_TASKBAR; + #endif + + #ifdef wxCC_GENERIC_TLW_IS_NONOWNEDWINDOW + m_winPopup = new wxComboPopupWindowBase2( this, wxID_ANY, + wxPoint(-21,-21), wxSize(20, 20), + tlwFlags ); + #else m_winPopup = new wxComboPopupWindowBase2( this, wxID_ANY, wxEmptyString, wxPoint(-21,-21), wxSize(20, 20), - wxNO_BORDER ); + tlwFlags ); + #endif #endif m_popupWinType = SECONDARY_POPUP_TYPE; } else -#endif +#endif // wxComboPopupWindowBase2 { m_winPopup = new wxComboPopupWindow( this, wxNO_BORDER ); m_popupWinType = PRIMARY_POPUP_TYPE; @@ -1633,8 +2156,8 @@ void wxComboCtrlBase::CreatePopup() 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 @@ -1647,27 +2170,29 @@ void wxComboCtrlBase::CreatePopup() // Destroy popup window and the child control void wxComboCtrlBase::DestroyPopup() { - HidePopup(); + 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 = (wxEvtHandler*) NULL; - m_popupInterface = (wxComboPopup*) NULL; - m_winPopup = (wxWindow*) NULL; - m_popup = (wxWindow*) NULL; + m_popup = NULL; } void wxComboCtrlBase::DoSetPopupControl(wxComboPopup* iface) @@ -1687,11 +2212,11 @@ void wxComboCtrlBase::DoSetPopupControl(wxComboPopup* iface) } else { - m_popup = (wxWindow*) NULL; + m_popup = NULL; } // This must be done after creation - if ( m_valueString.length() ) + if ( !m_valueString.empty() ) { iface->SetStringValue(m_valueString); //Refresh(); @@ -1709,6 +2234,29 @@ void wxComboCtrlBase::OnButtonClick() { // Derived classes can override this method for totally custom // popup action + switch ( GetPopupWindowState() ) + { + case Hidden: + { + Popup(); + break; + } + + case Animating: + case Visible: + { + HidePopup(true); + break; + } + } +} + +void wxComboCtrlBase::Popup() +{ + wxCommandEvent event(wxEVT_COMBOBOX_DROPDOWN, GetId()); + event.SetEventObject(this); + HandleWindowEvent(event); + ShowPopup(); } @@ -1756,7 +2304,8 @@ void wxComboCtrlBase::ShowPopup() // 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(); + wxWindow* mainCtrl = GetMainWindowOfCompositeControl(); + wxWindow* parent = mainCtrl->GetParent(); int parentFlags = parent->GetWindowStyle(); if ( parentFlags & wxTAB_TRAVERSAL ) { @@ -1776,7 +2325,7 @@ void wxComboCtrlBase::ShowPopup() } winPopup->Enable(); - + wxASSERT( !m_popup || m_popup == popup ); // Consistency check. wxSize adjustedSize = m_popupInterface->GetAdjustedSize(widthPopup, @@ -1803,6 +2352,10 @@ void wxComboCtrlBase::ShowPopup() int rightX = scrPos.x + ctrlSz.x + m_extRight - szp.x; int leftX = scrPos.x - m_extLeft; + + if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ) + leftX -= ctrlSz.x; + int screenWidth = wxSystemSettings::GetMetric( wxSYS_SCREEN_X ); // If there is not enough horizontal space, anchor on the other side. @@ -1903,9 +2456,21 @@ void wxComboCtrlBase::DoShowPopup( const wxRect& rect, int WXUNUSED(flags) ) // (though the bug was probably fixed). winPopup->SetSize( rect ); - winPopup->Show(); +#if USES_WXPOPUPTRANSIENTWINDOW + if ( m_popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW ) + ((wxPopupTransientWindow*)winPopup)->Popup(m_popup); + else +#endif + 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) ) { @@ -1915,9 +2480,11 @@ void wxComboCtrlBase::DoShowPopup( const wxRect& rect, int WXUNUSED(flags) ) m_popupWinState = Hidden; } + + Refresh(); } -void wxComboCtrlBase::OnPopupDismiss() +void wxComboCtrlBase::OnPopupDismiss(bool generateEvent) { // Just in case, avoid double dismiss if ( IsPopupWindowState(Hidden) ) @@ -1933,8 +2500,8 @@ void wxComboCtrlBase::OnPopupDismiss() // 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 @@ -1969,9 +2536,16 @@ void wxComboCtrlBase::OnPopupDismiss() Refresh(); SetFocus(); + + if ( generateEvent ) + { + wxCommandEvent event(wxEVT_COMBOBOX_CLOSEUP, GetId()); + event.SetEventObject(this); + HandleWindowEvent(event); + } } -void wxComboCtrlBase::HidePopup() +void wxComboCtrlBase::HidePopup(bool generateEvent) { // Should be able to call this without popup interface if ( IsPopupWindowState(Hidden) ) @@ -1979,11 +2553,11 @@ void wxComboCtrlBase::HidePopup() // transfer value and show it in textctrl, if any if ( !IsPopupWindowState(Animating) ) - SetValue( m_popupInterface->GetStringValue() ); + SetValueByUser( m_popupInterface->GetStringValue() ); m_winPopup->Hide(); - OnPopupDismiss(); + OnPopupDismiss(generateEvent); } // ---------------------------------------------------------------------------- @@ -1998,6 +2572,9 @@ void wxComboCtrlBase::SetButtonPosition( int width, int height, m_btnSide = side; m_btnSpacingX = spacingX; + if ( width > 0 || height > 0 || spacingX ) + m_iFlags |= wxCC_IFLAG_HAS_NONSTANDARD_BUTTON; + RecalcAndRefresh(); } @@ -2029,17 +2606,17 @@ void wxComboCtrlBase::SetButtonBitmaps( const wxBitmap& bmpNormal, 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; @@ -2064,65 +2641,139 @@ void wxComboCtrlBase::SetCustomPaintWidth( int width ) RecalcAndRefresh(); } +bool wxComboCtrlBase::DoSetMargins(const wxPoint& margins) +{ + // For general sanity's sake, we ignore top margin. Instead + // we will always try to center the text vertically. + bool res = true; + + if ( margins.x != -1 ) + { + m_marginLeft = margins.x; + m_iFlags |= wxCC_IFLAG_LEFT_MARGIN_SET; + } + else + { + m_marginLeft = GetNativeTextIndent(); + m_iFlags &= ~(wxCC_IFLAG_LEFT_MARGIN_SET); + } + + if ( margins.y != -1 ) + { + res = false; + } + + RecalcAndRefresh(); + + return res; +} + +wxPoint wxComboCtrlBase::DoGetMargins() const +{ + return wxPoint(m_marginLeft, -1); +} + +#if WXWIN_COMPATIBILITY_2_8 void wxComboCtrlBase::SetTextIndent( int indent ) { if ( indent < 0 ) { - m_absIndent = GetNativeTextIndent(); - m_iFlags &= ~(wxCC_IFLAG_INDENT_SET); + m_marginLeft = GetNativeTextIndent(); + m_iFlags &= ~(wxCC_IFLAG_LEFT_MARGIN_SET); } else { - m_absIndent = indent; - m_iFlags |= wxCC_IFLAG_INDENT_SET; + m_marginLeft = indent; + m_iFlags |= wxCC_IFLAG_LEFT_MARGIN_SET; } RecalcAndRefresh(); } +wxCoord wxComboCtrlBase::GetTextIndent() const +{ + return m_marginLeft; +} +#endif + wxCoord wxComboCtrlBase::GetNativeTextIndent() const { 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); - if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) ) - m_text->SelectAll(); - } - - m_valueString = value; + DoSetValue(value, withEvent ? SetValue_SendEvent : 0); +} - Refresh(); +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_popupInterface ) + if ( m_valueString != value ) { - m_popupInterface->SetStringValue(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(); + + 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 @@ -2167,12 +2818,6 @@ void wxComboCtrlBase::SetInsertionPoint(long pos) m_text->SetInsertionPoint(pos); } -void wxComboCtrlBase::SetInsertionPointEnd() -{ - if ( m_text ) - m_text->SetInsertionPointEnd(); -} - long wxComboCtrlBase::GetInsertionPoint() const { if ( m_text ) @@ -2189,16 +2834,48 @@ long wxComboCtrlBase::GetLastPosition() const 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) @@ -2207,10 +2884,73 @@ 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; + bool res = true; + if ( m_text ) + res = m_text->SetHint(hint); + Refresh(); + return res; +} + +wxString wxComboCtrlBase::GetHint() const +{ + return m_hintText; +} + #endif // wxUSE_COMBOCTRL