1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/combobox.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Id:          $Id: combobox.cpp 54967 2008-08-04 15:33:13Z JS $
6 // Copyright:   (c) 1998 Robert Roebling
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 #if wxUSE_COMBOBOX
14 
15 #include "wx/combobox.h"
16 
17 #ifndef WX_PRECOMP
18     #include "wx/intl.h"
19     #include "wx/settings.h"
20     #include "wx/textctrl.h"    // for wxEVT_COMMAND_TEXT_UPDATED
21     #include "wx/arrstr.h"
22 #endif
23 
24 // We use GtkCombo which has been deprecated since GTK+ 2.3.0
25 // in favour of GtkComboBox for <GTK2.4 runtime
26 // We also use GtkList
27 #ifdef GTK_DISABLE_DEPRECATED
28 #undef GTK_DISABLE_DEPRECATED
29 #endif
30 #include "wx/gtk/private.h"
31 
32 //-----------------------------------------------------------------------------
33 // data
34 //-----------------------------------------------------------------------------
35 
36 extern bool   g_blockEventsOnDrag;
37 static int    g_SelectionBeforePopup = wxID_NONE; // this means the popup is hidden
38 
39 //-----------------------------------------------------------------------------
40 //  "changed" - typing and list item matches get changed, select-child
41 //              if it doesn't match an item then just get a single changed
42 //-----------------------------------------------------------------------------
43 
44 extern "C" {
45 static void
gtkcombo_text_changed_callback(GtkWidget * WXUNUSED (widget),wxComboBox * combo)46 gtkcombo_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
47 {
48     if (g_isIdle) wxapp_install_idle_handler();
49 
50     if (combo->m_ignoreNextUpdate)
51     {
52         combo->m_ignoreNextUpdate = false;
53         return;
54     }
55 
56     if (!combo->m_hasVMT) return;
57 
58     wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
59     event.SetString( combo->GetValue() );
60     event.SetEventObject( combo );
61     combo->GetEventHandler()->ProcessEvent( event );
62 }
63 }
64 
65 extern "C" {
66 static void
gtkcombo_dummy_callback(GtkEntry * WXUNUSED (entry),GtkCombo * WXUNUSED (combo))67 gtkcombo_dummy_callback(GtkEntry *WXUNUSED(entry), GtkCombo *WXUNUSED(combo))
68 {
69 }
70 }
71 
72 extern "C" {
73 static void
gtkcombo_popup_hide_callback(GtkCombo * WXUNUSED (gtk_combo),wxComboBox * combo)74 gtkcombo_popup_hide_callback(GtkCombo *WXUNUSED(gtk_combo), wxComboBox *combo)
75 {
76     // when the popup is hidden, throw a SELECTED event only if the combobox
77     // selection changed.
78     const int curSelection = combo->GetCurrentSelection();
79 
80     const bool hasChanged = curSelection != g_SelectionBeforePopup;
81 
82     // reset the selection flag to value meaning that it is hidden and do it
83     // now, before generating the events, so that GetSelection() returns the
84     // new value from the event handler
85     g_SelectionBeforePopup = wxID_NONE;
86 
87     if ( hasChanged )
88     {
89         wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
90         event.SetInt( curSelection );
91         event.SetString( combo->GetStringSelection() );
92         event.SetEventObject( combo );
93         combo->GetEventHandler()->ProcessEvent( event );
94 
95         // for consistency with the other ports, send TEXT event
96         wxCommandEvent event2( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
97         event2.SetString( combo->GetStringSelection() );
98         event2.SetEventObject( combo );
99         combo->GetEventHandler()->ProcessEvent( event2 );
100     }
101 }
102 }
103 
104 extern "C" {
105 static void
gtkcombo_popup_show_callback(GtkCombo * WXUNUSED (gtk_combo),wxComboBox * combo)106 gtkcombo_popup_show_callback(GtkCombo *WXUNUSED(gtk_combo), wxComboBox *combo)
107 {
108     // store the combobox selection value before the popup is shown
109     g_SelectionBeforePopup = combo->GetCurrentSelection();
110 }
111 }
112 
113 //-----------------------------------------------------------------------------
114 // "select-child" - click/cursor get select-child, changed, select-child
115 //-----------------------------------------------------------------------------
116 
117 extern "C" {
118 static void
gtkcombo_combo_select_child_callback(GtkList * WXUNUSED (list),GtkWidget * WXUNUSED (widget),wxComboBox * combo)119 gtkcombo_combo_select_child_callback( GtkList *WXUNUSED(list), GtkWidget *WXUNUSED(widget), wxComboBox *combo )
120 {
121     if (g_isIdle) wxapp_install_idle_handler();
122 
123     if (!combo->m_hasVMT) return;
124 
125     if (g_blockEventsOnDrag) return;
126 
127     int curSelection = combo->GetCurrentSelection();
128 
129     if (combo->m_prevSelection == curSelection) return;
130 
131     GtkWidget *list = GTK_COMBO(combo->m_widget)->list;
132     gtk_list_unselect_item( GTK_LIST(list), combo->m_prevSelection );
133 
134     combo->m_prevSelection = curSelection;
135 
136     // Quickly set the value of the combo box
137     // as GTK+ does that only AFTER the event
138     // is sent.
139     g_signal_handlers_disconnect_by_func (GTK_COMBO (combo->GetHandle())->entry,
140                                           (gpointer) gtkcombo_text_changed_callback,
141                                           combo);
142     combo->SetValue( combo->GetStringSelection() );
143     g_signal_connect_after (GTK_COMBO (combo->GetHandle())->entry, "changed",
144                             G_CALLBACK (gtkcombo_text_changed_callback), combo);
145 
146     // throw a SELECTED event only if the combobox popup is hidden (wxID_NONE)
147     // because when combobox popup is shown, gtkcombo_combo_select_child_callback is
148     // called each times the mouse is over an item with a pressed button so a lot
149     // of SELECTED event could be generated if the user keep the mouse button down
150     // and select other items ...
151     if (g_SelectionBeforePopup == wxID_NONE)
152     {
153         wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
154         event.SetInt( curSelection );
155         event.SetString( combo->GetStringSelection() );
156         event.SetEventObject( combo );
157         combo->GetEventHandler()->ProcessEvent( event );
158 
159         // for consistency with the other ports, don't generate text update
160         // events while the user is browsing the combobox neither
161         wxCommandEvent event2( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
162         event2.SetString( combo->GetValue() );
163         event2.SetEventObject( combo );
164         combo->GetEventHandler()->ProcessEvent( event2 );
165     }
166 }
167 }
168 
169 #ifdef __WXGTK24__
170 extern "C" {
171 static void
gtkcombobox_text_changed_callback(GtkWidget * WXUNUSED (widget),wxComboBox * combo)172 gtkcombobox_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
173 {
174     if (g_isIdle) wxapp_install_idle_handler();
175 
176     if (!combo->m_hasVMT) return;
177 
178     wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
179     event.SetString( combo->GetValue() );
180     event.SetEventObject( combo );
181     combo->GetEventHandler()->ProcessEvent( event );
182 }
183 }
184 
185 extern "C" {
186 static void
gtkcombobox_changed_callback(GtkWidget * WXUNUSED (widget),wxComboBox * combo)187 gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
188 {
189     if (g_isIdle) wxapp_install_idle_handler();
190 
191     if (!combo->m_hasVMT) return;
192 
193     if (combo->GetSelection() == -1)
194         return;
195 
196     wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
197     event.SetInt( combo->GetSelection() );
198     event.SetString( combo->GetStringSelection() );
199     event.SetEventObject( combo );
200     combo->GetEventHandler()->ProcessEvent( event );
201 }
202 }
203 
204 #endif
205 
206 //-----------------------------------------------------------------------------
207 // wxComboBox
208 //-----------------------------------------------------------------------------
209 
IMPLEMENT_DYNAMIC_CLASS(wxComboBox,wxControl)210 IMPLEMENT_DYNAMIC_CLASS(wxComboBox,wxControl)
211 
212 BEGIN_EVENT_TABLE(wxComboBox, wxControl)
213     EVT_SIZE(wxComboBox::OnSize)
214     EVT_CHAR(wxComboBox::OnChar)
215 
216     EVT_MENU(wxID_CUT, wxComboBox::OnCut)
217     EVT_MENU(wxID_COPY, wxComboBox::OnCopy)
218     EVT_MENU(wxID_PASTE, wxComboBox::OnPaste)
219     EVT_MENU(wxID_UNDO, wxComboBox::OnUndo)
220     EVT_MENU(wxID_REDO, wxComboBox::OnRedo)
221     EVT_MENU(wxID_CLEAR, wxComboBox::OnDelete)
222     EVT_MENU(wxID_SELECTALL, wxComboBox::OnSelectAll)
223 
224     EVT_UPDATE_UI(wxID_CUT, wxComboBox::OnUpdateCut)
225     EVT_UPDATE_UI(wxID_COPY, wxComboBox::OnUpdateCopy)
226     EVT_UPDATE_UI(wxID_PASTE, wxComboBox::OnUpdatePaste)
227     EVT_UPDATE_UI(wxID_UNDO, wxComboBox::OnUpdateUndo)
228     EVT_UPDATE_UI(wxID_REDO, wxComboBox::OnUpdateRedo)
229     EVT_UPDATE_UI(wxID_CLEAR, wxComboBox::OnUpdateDelete)
230     EVT_UPDATE_UI(wxID_SELECTALL, wxComboBox::OnUpdateSelectAll)
231 END_EVENT_TABLE()
232 
233 bool wxComboBox::Create( wxWindow *parent, wxWindowID id,
234                          const wxString& value,
235                          const wxPoint& pos, const wxSize& size,
236                          const wxArrayString& choices,
237                          long style, const wxValidator& validator,
238                          const wxString& name )
239 {
240     wxCArrayString chs(choices);
241 
242     return Create( parent, id, value, pos, size, chs.GetCount(),
243                    chs.GetStrings(), style, validator, name );
244 }
245 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],long style,const wxValidator & validator,const wxString & name)246 bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
247                          const wxPoint& pos, const wxSize& size,
248                          int n, const wxString choices[],
249                          long style, const wxValidator& validator,
250                          const wxString& name )
251 {
252     m_ignoreNextUpdate = false;
253     m_needParent = true;
254     m_acceptsFocus = true;
255     m_prevSelection = 0;
256 
257     if (!PreCreation( parent, pos, size ) ||
258         !CreateBase( parent, id, pos, size, style, validator, name ))
259     {
260         wxFAIL_MSG( wxT("wxComboBox creation failed") );
261         return false;
262     }
263 
264 #ifdef __WXGTK24__
265     if (!gtk_check_version(2,4,0))
266     {
267         m_widget = gtk_combo_box_entry_new_text();
268         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
269 
270         gtk_entry_set_editable( GTK_ENTRY( GTK_BIN(m_widget)->child ), TRUE );
271 
272         for (int i = 0; i < n; i++)
273         {
274             gtk_combo_box_append_text( combobox,  wxGTK_CONV( choices[i] ) );
275 
276             m_clientDataList.Append( (wxObject*)NULL );
277             m_clientObjectList.Append( (wxObject*)NULL );
278         }
279     }
280     else
281 #endif
282     {
283         m_widget = gtk_combo_new();
284         GtkCombo* combo = GTK_COMBO(m_widget);
285 
286         // Disable GTK's broken events ...
287         g_signal_handler_disconnect (combo->entry, combo->entry_change_id);
288         // ... and add surrogate handler.
289         combo->entry_change_id = g_signal_connect (combo->entry, "changed",
290                                                G_CALLBACK (gtkcombo_dummy_callback),
291                                                combo);
292 
293         // make it more useable
294         gtk_combo_set_use_arrows_always( GTK_COMBO(m_widget), TRUE );
295 
296         // and case-sensitive
297         gtk_combo_set_case_sensitive( GTK_COMBO(m_widget), TRUE );
298 
299         if (style & wxNO_BORDER)
300             g_object_set (combo->entry, "has-frame", FALSE, NULL );
301 
302         GtkWidget *list = combo->list;
303 
304         for (int i = 0; i < n; i++)
305         {
306             GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( choices[i] ) );
307 
308             m_clientDataList.Append( (wxObject*)NULL );
309             m_clientObjectList.Append( (wxObject*)NULL );
310 
311             gtk_container_add( GTK_CONTAINER(list), list_item );
312 
313             gtk_widget_show( list_item );
314         }
315     }
316 
317 
318     m_parent->DoAddChild( this );
319 
320     GtkEntry *entry = NULL;
321 #ifdef __WXGTK24__
322     if (!gtk_check_version(2,4,0))
323         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
324     else
325 #endif
326         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
327 
328     m_focusWidget = GTK_WIDGET( entry );
329 
330     PostCreation(size);
331 
332 #ifdef __WXGTK24__
333     if (!gtk_check_version(2,4,0))
334         ConnectWidget( m_widget );
335     else
336 #endif
337         ConnectWidget( GTK_COMBO(m_widget)->button );
338 
339 #ifdef __WXGTK24__
340     if (!gtk_check_version(2,4,0))
341     {
342         gtk_entry_set_text( entry, wxGTK_CONV(value) );
343 
344         if (style & wxCB_READONLY)
345             gtk_entry_set_editable( entry, FALSE );
346 
347         g_signal_connect_after (entry, "changed",
348                             G_CALLBACK (gtkcombobox_text_changed_callback), this);
349 
350         g_signal_connect_after (m_widget, "changed",
351                             G_CALLBACK (gtkcombobox_changed_callback), this);
352 
353     }
354     else
355 #endif
356     {
357         GtkCombo *combo = GTK_COMBO(m_widget);
358         // MSW's combo box shows the value and the selection is -1
359         gtk_entry_set_text( entry, wxGTK_CONV(value) );
360         gtk_list_unselect_all( GTK_LIST(combo->list) );
361 
362         if (style & wxCB_READONLY)
363             gtk_entry_set_editable( entry, FALSE );
364 
365         // "show" and "hide" events are generated when user click on the combobox button which popups a list
366         // this list is the "popwin" gtk widget
367         g_signal_connect (GTK_COMBO(combo)->popwin, "hide",
368                       G_CALLBACK (gtkcombo_popup_hide_callback), this);
369         g_signal_connect (GTK_COMBO(combo)->popwin, "show",
370                       G_CALLBACK (gtkcombo_popup_show_callback), this);
371         g_signal_connect_after (combo->list, "select-child",
372                             G_CALLBACK (gtkcombo_combo_select_child_callback),
373                             this);
374         g_signal_connect_after (entry, "changed",
375                             G_CALLBACK (gtkcombo_text_changed_callback), this);
376     }
377 
378     SetInitialSize(size); // need this too because this is a wxControlWithItems
379 
380     return true;
381 }
382 
~wxComboBox()383 wxComboBox::~wxComboBox()
384 {
385     wxList::compatibility_iterator node = m_clientObjectList.GetFirst();
386     while (node)
387     {
388         wxClientData *cd = (wxClientData*)node->GetData();
389         if (cd) delete cd;
390         node = node->GetNext();
391     }
392     m_clientObjectList.Clear();
393 
394     m_clientDataList.Clear();
395 }
396 
SetFocus()397 void wxComboBox::SetFocus()
398 {
399     if ( m_hasFocus )
400     {
401         // don't do anything if we already have focus
402         return;
403     }
404 
405     gtk_widget_grab_focus( m_focusWidget );
406 }
407 
DoAppend(const wxString & item)408 int wxComboBox::DoAppend( const wxString &item )
409 {
410     wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
411 
412 #ifdef __WXGTK24__
413     if (!gtk_check_version(2,4,0))
414     {
415         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
416         gtk_combo_box_append_text( combobox,  wxGTK_CONV( item ) );
417     }
418     else
419 #endif
420     {
421         DisableEvents();
422 
423         GtkWidget *list = GTK_COMBO(m_widget)->list;
424         GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( item ) );
425 
426         gtk_container_add( GTK_CONTAINER(list), list_item );
427 
428         if (GTK_WIDGET_REALIZED(m_widget))
429         {
430             gtk_widget_realize( list_item );
431             gtk_widget_realize( GTK_BIN(list_item)->child );
432         }
433 
434         // Apply current widget style to the new list_item
435         GtkRcStyle *style = CreateWidgetStyle();
436         if (style)
437         {
438             gtk_widget_modify_style( GTK_WIDGET( list_item ), style );
439             GtkBin *bin = GTK_BIN( list_item );
440             GtkWidget *label = GTK_WIDGET( bin->child );
441             gtk_widget_modify_style( label, style );
442             gtk_rc_style_unref( style );
443         }
444 
445         gtk_widget_show( list_item );
446 
447         EnableEvents();
448     }
449 
450     const unsigned int count = GetCount();
451 
452     if ( m_clientDataList.GetCount() < count )
453         m_clientDataList.Append( (wxObject*) NULL );
454     if ( m_clientObjectList.GetCount() < count )
455         m_clientObjectList.Append( (wxObject*) NULL );
456 
457     InvalidateBestSize();
458 
459     return count - 1;
460 }
461 
DoInsert(const wxString & item,unsigned int pos)462 int wxComboBox::DoInsert(const wxString &item, unsigned int pos)
463 {
464     wxCHECK_MSG( !(GetWindowStyle() & wxCB_SORT), -1,
465                     wxT("can't insert into sorted list"));
466 
467     wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
468     wxCHECK_MSG( IsValidInsert(pos), -1, wxT("invalid index") );
469 
470     unsigned int count = GetCount();
471 
472     if (pos == count)
473         return Append(item);
474 
475 #ifdef __WXGTK24__
476     if (!gtk_check_version(2,4,0))
477     {
478         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
479         gtk_combo_box_insert_text( combobox, pos, wxGTK_CONV( item ) );
480     }
481     else
482 #endif
483     {
484         DisableEvents();
485 
486         GtkWidget *list = GTK_COMBO(m_widget)->list;
487         GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( item ) );
488 
489         GList *gitem_list = g_list_alloc ();
490         gitem_list->data = list_item;
491         gtk_list_insert_items( GTK_LIST (list), gitem_list, pos );
492 
493         if (GTK_WIDGET_REALIZED(m_widget))
494         {
495             gtk_widget_realize( list_item );
496             gtk_widget_realize( GTK_BIN(list_item)->child );
497 
498             ApplyWidgetStyle();
499         }
500 
501         gtk_widget_show( list_item );
502 
503         EnableEvents();
504     }
505 
506     count = GetCount();
507 
508     if ( m_clientDataList.GetCount() < count )
509         m_clientDataList.Insert( pos, (wxObject*) NULL );
510     if ( m_clientObjectList.GetCount() < count )
511         m_clientObjectList.Insert( pos, (wxObject*) NULL );
512 
513     InvalidateBestSize();
514 
515     return pos;
516 }
517 
DoSetItemClientData(unsigned int n,void * clientData)518 void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
519 {
520     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
521 
522     wxList::compatibility_iterator node = m_clientDataList.Item( n );
523     if (!node) return;
524 
525     node->SetData( (wxObject*) clientData );
526 }
527 
DoGetItemClientData(unsigned int n) const528 void* wxComboBox::DoGetItemClientData(unsigned int n) const
529 {
530     wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid combobox") );
531 
532     wxList::compatibility_iterator node = m_clientDataList.Item( n );
533 
534     return node ? node->GetData() : NULL;
535 }
536 
DoSetItemClientObject(unsigned int n,wxClientData * clientData)537 void wxComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
538 {
539     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
540 
541     wxList::compatibility_iterator node = m_clientObjectList.Item( n );
542     if (!node) return;
543 
544     // wxItemContainer already deletes data for us
545 
546     node->SetData( (wxObject*) clientData );
547 }
548 
DoGetItemClientObject(unsigned int n) const549 wxClientData* wxComboBox::DoGetItemClientObject(unsigned int n) const
550 {
551     wxCHECK_MSG( m_widget != NULL, (wxClientData*)NULL, wxT("invalid combobox") );
552 
553     wxList::compatibility_iterator node = m_clientObjectList.Item( n );
554 
555     return node ? (wxClientData*) node->GetData() : NULL;
556 }
557 
Clear()558 void wxComboBox::Clear()
559 {
560     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
561 
562     DisableEvents();
563 
564 #ifdef __WXGTK24__
565     if (!gtk_check_version(2,4,0))
566     {
567         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
568         const unsigned int count = GetCount();
569         for (unsigned int i = 0; i < count; i++)
570             gtk_combo_box_remove_text( combobox, 0 );
571     }
572     else // GTK+ < 2.4.0
573 #endif // __WXGTK24__
574     {
575         GtkWidget *list = GTK_COMBO(m_widget)->list;
576         gtk_list_clear_items( GTK_LIST(list), 0, GetCount() );
577     }
578 
579     wxList::compatibility_iterator node = m_clientObjectList.GetFirst();
580     while (node)
581     {
582         wxClientData *cd = (wxClientData*)node->GetData();
583         delete cd;
584         node = node->GetNext();
585     }
586     m_clientObjectList.Clear();
587 
588     m_clientDataList.Clear();
589 
590     EnableEvents();
591 
592     InvalidateBestSize();
593 }
594 
Delete(unsigned int n)595 void wxComboBox::Delete(unsigned int n)
596 {
597     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
598 
599 #ifdef __WXGTK24__
600     if (!gtk_check_version(2,4,0))
601     {
602         wxCHECK_RET( IsValid(n), wxT("invalid index") );
603 
604         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
605         gtk_combo_box_remove_text( combobox, n );
606     }
607     else
608 #endif
609     {
610         GtkList *listbox = GTK_LIST( GTK_COMBO(m_widget)->list );
611 
612         GList *child = g_list_nth( listbox->children, n );
613 
614         if (!child)
615         {
616             wxFAIL_MSG(wxT("wrong index"));
617             return;
618         }
619 
620         DisableEvents();
621 
622         GList *list = g_list_append( (GList*) NULL, child->data );
623         gtk_list_remove_items( listbox, list );
624         g_list_free( list );
625 
626         EnableEvents();
627     }
628 
629     wxList::compatibility_iterator node = m_clientObjectList.Item( n );
630     if (node)
631     {
632         wxClientData *cd = (wxClientData*)node->GetData();
633         if (cd) delete cd;
634         m_clientObjectList.Erase( node );
635     }
636 
637     node = m_clientDataList.Item( n );
638     if (node)
639         m_clientDataList.Erase( node );
640 
641     InvalidateBestSize();
642 }
643 
SetString(unsigned int n,const wxString & text)644 void wxComboBox::SetString(unsigned int n, const wxString &text)
645 {
646     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
647 
648 #ifdef __WXGTK24__
649     if (!gtk_check_version(2,4,0))
650     {
651         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
652         wxCHECK_RET( IsValid(n), wxT("invalid index") );
653 
654         GtkTreeModel *model = gtk_combo_box_get_model( combobox );
655         GtkTreeIter iter;
656         if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
657         {
658             GValue value = { 0, };
659             g_value_init( &value, G_TYPE_STRING );
660             g_value_set_string( &value, wxGTK_CONV( text ) );
661             gtk_list_store_set_value( GTK_LIST_STORE(model), &iter, 0, &value );
662             g_value_unset( &value );
663         }
664     }
665     else
666 #endif
667     {
668         GtkWidget *list = GTK_COMBO(m_widget)->list;
669 
670         GList *child = g_list_nth( GTK_LIST(list)->children, n );
671         if (child)
672         {
673             GtkBin *bin = GTK_BIN( child->data );
674             GtkLabel *label = GTK_LABEL( bin->child );
675             gtk_label_set_text(label, wxGTK_CONV(text));
676         }
677         else
678         {
679             wxFAIL_MSG( wxT("wxComboBox: wrong index") );
680         }
681     }
682 
683     InvalidateBestSize();
684 }
685 
FindString(const wxString & item,bool bCase) const686 int wxComboBox::FindString( const wxString &item, bool bCase ) const
687 {
688     wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid combobox") );
689 
690 #ifdef __WXGTK24__
691     if (!gtk_check_version(2,4,0))
692     {
693         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
694         GtkTreeModel* model = gtk_combo_box_get_model( combobox );
695         GtkTreeIter iter;
696         gtk_tree_model_get_iter_first( model, &iter );
697         if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
698             return -1;
699         int count = 0;
700         do
701         {
702             GValue value = { 0, };
703             gtk_tree_model_get_value( model, &iter, 0, &value );
704             wxString str = wxGTK_CONV_BACK( g_value_get_string( &value ) );
705             g_value_unset( &value );
706 
707             if (item.IsSameAs( str, bCase ) )
708                 return count;
709 
710             count++;
711 
712         } while (gtk_tree_model_iter_next( model, &iter ));
713     }
714     else
715 #endif
716     {
717         GtkWidget *list = GTK_COMBO(m_widget)->list;
718 
719         GList *child = GTK_LIST(list)->children;
720         int count = 0;
721         while (child)
722         {
723             GtkBin *bin = GTK_BIN( child->data );
724             GtkLabel *label = GTK_LABEL( bin->child );
725             wxString str( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
726 
727             if (item.IsSameAs( str , bCase ) )
728                 return count;
729 
730             count++;
731             child = child->next;
732         }
733     }
734 
735     return wxNOT_FOUND;
736 }
737 
GetSelection() const738 int wxComboBox::GetSelection() const
739 {
740 #ifdef __WXGTK24__
741     if (!gtk_check_version(2,4,0))
742     {
743         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
744         return gtk_combo_box_get_active( combobox );
745     }
746     else
747 #endif
748         // if the popup is currently opened, use the selection as it had been
749         // before it dropped down
750         return g_SelectionBeforePopup == wxID_NONE ? GetCurrentSelection()
751                                                : g_SelectionBeforePopup;
752 }
753 
GetCurrentSelection() const754 int wxComboBox::GetCurrentSelection() const
755 {
756     wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
757 
758 #ifdef __WXGTK24__
759     if (!gtk_check_version(2,4,0))
760     {
761         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
762         return gtk_combo_box_get_active( combobox );
763     }
764     else
765 #endif
766     {
767         GtkWidget *list = GTK_COMBO(m_widget)->list;
768 
769         GList *selection = GTK_LIST(list)->selection;
770         if (selection)
771         {
772             GList *child = GTK_LIST(list)->children;
773             int count = 0;
774             while (child)
775             {
776                 if (child->data == selection->data) return count;
777                 count++;
778                 child = child->next;
779             }
780         }
781     }
782 
783     return -1;
784 }
785 
GetString(unsigned int n) const786 wxString wxComboBox::GetString(unsigned int n) const
787 {
788     wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
789 
790     wxString str;
791 
792 #ifdef __WXGTK24__
793     if (!gtk_check_version(2,4,0))
794     {
795         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
796         GtkTreeModel *model = gtk_combo_box_get_model( combobox );
797         GtkTreeIter iter;
798         if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
799         {
800             GValue value = { 0, };
801             gtk_tree_model_get_value( model, &iter, 0, &value );
802             wxString tmp = wxGTK_CONV_BACK( g_value_get_string( &value ) );
803             g_value_unset( &value );
804             return tmp;
805         }
806     }
807     else
808 #endif
809     {
810         GtkWidget *list = GTK_COMBO(m_widget)->list;
811 
812         GList *child = g_list_nth( GTK_LIST(list)->children, n );
813         if (child)
814         {
815             GtkBin *bin = GTK_BIN( child->data );
816             GtkLabel *label = GTK_LABEL( bin->child );
817             str = wxGTK_CONV_BACK( gtk_label_get_text(label) );
818         }
819         else
820         {
821             wxFAIL_MSG( wxT("wxComboBox: wrong index") );
822         }
823     }
824 
825     return str;
826 }
827 
GetStringSelection() const828 wxString wxComboBox::GetStringSelection() const
829 {
830     wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
831 
832 #ifdef __WXGTK24__
833     if (!gtk_check_version(2,4,0))
834     {
835         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
836         int sel = gtk_combo_box_get_active( combobox );
837         if (sel == -1)
838             return wxEmptyString;
839         return GetString(sel);
840     }
841     else
842 #endif
843     {
844         GtkWidget *list = GTK_COMBO(m_widget)->list;
845 
846         GList *selection = GTK_LIST(list)->selection;
847         if (selection)
848         {
849             GtkBin *bin = GTK_BIN( selection->data );
850             GtkLabel *label = GTK_LABEL( bin->child );
851             wxString tmp( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
852             return tmp;
853         }
854 
855         wxFAIL_MSG( wxT("wxComboBox: no selection") );
856     }
857 
858     return wxEmptyString;
859 }
860 
GetCount() const861 unsigned int wxComboBox::GetCount() const
862 {
863     wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid combobox") );
864 
865 #ifdef __WXGTK24__
866     if (!gtk_check_version(2,4,0))
867     {
868         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
869         GtkTreeModel* model = gtk_combo_box_get_model( combobox );
870         GtkTreeIter iter;
871         gtk_tree_model_get_iter_first( model, &iter );
872         if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
873             return 0;
874         unsigned int ret = 1;
875         while (gtk_tree_model_iter_next( model, &iter ))
876             ret++;
877         return ret;
878     }
879     else
880 #endif
881     {
882         GtkWidget *list = GTK_COMBO(m_widget)->list;
883 
884         GList *child = GTK_LIST(list)->children;
885         unsigned int count = 0;
886         while (child)
887         {
888             count++;
889             child = child->next;
890         }
891         return count;
892     }
893 
894     return 0;
895 }
896 
SetSelection(int n)897 void wxComboBox::SetSelection( int n )
898 {
899     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
900 
901     DisableEvents();
902 
903 #ifdef __WXGTK24__
904     if (!gtk_check_version(2,4,0))
905     {
906         GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
907         gtk_combo_box_set_active( combobox, n );
908     }
909     else
910 #endif
911     {
912         GtkWidget *list = GTK_COMBO(m_widget)->list;
913         gtk_list_unselect_item( GTK_LIST(list), m_prevSelection );
914         gtk_list_select_item( GTK_LIST(list), n );
915         m_prevSelection = n;
916     }
917 
918     EnableEvents();
919 }
920 
GetValue() const921 wxString wxComboBox::GetValue() const
922 {
923     GtkEntry *entry = NULL;
924 #ifdef __WXGTK24__
925     if (!gtk_check_version(2,4,0))
926         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
927     else
928 #endif
929         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
930 
931     wxString tmp( wxGTK_CONV_BACK( gtk_entry_get_text( entry ) ) );
932 
933 #if 0
934     for (int i = 0; i < wxStrlen(tmp.c_str()) +1; i++)
935     {
936         wxChar c = tmp[i];
937         printf( "%d ", (int) (c) );
938     }
939     printf( "\n" );
940 #endif
941 
942     return tmp;
943 }
944 
SetValue(const wxString & value)945 void wxComboBox::SetValue( const wxString& value )
946 {
947     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
948 
949     GtkEntry *entry = NULL;
950 #ifdef __WXGTK24__
951     if (!gtk_check_version(2,4,0))
952         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
953     else
954 #endif
955         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
956 
957     DisableEvents();
958     wxString tmp;
959     if (!value.IsNull()) tmp = value;
960     gtk_entry_set_text( entry, wxGTK_CONV( tmp ) );
961     EnableEvents();
962 
963     InvalidateBestSize();
964 }
965 
Copy()966 void wxComboBox::Copy()
967 {
968     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
969 
970     GtkEntry *entry = NULL;
971 #ifdef __WXGTK24__
972     if (!gtk_check_version(2,4,0))
973         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
974     else
975 #endif
976         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
977 
978     gtk_editable_copy_clipboard(GTK_EDITABLE(entry));
979 }
980 
Cut()981 void wxComboBox::Cut()
982 {
983     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
984 
985     GtkEntry *entry = NULL;
986 #ifdef __WXGTK24__
987     if (!gtk_check_version(2,4,0))
988         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
989     else
990 #endif
991         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
992 
993     gtk_editable_cut_clipboard(GTK_EDITABLE(entry));
994 }
995 
Paste()996 void wxComboBox::Paste()
997 {
998     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
999 
1000     GtkEntry *entry = NULL;
1001 #ifdef __WXGTK24__
1002     if (!gtk_check_version(2,4,0))
1003         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1004     else
1005 #endif
1006         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1007 
1008     gtk_editable_paste_clipboard(GTK_EDITABLE(entry));
1009 }
1010 
Undo()1011 void wxComboBox::Undo()
1012 {
1013     // TODO
1014 }
1015 
Redo()1016 void wxComboBox::Redo()
1017 {
1018     // TODO
1019 }
1020 
SelectAll()1021 void wxComboBox::SelectAll()
1022 {
1023     SetSelection(0, GetLastPosition());
1024 }
1025 
CanUndo() const1026 bool wxComboBox::CanUndo() const
1027 {
1028     // TODO
1029     return false;
1030 }
1031 
CanRedo() const1032 bool wxComboBox::CanRedo() const
1033 {
1034     // TODO
1035     return false;
1036 }
1037 
HasSelection() const1038 bool wxComboBox::HasSelection() const
1039 {
1040     long from, to;
1041     GetSelection(&from, &to);
1042     return from != to;
1043 }
1044 
CanCopy() const1045 bool wxComboBox::CanCopy() const
1046 {
1047     // Can copy if there's a selection
1048     return HasSelection();
1049 }
1050 
CanCut() const1051 bool wxComboBox::CanCut() const
1052 {
1053     return CanCopy() && IsEditable();
1054 }
1055 
CanPaste() const1056 bool wxComboBox::CanPaste() const
1057 {
1058     // TODO: check for text on the clipboard
1059     return IsEditable() ;
1060 }
1061 
IsEditable() const1062 bool wxComboBox::IsEditable() const
1063 {
1064     return !HasFlag(wxCB_READONLY);
1065 }
1066 
1067 
SetInsertionPoint(long pos)1068 void wxComboBox::SetInsertionPoint( long pos )
1069 {
1070     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
1071 
1072     if ( pos == GetLastPosition() )
1073         pos = -1;
1074 
1075     GtkEntry *entry = NULL;
1076 #ifdef __WXGTK24__
1077     if (!gtk_check_version(2,4,0))
1078         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1079     else
1080 #endif
1081         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1082 
1083     gtk_entry_set_position( entry, (int)pos );
1084 }
1085 
GetInsertionPoint() const1086 long wxComboBox::GetInsertionPoint() const
1087 {
1088     GtkEntry *entry = NULL;
1089 #ifdef __WXGTK24__
1090     if (!gtk_check_version(2,4,0))
1091         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1092     else
1093 #endif
1094         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1095 
1096     return (long) gtk_editable_get_position(GTK_EDITABLE(entry));
1097 }
1098 
GetLastPosition() const1099 wxTextPos wxComboBox::GetLastPosition() const
1100 {
1101     GtkEntry *entry = NULL;
1102 #ifdef __WXGTK24__
1103     if (!gtk_check_version(2,4,0))
1104         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1105     else
1106 #endif
1107         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1108 
1109     int pos = entry->text_length;
1110     return (long) pos-1;
1111 }
1112 
Replace(long from,long to,const wxString & value)1113 void wxComboBox::Replace( long from, long to, const wxString& value )
1114 {
1115     wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
1116 
1117     GtkEntry *entry = NULL;
1118 #ifdef __WXGTK24__
1119     if (!gtk_check_version(2,4,0))
1120         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1121     else
1122 #endif
1123         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1124 
1125     gtk_editable_delete_text( GTK_EDITABLE(entry), (gint)from, (gint)to );
1126     if (value.IsNull()) return;
1127     gint pos = (gint)to;
1128 
1129 #if wxUSE_UNICODE
1130     wxCharBuffer buffer = wxConvUTF8.cWX2MB( value );
1131     gtk_editable_insert_text( GTK_EDITABLE(entry), (const char*) buffer, strlen( (const char*) buffer ), &pos );
1132 #else
1133     gtk_editable_insert_text( GTK_EDITABLE(entry), value.c_str(), value.length(), &pos );
1134 #endif
1135 }
1136 
SetSelection(long from,long to)1137 void wxComboBox::SetSelection( long from, long to )
1138 {
1139     GtkEntry *entry = NULL;
1140 #ifdef __WXGTK24__
1141     if (!gtk_check_version(2,4,0))
1142         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1143     else
1144 #endif
1145         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1146 
1147     gtk_editable_select_region( GTK_EDITABLE(entry), (gint)from, (gint)to );
1148 }
1149 
GetSelection(long * from,long * to) const1150 void wxComboBox::GetSelection( long* from, long* to ) const
1151 {
1152     GtkEntry *entry = NULL;
1153 #ifdef __WXGTK24__
1154     if (!gtk_check_version(2,4,0))
1155         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1156     else
1157 #endif
1158         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1159 
1160     if (IsEditable())
1161     {
1162         GtkEditable *editable = GTK_EDITABLE(entry);
1163         gint start, end;
1164         gtk_editable_get_selection_bounds(editable, & start, & end);
1165         *from = start;
1166         *to = end;
1167     }
1168 }
1169 
SetEditable(bool editable)1170 void wxComboBox::SetEditable( bool editable )
1171 {
1172     GtkEntry *entry = NULL;
1173 #ifdef __WXGTK24__
1174     if (!gtk_check_version(2,4,0))
1175         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1176     else
1177 #endif
1178         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1179 
1180     gtk_entry_set_editable( GTK_ENTRY(entry), editable );
1181 }
1182 
OnChar(wxKeyEvent & event)1183 void wxComboBox::OnChar( wxKeyEvent &event )
1184 {
1185     if ( event.GetKeyCode() == WXK_RETURN )
1186     {
1187         // GTK automatically selects an item if its in the list
1188         wxCommandEvent eventEnter(wxEVT_COMMAND_TEXT_ENTER, GetId());
1189         eventEnter.SetString( GetValue() );
1190         eventEnter.SetInt( GetSelection() );
1191         eventEnter.SetEventObject( this );
1192 
1193         if (!GetEventHandler()->ProcessEvent( eventEnter ))
1194         {
1195             // This will invoke the dialog default action, such
1196             // as the clicking the default button.
1197 
1198             wxWindow *top_frame = m_parent;
1199             while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
1200                 top_frame = top_frame->GetParent();
1201 
1202             if (top_frame && GTK_IS_WINDOW(top_frame->m_widget))
1203             {
1204                 GtkWindow *window = GTK_WINDOW(top_frame->m_widget);
1205 
1206                 if (window->default_widget)
1207                         gtk_widget_activate (window->default_widget);
1208             }
1209         }
1210 
1211         // Catch GTK event so that GTK doesn't open the drop
1212         // down list upon RETURN.
1213         return;
1214     }
1215 
1216     event.Skip();
1217 }
1218 
DisableEvents()1219 void wxComboBox::DisableEvents()
1220 {
1221 #ifdef __WXGTK24__
1222     if (!gtk_check_version(2,4,0))
1223     {
1224         g_signal_handlers_disconnect_by_func (GTK_BIN(m_widget)->child,
1225                 (gpointer)gtkcombobox_text_changed_callback, this);
1226 
1227         g_signal_handlers_disconnect_by_func (m_widget,
1228                 (gpointer)gtkcombobox_changed_callback, this);
1229     }
1230     else
1231 #endif
1232     {
1233         g_signal_handlers_disconnect_by_func (GTK_COMBO(m_widget)->list,
1234                 (gpointer) gtkcombo_combo_select_child_callback, this);
1235 
1236         g_signal_handlers_disconnect_by_func (GTK_COMBO(m_widget)->entry,
1237                 (gpointer) gtkcombo_text_changed_callback, this);
1238     }
1239 }
1240 
EnableEvents()1241 void wxComboBox::EnableEvents()
1242 {
1243 #ifdef __WXGTK24__
1244     if (!gtk_check_version(2,4,0))
1245     {
1246         g_signal_connect_after (GTK_BIN(m_widget)->child, "changed",
1247                             G_CALLBACK (gtkcombobox_text_changed_callback), this);
1248 
1249         g_signal_connect_after (m_widget, "changed",
1250                             G_CALLBACK (gtkcombobox_changed_callback), this);
1251     }
1252     else
1253 #endif
1254     {
1255         g_signal_connect_after (GTK_COMBO(m_widget)->list, "select-child",
1256                             G_CALLBACK (gtkcombo_combo_select_child_callback),
1257                             this);
1258         g_signal_connect_after (GTK_COMBO(m_widget)->entry, "changed",
1259                             G_CALLBACK (gtkcombo_text_changed_callback),
1260                             this );
1261     }
1262 }
1263 
OnSize(wxSizeEvent & event)1264 void wxComboBox::OnSize( wxSizeEvent &event )
1265 {
1266 #ifdef __WXGTK24__
1267     if (!gtk_check_version(2,4,0))
1268     {
1269         // Do nothing
1270     }
1271     else
1272 #endif
1273     {
1274         // NB: In some situations (e.g. on non-first page of a wizard, if the
1275         //     size used is default size), GtkCombo widget is resized correctly,
1276         //     but it's look is not updated, it's rendered as if it was much wider.
1277         //     No other widgets are affected, so it looks like a bug in GTK+.
1278         //     Manually requesting resize calculation (as gtk_pizza_set_size does)
1279         //     fixes it.
1280         if (GTK_WIDGET_VISIBLE(m_widget))
1281             gtk_widget_queue_resize(m_widget);
1282     }
1283 
1284     event.Skip();
1285 }
1286 
DoApplyWidgetStyle(GtkRcStyle * style)1287 void wxComboBox::DoApplyWidgetStyle(GtkRcStyle *style)
1288 {
1289 #ifdef __WXGTK24__
1290     if (!gtk_check_version(2,4,0))
1291     {
1292         // Do nothing
1293     }
1294     else
1295 #endif
1296     {
1297 //    gtk_widget_modify_style( GTK_COMBO(m_widget)->button, syle );
1298 
1299         gtk_widget_modify_style( GTK_COMBO(m_widget)->entry, style );
1300         gtk_widget_modify_style( GTK_COMBO(m_widget)->list, style );
1301 
1302         GtkList *list = GTK_LIST( GTK_COMBO(m_widget)->list );
1303         GList *child = list->children;
1304         while (child)
1305         {
1306             gtk_widget_modify_style( GTK_WIDGET(child->data), style );
1307 
1308             GtkBin *bin = GTK_BIN(child->data);
1309             gtk_widget_modify_style( bin->child, style );
1310 
1311             child = child->next;
1312         }
1313     }
1314 }
1315 
GetConnectWidget()1316 GtkWidget* wxComboBox::GetConnectWidget()
1317 {
1318     GtkEntry *entry = NULL;
1319 #ifdef __WXGTK24__
1320     if (!gtk_check_version(2,4,0))
1321         entry = GTK_ENTRY( GTK_BIN(m_widget)->child );
1322     else
1323 #endif
1324         entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
1325 
1326     return GTK_WIDGET( entry );
1327 }
1328 
GTKGetWindow(wxArrayGdkWindows & windows) const1329 GdkWindow *wxComboBox::GTKGetWindow(wxArrayGdkWindows& windows) const
1330 {
1331 #ifdef __WXGTK24__
1332     if (!gtk_check_version(2,4,0))
1333     {
1334         wxUnusedVar(windows);
1335 
1336         return GTK_ENTRY(GTK_BIN(m_widget)->child)->text_area;
1337     }
1338     else
1339 #endif // GTK+ 2.4
1340     {
1341         windows.push_back(GTK_ENTRY(GTK_COMBO(m_widget)->entry)->text_area);
1342         windows.push_back(GTK_COMBO(m_widget)->button->window);
1343 
1344         // indicate that we return multiple windows in the windows array
1345         return NULL;
1346     }
1347 }
1348 
DoGetBestSize() const1349 wxSize wxComboBox::DoGetBestSize() const
1350 {
1351     wxSize ret( wxControl::DoGetBestSize() );
1352 
1353     // we know better our horizontal extent: it depends on the longest string
1354     // in the combobox
1355     if ( m_widget )
1356     {
1357         int width;
1358         unsigned int count = GetCount();
1359         for ( unsigned int n = 0; n < count; n++ )
1360         {
1361             GetTextExtent(GetString(n), &width, NULL, NULL, NULL );
1362             if ( width > ret.x )
1363                 ret.x = width;
1364         }
1365     }
1366 
1367     // empty combobox should have some reasonable default size too
1368     if ( GetCount() == 0 && ret.x < 100 )
1369         ret.x = 100;
1370 
1371     CacheBestSize(ret);
1372     return ret;
1373 }
1374 
1375 // static
1376 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))1377 wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
1378 {
1379 #ifdef __WXGTK24__
1380     if (!gtk_check_version(2,4,0))
1381         return GetDefaultAttributesFromGTKWidget(gtk_combo_box_entry_new, true);
1382     else
1383 #endif
1384         return GetDefaultAttributesFromGTKWidget(gtk_combo_new, true);
1385 }
1386 
1387 // ----------------------------------------------------------------------------
1388 // standard event handling
1389 // ----------------------------------------------------------------------------
1390 
OnCut(wxCommandEvent & WXUNUSED (event))1391 void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
1392 {
1393     Cut();
1394 }
1395 
OnCopy(wxCommandEvent & WXUNUSED (event))1396 void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
1397 {
1398     Copy();
1399 }
1400 
OnPaste(wxCommandEvent & WXUNUSED (event))1401 void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
1402 {
1403     Paste();
1404 }
1405 
OnUndo(wxCommandEvent & WXUNUSED (event))1406 void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
1407 {
1408     Undo();
1409 }
1410 
OnRedo(wxCommandEvent & WXUNUSED (event))1411 void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
1412 {
1413     Redo();
1414 }
1415 
OnDelete(wxCommandEvent & WXUNUSED (event))1416 void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
1417 {
1418     long from, to;
1419     GetSelection(& from, & to);
1420     if (from != -1 && to != -1)
1421         Remove(from, to);
1422 }
1423 
OnSelectAll(wxCommandEvent & WXUNUSED (event))1424 void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
1425 {
1426     SetSelection(-1, -1);
1427 }
1428 
OnUpdateCut(wxUpdateUIEvent & event)1429 void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
1430 {
1431     event.Enable( CanCut() );
1432 }
1433 
OnUpdateCopy(wxUpdateUIEvent & event)1434 void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
1435 {
1436     event.Enable( CanCopy() );
1437 }
1438 
OnUpdatePaste(wxUpdateUIEvent & event)1439 void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
1440 {
1441     event.Enable( CanPaste() );
1442 }
1443 
OnUpdateUndo(wxUpdateUIEvent & event)1444 void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
1445 {
1446     event.Enable( CanUndo() );
1447 }
1448 
OnUpdateRedo(wxUpdateUIEvent & event)1449 void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
1450 {
1451     event.Enable( CanRedo() );
1452 }
1453 
OnUpdateDelete(wxUpdateUIEvent & event)1454 void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
1455 {
1456     event.Enable(HasSelection() && IsEditable()) ;
1457 }
1458 
OnUpdateSelectAll(wxUpdateUIEvent & event)1459 void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
1460 {
1461     event.Enable(GetLastPosition() > 0);
1462 }
1463 
1464 #endif
1465