Refactor and simplify wxChoice::DoGetBestSize().
[wxWidgets.git] / src / gtk / choice.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/choice.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #include "wx/wxprec.h"
11
12 #if wxUSE_CHOICE || wxUSE_COMBOBOX
13
14 #include "wx/choice.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/arrstr.h"
18 #endif
19
20 #include <gtk/gtk.h>
21 #include "wx/gtk/private.h"
22 #include "wx/gtk/private/gtk2-compat.h"
23
24 // ----------------------------------------------------------------------------
25 // GTK callbacks
26 // ----------------------------------------------------------------------------
27
28 extern "C" {
29
30 static void
31 gtk_choice_changed_callback( GtkWidget *WXUNUSED(widget), wxChoice *choice )
32 {
33 choice->SendSelectionChangedEvent(wxEVT_COMMAND_CHOICE_SELECTED);
34 }
35
36 }
37
38 //-----------------------------------------------------------------------------
39 // wxChoice
40 //-----------------------------------------------------------------------------
41
42 void wxChoice::Init()
43 {
44 m_strings = NULL;
45 m_stringCellIndex = 0;
46 }
47
48 bool wxChoice::Create( wxWindow *parent, wxWindowID id,
49 const wxPoint &pos, const wxSize &size,
50 const wxArrayString& choices,
51 long style, const wxValidator& validator,
52 const wxString &name )
53 {
54 wxCArrayString chs(choices);
55
56 return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
57 style, validator, name );
58 }
59
60 bool wxChoice::Create( wxWindow *parent, wxWindowID id,
61 const wxPoint &pos, const wxSize &size,
62 int n, const wxString choices[],
63 long style, const wxValidator& validator,
64 const wxString &name )
65 {
66 if (!PreCreation( parent, pos, size ) ||
67 !CreateBase( parent, id, pos, size, style, validator, name ))
68 {
69 wxFAIL_MSG( wxT("wxChoice creation failed") );
70 return false;
71 }
72
73 if ( IsSorted() )
74 {
75 // if our m_strings != NULL, Append() will check for it and insert
76 // items in the correct order
77 m_strings = new wxGtkCollatedArrayString;
78 }
79
80 #ifdef __WXGTK3__
81 m_widget = gtk_combo_box_text_new();
82 #else
83 m_widget = gtk_combo_box_new_text();
84 #endif
85 g_object_ref(m_widget);
86
87 Append(n, choices);
88
89 m_parent->DoAddChild( this );
90
91 PostCreation(size);
92
93 g_signal_connect_after (m_widget, "changed",
94 G_CALLBACK (gtk_choice_changed_callback), this);
95
96 return true;
97 }
98
99 wxChoice::~wxChoice()
100 {
101 delete m_strings;
102 }
103
104 void wxChoice::SendSelectionChangedEvent(wxEventType evt_type)
105 {
106 if (GetSelection() == -1)
107 return;
108
109 wxCommandEvent event( evt_type, GetId() );
110
111 int n = GetSelection();
112 event.SetInt( n );
113 event.SetString( GetStringSelection() );
114 event.SetEventObject( this );
115 InitCommandEventWithItems( event, n );
116
117 HandleWindowEvent( event );
118 }
119
120 void wxChoice::GTKInsertComboBoxTextItem( unsigned int n, const wxString& text )
121 {
122 #ifdef __WXGTK3__
123 gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(m_widget), n, wxGTK_CONV(text));
124 #else
125 gtk_combo_box_insert_text( GTK_COMBO_BOX( m_widget ), n, wxGTK_CONV( text ) );
126 #endif
127 }
128
129 int wxChoice::DoInsertItems(const wxArrayStringsAdapter & items,
130 unsigned int pos,
131 void **clientData, wxClientDataType type)
132 {
133 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid control") );
134
135 wxASSERT_MSG( !IsSorted() || (pos == GetCount()),
136 wxT("In a sorted choice data could only be appended"));
137
138 const int count = items.GetCount();
139
140 int n = wxNOT_FOUND;
141
142 for ( int i = 0; i < count; ++i )
143 {
144 n = pos + i;
145 // If sorted, use this wxSortedArrayStrings to determine
146 // the right insertion point
147 if (m_strings)
148 n = m_strings->Add(items[i]);
149
150 GTKInsertComboBoxTextItem( n, items[i] );
151
152 m_clientData.Insert( NULL, n );
153 AssignNewItemClientData(n, clientData, i, type);
154 }
155
156 InvalidateBestSize();
157
158 return n;
159 }
160
161 void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
162 {
163 m_clientData[n] = clientData;
164 }
165
166 void* wxChoice::DoGetItemClientData(unsigned int n) const
167 {
168 return m_clientData[n];
169 }
170
171 void wxChoice::DoClear()
172 {
173 wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
174
175 GTKDisableEvents();
176
177 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
178 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
179 gtk_list_store_clear(GTK_LIST_STORE(model));
180
181 m_clientData.Clear();
182
183 if (m_strings)
184 m_strings->Clear();
185
186 GTKEnableEvents();
187
188 InvalidateBestSize();
189 }
190
191 void wxChoice::DoDeleteOneItem(unsigned int n)
192 {
193 wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
194 wxCHECK_RET( IsValid(n), wxT("invalid index in wxChoice::Delete") );
195
196 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
197 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
198 GtkListStore* store = GTK_LIST_STORE(model);
199 GtkTreeIter iter;
200 gtk_tree_model_iter_nth_child( model, &iter,
201 NULL, (gint) n );
202 gtk_list_store_remove( store, &iter );
203
204 m_clientData.RemoveAt( n );
205 if ( m_strings )
206 m_strings->RemoveAt( n );
207
208 InvalidateBestSize();
209 }
210
211 int wxChoice::FindString( const wxString &item, bool bCase ) const
212 {
213 wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid control") );
214
215 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
216 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
217 GtkTreeIter iter;
218 gtk_tree_model_get_iter_first( model, &iter );
219 if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
220 return -1;
221 int count = 0;
222 do
223 {
224 GValue value = { 0, };
225 gtk_tree_model_get_value( model, &iter, m_stringCellIndex, &value );
226 wxString str = wxGTK_CONV_BACK( g_value_get_string( &value ) );
227 g_value_unset( &value );
228
229 if (item.IsSameAs( str, bCase ) )
230 return count;
231
232 count++;
233 }
234 while ( gtk_tree_model_iter_next(model, &iter) );
235
236 return wxNOT_FOUND;
237 }
238
239 int wxChoice::GetSelection() const
240 {
241 return gtk_combo_box_get_active( GTK_COMBO_BOX( m_widget ) );
242 }
243
244 void wxChoice::SetString(unsigned int n, const wxString &text)
245 {
246 wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
247
248 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
249 wxCHECK_RET( IsValid(n), wxT("invalid index") );
250
251 GtkTreeModel *model = gtk_combo_box_get_model( combobox );
252 GtkTreeIter iter;
253 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
254 {
255 GValue value = { 0, };
256 g_value_init( &value, G_TYPE_STRING );
257 g_value_set_string( &value, wxGTK_CONV( text ) );
258 gtk_list_store_set_value( GTK_LIST_STORE(model), &iter, m_stringCellIndex, &value );
259 g_value_unset( &value );
260 }
261
262 InvalidateBestSize();
263 }
264
265 wxString wxChoice::GetString(unsigned int n) const
266 {
267 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid control") );
268
269 wxString str;
270
271 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
272 GtkTreeModel *model = gtk_combo_box_get_model( combobox );
273 GtkTreeIter iter;
274 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
275 {
276 GValue value = { 0, };
277 gtk_tree_model_get_value( model, &iter, m_stringCellIndex, &value );
278 wxString tmp = wxGTK_CONV_BACK( g_value_get_string( &value ) );
279 g_value_unset( &value );
280 return tmp;
281 }
282
283 return str;
284 }
285
286 unsigned int wxChoice::GetCount() const
287 {
288 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid control") );
289
290 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
291 GtkTreeModel* model = gtk_combo_box_get_model( combobox );
292 GtkTreeIter iter;
293 gtk_tree_model_get_iter_first( model, &iter );
294 if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
295 return 0;
296 unsigned int ret = 1;
297 while (gtk_tree_model_iter_next( model, &iter ))
298 ret++;
299 return ret;
300 }
301
302 void wxChoice::SetSelection( int n )
303 {
304 wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
305
306 GTKDisableEvents();
307
308 GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
309 gtk_combo_box_set_active( combobox, n );
310
311 GTKEnableEvents();
312 }
313
314 void wxChoice::SetColumns(int n)
315 {
316 gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(m_widget), n);
317 }
318
319 int wxChoice::GetColumns() const
320 {
321 // gtk_combo_box_get_wrap_width() was added in gtk 2.6
322 gint intval;
323 g_object_get(G_OBJECT(m_widget), "wrap-width", &intval, NULL);
324 return intval;
325 }
326
327
328 void wxChoice::GTKDisableEvents()
329 {
330 g_signal_handlers_block_by_func(m_widget,
331 (gpointer) gtk_choice_changed_callback, this);
332 }
333
334 void wxChoice::GTKEnableEvents()
335 {
336 g_signal_handlers_unblock_by_func(m_widget,
337 (gpointer) gtk_choice_changed_callback, this);
338 }
339
340
341 GdkWindow *wxChoice::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
342 {
343 return gtk_widget_get_window(m_widget);
344 }
345
346 wxSize wxChoice::DoGetBestSize() const
347 {
348 // Get the height of the control from GTK+ itself, but use our own version
349 // to compute the width large enough to show all our strings as GTK+
350 // doesn't seem to take the control contents into account.
351 return wxSize(wxChoiceBase::DoGetBestSize().x + 40,
352 wxControl::DoGetBestSize().y);
353 }
354
355 void wxChoice::DoApplyWidgetStyle(GtkRcStyle *style)
356 {
357 GTKApplyStyle(m_widget, style);
358 GTKApplyStyle(gtk_bin_get_child(GTK_BIN(m_widget)), style);
359 }
360
361
362 // static
363 wxVisualAttributes
364 wxChoice::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
365 {
366 return GetDefaultAttributesFromGTKWidget(gtk_combo_box_new);
367 }
368
369
370 #endif // wxUSE_CHOICE || wxUSE_COMBOBOX