]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/combobox.cpp
Option for variable line heights (MSW and GTK+ sofar)
[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
bfc262ae
RR
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
2f515791
RR
124 if (HasFlag(wxBORDER_NONE))
125 {
126 // Doesn't seem to work
127 // g_object_set (m_widget, "has-frame", FALSE, NULL);
128 }
129
ff654490 130 GtkEntry * const entry = GetEntry();
3ca6a5f0 131
ff654490 132 gtk_entry_set_editable( entry, TRUE );
805dd538 133
a236aa20 134 Append(n, choices);
590f50d6 135
f03fc89f 136 m_parent->DoAddChild( this );
30ed6e5c 137
590f50d6 138 m_focusWidget = GTK_WIDGET( entry );
805dd538 139
abdeb9e7 140 PostCreation(size);
53010e52 141
ff654490 142 ConnectWidget( m_widget );
805dd538 143
ff654490 144 gtk_entry_set_text( entry, wxGTK_CONV(value) );
8228b893 145
ff654490
VZ
146 if (style & wxCB_READONLY)
147 gtk_entry_set_editable( entry, FALSE );
8228b893 148
ff654490
VZ
149 g_signal_connect_after (entry, "changed",
150 G_CALLBACK (gtkcombobox_text_changed_callback), this);
8228b893 151
ff654490
VZ
152 g_signal_connect_after (m_widget, "changed",
153 G_CALLBACK (gtkcombobox_changed_callback), this);
f4322df6 154
805dd538 155
170acdc9 156 SetInitialSize(size); // need this too because this is a wxControlWithItems
805dd538 157
7d8268a1 158 return true;
fd0eed64
RR
159}
160
ff654490
VZ
161GtkEntry *wxComboBox::GetEntry() const
162{
163 return GTK_ENTRY(GTK_BIN(m_widget)->child);
164}
165
0ec1179b
VZ
166GtkEditable *wxComboBox::GetEditable() const
167{
ff654490 168 return GTK_EDITABLE( GTK_BIN(m_widget)->child );
0ec1179b
VZ
169}
170
fd0eed64
RR
171wxComboBox::~wxComboBox()
172{
a236aa20 173 Clear();
7d6d2cd4 174
a236aa20 175 delete m_strings;
6de97a3b 176}
53010e52 177
a236aa20
VZ
178int wxComboBox::DoInsertItems(const wxArrayStringsAdapter & items,
179 unsigned int pos,
180 void **clientData, wxClientDataType type)
53010e52 181{
2a68b7a0 182 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
805dd538 183
a236aa20
VZ
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
ff654490
VZ
191 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
192 for( int i = 0; i < count; ++i )
590f50d6 193 {
ff654490
VZ
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]);
243dbf1a 199
ff654490 200 gtk_combo_box_insert_text( combobox, n, wxGTK_CONV( items[i] ) );
243dbf1a 201
ff654490
VZ
202 m_clientData.Insert( NULL, n );
203 AssignNewItemClientData(n, clientData, i, type);
590f50d6 204 }
8228b893 205
b0021947 206 InvalidateBestSize();
243dbf1a 207
a236aa20 208 return n;
243dbf1a
VZ
209}
210
aa61d352 211void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
fd0eed64 212{
a236aa20 213 m_clientData[n] = clientData;
6de97a3b 214}
53010e52 215
aa61d352 216void* wxComboBox::DoGetItemClientData(unsigned int n) const
53010e52 217{
a236aa20 218 return m_clientData[n];
fd0eed64
RR
219}
220
a236aa20 221void wxComboBox::DoClear()
fd0eed64 222{
223d09f6 223 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
805dd538 224
461573cc 225 DisableEvents();
30ed6e5c 226
ff654490
VZ
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 );
8228b893 231
a236aa20 232 m_clientData.Clear();
805dd538 233
a236aa20
VZ
234 if(m_strings)
235 m_strings->Clear();
30ed6e5c 236
461573cc 237 EnableEvents();
b0021947
VS
238
239 InvalidateBestSize();
6de97a3b 240}
53010e52 241
a236aa20 242void wxComboBox::DoDeleteOneItem(unsigned int n)
53010e52 243{
223d09f6 244 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
805dd538 245
ff654490 246 wxCHECK_RET( IsValid(n), wxT("invalid index") );
30ed6e5c 247
ff654490
VZ
248 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
249 gtk_combo_box_remove_text( combobox, n );
8228b893 250
a236aa20
VZ
251 m_clientData.RemoveAt( n );
252 if(m_strings)
253 m_strings->RemoveAt( n );
150e31d2 254
b0021947 255 InvalidateBestSize();
461573cc
RR
256}
257
aa61d352 258void wxComboBox::SetString(unsigned int n, const wxString &text)
461573cc
RR
259{
260 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
261
ff654490
VZ
262 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
263 wxCHECK_RET( IsValid(n), wxT("invalid index") );
8228b893 264
ff654490
VZ
265 GtkTreeModel *model = gtk_combo_box_get_model( combobox );
266 GtkTreeIter iter;
267 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
461573cc 268 {
ff654490
VZ
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 );
590f50d6 274 }
8228b893 275
b0021947 276 InvalidateBestSize();
6de97a3b 277}
53010e52 278
11e62fe6 279int wxComboBox::FindString( const wxString &item, bool bCase ) const
53010e52 280{
0a164d4c 281 wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid combobox") );
805dd538 282
ff654490
VZ
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
53010e52 291 {
ff654490
VZ
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 );
8228b893 296
ff654490
VZ
297 if (item.IsSameAs( str, bCase ) )
298 return count;
8228b893 299
ff654490 300 count++;
fd0eed64 301 }
ff654490 302 while ( gtk_tree_model_iter_next(model, &iter) );
805dd538 303
7cf8cb48 304 return wxNOT_FOUND;
fd0eed64
RR
305}
306
307int wxComboBox::GetSelection() const
40eb3606 308{
ff654490
VZ
309 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
310 return gtk_combo_box_get_active( combobox );
40eb3606
VZ
311}
312
313int wxComboBox::GetCurrentSelection() const
fd0eed64 314{
223d09f6 315 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
805dd538 316
ff654490
VZ
317 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
318 return gtk_combo_box_get_active( combobox );
6de97a3b 319}
53010e52 320
aa61d352 321wxString wxComboBox::GetString(unsigned int n) const
53010e52 322{
0a164d4c 323 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
805dd538 324
7cf8cb48 325 wxString str;
8228b893 326
ff654490
VZ
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))
8228b893 331 {
ff654490
VZ
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;
fd0eed64 337 }
805dd538 338
7cf8cb48 339 return str;
6de97a3b 340}
53010e52 341
aa61d352 342unsigned int wxComboBox::GetCount() const
53010e52 343{
223d09f6 344 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid combobox") );
805dd538 345
ff654490
VZ
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;
6de97a3b 356}
53010e52 357
debe6624 358void wxComboBox::SetSelection( int n )
53010e52 359{
223d09f6 360 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
805dd538 361
953704c1
RR
362 DisableEvents();
363
ff654490
VZ
364 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
365 gtk_combo_box_set_active( combobox, n );
8228b893 366
953704c1 367 EnableEvents();
6de97a3b 368}
53010e52 369
8a85884a
VZ
370void wxComboBox::OnChar( wxKeyEvent &event )
371{
643c973b 372 switch ( event.GetKeyCode() )
8a85884a 373 {
643c973b
VZ
374 case WXK_RETURN:
375 if ( HasFlag(wxTE_PROCESS_ENTER) )
3352cfff 376 {
643c973b
VZ
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
937013e0 383 if ( HandleWindowEvent(eventEnter) )
643c973b
VZ
384 {
385 // Catch GTK event so that GTK doesn't open the drop
386 // down list upon RETURN.
387 return;
388 }
3352cfff 389 }
643c973b 390 break;
8a85884a 391 }
30ed6e5c 392
7cf8cb48 393 event.Skip();
8a85884a
VZ
394}
395
953704c1
RR
396void wxComboBox::DisableEvents()
397{
ff654490
VZ
398 g_signal_handlers_block_by_func(GTK_BIN(m_widget)->child,
399 (gpointer)gtkcombobox_text_changed_callback, this);
8228b893 400
ff654490
VZ
401 g_signal_handlers_block_by_func(m_widget,
402 (gpointer)gtkcombobox_changed_callback, this);
953704c1
RR
403}
404
405void wxComboBox::EnableEvents()
406{
ff654490
VZ
407 g_signal_handlers_unblock_by_func(GTK_BIN(m_widget)->child,
408 (gpointer)gtkcombobox_text_changed_callback, this);
ea2d542c 409
ff654490
VZ
410 g_signal_handlers_unblock_by_func(m_widget,
411 (gpointer)gtkcombobox_changed_callback, this);
868a2826 412}
b4071e91 413
fd0eed64 414GtkWidget* wxComboBox::GetConnectWidget()
97b3455a 415{
ff654490 416 return GTK_WIDGET( GetEntry() );
97b3455a
RR
417}
418
ef5c70f9 419GdkWindow *wxComboBox::GTKGetWindow(wxArrayGdkWindows& windows) const
b4071e91 420{
ff654490 421 wxUnusedVar(windows);
ef5c70f9 422
ff654490 423 return GetEntry()->text_area;
b4071e91 424}
ac57418f 425
f68586e5
VZ
426wxSize wxComboBox::DoGetBestSize() const
427{
d1b190d4 428 // strangely, this returns a width of 188 pixels from GTK+ (?)
db434467 429 wxSize ret( wxControl::DoGetBestSize() );
a6fc8ae3
VZ
430
431 // we know better our horizontal extent: it depends on the longest string
432 // in the combobox
a6fc8ae3
VZ
433 if ( m_widget )
434 {
d1b190d4 435 ret.x = 60; // start with something "sensible"
60d85ccb 436 int width;
aa61d352
VZ
437 unsigned int count = GetCount();
438 for ( unsigned int n = 0; n < count; n++ )
a6fc8ae3 439 {
aa61d352 440 GetTextExtent(GetString(n), &width, NULL, NULL, NULL );
d1b190d4
RR
441 if ( width + 40 > ret.x ) // 40 for drop down arrow and space around text
442 ret.x = width + 40;
a6fc8ae3
VZ
443 }
444 }
445
446 // empty combobox should have some reasonable default size too
d1b190d4
RR
447 if ((GetCount() == 0) && (ret.x < 80))
448 ret.x = 80;
9f884528
RD
449
450 CacheBestSize(ret);
db434467 451 return ret;
f68586e5
VZ
452}
453
9d522606
RD
454// static
455wxVisualAttributes
456wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
457{
ff654490 458 return GetDefaultAttributesFromGTKWidget(gtk_combo_box_entry_new, true);
9d522606
RD
459}
460
150e31d2
JS
461// ----------------------------------------------------------------------------
462// standard event handling
463// ----------------------------------------------------------------------------
464
465void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
466{
467 Cut();
468}
469
470void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
471{
472 Copy();
473}
474
475void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
476{
477 Paste();
478}
479
480void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
481{
482 Undo();
483}
484
485void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
486{
487 Redo();
488}
489
490void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
491{
5a25f858 492 RemoveSelection();
150e31d2
JS
493}
494
495void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
496{
e976429d 497 SelectAll();
150e31d2
JS
498}
499
500void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
501{
502 event.Enable( CanCut() );
503}
504
505void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
506{
507 event.Enable( CanCopy() );
508}
509
510void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
511{
512 event.Enable( CanPaste() );
513}
514
515void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
516{
517 event.Enable( CanUndo() );
518}
519
520void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
521{
522 event.Enable( CanRedo() );
523}
524
525void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
526{
527 event.Enable(HasSelection() && IsEditable()) ;
528}
529
530void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
531{
e976429d 532 event.Enable(!wxTextEntry::IsEmpty());
150e31d2
JS
533}
534
0ec1179b 535#endif // wxUSE_COMBOBOX