1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/control.cpp
3 // Purpose: wxControl implementation for wxGTK
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling, Julian Smart and Vadim Zeitlin
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_CONTROLS
13
14 #include "wx/control.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/log.h"
18 #include "wx/settings.h"
19 #endif
20
21 #include "wx/fontutil.h"
22 #include "wx/utils.h"
23 #include "wx/sysopt.h"
24
25 #include "wx/gtk/private.h"
26 #include "wx/gtk/private/mnemonics.h"
27
28 // ============================================================================
29 // wxControl implementation
30 // ============================================================================
31
32 // ----------------------------------------------------------------------------
33 // wxControl creation
34 // ----------------------------------------------------------------------------
35
36 wxIMPLEMENT_DYNAMIC_CLASS(wxControl, wxWindow);
37
wxControl()38 wxControl::wxControl()
39 {
40 }
41
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxValidator & wxVALIDATOR_PARAM (validator),const wxString & name)42 bool wxControl::Create( wxWindow *parent,
43 wxWindowID id,
44 const wxPoint &pos,
45 const wxSize &size,
46 long style,
47 const wxValidator& wxVALIDATOR_PARAM(validator),
48 const wxString &name )
49 {
50 bool ret = wxWindow::Create(parent, id, pos, size, style, name);
51
52 #if wxUSE_VALIDATORS
53 SetValidator(validator);
54 #endif
55
56 return ret;
57 }
58
59 #ifdef __WXGTK3__
SetFont(const wxFont & font)60 bool wxControl::SetFont(const wxFont& font)
61 {
62 const bool changed = base_type::SetFont(font);
63 if (changed && m_widget && !gtk_widget_get_realized(m_widget) && gtk_check_version(3,5,0))
64 {
65 // GTK defers sending "style-updated" until widget is realized, but
66 // GetBestSize() won't compute correct result until the signal is sent,
67 // so we have to do it now
68 // But don't bother for GTK > 3.4, the change won't take effect until
69 // GTK updates it's style cache
70 g_signal_emit_by_name(m_widget, "style-updated");
71 }
72 return changed;
73 }
74 #endif
75
DoGetBestSize() const76 wxSize wxControl::DoGetBestSize() const
77 {
78 // Do not return any arbitrary default value...
79 wxASSERT_MSG( m_widget, wxT("DoGetBestSize called before creation") );
80
81 wxSize best;
82 if (m_wxwindow)
83 {
84 // this is not a native control, size_request is likely to be (0,0)
85 best = wxControlBase::DoGetBestSize();
86 }
87 else
88 {
89 best = GTKGetPreferredSize(m_widget);
90 }
91
92 return best;
93 }
94
PostCreation(const wxSize & size)95 void wxControl::PostCreation(const wxSize& size)
96 {
97 wxWindow::PostCreation();
98
99 #ifdef __WXGTK3__
100 if (HasFlag(wxNO_BORDER))
101 GTKApplyCssStyle("*{ border:none; border-radius:0; padding:0 }");
102 #endif
103
104 #ifndef __WXGTK3__
105 // NB: GetBestSize needs to know the style, otherwise it will assume
106 // default font and if the user uses a different font, determined
107 // best size will be different (typically, smaller) than the desired
108 // size. This call ensure that a style is available at the time
109 // GetBestSize is called.
110 gtk_widget_ensure_style(m_widget);
111 #endif
112
113 SetInitialSize(size);
114 }
115
116 // ----------------------------------------------------------------------------
117 // Work around a GTK+ bug whereby button is insensitive after being
118 // enabled
119 // ----------------------------------------------------------------------------
120
121 // Fix sensitivity due to bug in GTK+ < 2.14
GTKFixSensitivity(bool WXUNUSED_IN_GTK3 (onlyIfUnderMouse))122 void wxControl::GTKFixSensitivity(bool WXUNUSED_IN_GTK3(onlyIfUnderMouse))
123 {
124 #ifndef __WXGTK3__
125 if (!wx_is_at_least_gtk2(14)
126 #if wxUSE_SYSTEM_OPTIONS
127 && (wxSystemOptions::GetOptionInt(wxT("gtk.control.disable-sensitivity-fix")) != 1)
128 #endif
129 )
130 {
131 if (!onlyIfUnderMouse || GetScreenRect().Contains(wxGetMousePosition()))
132 {
133 Hide();
134 Show();
135 }
136 }
137 #endif
138 }
139
140 // ----------------------------------------------------------------------------
141 // wxControl dealing with labels
142 // ----------------------------------------------------------------------------
143
GTKSetLabelForLabel(GtkLabel * w,const wxString & label)144 void wxControl::GTKSetLabelForLabel(GtkLabel *w, const wxString& label)
145 {
146 const wxString labelGTK = GTKConvertMnemonics(label);
147 gtk_label_set_text_with_mnemonic(w, wxGTK_CONV(labelGTK));
148 }
149
150 #if wxUSE_MARKUP
151
GTKSetLabelWithMarkupForLabel(GtkLabel * w,const wxString & label)152 void wxControl::GTKSetLabelWithMarkupForLabel(GtkLabel *w, const wxString& label)
153 {
154 const wxString labelGTK = GTKConvertMnemonicsWithMarkup(label);
155 gtk_label_set_markup_with_mnemonic(w, wxGTK_CONV(labelGTK));
156 }
157
158 #endif // wxUSE_MARKUP
159
160 // ----------------------------------------------------------------------------
161 // GtkFrame helpers
162 //
163 // GtkFrames do in fact support mnemonics in GTK2+ but not through
164 // gtk_frame_set_label, rather you need to use a custom label widget
165 // instead (idea gleaned from the native gtk font dialog code in GTK)
166 // ----------------------------------------------------------------------------
167
GTKCreateFrame(const wxString & label)168 GtkWidget* wxControl::GTKCreateFrame(const wxString& label)
169 {
170 const wxString labelGTK = GTKConvertMnemonics(label);
171 GtkWidget* labelwidget = gtk_label_new_with_mnemonic(wxGTK_CONV(labelGTK));
172 gtk_widget_show(labelwidget); // without this it won't show...
173
174 GtkWidget* framewidget = gtk_frame_new(NULL);
175 gtk_frame_set_label_widget(GTK_FRAME(framewidget), labelwidget);
176
177 return framewidget; // note that the label is already set so you'll
178 // only need to call wxControl::SetLabel afterwards
179 }
180
GTKSetLabelForFrame(GtkFrame * w,const wxString & label)181 void wxControl::GTKSetLabelForFrame(GtkFrame *w, const wxString& label)
182 {
183 wxControlBase::SetLabel(label);
184
185 GtkLabel* labelwidget = GTK_LABEL(gtk_frame_get_label_widget(w));
186 GTKSetLabelForLabel(labelwidget, label);
187 }
188
GTKFrameApplyWidgetStyle(GtkFrame * w,GtkRcStyle * style)189 void wxControl::GTKFrameApplyWidgetStyle(GtkFrame* w, GtkRcStyle* style)
190 {
191 GTKApplyStyle(GTK_WIDGET(w), style);
192 GTKApplyStyle(gtk_frame_get_label_widget(w), style);
193 }
194
GTKFrameSetMnemonicWidget(GtkFrame * w,GtkWidget * widget)195 void wxControl::GTKFrameSetMnemonicWidget(GtkFrame* w, GtkWidget* widget)
196 {
197 GtkLabel* labelwidget = GTK_LABEL(gtk_frame_get_label_widget(w));
198
199 gtk_label_set_mnemonic_widget(labelwidget, widget);
200 }
201
202 // ----------------------------------------------------------------------------
203 // worker function implementing GTK*Mnemonics() functions
204 // ----------------------------------------------------------------------------
205
206 /* static */
GTKRemoveMnemonics(const wxString & label)207 wxString wxControl::GTKRemoveMnemonics(const wxString& label)
208 {
209 return wxGTKRemoveMnemonics(label);
210 }
211
212 /* static */
GTKConvertMnemonics(const wxString & label)213 wxString wxControl::GTKConvertMnemonics(const wxString& label)
214 {
215 return wxConvertMnemonicsToGTK(label);
216 }
217
218 /* static */
GTKConvertMnemonicsWithMarkup(const wxString & label)219 wxString wxControl::GTKConvertMnemonicsWithMarkup(const wxString& label)
220 {
221 return wxConvertMnemonicsToGTKMarkup(label);
222 }
223
224 // ----------------------------------------------------------------------------
225 // wxControl styles (a.k.a. attributes)
226 // ----------------------------------------------------------------------------
227
GetDefaultAttributes() const228 wxVisualAttributes wxControl::GetDefaultAttributes() const
229 {
230 return GetDefaultAttributesFromGTKWidget(m_widget,
231 UseGTKStyleBase());
232 }
233
234 // static
235 wxVisualAttributes
GetDefaultAttributesFromGTKWidget(GtkWidget * widget,bool WXUNUSED_IN_GTK3 (useBase),int state)236 wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget,
237 bool WXUNUSED_IN_GTK3(useBase),
238 int state)
239 {
240 wxVisualAttributes attr;
241
242 GtkWidget* tlw = NULL;
243 if (gtk_widget_get_parent(widget) == NULL)
244 {
245 tlw = gtk_window_new(GTK_WINDOW_TOPLEVEL);
246 gtk_container_add(GTK_CONTAINER(tlw), widget);
247 }
248
249 #ifdef __WXGTK3__
250 GtkStateFlags stateFlag = GTK_STATE_FLAG_NORMAL;
251 if (state)
252 {
253 wxASSERT(state == GTK_STATE_ACTIVE);
254 stateFlag = GTK_STATE_FLAG_ACTIVE;
255 }
256 GtkStyleContext* sc = gtk_widget_get_style_context(widget);
257 gtk_style_context_save(sc);
258 GdkRGBA *fc, *bc;
259 wxNativeFontInfo info;
260 gtk_style_context_set_state(sc, stateFlag);
261 gtk_style_context_get(sc, stateFlag,
262 "color", &fc, "background-color", &bc,
263 GTK_STYLE_PROPERTY_FONT, &info.description, NULL);
264 gtk_style_context_restore(sc);
265 attr.colFg = wxColour(*fc);
266 attr.colBg = wxColour(*bc);
267 attr.font = wxFont(info);
268 gdk_rgba_free(fc);
269 gdk_rgba_free(bc);
270
271 // Go up the parent chain for a background color
272 while (attr.colBg.Alpha() == 0 && (widget = gtk_widget_get_parent(widget)))
273 {
274 sc = gtk_widget_get_style_context(widget);
275 gtk_style_context_save(sc);
276 gtk_style_context_set_state(sc, stateFlag);
277 gtk_style_context_get(sc, stateFlag, "background-color", &bc, NULL);
278 gtk_style_context_restore(sc);
279 attr.colBg = wxColour(*bc);
280 gdk_rgba_free(bc);
281 }
282 #else
283 GtkStyle* style;
284
285 style = gtk_rc_get_style(widget);
286 if (!style)
287 style = gtk_widget_get_default_style();
288
289 if (style)
290 {
291 // get the style's colours
292 attr.colFg = wxColour(style->fg[state]);
293 if (useBase)
294 attr.colBg = wxColour(style->base[state]);
295 else
296 attr.colBg = wxColour(style->bg[state]);
297
298 // get the style's font
299 if (!style->font_desc)
300 style = gtk_widget_get_default_style();
301 if (style && style->font_desc)
302 {
303 wxNativeFontInfo info;
304 info.description = style->font_desc;
305 attr.font = wxFont(info);
306 info.description = NULL;
307 }
308 }
309 else
310 attr = wxWindow::GetClassDefaultAttributes(wxWINDOW_VARIANT_NORMAL);
311 #endif
312
313 if (!attr.font.IsOk())
314 {
315 GtkSettings *settings = gtk_settings_get_default();
316 gchar *font_name = NULL;
317 g_object_get ( settings,
318 "gtk-font-name",
319 &font_name,
320 NULL);
321 if (!font_name)
322 attr.font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
323 else
324 {
325 attr.font = wxFont(wxString::FromUTF8(font_name));
326 g_free(font_name);
327 }
328 }
329
330 if (tlw)
331 gtk_widget_destroy(tlw);
332
333 return attr;
334 }
335
336 // This is not the same as GetBestSize() because that size may have
337 // been recalculated and cached by us. We want GTK+ information.
GTKGetPreferredSize(GtkWidget * widget) const338 wxSize wxControl::GTKGetPreferredSize(GtkWidget* widget) const
339 {
340 GtkRequisition req = { 0, 0 };
341 #ifdef __WXGTK3__
342 int w, h;
343 gtk_widget_get_size_request(widget, &w, &h);
344
345 // gtk_widget_get_preferred_size() just returns 0 if the control is hidden,
346 // so we have to temporarily show the widget before calling it to get
347 // something useful from it, if it's currently hidden.
348 // So workaround this case.
349 const bool wasHidden = !gtk_widget_get_visible(widget);
350 if ( wasHidden )
351 gtk_widget_show(widget);
352
353 gtk_widget_set_size_request(widget, -1, -1);
354 gtk_widget_get_preferred_size(widget, NULL, &req);
355 gtk_widget_set_size_request(widget, w, h);
356
357 if ( wasHidden )
358 gtk_widget_hide(widget);
359 #else
360 GTK_WIDGET_GET_CLASS(widget)->size_request(widget, &req);
361 #endif
362
363 return wxSize(req.width, req.height);
364 }
365
GTKGetEntryMargins(GtkEntry * entry) const366 wxSize wxControl::GTKGetEntryMargins(GtkEntry* entry) const
367 {
368 wxSize size;
369 gtk_entry_get_layout_offsets(entry, &size.x, &size.y);
370
371 #ifdef __WXGTK3__
372 GtkBorder border;
373 GtkStyleContext* sc = gtk_widget_get_style_context(GTK_WIDGET(entry));
374 gtk_style_context_get_padding(sc, gtk_style_context_get_state(sc), &border);
375 #else
376 // Equivalent to the GTK2 private function _gtk_entry_effective_inner_border()
377
378 GtkBorder border = { 2, 2, 2, 2 };
379
380 #if GTK_CHECK_VERSION(2,10,0)
381 if (wx_is_at_least_gtk2(10))
382 {
383 const GtkBorder* innerBorder1 = gtk_entry_get_inner_border(entry);
384 if (innerBorder1)
385 border = *innerBorder1;
386 else
387 {
388 GtkBorder* innerBorder2;
389 gtk_widget_style_get(GTK_WIDGET(entry), "inner-border", &innerBorder2, NULL);
390 if (innerBorder2)
391 {
392 border = *innerBorder2;
393 gtk_border_free(innerBorder2);
394 }
395 }
396 }
397 #endif // GTK+ 2.10+
398 #endif
399
400 size.x += border.left + border.right;
401 size.y += border.top + border.bottom;
402
403 return size;
404 }
405
406
407 #endif // wxUSE_CONTROLS
408