1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/control.cpp
3 // Purpose:     wxControl implementation for wxGTK
4 // Author:      Robert Roebling
5 // Id:          $Id: control.cpp 58191 2009-01-18 12:21:04Z JS $
6 // Copyright:   (c) 1998 Robert Roebling, Julian Smart and Vadim Zeitlin
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 #if wxUSE_CONTROLS
14 
15 #include "wx/control.h"
16 
17 #ifndef WX_PRECOMP
18     #include "wx/log.h"
19     #include "wx/settings.h"
20 #endif
21 
22 #include "wx/fontutil.h"
23 #include "wx/utils.h"
24 #include "wx/gtk/private.h"
25 #include "wx/sysopt.h"
26 
27 // ============================================================================
28 // wxControl implementation
29 // ============================================================================
30 
31 // ----------------------------------------------------------------------------
32 // wxControl creation
33 // ----------------------------------------------------------------------------
34 
IMPLEMENT_DYNAMIC_CLASS(wxControl,wxWindow)35 IMPLEMENT_DYNAMIC_CLASS(wxControl, wxWindow)
36 
37 wxControl::wxControl()
38 {
39     m_needParent = true;
40 }
41 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxValidator & 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& 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 
DoGetBestSize() const59 wxSize wxControl::DoGetBestSize() const
60 {
61     // Do not return any arbitrary default value...
62     wxASSERT_MSG( m_widget, wxT("DoGetBestSize called before creation") );
63 
64     GtkRequisition req;
65     req.width = 2;
66     req.height = 2;
67     (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_widget) )->size_request )
68         (m_widget, &req );
69 
70     wxSize best(req.width, req.height);
71     CacheBestSize(best);
72     return best;
73 }
74 
PostCreation(const wxSize & size)75 void wxControl::PostCreation(const wxSize& size)
76 {
77     wxWindow::PostCreation();
78 
79     // NB: GetBestSize needs to know the style, otherwise it will assume
80     //     default font and if the user uses a different font, determined
81     //     best size will be different (typically, smaller) than the desired
82     //     size. This call ensure that a style is available at the time
83     //     GetBestSize is called.
84     gtk_widget_ensure_style(m_widget);
85 
86     ApplyWidgetStyle();
87     SetInitialSize(size);
88 }
89 
90 // ----------------------------------------------------------------------------
91 // wxControl dealing with labels
92 // ----------------------------------------------------------------------------
93 
SetLabel(const wxString & label)94 void wxControl::SetLabel( const wxString &label )
95 {
96     // keep the original string internally to be able to return it later (for
97     // consistency with the other ports)
98     m_label = label;
99 
100     InvalidateBestSize();
101 }
102 
GetLabel() const103 wxString wxControl::GetLabel() const
104 {
105     return m_label;
106 }
107 
GTKSetLabelForLabel(GtkLabel * w,const wxString & label)108 void wxControl::GTKSetLabelForLabel(GtkLabel *w, const wxString& label)
109 {
110     // don't call the virtual function which might call this one back again
111     wxControl::SetLabel(label);
112 
113     const wxString labelGTK = GTKConvertMnemonics(label);
114 
115     gtk_label_set_text_with_mnemonic(w, wxGTK_CONV(labelGTK));
116 }
117 
118 // ----------------------------------------------------------------------------
119 // GtkFrame helpers
120 //
121 // GtkFrames do in fact support mnemonics in GTK2+ but not through
122 // gtk_frame_set_label, rather you need to use a custom label widget
123 // instead (idea gleaned from the native gtk font dialog code in GTK)
124 // ----------------------------------------------------------------------------
125 
GTKCreateFrame(const wxString & label)126 GtkWidget* wxControl::GTKCreateFrame(const wxString& label)
127 {
128     const wxString labelGTK = GTKConvertMnemonics(label);
129     GtkWidget* labelwidget = gtk_label_new_with_mnemonic(wxGTK_CONV(labelGTK));
130     gtk_widget_show(labelwidget); // without this it won't show...
131 
132     GtkWidget* framewidget = gtk_frame_new(NULL);
133     gtk_frame_set_label_widget(GTK_FRAME(framewidget), labelwidget);
134 
135     return framewidget; //note that the label is already set so you'll
136                         //only need to call wxControl::SetLabel afterwards
137 }
138 
GTKSetLabelForFrame(GtkFrame * w,const wxString & label)139 void wxControl::GTKSetLabelForFrame(GtkFrame *w, const wxString& label)
140 {
141     GtkLabel* labelwidget = GTK_LABEL(gtk_frame_get_label_widget(w));
142     GTKSetLabelForLabel(labelwidget, label);
143 }
144 
GTKFrameApplyWidgetStyle(GtkFrame * w,GtkRcStyle * style)145 void wxControl::GTKFrameApplyWidgetStyle(GtkFrame* w, GtkRcStyle* style)
146 {
147     gtk_widget_modify_style(GTK_WIDGET(w), style);
148     gtk_widget_modify_style(gtk_frame_get_label_widget (w), style);
149 }
150 
GTKFrameSetMnemonicWidget(GtkFrame * w,GtkWidget * widget)151 void wxControl::GTKFrameSetMnemonicWidget(GtkFrame* w, GtkWidget* widget)
152 {
153     GtkLabel* labelwidget = GTK_LABEL(gtk_frame_get_label_widget(w));
154 
155     gtk_label_set_mnemonic_widget(labelwidget, widget);
156 }
157 
158 // ----------------------------------------------------------------------------
159 // worker function implementing both GTKConvert/RemoveMnemonics()
160 //
161 // notice that under GTK+ 1 we only really need to support MNEMONICS_REMOVE as
162 // it doesn't support mnemonics anyhow but this would make the code so ugly
163 // that we do the same thing for GKT+ 1 and 2
164 // ----------------------------------------------------------------------------
165 
166 enum MnemonicsFlag
167 {
168     MNEMONICS_REMOVE,
169     MNEMONICS_CONVERT
170 };
171 
GTKProcessMnemonics(const wxString & label,MnemonicsFlag flag)172 static wxString GTKProcessMnemonics(const wxString& label, MnemonicsFlag flag)
173 {
174     const size_t len = label.length();
175     wxString labelGTK;
176     labelGTK.reserve(len);
177     for ( size_t i = 0; i < len; i++ )
178     {
179         wxChar ch = label[i];
180 
181         switch ( ch )
182         {
183             case wxT('&'):
184                 if ( i == len - 1 )
185                 {
186                     // "&" at the end of string is an error
187                     wxLogDebug(wxT("Invalid label \"%s\"."), label.c_str());
188                     break;
189                 }
190 
191                 ch = label[++i]; // skip '&' itself
192                 switch ( ch )
193                 {
194                     case wxT('&'):
195                         // special case: "&&" is not a mnemonic at all but just
196                         // an escaped "&"
197                         labelGTK += wxT('&');
198                         break;
199 
200                     case wxT('_'):
201                         if ( flag == MNEMONICS_CONVERT )
202                         {
203                             // '_' can't be a GTK mnemonic apparently so
204                             // replace it with something similar
205                             labelGTK += wxT("_-");
206                             break;
207                         }
208                         //else: fall through
209 
210                     default:
211                         if ( flag == MNEMONICS_CONVERT )
212                             labelGTK += wxT('_');
213                         labelGTK += ch;
214                 }
215                 break;
216 
217             case wxT('_'):
218                 if ( flag == MNEMONICS_CONVERT )
219                 {
220                     // escape any existing underlines in the string so that
221                     // they don't become mnemonics accidentally
222                     labelGTK += wxT("__");
223                     break;
224                 }
225                 //else: fall through
226 
227             default:
228                 labelGTK += ch;
229         }
230     }
231 
232     return labelGTK;
233 }
234 
235 /* static */
GTKRemoveMnemonics(const wxString & label)236 wxString wxControl::GTKRemoveMnemonics(const wxString& label)
237 {
238     return GTKProcessMnemonics(label, MNEMONICS_REMOVE);
239 }
240 
241 /* static */
GTKConvertMnemonics(const wxString & label)242 wxString wxControl::GTKConvertMnemonics(const wxString& label)
243 {
244     return GTKProcessMnemonics(label, MNEMONICS_CONVERT);
245 }
246 
247 // ----------------------------------------------------------------------------
248 // wxControl styles (a.k.a. attributes)
249 // ----------------------------------------------------------------------------
250 
GetDefaultAttributes() const251 wxVisualAttributes wxControl::GetDefaultAttributes() const
252 {
253     return GetDefaultAttributesFromGTKWidget(m_widget,
254                                              UseGTKStyleBase());
255 }
256 
257 // static
258 wxVisualAttributes
GetDefaultAttributesFromGTKWidget(GtkWidget * widget,bool useBase,int state)259 wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget,
260                                              bool useBase,
261                                              int state)
262 {
263     GtkStyle* style;
264     wxVisualAttributes attr;
265 
266     style = gtk_rc_get_style(widget);
267     if (!style)
268         style = gtk_widget_get_default_style();
269 
270     if (!style)
271     {
272         return wxWindow::GetClassDefaultAttributes(wxWINDOW_VARIANT_NORMAL);
273     }
274 
275     if (state == -1)
276         state = GTK_STATE_NORMAL;
277 
278     // get the style's colours
279     attr.colFg = wxColour(style->fg[state]);
280     if (useBase)
281         attr.colBg = wxColour(style->base[state]);
282     else
283         attr.colBg = wxColour(style->bg[state]);
284 
285     // get the style's font
286     if ( !style->font_desc )
287         style = gtk_widget_get_default_style();
288     if ( style && style->font_desc )
289     {
290         wxNativeFontInfo info;
291         info.description = pango_font_description_copy(style->font_desc);
292         attr.font = wxFont(info);
293     }
294     else
295     {
296         GtkSettings *settings = gtk_settings_get_default();
297         gchar *font_name = NULL;
298         g_object_get ( settings,
299                        "gtk-font-name",
300                        &font_name,
301                        NULL);
302         if (!font_name)
303             attr.font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
304         else
305             attr.font = wxFont(wxString::FromAscii(font_name));
306         g_free (font_name);
307     }
308 
309     return attr;
310 }
311 
312 
313 //static
314 wxVisualAttributes
GetDefaultAttributesFromGTKWidget(wxGtkWidgetNew_t widget_new,bool useBase,int state)315 wxControl::GetDefaultAttributesFromGTKWidget(wxGtkWidgetNew_t widget_new,
316                                              bool useBase,
317                                              int state)
318 {
319     wxVisualAttributes attr;
320     // NB: we need toplevel window so that GTK+ can find the right style
321     GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
322     GtkWidget* widget = widget_new();
323     gtk_container_add(GTK_CONTAINER(wnd), widget);
324     attr = GetDefaultAttributesFromGTKWidget(widget, useBase, state);
325     gtk_widget_destroy(wnd);
326     return attr;
327 }
328 
329 //static
330 wxVisualAttributes
GetDefaultAttributesFromGTKWidget(wxGtkWidgetNewFromStr_t widget_new,bool useBase,int state)331 wxControl::GetDefaultAttributesFromGTKWidget(wxGtkWidgetNewFromStr_t widget_new,
332                                              bool useBase,
333                                              int state)
334 {
335     wxVisualAttributes attr;
336     // NB: we need toplevel window so that GTK+ can find the right style
337     GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
338     GtkWidget* widget = widget_new("");
339     gtk_container_add(GTK_CONTAINER(wnd), widget);
340     attr = GetDefaultAttributesFromGTKWidget(widget, useBase, state);
341     gtk_widget_destroy(wnd);
342     return attr;
343 }
344 
345 
346 //static
347 wxVisualAttributes
GetDefaultAttributesFromGTKWidget(wxGtkWidgetNewFromAdj_t widget_new,bool useBase,int state)348 wxControl::GetDefaultAttributesFromGTKWidget(wxGtkWidgetNewFromAdj_t widget_new,
349                                              bool useBase,
350                                              int state)
351 {
352     wxVisualAttributes attr;
353     // NB: we need toplevel window so that GTK+ can find the right style
354     GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
355     GtkWidget* widget = widget_new(NULL);
356     gtk_container_add(GTK_CONTAINER(wnd), widget);
357     attr = GetDefaultAttributesFromGTKWidget(widget, useBase, state);
358     gtk_widget_destroy(wnd);
359     return attr;
360 }
361 
362 // ----------------------------------------------------------------------------
363 // idle handling
364 // ----------------------------------------------------------------------------
365 
OnInternalIdle()366 void wxControl::OnInternalIdle()
367 {
368     if ( GtkShowFromOnIdle() )
369         return;
370 
371     if ( GTK_WIDGET_REALIZED(m_widget) )
372     {
373         GTKUpdateCursor();
374 
375         GTKSetDelayedFocusIfNeeded();
376     }
377 
378     if ( wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen() )
379         UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
380 }
381 
382 // Fix sensitivity due to bug in GTK+ < 2.14
wxGtkFixSensitivity(wxWindow * ctrl)383 void wxGtkFixSensitivity(wxWindow* ctrl)
384 {
385 #ifdef __WXGTK24__
386     // Work around a GTK+ bug whereby button is insensitive after being
387     // enabled
388     if (gtk_check_version(2,14,0)
389 #if wxUSE_SYSTEM_OPTIONS
390         && (wxSystemOptions::GetOptionInt(wxT("gtk.control.disable-sensitivity-fix")) != 1)
391 #endif
392         )
393     {
394         wxPoint pt = wxGetMousePosition();
395         wxRect rect(ctrl->ClientToScreen(wxPoint(0, 0)), ctrl->GetSize());
396         if (rect.Contains(pt))
397         {
398             ctrl->Hide();
399             ctrl->Show();
400         }
401     }
402 #endif
403 }
404 
405 #endif // wxUSE_CONTROLS
406