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