[ 1907169 ] GTK - trunk - dialog default button, part II for comboctrl
[wxWidgets.git] / src / gtk / combobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/combobox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
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/gtk/private.h"
25
26 // ----------------------------------------------------------------------------
27 // GTK callbacks
28 // ----------------------------------------------------------------------------
29
30 extern "C" {
31 static void
32 gtkcombobox_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
33 {
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 );
39 combo->HandleWindowEvent( event );
40 }
41
42 static void
43 gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
44 {
45 if (!combo->m_hasVMT) return;
46
47 if (combo->GetSelection() == -1)
48 return;
49
50 wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
51 event.SetInt( combo->GetSelection() );
52 event.SetString( combo->GetStringSelection() );
53 event.SetEventObject( combo );
54 combo->HandleWindowEvent( event );
55 }
56 }
57
58 //-----------------------------------------------------------------------------
59 // wxComboBox
60 //-----------------------------------------------------------------------------
61
62 IMPLEMENT_DYNAMIC_CLASS(wxComboBox,wxControl)
63
64 BEGIN_EVENT_TABLE(wxComboBox, wxControl)
65 EVT_CHAR(wxComboBox::OnChar)
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)
82 END_EVENT_TABLE()
83
84 bool 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
97 bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
98 const wxPoint& pos, const wxSize& size,
99 int n, const wxString choices[],
100 long style, const wxValidator& validator,
101 const wxString& name )
102 {
103 m_strings = NULL;
104 m_ignoreNextUpdate = false;
105 m_prevSelection = 0;
106
107 if (!PreCreation( parent, pos, size ) ||
108 !CreateBase( parent, id, pos, size, style, validator, name ))
109 {
110 wxFAIL_MSG( wxT("wxComboBox creation failed") );
111 return false;
112 }
113
114 if (HasFlag(wxCB_SORT))
115 m_strings = new wxSortedArrayString();
116
117 m_widget = gtk_combo_box_entry_new_text();
118
119 // Set it up to trigger default item on enter key press
120 GtkWidget *widget = gtk_bin_get_child(GTK_BIN(m_widget));
121 gtk_entry_set_activates_default(GTK_ENTRY(widget),
122 !HasFlag(wxTE_PROCESS_ENTER));
123
124 if (HasFlag(wxBORDER_NONE))
125 {
126 // Doesn't seem to work
127 // g_object_set (m_widget, "has-frame", FALSE, NULL);
128 }
129
130 GtkEntry * const entry = GetEntry();
131
132 gtk_entry_set_editable( entry, TRUE );
133
134 Append(n, choices);
135
136 m_parent->DoAddChild( this );
137
138 m_focusWidget = GTK_WIDGET( entry );
139
140 PostCreation(size);
141
142 ConnectWidget( m_widget );
143
144 gtk_entry_set_text( entry, wxGTK_CONV(value) );
145
146 if (style & wxCB_READONLY)
147 gtk_entry_set_editable( entry, FALSE );
148
149 g_signal_connect_after (entry, "changed",
150 G_CALLBACK (gtkcombobox_text_changed_callback), this);
151
152 g_signal_connect_after (m_widget, "changed",
153 G_CALLBACK (gtkcombobox_changed_callback), this);
154
155
156 SetInitialSize(size); // need this too because this is a wxControlWithItems
157
158 return true;
159 }
160
161 GtkEntry *wxComboBox::GetEntry() const
162 {
163 return GTK_ENTRY(GTK_BIN(m_widget)->child);
164 }
165
166 GtkEditable *wxComboBox::GetEditable() const
167 {
168 return GTK_EDITABLE( GTK_BIN(m_widget)->child );
169 }
170
171 wxComboBox::~wxComboBox()
172 {
173 Clear();
174
175 delete m_strings;
176 }
177
178 int wxComboBox::DoInsertItems(const wxArrayStringsAdapter & items,
179 unsigned int pos,
180 void **clientData, wxClientDataType type)
181 {
182 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
183
184 wxASSERT_MSG( !IsSorted() || (pos == GetCount()),
185 _T("In a sorted combobox data could only be appended"));
186
187 const int count = items.GetCount();
188
189 int n = wxNOT_FOUND;
190
191 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
192 for( int i = 0; i < count; ++i )
193 {
194 n = pos + i;
195 // If sorted, use this wxSortedArrayStrings to determine
196 // the right insertion point
197 if(m_strings)
198 n = m_strings->Add(items[i]);
199
200 gtk_combo_box_insert_text( combobox, n, wxGTK_CONV( items[i] ) );
201
202 m_clientData.Insert( NULL, n );
203 AssignNewItemClientData(n, clientData, i, type);
204 }
205
206 InvalidateBestSize();
207
208 return n;
209 }
210
211 void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
212 {
213 m_clientData[n] = clientData;
214 }
215
216 void* wxComboBox::DoGetItemClientData(unsigned int n) const
217 {
218 return m_clientData[n];
219 }
220
221 void wxComboBox::DoClear()
222 {
223 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
224
225 DisableEvents();
226
227 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
228 const unsigned int count = GetCount();
229 for (unsigned int i = 0; i < count; i++)
230 gtk_combo_box_remove_text( combobox, 0 );
231
232 m_clientData.Clear();
233
234 if(m_strings)
235 m_strings->Clear();
236
237 EnableEvents();
238
239 InvalidateBestSize();
240 }
241
242 void wxComboBox::DoDeleteOneItem(unsigned int n)
243 {
244 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
245
246 wxCHECK_RET( IsValid(n), wxT("invalid index") );
247
248 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
249 gtk_combo_box_remove_text( combobox, n );
250
251 m_clientData.RemoveAt( n );
252 if(m_strings)
253 m_strings->RemoveAt( n );
254
255 InvalidateBestSize();
256 }
257
258 void wxComboBox::SetString(unsigned int n, const wxString &text)
259 {
260 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
261
262 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
263 wxCHECK_RET( IsValid(n), wxT("invalid index") );
264
265 GtkTreeModel *model = gtk_combo_box_get_model( combobox );
266 GtkTreeIter iter;
267 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
268 {
269 GValue value = { 0, };
270 g_value_init( &value, G_TYPE_STRING );
271 g_value_set_string( &value, wxGTK_CONV( text ) );
272 gtk_list_store_set_value( GTK_LIST_STORE(model), &iter, 0, &value );
273 g_value_unset( &value );
274 }
275
276 InvalidateBestSize();
277 }
278
279 int wxComboBox::FindString( const wxString &item, bool bCase ) const
280 {
281 wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid combobox") );
282
283 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
284 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
285 GtkTreeIter iter;
286 gtk_tree_model_get_iter_first( model, &iter );
287 if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
288 return -1;
289 int count = 0;
290 do
291 {
292 GValue value = { 0, };
293 gtk_tree_model_get_value( model, &iter, 0, &value );
294 wxString str = wxGTK_CONV_BACK( g_value_get_string( &value ) );
295 g_value_unset( &value );
296
297 if (item.IsSameAs( str, bCase ) )
298 return count;
299
300 count++;
301 }
302 while ( gtk_tree_model_iter_next(model, &iter) );
303
304 return wxNOT_FOUND;
305 }
306
307 int wxComboBox::GetSelection() const
308 {
309 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
310 return gtk_combo_box_get_active( combobox );
311 }
312
313 int wxComboBox::GetCurrentSelection() const
314 {
315 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
316
317 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
318 return gtk_combo_box_get_active( combobox );
319 }
320
321 wxString wxComboBox::GetString(unsigned int n) const
322 {
323 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
324
325 wxString str;
326
327 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
328 GtkTreeModel *model = gtk_combo_box_get_model( combobox );
329 GtkTreeIter iter;
330 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
331 {
332 GValue value = { 0, };
333 gtk_tree_model_get_value( model, &iter, 0, &value );
334 wxString tmp = wxGTK_CONV_BACK( g_value_get_string( &value ) );
335 g_value_unset( &value );
336 return tmp;
337 }
338
339 return str;
340 }
341
342 unsigned int wxComboBox::GetCount() const
343 {
344 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid combobox") );
345
346 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
347 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
348 GtkTreeIter iter;
349 gtk_tree_model_get_iter_first( model, &iter );
350 if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
351 return 0;
352 unsigned int ret = 1;
353 while (gtk_tree_model_iter_next( model, &iter ))
354 ret++;
355 return ret;
356 }
357
358 void wxComboBox::SetSelection( int n )
359 {
360 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
361
362 DisableEvents();
363
364 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
365 gtk_combo_box_set_active( combobox, n );
366
367 EnableEvents();
368 }
369
370 void wxComboBox::OnChar( wxKeyEvent &event )
371 {
372 switch ( event.GetKeyCode() )
373 {
374 case WXK_RETURN:
375 if ( HasFlag(wxTE_PROCESS_ENTER) )
376 {
377 // GTK automatically selects an item if its in the list
378 wxCommandEvent eventEnter(wxEVT_COMMAND_TEXT_ENTER, GetId());
379 eventEnter.SetString( GetValue() );
380 eventEnter.SetInt( GetSelection() );
381 eventEnter.SetEventObject( this );
382
383 if ( HandleWindowEvent(eventEnter) )
384 {
385 // Catch GTK event so that GTK doesn't open the drop
386 // down list upon RETURN.
387 return;
388 }
389 }
390 break;
391 }
392
393 event.Skip();
394 }
395
396 void wxComboBox::DisableEvents()
397 {
398 g_signal_handlers_block_by_func(GTK_BIN(m_widget)->child,
399 (gpointer)gtkcombobox_text_changed_callback, this);
400
401 g_signal_handlers_block_by_func(m_widget,
402 (gpointer)gtkcombobox_changed_callback, this);
403 }
404
405 void wxComboBox::EnableEvents()
406 {
407 g_signal_handlers_unblock_by_func(GTK_BIN(m_widget)->child,
408 (gpointer)gtkcombobox_text_changed_callback, this);
409
410 g_signal_handlers_unblock_by_func(m_widget,
411 (gpointer)gtkcombobox_changed_callback, this);
412 }
413
414 GtkWidget* wxComboBox::GetConnectWidget()
415 {
416 return GTK_WIDGET( GetEntry() );
417 }
418
419 GdkWindow *wxComboBox::GTKGetWindow(wxArrayGdkWindows& windows) const
420 {
421 wxUnusedVar(windows);
422
423 return GetEntry()->text_area;
424 }
425
426 wxSize wxComboBox::DoGetBestSize() const
427 {
428 // strangely, this returns a width of 188 pixels from GTK+ (?)
429 wxSize ret( wxControl::DoGetBestSize() );
430
431 // we know better our horizontal extent: it depends on the longest string
432 // in the combobox
433 if ( m_widget )
434 {
435 ret.x = 60; // start with something "sensible"
436 int width;
437 unsigned int count = GetCount();
438 for ( unsigned int n = 0; n < count; n++ )
439 {
440 GetTextExtent(GetString(n), &width, NULL, NULL, NULL );
441 if ( width + 40 > ret.x ) // 40 for drop down arrow and space around text
442 ret.x = width + 40;
443 }
444 }
445
446 // empty combobox should have some reasonable default size too
447 if ((GetCount() == 0) && (ret.x < 80))
448 ret.x = 80;
449
450 CacheBestSize(ret);
451 return ret;
452 }
453
454 // static
455 wxVisualAttributes
456 wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
457 {
458 return GetDefaultAttributesFromGTKWidget(gtk_combo_box_entry_new, true);
459 }
460
461 // ----------------------------------------------------------------------------
462 // standard event handling
463 // ----------------------------------------------------------------------------
464
465 void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
466 {
467 Cut();
468 }
469
470 void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
471 {
472 Copy();
473 }
474
475 void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
476 {
477 Paste();
478 }
479
480 void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
481 {
482 Undo();
483 }
484
485 void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
486 {
487 Redo();
488 }
489
490 void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
491 {
492 RemoveSelection();
493 }
494
495 void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
496 {
497 SelectAll();
498 }
499
500 void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
501 {
502 event.Enable( CanCut() );
503 }
504
505 void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
506 {
507 event.Enable( CanCopy() );
508 }
509
510 void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
511 {
512 event.Enable( CanPaste() );
513 }
514
515 void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
516 {
517 event.Enable( CanUndo() );
518 }
519
520 void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
521 {
522 event.Enable( CanRedo() );
523 }
524
525 void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
526 {
527 event.Enable(HasSelection() && IsEditable()) ;
528 }
529
530 void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
531 {
532 event.Enable(!wxTextEntry::IsEmpty());
533 }
534
535 #endif // wxUSE_COMBOBOX