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