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