1 ///////////////////////////////////////////////////////////////////////////// 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  11 #pragma implementation "combobox.h" 
  14 #include "wx/combobox.h" 
  18 #include "wx/settings.h" 
  24 //----------------------------------------------------------------------------- 
  26 //----------------------------------------------------------------------------- 
  28 extern void wxapp_install_idle_handler(); 
  31 //----------------------------------------------------------------------------- 
  33 //----------------------------------------------------------------------------- 
  35 extern bool   g_blockEventsOnDrag
; 
  37 //----------------------------------------------------------------------------- 
  39 //----------------------------------------------------------------------------- 
  42 gtk_combo_clicked_callback( GtkWidget 
*WXUNUSED(widget
), wxComboBox 
*combo 
) 
  44     if (g_isIdle
) wxapp_install_idle_handler(); 
  46     if (!combo
->m_hasVMT
) return; 
  48     if (g_blockEventsOnDrag
) return; 
  50     if (combo
->m_alreadySent
) 
  52         combo
->m_alreadySent 
= FALSE
; 
  56     combo
->m_alreadySent 
= TRUE
; 
  58     wxCommandEvent 
event( wxEVT_COMMAND_COMBOBOX_SELECTED
, combo
->GetId() ); 
  59     event
.SetInt( combo
->GetSelection() ); 
  60     event
.SetString( combo
->GetStringSelection() ); 
  61     event
.SetEventObject( combo 
); 
  63     combo
->GetEventHandler()->ProcessEvent( event 
); 
  66 //----------------------------------------------------------------------------- 
  68 //----------------------------------------------------------------------------- 
  71 gtk_text_changed_callback( GtkWidget 
*WXUNUSED(widget
), wxComboBox 
*combo 
) 
  73     if (g_isIdle
) wxapp_install_idle_handler(); 
  75     if (!combo
->m_hasVMT
) return; 
  77     wxCommandEvent 
event( wxEVT_COMMAND_TEXT_UPDATED
, combo
->GetId() ); 
  78     event
.SetString( combo
->GetValue() ); 
  79     event
.SetEventObject( combo 
); 
  80     combo
->GetEventHandler()->ProcessEvent( event 
); 
  83 //----------------------------------------------------------------------------- 
  85 //----------------------------------------------------------------------------- 
  87 IMPLEMENT_DYNAMIC_CLASS(wxComboBox
,wxControl
) 
  89 BEGIN_EVENT_TABLE(wxComboBox
, wxControl
) 
  90     EVT_SIZE(wxComboBox::OnSize
) 
  91     EVT_CHAR(wxComboBox::OnChar
) 
  94 bool wxComboBox::Create( wxWindow 
*parent
, wxWindowID id
, const wxString
& value
, 
  95                          const wxPoint
& pos
, const wxSize
& size
, 
  96                          int n
, const wxString choices
[], 
  97                          long style
, const wxValidator
& validator
, 
  98                          const wxString
& name 
) 
 100     m_alreadySent 
= FALSE
; 
 102     m_acceptsFocus 
= TRUE
; 
 104     if (!PreCreation( parent
, pos
, size 
) || 
 105         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 107         wxFAIL_MSG( wxT("wxComboBox creation failed") ); 
 111     m_widget 
= gtk_combo_new(); 
 113     // make it more useable 
 114     gtk_combo_set_use_arrows_always(GTK_COMBO(m_widget
), TRUE
); 
 116     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 118     for (int i 
= 0; i 
< n
; i
++) 
 120         /* don't send first event, which GTK sends aways when 
 121            inserting the first item */ 
 122         m_alreadySent 
= TRUE
; 
 124         GtkWidget 
*list_item 
= gtk_list_item_new_with_label( choices
[i
].mbc_str() ); 
 126         m_clientDataList
.Append( (wxObject
*)NULL 
); 
 127         m_clientObjectList
.Append( (wxObject
*)NULL 
); 
 129         gtk_container_add( GTK_CONTAINER(list
), list_item 
); 
 131         gtk_signal_connect( GTK_OBJECT(list_item
), "select", 
 132            GTK_SIGNAL_FUNC(gtk_combo_clicked_callback
), (gpointer
)this ); 
 134         gtk_widget_show( list_item 
); 
 137     m_parent
->DoAddChild( this ); 
 141     ConnectWidget( GTK_COMBO(m_widget
)->button 
); 
 143     if (!value
.IsNull()) SetValue( value 
); 
 145     if (style 
& wxCB_READONLY
) 
 146         gtk_entry_set_editable( GTK_ENTRY( GTK_COMBO(m_widget
)->entry 
), FALSE 
); 
 148     gtk_signal_connect( GTK_OBJECT(GTK_COMBO(m_widget
)->entry
), "changed", 
 149       GTK_SIGNAL_FUNC(gtk_text_changed_callback
), (gpointer
)this); 
 151     wxSize 
size_best( DoGetBestSize() ); 
 152     wxSize 
new_size( size 
); 
 153     if (new_size
.x 
== -1) 
 154         new_size
.x 
= size_best
.x
; 
 155     if (new_size
.y 
== -1) 
 156         new_size
.y 
= size_best
.y
; 
 157     if (new_size
.y 
> size_best
.y
) 
 158         new_size
.y 
= size_best
.y
; 
 159     if ((new_size
.x 
!= size
.x
) || (new_size
.y 
!= size
.y
)) 
 160         SetSize( new_size
.x
, new_size
.y 
); 
 162     SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOW 
) ); 
 163     SetForegroundColour( parent
->GetForegroundColour() ); 
 170 wxComboBox::~wxComboBox() 
 172     wxNode 
*node 
= m_clientObjectList
.First(); 
 175         wxClientData 
*cd 
= (wxClientData
*)node
->Data(); 
 179     m_clientObjectList
.Clear(); 
 181     m_clientDataList
.Clear(); 
 184 void wxComboBox::AppendCommon( const wxString 
&item 
) 
 186     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 188     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 190     GtkWidget 
*list_item 
= gtk_list_item_new_with_label( item
.mbc_str() ); 
 192     gtk_container_add( GTK_CONTAINER(list
), list_item 
); 
 194     gtk_signal_connect( GTK_OBJECT(list_item
), "select", 
 195       GTK_SIGNAL_FUNC(gtk_combo_clicked_callback
), (gpointer
)this ); 
 197     if (GTK_WIDGET_REALIZED(m_widget
)) 
 199         gtk_widget_realize( list_item 
); 
 200         gtk_widget_realize( GTK_BIN(list_item
)->child 
); 
 202         if (m_widgetStyle
) ApplyWidgetStyle(); 
 205     gtk_widget_show( list_item 
); 
 208 void wxComboBox::Append( const wxString 
&item 
) 
 210     m_clientDataList
.Append( (wxObject
*) NULL 
); 
 211     m_clientObjectList
.Append( (wxObject
*) NULL 
); 
 213     AppendCommon( item 
); 
 216 void wxComboBox::Append( const wxString 
&item
, void *clientData 
) 
 218     m_clientDataList
.Append( (wxObject
*) clientData 
); 
 219     m_clientObjectList
.Append( (wxObject
*)NULL 
); 
 221     AppendCommon( item 
); 
 224 void wxComboBox::Append( const wxString 
&item
, wxClientData 
*clientData 
) 
 226     m_clientDataList
.Append( (wxObject
*) NULL 
); 
 227     m_clientObjectList
.Append( (wxObject
*) clientData 
); 
 229     AppendCommon( item 
); 
 232 void wxComboBox::SetClientData( int n
, void* clientData 
) 
 234     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 236     wxNode 
*node 
= m_clientDataList
.Nth( n 
); 
 239     node
->SetData( (wxObject
*) clientData 
); 
 242 void* wxComboBox::GetClientData( int n 
) 
 244     wxCHECK_MSG( m_widget 
!= NULL
, NULL
, wxT("invalid combobox") ); 
 246     wxNode 
*node 
= m_clientDataList
.Nth( n 
); 
 247     if (!node
) return NULL
; 
 252 void wxComboBox::SetClientObject( int n
, wxClientData
* clientData 
) 
 254     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 256     wxNode 
*node 
= m_clientObjectList
.Nth( n 
); 
 259     wxClientData 
*cd 
= (wxClientData
*) node
->Data(); 
 262     node
->SetData( (wxObject
*) clientData 
); 
 265 wxClientData
* wxComboBox::GetClientObject( int n 
) 
 267     wxCHECK_MSG( m_widget 
!= NULL
, (wxClientData
*)NULL
, wxT("invalid combobox") ); 
 269     wxNode 
*node 
= m_clientDataList
.Nth( n 
); 
 270     if (!node
) return (wxClientData
*) NULL
; 
 272     return (wxClientData
*) node
->Data(); 
 275 void wxComboBox::Clear() 
 277     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 279     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 280     gtk_list_clear_items( GTK_LIST(list
), 0, Number() ); 
 282     wxNode 
*node 
= m_clientObjectList
.First(); 
 285         wxClientData 
*cd 
= (wxClientData
*)node
->Data(); 
 289     m_clientObjectList
.Clear(); 
 291     m_clientDataList
.Clear(); 
 294 void wxComboBox::Delete( int n 
) 
 296     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 298     GtkList 
*listbox 
= GTK_LIST( GTK_COMBO(m_widget
)->list 
); 
 300     GList 
*child 
= g_list_nth( listbox
->children
, n 
); 
 304         wxFAIL_MSG(wxT("wrong index")); 
 308     GList 
*list 
= g_list_append( (GList
*) NULL
, child
->data 
); 
 309     gtk_list_remove_items( listbox
, list 
); 
 312     wxNode 
*node 
= m_clientObjectList
.Nth( n 
); 
 315         wxClientData 
*cd 
= (wxClientData
*)node
->Data(); 
 317         m_clientObjectList
.DeleteNode( node 
); 
 320     node 
= m_clientDataList
.Nth( n 
); 
 323         m_clientDataList
.DeleteNode( node 
); 
 327 int wxComboBox::FindString( const wxString 
&item 
) 
 329     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid combobox") ); 
 331     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 333     GList 
*child 
= GTK_LIST(list
)->children
; 
 337         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 338         GtkLabel 
*label 
= GTK_LABEL( bin
->child 
); 
 339         if (item 
== wxString(label
->label
,*wxConvCurrent
)) 
 348 int wxComboBox::GetSelection() const 
 350     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid combobox") ); 
 352     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 354     GList 
*selection 
= GTK_LIST(list
)->selection
; 
 357         GList 
*child 
= GTK_LIST(list
)->children
; 
 361             if (child
->data 
== selection
->data
) return count
; 
 370 wxString 
wxComboBox::GetString( int n 
) const 
 372     wxCHECK_MSG( m_widget 
!= NULL
, wxT(""), wxT("invalid combobox") ); 
 374     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 377     GList 
*child 
= g_list_nth( GTK_LIST(list
)->children
, n 
); 
 380         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 381         GtkLabel 
*label 
= GTK_LABEL( bin
->child 
); 
 382         str 
= wxString(label
->label
,*wxConvCurrent
); 
 386         wxFAIL_MSG( wxT("wxComboBox: wrong index") ); 
 392 wxString 
wxComboBox::GetStringSelection() const 
 394     wxCHECK_MSG( m_widget 
!= NULL
, wxT(""), wxT("invalid combobox") ); 
 396     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 398     GList 
*selection 
= GTK_LIST(list
)->selection
; 
 401         GtkBin 
*bin 
= GTK_BIN( selection
->data 
); 
 402         wxString tmp 
= wxString(GTK_LABEL( bin
->child 
)->label
,*wxConvCurrent
); 
 406     wxFAIL_MSG( wxT("wxComboBox: no selection") ); 
 411 int wxComboBox::Number() const 
 413     wxCHECK_MSG( m_widget 
!= NULL
, 0, wxT("invalid combobox") ); 
 415     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 417     GList 
*child 
= GTK_LIST(list
)->children
; 
 419     while (child
) { count
++; child 
= child
->next
; } 
 423 void wxComboBox::SetSelection( int n 
) 
 425     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 429     GtkWidget 
*list 
= GTK_COMBO(m_widget
)->list
; 
 430     gtk_list_select_item( GTK_LIST(list
), n 
); 
 435 void wxComboBox::SetStringSelection( const wxString 
&string 
) 
 437     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 439     int res 
= FindString( string 
); 
 440     if (res 
== -1) return; 
 444 wxString 
wxComboBox::GetValue() const 
 446     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 447     wxString tmp 
= wxString(gtk_entry_get_text( GTK_ENTRY(entry
) ),*wxConvCurrent
); 
 451 void wxComboBox::SetValue( const wxString
& value 
) 
 453     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 455     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 456     wxString tmp 
= wxT(""); 
 457     if (!value
.IsNull()) tmp 
= value
; 
 458     gtk_entry_set_text( GTK_ENTRY(entry
), tmp
.mbc_str() ); 
 461 void wxComboBox::Copy() 
 463     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 465     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 466 #if defined(__WXGTK13__) || (GTK_MINOR_VERSION > 0) 
 467     gtk_editable_copy_clipboard( GTK_EDITABLE(entry
) ); 
 469     gtk_editable_copy_clipboard( GTK_EDITABLE(entry
), 0 ); 
 473 void wxComboBox::Cut() 
 475     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 477     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 478 #if defined(__WXGTK13__) || (GTK_MINOR_VERSION > 0) 
 479     gtk_editable_cut_clipboard( GTK_EDITABLE(entry
) ); 
 481     gtk_editable_cut_clipboard( GTK_EDITABLE(entry
), 0 ); 
 485 void wxComboBox::Paste() 
 487     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 489     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 490 #if defined(__WXGTK13__) || (GTK_MINOR_VERSION > 0) 
 491     gtk_editable_paste_clipboard( GTK_EDITABLE(entry
) ); 
 493     gtk_editable_paste_clipboard( GTK_EDITABLE(entry
), 0 ); 
 497 void wxComboBox::SetInsertionPoint( long pos 
) 
 499     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 501     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 502     gtk_entry_set_position( GTK_ENTRY(entry
), (int)pos 
); 
 505 void wxComboBox::SetInsertionPointEnd() 
 507     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 509     SetInsertionPoint( -1 ); 
 512 long wxComboBox::GetInsertionPoint() const 
 514     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 515     return (long) GTK_EDITABLE(entry
)->current_pos
; 
 518 long wxComboBox::GetLastPosition() const 
 520     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 521     int pos 
= GTK_ENTRY(entry
)->text_length
; 
 525 void wxComboBox::Replace( long from
, long to
, const wxString
& value 
) 
 527     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 528     // FIXME: not quite sure how to do this method right in multibyte mode 
 530     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 531     gtk_editable_delete_text( GTK_EDITABLE(entry
), (gint
)from
, (gint
)to 
); 
 532     if (value
.IsNull()) return; 
 534     gtk_editable_insert_text( GTK_EDITABLE(entry
), value
.mbc_str(), value
.Length(), &pos 
); 
 537 void wxComboBox::Remove(long from
, long to
) 
 539     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid combobox") ); 
 541     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 542     gtk_editable_delete_text( GTK_EDITABLE(entry
), (gint
)from
, (gint
)to 
); 
 545 void wxComboBox::SetSelection( long from
, long to 
) 
 547     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 548     gtk_editable_select_region( GTK_EDITABLE(entry
), (gint
)from
, (gint
)to 
); 
 551 void wxComboBox::SetEditable( bool editable 
) 
 553     GtkWidget 
*entry 
= GTK_COMBO(m_widget
)->entry
; 
 554     gtk_entry_set_editable( GTK_ENTRY(entry
), editable 
); 
 557 void wxComboBox::OnChar( wxKeyEvent 
&event 
) 
 559     if ( event
.KeyCode() == WXK_RETURN 
) 
 561         wxString value 
= GetValue(); 
 565             // make Enter generate "selected" event if there is only one item 
 566             // in the combobox - without it, it's impossible to select it at 
 568             wxCommandEvent 
event( wxEVT_COMMAND_COMBOBOX_SELECTED
, GetId() ); 
 570             event
.SetString( value 
); 
 571             event
.SetEventObject( this ); 
 572             GetEventHandler()->ProcessEvent( event 
); 
 576             // add the item to the list if it's not there yet 
 577             if ( FindString(value
) == wxNOT_FOUND 
) 
 580                 SetStringSelection(value
); 
 582                 // and generate the selected event for it 
 583                 wxCommandEvent 
event( wxEVT_COMMAND_COMBOBOX_SELECTED
, GetId() ); 
 584                 event
.SetInt( Number() - 1 ); 
 585                 event
.SetString( value 
); 
 586                 event
.SetEventObject( this ); 
 587                 GetEventHandler()->ProcessEvent( event 
); 
 589             //else: do nothing, this will open the listbox 
 596 void wxComboBox::DisableEvents() 
 598     GtkList 
*list 
= GTK_LIST( GTK_COMBO(m_widget
)->list 
); 
 599     GList 
*child 
= list
->children
; 
 602         gtk_signal_disconnect_by_func( GTK_OBJECT(child
->data
), 
 603           GTK_SIGNAL_FUNC(gtk_combo_clicked_callback
), (gpointer
)this ); 
 609 void wxComboBox::EnableEvents() 
 611     GtkList 
*list 
= GTK_LIST( GTK_COMBO(m_widget
)->list 
); 
 612     GList 
*child 
= list
->children
; 
 615         gtk_signal_connect( GTK_OBJECT(child
->data
), "select", 
 616           GTK_SIGNAL_FUNC(gtk_combo_clicked_callback
), (gpointer
)this ); 
 622 void wxComboBox::OnSize( wxSizeEvent 
&event 
) 
 628     gtk_widget_set_usize( GTK_COMBO(m_widget
)->entry
, m_width
-w
-1, m_height 
); 
 630     gtk_widget_set_uposition( GTK_COMBO(m_widget
)->button
, m_x
+m_width
-w
, m_y 
); 
 631     gtk_widget_set_usize( GTK_COMBO(m_widget
)->button
, w
, m_height 
); 
 635 void wxComboBox::ApplyWidgetStyle() 
 639 //    gtk_widget_set_style( GTK_COMBO(m_widget)->button, m_widgetStyle ); 
 640     gtk_widget_set_style( GTK_COMBO(m_widget
)->entry
, m_widgetStyle 
); 
 641     gtk_widget_set_style( GTK_COMBO(m_widget
)->list
, m_widgetStyle 
); 
 643     GtkList 
*list 
= GTK_LIST( GTK_COMBO(m_widget
)->list 
); 
 644     GList 
*child 
= list
->children
; 
 647         gtk_widget_set_style( GTK_WIDGET(child
->data
), m_widgetStyle 
); 
 649         GtkBin 
*bin 
= GTK_BIN(child
->data
); 
 650         gtk_widget_set_style( bin
->child
, m_widgetStyle 
); 
 656 GtkWidget
* wxComboBox::GetConnectWidget() 
 658     return GTK_COMBO(m_widget
)->entry
; 
 661 bool wxComboBox::IsOwnGtkWindow( GdkWindow 
*window 
) 
 663     return ( (window 
== GTK_ENTRY( GTK_COMBO(m_widget
)->entry 
)->text_area
) || 
 664              (window 
== GTK_COMBO(m_widget
)->button
->window 
) ); 
 667 wxSize 
wxComboBox::DoGetBestSize() const 
 669     wxSize 
ret( wxControl::DoGetBestSize() ); 
 671     // we know better our horizontal extent: it depends on the longest string 
 676         GdkFont 
*font 
= m_font
.GetInternalFont(); 
 679         size_t count 
= Number(); 
 680         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 682             width 
= (wxCoord
)gdk_string_width(font
, GetString(n
).mbc_str()); 
 688     // empty combobox should have some reasonable default size too