combo.cpp

Go to the documentation of this file.
00001 // $Id: combo.cpp 1496 2006-07-22 13:09:45Z alex $
00002 /* @@tag:xara-cn-tp@@ THIRD PARTY COPYRIGHT */
00003 // The following line makes normalize.pl skip type fixing
00004 /* SKIPFIXTYPES: START */
00005 
00007 // Name:        combocmn.cpp
00008 // Purpose:     wxComboControlBase
00009 // Author:      Jaakko Salli
00010 // Modified by:
00011 // Created:     Apr-30-2006
00012 // RCS-ID:
00013 // Copyright:   (c) 2005 Jaakko Salli
00014 // Licence:     wxWindows licence
00016 
00017 // ============================================================================
00018 // declarations
00019 // ============================================================================
00020 
00021 // ----------------------------------------------------------------------------
00022 // headers
00023 // ----------------------------------------------------------------------------
00024 
00025 #include <wx/wx.h>
00026 
00027 #include "combo.h"
00028 #if wxXTRA_COMBOCTRL
00029 
00030 #include <wx/renderer.h>
00031 #include <wx/tooltip.h>
00032 
00033 // constants
00034 // ----------------------------------------------------------------------------
00035 
00036 // Milliseconds to wait for two mouse-ups after focus inorder
00037 // to trigger a double-click.
00038 #define DOUBLE_CLICK_CONVERSION_TRESHOLD        500
00039 
00040 #define DEFAULT_DROPBUTTON_WIDTH                19
00041 
00042 #define BMP_BUTTON_MARGIN                       4
00043 
00044 #define DEFAULT_POPUP_HEIGHT                    200
00045 
00046 #define DEFAULT_TEXT_INDENT                     3
00047 
00048 #define COMBO_MARGIN                            2 // spacing right of wxTextCtrl
00049 
00050 
00051 #if defined(__WXMSW__)
00052 
00053 #define USE_TRANSIENT_POPUP           1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
00054 
00055 //#undef wxUSE_POPUPWIN
00056 //#define wxUSE_POPUPWIN 0
00057 
00058 #elif defined(__WXGTK__)
00059 
00060 #define USE_TRANSIENT_POPUP           1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
00061 
00062 #elif defined(__WXMAC__)
00063 
00064 #define USE_TRANSIENT_POPUP           0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
00065 
00066 #else
00067 
00068 #define USE_TRANSIENT_POPUP           0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
00069 
00070 #endif
00071 
00072 
00073 // Popupwin is really only supported on wxMSW (not WINCE) and wxGTK, regardless
00074 // what the wxUSE_POPUPWIN says.
00075 // FIXME: Why isn't wxUSE_POPUPWIN reliable any longer? (it was in wxW2.6.2)
00076 #if (!defined(__WXMSW__) && !defined(__WXGTK__)) || defined(__WXWINCE__)
00077 #undef wxUSE_POPUPWIN
00078 #define wxUSE_POPUPWIN 0
00079 #endif
00080 
00081 
00082 #if wxUSE_POPUPWIN
00083     #include "wx/popupwin.h"
00084 #else
00085     #undef USE_TRANSIENT_POPUP
00086     #define USE_TRANSIENT_POPUP 0
00087 #endif
00088 
00089 
00090 #if USE_TRANSIENT_POPUP
00091 
00092     #define wxComboPopupWindowBase wxPopupTransientWindow
00093     #define INSTALL_TOPLEV_HANDLER       0
00094 
00095 #elif wxUSE_POPUPWIN
00096 
00097     #define wxComboPopupWindowBase wxPopupWindow
00098     #define INSTALL_TOPLEV_HANDLER       1
00099 
00100 #else
00101 
00102     #define wxComboPopupWindowBase wxDialog
00103     #define INSTALL_TOPLEV_HANDLER      0 // Doesn't need since can monitor active event
00104 
00105 #endif
00106 
00107 
00108 
00109 //
00110 // ** TODO **
00111 // * wxComboPopupWindow for external use (ie. replace old wxUniv wxPopupComboWindow)
00112 //
00113 
00114 
00115 // ----------------------------------------------------------------------------
00116 // wxComboFrameEventHandler takes care of hiding the popup when events happen
00117 // in its top level parent.
00118 // ----------------------------------------------------------------------------
00119 
00120 #if INSTALL_TOPLEV_HANDLER
00121 
00122 //
00123 // This will no longer be necessary after wxTransientPopupWindow
00124 // works well on all platforms.
00125 //
00126 
00127 class wxComboFrameEventHandler : public wxEvtHandler
00128 {
00129 public:
00130     wxComboFrameEventHandler( wxComboCtrlBase* pCb );
00131     ~wxComboFrameEventHandler();
00132 
00133     void OnPopup();
00134 
00135     void OnIdle( wxIdleEvent& event );
00136     void OnMouseEvent( wxMouseEvent& event );
00137     void OnActivate( wxActivateEvent& event );
00138     void OnResize( wxSizeEvent& event );
00139     void OnMove( wxMoveEvent& event );
00140     void OnMenuEvent( wxMenuEvent& event );
00141     void OnClose( wxCloseEvent& event );
00142 
00143 protected:
00144     wxWindow*                       m_focusStart;
00145     wxComboCtrlBase*     m_combo;
00146 
00147 private:
00148     DECLARE_EVENT_TABLE()
00149 };
00150 
00151 BEGIN_EVENT_TABLE(wxComboFrameEventHandler, wxEvtHandler)
00152     EVT_IDLE(wxComboFrameEventHandler::OnIdle)
00153     EVT_LEFT_DOWN(wxComboFrameEventHandler::OnMouseEvent)
00154     EVT_RIGHT_DOWN(wxComboFrameEventHandler::OnMouseEvent)
00155     EVT_SIZE(wxComboFrameEventHandler::OnResize)
00156     EVT_MOVE(wxComboFrameEventHandler::OnMove)
00157     EVT_MENU_HIGHLIGHT(wxID_ANY,wxComboFrameEventHandler::OnMenuEvent)
00158     EVT_MENU_OPEN(wxComboFrameEventHandler::OnMenuEvent)
00159     EVT_ACTIVATE(wxComboFrameEventHandler::OnActivate)
00160     EVT_CLOSE(wxComboFrameEventHandler::OnClose)
00161 END_EVENT_TABLE()
00162 
00163 wxComboFrameEventHandler::wxComboFrameEventHandler( wxComboCtrlBase* combo )
00164     : wxEvtHandler()
00165 {
00166     m_combo = combo;
00167 }
00168 
00169 wxComboFrameEventHandler::~wxComboFrameEventHandler()
00170 {
00171 }
00172 
00173 void wxComboFrameEventHandler::OnPopup()
00174 {
00175     m_focusStart = ::wxWindow::FindFocus();
00176 }
00177 
00178 void wxComboFrameEventHandler::OnIdle( wxIdleEvent& event )
00179 {
00180     wxWindow* winFocused = ::wxWindow::FindFocus();
00181 
00182     wxWindow* popup = m_combo->GetPopupControl();
00183     wxWindow* winpopup = m_combo->GetPopupWindow();
00184 
00185     if (
00186          winFocused != m_focusStart &&
00187          winFocused != popup &&
00188          winFocused->GetParent() != popup &&
00189          winFocused != winpopup &&
00190          winFocused->GetParent() != winpopup &&
00191          winFocused != m_combo &&
00192          winFocused != m_combo->GetButton() // GTK (atleast) requires this
00193         )
00194     {
00195         m_combo->HidePopup();
00196     }
00197 
00198     event.Skip();
00199 }
00200 
00201 void wxComboFrameEventHandler::OnMenuEvent( wxMenuEvent& event )
00202 {
00203     m_combo->HidePopup();
00204     event.Skip();
00205 }
00206 
00207 void wxComboFrameEventHandler::OnMouseEvent( wxMouseEvent& event )
00208 {
00209     m_combo->HidePopup();
00210     event.Skip();
00211 }
00212 
00213 void wxComboFrameEventHandler::OnClose( wxCloseEvent& event )
00214 {
00215     m_combo->HidePopup();
00216     event.Skip();
00217 }
00218 
00219 void wxComboFrameEventHandler::OnActivate( wxActivateEvent& event )
00220 {
00221     m_combo->HidePopup();
00222     event.Skip();
00223 }
00224 
00225 void wxComboFrameEventHandler::OnResize( wxSizeEvent& event )
00226 {
00227     m_combo->HidePopup();
00228     event.Skip();
00229 }
00230 
00231 void wxComboFrameEventHandler::OnMove( wxMoveEvent& event )
00232 {
00233     m_combo->HidePopup();
00234     event.Skip();
00235 }
00236 
00237 #endif // INSTALL_TOPLEV_HANDLER
00238 
00239 // ----------------------------------------------------------------------------
00240 // wxComboPopupWindow is wxPopupWindow customized for
00241 // wxComboCtrl.
00242 // ----------------------------------------------------------------------------
00243 
00244 class wxComboPopupWindow : public wxComboPopupWindowBase
00245 {
00246 public:
00247 
00248     wxComboPopupWindow( wxComboCtrlBase *parent, int style = wxBORDER_NONE );
00249 
00250 #if USE_TRANSIENT_POPUP
00251     virtual bool ProcessLeftDown(wxMouseEvent& event);
00252 #endif
00253 
00254     void OnKeyEvent(wxKeyEvent& event);
00255 
00256     void OnMouseEvent( wxMouseEvent& event );
00257 #if !wxUSE_POPUPWIN
00258     void OnActivate( wxActivateEvent& event );
00259 #endif
00260 
00261 protected:
00262 
00263 #if USE_TRANSIENT_POPUP
00264     virtual void OnDismiss();
00265 #endif
00266 
00267 private:
00268     DECLARE_EVENT_TABLE()
00269 };
00270 
00271 
00272 BEGIN_EVENT_TABLE(wxComboPopupWindow, wxComboPopupWindowBase)
00273     EVT_MOUSE_EVENTS(wxComboPopupWindow::OnMouseEvent)
00274 #if !wxUSE_POPUPWIN
00275     EVT_ACTIVATE(wxComboPopupWindow::OnActivate)
00276 #endif
00277     EVT_KEY_DOWN(wxComboPopupWindow::OnKeyEvent)
00278     EVT_KEY_UP(wxComboPopupWindow::OnKeyEvent)
00279 END_EVENT_TABLE()
00280 
00281 
00282 wxComboPopupWindow::wxComboPopupWindow( wxComboCtrlBase *parent,
00283                                         int style )
00284 #if wxUSE_POPUPWIN
00285                                        : wxComboPopupWindowBase(parent,style)
00286 #else
00287                                        : wxComboPopupWindowBase(parent,
00288                                                                 wxID_ANY,
00289                                                                 wxEmptyString,
00290                                                                 wxPoint(-21,-21),
00291                                                                 wxSize(20,20),
00292                                                                 style)
00293 #endif
00294 {
00295 }
00296 
00297 void wxComboPopupWindow::OnKeyEvent( wxKeyEvent& event )
00298 {
00299     // Relay keyboard event to the main child controls
00300     // (just skipping may just cause the popup to close)
00301     wxWindowList children = GetChildren();
00302     wxWindowList::iterator node = children.begin();
00303     wxWindow* child = (wxWindow*)*node;
00304     child->AddPendingEvent(event);
00305 }
00306 
00307 void wxComboPopupWindow::OnMouseEvent( wxMouseEvent& event )
00308 {
00309     event.Skip();
00310 }
00311 
00312 #if !wxUSE_POPUPWIN
00313 void wxComboPopupWindow::OnActivate( wxActivateEvent& event )
00314 {
00315     if ( !event.GetActive() )
00316     {
00317         // Tell combo control that we are dismissed.
00318         wxComboCtrl* combo = (wxComboCtrl*) GetParent();
00319         wxASSERT( combo );
00320         wxASSERT( combo->IsKindOf(CLASSINFO(wxComboCtrl)) );
00321 
00322         combo->HidePopup();
00323 
00324         event.Skip();
00325     }
00326 }
00327 #endif
00328 
00329 #if USE_TRANSIENT_POPUP
00330 bool wxComboPopupWindow::ProcessLeftDown(wxMouseEvent& event )
00331 {
00332     return wxComboPopupWindowBase::ProcessLeftDown(event);
00333 }
00334 #endif
00335 
00336 #if USE_TRANSIENT_POPUP
00337 // First thing that happens when a transient popup closes is that this method gets called.
00338 void wxComboPopupWindow::OnDismiss()
00339 {
00340     wxComboCtrlBase* combo = (wxComboCtrlBase*) GetParent();
00341     wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxComboCtrlBase)),
00342                   wxT("parent might not be wxComboCtrl, but check IMPLEMENT_DYNAMIC_CLASS(2) macro for correctness") );
00343 
00344     combo->OnPopupDismiss();
00345 }
00346 #endif
00347 
00348 // ----------------------------------------------------------------------------
00349 // wxComboPopup
00350 //
00351 // ----------------------------------------------------------------------------
00352 
00353 wxComboPopup::~wxComboPopup()
00354 {
00355 }
00356 
00357 void wxComboPopup::OnPopup()
00358 {
00359 }
00360 
00361 void wxComboPopup::OnDismiss()
00362 {
00363 }
00364 
00365 wxSize wxComboPopup::GetAdjustedSize( int minWidth,
00366                                       int prefHeight,
00367                                       int WXUNUSED(maxHeight) )
00368 {
00369     return wxSize(minWidth,prefHeight);
00370 }
00371 
00372 void wxComboPopup::DefaultPaintComboControl( wxComboCtrlBase* combo,
00373                                              wxDC& dc, const wxRect& rect )
00374 {
00375     if ( combo->GetWindowStyle() & wxCB_READONLY ) // ie. no textctrl
00376     {
00377         combo->DrawFocusBackground(dc,rect,0);
00378 
00379         dc.DrawText( combo->GetValue(),
00380                      rect.x + combo->GetTextIndent(),
00381                      (rect.height-dc.GetCharHeight())/2 + rect.y );
00382     }
00383 }
00384 
00385 void wxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
00386 {
00387     DefaultPaintComboControl(m_combo,dc,rect);
00388 }
00389 
00390 void wxComboPopup::OnComboKeyEvent( wxKeyEvent& event )
00391 {
00392     event.Skip();
00393 }
00394 
00395 void wxComboPopup::OnComboDoubleClick()
00396 {
00397 }
00398 
00399 void wxComboPopup::SetStringValue( const wxString& WXUNUSED(value) )
00400 {
00401 }
00402 
00403 bool wxComboPopup::LazyCreate()
00404 {
00405     return false;
00406 }
00407 
00408 void wxComboPopup::Dismiss()
00409 {
00410     m_combo->HidePopup();
00411 }
00412 
00413 // ----------------------------------------------------------------------------
00414 // input handling
00415 // ----------------------------------------------------------------------------
00416 
00417 //
00418 // This is pushed to the event handler queue of either combo box
00419 // or its textctrl (latter if not readonly combo).
00420 //
00421 class wxComboBoxExtraInputHandler : public wxEvtHandler
00422 {
00423 public:
00424 
00425     wxComboBoxExtraInputHandler( wxComboCtrlBase* combo )
00426         : wxEvtHandler()
00427     {
00428         m_combo = combo;
00429     }
00430     ~wxComboBoxExtraInputHandler() { }
00431     void OnKey(wxKeyEvent& event);
00432     void OnFocus(wxFocusEvent& event);
00433 
00434 protected:
00435     wxComboCtrlBase*   m_combo;
00436 
00437 private:
00438     DECLARE_EVENT_TABLE()
00439 };
00440 
00441 
00442 BEGIN_EVENT_TABLE(wxComboBoxExtraInputHandler, wxEvtHandler)
00443     EVT_KEY_DOWN(wxComboBoxExtraInputHandler::OnKey)
00444     EVT_SET_FOCUS(wxComboBoxExtraInputHandler::OnFocus)
00445 END_EVENT_TABLE()
00446 
00447 
00448 void wxComboBoxExtraInputHandler::OnKey(wxKeyEvent& event)
00449 {
00450     int keycode = event.GetKeyCode();
00451 
00452     if ( keycode == WXK_TAB && !m_combo->IsPopupShown() )
00453     {
00454         wxNavigationKeyEvent evt;
00455         evt.SetFlags(wxNavigationKeyEvent::FromTab|
00456                      (!event.ShiftDown()?wxNavigationKeyEvent::IsForward:
00457                                          wxNavigationKeyEvent::IsBackward));
00458         evt.SetEventObject(m_combo);
00459         m_combo->GetParent()->GetEventHandler()->AddPendingEvent(evt);
00460         return;
00461     }
00462 
00463     if ( m_combo->IsPopupShown() )
00464     {
00465         // pass it to the popped up control
00466         m_combo->GetPopupControl()->GetControl()->AddPendingEvent(event);
00467     }
00468     else // no popup
00469     {
00470         int comboStyle = m_combo->GetWindowStyle();
00471         wxComboPopup* popupInterface = m_combo->GetPopupControl();
00472 
00473         if ( !popupInterface )
00474         {
00475             event.Skip();
00476             return;
00477         }
00478 
00479         if ( (comboStyle & wxCB_READONLY) ||
00480              ( keycode != WXK_RIGHT && keycode != WXK_LEFT )
00481             )
00482         {
00483             popupInterface->OnComboKeyEvent(event);
00484         }
00485         else
00486             event.Skip();
00487     }
00488 }
00489 
00490 void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event)
00491 {
00492     // FIXME: This code does run when control is clicked,
00493     //        yet on Windows it doesn't select all the text.
00494     if ( !(m_combo->GetInternalFlags() & wxCC_NO_TEXT_AUTO_SELECT) )
00495     {
00496         if ( m_combo->GetTextCtrl() )
00497             m_combo->GetTextCtrl()->SelectAll();
00498         else
00499             m_combo->SetSelection(-1,-1);
00500     }
00501 
00502     if ( event.GetId() != m_combo->GetId() )
00503     {
00504         // Add textctrl set focus events as combo set focus events
00505         // NOTE: Simply changing the event and skipping didn't seem
00506         // to do the trick.
00507         wxFocusEvent evt2(wxEVT_SET_FOCUS,m_combo->GetId());
00508         evt2.SetEventObject(m_combo);
00509         m_combo->GetEventHandler()->ProcessEvent(evt2);
00510     }
00511     else
00512         event.Skip();
00513 
00514     event.Skip();
00515 }
00516 
00517 
00518 //
00519 // This is pushed to the event handler queue of the control in popup.
00520 //
00521 
00522 class wxComboPopupExtraEventHandler : public wxEvtHandler
00523 {
00524 public:
00525 
00526     wxComboPopupExtraEventHandler( wxComboCtrlBase* combo )
00527         : wxEvtHandler()
00528     {
00529         m_combo = combo;
00530         m_beenInside = false;
00531     }
00532     ~wxComboPopupExtraEventHandler() { }
00533 
00534     void OnMouseEvent( wxMouseEvent& event );
00535 
00536     // Called from wxComboCtrlBase::OnPopupDismiss
00537     void OnPopupDismiss()
00538     {
00539         m_beenInside = false;
00540     }
00541 
00542 protected:
00543     wxComboCtrlBase*     m_combo;
00544 
00545     bool                    m_beenInside;
00546 
00547 private:
00548     DECLARE_EVENT_TABLE()
00549 };
00550 
00551 
00552 BEGIN_EVENT_TABLE(wxComboPopupExtraEventHandler, wxEvtHandler)
00553     EVT_MOUSE_EVENTS(wxComboPopupExtraEventHandler::OnMouseEvent)
00554 END_EVENT_TABLE()
00555 
00556 
00557 void wxComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event )
00558 {
00559     wxPoint pt = event.GetPosition();
00560     wxSize sz = m_combo->GetPopupControl()->GetControl()->GetClientSize();
00561     int evtType = event.GetEventType();
00562     bool isInside = pt.x >= 0 && pt.y >= 0 && pt.x < sz.x && pt.y < sz.y;
00563 
00564     if ( evtType == wxEVT_MOTION ||
00565          evtType == wxEVT_LEFT_DOWN ||
00566          evtType == wxEVT_RIGHT_DOWN )
00567     {
00568         // Block motion and click events outside the popup
00569         if ( !isInside )
00570         {
00571             event.Skip(false);
00572             return;
00573         }
00574     }
00575     else if ( evtType == wxEVT_LEFT_UP )
00576     {
00577         // Don't let left-down events in if outside
00578         if ( evtType == wxEVT_LEFT_DOWN )
00579         {
00580             if ( !isInside )
00581                 return;
00582         }
00583 
00584         if ( !m_beenInside )
00585         {
00586             if ( isInside )
00587             {
00588                 m_beenInside = true;
00589             }
00590             else
00591             {
00592                 //
00593                 // Some mouse events to popup that happen outside it, before cursor
00594                 // has been inside the popu, need to be ignored by it but relayed to
00595                 // the dropbutton.
00596                 //
00597                 wxWindow* btn = m_combo->GetButton();
00598                 if ( btn )
00599                     btn->GetEventHandler()->AddPendingEvent(event);
00600                 else
00601                     m_combo->GetEventHandler()->AddPendingEvent(event);
00602 
00603                 return;
00604             }
00605 
00606             event.Skip();
00607         }
00608     }
00609 
00610     event.Skip();
00611 }
00612 
00613 // ----------------------------------------------------------------------------
00614 // wxComboCtrlBase
00615 // ----------------------------------------------------------------------------
00616 
00617 
00618 BEGIN_EVENT_TABLE(wxComboCtrlBase, wxControl)
00619     EVT_TEXT(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
00620     EVT_SIZE(wxComboCtrlBase::OnSizeEvent)
00621     EVT_SET_FOCUS(wxComboCtrlBase::OnFocusEvent)
00622     EVT_KILL_FOCUS(wxComboCtrlBase::OnFocusEvent)
00623     //EVT_BUTTON(wxID_ANY,wxComboCtrlBase::OnButtonClickEvent)
00624     EVT_TEXT_ENTER(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
00625     EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged)
00626 END_EVENT_TABLE()
00627 
00628 
00629 IMPLEMENT_ABSTRACT_CLASS(wxComboCtrlBase, wxControl)
00630 
00631 // Have global double buffer - should be enough for multiple combos
00632 static wxBitmap* gs_doubleBuffer = (wxBitmap*) NULL;
00633 
00634 void wxComboCtrlBase::Init()
00635 {
00636     m_winPopup = (wxWindow *)NULL;
00637     m_popup = (wxWindow *)NULL;
00638     m_isPopupShown = false;
00639     m_btn = (wxWindow*) NULL;
00640     m_text = (wxTextCtrl*) NULL;
00641     m_popupInterface = (wxComboPopup*) NULL;
00642 
00643     m_extraEvtHandler = (wxEvtHandler*) NULL;
00644     m_popupExtraHandler = (wxEvtHandler*) NULL;
00645     m_textEvtHandler = (wxEvtHandler*) NULL;
00646 
00647 #if INSTALL_TOPLEV_HANDLER
00648     m_toplevEvtHandler = (wxEvtHandler*) NULL;
00649 #endif
00650 
00651     m_heightPopup = -1;
00652     m_widthMinPopup = -1;
00653     m_anchorSide = 0;
00654     m_widthCustomPaint = 0;
00655     m_widthCustomBorder = 0;
00656 
00657     m_btnState = 0;
00658     m_btnWidDefault = 0;
00659     m_blankButtonBg = false;
00660     m_btnWid = m_btnHei = -1;
00661     m_btnSide = wxRIGHT;
00662     m_btnSpacingX = 0;
00663 
00664     m_extLeft = 0;
00665     m_extRight = 0;
00666     m_absIndent = -1;
00667     m_iFlags = 0;
00668     m_downReceived = false;
00669     m_timeCanAcceptClick = 0;
00670 }
00671 
00672 bool wxComboCtrlBase::Create(wxWindow *parent,
00673                              wxWindowID id,
00674                              const wxString& value,
00675                              const wxPoint& pos,
00676                              const wxSize& size,
00677                              long style,
00678                              const wxValidator& validator,
00679                              const wxString& name)
00680 {
00681     if ( !wxControl::Create(parent,
00682                             id,
00683                             pos,
00684                             size,
00685                             style | wxWANTS_CHARS,
00686                             validator,
00687                             name) )
00688         return false;
00689 
00690     m_valueString = value;
00691 
00692     // Get colours
00693     OnThemeChange();
00694     m_absIndent = GetNativeTextIndent();
00695 
00696     m_iFlags |= wxCC_IFLAG_CREATED;
00697 
00698     // If x and y indicate valid size, wxSizeEvent won't be
00699     // emitted automatically, so we need to add artifical one.
00700     if ( size.x > 0 && size.y > 0 )
00701     {
00702         wxSizeEvent evt(size,GetId());
00703         GetEventHandler()->AddPendingEvent(evt);
00704     }
00705 
00706     return true;
00707 }
00708 
00709 void wxComboCtrlBase::InstallInputHandlers( bool alsoTextCtrl )
00710 {
00711     if ( m_text && alsoTextCtrl )
00712     {
00713         m_textEvtHandler = new wxComboBoxExtraInputHandler(this);
00714         m_text->PushEventHandler(m_textEvtHandler);
00715     }
00716 
00717     wxComboBoxExtraInputHandler* inputHandler = new wxComboBoxExtraInputHandler(this);
00718     PushEventHandler(inputHandler);
00719     m_extraEvtHandler = inputHandler;
00720 }
00721 
00722 void
00723 wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator)
00724 {
00725     if ( !(m_windowStyle & wxCB_READONLY) )
00726     {
00727         // wxTE_PROCESS_TAB is needed because on Windows, wxTAB_TRAVERSAL is
00728         // not used by the wxPropertyGrid and therefore the tab is processed by
00729         // looking at ancestors to see if they have wxTAB_TRAVERSAL. The
00730         // navigation event is then sent to the wrong window.
00731         style |= wxTE_PROCESS_TAB;
00732 
00733         if ( HasFlag(wxTE_PROCESS_ENTER) )
00734             style |= wxTE_PROCESS_ENTER;
00735 
00736         m_text = new wxTextCtrl(this, wxID_ANY, m_valueString,
00737                                 wxDefaultPosition, wxDefaultSize,
00738                                 style, validator);
00739 
00740         // This is required for some platforms (GTK+ atleast)
00741         m_text->SetSizeHints(2,4);
00742     }
00743 }
00744 
00745 void wxComboCtrlBase::OnThemeChange()
00746 {
00747     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
00748 }
00749 
00750 wxComboCtrlBase::~wxComboCtrlBase()
00751 {
00752     if ( HasCapture() )
00753         ReleaseMouse();
00754 
00755     delete gs_doubleBuffer;
00756     gs_doubleBuffer = (wxBitmap*) NULL;
00757 
00758 #if INSTALL_TOPLEV_HANDLER
00759     delete ((wxComboFrameEventHandler*)m_toplevEvtHandler);
00760     m_toplevEvtHandler = (wxEvtHandler*) NULL;
00761 #endif
00762 
00763     DestroyPopup();
00764 
00765     RemoveEventHandler(m_extraEvtHandler);
00766 
00767     if ( m_text )
00768         m_text->RemoveEventHandler(m_textEvtHandler);
00769 
00770     delete m_textEvtHandler;
00771     delete m_extraEvtHandler;
00772 }
00773 
00774 
00775 // ----------------------------------------------------------------------------
00776 // geometry stuff
00777 // ----------------------------------------------------------------------------
00778 
00779 // Recalculates button and textctrl areas
00780 void wxComboCtrlBase::CalculateAreas( int btnWidth )
00781 {
00782     wxSize sz = GetClientSize();
00783     int customBorder = m_widthCustomBorder;
00784     int btnBorder; // border for button only
00785 
00786     // check if button should really be outside the border: we'll do it it if
00787     // its platform default or bitmap+pushbutton background is used, but not if
00788     // there is vertical size adjustment or horizontal spacing.
00789     if ( ( (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) ||
00790                 (m_bmpNormal.Ok() && m_blankButtonBg) ) &&
00791          m_btnSpacingX == 0 &&
00792          m_btnHei <= 0 )
00793     {
00794         m_iFlags |= wxCC_IFLAG_BUTTON_OUTSIDE;
00795         btnBorder = 0;
00796     }
00797     else
00798     {
00799         m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE);
00800         btnBorder = customBorder;
00801     }
00802 
00803     // Defaul indentation
00804     if ( m_absIndent < 0 )
00805         m_absIndent = GetNativeTextIndent();
00806 
00807     int butWidth = btnWidth;
00808 
00809     if ( butWidth <= 0 )
00810         butWidth = m_btnWidDefault;
00811     else
00812         m_btnWidDefault = butWidth;
00813 
00814     if ( butWidth <= 0 )
00815         return;
00816 
00817     int butHeight = sz.y - btnBorder*2;
00818 
00819     // Adjust button width
00820     if ( m_btnWid > 0 )
00821         butWidth = m_btnWid;
00822     else
00823     {
00824         // Adjust button width to match aspect ratio
00825         // (but only if control is smaller than best size).
00826         int bestHeight = GetBestSize().y;
00827         int height = GetSize().y;
00828 
00829         if ( height < bestHeight )
00830         {
00831             // Make very small buttons square, as it makes
00832             // them accommodate arrow image better and still
00833             // looks decent.
00834             if ( height > 18 )
00835                 butWidth = (height*butWidth)/bestHeight;
00836             else
00837                 butWidth = butHeight;
00838         }
00839     }
00840 
00841     // Adjust button height
00842     if ( m_btnHei > 0 )
00843         butHeight = m_btnHei;
00844 
00845     // Use size of normal bitmap if...
00846     //   It is larger
00847     //   OR
00848     //   button width is set to default and blank button bg is not drawn
00849     if ( m_bmpNormal.Ok() )
00850     {
00851         int bmpReqWidth = m_bmpNormal.GetWidth();
00852         int bmpReqHeight = m_bmpNormal.GetHeight();
00853 
00854         // If drawing blank button background, we need to add some margin.
00855         if ( m_blankButtonBg )
00856         {
00857             bmpReqWidth += BMP_BUTTON_MARGIN*2;
00858             bmpReqHeight += BMP_BUTTON_MARGIN*2;
00859         }
00860 
00861         if ( butWidth < bmpReqWidth || ( m_btnWid == 0 && !m_blankButtonBg ) )
00862             butWidth = bmpReqWidth;
00863         if ( butHeight < bmpReqHeight || ( m_btnHei == 0 && !m_blankButtonBg ) )
00864             butHeight = bmpReqHeight;
00865 
00866         // Need to fix height?
00867         if ( (sz.y-(customBorder*2)) < butHeight && btnWidth == 0 )
00868         {
00869             int newY = butHeight+(customBorder*2);
00870             SetClientSize(wxDefaultCoord,newY);
00871             sz.y = newY;
00872         }
00873     }
00874 
00875     int butAreaWid = butWidth + (m_btnSpacingX*2);
00876 
00877     m_btnSize.x = butWidth;
00878     m_btnSize.y = butHeight;
00879 
00880     m_btnArea.x = ( m_btnSide==wxRIGHT ? sz.x - butAreaWid - btnBorder : btnBorder );
00881     m_btnArea.y = btnBorder;
00882     m_btnArea.width = butAreaWid;
00883     m_btnArea.height = sz.y - (btnBorder*2);
00884 
00885     m_tcArea.x = ( m_btnSide==wxRIGHT ? 0 : butAreaWid ) + customBorder;
00886     m_tcArea.y = customBorder;
00887     m_tcArea.width = sz.x - butAreaWid - (customBorder*2);
00888     m_tcArea.height = sz.y - (customBorder*2);
00889 
00890 /*
00891     if ( m_text )
00892     {
00893         ::wxMessageBox(wxString::Format(wxT("ButtonArea (%i,%i,%i,%i)\n"),m_btnArea.x,m_btnArea.y,m_btnArea.width,m_btnArea.height) +
00894                        wxString::Format(wxT("TextCtrlArea (%i,%i,%i,%i)"),m_tcArea.x,m_tcArea.y,m_tcArea.width,m_tcArea.height));
00895     }
00896 */
00897 }
00898 
00899 void wxComboCtrlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust )
00900 {
00901     if ( !m_text )
00902         return;
00903 
00904     wxSize sz = GetClientSize();
00905     int customBorder = m_widthCustomBorder;
00906 
00907     if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER )
00908     {
00909         // Centre textctrl
00910         int tcSizeY = m_text->GetBestSize().y;
00911         int diff = sz.y - tcSizeY;
00912         int y = textCtrlYAdjust + (diff/2);
00913 
00914         if ( y < customBorder )
00915             y = customBorder;
00916 
00917         m_text->SetSize( m_tcArea.x + m_widthCustomPaint + m_absIndent + textCtrlXAdjust,
00918                          y,
00919                          m_tcArea.width - COMBO_MARGIN -
00920                          (textCtrlXAdjust + m_widthCustomPaint + m_absIndent),
00921                          -1 );
00922 
00923         // Make sure textctrl doesn't exceed the bottom custom border
00924         wxSize tsz = m_text->GetSize();
00925         diff = (y + tsz.y) - (sz.y - customBorder);
00926         if ( diff >= 0 )
00927         {
00928             tsz.y = tsz.y - diff - 1;
00929             m_text->SetSize(tsz);
00930         }
00931     }
00932     else
00933     {
00934         m_text->SetSize( m_tcArea.x,
00935                          0,
00936                          sz.x - m_btnArea.x - m_widthCustomPaint - customBorder,
00937                          sz.y );
00938     }
00939 }
00940 
00941 wxSize wxComboCtrlBase::DoGetBestSize() const
00942 {
00943     wxSize sizeText(150,0);
00944 
00945     if ( m_text )
00946         sizeText = m_text->GetBestSize();
00947 
00948     // TODO: Better method to calculate close-to-native control height.
00949 
00950     int fhei;
00951     if ( m_font.Ok() )
00952         fhei = (m_font.GetPointSize()*2) + 5;
00953     else if ( wxNORMAL_FONT->Ok() )
00954         fhei = (wxNORMAL_FONT->GetPointSize()*2) + 5;
00955     else
00956         fhei = sizeText.y + 4;
00957 
00958     // Need to force height to accomodate bitmap?
00959     int btnSizeY = m_btnSize.y;
00960     if ( m_bmpNormal.Ok() && fhei < btnSizeY )
00961         fhei = btnSizeY;
00962 
00963     // Control height doesn't depend on border
00964 /*
00965     // Add border
00966     int border = m_windowStyle & wxBORDER_MASK;
00967     if ( border == wxSIMPLE_BORDER )
00968         fhei += 2;
00969     else if ( border == wxNO_BORDER )
00970         fhei += (m_widthCustomBorder*2);
00971     else
00972         // Sunken etc.
00973         fhei += 4;
00974 */
00975 
00976     // Final adjustments
00977 #ifdef __WXGTK__
00978     fhei += 1;
00979 #endif
00980 
00981     wxSize ret(sizeText.x + COMBO_MARGIN + DEFAULT_DROPBUTTON_WIDTH,
00982                fhei);
00983 
00984     CacheBestSize(ret);
00985     return ret;
00986 }
00987 
00988 void wxComboCtrlBase::OnSizeEvent( wxSizeEvent& event )
00989 {
00990     if ( !IsCreated() )
00991         return;
00992 
00993     // defined by actual wxComboCtrls
00994     OnResize();
00995 
00996     event.Skip();
00997 }
00998 
00999 // ----------------------------------------------------------------------------
01000 // standard operations
01001 // ----------------------------------------------------------------------------
01002 
01003 bool wxComboCtrlBase::Enable(bool enable)
01004 {
01005     if ( !wxControl::Enable(enable) )
01006         return false;
01007 
01008     if ( m_btn )
01009         m_btn->Enable(enable);
01010     if ( m_text )
01011         m_text->Enable(enable);
01012 
01013     return true;
01014 }
01015 
01016 bool wxComboCtrlBase::Show(bool show)
01017 {
01018     if ( !wxControl::Show(show) )
01019         return false;
01020 
01021     if (m_btn)
01022         m_btn->Show(show);
01023 
01024     if (m_text)
01025         m_text->Show(show);
01026 
01027     return true;
01028 }
01029 
01030 bool wxComboCtrlBase::SetFont ( const wxFont& font )
01031 {
01032     if ( !wxControl::SetFont(font) )
01033         return false;
01034 
01035     if (m_text)
01036         m_text->SetFont(font);
01037 
01038     return true;
01039 }
01040 
01041 #if wxUSE_TOOLTIPS
01042 void wxComboCtrlBase::DoSetToolTip(wxToolTip *tooltip)
01043 {
01044     wxControl::DoSetToolTip(tooltip);
01045 
01046     // Set tool tip for button and text box
01047     if ( tooltip )
01048     {
01049         const wxString &tip = tooltip->GetTip();
01050         if ( m_text ) m_text->SetToolTip(tip);
01051         if ( m_btn ) m_btn->SetToolTip(tip);
01052     }
01053     else
01054     {
01055         if ( m_text ) m_text->SetToolTip( (wxToolTip*) NULL );
01056         if ( m_btn ) m_btn->SetToolTip( (wxToolTip*) NULL );
01057     }
01058 }
01059 #endif // wxUSE_TOOLTIPS
01060 
01061 // ----------------------------------------------------------------------------
01062 // painting
01063 // ----------------------------------------------------------------------------
01064 
01065 // draw focus background on area in a way typical on platform
01066 void wxComboCtrlBase::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags ) const
01067 {
01068     wxSize sz = GetClientSize();
01069     bool isEnabled;
01070     bool isFocused; // also selected
01071 
01072     // For smaller size control (and for disabled background) use less spacing
01073     int focusSpacingX;
01074     int focusSpacingY;
01075 
01076     if ( !(flags & wxCONTROL_ISSUBMENU) )
01077     {
01078         // Drawing control
01079         isEnabled = IsEnabled();
01080         isFocused = ShouldDrawFocus();
01081 
01082         // Windows-style: for smaller size control (and for disabled background) use less spacing
01083         focusSpacingX = isEnabled ? 2 : 1;
01084         focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
01085     }
01086     else
01087     {
01088         // Drawing a list item
01089         isEnabled = true; // they are never disabled
01090         isFocused = flags & wxCONTROL_SELECTED ? true : false;
01091 
01092         focusSpacingX = 0;
01093         focusSpacingY = 0;
01094     }
01095 
01096     // Set the background sub-rectangle for selection, disabled etc
01097     wxRect selRect(rect);
01098     selRect.y += focusSpacingY;
01099     selRect.height -= (focusSpacingY*2);
01100 
01101     int wcp = 0;
01102 
01103     if ( !(flags & wxCONTROL_ISSUBMENU) )
01104         wcp += m_widthCustomPaint;
01105 
01106     selRect.x += wcp + focusSpacingX;
01107     selRect.width -= wcp + (focusSpacingX*2);
01108 
01109     wxColour bgCol;
01110 
01111     if ( isEnabled )
01112     {
01113         // If popup is hidden and this control is focused,
01114         // then draw the focus-indicator (selbgcolor background etc.).
01115         if ( isFocused )
01116         {
01117             dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
01118             bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
01119         }
01120         else
01121         {
01122             dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
01123             bgCol = GetBackgroundColour();
01124         }
01125     }
01126     else
01127     {
01128         dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
01129         bgCol = GetBackgroundColour();
01130     }
01131 
01132     dc.SetBrush( bgCol );
01133     dc.SetPen( bgCol );
01134     dc.DrawRectangle( selRect );
01135 }
01136 
01137 void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, bool paintBg )
01138 {
01139     int drawState = m_btnState;
01140 
01141 #ifdef __WXGTK__
01142     if ( m_isPopupShown )
01143         drawState |= wxCONTROL_PRESSED;
01144 #endif
01145 
01146     wxRect drawRect(rect.x+m_btnSpacingX,
01147                     rect.y+((rect.height-m_btnSize.y)/2),
01148                     m_btnSize.x,
01149                     m_btnSize.y);
01150 
01151     // Make sure area is not larger than the control
01152     if ( drawRect.y < rect.y )
01153         drawRect.y = rect.y;
01154     if ( drawRect.height > rect.height )
01155         drawRect.height = rect.height;
01156 
01157     bool enabled = IsEnabled();
01158 
01159     if ( !enabled )
01160         drawState |= wxCONTROL_DISABLED;
01161 
01162     if ( !m_bmpNormal.Ok() )
01163     {
01164         // Need to clear button background even if m_btn is present
01165         if ( paintBg )
01166         {
01167             wxColour bgCol;
01168 
01169             if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
01170                 bgCol = GetParent()->GetBackgroundColour();
01171             else
01172                 bgCol = GetBackgroundColour();
01173 
01174             dc.SetBrush(bgCol);
01175             dc.SetPen(bgCol);
01176             dc.DrawRectangle(rect);
01177         }
01178 
01179         // Draw standard button
01180         wxRendererNative::Get().DrawComboBoxDropButton(this,
01181                                                        dc,
01182                                                        drawRect,
01183                                                        drawState);
01184     }
01185     else
01186     {
01187         // Draw bitmap
01188 
01189         wxBitmap* pBmp;
01190 
01191         if ( !enabled )
01192             pBmp = &m_bmpDisabled;
01193         else if ( m_btnState & wxCONTROL_PRESSED )
01194             pBmp = &m_bmpPressed;
01195         else if ( m_btnState & wxCONTROL_CURRENT )
01196             pBmp = &m_bmpHover;
01197         else
01198             pBmp = &m_bmpNormal;
01199 
01200         if ( m_blankButtonBg )
01201         {
01202             // If using blank button background, we need to clear its background
01203             // with button face colour instead of colour for rest of the control.
01204             if ( paintBg )
01205             {
01206                 wxColour bgCol = GetParent()->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
01207                 //wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
01208                 dc.SetPen(bgCol);
01209                 dc.SetBrush(bgCol);
01210                 dc.DrawRectangle(rect);
01211             }
01212 #if 0
01213             wxRendererNative::Get().DrawPushButton(this,
01214                                                    dc,
01215                                                    drawRect,
01216                                                    drawState);
01217 #endif
01218         }
01219         else
01220 
01221         {
01222             // Need to clear button background even if m_btn is present
01223             // (assume non-button background was cleared just before this call so brushes are good)
01224             if ( paintBg )
01225                 dc.DrawRectangle(rect);
01226         }
01227 
01228         // Draw bitmap centered in drawRect
01229         dc.DrawBitmap(*pBmp,
01230                       drawRect.x + (drawRect.width-pBmp->GetWidth())/2,
01231                       drawRect.y + (drawRect.height-pBmp->GetHeight())/2,
01232                       true);
01233     }
01234 }
01235 
01236 void wxComboCtrlBase::RecalcAndRefresh()
01237 {
01238     if ( IsCreated() )
01239     {
01240         wxSizeEvent evt(GetSize(),GetId());
01241         GetEventHandler()->ProcessEvent(evt);
01242         Refresh();
01243     }
01244 }
01245 
01246 wxBitmap& wxComboCtrlBase::GetBufferBitmap( const wxSize& sz ) const
01247 {
01248     // If size is larger, recalculate double buffer bitmap
01249     if ( !gs_doubleBuffer ||
01250          sz.x > gs_doubleBuffer->GetWidth() ||
01251          sz.y > gs_doubleBuffer->GetHeight() )
01252     {
01253         delete gs_doubleBuffer;
01254         gs_doubleBuffer = new wxBitmap(sz.x+25,sz.y);
01255     }
01256     return *gs_doubleBuffer;
01257 }
01258 
01259 // ----------------------------------------------------------------------------
01260 // miscellaneous event handlers
01261 // ----------------------------------------------------------------------------
01262 
01263 void wxComboCtrlBase::OnTextCtrlEvent(wxCommandEvent& event)
01264 {
01265     // Change event id, object and string before relaying it forward
01266     event.SetId(GetId());
01267     wxString s = event.GetString();
01268     event.SetEventObject(this);
01269     event.SetString(s);
01270     event.Skip();
01271 }
01272 
01273 // call if cursor is on button area or mouse is captured for the button
01274 bool wxComboCtrlBase::HandleButtonMouseEvent( wxMouseEvent& event,
01275                                                  int flags )
01276 {
01277     int type = event.GetEventType();
01278 
01279     if ( type == wxEVT_MOTION )
01280     {
01281         if ( flags & wxCC_MF_ON_BUTTON )
01282         {
01283             if ( !(m_btnState & wxCONTROL_CURRENT) )
01284             {
01285                 // Mouse hover begins
01286                 m_btnState |= wxCONTROL_CURRENT;
01287                 if ( HasCapture() ) // Retain pressed state.
01288                     m_btnState |= wxCONTROL_PRESSED;
01289                 Refresh();
01290             }
01291         }
01292         else if ( (m_btnState & wxCONTROL_CURRENT) )
01293         {
01294             // Mouse hover ends
01295             m_btnState &= ~(wxCONTROL_CURRENT|wxCONTROL_PRESSED);
01296             Refresh();
01297         }
01298     }
01299     else if ( type == wxEVT_LEFT_DOWN )
01300     {
01301         // Only accept event if it wasn't right after popup dismiss
01302         //if ( ::wxGetLocalTimeMillis() > m_timeCanClick )
01303         {
01304             // Need to test this, because it might be outside.
01305             if ( flags & wxCC_MF_ON_BUTTON )
01306             {
01307                 m_btnState |= wxCONTROL_PRESSED;
01308                 Refresh();
01309 
01310                 if ( !(m_iFlags & wxCC_POPUP_ON_MOUSE_UP) )
01311                     OnButtonClick();
01312                 else
01313                     // If showing popup now, do not capture mouse or there will be interference
01314                     CaptureMouse();
01315             }
01316         }
01317         /*else
01318         {
01319             m_btnState = 0;
01320         }*/
01321     }
01322     else if ( type == wxEVT_LEFT_UP )
01323     {
01324 
01325         // Only accept event if mouse was left-press was previously accepted
01326         if ( HasCapture() )
01327             ReleaseMouse();
01328 
01329         if ( m_btnState & wxCONTROL_PRESSED )
01330         {
01331             // If mouse was inside, fire the click event.
01332             if ( m_iFlags & wxCC_POPUP_ON_MOUSE_UP )
01333             {
01334                 if ( flags & wxCC_MF_ON_BUTTON )
01335                     OnButtonClick();
01336             }
01337 
01338             m_btnState &= ~(wxCONTROL_PRESSED);
01339             Refresh();
01340         }
01341     }
01342     else if ( type == wxEVT_LEAVE_WINDOW )
01343     {
01344         if ( m_btnState & (wxCONTROL_CURRENT|wxCONTROL_PRESSED) )
01345         {
01346             m_btnState &= ~(wxCONTROL_CURRENT);
01347 
01348             // Mouse hover ends
01349             if ( !m_isPopupShown )
01350             {
01351                 m_btnState &= ~(wxCONTROL_PRESSED);
01352                 Refresh();
01353             }
01354         }
01355     }
01356     else
01357         return false;
01358 
01359     return true;
01360 }
01361 
01362 // Conversion to double-clicks and some basic filtering
01363 // returns true if event was consumed or filtered
01364 //bool wxComboCtrlBase::PreprocessMouseEvent( wxMouseEvent& event, bool isOnButtonArea )
01365 bool wxComboCtrlBase::PreprocessMouseEvent( wxMouseEvent& event,
01366                                                int flags )
01367 {
01368     wxLongLong t = ::wxGetLocalTimeMillis();
01369     int evtType = event.GetEventType();
01370 
01371     //
01372     // Generate our own double-clicks
01373     // (to allow on-focus dc-event on double-clicks instead of triple-clicks)
01374     if ( (m_windowStyle & wxCC_SPECIAL_DCLICK) &&
01375          !m_isPopupShown &&
01377          !(flags & wxCC_MF_ON_BUTTON) )
01378     {
01379         if ( evtType == wxEVT_LEFT_DOWN )
01380         {
01381             // Set value to avoid up-events without corresponding downs
01382             m_downReceived = true;
01383         }
01384         else if ( evtType == wxEVT_LEFT_DCLICK )
01385         {
01386             // We'll make our own double-clicks
01387             //evtType = 0;
01388             event.SetEventType(0);
01389             return true;
01390         }
01391         else if ( evtType == wxEVT_LEFT_UP )
01392         {
01393             if ( m_downReceived || m_timeLastMouseUp == 1 )
01394             {
01395                 wxLongLong timeFromLastUp = (t-m_timeLastMouseUp);
01396 
01397                 if ( timeFromLastUp < DOUBLE_CLICK_CONVERSION_TRESHOLD )
01398                 {
01399                     //type = wxEVT_LEFT_DCLICK;
01400                     event.SetEventType(wxEVT_LEFT_DCLICK);
01401                     m_timeLastMouseUp = 1;
01402                 }
01403                 else
01404                 {
01405                     m_timeLastMouseUp = t;
01406                 }
01407 
01408                 //m_downReceived = false;
01409             }
01410         }
01411     }
01412 
01413     // Filter out clicks on button immediately after popup dismiss (Windows like behaviour)
01414     if ( evtType == wxEVT_LEFT_DOWN && t < m_timeCanAcceptClick )
01415     {
01416         event.SetEventType(0);
01417         return true;
01418     }
01419 
01420     return false;
01421 }
01422 
01423 void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event )
01424 {
01425     int evtType = event.GetEventType();
01426 
01427     if ( (evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_LEFT_DCLICK) &&
01428          (m_windowStyle & wxCB_READONLY) )
01429     {
01430         if ( m_isPopupShown )
01431         {
01432     #if !wxUSE_POPUPWIN
01433         // Normally do nothing - evt handler should close it for us
01434     #elif !USE_TRANSIENT_POPUP
01435             // Click here always hides the popup.
01436             HidePopup();
01437     #endif
01438         }
01439         else
01440         {
01441             if ( !(m_windowStyle & wxCC_SPECIAL_DCLICK) )
01442             {
01443                 // In read-only mode, clicking the text is the
01444                 // same as clicking the button.
01445                 OnButtonClick();
01446             }
01447             else if ( /*evtType == wxEVT_LEFT_UP || */evtType == wxEVT_LEFT_DCLICK )
01448             {
01449                 //if ( m_popupInterface->CycleValue() )
01450                 //    Refresh();
01451                 if ( m_popupInterface )
01452                     m_popupInterface->OnComboDoubleClick();
01453             }
01454         }
01455     }
01456     else
01457     if ( m_isPopupShown )
01458     {
01459         // relay (some) mouse events to the popup
01460         if ( evtType == wxEVT_MOUSEWHEEL )
01461             m_popup->AddPendingEvent(event);
01462     }
01463     else if ( evtType )
01464         event.Skip();
01465 }
01466 
01467 void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& )
01468 {
01469     // First click is the first part of double-click
01470     // Some platforms don't generate down-less mouse up-event
01471     // (Windows does, GTK+2 doesn't), so that's why we have
01472     // to do this.
01473     m_timeLastMouseUp = ::wxGetLocalTimeMillis();
01474 
01475     if ( m_text )
01476     {
01477         m_text->SetFocus();
01478     }
01479     else
01480         // no need to check for m_widthCustomPaint - that
01481         // area never gets special handling when selected
01482         // (in writable mode, that is)
01483         Refresh();
01484 }
01485 
01486 void wxComboCtrlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
01487 {
01488     OnThemeChange();
01489     // indentation may also have changed
01490     if ( !(m_iFlags & wxCC_IFLAG_INDENT_SET) )
01491         m_absIndent = GetNativeTextIndent();
01492     RecalcAndRefresh();
01493 }
01494 
01495 // ----------------------------------------------------------------------------
01496 // popup handling
01497 // ----------------------------------------------------------------------------
01498 
01499 // Create popup window and the child control
01500 void wxComboCtrlBase::CreatePopup()
01501 {
01502     wxComboPopup* popupInterface = m_popupInterface;
01503     wxWindow* popup;
01504 
01505     if ( !m_winPopup )
01506         m_winPopup = new wxComboPopupWindow( this, wxNO_BORDER );
01507 
01508     popupInterface->Create(m_winPopup);
01509     m_popup = popup = popupInterface->GetControl();
01510 
01511     m_popupExtraHandler = new wxComboPopupExtraEventHandler(this);
01512     popup->PushEventHandler( m_popupExtraHandler );
01513 
01514     // This may be helpful on some platforms
01515     //   (eg. it bypasses a wxGTK popupwindow bug where
01516     //    window is not initially hidden when it should be)
01517     m_winPopup->Hide();
01518 
01519     popupInterface->m_iFlags |= wxCP_IFLAG_CREATED;
01520 }
01521 
01522 // Destroy popup window and the child control
01523 void wxComboCtrlBase::DestroyPopup()
01524 {
01525     if ( m_popup )
01526         m_popup->RemoveEventHandler(m_popupExtraHandler);
01527 
01528     delete m_popupExtraHandler;
01529 
01530     HidePopup();
01531 
01532     delete m_popupInterface;
01533 
01534     if ( m_winPopup )
01535         m_winPopup->Destroy();
01536 
01537     m_popupExtraHandler = (wxEvtHandler*) NULL;
01538     m_popupInterface = (wxComboPopup*) NULL;
01539     m_winPopup = (wxWindow*) NULL;
01540     m_popup = (wxWindow*) NULL;
01541 }
01542 
01543 void wxComboCtrlBase::DoSetPopupControl(wxComboPopup* iface)
01544 {
01545     wxCHECK_RET( iface, wxT("no popup interface set for wxComboCtrl") );
01546 
01547     DestroyPopup();
01548 
01549     iface->InitBase(this);
01550     iface->Init();
01551 
01552     m_popupInterface = iface;
01553 
01554     if ( !iface->LazyCreate() )
01555     {
01556         CreatePopup();
01557     }
01558     else
01559     {
01560         m_popup = (wxWindow*) NULL;
01561     }
01562 
01563     // This must be done after creation
01564     if ( m_valueString.length() )
01565     {
01566         iface->SetStringValue(m_valueString);
01567         //Refresh();
01568     }
01569 }
01570 
01571 // Ensures there is atleast the default popup
01572 void wxComboCtrlBase::EnsurePopupControl()
01573 {
01574     if ( !m_popupInterface )
01575         SetPopupControl(NULL);
01576 }
01577 
01578 void wxComboCtrlBase::OnButtonClick()
01579 {
01580     // Derived classes can override this method for totally custom
01581     // popup action
01582     ShowPopup();
01583 }
01584 
01585 void wxComboCtrlBase::ShowPopup()
01586 {
01587     EnsurePopupControl();
01588     wxCHECK_RET( !IsPopupShown(), wxT("popup window already shown") );
01589 
01590     SetFocus();
01591 
01592     // Space above and below
01593     int screenHeight;
01594     wxPoint scrPos;
01595     int spaceAbove;
01596     int spaceBelow;
01597     int maxHeightPopup;
01598     wxSize ctrlSz = GetSize();
01599 
01600     screenHeight = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y );
01601     scrPos = GetParent()->ClientToScreen(GetPosition());
01602 
01603     spaceAbove = scrPos.y;
01604     spaceBelow = screenHeight - spaceAbove - ctrlSz.y;
01605 
01606     maxHeightPopup = spaceBelow;
01607     if ( spaceAbove > spaceBelow )
01608         maxHeightPopup = spaceAbove;
01609 
01610     // Width
01611     int widthPopup = ctrlSz.x + m_extLeft + m_extRight;
01612 
01613     if ( widthPopup < m_widthMinPopup )
01614         widthPopup = m_widthMinPopup;
01615 
01616     wxWindow* winPopup = m_winPopup;
01617     wxWindow* popup;
01618 
01619     // Need to disable tab traversal of parent
01620     //
01621     // NB: This is to fix a bug in wxMSW. In theory it could also be fixed
01622     //     by, for instance, adding check to window.cpp:wxWindowMSW::MSWProcessMessage
01623     //     that if transient popup is open, then tab traversal is to be ignored.
01624     //     However, I think this code would still be needed for cases where
01625     //     transient popup doesn't work yet (wxWinCE?).
01626     wxWindow* parent = GetParent();
01627     int parentFlags = parent->GetWindowStyle();
01628     if ( parentFlags & wxTAB_TRAVERSAL )
01629     {
01630         parent->SetWindowStyle( parentFlags & ~(wxTAB_TRAVERSAL) );
01631         m_iFlags |= wxCC_IFLAG_PARENT_TAB_TRAVERSAL;
01632     }
01633 
01634     if ( !winPopup )
01635     {
01636         CreatePopup();
01637         winPopup = m_winPopup;
01638         popup = m_popup;
01639     }
01640     else
01641     {
01642         popup = m_popup;
01643     }
01644 
01645     wxASSERT( !m_popup || m_popup == popup ); // Consistency check.
01646 
01647     wxSize adjustedSize = m_popupInterface->GetAdjustedSize(widthPopup,
01648                                                             m_heightPopup<=0?DEFAULT_POPUP_HEIGHT:m_heightPopup,
01649                                                             maxHeightPopup);
01650 
01651     popup->SetSize(adjustedSize);
01652     popup->Move(0,0);
01653     m_popupInterface->OnPopup();
01654 
01655     //
01656     // Reposition and resize popup window
01657     //
01658 
01659     wxSize szp = popup->GetSize();
01660 
01661     int popupX;
01662     int popupY = scrPos.y + ctrlSz.y;
01663 
01664     // Default anchor is wxLEFT
01665     int anchorSide = m_anchorSide;
01666     if ( !anchorSide )
01667         anchorSide = wxLEFT;
01668 
01669     int rightX = scrPos.x + ctrlSz.x + m_extRight - szp.x;
01670     int leftX = scrPos.x - m_extLeft;
01671     int screenWidth = wxSystemSettings::GetMetric( wxSYS_SCREEN_X );
01672 
01673     // If there is not enough horizontal space, anchor on the other side.
01674     // If there is no space even then, place the popup at x 0.
01675     if ( anchorSide == wxRIGHT )
01676     {
01677         if ( rightX < 0 )
01678         {
01679             if ( (leftX+szp.x) < screenWidth )
01680                 anchorSide = wxLEFT;
01681             else
01682                 anchorSide = 0;
01683         }
01684     }
01685     else
01686     {
01687         if ( (leftX+szp.x) >= screenWidth )
01688         {
01689             if ( rightX >= 0 )
01690                 anchorSide = wxRIGHT;
01691             else
01692                 anchorSide = 0;
01693         }
01694     }
01695 
01696     // Select x coordinate according to the anchor side
01697     if ( anchorSide == wxRIGHT )
01698         popupX = rightX;
01699     else if ( anchorSide == wxLEFT )
01700         popupX = leftX;
01701     else
01702         popupX = 0;
01703 
01704     if ( spaceBelow < szp.y )
01705     {
01706         popupY = scrPos.y - szp.y;
01707     }
01708 
01709     // Move to position
01710     //wxLogDebug(wxT("popup scheduled position1: %i,%i"),ptp.x,ptp.y);
01711     //wxLogDebug(wxT("popup position1: %i,%i"),winPopup->GetPosition().x,winPopup->GetPosition().y);
01712 
01713     // Some platforms (GTK) may need these two to be separate
01714     winPopup->SetSize( szp.x, szp.y );
01715     winPopup->Move( popupX, popupY );
01716 
01717     //wxLogDebug(wxT("popup position2: %i,%i"),winPopup->GetPosition().x,winPopup->GetPosition().y);
01718 
01719     m_popup = popup;
01720 
01721     // Set string selection (must be this way instead of SetStringSelection)
01722     if ( m_text )
01723     {
01724         if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
01725             m_text->SelectAll();
01726 
01727         m_popupInterface->SetStringValue( m_text->GetValue() );
01728     }
01729     else
01730     {
01731         // This is neede since focus/selection indication may change when popup is shown
01732         Refresh();
01733     }
01734 
01735     // This must be after SetStringValue
01736     m_isPopupShown = true;
01737 
01738     // Show it
01739 #if USE_TRANSIENT_POPUP
01740     ((wxPopupTransientWindow*)winPopup)->Popup(popup);
01741 #else
01742     winPopup->Show();
01743 #endif
01744 
01745 #if INSTALL_TOPLEV_HANDLER
01746     // Put top level window event handler into place
01747     if ( !m_toplevEvtHandler )
01748         m_toplevEvtHandler = new wxComboFrameEventHandler(this);
01749 
01750     wxWindow* toplev = ::wxGetTopLevelParent( this );
01751     wxASSERT( toplev );
01752     ((wxComboFrameEventHandler*)m_toplevEvtHandler)->OnPopup();
01753     toplev->PushEventHandler( m_toplevEvtHandler );
01754 #endif
01755 
01756 }
01757 
01758 void wxComboCtrlBase::OnPopupDismiss()
01759 {
01760     // Just in case, avoid double dismiss
01761     if ( !m_isPopupShown )
01762         return;
01763 
01764     // *Must* set this before focus etc.
01765     m_isPopupShown = false;
01766 
01767     // Inform popup control itself
01768     m_popupInterface->OnDismiss();
01769 
01770     if ( m_popupExtraHandler )
01771         ((wxComboPopupExtraEventHandler*)m_popupExtraHandler)->OnPopupDismiss();
01772 
01773 #if INSTALL_TOPLEV_HANDLER
01774     // Remove top level window event handler
01775     if ( m_toplevEvtHandler )
01776     {
01777         wxWindow* toplev = ::wxGetTopLevelParent( this );
01778         if ( toplev )
01779             toplev->RemoveEventHandler( m_toplevEvtHandler );
01780     }
01781 #endif
01782 
01783     m_timeCanAcceptClick = ::wxGetLocalTimeMillis() + 150;
01784 
01785     // If cursor not on dropdown button, then clear its state
01786     // (technically not required by all ports, but do it for all just in case)
01787     if ( !m_btnArea.Inside(ScreenToClient(::wxGetMousePosition())) )
01788         m_btnState = 0;
01789 
01790     // Return parent's tab traversal flag.
01791     // See ShowPopup for notes.
01792     if ( m_iFlags & wxCC_IFLAG_PARENT_TAB_TRAVERSAL )
01793     {
01794         wxWindow* parent = GetParent();
01795         parent->SetWindowStyle( parent->GetWindowStyle() | wxTAB_TRAVERSAL );
01796         m_iFlags &= ~(wxCC_IFLAG_PARENT_TAB_TRAVERSAL);
01797     }
01798 
01799     // refresh control (necessary even if m_text)
01800     Refresh();
01801 
01802 #if !wxUSE_POPUPWIN
01803     SetFocus();
01804 #endif
01805 
01806 }
01807 
01808 void wxComboCtrlBase::HidePopup()
01809 {
01810     // Should be able to call this without popup interface
01811     //wxCHECK_RET( m_popupInterface, _T("no popup interface") );
01812     if ( !m_isPopupShown )
01813         return;
01814 
01815     // transfer value and show it in textctrl, if any
01816     SetValue( m_popupInterface->GetStringValue() );
01817 
01818 #if USE_TRANSIENT_POPUP
01819     ((wxPopupTransientWindow*)m_winPopup)->Dismiss();
01820 #else
01821     m_winPopup->Hide();
01822 #endif
01823 
01824     OnPopupDismiss();
01825 }
01826 
01827 // ----------------------------------------------------------------------------
01828 // customization methods
01829 // ----------------------------------------------------------------------------
01830 
01831 void wxComboCtrlBase::SetButtonPosition( int width, int height,
01832                                          int side, int spacingX )
01833 {
01834     m_btnWid = width;
01835     m_btnHei = height;
01836     m_btnSide = side;
01837     m_btnSpacingX = spacingX;
01838 
01839     RecalcAndRefresh();
01840 }
01841 
01842 wxSize wxComboCtrlBase::GetButtonSize()
01843 {
01844     if ( m_btnSize.x > 0 )
01845         return m_btnSize;
01846 
01847     wxSize retSize(m_btnWid,m_btnHei);
01848 
01849     // Need to call CalculateAreas now if button size is
01850     // is not explicitly specified.
01851     if ( retSize.x <= 0 || retSize.y <= 0)
01852     {
01853         OnResize();
01854 
01855         retSize = m_btnSize;
01856     }
01857 
01858     return retSize;
01859 }
01860 
01861 void wxComboCtrlBase::SetButtonBitmaps( const wxBitmap& bmpNormal,
01862                                            bool blankButtonBg,
01863                                            const wxBitmap& bmpPressed,
01864                                            const wxBitmap& bmpHover,
01865                                            const wxBitmap& bmpDisabled )
01866 {
01867     m_bmpNormal = bmpNormal;
01868     m_blankButtonBg = blankButtonBg;
01869 
01870     if ( bmpPressed.Ok() )
01871         m_bmpPressed = bmpPressed;
01872     else
01873         m_bmpPressed = bmpNormal;
01874 
01875     if ( bmpHover.Ok() )
01876         m_bmpHover = bmpHover;
01877     else
01878         m_bmpHover = bmpNormal;
01879 
01880     if ( bmpDisabled.Ok() )
01881         m_bmpDisabled = bmpDisabled;
01882     else
01883         m_bmpDisabled = bmpNormal;
01884 
01885     RecalcAndRefresh();
01886 }
01887 
01888 void wxComboCtrlBase::SetCustomPaintWidth( int width )
01889 {
01890     if ( m_text )
01891     {
01892         // move textctrl accordingly
01893         wxRect r = m_text->GetRect();
01894         int inc = width - m_widthCustomPaint;
01895         r.x += inc;
01896         r.width -= inc;
01897         m_text->SetSize( r );
01898     }
01899 
01900     m_widthCustomPaint = width;
01901 
01902     RecalcAndRefresh();
01903 }
01904 
01905 void wxComboCtrlBase::SetTextIndent( int indent )
01906 {
01907     if ( indent < 0 )
01908     {
01909         m_absIndent = GetNativeTextIndent();
01910         m_iFlags &= ~(wxCC_IFLAG_INDENT_SET);
01911     }
01912     else
01913     {
01914         m_absIndent = indent;
01915         m_iFlags |= wxCC_IFLAG_INDENT_SET;
01916     }
01917 
01918     RecalcAndRefresh();
01919 }
01920 
01921 wxCoord wxComboCtrlBase::GetNativeTextIndent() const
01922 {
01923     return DEFAULT_TEXT_INDENT;
01924 }
01925 
01926 // ----------------------------------------------------------------------------
01927 // methods forwarded to wxTextCtrl
01928 // ----------------------------------------------------------------------------
01929 
01930 wxString wxComboCtrlBase::GetValue() const
01931 {
01932     if ( m_text )
01933         return m_text->GetValue();
01934     return m_valueString;
01935 }
01936 
01937 void wxComboCtrlBase::SetValue(const wxString& value)
01938 {
01939     if ( m_text )
01940     {
01941         m_text->SetValue(value);
01942         if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
01943             m_text->SelectAll();
01944     }
01945 
01946     m_valueString = value;
01947 
01948     Refresh();
01949 
01950     // Since wxComboPopup may want to paint the combo as well, we need
01951     // to set the string value here (as well as sometimes in ShowPopup).
01952     if ( m_valueString != value && m_popupInterface )
01953     {
01954         m_popupInterface->SetStringValue(value);
01955     }
01956 }
01957 
01958 // In this SetValue variant wxComboPopup::SetStringValue is not called
01959 void wxComboCtrlBase::SetText(const wxString& value)
01960 {
01961     // Unlike in SetValue(), this must be called here or
01962     // the behaviour will no be consistent in readonlys.
01963     EnsurePopupControl();
01964 
01965     m_valueString = value;
01966 
01967     Refresh();
01968 }
01969 
01970 void wxComboCtrlBase::Copy()
01971 {
01972     if ( m_text )
01973         m_text->Copy();
01974 }
01975 
01976 void wxComboCtrlBase::Cut()
01977 {
01978     if ( m_text )
01979         m_text->Cut();
01980 }
01981 
01982 void wxComboCtrlBase::Paste()
01983 {
01984     if ( m_text )
01985         m_text->Paste();
01986 }
01987 
01988 void wxComboCtrlBase::SetInsertionPoint(long pos)
01989 {
01990     if ( m_text )
01991         m_text->SetInsertionPoint(pos);
01992 }
01993 
01994 void wxComboCtrlBase::SetInsertionPointEnd()
01995 {
01996     if ( m_text )
01997         m_text->SetInsertionPointEnd();
01998 }
01999 
02000 long wxComboCtrlBase::GetInsertionPoint() const
02001 {
02002     if ( m_text )
02003         return m_text->GetInsertionPoint();
02004 
02005     return 0;
02006 }
02007 
02008 long wxComboCtrlBase::GetLastPosition() const
02009 {
02010     if ( m_text )
02011         return m_text->GetLastPosition();
02012 
02013     return 0;
02014 }
02015 
02016 void wxComboCtrlBase::Replace(long from, long to, const wxString& value)
02017 {
02018     if ( m_text )
02019         m_text->Replace(from, to, value);
02020 }
02021 
02022 void wxComboCtrlBase::Remove(long from, long to)
02023 {
02024     if ( m_text )
02025         m_text->Remove(from, to);
02026 }
02027 
02028 void wxComboCtrlBase::SetSelection(long from, long to)
02029 {
02030     if ( m_text )
02031         m_text->SetSelection(from, to);
02032 }
02033 
02034 void wxComboCtrlBase::Undo()
02035 {
02036     if ( m_text )
02037         m_text->Undo();
02038 }
02039 
02040 #endif // wxUSE_COMBOCONTROL

Generated on Sat Nov 10 03:49:00 2007 for Camelot by  doxygen 1.4.4