1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk1/combobox.cpp
3// Purpose:
4// Author:      Robert Roebling
5// Id:          $Id: combobox.cpp 42816 2006-10-31 08:50:17Z RD $
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#include "wx/gtk1/private.h"
25
26//-----------------------------------------------------------------------------
27// idle system
28//-----------------------------------------------------------------------------
29
30extern void wxapp_install_idle_handler();
31extern bool g_isIdle;
32
33//-----------------------------------------------------------------------------
34// data
35//-----------------------------------------------------------------------------
36
37extern bool   g_blockEventsOnDrag;
38static int    g_SelectionBeforePopup = wxID_NONE; // this means the popup is hidden
39
40//-----------------------------------------------------------------------------
41//  "changed" - typing and list item matches get changed, select-child
42//              if it doesn't match an item then just get a single changed
43//-----------------------------------------------------------------------------
44
45extern "C" {
46static void
47gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
48{
49    if (g_isIdle) wxapp_install_idle_handler();
50
51    if (combo->m_ignoreNextUpdate)
52    {
53        combo->m_ignoreNextUpdate = false;
54        return;
55    }
56
57    if (!combo->m_hasVMT) return;
58
59    wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
60    event.SetString( combo->GetValue() );
61    event.SetEventObject( combo );
62    combo->GetEventHandler()->ProcessEvent( event );
63}
64}
65
66extern "C" {
67static void
68gtk_dummy_callback(GtkEntry *WXUNUSED(entry), GtkCombo *WXUNUSED(combo))
69{
70}
71}
72
73extern "C" {
74static void
75gtk_popup_hide_callback(GtkCombo *WXUNUSED(gtk_combo), wxComboBox *combo)
76{
77    // when the popup is hidden, throw a SELECTED event only if the combobox
78    // selection changed.
79    const int curSelection = combo->GetCurrentSelection();
80
81    const bool hasChanged = curSelection != g_SelectionBeforePopup;
82
83    // reset the selection flag to value meaning that it is hidden and do it
84    // now, before generating the events, so that GetSelection() returns the
85    // new value from the event handler
86    g_SelectionBeforePopup = wxID_NONE;
87
88    if ( hasChanged )
89    {
90        wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
91        event.SetInt( curSelection );
92        event.SetString( combo->GetStringSelection() );
93        event.SetEventObject( combo );
94        combo->GetEventHandler()->ProcessEvent( event );
95
96        // for consistency with the other ports, send TEXT event
97        wxCommandEvent event2( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
98        event2.SetString( combo->GetStringSelection() );
99        event2.SetEventObject( combo );
100        combo->GetEventHandler()->ProcessEvent( event2 );
101    }
102}
103}
104
105extern "C" {
106static void
107gtk_popup_show_callback(GtkCombo *WXUNUSED(gtk_combo), wxComboBox *combo)
108{
109    // store the combobox selection value before the popup is shown
110    g_SelectionBeforePopup = combo->GetCurrentSelection();
111}
112}
113
114//-----------------------------------------------------------------------------
115// "select-child" - click/cursor get select-child, changed, select-child
116//-----------------------------------------------------------------------------
117
118extern "C" {
119static void
120gtk_combo_select_child_callback( GtkList *WXUNUSED(list), GtkWidget *WXUNUSED(widget), wxComboBox *combo )
121{
122    if (g_isIdle) wxapp_install_idle_handler();
123
124    if (!combo->m_hasVMT) return;
125
126    if (g_blockEventsOnDrag) return;
127
128    int curSelection = combo->GetCurrentSelection();
129
130    if (combo->m_prevSelection == curSelection) return;
131
132    GtkWidget *list = GTK_COMBO(combo->m_widget)->list;
133    gtk_list_unselect_item( GTK_LIST(list), combo->m_prevSelection );
134
135    combo->m_prevSelection = curSelection;
136
137    // Quickly set the value of the combo box
138    // as GTK+ does that only AFTER the event
139    // is sent.
140    gtk_signal_disconnect_by_func( GTK_OBJECT(GTK_COMBO(combo->GetHandle())->entry),
141      GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)combo );
142    combo->SetValue( combo->GetStringSelection() );
143    gtk_signal_connect_after( GTK_OBJECT(GTK_COMBO(combo->GetHandle())->entry), "changed",
144      GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)combo );
145
146    // throw a SELECTED event only if the combobox popup is hidden (wxID_NONE)
147    // because when combobox popup is shown, gtk_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//-----------------------------------------------------------------------------
170// wxComboBox
171//-----------------------------------------------------------------------------
172
173IMPLEMENT_DYNAMIC_CLASS(wxComboBox,wxControl)
174
175BEGIN_EVENT_TABLE(wxComboBox, wxControl)
176    EVT_SIZE(wxComboBox::OnSize)
177    EVT_CHAR(wxComboBox::OnChar)
178
179    EVT_MENU(wxID_CUT, wxComboBox::OnCut)
180    EVT_MENU(wxID_COPY, wxComboBox::OnCopy)
181    EVT_MENU(wxID_PASTE, wxComboBox::OnPaste)
182    EVT_MENU(wxID_UNDO, wxComboBox::OnUndo)
183    EVT_MENU(wxID_REDO, wxComboBox::OnRedo)
184    EVT_MENU(wxID_CLEAR, wxComboBox::OnDelete)
185    EVT_MENU(wxID_SELECTALL, wxComboBox::OnSelectAll)
186
187    EVT_UPDATE_UI(wxID_CUT, wxComboBox::OnUpdateCut)
188    EVT_UPDATE_UI(wxID_COPY, wxComboBox::OnUpdateCopy)
189    EVT_UPDATE_UI(wxID_PASTE, wxComboBox::OnUpdatePaste)
190    EVT_UPDATE_UI(wxID_UNDO, wxComboBox::OnUpdateUndo)
191    EVT_UPDATE_UI(wxID_REDO, wxComboBox::OnUpdateRedo)
192    EVT_UPDATE_UI(wxID_CLEAR, wxComboBox::OnUpdateDelete)
193    EVT_UPDATE_UI(wxID_SELECTALL, wxComboBox::OnUpdateSelectAll)
194END_EVENT_TABLE()
195
196bool wxComboBox::Create( wxWindow *parent, wxWindowID id,
197                         const wxString& value,
198                         const wxPoint& pos, const wxSize& size,
199                         const wxArrayString& choices,
200                         long style, const wxValidator& validator,
201                         const wxString& name )
202{
203    wxCArrayString chs(choices);
204
205    return Create( parent, id, value, pos, size, chs.GetCount(),
206                   chs.GetStrings(), style, validator, name );
207}
208
209bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
210                         const wxPoint& pos, const wxSize& size,
211                         int n, const wxString choices[],
212                         long style, const wxValidator& validator,
213                         const wxString& name )
214{
215    m_ignoreNextUpdate = false;
216    m_needParent = true;
217    m_acceptsFocus = true;
218    m_prevSelection = 0;
219
220    if (!PreCreation( parent, pos, size ) ||
221        !CreateBase( parent, id, pos, size, style, validator, name ))
222    {
223        wxFAIL_MSG( wxT("wxComboBox creation failed") );
224        return false;
225    }
226
227    m_widget = gtk_combo_new();
228    GtkCombo *combo = GTK_COMBO(m_widget);
229
230    // Disable GTK's broken events ...
231    gtk_signal_disconnect( GTK_OBJECT(combo->entry), combo->entry_change_id );
232    // ... and add surrogate handler.
233    combo->entry_change_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "changed",
234                  (GtkSignalFunc) gtk_dummy_callback, combo);
235
236    // make it more useable
237    gtk_combo_set_use_arrows_always( GTK_COMBO(m_widget), TRUE );
238
239    // and case-sensitive
240    gtk_combo_set_case_sensitive( GTK_COMBO(m_widget), TRUE );
241
242    GtkWidget *list = GTK_COMBO(m_widget)->list;
243
244    // gtk_list_set_selection_mode( GTK_LIST(list), GTK_SELECTION_MULTIPLE );
245
246    for (int i = 0; i < n; i++)
247    {
248        GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( choices[i] ) );
249
250        m_clientDataList.Append( (wxObject*)NULL );
251        m_clientObjectList.Append( (wxObject*)NULL );
252
253        gtk_container_add( GTK_CONTAINER(list), list_item );
254
255        gtk_widget_show( list_item );
256    }
257
258    m_parent->DoAddChild( this );
259
260    m_focusWidget = combo->entry;
261
262    PostCreation(size);
263
264    ConnectWidget( combo->button );
265
266    // MSW's combo box shows the value and the selection is -1
267    gtk_entry_set_text( GTK_ENTRY(combo->entry), wxGTK_CONV(value) );
268    gtk_list_unselect_all( GTK_LIST(combo->list) );
269
270    if (style & wxCB_READONLY)
271        gtk_entry_set_editable( GTK_ENTRY( combo->entry ), FALSE );
272
273    // "show" and "hide" events are generated when user click on the combobox button which popups a list
274    // this list is the "popwin" gtk widget
275    gtk_signal_connect( GTK_OBJECT(GTK_COMBO(combo)->popwin), "hide",
276                        GTK_SIGNAL_FUNC(gtk_popup_hide_callback), (gpointer)this );
277    gtk_signal_connect( GTK_OBJECT(GTK_COMBO(combo)->popwin), "show",
278                        GTK_SIGNAL_FUNC(gtk_popup_show_callback), (gpointer)this );
279
280    gtk_signal_connect_after( GTK_OBJECT(combo->entry), "changed",
281      GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this );
282
283    gtk_signal_connect_after( GTK_OBJECT(combo->list), "select-child",
284      GTK_SIGNAL_FUNC(gtk_combo_select_child_callback), (gpointer)this );
285
286    SetInitialSize(size); // need this too because this is a wxControlWithItems
287
288    // This is required for tool bar support
289//    wxSize setsize = GetSize();
290//    gtk_widget_set_usize( m_widget, setsize.x, setsize.y );
291
292    return true;
293}
294
295wxComboBox::~wxComboBox()
296{
297    wxList::compatibility_iterator node = m_clientObjectList.GetFirst();
298    while (node)
299    {
300        wxClientData *cd = (wxClientData*)node->GetData();
301        if (cd) delete cd;
302        node = node->GetNext();
303    }
304    m_clientObjectList.Clear();
305
306    m_clientDataList.Clear();
307}
308
309void wxComboBox::SetFocus()
310{
311    if ( m_hasFocus )
312    {
313        // don't do anything if we already have focus
314        return;
315    }
316
317    gtk_widget_grab_focus( m_focusWidget );
318}
319
320int wxComboBox::DoAppend( const wxString &item )
321{
322    wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
323
324    DisableEvents();
325
326    GtkWidget *list = GTK_COMBO(m_widget)->list;
327
328    GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( item ) );
329
330    gtk_container_add( GTK_CONTAINER(list), list_item );
331
332    if (GTK_WIDGET_REALIZED(m_widget))
333    {
334        gtk_widget_realize( list_item );
335        gtk_widget_realize( GTK_BIN(list_item)->child );
336    }
337
338    // Apply current widget style to the new list_item
339    GtkRcStyle *style = CreateWidgetStyle();
340    if (style)
341    {
342        gtk_widget_modify_style( GTK_WIDGET( list_item ), style );
343        GtkBin *bin = GTK_BIN( list_item );
344        GtkWidget *label = GTK_WIDGET( bin->child );
345        gtk_widget_modify_style( label, style );
346        gtk_rc_style_unref( style );
347    }
348
349    gtk_widget_show( list_item );
350
351    const unsigned int count = GetCount();
352
353    if ( m_clientDataList.GetCount() < count )
354        m_clientDataList.Append( (wxObject*) NULL );
355    if ( m_clientObjectList.GetCount() < count )
356        m_clientObjectList.Append( (wxObject*) NULL );
357
358    EnableEvents();
359
360    InvalidateBestSize();
361
362    return count - 1;
363}
364
365int wxComboBox::DoInsert( const wxString &item, unsigned int pos )
366{
367    wxCHECK_MSG( !(GetWindowStyle() & wxCB_SORT), -1,
368                    wxT("can't insert into sorted list"));
369
370    wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
371
372    wxCHECK_MSG( IsValidInsert(pos), -1, wxT("invalid index") );
373
374    if (pos == GetCount())
375        return Append(item);
376
377    DisableEvents();
378
379    GtkWidget *list = GTK_COMBO(m_widget)->list;
380
381    GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( item ) );
382
383    GList *gitem_list = g_list_alloc ();
384    gitem_list->data = list_item;
385    gtk_list_insert_items( GTK_LIST (list), gitem_list, pos );
386
387    if (GTK_WIDGET_REALIZED(m_widget))
388    {
389        gtk_widget_realize( list_item );
390        gtk_widget_realize( GTK_BIN(list_item)->child );
391
392        ApplyWidgetStyle();
393    }
394
395    gtk_widget_show( list_item );
396
397    const unsigned int count = GetCount();
398
399    if ( m_clientDataList.GetCount() < count )
400        m_clientDataList.Insert( pos, (wxObject*) NULL );
401    if ( m_clientObjectList.GetCount() < count )
402        m_clientObjectList.Insert( pos, (wxObject*) NULL );
403
404    EnableEvents();
405
406    InvalidateBestSize();
407
408    return pos;
409}
410
411void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
412{
413    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
414
415    wxList::compatibility_iterator node = m_clientDataList.Item( n );
416    if (!node) return;
417
418    node->SetData( (wxObject*) clientData );
419}
420
421void* wxComboBox::DoGetItemClientData(unsigned int n) const
422{
423    wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid combobox") );
424
425    wxList::compatibility_iterator node = m_clientDataList.Item( n );
426
427    return node ? node->GetData() : NULL;
428}
429
430void wxComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
431{
432    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
433
434    wxList::compatibility_iterator node = m_clientObjectList.Item( n );
435    if (!node) return;
436
437    // wxItemContainer already deletes data for us
438
439    node->SetData( (wxObject*) clientData );
440}
441
442wxClientData* wxComboBox::DoGetItemClientObject(unsigned int n) const
443{
444    wxCHECK_MSG( m_widget != NULL, (wxClientData*)NULL, wxT("invalid combobox") );
445
446    wxList::compatibility_iterator node = m_clientObjectList.Item( n );
447
448    return node ? (wxClientData*) node->GetData() : NULL;
449}
450
451void wxComboBox::Clear()
452{
453    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
454
455    DisableEvents();
456
457    GtkWidget *list = GTK_COMBO(m_widget)->list;
458    gtk_list_clear_items( GTK_LIST(list), 0, (int)GetCount() );
459
460    wxList::compatibility_iterator node = m_clientObjectList.GetFirst();
461    while (node)
462    {
463        wxClientData *cd = (wxClientData*)node->GetData();
464        if (cd) delete cd;
465        node = node->GetNext();
466    }
467    m_clientObjectList.Clear();
468
469    m_clientDataList.Clear();
470
471    EnableEvents();
472
473    InvalidateBestSize();
474}
475
476void wxComboBox::Delete(unsigned int n)
477{
478    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
479
480    GtkList *listbox = GTK_LIST( GTK_COMBO(m_widget)->list );
481
482    GList *child = g_list_nth( listbox->children, n );
483
484    if (!child)
485    {
486        wxFAIL_MSG(wxT("wrong index"));
487        return;
488    }
489
490    DisableEvents();
491
492    GList *list = g_list_append( (GList*) NULL, child->data );
493    gtk_list_remove_items( listbox, list );
494    g_list_free( list );
495
496    wxList::compatibility_iterator node = m_clientObjectList.Item( n );
497    if (node)
498    {
499        wxClientData *cd = (wxClientData*)node->GetData();
500        if (cd) delete cd;
501        m_clientObjectList.Erase( node );
502    }
503
504    node = m_clientDataList.Item( n );
505    if (node)
506        m_clientDataList.Erase( node );
507
508    EnableEvents();
509
510    InvalidateBestSize();
511}
512
513void wxComboBox::SetString(unsigned int n, const wxString &text)
514{
515    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
516
517    GtkWidget *list = GTK_COMBO(m_widget)->list;
518
519    GList *child = g_list_nth( GTK_LIST(list)->children, n );
520    if (child)
521    {
522        GtkBin *bin = GTK_BIN( child->data );
523        GtkLabel *label = GTK_LABEL( bin->child );
524        gtk_label_set_text(label, wxGTK_CONV(text));
525    }
526    else
527    {
528        wxFAIL_MSG( wxT("wxComboBox: wrong index") );
529    }
530
531    InvalidateBestSize();
532}
533
534int wxComboBox::FindString( const wxString &item, bool bCase ) const
535{
536    wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid combobox") );
537
538    GtkWidget *list = GTK_COMBO(m_widget)->list;
539
540    GList *child = GTK_LIST(list)->children;
541    int count = 0;
542    while (child)
543    {
544        GtkBin *bin = GTK_BIN( child->data );
545        GtkLabel *label = GTK_LABEL( bin->child );
546        wxString str( label->label );
547        if (item.IsSameAs( str , bCase ) )
548            return count;
549
550        count++;
551        child = child->next;
552    }
553
554    return wxNOT_FOUND;
555}
556
557int wxComboBox::GetSelection() const
558{
559    // if the popup is currently opened, use the selection as it had been
560    // before it dropped down
561    return g_SelectionBeforePopup == wxID_NONE ? GetCurrentSelection()
562                                               : g_SelectionBeforePopup;
563}
564
565int wxComboBox::GetCurrentSelection() const
566{
567    wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
568
569    GtkWidget *list = GTK_COMBO(m_widget)->list;
570
571    GList *selection = GTK_LIST(list)->selection;
572    if (selection)
573    {
574        GList *child = GTK_LIST(list)->children;
575        int count = 0;
576        while (child)
577        {
578            if (child->data == selection->data) return count;
579            count++;
580            child = child->next;
581        }
582    }
583
584    return wxNOT_FOUND;
585}
586
587wxString wxComboBox::GetString(unsigned int n) const
588{
589    wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
590
591    GtkWidget *list = GTK_COMBO(m_widget)->list;
592
593    wxString str;
594    GList *child = g_list_nth( GTK_LIST(list)->children, n );
595    if (child)
596    {
597        GtkBin *bin = GTK_BIN( child->data );
598        GtkLabel *label = GTK_LABEL( bin->child );
599        str = wxString( label->label );
600    }
601    else
602    {
603        wxFAIL_MSG( wxT("wxComboBox: wrong index") );
604    }
605
606    return str;
607}
608
609wxString wxComboBox::GetStringSelection() const
610{
611    wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
612
613    GtkWidget *list = GTK_COMBO(m_widget)->list;
614
615    GList *selection = GTK_LIST(list)->selection;
616    if (selection)
617    {
618        GtkBin *bin = GTK_BIN( selection->data );
619        GtkLabel *label = GTK_LABEL( bin->child );
620        wxString tmp( label->label );
621        return tmp;
622    }
623
624    wxFAIL_MSG( wxT("wxComboBox: no selection") );
625
626    return wxEmptyString;
627}
628
629unsigned int wxComboBox::GetCount() const
630{
631    wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid combobox") );
632
633    GtkWidget *list = GTK_COMBO(m_widget)->list;
634
635    GList *child = GTK_LIST(list)->children;
636    unsigned int count = 0;
637    while (child) { count++; child = child->next; }
638    return count;
639}
640
641void wxComboBox::SetSelection( int n )
642{
643    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
644
645    DisableEvents();
646
647    GtkWidget *list = GTK_COMBO(m_widget)->list;
648    gtk_list_unselect_item( GTK_LIST(list), m_prevSelection );
649    gtk_list_select_item( GTK_LIST(list), n );
650    m_prevSelection = n;
651
652    EnableEvents();
653}
654
655wxString wxComboBox::GetValue() const
656{
657    GtkEntry *entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
658    wxString tmp( wxGTK_CONV_BACK( gtk_entry_get_text( entry ) ) );
659
660#if 0
661    for (int i = 0; i < wxStrlen(tmp.c_str()) +1; i++)
662    {
663        wxChar c = tmp[i];
664        printf( "%d ", (int) (c) );
665    }
666    printf( "\n" );
667#endif
668
669    return tmp;
670}
671
672void wxComboBox::SetValue( const wxString& value )
673{
674    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
675
676    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
677    wxString tmp;
678    if (!value.IsNull()) tmp = value;
679    gtk_entry_set_text( GTK_ENTRY(entry), wxGTK_CONV( tmp ) );
680
681    InvalidateBestSize();
682}
683
684void wxComboBox::Copy()
685{
686    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
687
688    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
689    gtk_editable_copy_clipboard( GTK_EDITABLE(entry) DUMMY_CLIPBOARD_ARG );
690}
691
692void wxComboBox::Cut()
693{
694    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
695
696    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
697    gtk_editable_cut_clipboard( GTK_EDITABLE(entry) DUMMY_CLIPBOARD_ARG );
698}
699
700void wxComboBox::Paste()
701{
702    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
703
704    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
705    gtk_editable_paste_clipboard( GTK_EDITABLE(entry) DUMMY_CLIPBOARD_ARG);
706}
707
708void wxComboBox::Undo()
709{
710    // TODO
711}
712
713void wxComboBox::Redo()
714{
715    // TODO
716}
717
718void wxComboBox::SelectAll()
719{
720    SetSelection(0, GetLastPosition());
721}
722
723bool wxComboBox::CanUndo() const
724{
725    // TODO
726    return false;
727}
728
729bool wxComboBox::CanRedo() const
730{
731    // TODO
732    return false;
733}
734
735bool wxComboBox::HasSelection() const
736{
737    long from, to;
738    GetSelection(&from, &to);
739    return from != to;
740}
741
742bool wxComboBox::CanCopy() const
743{
744    // Can copy if there's a selection
745    return HasSelection();
746}
747
748bool wxComboBox::CanCut() const
749{
750    return CanCopy() && IsEditable();
751}
752
753bool wxComboBox::CanPaste() const
754{
755    // TODO: check for text on the clipboard
756    return IsEditable() ;
757}
758
759bool wxComboBox::IsEditable() const
760{
761    return !HasFlag(wxCB_READONLY);
762}
763
764
765void wxComboBox::SetInsertionPoint( long pos )
766{
767    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
768
769    if ( pos == GetLastPosition() )
770        pos = -1;
771
772    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
773    gtk_entry_set_position( GTK_ENTRY(entry), (int)pos );
774}
775
776long wxComboBox::GetInsertionPoint() const
777{
778    return (long) GET_EDITABLE_POS( GTK_COMBO(m_widget)->entry );
779}
780
781wxTextPos wxComboBox::GetLastPosition() const
782{
783    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
784    int pos = GTK_ENTRY(entry)->text_length;
785    return (long) pos-1;
786}
787
788void wxComboBox::Replace( long from, long to, const wxString& value )
789{
790    wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
791
792    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
793    gtk_editable_delete_text( GTK_EDITABLE(entry), (gint)from, (gint)to );
794    if (value.IsNull()) return;
795    gint pos = (gint)to;
796
797#if wxUSE_UNICODE
798    wxCharBuffer buffer = wxConvUTF8.cWX2MB( value );
799    gtk_editable_insert_text( GTK_EDITABLE(entry), (const char*) buffer, strlen( (const char*) buffer ), &pos );
800#else
801    gtk_editable_insert_text( GTK_EDITABLE(entry), value.c_str(), value.length(), &pos );
802#endif
803}
804
805void wxComboBox::SetSelection( long from, long to )
806{
807    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
808    gtk_editable_select_region( GTK_EDITABLE(entry), (gint)from, (gint)to );
809}
810
811void wxComboBox::GetSelection( long* from, long* to ) const
812{
813    if (IsEditable())
814    {
815        GtkEditable *editable = GTK_EDITABLE(GTK_COMBO(m_widget)->entry);
816        *from = (long) editable->selection_start_pos;
817        *to = (long) editable->selection_end_pos;
818    }
819}
820
821void wxComboBox::SetEditable( bool editable )
822{
823    GtkWidget *entry = GTK_COMBO(m_widget)->entry;
824    gtk_entry_set_editable( GTK_ENTRY(entry), editable );
825}
826
827void wxComboBox::OnChar( wxKeyEvent &event )
828{
829    if ( event.GetKeyCode() == WXK_RETURN )
830    {
831        // GTK automatically selects an item if its in the list
832        wxCommandEvent eventEnter(wxEVT_COMMAND_TEXT_ENTER, GetId());
833        eventEnter.SetString( GetValue() );
834        eventEnter.SetInt( GetSelection() );
835        eventEnter.SetEventObject( this );
836
837        if (!GetEventHandler()->ProcessEvent( eventEnter ))
838        {
839            // This will invoke the dialog default action, such
840            // as the clicking the default button.
841
842            wxWindow *top_frame = m_parent;
843            while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
844                top_frame = top_frame->GetParent();
845
846            if (top_frame && GTK_IS_WINDOW(top_frame->m_widget))
847            {
848                GtkWindow *window = GTK_WINDOW(top_frame->m_widget);
849
850                if (window->default_widget)
851                        gtk_widget_activate (window->default_widget);
852            }
853        }
854
855        // Catch GTK event so that GTK doesn't open the drop
856        // down list upon RETURN.
857        return;
858    }
859
860    event.Skip();
861}
862
863void wxComboBox::DisableEvents()
864{
865    gtk_signal_disconnect_by_func( GTK_OBJECT(GTK_COMBO(m_widget)->list),
866      GTK_SIGNAL_FUNC(gtk_combo_select_child_callback), (gpointer)this );
867    gtk_signal_disconnect_by_func( GTK_OBJECT(GTK_COMBO(m_widget)->entry),
868      GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this );
869}
870
871void wxComboBox::EnableEvents()
872{
873    gtk_signal_connect_after( GTK_OBJECT(GTK_COMBO(m_widget)->list), "select-child",
874      GTK_SIGNAL_FUNC(gtk_combo_select_child_callback), (gpointer)this );
875    gtk_signal_connect_after( GTK_OBJECT(GTK_COMBO(m_widget)->entry), "changed",
876      GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this );
877}
878
879void wxComboBox::OnSize( wxSizeEvent &event )
880{
881    // NB: In some situations (e.g. on non-first page of a wizard, if the
882    //     size used is default size), GtkCombo widget is resized correctly,
883    //     but it's look is not updated, it's rendered as if it was much wider.
884    //     No other widgets are affected, so it looks like a bug in GTK+.
885    //     Manually requesting resize calculation (as gtk_pizza_set_size does)
886    //     fixes it.
887    if (GTK_WIDGET_VISIBLE(m_widget))
888        gtk_widget_queue_resize(m_widget);
889
890    event.Skip();
891}
892
893void wxComboBox::DoApplyWidgetStyle(GtkRcStyle *style)
894{
895//    gtk_widget_modify_style( GTK_COMBO(m_widget)->button, syle );
896
897    gtk_widget_modify_style( GTK_COMBO(m_widget)->entry, style );
898    gtk_widget_modify_style( GTK_COMBO(m_widget)->list, style );
899
900    GtkList *list = GTK_LIST( GTK_COMBO(m_widget)->list );
901    GList *child = list->children;
902    while (child)
903    {
904        gtk_widget_modify_style( GTK_WIDGET(child->data), style );
905
906        GtkBin *bin = GTK_BIN(child->data);
907        gtk_widget_modify_style( bin->child, style );
908
909        child = child->next;
910    }
911}
912
913GtkWidget* wxComboBox::GetConnectWidget()
914{
915    return GTK_COMBO(m_widget)->entry;
916}
917
918bool wxComboBox::IsOwnGtkWindow( GdkWindow *window )
919{
920    return ( (window == GTK_ENTRY( GTK_COMBO(m_widget)->entry )->text_area) ||
921             (window == GTK_COMBO(m_widget)->button->window ) );
922}
923
924wxSize wxComboBox::DoGetBestSize() const
925{
926    wxSize ret( wxControl::DoGetBestSize() );
927
928    // we know better our horizontal extent: it depends on the longest string
929    // in the combobox
930    if ( m_widget )
931    {
932        int width;
933        unsigned int count = GetCount();
934        for ( unsigned int n = 0; n < count; n++ )
935        {
936            GetTextExtent(GetString(n), &width, NULL, NULL, NULL );
937            if ( width > ret.x )
938                ret.x = width;
939        }
940    }
941
942    // empty combobox should have some reasonable default size too
943    if ( ret.x < 100 )
944        ret.x = 100;
945
946    CacheBestSize(ret);
947    return ret;
948}
949
950// static
951wxVisualAttributes
952wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
953{
954    return GetDefaultAttributesFromGTKWidget(gtk_combo_new, true);
955}
956
957// ----------------------------------------------------------------------------
958// standard event handling
959// ----------------------------------------------------------------------------
960
961void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
962{
963    Cut();
964}
965
966void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
967{
968    Copy();
969}
970
971void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
972{
973    Paste();
974}
975
976void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
977{
978    Undo();
979}
980
981void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
982{
983    Redo();
984}
985
986void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
987{
988    long from, to;
989    GetSelection(& from, & to);
990    if (from != -1 && to != -1)
991        Remove(from, to);
992}
993
994void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
995{
996    SetSelection(-1, -1);
997}
998
999void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
1000{
1001    event.Enable( CanCut() );
1002}
1003
1004void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
1005{
1006    event.Enable( CanCopy() );
1007}
1008
1009void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
1010{
1011    event.Enable( CanPaste() );
1012}
1013
1014void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
1015{
1016    event.Enable( CanUndo() );
1017}
1018
1019void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
1020{
1021    event.Enable( CanRedo() );
1022}
1023
1024void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
1025{
1026    event.Enable(HasSelection() && IsEditable()) ;
1027}
1028
1029void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
1030{
1031    event.Enable(GetLastPosition() > 0);
1032}
1033
1034#endif
1035