added support for ellipsization and markup in wxStaticText (modified patch 1629946)
[wxWidgets.git] / src / gtk / control.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/control.cpp
3 // Purpose: wxControl implementation for wxGTK
4 // Author: Robert Roebling
5 // Id: $Id$
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/gtk/private.h"
24
25 #include "wx/private/stattext.h"
26
27
28 // ============================================================================
29 // wxControl implementation
30 // ============================================================================
31
32 // ----------------------------------------------------------------------------
33 // wxControl creation
34 // ----------------------------------------------------------------------------
35
36 IMPLEMENT_DYNAMIC_CLASS(wxControl, wxWindow)
37
38 wxControl::wxControl()
39 {
40 m_needParent = true;
41 }
42
43 bool wxControl::Create( wxWindow *parent,
44 wxWindowID id,
45 const wxPoint &pos,
46 const wxSize &size,
47 long style,
48 const wxValidator& validator,
49 const wxString &name )
50 {
51 bool ret = wxWindow::Create(parent, id, pos, size, style, name);
52
53 #if wxUSE_VALIDATORS
54 SetValidator(validator);
55 #endif
56
57 return ret;
58 }
59
60 wxSize wxControl::DoGetBestSize() const
61 {
62 // Do not return any arbitrary default value...
63 wxASSERT_MSG( m_widget, wxT("DoGetBestSize called before creation") );
64
65 GtkRequisition req;
66 req.width = 2;
67 req.height = 2;
68 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_widget) )->size_request )
69 (m_widget, &req );
70
71 wxSize best(req.width, req.height);
72 CacheBestSize(best);
73 return best;
74 }
75
76 void wxControl::PostCreation(const wxSize& size)
77 {
78 wxWindow::PostCreation();
79
80 // NB: GetBestSize needs to know the style, otherwise it will assume
81 // default font and if the user uses a different font, determined
82 // best size will be different (typically, smaller) than the desired
83 // size. This call ensure that a style is available at the time
84 // GetBestSize is called.
85 gtk_widget_ensure_style(m_widget);
86
87 ApplyWidgetStyle();
88 SetInitialSize(size);
89 }
90
91 // ----------------------------------------------------------------------------
92 // wxControl dealing with labels
93 // ----------------------------------------------------------------------------
94
95 void wxControl::GTKSetLabelForLabel(GtkLabel *w, const wxString& label)
96 {
97 // save the original label
98 wxControlBase::SetLabel(label);
99
100 const wxString labelGTK = GTKConvertMnemonics(label);
101 gtk_label_set_text_with_mnemonic(w, wxGTK_CONV(labelGTK));
102 }
103
104 void wxControl::GTKSetLabelWithMarkupForLabel(GtkLabel *w, const wxString& label)
105 {
106 const wxString labelGTK = GTKConvertMnemonicsWithMarkup(label);
107 gtk_label_set_markup_with_mnemonic(w, wxGTK_CONV(labelGTK));
108 }
109
110
111 // ----------------------------------------------------------------------------
112 // GtkFrame helpers
113 //
114 // GtkFrames do in fact support mnemonics in GTK2+ but not through
115 // gtk_frame_set_label, rather you need to use a custom label widget
116 // instead (idea gleaned from the native gtk font dialog code in GTK)
117 // ----------------------------------------------------------------------------
118
119 GtkWidget* wxControl::GTKCreateFrame(const wxString& label)
120 {
121 const wxString labelGTK = GTKConvertMnemonics(label);
122 GtkWidget* labelwidget = gtk_label_new_with_mnemonic(wxGTK_CONV(labelGTK));
123 gtk_widget_show(labelwidget); // without this it won't show...
124
125 GtkWidget* framewidget = gtk_frame_new(NULL);
126 gtk_frame_set_label_widget(GTK_FRAME(framewidget), labelwidget);
127
128 return framewidget; // note that the label is already set so you'll
129 // only need to call wxControl::SetLabel afterwards
130 }
131
132 void wxControl::GTKSetLabelForFrame(GtkFrame *w, const wxString& label)
133 {
134 GtkLabel* labelwidget = GTK_LABEL(gtk_frame_get_label_widget(w));
135 GTKSetLabelForLabel(labelwidget, label);
136 }
137
138 void wxControl::GTKFrameApplyWidgetStyle(GtkFrame* w, GtkRcStyle* style)
139 {
140 gtk_widget_modify_style(GTK_WIDGET(w), style);
141 gtk_widget_modify_style(gtk_frame_get_label_widget (w), style);
142 }
143
144 void wxControl::GTKFrameSetMnemonicWidget(GtkFrame* w, GtkWidget* widget)
145 {
146 GtkLabel* labelwidget = GTK_LABEL(gtk_frame_get_label_widget(w));
147
148 gtk_label_set_mnemonic_widget(labelwidget, widget);
149 }
150
151 // ----------------------------------------------------------------------------
152 // worker function implementing GTK*Mnemonics() functions
153 // ----------------------------------------------------------------------------
154
155 enum MnemonicsFlag
156 {
157 MNEMONICS_REMOVE,
158 MNEMONICS_CONVERT,
159 MNEMONICS_CONVERT_MARKUP
160 };
161
162 static wxString GTKProcessMnemonics(const wxChar* label, MnemonicsFlag flag)
163 {
164 const size_t len = wxStrlen(label);
165 wxString labelGTK;
166 labelGTK.reserve(len);
167 for ( size_t i = 0; i < len; i++ )
168 {
169 wxChar ch = label[i];
170
171 switch ( ch )
172 {
173 case wxT('&'):
174 if ( i == len - 1 )
175 {
176 // "&" at the end of string is an error
177 wxLogDebug(wxT("Invalid label \"%s\"."), label);
178 break;
179 }
180
181 if ( flag == MNEMONICS_CONVERT_MARKUP )
182 {
183 bool isMnemonic = true;
184
185 // is this ampersand introducing a mnemonic or rather an entity?
186 for (size_t j=0; j < wxMARKUP_ENTITY_MAX; j++)
187 {
188 const wxChar *entity = wxMarkupEntities[wxMARKUP_ELEMENT_NAME][j];
189 size_t entityLen = wxStrlen(entity);
190
191 if (len - i >= entityLen &&
192 wxStrncmp(entity, &label[i], entityLen) == 0)
193 {
194 labelGTK << entity;
195 i += entityLen - 1; // the -1 is because main for()
196 // loop already increments i
197 isMnemonic = false;
198
199 break;
200 }
201 }
202
203 if (!isMnemonic)
204 continue;
205 }
206
207 ch = label[++i]; // skip '&' itself
208 switch ( ch )
209 {
210 case wxT('&'):
211 // special case: "&&" is not a mnemonic at all but just
212 // an escaped "&"
213 if ( flag == MNEMONICS_CONVERT_MARKUP )
214 labelGTK += wxT("&amp;");
215 else
216 labelGTK += wxT('&');
217 break;
218
219 case wxT('_'):
220 if ( flag != MNEMONICS_REMOVE )
221 {
222 // '_' can't be a GTK mnemonic apparently so
223 // replace it with something similar
224 labelGTK += wxT("_-");
225 break;
226 }
227 //else: fall through
228
229 default:
230 if ( flag != MNEMONICS_REMOVE )
231 labelGTK += wxT('_');
232 labelGTK += ch;
233 }
234 break;
235
236 case wxT('_'):
237 if ( flag != MNEMONICS_REMOVE )
238 {
239 // escape any existing underlines in the string so that
240 // they don't become mnemonics accidentally
241 labelGTK += wxT("__");
242 break;
243 }
244 //else: fall through
245
246 default:
247 labelGTK += ch;
248 }
249 }
250
251 return labelGTK;
252 }
253
254 /* static */
255 wxString wxControl::GTKRemoveMnemonics(const wxString& label)
256 {
257 return GTKProcessMnemonics(label, MNEMONICS_REMOVE);
258 }
259
260 /* static */
261 wxString wxControl::GTKConvertMnemonics(const wxString& label)
262 {
263 return GTKProcessMnemonics(label, MNEMONICS_CONVERT);
264 }
265
266 /* static */
267 wxString wxControl::GTKConvertMnemonicsWithMarkup(const wxString& label)
268 {
269 return GTKProcessMnemonics(label, MNEMONICS_CONVERT_MARKUP);
270 }
271
272 // ----------------------------------------------------------------------------
273 // wxControl styles (a.k.a. attributes)
274 // ----------------------------------------------------------------------------
275
276 wxVisualAttributes wxControl::GetDefaultAttributes() const
277 {
278 return GetDefaultAttributesFromGTKWidget(m_widget,
279 UseGTKStyleBase());
280 }
281
282 // static
283 wxVisualAttributes
284 wxControl::GetDefaultAttributesFromGTKWidget(GtkWidget* widget,
285 bool useBase,
286 int state)
287 {
288 GtkStyle* style;
289 wxVisualAttributes attr;
290
291 style = gtk_rc_get_style(widget);
292 if (!style)
293 style = gtk_widget_get_default_style();
294
295 if (!style)
296 {
297 return wxWindow::GetClassDefaultAttributes(wxWINDOW_VARIANT_NORMAL);
298 }
299
300 if (state == -1)
301 state = GTK_STATE_NORMAL;
302
303 // get the style's colours
304 attr.colFg = wxColour(style->fg[state]);
305 if (useBase)
306 attr.colBg = wxColour(style->base[state]);
307 else
308 attr.colBg = wxColour(style->bg[state]);
309
310 // get the style's font
311 if ( !style->font_desc )
312 style = gtk_widget_get_default_style();
313 if ( style && style->font_desc )
314 {
315 wxNativeFontInfo info;
316 info.description = pango_font_description_copy(style->font_desc);
317 attr.font = wxFont(info);
318 }
319 else
320 {
321 GtkSettings *settings = gtk_settings_get_default();
322 gchar *font_name = NULL;
323 g_object_get ( settings,
324 "gtk-font-name",
325 &font_name,
326 NULL);
327 if (!font_name)
328 attr.font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
329 else
330 attr.font = wxFont(wxString::FromAscii(font_name));
331 g_free (font_name);
332 }
333
334 return attr;
335 }
336
337
338 //static
339 wxVisualAttributes
340 wxControl::GetDefaultAttributesFromGTKWidget(wxGtkWidgetNew_t widget_new,
341 bool useBase,
342 int state)
343 {
344 wxVisualAttributes attr;
345 // NB: we need toplevel window so that GTK+ can find the right style
346 GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
347 GtkWidget* widget = widget_new();
348 gtk_container_add(GTK_CONTAINER(wnd), widget);
349 attr = GetDefaultAttributesFromGTKWidget(widget, useBase, state);
350 gtk_widget_destroy(wnd);
351 return attr;
352 }
353
354 //static
355 wxVisualAttributes
356 wxControl::GetDefaultAttributesFromGTKWidget(wxGtkWidgetNewFromStr_t widget_new,
357 bool useBase,
358 int state)
359 {
360 wxVisualAttributes attr;
361 // NB: we need toplevel window so that GTK+ can find the right style
362 GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
363 GtkWidget* widget = widget_new("");
364 gtk_container_add(GTK_CONTAINER(wnd), widget);
365 attr = GetDefaultAttributesFromGTKWidget(widget, useBase, state);
366 gtk_widget_destroy(wnd);
367 return attr;
368 }
369
370
371 //static
372 wxVisualAttributes
373 wxControl::GetDefaultAttributesFromGTKWidget(wxGtkWidgetNewFromAdj_t widget_new,
374 bool useBase,
375 int state)
376 {
377 wxVisualAttributes attr;
378 // NB: we need toplevel window so that GTK+ can find the right style
379 GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
380 GtkWidget* widget = widget_new(NULL);
381 gtk_container_add(GTK_CONTAINER(wnd), widget);
382 attr = GetDefaultAttributesFromGTKWidget(widget, useBase, state);
383 gtk_widget_destroy(wnd);
384 return attr;
385 }
386
387 // ----------------------------------------------------------------------------
388 // idle handling
389 // ----------------------------------------------------------------------------
390
391 void wxControl::OnInternalIdle()
392 {
393 if ( GtkShowFromOnIdle() )
394 return;
395
396 if ( GTK_WIDGET_REALIZED(m_widget) )
397 {
398 GTKUpdateCursor();
399
400 GTKSetDelayedFocusIfNeeded();
401 }
402
403 if ( wxUpdateUIEvent::CanUpdate(this) )
404 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
405 }
406
407 #endif // wxUSE_CONTROLS