wx/debug.h is always unconditionally included by wx/defs.h
[wxWidgets.git] / src / gtk / settings.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/settings.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Modified by: Mart Raudsepp (GetMetric)
6 // Id: $Id$
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #include "wx/settings.h"
15
16 #include "wx/cmndata.h"
17 #include "wx/fontutil.h"
18 #include "wx/toplevel.h"
19
20 // Using gtk_list_new, which is deprecated since GTK2
21 // Using gtk_object_sink, which is deprecated since GTK+-2.9.0
22 #ifdef GTK_DISABLE_DEPRECATED
23 #undef GTK_DISABLE_DEPRECATED
24 #endif
25
26 #include <gdk/gdk.h>
27 #include <gdk/gdkx.h>
28 #include <gdk/gdkprivate.h>
29 #include <gtk/gtk.h>
30
31 #include <X11/Xatom.h>
32
33 #define SHIFT (8*(sizeof(short int)-sizeof(char)))
34
35 // ----------------------------------------------------------------------------
36 // wxSystemObjects
37 // ----------------------------------------------------------------------------
38
39 struct wxSystemObjects
40 {
41 wxColour m_colBtnFace,
42 m_colBtnShadow,
43 m_colBtnHighlight,
44 m_colHighlight,
45 m_colHighlightText,
46 m_colListBox,
47 m_colBtnText,
48 m_colMenuItemHighlight,
49 m_colTooltip,
50 m_colTooltipText;
51
52 wxFont m_fontSystem;
53 };
54
55 static wxSystemObjects gs_objects;
56
57 // ----------------------------------------------------------------------------
58 // wxSystemSettings implementation
59 // ----------------------------------------------------------------------------
60
61 // kind of widget to use in GetColourFromGTKWidget
62 enum wxGtkWidgetType
63 {
64 wxGTK_BUTTON,
65 wxGTK_LIST,
66 wxGTK_MENUITEM
67 };
68
69 // the colour we need
70 enum wxGtkColourType
71 {
72 wxGTK_FG,
73 wxGTK_BG,
74 wxGTK_BASE
75 };
76
77 // wxSystemSettings::GetColour() helper: get the colours from a GTK+
78 // widget style, return true if we did get them, false to use defaults
79 static bool GetColourFromGTKWidget(int& red, int& green, int& blue,
80 wxGtkWidgetType type = wxGTK_BUTTON,
81 GtkStateType state = GTK_STATE_NORMAL,
82 wxGtkColourType colour = wxGTK_BG)
83 {
84 GtkWidget *widget;
85 switch ( type )
86 {
87 default:
88 wxFAIL_MSG( _T("unexpected GTK widget type") );
89 // fall through
90
91 case wxGTK_BUTTON:
92 widget = gtk_button_new();
93 break;
94
95 case wxGTK_LIST:
96 widget = gtk_list_new();
97 break;
98
99 case wxGTK_MENUITEM:
100 widget = gtk_menu_item_new();
101 }
102
103 GtkStyle *def = gtk_rc_get_style( widget );
104 if ( !def )
105 def = gtk_widget_get_default_style();
106
107 bool ok;
108 if ( def )
109 {
110 GdkColor *col;
111 switch ( colour )
112 {
113 default:
114 wxFAIL_MSG( _T("unexpected GTK colour type") );
115 // fall through
116
117 case wxGTK_FG:
118 col = def->fg;
119 break;
120
121 case wxGTK_BG:
122 col = def->bg;
123 break;
124
125 case wxGTK_BASE:
126 col = def->base;
127 break;
128 }
129
130 red = col[state].red;
131 green = col[state].green;
132 blue = col[state].blue;
133
134 ok = true;
135 }
136 else
137 {
138 ok = false;
139 }
140
141 gtk_widget_destroy( widget );
142
143 return ok;
144 }
145
146 static void GetTooltipColors()
147 {
148 GtkTooltips* tooltips = gtk_tooltips_new();
149 gtk_tooltips_force_window(tooltips);
150 gtk_widget_ensure_style(tooltips->tip_window);
151 GdkColor c = tooltips->tip_window->style->bg[GTK_STATE_NORMAL];
152 gs_objects.m_colTooltip = wxColor(c.red >> SHIFT, c.green >> SHIFT, c.blue >> SHIFT);
153 c = tooltips->tip_window->style->fg[GTK_STATE_NORMAL];
154 gs_objects.m_colTooltipText = wxColor(c.red >> SHIFT, c.green >> SHIFT, c.blue >> SHIFT);
155 gtk_object_sink(wx_reinterpret_cast(GtkObject*, tooltips));
156 }
157
158 wxColour wxSystemSettingsNative::GetColour( wxSystemColour index )
159 {
160 switch (index)
161 {
162 case wxSYS_COLOUR_SCROLLBAR:
163 case wxSYS_COLOUR_BACKGROUND:
164 case wxSYS_COLOUR_INACTIVECAPTION:
165 case wxSYS_COLOUR_MENU:
166 case wxSYS_COLOUR_WINDOWFRAME:
167 case wxSYS_COLOUR_ACTIVEBORDER:
168 case wxSYS_COLOUR_INACTIVEBORDER:
169 case wxSYS_COLOUR_BTNFACE:
170 case wxSYS_COLOUR_MENUBAR:
171 case wxSYS_COLOUR_3DLIGHT:
172 if (!gs_objects.m_colBtnFace.Ok())
173 {
174 int red, green, blue;
175 if ( !GetColourFromGTKWidget(red, green, blue) )
176 {
177 red =
178 green = 0;
179 blue = 0x9c40;
180 }
181
182 gs_objects.m_colBtnFace = wxColour( red >> SHIFT,
183 green >> SHIFT,
184 blue >> SHIFT );
185 }
186 return gs_objects.m_colBtnFace;
187
188 case wxSYS_COLOUR_WINDOW:
189 return *wxWHITE;
190
191 case wxSYS_COLOUR_3DDKSHADOW:
192 return *wxBLACK;
193
194 case wxSYS_COLOUR_GRAYTEXT:
195 case wxSYS_COLOUR_BTNSHADOW:
196 //case wxSYS_COLOUR_3DSHADOW:
197 if (!gs_objects.m_colBtnShadow.Ok())
198 {
199 wxColour faceColour(GetColour(wxSYS_COLOUR_3DFACE));
200 gs_objects.m_colBtnShadow =
201 wxColour((unsigned char) (faceColour.Red() * 0.666),
202 (unsigned char) (faceColour.Green() * 0.666),
203 (unsigned char) (faceColour.Blue() * 0.666));
204 }
205
206 return gs_objects.m_colBtnShadow;
207
208 case wxSYS_COLOUR_3DHIGHLIGHT:
209 //case wxSYS_COLOUR_BTNHIGHLIGHT:
210 return * wxWHITE;
211
212 case wxSYS_COLOUR_HIGHLIGHT:
213 if (!gs_objects.m_colHighlight.Ok())
214 {
215 int red, green, blue;
216 if ( !GetColourFromGTKWidget(red, green, blue,
217 wxGTK_BUTTON,
218 GTK_STATE_SELECTED) )
219 {
220 red =
221 green = 0;
222 blue = 0x9c40;
223 }
224
225 gs_objects.m_colHighlight = wxColour( red >> SHIFT,
226 green >> SHIFT,
227 blue >> SHIFT );
228 }
229 return gs_objects.m_colHighlight;
230
231 case wxSYS_COLOUR_LISTBOX:
232 if (!gs_objects.m_colListBox.Ok())
233 {
234 int red, green, blue;
235 if ( GetColourFromGTKWidget(red, green, blue,
236 wxGTK_LIST,
237 GTK_STATE_NORMAL,
238 wxGTK_BASE) )
239 {
240 gs_objects.m_colListBox = wxColour( red >> SHIFT,
241 green >> SHIFT,
242 blue >> SHIFT );
243 }
244 else
245 {
246 gs_objects.m_colListBox = wxColour(*wxWHITE);
247 }
248 }
249 return gs_objects.m_colListBox;
250
251 case wxSYS_COLOUR_MENUTEXT:
252 case wxSYS_COLOUR_WINDOWTEXT:
253 case wxSYS_COLOUR_CAPTIONTEXT:
254 case wxSYS_COLOUR_INACTIVECAPTIONTEXT:
255 case wxSYS_COLOUR_BTNTEXT:
256 if (!gs_objects.m_colBtnText.Ok())
257 {
258 int red, green, blue;
259 if ( !GetColourFromGTKWidget(red, green, blue,
260 wxGTK_BUTTON,
261 GTK_STATE_NORMAL,
262 wxGTK_FG) )
263 {
264 red =
265 green =
266 blue = 0;
267 }
268
269 gs_objects.m_colBtnText = wxColour( red >> SHIFT,
270 green >> SHIFT,
271 blue >> SHIFT );
272 }
273 return gs_objects.m_colBtnText;
274
275 case wxSYS_COLOUR_INFOBK:
276 if (!gs_objects.m_colTooltip.Ok()) {
277 GetTooltipColors();
278 }
279 return gs_objects.m_colTooltip;
280
281 case wxSYS_COLOUR_INFOTEXT:
282 if (!gs_objects.m_colTooltipText.Ok()) {
283 GetTooltipColors();
284 }
285 return gs_objects.m_colTooltipText;
286
287 case wxSYS_COLOUR_HIGHLIGHTTEXT:
288 if (!gs_objects.m_colHighlightText.Ok())
289 {
290 wxColour hclr = GetColour(wxSYS_COLOUR_HIGHLIGHT);
291 if (hclr.Red() > 200 && hclr.Green() > 200 && hclr.Blue() > 200)
292 gs_objects.m_colHighlightText = wxColour(*wxBLACK);
293 else
294 gs_objects.m_colHighlightText = wxColour(*wxWHITE);
295 }
296 return gs_objects.m_colHighlightText;
297
298 case wxSYS_COLOUR_APPWORKSPACE:
299 return *wxWHITE; // ?
300
301 case wxSYS_COLOUR_ACTIVECAPTION:
302 case wxSYS_COLOUR_MENUHILIGHT:
303 if (!gs_objects.m_colMenuItemHighlight.Ok())
304 {
305 int red, green, blue;
306 if ( !GetColourFromGTKWidget(red, green, blue,
307 wxGTK_MENUITEM,
308 GTK_STATE_SELECTED,
309 wxGTK_BG) )
310 {
311 red =
312 green =
313 blue = 0;
314 }
315
316 gs_objects.m_colMenuItemHighlight = wxColour( red >> SHIFT,
317 green >> SHIFT,
318 blue >> SHIFT );
319 }
320 return gs_objects.m_colMenuItemHighlight;
321
322 case wxSYS_COLOUR_HOTLIGHT:
323 case wxSYS_COLOUR_GRADIENTACTIVECAPTION:
324 case wxSYS_COLOUR_GRADIENTINACTIVECAPTION:
325 // TODO
326 return *wxBLACK;
327
328 case wxSYS_COLOUR_MAX:
329 default:
330 wxFAIL_MSG( _T("unknown system colour index") );
331 }
332
333 return *wxWHITE;
334 }
335
336 wxFont wxSystemSettingsNative::GetFont( wxSystemFont index )
337 {
338 switch (index)
339 {
340 case wxSYS_OEM_FIXED_FONT:
341 case wxSYS_ANSI_FIXED_FONT:
342 case wxSYS_SYSTEM_FIXED_FONT:
343 {
344 return *wxNORMAL_FONT;
345 }
346 case wxSYS_ANSI_VAR_FONT:
347 case wxSYS_SYSTEM_FONT:
348 case wxSYS_DEVICE_DEFAULT_FONT:
349 case wxSYS_DEFAULT_GUI_FONT:
350 {
351 if (!gs_objects.m_fontSystem.Ok())
352 {
353 GtkWidget *widget = gtk_button_new();
354 GtkStyle *def = gtk_rc_get_style( widget );
355 if ( !def || !def->font_desc )
356 def = gtk_widget_get_default_style();
357 if ( def && def->font_desc )
358 {
359 wxNativeFontInfo info;
360 info.description =
361 pango_font_description_copy(def->font_desc);
362 gs_objects.m_fontSystem = wxFont(info);
363 }
364 else
365 {
366 GtkSettings *settings = gtk_settings_get_default();
367 gchar *font_name = NULL;
368 g_object_get ( settings,
369 "gtk-font-name",
370 &font_name,
371 NULL);
372 if (!font_name)
373 gs_objects.m_fontSystem = wxFont( 12, wxSWISS, wxNORMAL, wxNORMAL );
374 else
375 gs_objects.m_fontSystem = wxFont(wxString::FromAscii(font_name));
376 g_free (font_name);
377 }
378 gtk_widget_destroy( widget );
379 }
380 return gs_objects.m_fontSystem;
381 }
382
383 default:
384 return wxNullFont;
385 }
386 }
387
388 int wxSystemSettingsNative::GetMetric( wxSystemMetric index, wxWindow* win )
389 {
390 bool success = false;
391
392 guchar *data = NULL;
393 GdkWindow *window = NULL;
394 if(win && GTK_WIDGET_REALIZED(win->GetHandle()))
395 window = win->GetHandle()->window;
396
397 switch (index)
398 {
399 case wxSYS_BORDER_X:
400 case wxSYS_BORDER_Y:
401 case wxSYS_EDGE_X:
402 case wxSYS_EDGE_Y:
403 case wxSYS_FRAMESIZE_X:
404 case wxSYS_FRAMESIZE_Y:
405 // If a window is specified/realized, and it is a toplevel window, we can query from wm.
406 // The returned border thickness is outside the client area in that case.
407 if (window)
408 {
409 wxTopLevelWindow *tlw = wxDynamicCast(win, wxTopLevelWindow);
410 if (!tlw)
411 return -1; // not a tlw, not sure how to approach
412 else
413 {
414 // Check if wm supports frame extents - we can't know
415 // the border widths if it does not.
416 #if GTK_CHECK_VERSION(2,2,0)
417 if (!gtk_check_version(2,2,0))
418 {
419 if (!gdk_x11_screen_supports_net_wm_hint(
420 gdk_drawable_get_screen(window),
421 gdk_atom_intern("_NET_FRAME_EXTENTS", false) ) )
422 return -1;
423 }
424 else
425 #endif
426 {
427 if (!gdk_net_wm_supports(gdk_atom_intern("_NET_FRAME_EXTENTS", false)))
428 return -1;
429 }
430
431 // Get the frame extents from the windowmanager.
432 // In most cases the top extent is the titlebar, so we use the bottom extent
433 // for the heights.
434
435 Atom type;
436 gint format;
437 gulong nitems;
438
439 #if GTK_CHECK_VERSION(2,2,0)
440 if (!gtk_check_version(2,2,0))
441 {
442 gulong bytes_after;
443 success = (XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_drawable_get_display(window)),
444 GDK_WINDOW_XWINDOW(window),
445 gdk_x11_get_xatom_by_name_for_display (
446 gdk_drawable_get_display(window),
447 "_NET_FRAME_EXTENTS" ),
448 0, // left, right, top, bottom, CARDINAL[4]/32
449 G_MAXLONG, // size of long
450 false, // do not delete property
451 XA_CARDINAL, // 32 bit
452 &type, &format, &nitems, &bytes_after, &data
453 ) == Success);
454 }
455 #endif
456 if (success)
457 {
458 int border_return = -1;
459
460 if ((type == XA_CARDINAL) && (format == 32) && (nitems >= 4) && (data))
461 {
462 long *borders;
463 borders = (long*)data;
464 switch(index)
465 {
466 case wxSYS_BORDER_X:
467 case wxSYS_EDGE_X:
468 case wxSYS_FRAMESIZE_X:
469 border_return = borders[1]; // width of right extent
470 break;
471 default:
472 border_return = borders[3]; // height of bottom extent
473 break;
474 }
475 }
476
477 if (data)
478 XFree(data);
479
480 return border_return;
481 }
482 }
483 }
484
485 return -1; // no window specified
486
487 case wxSYS_CURSOR_X:
488 case wxSYS_CURSOR_Y:
489 #ifdef __WXGTK24__
490 if (!gtk_check_version(2,4,0))
491 {
492 if (window)
493 return gdk_display_get_default_cursor_size(gdk_drawable_get_display(window));
494 else
495 return gdk_display_get_default_cursor_size(gdk_display_get_default());
496 }
497 else
498 #endif
499 return 16;
500
501 case wxSYS_DCLICK_X:
502 case wxSYS_DCLICK_Y:
503 gint dclick_distance;
504 #if GTK_CHECK_VERSION(2,2,0)
505 if (window && !gtk_check_version(2,2,0))
506 g_object_get(gtk_settings_get_for_screen(gdk_drawable_get_screen(window)),
507 "gtk-double-click-distance", &dclick_distance, NULL);
508 else
509 #endif
510 g_object_get(gtk_settings_get_default(),
511 "gtk-double-click-distance", &dclick_distance, NULL);
512
513 return dclick_distance * 2;
514
515 case wxSYS_DRAG_X:
516 case wxSYS_DRAG_Y:
517 gint drag_threshold;
518 #if GTK_CHECK_VERSION(2,2,0)
519 if (window && !gtk_check_version(2,2,0))
520 {
521 g_object_get(
522 gtk_settings_get_for_screen(gdk_drawable_get_screen(window)),
523 "gtk-dnd-drag-threshold",
524 &drag_threshold, NULL);
525 }
526 else
527 #endif
528 {
529 g_object_get(gtk_settings_get_default(),
530 "gtk-dnd-drag-threshold", &drag_threshold, NULL);
531 }
532
533 return drag_threshold * 2;
534
535 // MBN: ditto for icons
536 case wxSYS_ICON_X: return 32;
537 case wxSYS_ICON_Y: return 32;
538
539 case wxSYS_SCREEN_X:
540 #if GTK_CHECK_VERSION(2,2,0)
541 if (window && !gtk_check_version(2,2,0))
542 return gdk_screen_get_width(gdk_drawable_get_screen(window));
543 else
544 #endif
545 return gdk_screen_width();
546
547 case wxSYS_SCREEN_Y:
548 #if GTK_CHECK_VERSION(2,2,0)
549 if (window && !gtk_check_version(2,2,0))
550 return gdk_screen_get_height(gdk_drawable_get_screen(window));
551 else
552 #endif
553 return gdk_screen_height();
554
555 case wxSYS_HSCROLL_Y: return 15;
556 case wxSYS_VSCROLL_X: return 15;
557
558 case wxSYS_CAPTION_Y:
559 if (!window)
560 // No realized window specified, and no implementation for that case yet.
561 return -1;
562
563 // Check if wm supports frame extents - we can't know the caption height if it does not.
564 #if GTK_CHECK_VERSION(2,2,0)
565 if (!gtk_check_version(2,2,0))
566 {
567 if (!gdk_x11_screen_supports_net_wm_hint(
568 gdk_drawable_get_screen(window),
569 gdk_atom_intern("_NET_FRAME_EXTENTS", false) ) )
570 return -1;
571 }
572 else
573 #endif
574 {
575 if (!gdk_net_wm_supports(gdk_atom_intern("_NET_FRAME_EXTENTS", false)))
576 return -1;
577 }
578
579 wxASSERT_MSG( wxDynamicCast(win, wxTopLevelWindow),
580 wxT("Asking for caption height of a non toplevel window") );
581
582 // Get the height of the top windowmanager border.
583 // This is the titlebar in most cases. The titlebar might be elsewhere, and
584 // we could check which is the thickest wm border to decide on which side the
585 // titlebar is, but this might lead to interesting behaviours in used code.
586 // Reconsider when we have a way to report to the user on which side it is.
587
588 Atom type;
589 gint format;
590 gulong nitems;
591
592 #if GTK_CHECK_VERSION(2,2,0)
593 if (!gtk_check_version(2,2,0))
594 {
595 gulong bytes_after;
596 success = (XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_drawable_get_display(window)),
597 GDK_WINDOW_XWINDOW(window),
598 gdk_x11_get_xatom_by_name_for_display (
599 gdk_drawable_get_display(window),
600 "_NET_FRAME_EXTENTS" ),
601 0, // left, right, top, bottom, CARDINAL[4]/32
602 G_MAXLONG, // size of long
603 false, // do not delete property
604 XA_CARDINAL, // 32 bit
605 &type, &format, &nitems, &bytes_after, &data
606 ) == Success);
607 }
608 #endif
609 if (success)
610 {
611 int caption_height = -1;
612
613 if ((type == XA_CARDINAL) && (format == 32) && (nitems >= 3) && (data))
614 {
615 long *borders;
616 borders = (long*)data;
617 caption_height = borders[2]; // top frame extent
618 }
619
620 if (data)
621 XFree(data);
622
623 return caption_height;
624 }
625
626 // Try a default approach without a window pointer, if possible
627 // ...
628
629 return -1;
630
631 case wxSYS_PENWINDOWS_PRESENT:
632 // No MS Windows for Pen computing extension available in X11 based gtk+.
633 return 0;
634
635 default:
636 return -1; // metric is unknown
637 }
638 }
639
640 bool wxSystemSettingsNative::HasFeature(wxSystemFeature index)
641 {
642 switch (index)
643 {
644 case wxSYS_CAN_ICONIZE_FRAME:
645 return false;
646
647 case wxSYS_CAN_DRAW_FRAME_DECORATIONS:
648 return true;
649
650 default:
651 return false;
652 }
653 }