]> git.saurik.com Git - wxWidgets.git/blob - src/univ/combobox.cpp
Applied patch [ 1381420 ] wxHTTP Basic authentication support
[wxWidgets.git] / src / univ / combobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/univ/combobox.cpp
3 // Purpose:     wxComboControl and wxComboBox implementation
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     15.12.00
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
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_COMBOBOX
27
28 #ifndef WX_PRECOMP
29     #include "wx/log.h"
30
31     #include "wx/button.h"
32     #include "wx/combobox.h"
33     #include "wx/listbox.h"
34     #include "wx/textctrl.h"
35     #include "wx/bmpbuttn.h"
36
37     #include "wx/validate.h"
38 #endif
39
40 #include "wx/tooltip.h"
41 #include "wx/popupwin.h"
42
43 #include "wx/univ/renderer.h"
44 #include "wx/univ/inphand.h"
45 #include "wx/univ/theme.h"
46
47 /*
48    The keyboard event flow:
49
50    1. they always come to the text ctrl
51    2. it forwards the ones it doesn't process to the wxComboControl
52    3. which passes them to the popup window if it is popped up
53  */
54
55 // constants
56 // ----------------------------------------------------------------------------
57
58 // the margin between the text control and the combo button
59 static const wxCoord g_comboMargin = 2;
60
61 // ----------------------------------------------------------------------------
62 // wxComboButton is just a normal button except that it sends commands to the
63 // combobox and not its parent
64 // ----------------------------------------------------------------------------
65
66 class wxComboButton : public wxBitmapButton
67 {
68 public:
69     wxComboButton(wxComboControl *combo)
70         : wxBitmapButton(combo->GetParent(), wxID_ANY, wxNullBitmap,
71                          wxDefaultPosition, wxDefaultSize,
72                          wxBORDER_NONE | wxBU_EXACTFIT)
73     {
74         m_combo = combo;
75
76         wxBitmap bmpNormal, bmpFocus, bmpPressed, bmpDisabled;
77
78         GetRenderer()->GetComboBitmaps(&bmpNormal,
79                                        &bmpFocus,
80                                        &bmpPressed,
81                                        &bmpDisabled);
82
83         SetBitmapLabel(bmpNormal);
84         SetBitmapFocus(bmpFocus.Ok() ? bmpFocus : bmpNormal);
85         SetBitmapSelected(bmpPressed.Ok() ? bmpPressed : bmpNormal);
86         SetBitmapDisabled(bmpDisabled.Ok() ? bmpDisabled : bmpNormal);
87
88         SetBestSize(wxDefaultSize);
89     }
90
91 protected:
92     void OnButton(wxCommandEvent& WXUNUSED(event)) { m_combo->ShowPopup(); }
93
94     virtual wxSize DoGetBestClientSize() const
95     {
96         const wxBitmap& bmp = GetBitmapLabel();
97
98         return wxSize(bmp.GetWidth(), bmp.GetHeight());
99
100     }
101
102 private:
103     wxComboControl *m_combo;
104
105     DECLARE_EVENT_TABLE()
106 };
107
108 // ----------------------------------------------------------------------------
109 // wxComboListBox is a listbox modified to be used as a popup window in a
110 // combobox
111 // ----------------------------------------------------------------------------
112
113 class wxComboListBox : public wxListBox, public wxComboPopup
114 {
115 public:
116     // ctor and dtor
117     wxComboListBox(wxComboControl *combo, int style = 0);
118     virtual ~wxComboListBox();
119
120     // implement wxComboPopup methods
121     virtual bool SetSelection(const wxString& s);
122     virtual wxControl *GetControl() { return this; }
123     virtual void OnShow();
124     virtual wxCoord GetBestWidth() const;
125
126     // fix virtual function hiding
127     virtual void SetSelection(int n) { DoSetSelection(n, true); }
128     void SetSelection(int n, bool select) { DoSetSelection(n, select); }
129
130 protected:
131     // we shouldn't return height too big from here
132     virtual wxSize DoGetBestClientSize() const;
133
134     // filter mouse move events happening outside the list box
135     void OnMouseMove(wxMouseEvent& event);
136
137     // set m_clicked value from here
138     void OnLeftUp(wxMouseEvent& event);
139
140     // called whenever the user selects or activates a listbox item
141     void OnSelect(wxCommandEvent& event);
142
143     // used to process wxUniv actions
144     bool PerformAction(const wxControlAction& action,
145                        long numArg,
146                        const wxString& strArg);
147
148 private:
149     // has the mouse been released on this control?
150     bool m_clicked;
151
152     DECLARE_EVENT_TABLE()
153 };
154
155 // ----------------------------------------------------------------------------
156 // wxComboTextCtrl is a simple text ctrl which forwards
157 // wxEVT_COMMAND_TEXT_UPDATED events and all key events to the combobox
158 // ----------------------------------------------------------------------------
159
160 class wxComboTextCtrl : public wxTextCtrl
161 {
162 public:
163     wxComboTextCtrl(wxComboControl *combo,
164                     const wxString& value,
165                     long style,
166                     const wxValidator& validator);
167
168 protected:
169     void OnKey(wxKeyEvent& event);
170     void OnText(wxCommandEvent& event);
171
172 private:
173     wxComboControl *m_combo;
174
175     DECLARE_EVENT_TABLE()
176 };
177
178 // ----------------------------------------------------------------------------
179 // event tables and such
180 // ----------------------------------------------------------------------------
181
182 BEGIN_EVENT_TABLE(wxComboButton, wxButton)
183     EVT_BUTTON(wxID_ANY, wxComboButton::OnButton)
184 END_EVENT_TABLE()
185
186 BEGIN_EVENT_TABLE(wxComboListBox, wxListBox)
187     EVT_LISTBOX(wxID_ANY, wxComboListBox::OnSelect)
188     EVT_LISTBOX_DCLICK(wxID_ANY, wxComboListBox::OnSelect)
189     EVT_MOTION(wxComboListBox::OnMouseMove)
190     EVT_LEFT_UP(wxComboListBox::OnLeftUp)
191 END_EVENT_TABLE()
192
193 BEGIN_EVENT_TABLE(wxComboControl, wxControl)
194     EVT_KEY_DOWN(wxComboControl::OnKey)
195     EVT_KEY_UP(wxComboControl::OnKey)
196 END_EVENT_TABLE()
197
198 BEGIN_EVENT_TABLE(wxComboTextCtrl, wxTextCtrl)
199     EVT_KEY_DOWN(wxComboTextCtrl::OnKey)
200     EVT_KEY_UP(wxComboTextCtrl::OnKey)
201     EVT_TEXT(wxID_ANY, wxComboTextCtrl::OnText)
202 END_EVENT_TABLE()
203
204 IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
205
206 // ============================================================================
207 // implementation
208 // ============================================================================
209
210 // ----------------------------------------------------------------------------
211 // wxComboControl creation
212 // ----------------------------------------------------------------------------
213
214 void wxComboControl::Init()
215 {
216     m_popup = (wxComboPopup *)NULL;
217     m_winPopup = (wxPopupComboWindow *)NULL;
218     m_isPopupShown = false;
219     m_btn = NULL;
220     m_text = NULL;
221 }
222
223 bool wxComboControl::Create(wxWindow *parent,
224                             wxWindowID id,
225                             const wxString& value,
226                             const wxPoint& pos,
227                             const wxSize& size,
228                             long style,
229                             const wxValidator& validator,
230                             const wxString& name)
231 {
232     // first create our own window, i.e. the one which will contain all
233     // subcontrols
234     style &= ~wxBORDER_NONE;
235     style |= wxBORDER_SUNKEN;
236     if ( !wxControl::Create(parent, id, pos, size, style, validator, name) )
237         return false;
238
239     // create the text control and the button as our siblings (*not* children),
240     // don't care about size/position here - they will be set in DoMoveWindow()
241     m_btn = new wxComboButton(this);
242     m_text = new wxComboTextCtrl(this,
243                                  value,
244                                  style & wxCB_READONLY ? wxTE_READONLY : 0,
245                                  validator);
246
247     // for compatibility with the other ports, the height specified is the
248     // combined height of the combobox itself and the popup
249     if ( size.y == wxDefaultCoord )
250     {
251         // ok, use default height for popup too
252         m_heightPopup = wxDefaultCoord;
253     }
254     else
255     {
256         m_heightPopup = size.y - DoGetBestSize().y;
257     }
258
259     SetBestSize(size);
260     Move(pos);
261
262     // create the popup window immediately here to allow creating the controls
263     // with parent == GetPopupWindow() from the derived class ctor
264     m_winPopup = new wxPopupComboWindow(this);
265
266     // have to disable this window to avoid interfering it with message
267     // processing to the text and the button... but pretend it is enabled to
268     // make IsEnabled() return true
269     wxControl::Enable(false); // don't use non virtual Disable() here!
270     m_isEnabled = true;
271
272     CreateInputHandler(wxINP_HANDLER_COMBOBOX);
273
274     return true;
275 }
276
277 wxComboControl::~wxComboControl()
278 {
279     // as the button and the text control are the parent's children and not
280     // ours, we have to delete them manually - they are not deleted
281     // automatically by wxWidgets when we're deleted
282     delete m_btn;
283     delete m_text;
284
285     delete m_winPopup;
286 }
287
288 // ----------------------------------------------------------------------------
289 // geometry stuff
290 // ----------------------------------------------------------------------------
291
292 void wxComboControl::DoSetSize(int x, int y,
293                                int width, int WXUNUSED(height),
294                                int sizeFlags)
295 {
296     // combo height is always fixed
297     wxControl::DoSetSize(x, y, width, DoGetBestSize().y, sizeFlags);
298 }
299
300 wxSize wxComboControl::DoGetBestClientSize() const
301 {
302     wxSize sizeBtn = m_btn->GetBestSize(),
303            sizeText = m_text->GetBestSize();
304     wxCoord widthPopup = 0;
305
306     if (m_popup)
307     {
308         widthPopup = m_popup->GetBestWidth();
309     }
310
311     return wxSize(wxMax(sizeText.x + g_comboMargin + sizeBtn.x, widthPopup),
312                   wxMax(sizeBtn.y, sizeText.y));
313 }
314
315 void wxComboControl::DoMoveWindow(int x, int y, int width, int height)
316 {
317     wxControl::DoMoveWindow(x, y, width, height);
318
319     // position the subcontrols inside the client area
320     wxRect rectBorders = GetRenderer()->GetBorderDimensions(GetBorder());
321     x += rectBorders.x;
322     y += rectBorders.y;
323     width -= rectBorders.x + rectBorders.width;
324     height -= rectBorders.y + rectBorders.height;
325
326     wxSize sizeBtn = m_btn->GetBestSize();
327
328     wxCoord wText = width - sizeBtn.x;
329     wxPoint p = GetParent() ? GetParent()->GetClientAreaOrigin() : wxPoint(0,0);
330     m_text->SetSize(x - p.x, y - p.y, wText, height);
331     m_btn->SetSize(x - p.x + wText, y - p.y, sizeBtn.x, height);
332 }
333
334 // ----------------------------------------------------------------------------
335 // operations
336 // ----------------------------------------------------------------------------
337
338 bool wxComboControl::Enable(bool enable)
339 {
340     if ( !wxControl::Enable(enable) )
341         return false;
342
343     m_btn->Enable(enable);
344     m_text->Enable(enable);
345
346     return true;
347 }
348
349 bool wxComboControl::Show(bool show)
350 {
351     if ( !wxControl::Show(show) )
352         return false;
353
354     if (m_btn)
355         m_btn->Show(show);
356
357     if (m_text)
358         m_text->Show(show);
359
360     return true;
361 }
362
363 #if wxUSE_TOOLTIPS
364 void wxComboControl::DoSetToolTip(wxToolTip *tooltip)
365 {
366     wxControl::DoSetToolTip(tooltip);
367
368     // Set tool tip for button and text box
369     if (m_text && m_btn)
370     {
371         if (tooltip)
372         {
373             const wxString &tip = tooltip->GetTip();
374             m_text->SetToolTip(tip);
375             m_btn->SetToolTip(tip);
376         }
377         else
378         {
379             m_text->SetToolTip(NULL);
380             m_btn->SetToolTip(NULL);
381         }
382     }
383 }
384 #endif // wxUSE_TOOLTIPS
385
386 // ----------------------------------------------------------------------------
387 // popup window handling
388 // ----------------------------------------------------------------------------
389
390 void wxComboControl::SetPopupControl(wxComboPopup *popup)
391 {
392     m_popup = popup;
393 }
394
395 void wxComboControl::ShowPopup()
396 {
397     wxCHECK_RET( m_popup, _T("no popup to show in wxComboControl") );
398     wxCHECK_RET( !IsPopupShown(), _T("popup window already shown") );
399
400     wxControl *control = m_popup->GetControl();
401
402     // size and position the popup window correctly
403     m_winPopup->SetSize(GetSize().x,
404                         m_heightPopup == wxDefaultCoord ? control->GetBestSize().y
405                                             : m_heightPopup);
406     wxSize sizePopup = m_winPopup->GetClientSize();
407     control->SetSize(0, 0, sizePopup.x, sizePopup.y);
408
409     // some controls don't accept the size we give then: e.g. a listbox may
410     // require more space to show its last row
411     wxSize sizeReal = control->GetSize();
412     if ( sizeReal != sizePopup )
413     {
414         m_winPopup->SetClientSize(sizeReal);
415     }
416
417     m_winPopup->PositionNearCombo();
418
419     // show it
420     m_popup->OnShow();
421     m_winPopup->Popup(m_text);
422     m_text->SelectAll();
423     m_popup->SetSelection(m_text->GetValue());
424
425     m_isPopupShown = true;
426 }
427
428 void wxComboControl::HidePopup()
429 {
430     wxCHECK_RET( m_popup, _T("no popup to hide in wxComboControl") );
431     wxCHECK_RET( IsPopupShown(), _T("popup window not shown") );
432
433     m_winPopup->Dismiss();
434
435     m_isPopupShown = false;
436 }
437
438 void wxComboControl::OnSelect(const wxString& value)
439 {
440     m_text->SetValue(value);
441     m_text->SelectAll();
442
443     OnDismiss();
444 }
445
446 void wxComboControl::OnDismiss()
447 {
448     HidePopup();
449     m_text->SetFocus();
450 }
451
452 // ----------------------------------------------------------------------------
453 // wxComboTextCtrl
454 // ----------------------------------------------------------------------------
455
456 wxComboTextCtrl::wxComboTextCtrl(wxComboControl *combo,
457                                  const wxString& value,
458                                  long style,
459                                  const wxValidator& validator)
460                : wxTextCtrl(combo->GetParent(), wxID_ANY, value,
461                             wxDefaultPosition, wxDefaultSize,
462                             wxBORDER_NONE | style,
463                             validator)
464 {
465     m_combo = combo;
466 }
467
468 void wxComboTextCtrl::OnText(wxCommandEvent& event)
469 {
470     if ( m_combo->IsPopupShown() )
471     {
472         m_combo->GetPopupControl()->SetSelection(GetValue());
473     }
474
475     // we need to make a copy of the event to have the correct originating
476     // object and id
477     wxCommandEvent event2 = event;
478     event2.SetEventObject(m_combo);
479     event2.SetId(m_combo->GetId());
480
481     // there is a small incompatibility with wxMSW here: the combobox gets the
482     // event before the text control in our case which corresponds to SMW
483     // CBN_EDITUPDATE notification and not CBN_EDITCHANGE one wxMSW currently
484     // uses
485     //
486     // if this is really a problem, we can play games with the event handlers
487     // to circumvent this
488     (void)m_combo->ProcessEvent(event2);
489
490     event.Skip();
491 }
492
493 // pass the keys we don't process to the combo first
494 void wxComboTextCtrl::OnKey(wxKeyEvent& event)
495 {
496     switch ( event.GetKeyCode() )
497     {
498         case WXK_RETURN:
499             // the popup control gets it first but only if it is shown
500             if ( !m_combo->IsPopupShown() )
501                 break;
502             //else: fall through
503
504         case WXK_UP:
505         case WXK_DOWN:
506         case WXK_ESCAPE:
507         case WXK_PAGEDOWN:
508         case WXK_PAGEUP:
509         case WXK_PRIOR:
510         case WXK_NEXT:
511             (void)m_combo->ProcessEvent(event);
512             return;
513     }
514
515     event.Skip();
516 }
517
518 // ----------------------------------------------------------------------------
519 // wxComboListBox
520 // ----------------------------------------------------------------------------
521
522 wxComboListBox::wxComboListBox(wxComboControl *combo, int style)
523               : wxListBox(combo->GetPopupWindow(), wxID_ANY,
524                           wxDefaultPosition, wxDefaultSize,
525                           0, NULL,
526                           wxBORDER_SIMPLE | wxLB_INT_HEIGHT | style),
527                 wxComboPopup(combo)
528 {
529     // we don't react to the mouse events outside the window at all
530     StopAutoScrolling();
531 }
532
533 wxComboListBox::~wxComboListBox()
534 {
535 }
536
537 bool wxComboListBox::SetSelection(const wxString& value)
538 {
539     // FindItem() would just find the current item for an empty string (it
540     // always matches), but we want to show the first one in such case
541     if ( value.empty() )
542     {
543         if ( GetCount() )
544         {
545             wxListBox::SetSelection(0);
546         }
547         //else: empty listbox - nothing to do
548     }
549     else if ( !FindItem(value) )
550     {
551         // no match att all
552         return false;
553     }
554
555     return true;
556 }
557
558 void wxComboListBox::OnSelect(wxCommandEvent& event)
559 {
560     if ( m_clicked )
561     {
562         // first update the combo and close the listbox
563         m_combo->OnSelect(event.GetString());
564
565         // next let the user code have the event
566
567         // all fields are already filled by the listbox, just change the event
568         // type and send it to the combo
569         wxCommandEvent event2 = event;
570         event2.SetEventType(wxEVT_COMMAND_COMBOBOX_SELECTED);
571         event2.SetEventObject(m_combo);
572         event2.SetId(m_combo->GetId());
573         m_combo->ProcessEvent(event2);
574     }
575     //else: ignore the events resulting from just moving the mouse initially
576 }
577
578 void wxComboListBox::OnShow()
579 {
580     // nobody clicked us yet
581     m_clicked = false;
582 }
583
584 bool wxComboListBox::PerformAction(const wxControlAction& action,
585                                    long numArg,
586                                    const wxString& strArg)
587
588 {
589     if ( action == wxACTION_LISTBOX_FIND )
590     {
591         // we don't let the listbox handle this as instead of just using the
592         // single key presses, as usual, we use the text ctrl value as prefix
593         // and this is done by wxComboControl itself
594         return true;
595     }
596
597     return wxListBox::PerformAction(action, numArg, strArg);
598 }
599
600 void wxComboListBox::OnLeftUp(wxMouseEvent& event)
601 {
602     // we should dismiss the combo now
603     m_clicked = true;
604
605     event.Skip();
606 }
607
608 void wxComboListBox::OnMouseMove(wxMouseEvent& event)
609 {
610     // while a wxComboListBox is shown, it always has capture, so if it doesn't
611     // we're about to go away anyhow (normally this shouldn't happen at all,
612     // but I don't put assert here as it just might do on other platforms and
613     // it doesn't break anything anyhow)
614     if ( this == wxWindow::GetCapture() )
615     {
616         if ( HitTest(event.GetPosition()) == wxHT_WINDOW_INSIDE )
617         {
618             event.Skip();
619         }
620         //else: popup shouldn't react to the mouse motions outside it, it only
621         //      captures the mouse to be able to detect when it must be
622         //      dismissed, so don't call Skip()
623     }
624 }
625
626 wxCoord wxComboListBox::GetBestWidth() const
627 {
628     wxSize size = wxListBox::GetBestSize();
629     return size.x;
630 }
631
632 wxSize wxComboListBox::DoGetBestClientSize() const
633 {
634     // don't return size too big or we risk to not fit on the screen
635     wxSize size = wxListBox::DoGetBestClientSize();
636     wxCoord hChar = GetCharHeight();
637
638     int nLines = size.y / hChar;
639
640     // 10 is the same limit as used by wxMSW
641     if ( nLines > 10 )
642     {
643         size.y = 10*hChar;
644     }
645
646     return size;
647 }
648
649 // ----------------------------------------------------------------------------
650 // wxComboBox
651 // ----------------------------------------------------------------------------
652
653 void wxComboBox::Init()
654 {
655     m_lbox = (wxListBox *)NULL;
656 }
657
658 wxComboBox::wxComboBox(wxWindow *parent,
659                        wxWindowID id,
660                        const wxString& value,
661                        const wxPoint& pos,
662                        const wxSize& size,
663                        const wxArrayString& choices,
664                        long style,
665                        const wxValidator& validator,
666                        const wxString& name)
667 {
668     Init();
669
670     Create(parent, id, value, pos, size, choices, style, validator, name);
671 }
672
673 bool wxComboBox::Create(wxWindow *parent,
674                         wxWindowID id,
675                         const wxString& value,
676                         const wxPoint& pos,
677                         const wxSize& size,
678                         const wxArrayString& choices,
679                         long style,
680                         const wxValidator& validator,
681                         const wxString& name)
682 {
683     wxCArrayString chs(choices);
684
685     return Create(parent, id, value, pos, size, chs.GetCount(),
686                   chs.GetStrings(), style, validator, name);
687 }
688
689 bool wxComboBox::Create(wxWindow *parent,
690                         wxWindowID id,
691                         const wxString& value,
692                         const wxPoint& pos,
693                         const wxSize& size,
694                         int n,
695                         const wxString choices[],
696                         long style,
697                         const wxValidator& validator,
698                         const wxString& name)
699 {
700     if ( !wxComboControl::Create(parent, id, value, pos, size, style,
701                                  validator, name) )
702     {
703         return false;
704     }
705
706     wxComboListBox *combolbox =
707         new wxComboListBox(this, style & wxCB_SORT ? wxLB_SORT : 0);
708     m_lbox = combolbox;
709     m_lbox->Set(n, choices);
710
711     SetPopupControl(combolbox);
712
713     return true;
714 }
715
716 wxComboBox::~wxComboBox()
717 {
718 }
719
720 // ----------------------------------------------------------------------------
721 // wxComboBox methods forwarded to wxTextCtrl
722 // ----------------------------------------------------------------------------
723
724 wxString wxComboBox::GetValue() const
725 {
726     return GetText()->GetValue();
727 }
728
729 void wxComboBox::SetValue(const wxString& value)
730 {
731     GetText()->SetValue(value);
732 }
733
734 void wxComboBox::Copy()
735 {
736     GetText()->Copy();
737 }
738
739 void wxComboBox::Cut()
740 {
741     GetText()->Cut();
742 }
743
744 void wxComboBox::Paste()
745 {
746     GetText()->Paste();
747 }
748
749 void wxComboBox::SetInsertionPoint(long pos)
750 {
751     GetText()->SetInsertionPoint(pos);
752 }
753
754 void wxComboBox::SetInsertionPointEnd()
755 {
756     GetText()->SetInsertionPointEnd();
757 }
758
759 long wxComboBox::GetInsertionPoint() const
760 {
761     return GetText()->GetInsertionPoint();
762 }
763
764 wxTextPos wxComboBox::GetLastPosition() const
765 {
766     return GetText()->GetLastPosition();
767 }
768
769 void wxComboBox::Replace(long from, long to, const wxString& value)
770 {
771     GetText()->Replace(from, to, value);
772 }
773
774 void wxComboBox::Remove(long from, long to)
775 {
776     GetText()->Remove(from, to);
777 }
778
779 void wxComboBox::SetSelection(long from, long to)
780 {
781     GetText()->SetSelection(from, to);
782 }
783
784 void wxComboBox::SetEditable(bool editable)
785 {
786     GetText()->SetEditable(editable);
787 }
788
789 // ----------------------------------------------------------------------------
790 // wxComboBox methods forwarded to wxListBox
791 // ----------------------------------------------------------------------------
792
793 void wxComboBox::Clear()
794 {
795     GetLBox()->Clear();
796     GetText()->SetValue(wxEmptyString);
797 }
798
799 void wxComboBox::Delete(int n)
800 {
801     wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid index in wxComboBox::Delete") );
802
803     if (GetSelection() == n)
804         GetText()->SetValue(wxEmptyString);
805
806     GetLBox()->Delete(n);
807 }
808
809 int wxComboBox::GetCount() const
810 {
811     return GetLBox()->GetCount();
812 }
813
814 wxString wxComboBox::GetString(int n) const
815 {
816     wxCHECK_MSG( (n >= 0) && (n < GetCount()), wxEmptyString, _T("invalid index in wxComboBox::GetString") );
817
818     return GetLBox()->GetString(n);
819 }
820
821 void wxComboBox::SetString(int n, const wxString& s)
822 {
823     wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid index in wxComboBox::SetString") );
824
825     GetLBox()->SetString(n, s);
826 }
827
828 int wxComboBox::FindString(const wxString& s, bool bCase) const
829 {
830     return GetLBox()->FindString(s, bCase);
831 }
832
833 void wxComboBox::SetSelection(int n)
834 {
835     wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid index in wxComboBox::Select") );
836
837     GetLBox()->SetSelection(n);
838     GetText()->SetValue(GetLBox()->GetString(n));
839 }
840
841 int wxComboBox::GetSelection() const
842 {
843 #if 1 // FIXME:: What is the correct behavior?
844     // if the current value isn't one of the listbox strings, return -1
845     return GetLBox()->GetSelection();
846 #else
847     // Why oh why is this done this way?
848     // It is not because the value displayed in the text can be found
849     // in the list that it is the item that is selected!
850     return FindString(GetText()->GetValue());
851 #endif
852 }
853
854 int wxComboBox::DoAppend(const wxString& item)
855 {
856     return GetLBox()->Append(item);
857 }
858
859 int wxComboBox::DoInsert(const wxString& item, int pos)
860 {
861     wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into sorted list"));
862     wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
863
864     if (pos == GetCount())
865         return DoAppend(item);
866
867     GetLBox()->Insert(item, pos);
868     return pos;
869 }
870
871 void wxComboBox::DoSetItemClientData(int n, void* clientData)
872 {
873     GetLBox()->SetClientData(n, clientData);
874 }
875
876 void *wxComboBox::DoGetItemClientData(int n) const
877 {
878     return GetLBox()->GetClientData(n);
879 }
880
881 void wxComboBox::DoSetItemClientObject(int n, wxClientData* clientData)
882 {
883     GetLBox()->SetClientObject(n, clientData);
884 }
885
886 wxClientData* wxComboBox::DoGetItemClientObject(int n) const
887 {
888     return GetLBox()->GetClientObject(n);
889 }
890
891 bool wxComboBox::IsEditable() const
892 {
893     return GetText() != NULL && (!HasFlag(wxCB_READONLY) || GetText()->IsEditable());
894 }
895
896 void wxComboBox::Undo()
897 {
898     if (IsEditable())
899         GetText()->Undo();
900 }
901
902 void wxComboBox::Redo()
903 {
904     if (IsEditable())
905         GetText()->Redo();
906 }
907
908 void wxComboBox::SelectAll()
909 {
910     GetText()->SelectAll();
911 }
912
913 bool wxComboBox::CanCopy() const
914 {
915     if (GetText() != NULL)
916         return GetText()->CanCopy();
917     else
918         return false;
919 }
920
921 bool wxComboBox::CanCut() const
922 {
923     if (GetText() != NULL)
924         return GetText()->CanCut();
925     else
926         return false;
927 }
928
929 bool wxComboBox::CanPaste() const
930 {
931     if (IsEditable())
932         return GetText()->CanPaste();
933     else
934         return false;
935 }
936
937 bool wxComboBox::CanUndo() const
938 {
939     if (IsEditable())
940         return GetText()->CanUndo();
941     else
942         return false;
943 }
944
945 bool wxComboBox::CanRedo() const
946 {
947     if (IsEditable())
948         return GetText()->CanRedo();
949     else
950         return false;
951 }
952
953
954 // ----------------------------------------------------------------------------
955 // input handling
956 // ----------------------------------------------------------------------------
957
958 void wxComboControl::OnKey(wxKeyEvent& event)
959 {
960     if ( m_isPopupShown )
961     {
962         // pass it to the popped up control
963         (void)m_popup->GetControl()->ProcessEvent(event);
964     }
965     else // no popup
966     {
967         event.Skip();
968     }
969 }
970
971 bool wxComboControl::PerformAction(const wxControlAction& action,
972                                    long numArg,
973                                    const wxString& strArg)
974 {
975     bool processed = false;
976     if ( action == wxACTION_COMBOBOX_POPUP )
977     {
978         if ( !m_isPopupShown )
979         {
980             ShowPopup();
981
982             processed = true;
983         }
984     }
985     else if ( action == wxACTION_COMBOBOX_DISMISS )
986     {
987         if ( m_isPopupShown )
988         {
989             HidePopup();
990
991             processed = true;
992         }
993     }
994
995     if ( !processed )
996     {
997         // pass along
998         return wxControl::PerformAction(action, numArg, strArg);
999     }
1000
1001     return true;
1002 }
1003
1004 // ----------------------------------------------------------------------------
1005 // wxStdComboBoxInputHandler
1006 // ----------------------------------------------------------------------------
1007
1008 wxStdComboBoxInputHandler::wxStdComboBoxInputHandler(wxInputHandler *inphand)
1009                          : wxStdInputHandler(inphand)
1010 {
1011 }
1012
1013 bool wxStdComboBoxInputHandler::HandleKey(wxInputConsumer *consumer,
1014                                           const wxKeyEvent& event,
1015                                           bool pressed)
1016 {
1017     if ( pressed )
1018     {
1019         wxControlAction action;
1020         switch ( event.GetKeyCode() )
1021         {
1022             case WXK_DOWN:
1023                 action = wxACTION_COMBOBOX_POPUP;
1024                 break;
1025
1026             case WXK_ESCAPE:
1027                 action = wxACTION_COMBOBOX_DISMISS;
1028                 break;
1029         }
1030
1031         if ( !action.IsEmpty() )
1032         {
1033             consumer->PerformAction(action);
1034
1035             return true;
1036         }
1037     }
1038
1039     return wxStdInputHandler::HandleKey(consumer, event, pressed);
1040 }
1041
1042 #endif // wxUSE_COMBOBOX