]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/combobox.cpp
decouple item index from string value (patch 1905702)
[wxWidgets.git] / src / gtk / combobox.cpp
CommitLineData
53010e52 1/////////////////////////////////////////////////////////////////////////////
11e62fe6 2// Name: src/gtk/combobox.cpp
53010e52
RR
3// Purpose:
4// Author: Robert Roebling
dbf858b5 5// Id: $Id$
01111366 6// Copyright: (c) 1998 Robert Roebling
65571936 7// Licence: wxWindows licence
53010e52
RR
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
dcf924a3
RR
13#if wxUSE_COMBOBOX
14
8228b893
WS
15#include "wx/combobox.h"
16
88a7a4e1
WS
17#ifndef WX_PRECOMP
18 #include "wx/intl.h"
9eddec69 19 #include "wx/settings.h"
fec9cc08 20 #include "wx/textctrl.h" // for wxEVT_COMMAND_TEXT_UPDATED
aaa6d89a 21 #include "wx/arrstr.h"
88a7a4e1
WS
22#endif
23
9e691f46 24#include "wx/gtk/private.h"
83624f79 25
ff654490
VZ
26// ----------------------------------------------------------------------------
27// GTK callbacks
28// ----------------------------------------------------------------------------
461573cc 29
590f50d6
RR
30extern "C" {
31static void
32gtkcombobox_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
33{
590f50d6
RR
34 if (!combo->m_hasVMT) return;
35
36 wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
37 event.SetString( combo->GetValue() );
38 event.SetEventObject( combo );
937013e0 39 combo->HandleWindowEvent( event );
590f50d6 40}
590f50d6 41
590f50d6
RR
42static void
43gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
44{
590f50d6 45 if (!combo->m_hasVMT) return;
ddd53873
RR
46
47 if (combo->GetSelection() == -1)
48 return;
590f50d6
RR
49
50 wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
51 event.SetInt( combo->GetSelection() );
52 event.SetString( combo->GetStringSelection() );
53 event.SetEventObject( combo );
937013e0 54 combo->HandleWindowEvent( event );
590f50d6
RR
55}
56}
a73ae836 57
e1e955e1
RR
58//-----------------------------------------------------------------------------
59// wxComboBox
53010e52
RR
60//-----------------------------------------------------------------------------
61
62IMPLEMENT_DYNAMIC_CLASS(wxComboBox,wxControl)
63
b4071e91 64BEGIN_EVENT_TABLE(wxComboBox, wxControl)
8a85884a 65 EVT_CHAR(wxComboBox::OnChar)
150e31d2
JS
66
67 EVT_MENU(wxID_CUT, wxComboBox::OnCut)
68 EVT_MENU(wxID_COPY, wxComboBox::OnCopy)
69 EVT_MENU(wxID_PASTE, wxComboBox::OnPaste)
70 EVT_MENU(wxID_UNDO, wxComboBox::OnUndo)
71 EVT_MENU(wxID_REDO, wxComboBox::OnRedo)
72 EVT_MENU(wxID_CLEAR, wxComboBox::OnDelete)
73 EVT_MENU(wxID_SELECTALL, wxComboBox::OnSelectAll)
74
75 EVT_UPDATE_UI(wxID_CUT, wxComboBox::OnUpdateCut)
76 EVT_UPDATE_UI(wxID_COPY, wxComboBox::OnUpdateCopy)
77 EVT_UPDATE_UI(wxID_PASTE, wxComboBox::OnUpdatePaste)
78 EVT_UPDATE_UI(wxID_UNDO, wxComboBox::OnUpdateUndo)
79 EVT_UPDATE_UI(wxID_REDO, wxComboBox::OnUpdateRedo)
80 EVT_UPDATE_UI(wxID_CLEAR, wxComboBox::OnUpdateDelete)
81 EVT_UPDATE_UI(wxID_SELECTALL, wxComboBox::OnUpdateSelectAll)
b4071e91
RR
82END_EVENT_TABLE()
83
584ad2a3
MB
84bool wxComboBox::Create( wxWindow *parent, wxWindowID id,
85 const wxString& value,
86 const wxPoint& pos, const wxSize& size,
87 const wxArrayString& choices,
88 long style, const wxValidator& validator,
89 const wxString& name )
90{
91 wxCArrayString chs(choices);
92
93 return Create( parent, id, value, pos, size, chs.GetCount(),
94 chs.GetStrings(), style, validator, name );
95}
96
fd0eed64
RR
97bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
98 const wxPoint& pos, const wxSize& size,
99 int n, const wxString choices[],
805dd538
VZ
100 long style, const wxValidator& validator,
101 const wxString& name )
53010e52 102{
a236aa20 103 m_strings = NULL;
7d8268a1 104 m_ignoreNextUpdate = false;
159b66c0 105 m_prevSelection = 0;
805dd538 106
db434467 107 if (!PreCreation( parent, pos, size ) ||
4dcaf11a
RR
108 !CreateBase( parent, id, pos, size, style, validator, name ))
109 {
223d09f6 110 wxFAIL_MSG( wxT("wxComboBox creation failed") );
7d8268a1 111 return false;
4dcaf11a 112 }
6de97a3b 113
2f515791 114 if (HasFlag(wxCB_SORT))
a236aa20
VZ
115 m_strings = new wxSortedArrayString();
116
ff654490 117 m_widget = gtk_combo_box_entry_new_text();
30ed6e5c 118
2f515791
RR
119 if (HasFlag(wxBORDER_NONE))
120 {
121 // Doesn't seem to work
122 // g_object_set (m_widget, "has-frame", FALSE, NULL);
123 }
124
ff654490 125 GtkEntry * const entry = GetEntry();
3ca6a5f0 126
ff654490 127 gtk_entry_set_editable( entry, TRUE );
805dd538 128
a236aa20 129 Append(n, choices);
590f50d6 130
f03fc89f 131 m_parent->DoAddChild( this );
30ed6e5c 132
590f50d6 133 m_focusWidget = GTK_WIDGET( entry );
805dd538 134
abdeb9e7 135 PostCreation(size);
53010e52 136
ff654490 137 ConnectWidget( m_widget );
805dd538 138
ff654490 139 gtk_entry_set_text( entry, wxGTK_CONV(value) );
8228b893 140
ff654490
VZ
141 if (style & wxCB_READONLY)
142 gtk_entry_set_editable( entry, FALSE );
8228b893 143
ff654490
VZ
144 g_signal_connect_after (entry, "changed",
145 G_CALLBACK (gtkcombobox_text_changed_callback), this);
8228b893 146
ff654490
VZ
147 g_signal_connect_after (m_widget, "changed",
148 G_CALLBACK (gtkcombobox_changed_callback), this);
f4322df6 149
805dd538 150
170acdc9 151 SetInitialSize(size); // need this too because this is a wxControlWithItems
805dd538 152
7d8268a1 153 return true;
fd0eed64
RR
154}
155
ff654490
VZ
156GtkEntry *wxComboBox::GetEntry() const
157{
158 return GTK_ENTRY(GTK_BIN(m_widget)->child);
159}
160
0ec1179b
VZ
161GtkEditable *wxComboBox::GetEditable() const
162{
ff654490 163 return GTK_EDITABLE( GTK_BIN(m_widget)->child );
0ec1179b
VZ
164}
165
fd0eed64
RR
166wxComboBox::~wxComboBox()
167{
a236aa20 168 Clear();
7d6d2cd4 169
a236aa20 170 delete m_strings;
6de97a3b 171}
53010e52 172
2b5f62a0
VZ
173void wxComboBox::SetFocus()
174{
175 if ( m_hasFocus )
176 {
177 // don't do anything if we already have focus
178 return;
179 }
180
181 gtk_widget_grab_focus( m_focusWidget );
182}
183
a236aa20
VZ
184int wxComboBox::DoInsertItems(const wxArrayStringsAdapter & items,
185 unsigned int pos,
186 void **clientData, wxClientDataType type)
53010e52 187{
2a68b7a0 188 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
805dd538 189
a236aa20
VZ
190 wxASSERT_MSG( !IsSorted() || (pos == GetCount()),
191 _T("In a sorted combobox data could only be appended"));
192
193 const int count = items.GetCount();
194
195 int n = wxNOT_FOUND;
196
ff654490
VZ
197 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
198 for( int i = 0; i < count; ++i )
590f50d6 199 {
ff654490
VZ
200 n = pos + i;
201 // If sorted, use this wxSortedArrayStrings to determine
202 // the right insertion point
203 if(m_strings)
204 n = m_strings->Add(items[i]);
243dbf1a 205
ff654490 206 gtk_combo_box_insert_text( combobox, n, wxGTK_CONV( items[i] ) );
243dbf1a 207
ff654490
VZ
208 m_clientData.Insert( NULL, n );
209 AssignNewItemClientData(n, clientData, i, type);
590f50d6 210 }
8228b893 211
b0021947 212 InvalidateBestSize();
243dbf1a 213
a236aa20 214 return n;
243dbf1a
VZ
215}
216
aa61d352 217void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
fd0eed64 218{
a236aa20 219 m_clientData[n] = clientData;
6de97a3b 220}
53010e52 221
aa61d352 222void* wxComboBox::DoGetItemClientData(unsigned int n) const
53010e52 223{
a236aa20 224 return m_clientData[n];
fd0eed64
RR
225}
226
a236aa20 227void wxComboBox::DoClear()
fd0eed64 228{
223d09f6 229 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
805dd538 230
461573cc 231 DisableEvents();
30ed6e5c 232
ff654490
VZ
233 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
234 const unsigned int count = GetCount();
235 for (unsigned int i = 0; i < count; i++)
236 gtk_combo_box_remove_text( combobox, 0 );
8228b893 237
a236aa20 238 m_clientData.Clear();
805dd538 239
a236aa20
VZ
240 if(m_strings)
241 m_strings->Clear();
30ed6e5c 242
461573cc 243 EnableEvents();
b0021947
VS
244
245 InvalidateBestSize();
6de97a3b 246}
53010e52 247
a236aa20 248void wxComboBox::DoDeleteOneItem(unsigned int n)
53010e52 249{
223d09f6 250 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
805dd538 251
ff654490 252 wxCHECK_RET( IsValid(n), wxT("invalid index") );
30ed6e5c 253
ff654490
VZ
254 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
255 gtk_combo_box_remove_text( combobox, n );
8228b893 256
a236aa20
VZ
257 m_clientData.RemoveAt( n );
258 if(m_strings)
259 m_strings->RemoveAt( n );
150e31d2 260
b0021947 261 InvalidateBestSize();
461573cc
RR
262}
263
aa61d352 264void wxComboBox::SetString(unsigned int n, const wxString &text)
461573cc
RR
265{
266 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
267
ff654490
VZ
268 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
269 wxCHECK_RET( IsValid(n), wxT("invalid index") );
8228b893 270
ff654490
VZ
271 GtkTreeModel *model = gtk_combo_box_get_model( combobox );
272 GtkTreeIter iter;
273 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
461573cc 274 {
ff654490
VZ
275 GValue value = { 0, };
276 g_value_init( &value, G_TYPE_STRING );
277 g_value_set_string( &value, wxGTK_CONV( text ) );
278 gtk_list_store_set_value( GTK_LIST_STORE(model), &iter, 0, &value );
279 g_value_unset( &value );
590f50d6 280 }
8228b893 281
b0021947 282 InvalidateBestSize();
6de97a3b 283}
53010e52 284
11e62fe6 285int wxComboBox::FindString( const wxString &item, bool bCase ) const
53010e52 286{
0a164d4c 287 wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid combobox") );
805dd538 288
ff654490
VZ
289 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
290 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
291 GtkTreeIter iter;
292 gtk_tree_model_get_iter_first( model, &iter );
293 if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
294 return -1;
295 int count = 0;
296 do
53010e52 297 {
ff654490
VZ
298 GValue value = { 0, };
299 gtk_tree_model_get_value( model, &iter, 0, &value );
300 wxString str = wxGTK_CONV_BACK( g_value_get_string( &value ) );
301 g_value_unset( &value );
8228b893 302
ff654490
VZ
303 if (item.IsSameAs( str, bCase ) )
304 return count;
8228b893 305
ff654490 306 count++;
fd0eed64 307 }
ff654490 308 while ( gtk_tree_model_iter_next(model, &iter) );
805dd538 309
7cf8cb48 310 return wxNOT_FOUND;
fd0eed64
RR
311}
312
313int wxComboBox::GetSelection() const
40eb3606 314{
ff654490
VZ
315 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
316 return gtk_combo_box_get_active( combobox );
40eb3606
VZ
317}
318
319int wxComboBox::GetCurrentSelection() const
fd0eed64 320{
223d09f6 321 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
805dd538 322
ff654490
VZ
323 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
324 return gtk_combo_box_get_active( combobox );
6de97a3b 325}
53010e52 326
aa61d352 327wxString wxComboBox::GetString(unsigned int n) const
53010e52 328{
0a164d4c 329 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
805dd538 330
7cf8cb48 331 wxString str;
8228b893 332
ff654490
VZ
333 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
334 GtkTreeModel *model = gtk_combo_box_get_model( combobox );
335 GtkTreeIter iter;
336 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
8228b893 337 {
ff654490
VZ
338 GValue value = { 0, };
339 gtk_tree_model_get_value( model, &iter, 0, &value );
340 wxString tmp = wxGTK_CONV_BACK( g_value_get_string( &value ) );
341 g_value_unset( &value );
342 return tmp;
fd0eed64 343 }
805dd538 344
7cf8cb48 345 return str;
6de97a3b 346}
53010e52 347
aa61d352 348unsigned int wxComboBox::GetCount() const
53010e52 349{
223d09f6 350 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid combobox") );
805dd538 351
ff654490
VZ
352 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
353 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
354 GtkTreeIter iter;
355 gtk_tree_model_get_iter_first( model, &iter );
356 if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
357 return 0;
358 unsigned int ret = 1;
359 while (gtk_tree_model_iter_next( model, &iter ))
360 ret++;
361 return ret;
6de97a3b 362}
53010e52 363
debe6624 364void wxComboBox::SetSelection( int n )
53010e52 365{
223d09f6 366 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
805dd538 367
953704c1
RR
368 DisableEvents();
369
ff654490
VZ
370 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
371 gtk_combo_box_set_active( combobox, n );
8228b893 372
953704c1 373 EnableEvents();
6de97a3b 374}
53010e52 375
8a85884a
VZ
376void wxComboBox::OnChar( wxKeyEvent &event )
377{
643c973b 378 switch ( event.GetKeyCode() )
8a85884a 379 {
643c973b
VZ
380 case WXK_RETURN:
381 if ( HasFlag(wxTE_PROCESS_ENTER) )
3352cfff 382 {
643c973b
VZ
383 // GTK automatically selects an item if its in the list
384 wxCommandEvent eventEnter(wxEVT_COMMAND_TEXT_ENTER, GetId());
385 eventEnter.SetString( GetValue() );
386 eventEnter.SetInt( GetSelection() );
387 eventEnter.SetEventObject( this );
388
937013e0 389 if ( HandleWindowEvent(eventEnter) )
643c973b
VZ
390 {
391 // Catch GTK event so that GTK doesn't open the drop
392 // down list upon RETURN.
393 return;
394 }
3352cfff 395 }
5e9b723f
RR
396
397 // On enter key press, we must give a signal to default control,
398 // Otherwise, nothing happens when pressing Enter from inside a
399 // combo box in a dialog.
400 wxWindow *top_frame = wxGetTopLevelParent(this);
401 if( top_frame && GTK_IS_WINDOW(top_frame->m_widget) )
402 {
403 GtkWindow *window = GTK_WINDOW(top_frame->m_widget);
404 if ( window->default_widget )
405 gtk_widget_activate( window->default_widget );
406 }
643c973b 407 break;
8a85884a 408 }
30ed6e5c 409
7cf8cb48 410 event.Skip();
8a85884a
VZ
411}
412
953704c1
RR
413void wxComboBox::DisableEvents()
414{
ff654490
VZ
415 g_signal_handlers_block_by_func(GTK_BIN(m_widget)->child,
416 (gpointer)gtkcombobox_text_changed_callback, this);
8228b893 417
ff654490
VZ
418 g_signal_handlers_block_by_func(m_widget,
419 (gpointer)gtkcombobox_changed_callback, this);
953704c1
RR
420}
421
422void wxComboBox::EnableEvents()
423{
ff654490
VZ
424 g_signal_handlers_unblock_by_func(GTK_BIN(m_widget)->child,
425 (gpointer)gtkcombobox_text_changed_callback, this);
ea2d542c 426
ff654490
VZ
427 g_signal_handlers_unblock_by_func(m_widget,
428 (gpointer)gtkcombobox_changed_callback, this);
868a2826 429}
b4071e91 430
fd0eed64 431GtkWidget* wxComboBox::GetConnectWidget()
97b3455a 432{
ff654490 433 return GTK_WIDGET( GetEntry() );
97b3455a
RR
434}
435
ef5c70f9 436GdkWindow *wxComboBox::GTKGetWindow(wxArrayGdkWindows& windows) const
b4071e91 437{
ff654490 438 wxUnusedVar(windows);
ef5c70f9 439
ff654490 440 return GetEntry()->text_area;
b4071e91 441}
ac57418f 442
f68586e5
VZ
443wxSize wxComboBox::DoGetBestSize() const
444{
db434467 445 wxSize ret( wxControl::DoGetBestSize() );
a6fc8ae3
VZ
446
447 // we know better our horizontal extent: it depends on the longest string
448 // in the combobox
a6fc8ae3
VZ
449 if ( m_widget )
450 {
60d85ccb 451 int width;
aa61d352
VZ
452 unsigned int count = GetCount();
453 for ( unsigned int n = 0; n < count; n++ )
a6fc8ae3 454 {
aa61d352 455 GetTextExtent(GetString(n), &width, NULL, NULL, NULL );
a6fc8ae3
VZ
456 if ( width > ret.x )
457 ret.x = width;
458 }
459 }
460
461 // empty combobox should have some reasonable default size too
462 if ( ret.x < 100 )
463 ret.x = 100;
9f884528
RD
464
465 CacheBestSize(ret);
db434467 466 return ret;
f68586e5
VZ
467}
468
9d522606
RD
469// static
470wxVisualAttributes
471wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
472{
ff654490 473 return GetDefaultAttributesFromGTKWidget(gtk_combo_box_entry_new, true);
9d522606
RD
474}
475
150e31d2
JS
476// ----------------------------------------------------------------------------
477// standard event handling
478// ----------------------------------------------------------------------------
479
480void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
481{
482 Cut();
483}
484
485void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
486{
487 Copy();
488}
489
490void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
491{
492 Paste();
493}
494
495void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
496{
497 Undo();
498}
499
500void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
501{
502 Redo();
503}
504
505void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
506{
507 long from, to;
508 GetSelection(& from, & to);
509 if (from != -1 && to != -1)
510 Remove(from, to);
511}
512
513void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
514{
515 SetSelection(-1, -1);
516}
517
518void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
519{
520 event.Enable( CanCut() );
521}
522
523void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
524{
525 event.Enable( CanCopy() );
526}
527
528void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
529{
530 event.Enable( CanPaste() );
531}
532
533void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
534{
535 event.Enable( CanUndo() );
536}
537
538void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
539{
540 event.Enable( CanRedo() );
541}
542
543void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
544{
545 event.Enable(HasSelection() && IsEditable()) ;
546}
547
548void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
549{
550 event.Enable(GetLastPosition() > 0);
551}
552
0ec1179b 553#endif // wxUSE_COMBOBOX