1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/combobox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_COMBOBOX
13
14 #include "wx/combobox.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/intl.h"
18 #include "wx/settings.h"
19 #include "wx/textctrl.h" // for wxEVT_TEXT
20 #include "wx/arrstr.h"
21 #endif
22
23 #include "wx/gtk/private.h"
24
25 // ----------------------------------------------------------------------------
26 // GTK callbacks
27 // ----------------------------------------------------------------------------
28
29 extern "C" {
30
31 static void
gtkcombobox_changed_callback(GtkWidget * WXUNUSED (widget),wxComboBox * combo)32 gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
33 {
34 combo->SendSelectionChangedEvent(wxEVT_COMBOBOX);
35 }
36
37 static void
gtkcombobox_popupshown_callback(GObject * WXUNUSED (gobject),GParamSpec * WXUNUSED (param_spec),wxComboBox * combo)38 gtkcombobox_popupshown_callback(GObject *WXUNUSED(gobject),
39 GParamSpec *WXUNUSED(param_spec),
40 wxComboBox *combo)
41 {
42 gboolean isShown;
43 g_object_get( combo->m_widget, "popup-shown", &isShown, NULL );
44 wxCommandEvent event( isShown ? wxEVT_COMBOBOX_DROPDOWN
45 : wxEVT_COMBOBOX_CLOSEUP,
46 combo->GetId() );
47 event.SetEventObject( combo );
48
49 #ifndef __WXGTK3__
50 // Process the close up event once the combobox is already closed with GTK+
51 // 2, otherwise changing the combobox from its handler result in errors.
52 if ( !isShown )
53 {
54 combo->GetEventHandler()->AddPendingEvent( event );
55 }
56 else
57 #endif // GTK+ < 3
58 {
59 combo->HandleWindowEvent( event );
60 }
61 }
62
63 }
64
65 //-----------------------------------------------------------------------------
66 // wxComboBox
67 //-----------------------------------------------------------------------------
68
wxBEGIN_EVENT_TABLE(wxComboBox,wxChoice)69 wxBEGIN_EVENT_TABLE(wxComboBox, wxChoice)
70 EVT_CHAR(wxComboBox::OnChar)
71
72 EVT_MENU(wxID_CUT, wxComboBox::OnCut)
73 EVT_MENU(wxID_COPY, wxComboBox::OnCopy)
74 EVT_MENU(wxID_PASTE, wxComboBox::OnPaste)
75 EVT_MENU(wxID_UNDO, wxComboBox::OnUndo)
76 EVT_MENU(wxID_REDO, wxComboBox::OnRedo)
77 EVT_MENU(wxID_CLEAR, wxComboBox::OnDelete)
78 EVT_MENU(wxID_SELECTALL, wxComboBox::OnSelectAll)
79
80 EVT_UPDATE_UI(wxID_CUT, wxComboBox::OnUpdateCut)
81 EVT_UPDATE_UI(wxID_COPY, wxComboBox::OnUpdateCopy)
82 EVT_UPDATE_UI(wxID_PASTE, wxComboBox::OnUpdatePaste)
83 EVT_UPDATE_UI(wxID_UNDO, wxComboBox::OnUpdateUndo)
84 EVT_UPDATE_UI(wxID_REDO, wxComboBox::OnUpdateRedo)
85 EVT_UPDATE_UI(wxID_CLEAR, wxComboBox::OnUpdateDelete)
86 EVT_UPDATE_UI(wxID_SELECTALL, wxComboBox::OnUpdateSelectAll)
87 wxEND_EVENT_TABLE()
88
89 wxComboBox::~wxComboBox()
90 {
91 if (m_entry)
92 {
93 GTKDisconnect(m_entry);
94 g_object_remove_weak_pointer(G_OBJECT(m_entry), (void**)&m_entry);
95 }
96 }
97
Init()98 void wxComboBox::Init()
99 {
100 m_entry = NULL;
101 }
102
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)103 bool wxComboBox::Create( wxWindow *parent, wxWindowID id,
104 const wxString& value,
105 const wxPoint& pos, const wxSize& size,
106 const wxArrayString& choices,
107 long style, const wxValidator& validator,
108 const wxString& name )
109 {
110 wxCArrayString chs(choices);
111
112 return Create( parent, id, value, pos, size, chs.GetCount(),
113 chs.GetStrings(), style, validator, name );
114 }
115
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],long style,const wxValidator & validator,const wxString & name)116 bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
117 const wxPoint& pos, const wxSize& size,
118 int n, const wxString choices[],
119 long style, const wxValidator& validator,
120 const wxString& name )
121 {
122 if (!PreCreation( parent, pos, size ) ||
123 !CreateBase( parent, id, pos, size, style, validator, name ))
124 {
125 wxFAIL_MSG( wxT("wxComboBox creation failed") );
126 return false;
127 }
128
129 if (HasFlag(wxCB_SORT))
130 m_strings = new wxGtkCollatedArrayString();
131
132 GTKCreateComboBoxWidget();
133
134 if (HasFlag(wxBORDER_NONE))
135 {
136 // Doesn't seem to work
137 // g_object_set (m_widget, "has-frame", FALSE, NULL);
138 }
139
140 GtkEntry * const entry = GetEntry();
141
142 if ( entry )
143 {
144 // Set it up to trigger default item on enter key press
145 gtk_entry_set_activates_default( entry,
146 !HasFlag(wxTE_PROCESS_ENTER) );
147
148 gtk_editable_set_editable(GTK_EDITABLE(entry), true);
149 #ifdef __WXGTK3__
150 gtk_entry_set_width_chars(entry, 0);
151 #endif
152 }
153
154 Append(n, choices);
155
156 m_parent->DoAddChild( this );
157
158 if ( entry )
159 m_focusWidget = GTK_WIDGET( entry );
160
161 PostCreation(size);
162
163 if ( entry )
164 {
165 if (style & wxCB_READONLY)
166 {
167 // this will assert and do nothing if the value is not in our list
168 // of strings which is the desired behaviour (for consistency with
169 // wxMSW and also because it doesn't make sense to have a string
170 // which is not a possible choice in a read-only combobox)
171 SetStringSelection(value);
172 gtk_editable_set_editable(GTK_EDITABLE(entry), false);
173 }
174 else // editable combobox
175 {
176 // any value is accepted, even if it's not in our list
177 gtk_entry_set_text( entry, wxGTK_CONV(value) );
178 }
179
180 GTKConnectChangedSignal();
181 GTKConnectInsertTextSignal(entry);
182 GTKConnectClipboardSignals(GTK_WIDGET(entry));
183 }
184
185 g_signal_connect_after (m_widget, "changed",
186 G_CALLBACK (gtkcombobox_changed_callback), this);
187
188 if ( wx_is_at_least_gtk2(10) )
189 {
190 g_signal_connect (m_widget, "notify::popup-shown",
191 G_CALLBACK (gtkcombobox_popupshown_callback), this);
192 }
193
194 return true;
195 }
196
GTKCreateComboBoxWidget()197 void wxComboBox::GTKCreateComboBoxWidget()
198 {
199 #ifdef __WXGTK3__
200 m_widget = gtk_combo_box_text_new_with_entry();
201 #else
202 m_widget = gtk_combo_box_entry_new_text();
203 #endif
204 g_object_ref(m_widget);
205
206 m_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(m_widget)));
207 g_object_add_weak_pointer(G_OBJECT(m_entry), (void**)&m_entry);
208 }
209
GetEditable() const210 GtkEditable *wxComboBox::GetEditable() const
211 {
212 return GTK_EDITABLE(m_entry);
213 }
214
OnChar(wxKeyEvent & event)215 void wxComboBox::OnChar( wxKeyEvent &event )
216 {
217 switch ( event.GetKeyCode() )
218 {
219 case WXK_RETURN:
220 if ( HasFlag(wxTE_PROCESS_ENTER) && GetEntry() )
221 {
222 // GTK automatically selects an item if its in the list
223 wxCommandEvent eventEnter(wxEVT_TEXT_ENTER, GetId());
224 eventEnter.SetString( GetValue() );
225 eventEnter.SetInt( GetSelection() );
226 eventEnter.SetEventObject( this );
227
228 if ( HandleWindowEvent(eventEnter) )
229 {
230 // Catch GTK event so that GTK doesn't open the drop
231 // down list upon RETURN.
232 return;
233 }
234
235 // We disable built-in default button activation when
236 // wxTE_PROCESS_ENTER is used, but we still should activate it
237 // if the event wasn't handled, so do it from here.
238 if ( ClickDefaultButtonIfPossible() )
239 return;
240 }
241 break;
242 }
243
244 event.Skip();
245 }
246
GTKDisableEvents()247 void wxComboBox::GTKDisableEvents()
248 {
249 EnableTextChangedEvents(false);
250
251 g_signal_handlers_block_by_func(m_widget,
252 (gpointer)gtkcombobox_changed_callback, this);
253 g_signal_handlers_block_by_func(m_widget,
254 (gpointer)gtkcombobox_popupshown_callback, this);
255 }
256
GTKEnableEvents()257 void wxComboBox::GTKEnableEvents()
258 {
259 EnableTextChangedEvents(true);
260
261 g_signal_handlers_unblock_by_func(m_widget,
262 (gpointer)gtkcombobox_changed_callback, this);
263 g_signal_handlers_unblock_by_func(m_widget,
264 (gpointer)gtkcombobox_popupshown_callback, this);
265 }
266
GetConnectWidget()267 GtkWidget* wxComboBox::GetConnectWidget()
268 {
269 return GTK_WIDGET( GetEntry() );
270 }
271
GTKGetWindow(wxArrayGdkWindows &) const272 GdkWindow* wxComboBox::GTKGetWindow(wxArrayGdkWindows& /* windows */) const
273 {
274 #ifdef __WXGTK3__
275 return GTKFindWindow(GTK_WIDGET(GetEntry()));
276 #else
277 return gtk_entry_get_text_window(GetEntry());
278 #endif
279 }
280
281 // static
282 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))283 wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
284 {
285 #ifdef __WXGTK3__
286 return GetDefaultAttributesFromGTKWidget(gtk_combo_box_new_with_entry(), true);
287 #else
288 return GetDefaultAttributesFromGTKWidget(gtk_combo_box_entry_new(), true);
289 #endif
290 }
291
Clear()292 void wxComboBox::Clear()
293 {
294 wxTextEntry::Clear();
295 wxItemContainer::Clear();
296 }
297
SetValue(const wxString & value)298 void wxComboBox::SetValue(const wxString& value)
299 {
300 if ( HasFlag(wxCB_READONLY) )
301 SetStringSelection(value);
302 else
303 wxTextEntry::SetValue(value);
304 }
305
SetString(unsigned int n,const wxString & text)306 void wxComboBox::SetString(unsigned int n, const wxString& text)
307 {
308 wxChoice::SetString(n, text);
309
310 if ( static_cast<int>(n) == GetSelection() )
311 {
312 // We also need to update the currently shown text, for consistency
313 // with wxMSW and also because it makes sense as leaving the old string
314 // in the text but not in the list would be confusing to the user.
315 SetValue(text);
316
317 // And we need to keep the selection unchanged, modifying the item is
318 // not supposed to deselect it.
319 SetSelection(n);
320 }
321 }
322
323 // ----------------------------------------------------------------------------
324 // standard event handling
325 // ----------------------------------------------------------------------------
326
OnCut(wxCommandEvent & WXUNUSED (event))327 void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
328 {
329 Cut();
330 }
331
OnCopy(wxCommandEvent & WXUNUSED (event))332 void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
333 {
334 Copy();
335 }
336
OnPaste(wxCommandEvent & WXUNUSED (event))337 void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
338 {
339 Paste();
340 }
341
OnUndo(wxCommandEvent & WXUNUSED (event))342 void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
343 {
344 Undo();
345 }
346
OnRedo(wxCommandEvent & WXUNUSED (event))347 void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
348 {
349 Redo();
350 }
351
OnDelete(wxCommandEvent & WXUNUSED (event))352 void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
353 {
354 RemoveSelection();
355 }
356
OnSelectAll(wxCommandEvent & WXUNUSED (event))357 void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
358 {
359 SelectAll();
360 }
361
OnUpdateCut(wxUpdateUIEvent & event)362 void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
363 {
364 event.Enable( CanCut() );
365 }
366
OnUpdateCopy(wxUpdateUIEvent & event)367 void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
368 {
369 event.Enable( CanCopy() );
370 }
371
OnUpdatePaste(wxUpdateUIEvent & event)372 void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
373 {
374 event.Enable( CanPaste() );
375 }
376
OnUpdateUndo(wxUpdateUIEvent & event)377 void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
378 {
379 event.Enable( CanUndo() );
380 }
381
OnUpdateRedo(wxUpdateUIEvent & event)382 void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
383 {
384 event.Enable( CanRedo() );
385 }
386
OnUpdateDelete(wxUpdateUIEvent & event)387 void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
388 {
389 event.Enable(HasSelection() && IsEditable()) ;
390 }
391
OnUpdateSelectAll(wxUpdateUIEvent & event)392 void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
393 {
394 event.Enable(!wxTextEntry::IsEmpty());
395 }
396
Popup()397 void wxComboBox::Popup()
398 {
399 gtk_combo_box_popup( GTK_COMBO_BOX(m_widget) );
400 }
401
Dismiss()402 void wxComboBox::Dismiss()
403 {
404 gtk_combo_box_popdown( GTK_COMBO_BOX(m_widget) );
405 }
406
DoGetSizeFromTextSize(int xlen,int ylen) const407 wxSize wxComboBox::DoGetSizeFromTextSize(int xlen, int ylen) const
408 {
409 wxSize tsize( wxChoice::DoGetSizeFromTextSize(xlen, ylen) );
410
411 GtkEntry* entry = GetEntry();
412 if (entry)
413 {
414 // Add the margins we have previously set, but only the horizontal border
415 // as vertical one has been taken account in the previous call.
416 // Also get other GTK+ margins.
417 tsize.IncBy(GTKGetEntryMargins(entry).x, 0);
418 }
419
420 return tsize;
421 }
422
423 #endif // wxUSE_COMBOBOX
424