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