]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/combobox.cpp
Border corrections, and more compact dialog sizes
[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->GetEventHandler()->ProcessEvent( 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->GetEventHandler()->ProcessEvent( 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 GtkEntry * const entry = GetEntry();
120
121 gtk_entry_set_editable( entry, TRUE );
122
123 Append(n, choices);
124
125 m_parent->DoAddChild( this );
126
127 m_focusWidget = GTK_WIDGET( entry );
128
129 PostCreation(size);
130
131 ConnectWidget( m_widget );
132
133 gtk_entry_set_text( entry, wxGTK_CONV(value) );
134
135 if (style & wxCB_READONLY)
136 gtk_entry_set_editable( entry, FALSE );
137
138 g_signal_connect_after (entry, "changed",
139 G_CALLBACK (gtkcombobox_text_changed_callback), this);
140
141 g_signal_connect_after (m_widget, "changed",
142 G_CALLBACK (gtkcombobox_changed_callback), this);
143
144
145 SetInitialSize(size); // need this too because this is a wxControlWithItems
146
147 return true;
148 }
149
150 GtkEntry *wxComboBox::GetEntry() const
151 {
152 return GTK_ENTRY(GTK_BIN(m_widget)->child);
153 }
154
155 GtkEditable *wxComboBox::GetEditable() const
156 {
157 return GTK_EDITABLE( GTK_BIN(m_widget)->child );
158 }
159
160 wxComboBox::~wxComboBox()
161 {
162 Clear();
163
164 delete m_strings;
165 }
166
167 void wxComboBox::SetFocus()
168 {
169 if ( m_hasFocus )
170 {
171 // don't do anything if we already have focus
172 return;
173 }
174
175 gtk_widget_grab_focus( m_focusWidget );
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 ( GetEventHandler()->ProcessEvent(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 wxSize ret( wxControl::DoGetBestSize() );
429
430 // we know better our horizontal extent: it depends on the longest string
431 // in the combobox
432 if ( m_widget )
433 {
434 int width;
435 unsigned int count = GetCount();
436 for ( unsigned int n = 0; n < count; n++ )
437 {
438 GetTextExtent(GetString(n), &width, NULL, NULL, NULL );
439 if ( width > ret.x )
440 ret.x = width;
441 }
442 }
443
444 // empty combobox should have some reasonable default size too
445 if ( ret.x < 100 )
446 ret.x = 100;
447
448 CacheBestSize(ret);
449 return ret;
450 }
451
452 // static
453 wxVisualAttributes
454 wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
455 {
456 return GetDefaultAttributesFromGTKWidget(gtk_combo_box_entry_new, true);
457 }
458
459 // ----------------------------------------------------------------------------
460 // standard event handling
461 // ----------------------------------------------------------------------------
462
463 void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
464 {
465 Cut();
466 }
467
468 void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
469 {
470 Copy();
471 }
472
473 void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
474 {
475 Paste();
476 }
477
478 void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
479 {
480 Undo();
481 }
482
483 void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
484 {
485 Redo();
486 }
487
488 void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
489 {
490 long from, to;
491 GetSelection(& from, & to);
492 if (from != -1 && to != -1)
493 Remove(from, to);
494 }
495
496 void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
497 {
498 SetSelection(-1, -1);
499 }
500
501 void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
502 {
503 event.Enable( CanCut() );
504 }
505
506 void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
507 {
508 event.Enable( CanCopy() );
509 }
510
511 void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
512 {
513 event.Enable( CanPaste() );
514 }
515
516 void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
517 {
518 event.Enable( CanUndo() );
519 }
520
521 void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
522 {
523 event.Enable( CanRedo() );
524 }
525
526 void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
527 {
528 event.Enable(HasSelection() && IsEditable()) ;
529 }
530
531 void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
532 {
533 event.Enable(GetLastPosition() > 0);
534 }
535
536 #endif // wxUSE_COMBOBOX