removed superfluous ;
[wxWidgets.git] / src / common / combocmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/combocmn.cpp
3 // Purpose: wxComboCtrlBase
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: Apr-30-2006
7 // RCS-ID: $Id$
8 // Copyright: (c) 2005 Jaakko Salli
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_COMBOCTRL
27
28 #include "wx/combobox.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/app.h"
32 #include "wx/log.h"
33 #include "wx/dcclient.h"
34 #include "wx/settings.h"
35 #include "wx/dialog.h"
36 #include "wx/timer.h"
37 #include "wx/textctrl.h"
38 #endif
39
40 #include "wx/tooltip.h"
41
42 #include "wx/combo.h"
43
44
45
46 // constants
47 // ----------------------------------------------------------------------------
48
49 #define DEFAULT_DROPBUTTON_WIDTH 19
50
51 #define BMP_BUTTON_MARGIN 4
52
53 #define DEFAULT_POPUP_HEIGHT 400
54
55 #define DEFAULT_TEXT_INDENT 3
56
57 #define COMBO_MARGIN 2 // spacing right of wxTextCtrl
58
59
60 #if defined(__WXMSW__)
61
62 #define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
63 #define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
64 // native controls work on it like normal.
65 #define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
66 #define TEXTCTRL_TEXT_CENTERED 0 // 1 if text in textctrl is vertically centered
67 #define FOCUS_RING 0 // No focus ring on wxMSW
68
69 #if !defined(__WXWINCE__)
70 // 1 if wxTextEntry::SetMargins() can be used to set the left margin
71 #define LEFT_MARGIN_CAN_BE_SET 1
72 #else
73 #define LEFT_MARGIN_CAN_BE_SET 0
74 #endif
75
76 //#undef wxUSE_POPUPWIN
77 //#define wxUSE_POPUPWIN 0
78
79 #elif defined(__WXGTK__)
80
81 #include "wx/gtk/private.h"
82
83 // NB: It is not recommended to use wxDialog as popup on wxGTK, because of
84 // this bug: If wxDialog is hidden, its position becomes corrupt
85 // between hide and next show, but without internal coordinates being
86 // reflected (or something like that - atleast commenting out ->Hide()
87 // seemed to eliminate the position change).
88
89 // NB: Let's not be afraid to use wxGTK's wxPopupTransientWindow as a
90 // 'perfect' popup, as it can succesfully host child controls even in
91 // popups that are shown in modal dialogs.
92
93 #define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
94 #define TRANSIENT_POPUPWIN_IS_PERFECT 1 // wxPopupTransientWindow works, its child can have focus, and common
95 // native controls work on it like normal.
96 #define POPUPWIN_IS_PERFECT 1 // Same, but for non-transient popup window.
97 #define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
98 #define FOCUS_RING 0 // No focus ring on wxGTK
99
100 #if GTK_CHECK_VERSION(2,10,0)
101 // 1 if wxTextEntry::SetMargins() can be used to set the left margin
102 #define LEFT_MARGIN_CAN_BE_SET 1
103 #else
104 #define LEFT_MARGIN_CAN_BE_SET 0
105 #endif
106
107 #elif defined(__WXMAC__)
108
109 #define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
110 #define TRANSIENT_POPUPWIN_IS_PERFECT 1 // wxPopupTransientWindow works, its child can have focus, and common
111 // native controls work on it like normal.
112 #define POPUPWIN_IS_PERFECT 1 // Same, but for non-transient popup window.
113 #define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
114 #define FOCUS_RING 3 // Reserve room for the textctrl's focus ring to display
115
116 #undef DEFAULT_DROPBUTTON_WIDTH
117 #define DEFAULT_DROPBUTTON_WIDTH 22
118 #undef COMBO_MARGIN
119 #define COMBO_MARGIN FOCUS_RING
120
121 // 1 if wxTextEntry::SetMargins() can be used to set the left margin
122 #define LEFT_MARGIN_CAN_BE_SET 0
123
124 #else
125
126 #define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
127 #define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
128 // native controls work on it like normal.
129 #define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
130 #define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
131 #define FOCUS_RING 0
132
133 // 1 if wxTextEntry::SetMargins() can be used to set the left margin
134 #define LEFT_MARGIN_CAN_BE_SET 0
135
136 #endif
137
138
139 // Popupwin is really only supported on wxMSW (not WINCE) and wxGTK, regardless
140 // what the wxUSE_POPUPWIN says.
141 // FIXME: Why isn't wxUSE_POPUPWIN reliable any longer? (it was in wxW2.6.2)
142 #if (!defined(__WXMSW__) && !defined(__WXGTK__) && !defined(__WXMAC__)) || defined(__WXWINCE__)
143 #undef wxUSE_POPUPWIN
144 #define wxUSE_POPUPWIN 0
145 #endif
146
147
148 #if wxUSE_POPUPWIN
149 #include "wx/popupwin.h"
150 #else
151 #undef USE_TRANSIENT_POPUP
152 #define USE_TRANSIENT_POPUP 0
153 #endif
154
155
156 // Define different types of popup windows
157 enum
158 {
159 POPUPWIN_NONE = 0,
160 POPUPWIN_WXPOPUPTRANSIENTWINDOW = 1,
161 POPUPWIN_WXPOPUPWINDOW = 2,
162 POPUPWIN_WXDIALOG = 3
163 };
164
165
166 #if USE_TRANSIENT_POPUP
167 // wxPopupTransientWindow is implemented
168
169 #define wxComboPopupWindowBase wxPopupTransientWindow
170 #define PRIMARY_POPUP_TYPE POPUPWIN_WXPOPUPTRANSIENTWINDOW
171 #define USES_WXPOPUPTRANSIENTWINDOW 1
172
173 #if TRANSIENT_POPUPWIN_IS_PERFECT
174 //
175 #elif POPUPWIN_IS_PERFECT
176 #define wxComboPopupWindowBase2 wxPopupWindow
177 #define SECONDARY_POPUP_TYPE POPUPWIN_WXPOPUPWINDOW
178 #define USES_WXPOPUPWINDOW 1
179 #else
180 #define wxComboPopupWindowBase2 wxDialog
181 #define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG
182 #define USES_WXDIALOG 1
183 #endif
184
185 #elif wxUSE_POPUPWIN
186 // wxPopupWindow (but not wxPopupTransientWindow) is properly implemented
187
188 #define wxComboPopupWindowBase wxPopupWindow
189 #define PRIMARY_POPUP_TYPE POPUPWIN_WXPOPUPWINDOW
190 #define USES_WXPOPUPWINDOW 1
191
192 #if !POPUPWIN_IS_PERFECT
193 #define wxComboPopupWindowBase2 wxDialog
194 #define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG
195 #define USES_WXDIALOG 1
196 #endif
197
198 #else
199 // wxPopupWindow is not implemented
200
201 #define wxComboPopupWindowBase wxDialog
202 #define PRIMARY_POPUP_TYPE POPUPWIN_WXDIALOG
203 #define USES_WXDIALOG 1
204
205 #endif
206
207
208 #ifndef USES_WXPOPUPTRANSIENTWINDOW
209 #define USES_WXPOPUPTRANSIENTWINDOW 0
210 #endif
211
212 #ifndef USES_WXPOPUPWINDOW
213 #define USES_WXPOPUPWINDOW 0
214 #endif
215
216 #ifndef USES_WXDIALOG
217 #define USES_WXDIALOG 0
218 #endif
219
220
221 #if USES_WXPOPUPWINDOW
222 #define INSTALL_TOPLEV_HANDLER 1
223 #else
224 #define INSTALL_TOPLEV_HANDLER 0
225 #endif
226
227
228 //
229 // ** TODO **
230 // * wxComboPopupWindow for external use (ie. replace old wxUniv wxPopupComboWindow)
231 //
232
233
234 // ----------------------------------------------------------------------------
235 // wxComboFrameEventHandler takes care of hiding the popup when events happen
236 // in its top level parent.
237 // ----------------------------------------------------------------------------
238
239 #if INSTALL_TOPLEV_HANDLER
240
241 //
242 // This will no longer be necessary after wxTransientPopupWindow
243 // works well on all platforms.
244 //
245
246 class wxComboFrameEventHandler : public wxEvtHandler
247 {
248 public:
249 wxComboFrameEventHandler( wxComboCtrlBase* pCb );
250 virtual ~wxComboFrameEventHandler();
251
252 void OnPopup();
253
254 void OnIdle( wxIdleEvent& event );
255 void OnMouseEvent( wxMouseEvent& event );
256 void OnActivate( wxActivateEvent& event );
257 void OnResize( wxSizeEvent& event );
258 void OnMove( wxMoveEvent& event );
259 void OnMenuEvent( wxMenuEvent& event );
260 void OnClose( wxCloseEvent& event );
261
262 protected:
263 wxWindow* m_focusStart;
264 wxComboCtrlBase* m_combo;
265
266 private:
267 DECLARE_EVENT_TABLE()
268 };
269
270 BEGIN_EVENT_TABLE(wxComboFrameEventHandler, wxEvtHandler)
271 EVT_IDLE(wxComboFrameEventHandler::OnIdle)
272 EVT_LEFT_DOWN(wxComboFrameEventHandler::OnMouseEvent)
273 EVT_RIGHT_DOWN(wxComboFrameEventHandler::OnMouseEvent)
274 EVT_SIZE(wxComboFrameEventHandler::OnResize)
275 EVT_MOVE(wxComboFrameEventHandler::OnMove)
276 EVT_MENU_HIGHLIGHT(wxID_ANY,wxComboFrameEventHandler::OnMenuEvent)
277 EVT_MENU_OPEN(wxComboFrameEventHandler::OnMenuEvent)
278 EVT_ACTIVATE(wxComboFrameEventHandler::OnActivate)
279 EVT_CLOSE(wxComboFrameEventHandler::OnClose)
280 END_EVENT_TABLE()
281
282 wxComboFrameEventHandler::wxComboFrameEventHandler( wxComboCtrlBase* combo )
283 : wxEvtHandler()
284 {
285 m_combo = combo;
286 }
287
288 wxComboFrameEventHandler::~wxComboFrameEventHandler()
289 {
290 }
291
292 void wxComboFrameEventHandler::OnPopup()
293 {
294 m_focusStart = ::wxWindow::FindFocus();
295 }
296
297 void wxComboFrameEventHandler::OnIdle( wxIdleEvent& event )
298 {
299 wxWindow* winFocused = ::wxWindow::FindFocus();
300
301 wxWindow* popup = m_combo->GetPopupControl()->GetControl();
302 wxWindow* winpopup = m_combo->GetPopupWindow();
303
304 if (
305 winFocused != m_focusStart &&
306 winFocused != popup &&
307 winFocused->GetParent() != popup &&
308 winFocused != winpopup &&
309 winFocused->GetParent() != winpopup &&
310 winFocused != m_combo &&
311 winFocused != m_combo->GetButton() // GTK (atleast) requires this
312 )
313 {
314 m_combo->HidePopup();
315 }
316
317 event.Skip();
318 }
319
320 void wxComboFrameEventHandler::OnMenuEvent( wxMenuEvent& event )
321 {
322 m_combo->HidePopup();
323 event.Skip();
324 }
325
326 void wxComboFrameEventHandler::OnMouseEvent( wxMouseEvent& event )
327 {
328 m_combo->HidePopup();
329 event.Skip();
330 }
331
332 void wxComboFrameEventHandler::OnClose( wxCloseEvent& event )
333 {
334 m_combo->HidePopup();
335 event.Skip();
336 }
337
338 void wxComboFrameEventHandler::OnActivate( wxActivateEvent& event )
339 {
340 m_combo->HidePopup();
341 event.Skip();
342 }
343
344 void wxComboFrameEventHandler::OnResize( wxSizeEvent& event )
345 {
346 m_combo->HidePopup();
347 event.Skip();
348 }
349
350 void wxComboFrameEventHandler::OnMove( wxMoveEvent& event )
351 {
352 m_combo->HidePopup();
353 event.Skip();
354 }
355
356 #endif // INSTALL_TOPLEV_HANDLER
357
358 // ----------------------------------------------------------------------------
359 // wxComboPopupWindow is, in essence, wxPopupWindow customized for
360 // wxComboCtrl.
361 // ----------------------------------------------------------------------------
362
363 class wxComboPopupWindow : public wxComboPopupWindowBase
364 {
365 public:
366
367 wxComboPopupWindow( wxComboCtrlBase *parent,
368 int style )
369 #if USES_WXPOPUPWINDOW || USES_WXPOPUPTRANSIENTWINDOW
370 : wxComboPopupWindowBase(parent,style)
371 #else
372 : wxComboPopupWindowBase(parent,
373 wxID_ANY,
374 wxEmptyString,
375 wxPoint(-21,-21),
376 wxSize(20,20),
377 style)
378 #endif
379 {
380 m_inShow = 0;
381 }
382
383 #if USES_WXPOPUPTRANSIENTWINDOW
384 virtual bool Show( bool show );
385 virtual bool ProcessLeftDown(wxMouseEvent& event);
386 protected:
387 virtual void OnDismiss();
388 #endif
389
390 private:
391 wxByte m_inShow;
392 };
393
394
395 #if USES_WXPOPUPTRANSIENTWINDOW
396 bool wxComboPopupWindow::Show( bool show )
397 {
398 // Guard against recursion
399 if ( m_inShow )
400 return wxComboPopupWindowBase::Show(show);
401
402 m_inShow++;
403
404 wxASSERT( IsKindOf(CLASSINFO(wxPopupTransientWindow)) );
405
406 wxPopupTransientWindow* ptw = (wxPopupTransientWindow*) this;
407
408 if ( show != ptw->IsShown() )
409 {
410 if ( show )
411 // We used to do wxPopupTransientWindow::Popup here,
412 // but this would hide normal Show, which we are
413 // also going to need.
414 ptw->Show();
415 else
416 ptw->Dismiss();
417 }
418
419 m_inShow--;
420
421 return true;
422 }
423
424 bool wxComboPopupWindow::ProcessLeftDown(wxMouseEvent& event)
425 {
426 return wxPopupTransientWindow::ProcessLeftDown(event);
427 }
428
429 // First thing that happens when a transient popup closes is that this method gets called.
430 void wxComboPopupWindow::OnDismiss()
431 {
432 wxComboCtrlBase* combo = (wxComboCtrlBase*) GetParent();
433 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxComboCtrlBase)),
434 wxT("parent might not be wxComboCtrl, but check IMPLEMENT_DYNAMIC_CLASS(2) macro for correctness") );
435
436 combo->OnPopupDismiss();
437 }
438 #endif // USES_WXPOPUPTRANSIENTWINDOW
439
440
441 // ----------------------------------------------------------------------------
442 // wxComboPopupWindowEvtHandler does bulk of the custom event handling
443 // of a popup window. It is separate so we can have different types
444 // of popup windows.
445 // ----------------------------------------------------------------------------
446
447 class wxComboPopupWindowEvtHandler : public wxEvtHandler
448 {
449 public:
450
451 wxComboPopupWindowEvtHandler( wxComboCtrlBase *parent )
452 {
453 m_combo = parent;
454 }
455
456 void OnSizeEvent( wxSizeEvent& event );
457 void OnKeyEvent(wxKeyEvent& event);
458 #if USES_WXDIALOG
459 void OnActivate( wxActivateEvent& event );
460 #endif
461
462 private:
463 wxComboCtrlBase* m_combo;
464
465 DECLARE_EVENT_TABLE()
466 };
467
468
469 BEGIN_EVENT_TABLE(wxComboPopupWindowEvtHandler, wxEvtHandler)
470 EVT_KEY_DOWN(wxComboPopupWindowEvtHandler::OnKeyEvent)
471 EVT_KEY_UP(wxComboPopupWindowEvtHandler::OnKeyEvent)
472 #if USES_WXDIALOG
473 EVT_ACTIVATE(wxComboPopupWindowEvtHandler::OnActivate)
474 #endif
475 EVT_SIZE(wxComboPopupWindowEvtHandler::OnSizeEvent)
476 END_EVENT_TABLE()
477
478
479 void wxComboPopupWindowEvtHandler::OnSizeEvent( wxSizeEvent& WXUNUSED(event) )
480 {
481 // Block the event so that the popup control does not get auto-resized.
482 }
483
484 void wxComboPopupWindowEvtHandler::OnKeyEvent( wxKeyEvent& event )
485 {
486 // Relay keyboard event to the main child controls
487 wxWindowList children = m_combo->GetPopupWindow()->GetChildren();
488 wxWindowList::iterator node = children.begin();
489 wxWindow* child = (wxWindow*)*node;
490 child->GetEventHandler()->AddPendingEvent(event);
491 }
492
493 #if USES_WXDIALOG
494 void wxComboPopupWindowEvtHandler::OnActivate( wxActivateEvent& event )
495 {
496 if ( !event.GetActive() )
497 {
498 // Tell combo control that we are dismissed.
499 m_combo->HidePopup();
500
501 event.Skip();
502 }
503 }
504 #endif
505
506
507 // ----------------------------------------------------------------------------
508 // wxComboPopup
509 //
510 // ----------------------------------------------------------------------------
511
512 wxComboPopup::~wxComboPopup()
513 {
514 }
515
516 void wxComboPopup::OnPopup()
517 {
518 }
519
520 void wxComboPopup::OnDismiss()
521 {
522 }
523
524 wxComboCtrl* wxComboPopup::GetComboCtrl() const
525 {
526 return wxStaticCast(m_combo, wxComboCtrl);
527 }
528
529 wxSize wxComboPopup::GetAdjustedSize( int minWidth,
530 int prefHeight,
531 int WXUNUSED(maxHeight) )
532 {
533 return wxSize(minWidth,prefHeight);
534 }
535
536 void wxComboPopup::DefaultPaintComboControl( wxComboCtrlBase* combo,
537 wxDC& dc, const wxRect& rect )
538 {
539 if ( combo->GetWindowStyle() & wxCB_READONLY ) // ie. no textctrl
540 {
541 combo->PrepareBackground(dc,rect,0);
542
543 dc.DrawText( combo->GetValue(),
544 rect.x + combo->m_marginLeft,
545 (rect.height-dc.GetCharHeight())/2 + rect.y );
546 }
547 }
548
549 void wxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
550 {
551 DefaultPaintComboControl(m_combo,dc,rect);
552 }
553
554 void wxComboPopup::OnComboKeyEvent( wxKeyEvent& event )
555 {
556 event.Skip();
557 }
558
559 void wxComboPopup::OnComboDoubleClick()
560 {
561 }
562
563 void wxComboPopup::SetStringValue( const wxString& WXUNUSED(value) )
564 {
565 }
566
567 bool wxComboPopup::LazyCreate()
568 {
569 return false;
570 }
571
572 void wxComboPopup::Dismiss()
573 {
574 m_combo->HidePopup();
575 }
576
577 // ----------------------------------------------------------------------------
578 // input handling
579 // ----------------------------------------------------------------------------
580
581 //
582 // This is pushed to the event handler queue of the child textctrl.
583 //
584 class wxComboBoxExtraInputHandler : public wxEvtHandler
585 {
586 public:
587
588 wxComboBoxExtraInputHandler( wxComboCtrlBase* combo )
589 : wxEvtHandler()
590 {
591 m_combo = combo;
592 }
593 virtual ~wxComboBoxExtraInputHandler() { }
594 void OnKey(wxKeyEvent& event);
595 void OnFocus(wxFocusEvent& event);
596
597 protected:
598 wxComboCtrlBase* m_combo;
599
600 private:
601 DECLARE_EVENT_TABLE()
602 };
603
604
605 BEGIN_EVENT_TABLE(wxComboBoxExtraInputHandler, wxEvtHandler)
606 EVT_KEY_DOWN(wxComboBoxExtraInputHandler::OnKey)
607 EVT_KEY_UP(wxComboBoxExtraInputHandler::OnKey)
608 EVT_CHAR(wxComboBoxExtraInputHandler::OnKey)
609 EVT_SET_FOCUS(wxComboBoxExtraInputHandler::OnFocus)
610 EVT_KILL_FOCUS(wxComboBoxExtraInputHandler::OnFocus)
611 END_EVENT_TABLE()
612
613
614 void wxComboBoxExtraInputHandler::OnKey(wxKeyEvent& event)
615 {
616 // Let the wxComboCtrl event handler have a go first.
617 wxComboCtrlBase* combo = m_combo;
618
619 wxKeyEvent redirectedEvent(event);
620 redirectedEvent.SetId(combo->GetId());
621 redirectedEvent.SetEventObject(combo);
622
623 if ( !combo->GetEventHandler()->ProcessEvent(redirectedEvent) )
624 {
625 // Don't let TAB through to the text ctrl - looks ugly
626 if ( event.GetKeyCode() != WXK_TAB )
627 event.Skip();
628 }
629 }
630
631 void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event)
632 {
633 // FIXME: This code does run when control is clicked,
634 // yet on Windows it doesn't select all the text.
635 if ( event.GetEventType() == wxEVT_SET_FOCUS &&
636 !(m_combo->GetInternalFlags() & wxCC_NO_TEXT_AUTO_SELECT) )
637 {
638 if ( m_combo->GetTextCtrl() )
639 m_combo->GetTextCtrl()->SelectAll();
640 else
641 m_combo->SetSelection(-1,-1);
642 }
643
644 // Send focus indication to parent.
645 // NB: This is needed for cases where the textctrl gets focus
646 // instead of its parent. While this may trigger multiple
647 // wxEVT_SET_FOCUSes (since m_text->SetFocus is called
648 // from combo's focus event handler), they should be quite
649 // harmless.
650 wxFocusEvent evt2(event.GetEventType(),m_combo->GetId());
651 evt2.SetEventObject(m_combo);
652 m_combo->GetEventHandler()->ProcessEvent(evt2);
653
654 event.Skip();
655 }
656
657
658 //
659 // This is pushed to the event handler queue of the control in popup.
660 //
661
662 class wxComboPopupExtraEventHandler : public wxEvtHandler
663 {
664 public:
665
666 wxComboPopupExtraEventHandler( wxComboCtrlBase* combo )
667 : wxEvtHandler()
668 {
669 m_combo = combo;
670 m_beenInside = false;
671 }
672 virtual ~wxComboPopupExtraEventHandler() { }
673
674 void OnMouseEvent( wxMouseEvent& event );
675
676 // Called from wxComboCtrlBase::OnPopupDismiss
677 void OnPopupDismiss()
678 {
679 m_beenInside = false;
680 }
681
682 protected:
683 wxComboCtrlBase* m_combo;
684
685 bool m_beenInside;
686
687 private:
688 DECLARE_EVENT_TABLE()
689 };
690
691
692 BEGIN_EVENT_TABLE(wxComboPopupExtraEventHandler, wxEvtHandler)
693 EVT_MOUSE_EVENTS(wxComboPopupExtraEventHandler::OnMouseEvent)
694 END_EVENT_TABLE()
695
696
697 void wxComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event )
698 {
699 wxPoint pt = event.GetPosition();
700 wxSize sz = m_combo->GetPopupControl()->GetControl()->GetClientSize();
701 int evtType = event.GetEventType();
702 bool isInside = pt.x >= 0 && pt.y >= 0 && pt.x < sz.x && pt.y < sz.y;
703 bool relayToButton = false;
704
705 event.Skip();
706
707 if ( evtType == wxEVT_MOTION ||
708 evtType == wxEVT_LEFT_DOWN ||
709 evtType == wxEVT_RIGHT_DOWN )
710 {
711 // Block motion and click events outside the popup
712 if ( !isInside || !m_combo->IsPopupShown() )
713 {
714 event.Skip(false);
715 }
716 }
717 else if ( evtType == wxEVT_LEFT_UP )
718 {
719 if ( !m_combo->IsPopupShown() )
720 {
721 event.Skip(false);
722 relayToButton = true;
723 }
724 else if ( !m_beenInside )
725 {
726 if ( isInside )
727 {
728 m_beenInside = true;
729 }
730 else
731 {
732 relayToButton = true;
733 }
734 }
735 }
736
737 if ( relayToButton )
738 {
739 //
740 // Some mouse events to popup that happen outside it, before cursor
741 // has been inside the popup, need to be ignored by it but relayed to
742 // the dropbutton.
743 //
744 wxWindow* eventSink = m_combo;
745 wxWindow* btn = m_combo->GetButton();
746 if ( btn )
747 eventSink = btn;
748
749 eventSink->GetEventHandler()->ProcessEvent(event);
750 }
751 }
752
753 // ----------------------------------------------------------------------------
754 // wxComboCtrlTextCtrl
755 // ----------------------------------------------------------------------------
756
757 class wxComboCtrlTextCtrl : public wxTextCtrl
758 {
759 public:
760 wxComboCtrlTextCtrl() : wxTextCtrl() { }
761 virtual ~wxComboCtrlTextCtrl() { }
762
763 virtual wxWindow *GetMainWindowOfCompositeControl()
764 {
765 wxComboCtrl* combo = (wxComboCtrl*) GetParent();
766
767 // Returning this instead of just 'parent' lets FindFocus work
768 // correctly even when parent control is a child of a composite
769 // generic control (as is case with wxGenericDatePickerCtrl).
770 return combo->GetMainWindowOfCompositeControl();
771 }
772 };
773
774 // ----------------------------------------------------------------------------
775 // wxComboCtrlBase
776 // ----------------------------------------------------------------------------
777
778
779 BEGIN_EVENT_TABLE(wxComboCtrlBase, wxControl)
780 EVT_TEXT(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
781 EVT_SIZE(wxComboCtrlBase::OnSizeEvent)
782 EVT_SET_FOCUS(wxComboCtrlBase::OnFocusEvent)
783 EVT_KILL_FOCUS(wxComboCtrlBase::OnFocusEvent)
784 EVT_IDLE(wxComboCtrlBase::OnIdleEvent)
785 //EVT_BUTTON(wxID_ANY,wxComboCtrlBase::OnButtonClickEvent)
786 EVT_KEY_DOWN(wxComboCtrlBase::OnKeyEvent)
787 EVT_TEXT_ENTER(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
788 EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged)
789 END_EVENT_TABLE()
790
791
792 IMPLEMENT_ABSTRACT_CLASS(wxComboCtrlBase, wxControl)
793
794 void wxComboCtrlBase::Init()
795 {
796 m_winPopup = NULL;
797 m_popup = NULL;
798 m_popupWinState = Hidden;
799 m_btn = NULL;
800 m_text = NULL;
801 m_popupInterface = NULL;
802
803 m_popupExtraHandler = NULL;
804 m_textEvtHandler = NULL;
805
806 #if INSTALL_TOPLEV_HANDLER
807 m_toplevEvtHandler = NULL;
808 #endif
809
810 m_mainCtrlWnd = this;
811
812 m_heightPopup = -1;
813 m_widthMinPopup = -1;
814 m_anchorSide = 0;
815 m_widthCustomPaint = 0;
816 m_widthCustomBorder = 0;
817
818 m_btnState = 0;
819 m_btnWidDefault = 0;
820 m_blankButtonBg = false;
821 m_ignoreEvtText = 0;
822 m_popupWinType = POPUPWIN_NONE;
823 m_btnWid = m_btnHei = -1;
824 m_btnSide = wxRIGHT;
825 m_btnSpacingX = 0;
826
827 m_extLeft = 0;
828 m_extRight = 0;
829 m_marginLeft = -1;
830 m_iFlags = 0;
831 m_timeCanAcceptClick = 0;
832
833 m_resetFocus = false;
834 }
835
836 bool wxComboCtrlBase::Create(wxWindow *parent,
837 wxWindowID id,
838 const wxString& value,
839 const wxPoint& pos,
840 const wxSize& size,
841 long style,
842 const wxValidator& validator,
843 const wxString& name)
844 {
845 if ( !wxControl::Create(parent,
846 id,
847 pos,
848 size,
849 style | wxWANTS_CHARS,
850 validator,
851 name) )
852 return false;
853
854 m_valueString = value;
855
856 // Get colours
857 OnThemeChange();
858 m_marginLeft = GetNativeTextIndent();
859
860 m_iFlags |= wxCC_IFLAG_CREATED;
861
862 // If x and y indicate valid size, wxSizeEvent won't be
863 // emitted automatically, so we need to add artifical one.
864 if ( size.x > 0 && size.y > 0 )
865 {
866 wxSizeEvent evt(size,GetId());
867 GetEventHandler()->AddPendingEvent(evt);
868 }
869
870 return true;
871 }
872
873 void wxComboCtrlBase::InstallInputHandlers()
874 {
875 if ( m_text )
876 {
877 m_textEvtHandler = new wxComboBoxExtraInputHandler(this);
878 m_text->PushEventHandler(m_textEvtHandler);
879 }
880 }
881
882 void
883 wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator)
884 {
885 if ( !(m_windowStyle & wxCB_READONLY) )
886 {
887 if ( m_text )
888 m_text->Destroy();
889
890 // wxTE_PROCESS_TAB is needed because on Windows, wxTAB_TRAVERSAL is
891 // not used by the wxPropertyGrid and therefore the tab is processed by
892 // looking at ancestors to see if they have wxTAB_TRAVERSAL. The
893 // navigation event is then sent to the wrong window.
894 style |= wxTE_PROCESS_TAB;
895
896 if ( HasFlag(wxTE_PROCESS_ENTER) )
897 style |= wxTE_PROCESS_ENTER;
898
899 // Ignore EVT_TEXT generated by the constructor (but only
900 // if the event redirector already exists)
901 // NB: This must be " = 1" instead of "++";
902 if ( m_textEvtHandler )
903 m_ignoreEvtText = 1;
904 else
905 m_ignoreEvtText = 0;
906
907 m_text = new wxComboCtrlTextCtrl();
908 m_text->Create(this, wxID_ANY, m_valueString,
909 wxDefaultPosition, wxSize(10,-1),
910 style, validator);
911 }
912 }
913
914 void wxComboCtrlBase::OnThemeChange()
915 {
916 // Leave the default bg on the Mac so the area used by the focus ring will
917 // be the correct colour and themed brush. Instead we'll use
918 // wxSYS_COLOUR_WINDOW in the EVT_PAINT handler as needed.
919 #ifndef __WXMAC__
920 SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
921 #endif
922 }
923
924 wxComboCtrlBase::~wxComboCtrlBase()
925 {
926 if ( HasCapture() )
927 ReleaseMouse();
928
929 #if INSTALL_TOPLEV_HANDLER
930 delete ((wxComboFrameEventHandler*)m_toplevEvtHandler);
931 m_toplevEvtHandler = NULL;
932 #endif
933
934 DestroyPopup();
935
936 if ( m_text )
937 m_text->RemoveEventHandler(m_textEvtHandler);
938
939 delete m_textEvtHandler;
940 }
941
942
943 // ----------------------------------------------------------------------------
944 // geometry stuff
945 // ----------------------------------------------------------------------------
946
947 // Recalculates button and textctrl areas
948 void wxComboCtrlBase::CalculateAreas( int btnWidth )
949 {
950 wxSize sz = GetClientSize();
951 int customBorder = m_widthCustomBorder;
952 int btnBorder; // border for button only
953
954 // check if button should really be outside the border: we'll do it it if
955 // its platform default or bitmap+pushbutton background is used, but not if
956 // there is vertical size adjustment or horizontal spacing.
957 if ( ( (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) ||
958 (m_bmpNormal.Ok() && m_blankButtonBg) ) &&
959 m_btnSpacingX == 0 &&
960 m_btnHei <= 0 )
961 {
962 m_iFlags |= wxCC_IFLAG_BUTTON_OUTSIDE;
963 btnBorder = 0;
964 }
965 else if ( (m_iFlags & wxCC_BUTTON_COVERS_BORDER) &&
966 m_btnSpacingX == 0 && !m_bmpNormal.Ok() )
967 {
968 m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE);
969 btnBorder = 0;
970 }
971 else
972 {
973 m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE);
974 btnBorder = customBorder;
975 }
976
977 // Defaul indentation
978 if ( m_marginLeft < 0 )
979 m_marginLeft = GetNativeTextIndent();
980
981 int butWidth = btnWidth;
982
983 if ( butWidth <= 0 )
984 butWidth = m_btnWidDefault;
985 else
986 m_btnWidDefault = butWidth;
987
988 if ( butWidth <= 0 )
989 return;
990
991 int butHeight = sz.y - btnBorder*2;
992
993 // Adjust button width
994 if ( m_btnWid > 0 )
995 butWidth = m_btnWid;
996 else
997 {
998 // Adjust button width to match aspect ratio
999 // (but only if control is smaller than best size).
1000 int bestHeight = GetBestSize().y;
1001 int height = GetSize().y;
1002
1003 if ( height < bestHeight )
1004 {
1005 // Make very small buttons square, as it makes
1006 // them accommodate arrow image better and still
1007 // looks decent.
1008 if ( height > 18 )
1009 butWidth = (height*butWidth)/bestHeight;
1010 else
1011 butWidth = butHeight;
1012 }
1013 }
1014
1015 // Adjust button height
1016 if ( m_btnHei > 0 )
1017 butHeight = m_btnHei;
1018
1019 // Use size of normal bitmap if...
1020 // It is larger
1021 // OR
1022 // button width is set to default and blank button bg is not drawn
1023 if ( m_bmpNormal.Ok() )
1024 {
1025 int bmpReqWidth = m_bmpNormal.GetWidth();
1026 int bmpReqHeight = m_bmpNormal.GetHeight();
1027
1028 // If drawing blank button background, we need to add some margin.
1029 if ( m_blankButtonBg )
1030 {
1031 bmpReqWidth += BMP_BUTTON_MARGIN*2;
1032 bmpReqHeight += BMP_BUTTON_MARGIN*2;
1033 }
1034
1035 if ( butWidth < bmpReqWidth || ( m_btnWid == 0 && !m_blankButtonBg ) )
1036 butWidth = bmpReqWidth;
1037 if ( butHeight < bmpReqHeight || ( m_btnHei == 0 && !m_blankButtonBg ) )
1038 butHeight = bmpReqHeight;
1039
1040 // Need to fix height?
1041 if ( (sz.y-(customBorder*2)) < butHeight && btnWidth == 0 )
1042 {
1043 int newY = butHeight+(customBorder*2);
1044 SetClientSize(wxDefaultCoord,newY);
1045 if ( m_bmpNormal.Ok() || m_btnArea.width != butWidth || m_btnArea.height != butHeight )
1046 m_iFlags |= wxCC_IFLAG_HAS_NONSTANDARD_BUTTON;
1047 else
1048 m_iFlags &= ~wxCC_IFLAG_HAS_NONSTANDARD_BUTTON;
1049
1050 sz.y = newY;
1051 }
1052 }
1053
1054 int butAreaWid = butWidth + (m_btnSpacingX*2);
1055
1056 m_btnSize.x = butWidth;
1057 m_btnSize.y = butHeight;
1058
1059 m_btnArea.x = ( m_btnSide==wxRIGHT ? sz.x - butAreaWid - btnBorder : btnBorder );
1060 m_btnArea.y = btnBorder + FOCUS_RING;
1061 m_btnArea.width = butAreaWid;
1062 m_btnArea.height = sz.y - ((btnBorder+FOCUS_RING)*2);
1063
1064 m_tcArea.x = ( m_btnSide==wxRIGHT ? 0 : butAreaWid ) + customBorder + FOCUS_RING;
1065 m_tcArea.y = customBorder + FOCUS_RING;
1066 m_tcArea.width = sz.x - butAreaWid - (customBorder*2) - (FOCUS_RING*2);
1067 m_tcArea.height = sz.y - ((customBorder+FOCUS_RING)*2);
1068
1069 /*
1070 if ( m_text )
1071 {
1072 ::wxMessageBox(wxString::Format(wxT("ButtonArea (%i,%i,%i,%i)\n"),m_btnArea.x,m_btnArea.y,m_btnArea.width,m_btnArea.height) +
1073 wxString::Format(wxT("TextCtrlArea (%i,%i,%i,%i)"),m_tcArea.x,m_tcArea.y,m_tcArea.width,m_tcArea.height));
1074 }
1075 */
1076 }
1077
1078 void wxComboCtrlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust )
1079 {
1080 if ( !m_text )
1081 return;
1082
1083 wxSize sz = GetClientSize();
1084
1085 int customBorder = m_widthCustomBorder;
1086 if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER )
1087 {
1088 #if LEFT_MARGIN_CAN_BE_SET
1089 // Call SetMargins() on textctrl if LEFT_MARGIN_CAN_BE_SET == 1
1090 wxUnusedVar(textCtrlXAdjust);
1091 m_text->SetMargins(0);
1092 textCtrlXAdjust = 0;
1093 #endif
1094
1095 // Centre textctrl
1096 #if !TEXTCTRL_TEXT_CENTERED
1097 int tcSizeY = m_text->GetBestSize().y;
1098 int diff0 = sz.y - tcSizeY;
1099 int y = textCtrlYAdjust + (diff0/2);
1100 #else
1101 wxUnusedVar(textCtrlYAdjust);
1102 int y = 0;
1103 #endif
1104
1105 if ( y < customBorder )
1106 y = customBorder;
1107
1108 int x = m_tcArea.x + m_widthCustomPaint +
1109 m_marginLeft + textCtrlXAdjust;
1110
1111 m_text->SetSize(x,
1112 y,
1113 m_tcArea.width - m_tcArea.x - x,
1114 /*m_tcArea.width - COMBO_MARGIN -
1115 (textCtrlXAdjust + m_widthCustomPaint +
1116 m_marginLeft),*/
1117 -1 );
1118
1119 // Make sure textctrl doesn't exceed the bottom custom border
1120 wxSize tsz = m_text->GetSize();
1121 int diff1 = (y + tsz.y) - (sz.y - customBorder);
1122 if ( diff1 >= 0 )
1123 {
1124 tsz.y = tsz.y - diff1 - 1;
1125 m_text->SetSize(tsz);
1126 }
1127 }
1128 else
1129 {
1130 // If it has border, have textctrl fill the entire text field.
1131 m_text->SetSize( m_tcArea.x + m_widthCustomPaint,
1132 m_tcArea.y,
1133 m_tcArea.width - m_widthCustomPaint,
1134 m_tcArea.height );
1135 }
1136 }
1137
1138 wxSize wxComboCtrlBase::DoGetBestSize() const
1139 {
1140 wxSize sizeText(150,0);
1141
1142 if ( m_text )
1143 sizeText = m_text->GetBestSize();
1144
1145 // TODO: Better method to calculate close-to-native control height.
1146
1147 int fhei;
1148 if ( m_font.Ok() )
1149 fhei = (m_font.GetPointSize()*2) + 5;
1150 else if ( wxNORMAL_FONT->Ok() )
1151 fhei = (wxNORMAL_FONT->GetPointSize()*2) + 5;
1152 else
1153 fhei = sizeText.y + 4;
1154
1155 // Need to force height to accomodate bitmap?
1156 int btnSizeY = m_btnSize.y;
1157 if ( m_bmpNormal.Ok() && fhei < btnSizeY )
1158 fhei = btnSizeY;
1159
1160 // Control height doesn't depend on border
1161 /*
1162 // Add border
1163 int border = m_windowStyle & wxBORDER_MASK;
1164 if ( border == wxSIMPLE_BORDER )
1165 fhei += 2;
1166 else if ( border == wxNO_BORDER )
1167 fhei += (m_widthCustomBorder*2);
1168 else
1169 // Sunken etc.
1170 fhei += 4;
1171 */
1172
1173 // Final adjustments
1174 #ifdef __WXGTK__
1175 fhei += 1;
1176 #endif
1177
1178 #ifdef __WXMAC__
1179 // these are the numbers from the HIG:
1180 switch ( m_windowVariant )
1181 {
1182 case wxWINDOW_VARIANT_NORMAL:
1183 default :
1184 fhei = 22;
1185 break;
1186 case wxWINDOW_VARIANT_SMALL:
1187 fhei = 19;
1188 break;
1189 case wxWINDOW_VARIANT_MINI:
1190 fhei = 15;
1191 break;
1192 }
1193 #endif
1194
1195 fhei += 2 * FOCUS_RING;
1196 int width = sizeText.x + FOCUS_RING + COMBO_MARGIN + DEFAULT_DROPBUTTON_WIDTH;
1197
1198 wxSize ret(width, fhei);
1199 CacheBestSize(ret);
1200 return ret;
1201 }
1202
1203 void wxComboCtrlBase::OnSizeEvent( wxSizeEvent& event )
1204 {
1205 if ( !IsCreated() )
1206 return;
1207
1208 // defined by actual wxComboCtrls
1209 OnResize();
1210
1211 event.Skip();
1212 }
1213
1214 // ----------------------------------------------------------------------------
1215 // standard operations
1216 // ----------------------------------------------------------------------------
1217
1218 bool wxComboCtrlBase::Enable(bool enable)
1219 {
1220 if ( !wxControl::Enable(enable) )
1221 return false;
1222
1223 if ( m_btn )
1224 m_btn->Enable(enable);
1225 if ( m_text )
1226 m_text->Enable(enable);
1227
1228 Refresh();
1229
1230 return true;
1231 }
1232
1233 bool wxComboCtrlBase::Show(bool show)
1234 {
1235 if ( !wxControl::Show(show) )
1236 return false;
1237
1238 if (m_btn)
1239 m_btn->Show(show);
1240
1241 if (m_text)
1242 m_text->Show(show);
1243
1244 return true;
1245 }
1246
1247 bool wxComboCtrlBase::SetFont ( const wxFont& font )
1248 {
1249 if ( !wxControl::SetFont(font) )
1250 return false;
1251
1252 if (m_text)
1253 m_text->SetFont(font);
1254
1255 return true;
1256 }
1257
1258 #if wxUSE_TOOLTIPS
1259 void wxComboCtrlBase::DoSetToolTip(wxToolTip *tooltip)
1260 {
1261 wxControl::DoSetToolTip(tooltip);
1262
1263 // Set tool tip for button and text box
1264 if ( tooltip )
1265 {
1266 const wxString &tip = tooltip->GetTip();
1267 if ( m_text ) m_text->SetToolTip(tip);
1268 if ( m_btn ) m_btn->SetToolTip(tip);
1269 }
1270 else
1271 {
1272 if ( m_text ) m_text->SetToolTip( NULL );
1273 if ( m_btn ) m_btn->SetToolTip( NULL );
1274 }
1275 }
1276 #endif // wxUSE_TOOLTIPS
1277
1278 #if wxUSE_VALIDATORS
1279 void wxComboCtrlBase::SetValidator(const wxValidator& validator)
1280 {
1281 wxTextCtrl* textCtrl = GetTextCtrl();
1282
1283 if ( textCtrl )
1284 textCtrl->SetValidator( validator );
1285 else
1286 wxControl::SetValidator( validator );
1287 }
1288
1289 wxValidator* wxComboCtrlBase::GetValidator()
1290 {
1291 wxTextCtrl* textCtrl = GetTextCtrl();
1292
1293 return textCtrl ? textCtrl->GetValidator() : wxControl::GetValidator();
1294 }
1295 #endif // wxUSE_VALIDATORS
1296
1297 // ----------------------------------------------------------------------------
1298 // painting
1299 // ----------------------------------------------------------------------------
1300
1301 #if (!defined(__WXMSW__)) || defined(__WXUNIVERSAL__)
1302 // prepare combo box background on area in a way typical on platform
1303 void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
1304 {
1305 wxSize sz = GetClientSize();
1306 bool isEnabled;
1307 bool doDrawFocusRect; // also selected
1308
1309 // For smaller size control (and for disabled background) use less spacing
1310 int focusSpacingX;
1311 int focusSpacingY;
1312
1313 if ( !(flags & wxCONTROL_ISSUBMENU) )
1314 {
1315 // Drawing control
1316 isEnabled = IsEnabled();
1317 doDrawFocusRect = ShouldDrawFocus() && !(m_iFlags & wxCC_FULL_BUTTON);
1318
1319 // Windows-style: for smaller size control (and for disabled background) use less spacing
1320 focusSpacingX = isEnabled ? 2 : 1;
1321 focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
1322 }
1323 else
1324 {
1325 // Drawing a list item
1326 isEnabled = true; // they are never disabled
1327 doDrawFocusRect = (flags & wxCONTROL_SELECTED) != 0;
1328
1329 focusSpacingX = 0;
1330 focusSpacingY = 0;
1331 }
1332
1333 // Set the background sub-rectangle for selection, disabled etc
1334 wxRect selRect(rect);
1335 selRect.y += focusSpacingY;
1336 selRect.height -= (focusSpacingY*2);
1337
1338 int wcp = 0;
1339
1340 if ( !(flags & wxCONTROL_ISSUBMENU) )
1341 wcp += m_widthCustomPaint;
1342
1343 selRect.x += wcp + focusSpacingX;
1344 selRect.width -= wcp + (focusSpacingX*2);
1345
1346 wxColour bgCol;
1347 bool doDrawSelRect = true;
1348
1349 if ( isEnabled )
1350 {
1351 // If popup is hidden and this control is focused,
1352 // then draw the focus-indicator (selbgcolor background etc.).
1353 if ( doDrawFocusRect )
1354 {
1355 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
1356 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
1357 }
1358 else
1359 {
1360 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
1361 #ifndef __WXMAC__ // see note in OnThemeChange
1362 doDrawSelRect = false;
1363 bgCol = GetBackgroundColour();
1364 #else
1365 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
1366 #endif
1367 }
1368 }
1369 else
1370 {
1371 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
1372 #ifndef __WXMAC__ // see note in OnThemeChange
1373 bgCol = GetBackgroundColour();
1374 #else
1375 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
1376 #endif
1377 }
1378
1379 dc.SetBrush( bgCol );
1380 if ( doDrawSelRect )
1381 {
1382 dc.SetPen( bgCol );
1383 dc.DrawRectangle( selRect );
1384 }
1385
1386 // Don't clip exactly to the selection rectangle so we can draw
1387 // to the non-selected area in front of it.
1388 wxRect clipRect(rect.x,rect.y,
1389 (selRect.x+selRect.width)-rect.x,rect.height);
1390 dc.SetClippingRegion(clipRect);
1391 }
1392 #else
1393 // Save the library size a bit for platforms that re-implement this.
1394 void wxComboCtrlBase::PrepareBackground( wxDC&, const wxRect&, int ) const
1395 {
1396 }
1397 #endif
1398
1399 void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int flags )
1400 {
1401 int drawState = m_btnState;
1402
1403 if ( (m_iFlags & wxCC_BUTTON_STAYS_DOWN) &&
1404 GetPopupWindowState() >= Animating )
1405 drawState |= wxCONTROL_PRESSED;
1406
1407 wxRect drawRect(rect.x+m_btnSpacingX,
1408 rect.y+((rect.height-m_btnSize.y)/2),
1409 m_btnSize.x,
1410 m_btnSize.y);
1411
1412 // Make sure area is not larger than the control
1413 if ( drawRect.y < rect.y )
1414 drawRect.y = rect.y;
1415 if ( drawRect.height > rect.height )
1416 drawRect.height = rect.height;
1417
1418 bool enabled = IsEnabled();
1419
1420 if ( !enabled )
1421 drawState |= wxCONTROL_DISABLED;
1422
1423 if ( !m_bmpNormal.Ok() )
1424 {
1425 if ( flags & Button_BitmapOnly )
1426 return;
1427
1428 // Need to clear button background even if m_btn is present
1429 if ( flags & Button_PaintBackground )
1430 {
1431 wxColour bgCol;
1432
1433 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
1434 bgCol = GetParent()->GetBackgroundColour();
1435 else
1436 bgCol = GetBackgroundColour();
1437
1438 dc.SetBrush(bgCol);
1439 dc.SetPen(bgCol);
1440 dc.DrawRectangle(rect);
1441 }
1442
1443 // Draw standard button
1444 wxRendererNative::Get().DrawComboBoxDropButton(this,
1445 dc,
1446 drawRect,
1447 drawState);
1448 }
1449 else
1450 {
1451 // Draw bitmap
1452
1453 wxBitmap* pBmp;
1454
1455 if ( !enabled )
1456 pBmp = &m_bmpDisabled;
1457 else if ( m_btnState & wxCONTROL_PRESSED )
1458 pBmp = &m_bmpPressed;
1459 else if ( m_btnState & wxCONTROL_CURRENT )
1460 pBmp = &m_bmpHover;
1461 else
1462 pBmp = &m_bmpNormal;
1463
1464 if ( m_blankButtonBg )
1465 {
1466 // If using blank button background, we need to clear its background
1467 // with button face colour instead of colour for rest of the control.
1468 if ( flags & Button_PaintBackground )
1469 {
1470 wxColour bgCol = GetParent()->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
1471 //wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
1472 dc.SetPen(bgCol);
1473 dc.SetBrush(bgCol);
1474 dc.DrawRectangle(rect);
1475 }
1476
1477 if ( !(flags & Button_BitmapOnly) )
1478 {
1479 wxRendererNative::Get().DrawPushButton(this,
1480 dc,
1481 drawRect,
1482 drawState);
1483 }
1484 }
1485 else
1486
1487 {
1488 // Need to clear button background even if m_btn is present
1489 // (assume non-button background was cleared just before this call so brushes are good)
1490 if ( flags & Button_PaintBackground )
1491 dc.DrawRectangle(rect);
1492 }
1493
1494 // Draw bitmap centered in drawRect
1495 dc.DrawBitmap(*pBmp,
1496 drawRect.x + (drawRect.width-pBmp->GetWidth())/2,
1497 drawRect.y + (drawRect.height-pBmp->GetHeight())/2,
1498 true);
1499 }
1500 }
1501
1502 void wxComboCtrlBase::RecalcAndRefresh()
1503 {
1504 if ( IsCreated() )
1505 {
1506 wxSizeEvent evt(GetSize(),GetId());
1507 GetEventHandler()->ProcessEvent(evt);
1508 Refresh();
1509 }
1510 }
1511
1512 // ----------------------------------------------------------------------------
1513 // miscellaneous event handlers
1514 // ----------------------------------------------------------------------------
1515
1516 void wxComboCtrlBase::OnTextCtrlEvent(wxCommandEvent& event)
1517 {
1518 if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED )
1519 {
1520 if ( m_ignoreEvtText > 0 )
1521 {
1522 m_ignoreEvtText--;
1523 return;
1524 }
1525 }
1526
1527 // Change event id, object and string before relaying it forward
1528 event.SetId(GetId());
1529 wxString s = event.GetString();
1530 event.SetEventObject(this);
1531 event.SetString(s);
1532 event.Skip();
1533 }
1534
1535 // call if cursor is on button area or mouse is captured for the button
1536 bool wxComboCtrlBase::HandleButtonMouseEvent( wxMouseEvent& event,
1537 int flags )
1538 {
1539 int type = event.GetEventType();
1540
1541 if ( type == wxEVT_MOTION )
1542 {
1543 if ( (flags & wxCC_MF_ON_BUTTON) &&
1544 IsPopupWindowState(Hidden) )
1545 {
1546 if ( !(m_btnState & wxCONTROL_CURRENT) )
1547 {
1548 // Mouse hover begins
1549 m_btnState |= wxCONTROL_CURRENT;
1550 if ( HasCapture() ) // Retain pressed state.
1551 m_btnState |= wxCONTROL_PRESSED;
1552 Refresh();
1553 }
1554 }
1555 else if ( (m_btnState & wxCONTROL_CURRENT) )
1556 {
1557 // Mouse hover ends
1558 m_btnState &= ~(wxCONTROL_CURRENT|wxCONTROL_PRESSED);
1559 Refresh();
1560 }
1561 }
1562 else if ( type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_DCLICK )
1563 {
1564 if ( flags & (wxCC_MF_ON_CLICK_AREA|wxCC_MF_ON_BUTTON) )
1565 {
1566 m_btnState |= wxCONTROL_PRESSED;
1567 Refresh();
1568
1569 if ( !(m_iFlags & wxCC_POPUP_ON_MOUSE_UP) )
1570 OnButtonClick();
1571 else
1572 // If showing popup now, do not capture mouse or there will be interference
1573 CaptureMouse();
1574 }
1575 }
1576 else if ( type == wxEVT_LEFT_UP )
1577 {
1578
1579 // Only accept event if mouse was left-press was previously accepted
1580 if ( HasCapture() )
1581 ReleaseMouse();
1582
1583 if ( m_btnState & wxCONTROL_PRESSED )
1584 {
1585 // If mouse was inside, fire the click event.
1586 if ( m_iFlags & wxCC_POPUP_ON_MOUSE_UP )
1587 {
1588 if ( flags & (wxCC_MF_ON_CLICK_AREA|wxCC_MF_ON_BUTTON) )
1589 OnButtonClick();
1590 }
1591
1592 m_btnState &= ~(wxCONTROL_PRESSED);
1593 Refresh();
1594 }
1595 }
1596 else if ( type == wxEVT_LEAVE_WINDOW )
1597 {
1598 if ( m_btnState & (wxCONTROL_CURRENT|wxCONTROL_PRESSED) )
1599 {
1600 m_btnState &= ~(wxCONTROL_CURRENT);
1601
1602 // Mouse hover ends
1603 if ( IsPopupWindowState(Hidden) )
1604 {
1605 m_btnState &= ~(wxCONTROL_PRESSED);
1606 Refresh();
1607 }
1608 }
1609 }
1610 else
1611 return false;
1612
1613 // Never have 'hot' state when popup is being shown
1614 // (this is mostly needed because of the animation).
1615 if ( !IsPopupWindowState(Hidden) )
1616 m_btnState &= ~wxCONTROL_CURRENT;
1617
1618 return true;
1619 }
1620
1621 // returns true if event was consumed or filtered
1622 bool wxComboCtrlBase::PreprocessMouseEvent( wxMouseEvent& event,
1623 int WXUNUSED(flags) )
1624 {
1625 wxLongLong t = ::wxGetLocalTimeMillis();
1626 int evtType = event.GetEventType();
1627
1628 #if USES_WXPOPUPWINDOW || USES_WXDIALOG
1629 if ( m_popupWinType != POPUPWIN_WXPOPUPTRANSIENTWINDOW )
1630 {
1631 if ( IsPopupWindowState(Visible) &&
1632 ( evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_RIGHT_DOWN ) )
1633 {
1634 HidePopup();
1635 return true;
1636 }
1637 }
1638 #endif
1639
1640 // Filter out clicks on button immediately after popup dismiss
1641 if ( evtType == wxEVT_LEFT_DOWN && t < m_timeCanAcceptClick )
1642 {
1643 event.SetEventType(0);
1644 return true;
1645 }
1646
1647 return false;
1648 }
1649
1650 void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event )
1651 {
1652 int evtType = event.GetEventType();
1653
1654 if ( (evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_LEFT_DCLICK) &&
1655 (m_windowStyle & wxCB_READONLY) )
1656 {
1657 if ( GetPopupWindowState() >= Animating )
1658 {
1659 #if USES_WXPOPUPWINDOW
1660 // Click here always hides the popup.
1661 if ( m_popupWinType == POPUPWIN_WXPOPUPWINDOW )
1662 HidePopup();
1663 #endif
1664 }
1665 else
1666 {
1667 if ( !(m_windowStyle & wxCC_SPECIAL_DCLICK) )
1668 {
1669 // In read-only mode, clicking the text is the
1670 // same as clicking the button.
1671 OnButtonClick();
1672 }
1673 else if ( /*evtType == wxEVT_LEFT_UP || */evtType == wxEVT_LEFT_DCLICK )
1674 {
1675 //if ( m_popupInterface->CycleValue() )
1676 // Refresh();
1677 if ( m_popupInterface )
1678 m_popupInterface->OnComboDoubleClick();
1679 }
1680 }
1681 }
1682 else
1683 if ( IsPopupShown() )
1684 {
1685 // relay (some) mouse events to the popup
1686 if ( evtType == wxEVT_MOUSEWHEEL )
1687 m_popup->GetEventHandler()->AddPendingEvent(event);
1688 }
1689 else if ( evtType )
1690 event.Skip();
1691 }
1692
1693 void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event)
1694 {
1695 if ( IsPopupShown() )
1696 {
1697 // pass it to the popped up control
1698 GetPopupControl()->GetControl()->GetEventHandler()->AddPendingEvent(event);
1699 }
1700 else // no popup
1701 {
1702 if ( GetParent()->HasFlag(wxTAB_TRAVERSAL) &&
1703 HandleAsNavigationKey(event) )
1704 return;
1705
1706 if ( IsKeyPopupToggle(event) )
1707 {
1708 OnButtonClick();
1709 return;
1710 }
1711
1712 int comboStyle = GetWindowStyle();
1713 wxComboPopup* popupInterface = GetPopupControl();
1714
1715 if ( !popupInterface )
1716 {
1717 event.Skip();
1718 return;
1719 }
1720
1721 int keycode = event.GetKeyCode();
1722
1723 if ( (comboStyle & wxCB_READONLY) ||
1724 (keycode != WXK_RIGHT && keycode != WXK_LEFT) )
1725 {
1726 popupInterface->OnComboKeyEvent(event);
1727 }
1728 else
1729 event.Skip();
1730 }
1731 }
1732
1733 void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& event )
1734 {
1735 if ( event.GetEventType() == wxEVT_SET_FOCUS )
1736 {
1737 wxWindow* tc = GetTextCtrl();
1738 if ( tc && tc != DoFindFocus() )
1739 #ifdef __WXMAC__
1740 m_resetFocus = true;
1741 #else
1742 {
1743 tc->SetFocus();
1744 }
1745 #endif
1746 }
1747
1748 Refresh();
1749 }
1750
1751 void wxComboCtrlBase::OnIdleEvent( wxIdleEvent& WXUNUSED(event) )
1752 {
1753 if ( m_resetFocus )
1754 {
1755 m_resetFocus = false;
1756 wxWindow* tc = GetTextCtrl();
1757 if ( tc )
1758 tc->SetFocus();
1759 }
1760 }
1761
1762 void wxComboCtrlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
1763 {
1764 OnThemeChange();
1765 // left margin may also have changed
1766 if ( !(m_iFlags & wxCC_IFLAG_LEFT_MARGIN_SET) )
1767 m_marginLeft = GetNativeTextIndent();
1768 RecalcAndRefresh();
1769 }
1770
1771 // ----------------------------------------------------------------------------
1772 // popup handling
1773 // ----------------------------------------------------------------------------
1774
1775 // Create popup window and the child control
1776 void wxComboCtrlBase::CreatePopup()
1777 {
1778 wxComboPopup* popupInterface = m_popupInterface;
1779 wxWindow* popup;
1780
1781 if ( !m_winPopup )
1782 {
1783 #ifdef wxComboPopupWindowBase2
1784 if ( m_iFlags & wxCC_IFLAG_USE_ALT_POPUP )
1785 {
1786 #if !USES_WXDIALOG
1787 m_winPopup = new wxComboPopupWindowBase2( this, wxNO_BORDER );
1788 #else
1789 m_winPopup = new wxComboPopupWindowBase2( this, wxID_ANY, wxEmptyString,
1790 wxPoint(-21,-21), wxSize(20, 20),
1791 wxNO_BORDER );
1792 #endif
1793 m_popupWinType = SECONDARY_POPUP_TYPE;
1794 }
1795 else
1796 #endif // wxComboPopupWindowBase2
1797 {
1798 m_winPopup = new wxComboPopupWindow( this, wxNO_BORDER );
1799 m_popupWinType = PRIMARY_POPUP_TYPE;
1800 }
1801 m_popupWinEvtHandler = new wxComboPopupWindowEvtHandler(this);
1802 m_winPopup->PushEventHandler(m_popupWinEvtHandler);
1803 }
1804
1805 popupInterface->Create(m_winPopup);
1806 m_popup = popup = popupInterface->GetControl();
1807
1808 m_popupExtraHandler = new wxComboPopupExtraEventHandler(this);
1809 popup->PushEventHandler( m_popupExtraHandler );
1810
1811 // This may be helpful on some platforms
1812 // (eg. it bypasses a wxGTK popupwindow bug where
1813 // window is not initially hidden when it should be)
1814 m_winPopup->Hide();
1815
1816 popupInterface->m_iFlags |= wxCP_IFLAG_CREATED;
1817 }
1818
1819 // Destroy popup window and the child control
1820 void wxComboCtrlBase::DestroyPopup()
1821 {
1822 HidePopup();
1823
1824 if ( m_popup )
1825 m_popup->RemoveEventHandler(m_popupExtraHandler);
1826
1827 delete m_popupExtraHandler;
1828
1829 delete m_popupInterface;
1830
1831 if ( m_winPopup )
1832 {
1833 m_winPopup->RemoveEventHandler(m_popupWinEvtHandler);
1834 delete m_popupWinEvtHandler;
1835 m_popupWinEvtHandler = NULL;
1836 m_winPopup->Destroy();
1837 }
1838
1839 m_popupExtraHandler = NULL;
1840 m_popupInterface = NULL;
1841 m_winPopup = NULL;
1842 m_popup = NULL;
1843 }
1844
1845 void wxComboCtrlBase::DoSetPopupControl(wxComboPopup* iface)
1846 {
1847 wxCHECK_RET( iface, wxT("no popup interface set for wxComboCtrl") );
1848
1849 DestroyPopup();
1850
1851 iface->InitBase(this);
1852 iface->Init();
1853
1854 m_popupInterface = iface;
1855
1856 if ( !iface->LazyCreate() )
1857 {
1858 CreatePopup();
1859 }
1860 else
1861 {
1862 m_popup = NULL;
1863 }
1864
1865 // This must be done after creation
1866 if ( m_valueString.length() )
1867 {
1868 iface->SetStringValue(m_valueString);
1869 //Refresh();
1870 }
1871 }
1872
1873 // Ensures there is atleast the default popup
1874 void wxComboCtrlBase::EnsurePopupControl()
1875 {
1876 if ( !m_popupInterface )
1877 SetPopupControl(NULL);
1878 }
1879
1880 void wxComboCtrlBase::OnButtonClick()
1881 {
1882 // Derived classes can override this method for totally custom
1883 // popup action
1884 if ( !IsPopupWindowState(Visible) )
1885 ShowPopup();
1886 else
1887 HidePopup();
1888 }
1889
1890 void wxComboCtrlBase::ShowPopup()
1891 {
1892 EnsurePopupControl();
1893 wxCHECK_RET( !IsPopupWindowState(Visible), wxT("popup window already shown") );
1894
1895 if ( IsPopupWindowState(Animating) )
1896 return;
1897
1898 SetFocus();
1899
1900 // Space above and below
1901 int screenHeight;
1902 wxPoint scrPos;
1903 int spaceAbove;
1904 int spaceBelow;
1905 int maxHeightPopup;
1906 wxSize ctrlSz = GetSize();
1907
1908 screenHeight = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y );
1909 scrPos = GetParent()->ClientToScreen(GetPosition());
1910
1911 spaceAbove = scrPos.y;
1912 spaceBelow = screenHeight - spaceAbove - ctrlSz.y;
1913
1914 maxHeightPopup = spaceBelow;
1915 if ( spaceAbove > spaceBelow )
1916 maxHeightPopup = spaceAbove;
1917
1918 // Width
1919 int widthPopup = ctrlSz.x + m_extLeft + m_extRight;
1920
1921 if ( widthPopup < m_widthMinPopup )
1922 widthPopup = m_widthMinPopup;
1923
1924 wxWindow* winPopup = m_winPopup;
1925 wxWindow* popup;
1926
1927 // Need to disable tab traversal of parent
1928 //
1929 // NB: This is to fix a bug in wxMSW. In theory it could also be fixed
1930 // by, for instance, adding check to window.cpp:wxWindowMSW::MSWProcessMessage
1931 // that if transient popup is open, then tab traversal is to be ignored.
1932 // However, I think this code would still be needed for cases where
1933 // transient popup doesn't work yet (wxWinCE?).
1934 wxWindow* parent = GetParent();
1935 int parentFlags = parent->GetWindowStyle();
1936 if ( parentFlags & wxTAB_TRAVERSAL )
1937 {
1938 parent->SetWindowStyle( parentFlags & ~(wxTAB_TRAVERSAL) );
1939 m_iFlags |= wxCC_IFLAG_PARENT_TAB_TRAVERSAL;
1940 }
1941
1942 if ( !winPopup )
1943 {
1944 CreatePopup();
1945 winPopup = m_winPopup;
1946 popup = m_popup;
1947 }
1948 else
1949 {
1950 popup = m_popup;
1951 }
1952
1953 winPopup->Enable();
1954
1955 wxASSERT( !m_popup || m_popup == popup ); // Consistency check.
1956
1957 wxSize adjustedSize = m_popupInterface->GetAdjustedSize(widthPopup,
1958 m_heightPopup<=0?DEFAULT_POPUP_HEIGHT:m_heightPopup,
1959 maxHeightPopup);
1960
1961 popup->SetSize(adjustedSize);
1962 popup->Move(0,0);
1963 m_popupInterface->OnPopup();
1964
1965 //
1966 // Reposition and resize popup window
1967 //
1968
1969 wxSize szp = popup->GetSize();
1970
1971 int popupX;
1972 int popupY = scrPos.y + ctrlSz.y;
1973
1974 // Default anchor is wxLEFT
1975 int anchorSide = m_anchorSide;
1976 if ( !anchorSide )
1977 anchorSide = wxLEFT;
1978
1979 int rightX = scrPos.x + ctrlSz.x + m_extRight - szp.x;
1980 int leftX = scrPos.x - m_extLeft;
1981
1982 if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft )
1983 leftX -= ctrlSz.x;
1984
1985 int screenWidth = wxSystemSettings::GetMetric( wxSYS_SCREEN_X );
1986
1987 // If there is not enough horizontal space, anchor on the other side.
1988 // If there is no space even then, place the popup at x 0.
1989 if ( anchorSide == wxRIGHT )
1990 {
1991 if ( rightX < 0 )
1992 {
1993 if ( (leftX+szp.x) < screenWidth )
1994 anchorSide = wxLEFT;
1995 else
1996 anchorSide = 0;
1997 }
1998 }
1999 else
2000 {
2001 if ( (leftX+szp.x) >= screenWidth )
2002 {
2003 if ( rightX >= 0 )
2004 anchorSide = wxRIGHT;
2005 else
2006 anchorSide = 0;
2007 }
2008 }
2009
2010 // Select x coordinate according to the anchor side
2011 if ( anchorSide == wxRIGHT )
2012 popupX = rightX;
2013 else if ( anchorSide == wxLEFT )
2014 popupX = leftX;
2015 else
2016 popupX = 0;
2017
2018 int showFlags = CanDeferShow;
2019
2020 if ( spaceBelow < szp.y )
2021 {
2022 popupY = scrPos.y - szp.y;
2023 showFlags |= ShowAbove;
2024 }
2025
2026 #if INSTALL_TOPLEV_HANDLER
2027 // Put top level window event handler into place
2028 if ( m_popupWinType == POPUPWIN_WXPOPUPWINDOW )
2029 {
2030 if ( !m_toplevEvtHandler )
2031 m_toplevEvtHandler = new wxComboFrameEventHandler(this);
2032
2033 wxWindow* toplev = ::wxGetTopLevelParent( this );
2034 wxASSERT( toplev );
2035 ((wxComboFrameEventHandler*)m_toplevEvtHandler)->OnPopup();
2036 toplev->PushEventHandler( m_toplevEvtHandler );
2037 }
2038 #endif
2039
2040 // Set string selection (must be this way instead of SetStringSelection)
2041 if ( m_text )
2042 {
2043 if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
2044 m_text->SelectAll();
2045
2046 m_popupInterface->SetStringValue( m_text->GetValue() );
2047 }
2048 else
2049 {
2050 // This is neede since focus/selection indication may change when popup is shown
2051 Refresh();
2052 }
2053
2054 // This must be after SetStringValue
2055 m_popupWinState = Animating;
2056
2057 wxRect popupWinRect( popupX, popupY, szp.x, szp.y );
2058
2059 m_popup = popup;
2060 if ( (m_iFlags & wxCC_IFLAG_DISABLE_POPUP_ANIM) ||
2061 AnimateShow( popupWinRect, showFlags ) )
2062 {
2063 DoShowPopup( popupWinRect, showFlags );
2064 }
2065 }
2066
2067 bool wxComboCtrlBase::AnimateShow( const wxRect& WXUNUSED(rect), int WXUNUSED(flags) )
2068 {
2069 return true;
2070 }
2071
2072 void wxComboCtrlBase::DoShowPopup( const wxRect& rect, int WXUNUSED(flags) )
2073 {
2074 wxWindow* winPopup = m_winPopup;
2075
2076 if ( IsPopupWindowState(Animating) )
2077 {
2078 // Make sure the popup window is shown in the right position.
2079 // Should not matter even if animation already did this.
2080
2081 // Some platforms (GTK) may like SetSize and Move to be separate
2082 // (though the bug was probably fixed).
2083 winPopup->SetSize( rect );
2084
2085 #if USES_WXPOPUPTRANSIENTWINDOW
2086 if ( m_popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW )
2087 ((wxPopupTransientWindow*)winPopup)->Popup(m_popup);
2088 else
2089 #endif
2090 winPopup->Show();
2091
2092 m_popupWinState = Visible;
2093 }
2094 else if ( IsPopupWindowState(Hidden) )
2095 {
2096 // Animation was aborted
2097
2098 wxASSERT( !winPopup->IsShown() );
2099
2100 m_popupWinState = Hidden;
2101 }
2102
2103 Refresh();
2104 }
2105
2106 void wxComboCtrlBase::OnPopupDismiss()
2107 {
2108 // Just in case, avoid double dismiss
2109 if ( IsPopupWindowState(Hidden) )
2110 return;
2111
2112 // This must be set before focus - otherwise there will be recursive
2113 // OnPopupDismisses.
2114 m_popupWinState = Hidden;
2115
2116 //SetFocus();
2117 m_winPopup->Disable();
2118
2119 // Inform popup control itself
2120 m_popupInterface->OnDismiss();
2121
2122 if ( m_popupExtraHandler )
2123 ((wxComboPopupExtraEventHandler*)m_popupExtraHandler)->OnPopupDismiss();
2124
2125 #if INSTALL_TOPLEV_HANDLER
2126 // Remove top level window event handler
2127 if ( m_toplevEvtHandler )
2128 {
2129 wxWindow* toplev = ::wxGetTopLevelParent( this );
2130 if ( toplev )
2131 toplev->RemoveEventHandler( m_toplevEvtHandler );
2132 }
2133 #endif
2134
2135 m_timeCanAcceptClick = ::wxGetLocalTimeMillis();
2136
2137 if ( m_popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW )
2138 m_timeCanAcceptClick += 150;
2139
2140 // If cursor not on dropdown button, then clear its state
2141 // (technically not required by all ports, but do it for all just in case)
2142 if ( !m_btnArea.Contains(ScreenToClient(::wxGetMousePosition())) )
2143 m_btnState = 0;
2144
2145 // Return parent's tab traversal flag.
2146 // See ShowPopup for notes.
2147 if ( m_iFlags & wxCC_IFLAG_PARENT_TAB_TRAVERSAL )
2148 {
2149 wxWindow* parent = GetParent();
2150 parent->SetWindowStyle( parent->GetWindowStyle() | wxTAB_TRAVERSAL );
2151 m_iFlags &= ~(wxCC_IFLAG_PARENT_TAB_TRAVERSAL);
2152 }
2153
2154 // refresh control (necessary even if m_text)
2155 Refresh();
2156
2157 SetFocus();
2158 }
2159
2160 void wxComboCtrlBase::HidePopup()
2161 {
2162 // Should be able to call this without popup interface
2163 if ( IsPopupWindowState(Hidden) )
2164 return;
2165
2166 // transfer value and show it in textctrl, if any
2167 if ( !IsPopupWindowState(Animating) )
2168 SetValue( m_popupInterface->GetStringValue() );
2169
2170 m_winPopup->Hide();
2171
2172 OnPopupDismiss();
2173 }
2174
2175 // ----------------------------------------------------------------------------
2176 // customization methods
2177 // ----------------------------------------------------------------------------
2178
2179 void wxComboCtrlBase::SetButtonPosition( int width, int height,
2180 int side, int spacingX )
2181 {
2182 m_btnWid = width;
2183 m_btnHei = height;
2184 m_btnSide = side;
2185 m_btnSpacingX = spacingX;
2186
2187 RecalcAndRefresh();
2188 }
2189
2190 wxSize wxComboCtrlBase::GetButtonSize()
2191 {
2192 if ( m_btnSize.x > 0 )
2193 return m_btnSize;
2194
2195 wxSize retSize(m_btnWid,m_btnHei);
2196
2197 // Need to call CalculateAreas now if button size is
2198 // is not explicitly specified.
2199 if ( retSize.x <= 0 || retSize.y <= 0)
2200 {
2201 OnResize();
2202
2203 retSize = m_btnSize;
2204 }
2205
2206 return retSize;
2207 }
2208
2209 void wxComboCtrlBase::SetButtonBitmaps( const wxBitmap& bmpNormal,
2210 bool blankButtonBg,
2211 const wxBitmap& bmpPressed,
2212 const wxBitmap& bmpHover,
2213 const wxBitmap& bmpDisabled )
2214 {
2215 m_bmpNormal = bmpNormal;
2216 m_blankButtonBg = blankButtonBg;
2217
2218 if ( bmpPressed.Ok() )
2219 m_bmpPressed = bmpPressed;
2220 else
2221 m_bmpPressed = bmpNormal;
2222
2223 if ( bmpHover.Ok() )
2224 m_bmpHover = bmpHover;
2225 else
2226 m_bmpHover = bmpNormal;
2227
2228 if ( bmpDisabled.Ok() )
2229 m_bmpDisabled = bmpDisabled;
2230 else
2231 m_bmpDisabled = bmpNormal;
2232
2233 RecalcAndRefresh();
2234 }
2235
2236 void wxComboCtrlBase::SetCustomPaintWidth( int width )
2237 {
2238 if ( m_text )
2239 {
2240 // move textctrl accordingly
2241 wxRect r = m_text->GetRect();
2242 int inc = width - m_widthCustomPaint;
2243 r.x += inc;
2244 r.width -= inc;
2245 m_text->SetSize( r );
2246 }
2247
2248 m_widthCustomPaint = width;
2249
2250 RecalcAndRefresh();
2251 }
2252
2253 bool wxComboCtrlBase::DoSetMargins(const wxPoint& margins)
2254 {
2255 // For general sanity's sake, we ignore top margin. Instead
2256 // we will always try to center the text vertically.
2257 bool res = true;
2258
2259 if ( margins.x != -1 )
2260 {
2261 m_marginLeft = margins.x;
2262 m_iFlags |= wxCC_IFLAG_LEFT_MARGIN_SET;
2263 }
2264 else
2265 {
2266 m_marginLeft = GetNativeTextIndent();
2267 m_iFlags &= ~(wxCC_IFLAG_LEFT_MARGIN_SET);
2268 }
2269
2270 if ( margins.y != -1 )
2271 {
2272 res = false;
2273 }
2274
2275 RecalcAndRefresh();
2276
2277 return res;
2278 }
2279
2280 wxPoint wxComboCtrlBase::DoGetMargins() const
2281 {
2282 return wxPoint(m_marginLeft, -1);
2283 }
2284
2285 #if WXWIN_COMPATIBILITY_2_6
2286 void wxComboCtrlBase::SetTextIndent( int indent )
2287 {
2288 if ( indent < 0 )
2289 {
2290 m_marginLeft = GetNativeTextIndent();
2291 m_iFlags &= ~(wxCC_IFLAG_LEFT_MARGIN_SET);
2292 }
2293 else
2294 {
2295 m_marginLeft = indent;
2296 m_iFlags |= wxCC_IFLAG_LEFT_MARGIN_SET;
2297 }
2298
2299 RecalcAndRefresh();
2300 }
2301
2302 wxCoord wxComboCtrlBase::GetTextIndent() const
2303 {
2304 return m_marginLeft;
2305 }
2306 #endif
2307
2308 wxCoord wxComboCtrlBase::GetNativeTextIndent() const
2309 {
2310 return DEFAULT_TEXT_INDENT;
2311 }
2312
2313 // ----------------------------------------------------------------------------
2314 // methods forwarded to wxTextCtrl
2315 // ----------------------------------------------------------------------------
2316
2317 wxString wxComboCtrlBase::GetValue() const
2318 {
2319 if ( m_text )
2320 return m_text->GetValue();
2321 return m_valueString;
2322 }
2323
2324 void wxComboCtrlBase::SetValueWithEvent(const wxString& value, bool withEvent)
2325 {
2326 if ( m_text )
2327 {
2328 if ( !withEvent )
2329 m_ignoreEvtText++;
2330
2331 m_text->SetValue(value);
2332
2333 if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
2334 m_text->SelectAll();
2335 }
2336
2337 // Since wxComboPopup may want to paint the combo as well, we need
2338 // to set the string value here (as well as sometimes in ShowPopup).
2339 if ( m_valueString != value )
2340 {
2341 m_valueString = value;
2342
2343 EnsurePopupControl();
2344
2345 if (m_popupInterface)
2346 m_popupInterface->SetStringValue(value);
2347 }
2348
2349 Refresh();
2350 }
2351
2352 void wxComboCtrlBase::SetValue(const wxString& value)
2353 {
2354 SetValueWithEvent(value, false);
2355 }
2356
2357 // In this SetValue variant wxComboPopup::SetStringValue is not called
2358 void wxComboCtrlBase::SetText(const wxString& value)
2359 {
2360 // Unlike in SetValue(), this must be called here or
2361 // the behaviour will no be consistent in readonlys.
2362 EnsurePopupControl();
2363
2364 m_valueString = value;
2365
2366 if ( m_text )
2367 {
2368 m_ignoreEvtText++;
2369 m_text->SetValue( value );
2370 }
2371
2372 Refresh();
2373 }
2374
2375 void wxComboCtrlBase::Copy()
2376 {
2377 if ( m_text )
2378 m_text->Copy();
2379 }
2380
2381 void wxComboCtrlBase::Cut()
2382 {
2383 if ( m_text )
2384 m_text->Cut();
2385 }
2386
2387 void wxComboCtrlBase::Paste()
2388 {
2389 if ( m_text )
2390 m_text->Paste();
2391 }
2392
2393 void wxComboCtrlBase::SetInsertionPoint(long pos)
2394 {
2395 if ( m_text )
2396 m_text->SetInsertionPoint(pos);
2397 }
2398
2399 void wxComboCtrlBase::SetInsertionPointEnd()
2400 {
2401 if ( m_text )
2402 m_text->SetInsertionPointEnd();
2403 }
2404
2405 long wxComboCtrlBase::GetInsertionPoint() const
2406 {
2407 if ( m_text )
2408 return m_text->GetInsertionPoint();
2409
2410 return 0;
2411 }
2412
2413 long wxComboCtrlBase::GetLastPosition() const
2414 {
2415 if ( m_text )
2416 return m_text->GetLastPosition();
2417
2418 return 0;
2419 }
2420
2421 void wxComboCtrlBase::Replace(long from, long to, const wxString& value)
2422 {
2423 if ( m_text )
2424 m_text->Replace(from, to, value);
2425 }
2426
2427 void wxComboCtrlBase::Remove(long from, long to)
2428 {
2429 if ( m_text )
2430 m_text->Remove(from, to);
2431 }
2432
2433 void wxComboCtrlBase::SetSelection(long from, long to)
2434 {
2435 if ( m_text )
2436 m_text->SetSelection(from, to);
2437 }
2438
2439 void wxComboCtrlBase::Undo()
2440 {
2441 if ( m_text )
2442 m_text->Undo();
2443 }
2444
2445 #endif // wxUSE_COMBOCTRL