widget/gtk/gtk2drawing.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * This file contains painting functions for each of the gtk2 widgets.
     8  * Adapted from the gtkdrawing.c, and gtk+2.0 source.
     9  */
    11 #include <gtk/gtk.h>
    12 #include <gdk/gdkprivate.h>
    13 #include <string.h>
    14 #include "gtkdrawing.h"
    15 #include "nsDebug.h"
    16 #include "prinrval.h"
    18 #include <math.h>
    20 #define XTHICKNESS(style) (style->xthickness)
    21 #define YTHICKNESS(style) (style->ythickness)
    22 #define WINDOW_IS_MAPPED(window) ((window) && GDK_IS_WINDOW(window) && gdk_window_is_visible(window))
    24 static GtkWidget* gProtoWindow;
    25 static GtkWidget* gProtoLayout;
    26 static GtkWidget* gButtonWidget;
    27 static GtkWidget* gToggleButtonWidget;
    28 static GtkWidget* gButtonArrowWidget;
    29 static GtkWidget* gCheckboxWidget;
    30 static GtkWidget* gRadiobuttonWidget;
    31 static GtkWidget* gHorizScrollbarWidget;
    32 static GtkWidget* gVertScrollbarWidget;
    33 static GtkWidget* gSpinWidget;
    34 static GtkWidget* gHScaleWidget;
    35 static GtkWidget* gVScaleWidget;
    36 static GtkWidget* gEntryWidget;
    37 static GtkWidget* gComboBoxWidget;
    38 static GtkWidget* gComboBoxButtonWidget;
    39 static GtkWidget* gComboBoxArrowWidget;
    40 static GtkWidget* gComboBoxSeparatorWidget;
    41 static GtkWidget* gComboBoxEntryWidget;
    42 static GtkWidget* gComboBoxEntryTextareaWidget;
    43 static GtkWidget* gComboBoxEntryButtonWidget;
    44 static GtkWidget* gComboBoxEntryArrowWidget;
    45 static GtkWidget* gHandleBoxWidget;
    46 static GtkWidget* gToolbarWidget;
    47 static GtkWidget* gFrameWidget;
    48 static GtkWidget* gStatusbarWidget;
    49 static GtkWidget* gProgressWidget;
    50 static GtkWidget* gTabWidget;
    51 static GtkWidget* gTooltipWidget;
    52 static GtkWidget* gMenuBarWidget;
    53 static GtkWidget* gMenuBarItemWidget;
    54 static GtkWidget* gMenuPopupWidget;
    55 static GtkWidget* gMenuItemWidget;
    56 static GtkWidget* gImageMenuItemWidget;
    57 static GtkWidget* gCheckMenuItemWidget;
    58 static GtkWidget* gTreeViewWidget;
    59 static GtkTreeViewColumn* gMiddleTreeViewColumn;
    60 static GtkWidget* gTreeHeaderCellWidget;
    61 static GtkWidget* gTreeHeaderSortArrowWidget;
    62 static GtkWidget* gExpanderWidget;
    63 static GtkWidget* gToolbarSeparatorWidget;
    64 static GtkWidget* gMenuSeparatorWidget;
    65 static GtkWidget* gHPanedWidget;
    66 static GtkWidget* gVPanedWidget;
    67 static GtkWidget* gScrolledWindowWidget;
    69 static style_prop_t style_prop_func;
    70 static gboolean have_arrow_scaling;
    71 static gboolean is_initialized;
    73 /* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine
    74    that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific
    75    things they may want to do. */
    76 static void
    77 moz_gtk_set_widget_name(GtkWidget* widget)
    78 {
    79     gtk_widget_set_name(widget, "MozillaGtkWidget");
    80 }
    82 gint
    83 moz_gtk_enable_style_props(style_prop_t styleGetProp)
    84 {
    85     style_prop_func = styleGetProp;
    86     return MOZ_GTK_SUCCESS;
    87 }
    89 static gint
    90 ensure_window_widget()
    91 {
    92     if (!gProtoWindow) {
    93         gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
    94         gtk_widget_realize(gProtoWindow);
    95         moz_gtk_set_widget_name(gProtoWindow);
    96     }
    97     return MOZ_GTK_SUCCESS;
    98 }
   100 static gint
   101 setup_widget_prototype(GtkWidget* widget)
   102 {
   103     ensure_window_widget();
   104     if (!gProtoLayout) {
   105         gProtoLayout = gtk_fixed_new();
   106         gtk_container_add(GTK_CONTAINER(gProtoWindow), gProtoLayout);
   107     }
   109     gtk_container_add(GTK_CONTAINER(gProtoLayout), widget);
   110     gtk_widget_realize(widget);
   111     g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   112     return MOZ_GTK_SUCCESS;
   113 }
   115 static gint
   116 ensure_button_widget()
   117 {
   118     if (!gButtonWidget) {
   119         gButtonWidget = gtk_button_new_with_label("M");
   120         setup_widget_prototype(gButtonWidget);
   121     }
   122     return MOZ_GTK_SUCCESS;
   123 }
   125 static gint
   126 ensure_hpaned_widget()
   127 {
   128     if (!gHPanedWidget) {
   129         gHPanedWidget = gtk_hpaned_new();
   130         setup_widget_prototype(gHPanedWidget);
   131     }
   132     return MOZ_GTK_SUCCESS;
   133 }
   135 static gint
   136 ensure_vpaned_widget()
   137 {
   138     if (!gVPanedWidget) {
   139         gVPanedWidget = gtk_vpaned_new();
   140         setup_widget_prototype(gVPanedWidget);
   141     }
   142     return MOZ_GTK_SUCCESS;
   143 }
   145 static gint
   146 ensure_toggle_button_widget()
   147 {
   148     if (!gToggleButtonWidget) {
   149         gToggleButtonWidget = gtk_toggle_button_new();
   150         setup_widget_prototype(gToggleButtonWidget);
   151         /* toggle button must be set active to get the right style on hover. */
   152         GTK_TOGGLE_BUTTON(gToggleButtonWidget)->active = TRUE;
   153   }
   154   return MOZ_GTK_SUCCESS;
   155 }
   157 static gint
   158 ensure_button_arrow_widget()
   159 {
   160     if (!gButtonArrowWidget) {
   161         ensure_toggle_button_widget();
   163         gButtonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
   164         gtk_container_add(GTK_CONTAINER(gToggleButtonWidget), gButtonArrowWidget);
   165         gtk_widget_realize(gButtonArrowWidget);
   166     }
   167     return MOZ_GTK_SUCCESS;
   168 }
   170 static gint
   171 ensure_checkbox_widget()
   172 {
   173     if (!gCheckboxWidget) {
   174         gCheckboxWidget = gtk_check_button_new_with_label("M");
   175         setup_widget_prototype(gCheckboxWidget);
   176     }
   177     return MOZ_GTK_SUCCESS;
   178 }
   180 static gint
   181 ensure_radiobutton_widget()
   182 {
   183     if (!gRadiobuttonWidget) {
   184         gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M");
   185         setup_widget_prototype(gRadiobuttonWidget);
   186     }
   187     return MOZ_GTK_SUCCESS;
   188 }
   190 static gint
   191 ensure_scrollbar_widget()
   192 {
   193     if (!gVertScrollbarWidget) {
   194         gVertScrollbarWidget = gtk_vscrollbar_new(NULL);
   195         setup_widget_prototype(gVertScrollbarWidget);
   196     }
   197     if (!gHorizScrollbarWidget) {
   198         gHorizScrollbarWidget = gtk_hscrollbar_new(NULL);
   199         setup_widget_prototype(gHorizScrollbarWidget);
   200     }
   201     return MOZ_GTK_SUCCESS;
   202 }
   204 static gint
   205 ensure_spin_widget()
   206 {
   207   if (!gSpinWidget) {
   208     gSpinWidget = gtk_spin_button_new(NULL, 1, 0);
   209     setup_widget_prototype(gSpinWidget);
   210   }
   211   return MOZ_GTK_SUCCESS;
   212 }
   214 static gint
   215 ensure_scale_widget()
   216 {
   217   if (!gHScaleWidget) {
   218     gHScaleWidget = gtk_hscale_new(NULL);
   219     setup_widget_prototype(gHScaleWidget);
   220   }
   221   if (!gVScaleWidget) {
   222     gVScaleWidget = gtk_vscale_new(NULL);
   223     setup_widget_prototype(gVScaleWidget);
   224   }
   225   return MOZ_GTK_SUCCESS;
   226 }
   228 static gint
   229 ensure_entry_widget()
   230 {
   231     if (!gEntryWidget) {
   232         gEntryWidget = gtk_entry_new();
   233         setup_widget_prototype(gEntryWidget);
   234     }
   235     return MOZ_GTK_SUCCESS;
   236 }
   238 /* We need to have pointers to the inner widgets (button, separator, arrow)
   239  * of the ComboBox to get the correct rendering from theme engines which
   240  * special cases their look. Since the inner layout can change, we ask GTK
   241  * to NULL our pointers when they are about to become invalid because the
   242  * corresponding widgets don't exist anymore. It's the role of
   243  * g_object_add_weak_pointer().
   244  * Note that if we don't find the inner widgets (which shouldn't happen), we
   245  * fallback to use generic "non-inner" widgets, and they don't need that kind
   246  * of weak pointer since they are explicit children of gProtoWindow and as
   247  * such GTK holds a strong reference to them. */
   248 static void
   249 moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data)
   250 {
   251     if (GTK_IS_TOGGLE_BUTTON(widget)) {
   252         gComboBoxButtonWidget = widget;
   253         g_object_add_weak_pointer(G_OBJECT(widget),
   254                                   (gpointer) &gComboBoxButtonWidget);
   255         gtk_widget_realize(widget);
   256         g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   257     }
   258 }
   260 static void
   261 moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget,
   262                                            gpointer client_data)
   263 {
   264     if (GTK_IS_SEPARATOR(widget)) {
   265         gComboBoxSeparatorWidget = widget;
   266         g_object_add_weak_pointer(G_OBJECT(widget),
   267                                   (gpointer) &gComboBoxSeparatorWidget);
   268     } else if (GTK_IS_ARROW(widget)) {
   269         gComboBoxArrowWidget = widget;
   270         g_object_add_weak_pointer(G_OBJECT(widget),
   271                                   (gpointer) &gComboBoxArrowWidget);
   272     } else
   273         return;
   274     gtk_widget_realize(widget);
   275     g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   276 }
   278 static gint
   279 ensure_combo_box_widgets()
   280 {
   281     GtkWidget* buttonChild;
   283     if (gComboBoxButtonWidget && gComboBoxArrowWidget)
   284         return MOZ_GTK_SUCCESS;
   286     /* Create a ComboBox if needed */
   287     if (!gComboBoxWidget) {
   288         gComboBoxWidget = gtk_combo_box_new();
   289         setup_widget_prototype(gComboBoxWidget);
   290     }
   292     /* Get its inner Button */
   293     gtk_container_forall(GTK_CONTAINER(gComboBoxWidget),
   294                          moz_gtk_get_combo_box_inner_button,
   295                          NULL);
   297     if (gComboBoxButtonWidget) {
   298         /* Get the widgets inside the Button */
   299         buttonChild = GTK_BIN(gComboBoxButtonWidget)->child;
   300         if (GTK_IS_HBOX(buttonChild)) {
   301             /* appears-as-list = FALSE, cell-view = TRUE; the button
   302              * contains an hbox. This hbox is there because the ComboBox
   303              * needs to place a cell renderer, a separator, and an arrow in
   304              * the button when appears-as-list is FALSE. */
   305             gtk_container_forall(GTK_CONTAINER(buttonChild),
   306                                  moz_gtk_get_combo_box_button_inner_widgets,
   307                                  NULL);
   308         } else if(GTK_IS_ARROW(buttonChild)) {
   309             /* appears-as-list = TRUE, or cell-view = FALSE;
   310              * the button only contains an arrow */
   311             gComboBoxArrowWidget = buttonChild;
   312             g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
   313                                       &gComboBoxArrowWidget);
   314             gtk_widget_realize(gComboBoxArrowWidget);
   315             g_object_set_data(G_OBJECT(gComboBoxArrowWidget),
   316                               "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   317         }
   318     } else {
   319         /* Shouldn't be reached with current internal gtk implementation; we
   320          * use a generic toggle button as last resort fallback to avoid
   321          * crashing. */
   322         ensure_toggle_button_widget();
   323         gComboBoxButtonWidget = gToggleButtonWidget;
   324     }
   326     if (!gComboBoxArrowWidget) {
   327         /* Shouldn't be reached with current internal gtk implementation;
   328          * we gButtonArrowWidget as last resort fallback to avoid
   329          * crashing. */
   330         ensure_button_arrow_widget();
   331         gComboBoxArrowWidget = gButtonArrowWidget;
   332     }
   334     /* We don't test the validity of gComboBoxSeparatorWidget since there
   335      * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it
   336      * is invalid we just won't paint it. */
   338     return MOZ_GTK_SUCCESS;
   339 }
   341 /* We need to have pointers to the inner widgets (entry, button, arrow) of
   342  * the ComboBoxEntry to get the correct rendering from theme engines which
   343  * special cases their look. Since the inner layout can change, we ask GTK
   344  * to NULL our pointers when they are about to become invalid because the
   345  * corresponding widgets don't exist anymore. It's the role of
   346  * g_object_add_weak_pointer().
   347  * Note that if we don't find the inner widgets (which shouldn't happen), we
   348  * fallback to use generic "non-inner" widgets, and they don't need that kind
   349  * of weak pointer since they are explicit children of gProtoWindow and as
   350  * such GTK holds a strong reference to them. */
   351 static void
   352 moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget,
   353                                           gpointer client_data)
   354 {
   355     if (GTK_IS_TOGGLE_BUTTON(widget)) {
   356         gComboBoxEntryButtonWidget = widget;
   357         g_object_add_weak_pointer(G_OBJECT(widget),
   358                                   (gpointer) &gComboBoxEntryButtonWidget);
   359     } else if (GTK_IS_ENTRY(widget)) {
   360         gComboBoxEntryTextareaWidget = widget;
   361         g_object_add_weak_pointer(G_OBJECT(widget),
   362                                   (gpointer) &gComboBoxEntryTextareaWidget);
   363     } else
   364         return;
   365     gtk_widget_realize(widget);
   366     g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   367 }
   369 static void
   370 moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data)
   371 {
   372     if (GTK_IS_ARROW(widget)) {
   373         gComboBoxEntryArrowWidget = widget;
   374         g_object_add_weak_pointer(G_OBJECT(widget),
   375                                   (gpointer) &gComboBoxEntryArrowWidget);
   376         gtk_widget_realize(widget);
   377         g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   378     }
   379 }
   381 static gint
   382 ensure_combo_box_entry_widgets()
   383 {
   384     GtkWidget* buttonChild;
   386     if (gComboBoxEntryTextareaWidget &&
   387             gComboBoxEntryButtonWidget &&
   388             gComboBoxEntryArrowWidget)
   389         return MOZ_GTK_SUCCESS;
   391     /* Create a ComboBoxEntry if needed */
   392     if (!gComboBoxEntryWidget) {
   393         gComboBoxEntryWidget = gtk_combo_box_entry_new();
   394         setup_widget_prototype(gComboBoxEntryWidget);
   395     }
   397     /* Get its inner Entry and Button */
   398     gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget),
   399                          moz_gtk_get_combo_box_entry_inner_widgets,
   400                          NULL);
   402     if (!gComboBoxEntryTextareaWidget) {
   403         ensure_entry_widget();
   404         gComboBoxEntryTextareaWidget = gEntryWidget;
   405     }
   407     if (gComboBoxEntryButtonWidget) {
   408         /* Get the Arrow inside the Button */
   409         buttonChild = GTK_BIN(gComboBoxEntryButtonWidget)->child;
   410         if (GTK_IS_HBOX(buttonChild)) {
   411             /* appears-as-list = FALSE, cell-view = TRUE; the button
   412              * contains an hbox. This hbox is there because ComboBoxEntry
   413              * inherits from ComboBox which needs to place a cell renderer,
   414              * a separator, and an arrow in the button when appears-as-list
   415              * is FALSE. Here the hbox should only contain an arrow, since
   416              * a ComboBoxEntry doesn't need all those widgets in the
   417              * button. */
   418             gtk_container_forall(GTK_CONTAINER(buttonChild),
   419                                  moz_gtk_get_combo_box_entry_arrow,
   420                                  NULL);
   421         } else if(GTK_IS_ARROW(buttonChild)) {
   422             /* appears-as-list = TRUE, or cell-view = FALSE;
   423              * the button only contains an arrow */
   424             gComboBoxEntryArrowWidget = buttonChild;
   425             g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
   426                                       &gComboBoxEntryArrowWidget);
   427             gtk_widget_realize(gComboBoxEntryArrowWidget);
   428             g_object_set_data(G_OBJECT(gComboBoxEntryArrowWidget),
   429                               "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   430         }
   431     } else {
   432         /* Shouldn't be reached with current internal gtk implementation;
   433          * we use a generic toggle button as last resort fallback to avoid
   434          * crashing. */
   435         ensure_toggle_button_widget();
   436         gComboBoxEntryButtonWidget = gToggleButtonWidget;
   437     }
   439     if (!gComboBoxEntryArrowWidget) {
   440         /* Shouldn't be reached with current internal gtk implementation;
   441          * we gButtonArrowWidget as last resort fallback to avoid
   442          * crashing. */
   443         ensure_button_arrow_widget();
   444         gComboBoxEntryArrowWidget = gButtonArrowWidget;
   445     }
   447     return MOZ_GTK_SUCCESS;
   448 }
   451 static gint
   452 ensure_handlebox_widget()
   453 {
   454     if (!gHandleBoxWidget) {
   455         gHandleBoxWidget = gtk_handle_box_new();
   456         setup_widget_prototype(gHandleBoxWidget);
   457     }
   458     return MOZ_GTK_SUCCESS;
   459 }
   461 static gint
   462 ensure_toolbar_widget()
   463 {
   464     if (!gToolbarWidget) {
   465         ensure_handlebox_widget();
   466         gToolbarWidget = gtk_toolbar_new();
   467         gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget);
   468         gtk_widget_realize(gToolbarWidget);
   469         g_object_set_data(G_OBJECT(gToolbarWidget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   470     }
   471     return MOZ_GTK_SUCCESS;
   472 }
   474 static gint
   475 ensure_toolbar_separator_widget()
   476 {
   477     if (!gToolbarSeparatorWidget) {
   478         ensure_toolbar_widget();
   479         gToolbarSeparatorWidget = GTK_WIDGET(gtk_separator_tool_item_new());
   480         setup_widget_prototype(gToolbarSeparatorWidget);
   481     }
   482     return MOZ_GTK_SUCCESS;
   483 }
   485 static gint
   486 ensure_tooltip_widget()
   487 {
   488     if (!gTooltipWidget) {
   489         gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP);
   490         gtk_widget_realize(gTooltipWidget);
   491         moz_gtk_set_widget_name(gTooltipWidget);
   492     }
   493     return MOZ_GTK_SUCCESS;
   494 }
   496 static gint
   497 ensure_tab_widget()
   498 {
   499     if (!gTabWidget) {
   500         gTabWidget = gtk_notebook_new();
   501         setup_widget_prototype(gTabWidget);
   502     }
   503     return MOZ_GTK_SUCCESS;
   504 }
   506 static gint
   507 ensure_progress_widget()
   508 {
   509     if (!gProgressWidget) {
   510         gProgressWidget = gtk_progress_bar_new();
   511         setup_widget_prototype(gProgressWidget);
   512     }
   513     return MOZ_GTK_SUCCESS;
   514 }
   516 static gint
   517 ensure_statusbar_widget()
   518 {
   519     if (!gStatusbarWidget) {
   520       gStatusbarWidget = gtk_statusbar_new();
   521       setup_widget_prototype(gStatusbarWidget);
   522     }
   523     return MOZ_GTK_SUCCESS;
   524 }
   526 static gint
   527 ensure_frame_widget()
   528 {
   529     if (!gFrameWidget) {
   530         ensure_statusbar_widget();
   531         gFrameWidget = gtk_frame_new(NULL);
   532         gtk_container_add(GTK_CONTAINER(gStatusbarWidget), gFrameWidget);
   533         gtk_widget_realize(gFrameWidget);
   534     }
   535     return MOZ_GTK_SUCCESS;
   536 }
   538 static gint
   539 ensure_menu_bar_widget()
   540 {
   541     if (!gMenuBarWidget) {
   542         gMenuBarWidget = gtk_menu_bar_new();
   543         setup_widget_prototype(gMenuBarWidget);
   544     }
   545     return MOZ_GTK_SUCCESS;
   546 }
   548 static gint
   549 ensure_menu_bar_item_widget()
   550 {
   551     if (!gMenuBarItemWidget) {
   552         ensure_menu_bar_widget();
   553         gMenuBarItemWidget = gtk_menu_item_new();
   554         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget),
   555                               gMenuBarItemWidget);
   556         gtk_widget_realize(gMenuBarItemWidget);
   557         g_object_set_data(G_OBJECT(gMenuBarItemWidget),
   558                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   559     }
   560     return MOZ_GTK_SUCCESS;
   561 }
   563 static gint
   564 ensure_menu_popup_widget()
   565 {
   566     if (!gMenuPopupWidget) {
   567         ensure_menu_bar_item_widget();
   568         gMenuPopupWidget = gtk_menu_new();
   569         gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget),
   570                                   gMenuPopupWidget);
   571         gtk_widget_realize(gMenuPopupWidget);
   572         g_object_set_data(G_OBJECT(gMenuPopupWidget),
   573                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   574     }
   575     return MOZ_GTK_SUCCESS;
   576 }
   578 static gint
   579 ensure_menu_item_widget()
   580 {
   581     if (!gMenuItemWidget) {
   582         ensure_menu_popup_widget();
   583         gMenuItemWidget = gtk_menu_item_new_with_label("M");
   584         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
   585                               gMenuItemWidget);
   586         gtk_widget_realize(gMenuItemWidget);
   587         g_object_set_data(G_OBJECT(gMenuItemWidget),
   588                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   589     }
   590     return MOZ_GTK_SUCCESS;
   591 }
   593 static gint
   594 ensure_image_menu_item_widget()
   595 {
   596     if (!gImageMenuItemWidget) {
   597         ensure_menu_popup_widget();
   598         gImageMenuItemWidget = gtk_image_menu_item_new();
   599         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
   600                               gImageMenuItemWidget);
   601         gtk_widget_realize(gImageMenuItemWidget);
   602         g_object_set_data(G_OBJECT(gImageMenuItemWidget),
   603                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   604     }
   605     return MOZ_GTK_SUCCESS;
   606 }
   608 static gint
   609 ensure_menu_separator_widget()
   610 {
   611     if (!gMenuSeparatorWidget) {
   612         ensure_menu_popup_widget();
   613         gMenuSeparatorWidget = gtk_separator_menu_item_new();
   614         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
   615                               gMenuSeparatorWidget);
   616         gtk_widget_realize(gMenuSeparatorWidget);
   617         g_object_set_data(G_OBJECT(gMenuSeparatorWidget),
   618                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   619     }
   620     return MOZ_GTK_SUCCESS;
   621 }
   623 static gint
   624 ensure_check_menu_item_widget()
   625 {
   626     if (!gCheckMenuItemWidget) {
   627         ensure_menu_popup_widget();
   628         gCheckMenuItemWidget = gtk_check_menu_item_new_with_label("M");
   629         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
   630                               gCheckMenuItemWidget);
   631         gtk_widget_realize(gCheckMenuItemWidget);
   632         g_object_set_data(G_OBJECT(gCheckMenuItemWidget),
   633                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   634     }
   635     return MOZ_GTK_SUCCESS;
   636 }
   638 static gint
   639 ensure_tree_view_widget()
   640 {
   641     if (!gTreeViewWidget) {
   642         gTreeViewWidget = gtk_tree_view_new();
   643         setup_widget_prototype(gTreeViewWidget);
   644     }
   645     return MOZ_GTK_SUCCESS;
   646 }
   648 static gint
   649 ensure_tree_header_cell_widget()
   650 {
   651     if(!gTreeHeaderCellWidget) {
   652         /*
   653          * Some GTK engines paint the first and last cell
   654          * of a TreeView header with a highlight.
   655          * Since we do not know where our widget will be relative
   656          * to the other buttons in the TreeView header, we must
   657          * paint it as a button that is between two others,
   658          * thus ensuring it is neither the first or last button
   659          * in the header.
   660          * GTK doesn't give us a way to do this explicitly,
   661          * so we must paint with a button that is between two
   662          * others.
   663          */
   665         GtkTreeViewColumn* firstTreeViewColumn;
   666         GtkTreeViewColumn* lastTreeViewColumn;
   668         ensure_tree_view_widget();
   670         /* Create and append our three columns */
   671         firstTreeViewColumn = gtk_tree_view_column_new();
   672         gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
   673         gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn);
   675         gMiddleTreeViewColumn = gtk_tree_view_column_new();
   676         gtk_tree_view_column_set_title(gMiddleTreeViewColumn, "M");
   677         gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget),
   678                                     gMiddleTreeViewColumn);
   680         lastTreeViewColumn = gtk_tree_view_column_new();
   681         gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
   682         gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn);
   684         /* Use the middle column's header for our button */
   685         gTreeHeaderCellWidget = gMiddleTreeViewColumn->button;
   686         gTreeHeaderSortArrowWidget = gMiddleTreeViewColumn->arrow;
   687         g_object_set_data(G_OBJECT(gTreeHeaderCellWidget),
   688                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   689         g_object_set_data(G_OBJECT(gTreeHeaderSortArrowWidget),
   690                           "transparent-bg-hint", GINT_TO_POINTER(TRUE));
   691     }
   692     return MOZ_GTK_SUCCESS;
   693 }
   695 static gint
   696 ensure_expander_widget()
   697 {
   698     if (!gExpanderWidget) {
   699         gExpanderWidget = gtk_expander_new("M");
   700         setup_widget_prototype(gExpanderWidget);
   701     }
   702     return MOZ_GTK_SUCCESS;
   703 }
   705 static gint
   706 ensure_scrolled_window_widget()
   707 {
   708     if (!gScrolledWindowWidget) {
   709         gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL);
   710         setup_widget_prototype(gScrolledWindowWidget);
   711     }
   712     return MOZ_GTK_SUCCESS;
   713 }
   715 static GtkStateType
   716 ConvertGtkState(GtkWidgetState* state)
   717 {
   718     if (state->disabled)
   719         return GTK_STATE_INSENSITIVE;
   720     else if (state->depressed)
   721         return (state->inHover ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
   722     else if (state->inHover)
   723         return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
   724     else
   725         return GTK_STATE_NORMAL;
   726 }
   728 static gint
   729 TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin)
   730 {
   731     int i;
   732     /* there are 5 gc's in each array, for each of the widget states */
   733     for (i = 0; i < 5; ++i)
   734         gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin);
   735     return MOZ_GTK_SUCCESS;
   736 }
   738 static gint
   739 TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin)
   740 {
   741     TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin);
   742     TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin);
   743     TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin);
   744     TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin);
   745     TSOffsetStyleGCArray(style->mid_gc, xorigin, yorigin);
   746     TSOffsetStyleGCArray(style->text_gc, xorigin, yorigin);
   747     TSOffsetStyleGCArray(style->base_gc, xorigin, yorigin);
   748     gdk_gc_set_ts_origin(style->black_gc, xorigin, yorigin);
   749     gdk_gc_set_ts_origin(style->white_gc, xorigin, yorigin);
   750     return MOZ_GTK_SUCCESS;
   751 }
   753 gint
   754 moz_gtk_init()
   755 {
   756     GtkWidgetClass *entry_class;
   758     if (is_initialized)
   759         return MOZ_GTK_SUCCESS;
   761     is_initialized = TRUE;
   762     have_arrow_scaling = (gtk_major_version > 2 ||
   763                           (gtk_major_version == 2 && gtk_minor_version >= 12));
   765     /* Add style property to GtkEntry.
   766      * Adding the style property to the normal GtkEntry class means that it
   767      * will work without issues inside GtkComboBox and for Spinbuttons. */
   768     entry_class = g_type_class_ref(GTK_TYPE_ENTRY);
   769     gtk_widget_class_install_style_property(entry_class,
   770         g_param_spec_boolean("honors-transparent-bg-hint",
   771                              "Transparent BG enabling flag",
   772                              "If TRUE, the theme is able to draw the GtkEntry on non-prefilled background.",
   773                              FALSE,
   774                              G_PARAM_READWRITE));
   776     return MOZ_GTK_SUCCESS;
   777 }
   779 GdkColormap*
   780 moz_gtk_widget_get_colormap()
   781 {
   782     /* Child widgets inherit the colormap from the GtkWindow. */
   783     ensure_window_widget();
   784     return gtk_widget_get_colormap(gProtoWindow);
   785 }
   787 gint
   788 moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing)
   789 {
   790     ensure_checkbox_widget();
   792     gtk_widget_style_get (gCheckboxWidget,
   793                           "indicator_size", indicator_size,
   794                           "indicator_spacing", indicator_spacing,
   795                           NULL);
   797     return MOZ_GTK_SUCCESS;
   798 }
   800 gint
   801 moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing)
   802 {
   803     ensure_radiobutton_widget();
   805     gtk_widget_style_get (gRadiobuttonWidget,
   806                           "indicator_size", indicator_size,
   807                           "indicator_spacing", indicator_spacing,
   808                           NULL);
   810     return MOZ_GTK_SUCCESS;
   811 }
   813 gint
   814 moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus,
   815                          gint* focus_width, gint* focus_pad) 
   816 {
   817     gtk_widget_style_get (widget,
   818                           "interior-focus", interior_focus,
   819                           "focus-line-width", focus_width,
   820                           "focus-padding", focus_pad,
   821                           NULL);
   823     return MOZ_GTK_SUCCESS;
   824 }
   826 gint
   827 moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding)
   828 {
   829     ensure_menu_item_widget();
   831     gtk_widget_style_get (gMenuItemWidget,
   832                           "horizontal-padding", horizontal_padding,
   833                           NULL);
   835     return MOZ_GTK_SUCCESS;
   836 }
   838 gint
   839 moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding)
   840 {
   841     ensure_check_menu_item_widget();
   843     gtk_widget_style_get (gCheckMenuItemWidget,
   844                           "horizontal-padding", horizontal_padding,
   845                           NULL);
   847     return MOZ_GTK_SUCCESS;
   848 }
   850 gint
   851 moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left,
   852                                     gint* border_bottom, gint* border_right)
   853 {
   854     GtkBorder* default_outside_border;
   856     ensure_button_widget();
   857     gtk_widget_style_get(gButtonWidget,
   858                          "default-outside-border", &default_outside_border,
   859                          NULL);
   861     if (default_outside_border) {
   862         *border_top = default_outside_border->top;
   863         *border_left = default_outside_border->left;
   864         *border_bottom = default_outside_border->bottom;
   865         *border_right = default_outside_border->right;
   866         gtk_border_free(default_outside_border);
   867     } else {
   868         *border_top = *border_left = *border_bottom = *border_right = 0;
   869     }
   870     return MOZ_GTK_SUCCESS;
   871 }
   873 static gint
   874 moz_gtk_button_get_default_border(gint* border_top, gint* border_left,
   875                                   gint* border_bottom, gint* border_right)
   876 {
   877     GtkBorder* default_border;
   879     ensure_button_widget();
   880     gtk_widget_style_get(gButtonWidget,
   881                          "default-border", &default_border,
   882                          NULL);
   884     if (default_border) {
   885         *border_top = default_border->top;
   886         *border_left = default_border->left;
   887         *border_bottom = default_border->bottom;
   888         *border_right = default_border->right;
   889         gtk_border_free(default_border);
   890     } else {
   891         /* see gtkbutton.c */
   892         *border_top = *border_left = *border_bottom = *border_right = 1;
   893     }
   894     return MOZ_GTK_SUCCESS;
   895 }
   897 gint
   898 moz_gtk_splitter_get_metrics(gint orientation, gint* size)
   899 {
   900     if (orientation == GTK_ORIENTATION_HORIZONTAL) {
   901         ensure_hpaned_widget();
   902         gtk_widget_style_get(gHPanedWidget, "handle_size", size, NULL);
   903     } else {
   904         ensure_vpaned_widget();
   905         gtk_widget_style_get(gVPanedWidget, "handle_size", size, NULL);
   906     }
   907     return MOZ_GTK_SUCCESS;
   908 }
   910 gint
   911 moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border)
   912 {
   913     static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
   914     GtkBorder *tmp_border;
   916     gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL);
   918     if (tmp_border) {
   919         *inner_border = *tmp_border;
   920         gtk_border_free(tmp_border);
   921     }
   922     else
   923         *inner_border = default_inner_border;
   925     return MOZ_GTK_SUCCESS;
   926 }
   928 static gint
   929 moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
   930                      GdkRectangle* cliprect, GtkWidgetState* state,
   931                      GtkReliefStyle relief, GtkWidget* widget,
   932                      GtkTextDirection direction)
   933 {
   934     GtkShadowType shadow_type;
   935     GtkStyle* style = widget->style;
   936     GtkStateType button_state = ConvertGtkState(state);
   937     gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
   939     gboolean interior_focus;
   940     gint focus_width, focus_pad;
   942     moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad);
   944     if (WINDOW_IS_MAPPED(drawable)) {
   945         gdk_window_set_back_pixmap(drawable, NULL, TRUE);
   946         gdk_window_clear_area(drawable, cliprect->x, cliprect->y,
   947                               cliprect->width, cliprect->height);
   948     }
   950     gtk_widget_set_state(widget, button_state);
   951     gtk_widget_set_direction(widget, direction);
   953     if (state->isDefault)
   954         GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_DEFAULT);
   956     GTK_BUTTON(widget)->relief = relief;
   958     /* Some theme engines love to cause us pain in that gtk_paint_focus is a
   959        no-op on buttons and button-like widgets. They only listen to this flag. */
   960     if (state->focused && !state->disabled)
   961         GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
   963     if (!interior_focus && state->focused) {
   964         x += focus_width + focus_pad;
   965         y += focus_width + focus_pad;
   966         width -= 2 * (focus_width + focus_pad);
   967         height -= 2 * (focus_width + focus_pad);
   968     }
   970     shadow_type = button_state == GTK_STATE_ACTIVE ||
   971                       state->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
   973     if (state->isDefault && relief == GTK_RELIEF_NORMAL) {
   974         /* handle default borders both outside and inside the button */
   975         gint default_top, default_left, default_bottom, default_right;
   976         moz_gtk_button_get_default_overflow(&default_top, &default_left,
   977                                             &default_bottom, &default_right);
   978         x -= default_left;
   979         y -= default_top;
   980         width += default_left + default_right;
   981         height += default_top + default_bottom;
   982         gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, cliprect,
   983                       widget, "buttondefault", x, y, width, height);
   985         moz_gtk_button_get_default_border(&default_top, &default_left,
   986                                           &default_bottom, &default_right);
   987         x += default_left;
   988         y += default_top;
   989         width -= (default_left + default_right);
   990         height -= (default_top + default_bottom);
   991     }
   993     if (relief != GTK_RELIEF_NONE || state->depressed ||
   994         (button_state != GTK_STATE_NORMAL &&
   995          button_state != GTK_STATE_INSENSITIVE)) {
   996         TSOffsetStyleGCs(style, x, y);
   997         /* the following line can trigger an assertion (Crux theme)
   998            file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
   999            assertion `GDK_IS_WINDOW (window)' failed */
  1000         gtk_paint_box(style, drawable, button_state, shadow_type, cliprect,
  1001                       widget, "button", x, y, width, height);
  1004     if (state->focused) {
  1005         if (interior_focus) {
  1006             x += widget->style->xthickness + focus_pad;
  1007             y += widget->style->ythickness + focus_pad;
  1008             width -= 2 * (widget->style->xthickness + focus_pad);
  1009             height -= 2 * (widget->style->ythickness + focus_pad);
  1010         } else {
  1011             x -= focus_width + focus_pad;
  1012             y -= focus_width + focus_pad;
  1013             width += 2 * (focus_width + focus_pad);
  1014             height += 2 * (focus_width + focus_pad);
  1017         TSOffsetStyleGCs(style, x, y);
  1018         gtk_paint_focus(style, drawable, button_state, cliprect,
  1019                         widget, "button", x, y, width, height);
  1022     GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_DEFAULT);
  1023     GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
  1024     return MOZ_GTK_SUCCESS;
  1027 static gint
  1028 moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1029                      GdkRectangle* cliprect, GtkWidgetState* state,
  1030                      gboolean selected, gboolean inconsistent,
  1031                      gboolean isradio, GtkTextDirection direction)
  1033     GtkStateType state_type = ConvertGtkState(state);
  1034     GtkShadowType shadow_type = (selected)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
  1035     gint indicator_size, indicator_spacing;
  1036     gint x, y, width, height;
  1037     gint focus_x, focus_y, focus_width, focus_height;
  1038     GtkWidget *w;
  1039     GtkStyle *style;
  1041     if (isradio) {
  1042         moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
  1043         w = gRadiobuttonWidget;
  1044     } else {
  1045         moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
  1046         w = gCheckboxWidget;
  1049     // XXX we should assert rect->height >= indicator_size too
  1050     // after bug 369581 is fixed.
  1051     NS_ASSERTION(rect->width >= indicator_size,
  1052                  "GetMinimumWidgetSize was ignored");
  1054     // Paint it center aligned in the rect.
  1055     x = rect->x + (rect->width - indicator_size) / 2;
  1056     y = rect->y + (rect->height - indicator_size) / 2;
  1057     width = indicator_size;
  1058     height = indicator_size;
  1060     focus_x = x - indicator_spacing;
  1061     focus_y = y - indicator_spacing;
  1062     focus_width = width + 2 * indicator_spacing;
  1063     focus_height = height + 2 * indicator_spacing;
  1065     style = w->style;
  1066     TSOffsetStyleGCs(style, x, y);
  1068     gtk_widget_set_sensitive(w, !state->disabled);
  1069     gtk_widget_set_direction(w, direction);
  1070     GTK_TOGGLE_BUTTON(w)->active = selected;
  1072     if (isradio) {
  1073         gtk_paint_option(style, drawable, state_type, shadow_type, cliprect,
  1074                          gRadiobuttonWidget, "radiobutton", x, y,
  1075                          width, height);
  1076         if (state->focused) {
  1077             gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect,
  1078                             gRadiobuttonWidget, "radiobutton", focus_x, focus_y,
  1079                             focus_width, focus_height);
  1082     else {
  1083        /*
  1084         * 'indeterminate' type on checkboxes. In GTK, the shadow type
  1085         * must also be changed for the state to be drawn.
  1086         */
  1087         if (inconsistent) {
  1088             gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE);
  1089             shadow_type = GTK_SHADOW_ETCHED_IN;
  1090         } else {
  1091             gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE);
  1094         gtk_paint_check(style, drawable, state_type, shadow_type, cliprect, 
  1095                         gCheckboxWidget, "checkbutton", x, y, width, height);
  1096         if (state->focused) {
  1097             gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect,
  1098                             gCheckboxWidget, "checkbutton", focus_x, focus_y,
  1099                             focus_width, focus_height);
  1103     return MOZ_GTK_SUCCESS;
  1106 static gint
  1107 calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect,
  1108                             GdkRectangle* inner_rect,
  1109                             GtkTextDirection direction,
  1110                             gboolean ignore_focus)
  1112     GtkBorder inner_border;
  1113     gboolean interior_focus;
  1114     gint focus_width, focus_pad;
  1115     GtkStyle* style;
  1117     style = button->style;
  1119     /* This mirrors gtkbutton's child positioning */
  1120     moz_gtk_button_get_inner_border(button, &inner_border);
  1121     moz_gtk_widget_get_focus(button, &interior_focus,
  1122                              &focus_width, &focus_pad);
  1124     if (ignore_focus)
  1125         focus_width = focus_pad = 0;
  1127     inner_rect->x = rect->x + XTHICKNESS(style) + focus_width + focus_pad;
  1128     inner_rect->x += direction == GTK_TEXT_DIR_LTR ?
  1129                         inner_border.left : inner_border.right;
  1130     inner_rect->y = rect->y + inner_border.top + YTHICKNESS(style) +
  1131                     focus_width + focus_pad;
  1132     inner_rect->width = MAX(1, rect->width - inner_border.left -
  1133        inner_border.right - (XTHICKNESS(style) + focus_pad + focus_width) * 2);
  1134     inner_rect->height = MAX(1, rect->height - inner_border.top -
  1135        inner_border.bottom - (YTHICKNESS(style) + focus_pad + focus_width) * 2);
  1137     return MOZ_GTK_SUCCESS;
  1141 static gint
  1142 calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect,
  1143                      GdkRectangle* arrow_rect, GtkTextDirection direction)
  1145     /* defined in gtkarrow.c */
  1146     gfloat arrow_scaling = 0.7;
  1147     gfloat xalign, xpad;
  1148     gint extent;
  1149     GtkMisc* misc = GTK_MISC(arrow);
  1151     if (have_arrow_scaling)
  1152         gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL);
  1154     extent = MIN((rect->width - misc->xpad * 2),
  1155                  (rect->height - misc->ypad * 2)) * arrow_scaling;
  1157     xalign = direction == GTK_TEXT_DIR_LTR ? misc->xalign : 1.0 - misc->xalign;
  1158     xpad = misc->xpad + (rect->width - extent) * xalign;
  1160     arrow_rect->x = direction == GTK_TEXT_DIR_LTR ?
  1161                         floor(rect->x + xpad) : ceil(rect->x + xpad);
  1162     arrow_rect->y = floor(rect->y + misc->ypad +
  1163                           ((rect->height - extent) * misc->yalign));
  1165     arrow_rect->width = arrow_rect->height = extent;
  1167     return MOZ_GTK_SUCCESS;
  1170 static gint
  1171 moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1172                                GdkRectangle* cliprect, GtkWidgetState* state,
  1173                                GtkScrollbarButtonFlags flags,
  1174                                GtkTextDirection direction)
  1176     GtkStateType state_type = ConvertGtkState(state);
  1177     GtkShadowType shadow_type = (state->active) ?
  1178         GTK_SHADOW_IN : GTK_SHADOW_OUT;
  1179     GdkRectangle arrow_rect;
  1180     GtkStyle* style;
  1181     GtkWidget *scrollbar;
  1182     GtkArrowType arrow_type;
  1183     gint arrow_displacement_x, arrow_displacement_y;
  1184     const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ?
  1185                            "vscrollbar" : "hscrollbar";
  1187     ensure_scrollbar_widget();
  1189     if (flags & MOZ_GTK_STEPPER_VERTICAL)
  1190         scrollbar = gVertScrollbarWidget;
  1191     else
  1192         scrollbar = gHorizScrollbarWidget;
  1194     gtk_widget_set_direction(scrollbar, direction);
  1196     /* Some theme engines (i.e., ClearLooks) check the scrollbar's allocation
  1197        to determine where it should paint rounded corners on the buttons.
  1198        We need to trick them into drawing the buttons the way we want them. */
  1200     scrollbar->allocation.x = rect->x;
  1201     scrollbar->allocation.y = rect->y;
  1202     scrollbar->allocation.width = rect->width;
  1203     scrollbar->allocation.height = rect->height;
  1205     if (flags & MOZ_GTK_STEPPER_VERTICAL) {
  1206         scrollbar->allocation.height *= 5;
  1207         if (flags & MOZ_GTK_STEPPER_DOWN) {
  1208             arrow_type = GTK_ARROW_DOWN;
  1209             if (flags & MOZ_GTK_STEPPER_BOTTOM)
  1210                 scrollbar->allocation.y -= 4 * rect->height;
  1211             else
  1212                 scrollbar->allocation.y -= rect->height;
  1214         } else {
  1215             arrow_type = GTK_ARROW_UP;
  1216             if (flags & MOZ_GTK_STEPPER_BOTTOM)
  1217                 scrollbar->allocation.y -= 3 * rect->height;
  1219     } else {
  1220         scrollbar->allocation.width *= 5;
  1221         if (flags & MOZ_GTK_STEPPER_DOWN) {
  1222             arrow_type = GTK_ARROW_RIGHT;
  1223             if (flags & MOZ_GTK_STEPPER_BOTTOM)
  1224                 scrollbar->allocation.x -= 4 * rect->width;
  1225             else
  1226                 scrollbar->allocation.x -= rect->width;
  1227         } else {
  1228             arrow_type = GTK_ARROW_LEFT;
  1229             if (flags & MOZ_GTK_STEPPER_BOTTOM)
  1230                 scrollbar->allocation.x -= 3 * rect->width;
  1234     style = scrollbar->style;
  1236     TSOffsetStyleGCs(style, rect->x, rect->y);
  1238     gtk_paint_box(style, drawable, state_type, shadow_type, cliprect,
  1239                   scrollbar, detail, rect->x, rect->y,
  1240                   rect->width, rect->height);
  1242     arrow_rect.width = rect->width / 2;
  1243     arrow_rect.height = rect->height / 2;
  1244     arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
  1245     arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
  1247     if (state_type == GTK_STATE_ACTIVE) {
  1248         gtk_widget_style_get(scrollbar,
  1249                              "arrow-displacement-x", &arrow_displacement_x,
  1250                              "arrow-displacement-y", &arrow_displacement_y,
  1251                              NULL);
  1253         arrow_rect.x += arrow_displacement_x;
  1254         arrow_rect.y += arrow_displacement_y;
  1257     gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
  1258                     scrollbar, detail, arrow_type, TRUE, arrow_rect.x,
  1259                     arrow_rect.y, arrow_rect.width, arrow_rect.height);
  1261     return MOZ_GTK_SUCCESS;
  1264 static gint
  1265 moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget,
  1266                                GdkDrawable* drawable, GdkRectangle* rect,
  1267                                GdkRectangle* cliprect, GtkWidgetState* state,
  1268                                GtkTextDirection direction)
  1270     GtkStyle* style;
  1271     GtkScrollbar *scrollbar;
  1273     ensure_scrollbar_widget();
  1275     if (widget ==  MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL)
  1276         scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
  1277     else
  1278         scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
  1280     gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
  1282     style = GTK_WIDGET(scrollbar)->style;
  1284     TSOffsetStyleGCs(style, rect->x, rect->y);
  1285     gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_ACTIVE,
  1286                                        cliprect, rect->x, rect->y,
  1287                                        rect->width, rect->height);
  1289     gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect,
  1290                   GTK_WIDGET(scrollbar), "trough", rect->x, rect->y,
  1291                   rect->width, rect->height);
  1293     if (state->focused) {
  1294         gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect,
  1295                         GTK_WIDGET(scrollbar), "trough",
  1296                         rect->x, rect->y, rect->width, rect->height);
  1299     return MOZ_GTK_SUCCESS;
  1302 static gint
  1303 moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget,
  1304                               GdkDrawable* drawable, GdkRectangle* rect,
  1305                               GdkRectangle* cliprect, GtkWidgetState* state,
  1306                               GtkTextDirection direction)
  1308     GtkStateType state_type = (state->inHover || state->active) ?
  1309         GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
  1310     GtkShadowType shadow_type = GTK_SHADOW_OUT;
  1311     GtkStyle* style;
  1312     GtkScrollbar *scrollbar;
  1313     GtkAdjustment *adj;
  1314     gboolean activate_slider;
  1316     ensure_scrollbar_widget();
  1318     if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL)
  1319         scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
  1320     else
  1321         scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
  1323     gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
  1325     /* Make sure to set the scrollbar range before painting so that
  1326        everything is drawn properly.  At least the bluecurve (and
  1327        maybe other) themes don't draw the top or bottom black line
  1328        surrounding the scrollbar if the theme thinks that it's butted
  1329        up against the scrollbar arrows.  Note the increases of the
  1330        clip rect below. */
  1331     adj = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
  1333     if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) {
  1334         adj->page_size = rect->width;
  1336     else {
  1337         adj->page_size = rect->height;
  1340     adj->lower = 0;
  1341     adj->value = state->curpos;
  1342     adj->upper = state->maxpos;
  1343     gtk_adjustment_changed(adj);
  1345     style = GTK_WIDGET(scrollbar)->style;
  1347     gtk_widget_style_get(GTK_WIDGET(scrollbar), "activate-slider",
  1348                          &activate_slider, NULL);
  1350     if (activate_slider && state->active) {
  1351         shadow_type = GTK_SHADOW_IN;
  1352         state_type = GTK_STATE_ACTIVE;
  1355     TSOffsetStyleGCs(style, rect->x, rect->y);
  1357     gtk_paint_slider(style, drawable, state_type, shadow_type, cliprect,
  1358                      GTK_WIDGET(scrollbar), "slider", rect->x, rect->y,
  1359                      rect->width,  rect->height,
  1360                      (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
  1361                      GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
  1363     return MOZ_GTK_SUCCESS;
  1366 static gint
  1367 moz_gtk_spin_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1368                    GtkTextDirection direction)
  1370     GtkStyle* style;
  1372     ensure_spin_widget();
  1373     gtk_widget_set_direction(gSpinWidget, direction);
  1374     style = gSpinWidget->style;
  1376     TSOffsetStyleGCs(style, rect->x, rect->y);
  1377     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL,
  1378                   gSpinWidget, "spinbutton",
  1379                   rect->x, rect->y, rect->width, rect->height);
  1380     return MOZ_GTK_SUCCESS;
  1383 static gint
  1384 moz_gtk_spin_updown_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1385                           gboolean isDown, GtkWidgetState* state,
  1386                           GtkTextDirection direction)
  1388     GdkRectangle arrow_rect;
  1389     GtkStateType state_type = ConvertGtkState(state);
  1390     GtkShadowType shadow_type = state_type == GTK_STATE_ACTIVE ?
  1391                                   GTK_SHADOW_IN : GTK_SHADOW_OUT;
  1392     GtkStyle* style;
  1394     ensure_spin_widget();
  1395     style = gSpinWidget->style;
  1396     gtk_widget_set_direction(gSpinWidget, direction);
  1398     TSOffsetStyleGCs(style, rect->x, rect->y);
  1399     gtk_paint_box(style, drawable, state_type, shadow_type, NULL, gSpinWidget,
  1400                   isDown ? "spinbutton_down" : "spinbutton_up",
  1401                   rect->x, rect->y, rect->width, rect->height);
  1403     /* hard code these values */
  1404     arrow_rect.width = 6;
  1405     arrow_rect.height = 6;
  1406     arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
  1407     arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
  1408     arrow_rect.y += isDown ? -1 : 1;
  1410     gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL,
  1411                     gSpinWidget, "spinbutton",
  1412                     isDown ? GTK_ARROW_DOWN : GTK_ARROW_UP, TRUE,
  1413                     arrow_rect.x, arrow_rect.y,
  1414                     arrow_rect.width, arrow_rect.height);
  1416     return MOZ_GTK_SUCCESS;
  1419 static gint
  1420 moz_gtk_scale_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1421                     GdkRectangle* cliprect, GtkWidgetState* state,
  1422                     GtkOrientation flags, GtkTextDirection direction)
  1424   gint x = 0, y = 0;
  1425   GtkStateType state_type = ConvertGtkState(state);
  1426   GtkStyle* style;
  1427   GtkWidget* widget;
  1429   ensure_scale_widget();
  1430   widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
  1431   gtk_widget_set_direction(widget, direction);
  1433   style = widget->style;
  1435   if (flags == GTK_ORIENTATION_HORIZONTAL) {
  1436     x = XTHICKNESS(style);
  1437     y++;
  1439   else {
  1440     x++;
  1441     y = YTHICKNESS(style);
  1444   TSOffsetStyleGCs(style, rect->x, rect->y);
  1446   gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect,
  1447                 widget, "trough", rect->x + x, rect->y + y,
  1448                 rect->width - 2*x, rect->height - 2*y);
  1450   if (state->focused)
  1451     gtk_paint_focus(style, drawable, state_type, cliprect, widget, "trough",
  1452                     rect->x, rect->y, rect->width, rect->height);
  1454   return MOZ_GTK_SUCCESS;
  1457 static gint
  1458 moz_gtk_scale_thumb_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1459                           GdkRectangle* cliprect, GtkWidgetState* state,
  1460                           GtkOrientation flags, GtkTextDirection direction)
  1462   GtkStateType state_type = ConvertGtkState(state);
  1463   GtkStyle* style;
  1464   GtkWidget* widget;
  1465   gint thumb_width, thumb_height, x, y;
  1467   ensure_scale_widget();
  1468   widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
  1469   gtk_widget_set_direction(widget, direction);
  1471   style = widget->style;
  1473   /* determine the thumb size, and position the thumb in the center in the opposite axis */
  1474   if (flags == GTK_ORIENTATION_HORIZONTAL) {
  1475     moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_width, &thumb_height);
  1476     x = rect->x;
  1477     y = rect->y + (rect->height - thumb_height) / 2;
  1479   else {
  1480     moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_width);
  1481     x = rect->x + (rect->width - thumb_width) / 2;
  1482     y = rect->y;
  1485   TSOffsetStyleGCs(style, rect->x, rect->y);
  1486   gtk_paint_slider(style, drawable, state_type, GTK_SHADOW_OUT, cliprect,
  1487                    widget, (flags == GTK_ORIENTATION_HORIZONTAL) ? "hscale" : "vscale",
  1488                    x, y, thumb_width, thumb_height, flags);
  1490   return MOZ_GTK_SUCCESS;
  1493 static gint
  1494 moz_gtk_gripper_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1495                       GdkRectangle* cliprect, GtkWidgetState* state,
  1496                       GtkTextDirection direction)
  1498     GtkStateType state_type = ConvertGtkState(state);
  1499     GtkShadowType shadow_type;
  1500     GtkStyle* style;
  1502     ensure_handlebox_widget();
  1503     gtk_widget_set_direction(gHandleBoxWidget, direction);
  1505     style = gHandleBoxWidget->style;
  1506     shadow_type = GTK_HANDLE_BOX(gHandleBoxWidget)->shadow_type;
  1508     TSOffsetStyleGCs(style, rect->x, rect->y);
  1509     gtk_paint_box(style, drawable, state_type, shadow_type, cliprect,
  1510                   gHandleBoxWidget, "handlebox_bin", rect->x, rect->y,
  1511                   rect->width, rect->height);
  1513     return MOZ_GTK_SUCCESS;
  1516 static gint
  1517 moz_gtk_hpaned_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1518                      GdkRectangle* cliprect, GtkWidgetState* state)
  1520     GtkStateType hpaned_state = ConvertGtkState(state);
  1522     ensure_hpaned_widget();
  1523     gtk_paint_handle(gHPanedWidget->style, drawable, hpaned_state,
  1524                      GTK_SHADOW_NONE, cliprect, gHPanedWidget, "paned",
  1525                      rect->x, rect->y, rect->width, rect->height,
  1526                      GTK_ORIENTATION_VERTICAL);
  1528     return MOZ_GTK_SUCCESS;
  1531 static gint
  1532 moz_gtk_vpaned_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1533                      GdkRectangle* cliprect, GtkWidgetState* state)
  1535     GtkStateType vpaned_state = ConvertGtkState(state);
  1537     ensure_vpaned_widget();
  1538     gtk_paint_handle(gVPanedWidget->style, drawable, vpaned_state,
  1539                      GTK_SHADOW_NONE, cliprect, gVPanedWidget, "paned",
  1540                      rect->x, rect->y, rect->width, rect->height,
  1541                      GTK_ORIENTATION_HORIZONTAL);
  1543     return MOZ_GTK_SUCCESS;
  1546 static gint
  1547 moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1548                     GdkRectangle* cliprect, GtkWidgetState* state,
  1549                     GtkWidget* widget, GtkTextDirection direction)
  1551     GtkStateType bg_state = state->disabled ?
  1552                                 GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
  1553     gint x, y, width = rect->width, height = rect->height;
  1554     GtkStyle* style;
  1555     gboolean interior_focus;
  1556     gboolean theme_honors_transparency = FALSE;
  1557     gint focus_width;
  1559     gtk_widget_set_direction(widget, direction);
  1561     style = widget->style;
  1563     gtk_widget_style_get(widget,
  1564                          "interior-focus", &interior_focus,
  1565                          "focus-line-width", &focus_width,
  1566                          "honors-transparent-bg-hint", &theme_honors_transparency,
  1567                          NULL);
  1569     /* gtkentry.c uses two windows, one for the entire widget and one for the
  1570      * text area inside it. The background of both windows is set to the "base"
  1571      * color of the new state in gtk_entry_state_changed, but only the inner
  1572      * textarea window uses gtk_paint_flat_box when exposed */
  1574     TSOffsetStyleGCs(style, rect->x, rect->y);
  1576     /* This gets us a lovely greyish disabledish look */
  1577     gtk_widget_set_sensitive(widget, !state->disabled);
  1579     /* GTK fills the outer widget window with the base color before drawing the widget.
  1580      * Some older themes rely on this behavior, but many themes nowadays use rounded
  1581      * corners on their widgets. While most GTK apps are blissfully unaware of this
  1582      * problem due to their use of the default window background, we render widgets on
  1583      * many kinds of backgrounds on the web.
  1584      * If the theme is able to cope with transparency, then we can skip pre-filling
  1585      * and notify the theme it will paint directly on the canvas. */
  1586     if (theme_honors_transparency) {
  1587         g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
  1588     } else {
  1589         GdkRectangle clipped_rect;
  1590         gdk_rectangle_intersect(rect, cliprect, &clipped_rect);
  1591         if (clipped_rect.width != 0) {
  1592             gdk_draw_rectangle(drawable, style->base_gc[bg_state], TRUE,
  1593                                clipped_rect.x, clipped_rect.y,
  1594                                clipped_rect.width, clipped_rect.height);
  1596         g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(FALSE));
  1599     /* Get the position of the inner window, see _gtk_entry_get_borders */
  1600     x = XTHICKNESS(style);
  1601     y = YTHICKNESS(style);
  1603     if (!interior_focus) {
  1604         x += focus_width;
  1605         y += focus_width;
  1608     /* Simulate an expose of the inner window */
  1609     gtk_paint_flat_box(style, drawable, bg_state, GTK_SHADOW_NONE,
  1610                        cliprect, widget, "entry_bg",  rect->x + x,
  1611                        rect->y + y, rect->width - 2*x, rect->height - 2*y);
  1613     /* Now paint the shadow and focus border.
  1614      * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad
  1615      * smaller when focused if the focus is not interior, then the focus. */
  1616     x = rect->x;
  1617     y = rect->y;
  1619     if (state->focused && !state->disabled) {
  1620         /* This will get us the lit borders that focused textboxes enjoy on
  1621          * some themes. */
  1622         GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
  1624         if (!interior_focus) {
  1625             /* Indent the border a little bit if we have exterior focus 
  1626                (this is what GTK does to draw native entries) */
  1627             x += focus_width;
  1628             y += focus_width;
  1629             width -= 2 * focus_width;
  1630             height -= 2 * focus_width;
  1634     gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
  1635                      cliprect, widget, "entry", x, y, width, height);
  1637     if (state->focused && !state->disabled) {
  1638         if (!interior_focus) {
  1639             gtk_paint_focus(style, drawable,  GTK_STATE_NORMAL, cliprect,
  1640                             widget, "entry",
  1641                             rect->x, rect->y, rect->width, rect->height);
  1644         /* Now unset the focus flag. We don't want other entries to look
  1645          * like they're focused too! */
  1646         GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
  1649     return MOZ_GTK_SUCCESS;
  1652 static gint 
  1653 moz_gtk_treeview_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1654                        GdkRectangle* cliprect, GtkWidgetState* state,
  1655                        GtkTextDirection direction)
  1657     gint xthickness, ythickness;
  1659     GtkStyle *style;
  1660     GtkStateType state_type;
  1662     ensure_tree_view_widget();
  1663     ensure_scrolled_window_widget();
  1665     gtk_widget_set_direction(gTreeViewWidget, direction);
  1666     gtk_widget_set_direction(gScrolledWindowWidget, direction);
  1668     /* only handle disabled and normal states, otherwise the whole background
  1669      * area will be painted differently with other states */
  1670     state_type = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
  1672     /* In GTK the treeview sets the background of the window
  1673      * which contains the cells to the treeview base color.
  1674      * If we don't set it here the background color will not be correct.*/
  1675     gtk_widget_modify_bg(gTreeViewWidget, state_type,
  1676                          &gTreeViewWidget->style->base[state_type]);
  1678     style = gScrolledWindowWidget->style;
  1679     xthickness = XTHICKNESS(style);
  1680     ythickness = YTHICKNESS(style);
  1682     TSOffsetStyleGCs(gTreeViewWidget->style, rect->x, rect->y);
  1683     TSOffsetStyleGCs(style, rect->x, rect->y);
  1685     gtk_paint_flat_box(gTreeViewWidget->style, drawable, state_type,
  1686                        GTK_SHADOW_NONE, cliprect, gTreeViewWidget, "treeview",
  1687                        rect->x + xthickness, rect->y + ythickness,
  1688                        rect->width - 2 * xthickness,
  1689                        rect->height - 2 * ythickness);
  1691     gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
  1692                      cliprect, gScrolledWindowWidget, "scrolled_window",
  1693                      rect->x, rect->y, rect->width, rect->height); 
  1695     return MOZ_GTK_SUCCESS;
  1698 static gint
  1699 moz_gtk_tree_header_cell_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1700                                GdkRectangle* cliprect, GtkWidgetState* state,
  1701                                gboolean isSorted, GtkTextDirection direction)
  1703     gtk_tree_view_column_set_sort_indicator(gMiddleTreeViewColumn,
  1704                                             isSorted);
  1706     moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
  1707                          gTreeHeaderCellWidget, direction);
  1708     return MOZ_GTK_SUCCESS;
  1711 static gint
  1712 moz_gtk_tree_header_sort_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1713                                      GdkRectangle* cliprect,
  1714                                      GtkWidgetState* state, GtkArrowType flags,
  1715                                      GtkTextDirection direction)
  1717     GdkRectangle arrow_rect;
  1718     GtkStateType state_type = ConvertGtkState(state);
  1719     GtkShadowType shadow_type = GTK_SHADOW_IN;
  1720     GtkArrowType arrow_type = flags;
  1721     GtkStyle* style;
  1723     ensure_tree_header_cell_widget();
  1724     gtk_widget_set_direction(gTreeHeaderSortArrowWidget, direction);
  1726     /* hard code these values */
  1727     arrow_rect.width = 11;
  1728     arrow_rect.height = 11;
  1729     arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
  1730     arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
  1732     style = gTreeHeaderSortArrowWidget->style;
  1733     TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y);
  1735     gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
  1736                     gTreeHeaderSortArrowWidget, "arrow",  arrow_type, TRUE,
  1737                     arrow_rect.x, arrow_rect.y,
  1738                     arrow_rect.width, arrow_rect.height);
  1740     return MOZ_GTK_SUCCESS;
  1743 static gint
  1744 moz_gtk_treeview_expander_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1745                                 GdkRectangle* cliprect, GtkWidgetState* state,
  1746                                 GtkExpanderStyle expander_state,
  1747                                 GtkTextDirection direction)
  1749     GtkStyle *style;
  1750     GtkStateType state_type;
  1752     ensure_tree_view_widget();
  1753     gtk_widget_set_direction(gTreeViewWidget, direction);
  1755     style = gTreeViewWidget->style;
  1757     /* Because the frame we get is of the entire treeview, we can't get the precise
  1758      * event state of one expander, thus rendering hover and active feedback useless. */
  1759     state_type = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
  1761     TSOffsetStyleGCs(style, rect->x, rect->y);
  1762     gtk_paint_expander(style, drawable, state_type, cliprect, gTreeViewWidget, "treeview",
  1763                        rect->x + rect->width / 2, rect->y + rect->height / 2, expander_state);
  1765     return MOZ_GTK_SUCCESS;
  1768 static gint
  1769 moz_gtk_combo_box_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1770                         GdkRectangle* cliprect, GtkWidgetState* state,
  1771                         gboolean ishtml, GtkTextDirection direction)
  1773     GdkRectangle arrow_rect, real_arrow_rect;
  1774     gint arrow_size, separator_width;
  1775     gboolean wide_separators;
  1776     GtkStateType state_type = ConvertGtkState(state);
  1777     GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
  1778     GtkStyle* style;
  1779     GtkRequisition arrow_req;
  1781     ensure_combo_box_widgets();
  1783     /* Also sets the direction on gComboBoxButtonWidget, which is then
  1784      * inherited by the separator and arrow */
  1785     moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
  1786                          gComboBoxButtonWidget, direction);
  1788     calculate_button_inner_rect(gComboBoxButtonWidget,
  1789                                 rect, &arrow_rect, direction, ishtml);
  1790     /* Now arrow_rect contains the inner rect ; we want to correct the width
  1791      * to what the arrow needs (see gtk_combo_box_size_allocate) */
  1792     gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req);
  1793     if (direction == GTK_TEXT_DIR_LTR)
  1794         arrow_rect.x += arrow_rect.width - arrow_req.width;
  1795     arrow_rect.width = arrow_req.width;
  1797     calculate_arrow_rect(gComboBoxArrowWidget,
  1798                          &arrow_rect, &real_arrow_rect, direction);
  1800     style = gComboBoxArrowWidget->style;
  1801     TSOffsetStyleGCs(style, rect->x, rect->y);
  1803     gtk_widget_size_allocate(gComboBoxWidget, rect);
  1805     gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
  1806                     gComboBoxArrowWidget, "arrow",  GTK_ARROW_DOWN, TRUE,
  1807                     real_arrow_rect.x, real_arrow_rect.y,
  1808                     real_arrow_rect.width, real_arrow_rect.height);
  1811     /* If there is no separator in the theme, there's nothing left to do. */
  1812     if (!gComboBoxSeparatorWidget)
  1813         return MOZ_GTK_SUCCESS;
  1815     style = gComboBoxSeparatorWidget->style;
  1816     TSOffsetStyleGCs(style, rect->x, rect->y);
  1818     gtk_widget_style_get(gComboBoxSeparatorWidget,
  1819                          "wide-separators", &wide_separators,
  1820                          "separator-width", &separator_width,
  1821                          NULL);
  1823     if (wide_separators) {
  1824         if (direction == GTK_TEXT_DIR_LTR)
  1825             arrow_rect.x -= separator_width;
  1826         else
  1827             arrow_rect.x += arrow_rect.width;
  1829         gtk_paint_box(style, drawable,
  1830                       GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
  1831                       cliprect, gComboBoxSeparatorWidget, "vseparator",
  1832                       arrow_rect.x, arrow_rect.y,
  1833                       separator_width, arrow_rect.height);
  1834     } else {
  1835         if (direction == GTK_TEXT_DIR_LTR)
  1836             arrow_rect.x -= XTHICKNESS(style);
  1837         else
  1838             arrow_rect.x += arrow_rect.width;
  1840         gtk_paint_vline(style, drawable, GTK_STATE_NORMAL, cliprect,
  1841                         gComboBoxSeparatorWidget, "vseparator",
  1842                         arrow_rect.y, arrow_rect.y + arrow_rect.height,
  1843                         arrow_rect.x);
  1846     return MOZ_GTK_SUCCESS;
  1849 static gint
  1850 moz_gtk_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1851                     GdkRectangle* cliprect, GtkWidgetState* state,
  1852                     GtkArrowType arrow_type, GtkTextDirection direction)
  1854     GtkStyle* style;
  1855     GtkStateType state_type = ConvertGtkState(state);
  1856     GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
  1857     GdkRectangle arrow_rect;
  1859     ensure_button_arrow_widget();
  1860     style = gButtonArrowWidget->style;
  1861     gtk_widget_set_direction(gButtonArrowWidget, direction);
  1863     calculate_arrow_rect(gButtonArrowWidget, rect, &arrow_rect,
  1864                          direction);
  1866     if (direction == GTK_TEXT_DIR_RTL) {
  1867         if (arrow_type == GTK_ARROW_LEFT)
  1868             arrow_type = GTK_ARROW_RIGHT;
  1869         else if (arrow_type == GTK_ARROW_RIGHT)
  1870             arrow_type = GTK_ARROW_LEFT;
  1873     TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y);
  1874     gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
  1875                     gButtonArrowWidget, "arrow",  arrow_type, TRUE,
  1876                     arrow_rect.x, arrow_rect.y, arrow_rect.width, arrow_rect.height);
  1878     return MOZ_GTK_SUCCESS;
  1881 static gint
  1882 moz_gtk_combo_box_entry_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1883                                      GdkRectangle* cliprect,
  1884                                      GtkWidgetState* state,
  1885                                      gboolean input_focus,
  1886                                      GtkTextDirection direction)
  1888     gint x_displacement, y_displacement;
  1889     GdkRectangle arrow_rect, real_arrow_rect;
  1890     GtkStateType state_type = ConvertGtkState(state);
  1891     GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
  1892     GtkStyle* style;
  1894     ensure_combo_box_entry_widgets();
  1896     if (input_focus) {
  1897         /* Some themes draw a complementary focus ring for the dropdown button
  1898          * when the dropdown entry has focus */
  1899         GTK_WIDGET_SET_FLAGS(gComboBoxEntryTextareaWidget, GTK_HAS_FOCUS);
  1902     moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
  1903                          gComboBoxEntryButtonWidget, direction);
  1905     if (input_focus)
  1906         GTK_WIDGET_UNSET_FLAGS(gComboBoxEntryTextareaWidget, GTK_HAS_FOCUS);
  1908     calculate_button_inner_rect(gComboBoxEntryButtonWidget,
  1909                                 rect, &arrow_rect, direction, FALSE);
  1910     if (state_type == GTK_STATE_ACTIVE) {
  1911         gtk_widget_style_get(gComboBoxEntryButtonWidget,
  1912                              "child-displacement-x", &x_displacement,
  1913                              "child-displacement-y", &y_displacement,
  1914                              NULL);
  1915         arrow_rect.x += x_displacement;
  1916         arrow_rect.y += y_displacement;
  1919     calculate_arrow_rect(gComboBoxEntryArrowWidget,
  1920                          &arrow_rect, &real_arrow_rect, direction);
  1922     style = gComboBoxEntryArrowWidget->style;
  1923     TSOffsetStyleGCs(style, real_arrow_rect.x, real_arrow_rect.y);
  1925     gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
  1926                     gComboBoxEntryArrowWidget, "arrow",  GTK_ARROW_DOWN, TRUE,
  1927                     real_arrow_rect.x, real_arrow_rect.y,
  1928                     real_arrow_rect.width, real_arrow_rect.height);
  1930     return MOZ_GTK_SUCCESS;
  1933 static gint
  1934 moz_gtk_container_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1935                         GdkRectangle* cliprect, GtkWidgetState* state, 
  1936                         gboolean isradio, GtkTextDirection direction)
  1938     GtkStateType state_type = ConvertGtkState(state);
  1939     GtkStyle* style;
  1940     GtkWidget *widget;
  1941     gboolean interior_focus;
  1942     gint focus_width, focus_pad;
  1944     if (isradio) {
  1945         ensure_radiobutton_widget();
  1946         widget = gRadiobuttonWidget;
  1947     } else {
  1948         ensure_checkbox_widget();
  1949         widget = gCheckboxWidget;
  1951     gtk_widget_set_direction(widget, direction);
  1953     style = widget->style;
  1954     moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width,
  1955                              &focus_pad);
  1957     TSOffsetStyleGCs(style, rect->x, rect->y);
  1959     /* The detail argument for the gtk_paint_* calls below are "checkbutton"
  1960        even for radio buttons, to match what gtk does. */
  1962     /* this is for drawing a prelight box */
  1963     if (state_type == GTK_STATE_PRELIGHT || state_type == GTK_STATE_ACTIVE) {
  1964         gtk_paint_flat_box(style, drawable, GTK_STATE_PRELIGHT,
  1965                            GTK_SHADOW_ETCHED_OUT, cliprect, widget,
  1966                            "checkbutton",
  1967                            rect->x, rect->y, rect->width, rect->height);
  1970     if (state_type != GTK_STATE_NORMAL && state_type != GTK_STATE_PRELIGHT)
  1971         state_type = GTK_STATE_NORMAL;
  1973     if (state->focused && !interior_focus) {
  1974         gtk_paint_focus(style, drawable, state_type, cliprect, widget,
  1975                         "checkbutton",
  1976                         rect->x, rect->y, rect->width, rect->height);
  1979     return MOZ_GTK_SUCCESS;
  1982 static gint
  1983 moz_gtk_toggle_label_paint(GdkDrawable* drawable, GdkRectangle* rect,
  1984                            GdkRectangle* cliprect, GtkWidgetState* state, 
  1985                            gboolean isradio, GtkTextDirection direction)
  1987     GtkStateType state_type;
  1988     GtkStyle *style;
  1989     GtkWidget *widget;
  1990     gboolean interior_focus;
  1992     if (!state->focused)
  1993         return MOZ_GTK_SUCCESS;
  1995     if (isradio) {
  1996         ensure_radiobutton_widget();
  1997         widget = gRadiobuttonWidget;
  1998     } else {
  1999         ensure_checkbox_widget();
  2000         widget = gCheckboxWidget;
  2002     gtk_widget_set_direction(widget, direction);
  2004     gtk_widget_style_get(widget, "interior-focus", &interior_focus, NULL);
  2005     if (!interior_focus)
  2006         return MOZ_GTK_SUCCESS;
  2008     state_type = ConvertGtkState(state);
  2010     style = widget->style;
  2011     TSOffsetStyleGCs(style, rect->x, rect->y);
  2013     /* Always "checkbutton" to match gtkcheckbutton.c */
  2014     gtk_paint_focus(style, drawable, state_type, cliprect, widget,
  2015                     "checkbutton",
  2016                     rect->x, rect->y, rect->width, rect->height);
  2018     return MOZ_GTK_SUCCESS;
  2021 static gint
  2022 moz_gtk_toolbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2023                       GdkRectangle* cliprect, GtkTextDirection direction)
  2025     GtkStyle* style;
  2026     GtkShadowType shadow_type;
  2028     ensure_toolbar_widget();
  2029     gtk_widget_set_direction(gToolbarWidget, direction);
  2031     style = gToolbarWidget->style;
  2033     TSOffsetStyleGCs(style, rect->x, rect->y);
  2035     gtk_style_apply_default_background(style, drawable, TRUE,
  2036                                        GTK_STATE_NORMAL,
  2037                                        cliprect, rect->x, rect->y,
  2038                                        rect->width, rect->height);
  2040     gtk_widget_style_get(gToolbarWidget, "shadow-type", &shadow_type, NULL);
  2042     gtk_paint_box (style, drawable, GTK_STATE_NORMAL, shadow_type,
  2043                    cliprect, gToolbarWidget, "toolbar",
  2044                    rect->x, rect->y, rect->width, rect->height);
  2046     return MOZ_GTK_SUCCESS;
  2049 static gint
  2050 moz_gtk_toolbar_separator_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2051                                 GdkRectangle* cliprect,
  2052                                 GtkTextDirection direction)
  2054     GtkStyle* style;
  2055     gint     separator_width;
  2056     gint     paint_width;
  2057     gboolean wide_separators;
  2059     /* Defined as constants in GTK+ 2.10.14 */
  2060     const double start_fraction = 0.2;
  2061     const double end_fraction = 0.8;
  2063     ensure_toolbar_separator_widget();
  2064     gtk_widget_set_direction(gToolbarSeparatorWidget, direction);
  2066     style = gToolbarSeparatorWidget->style;
  2068     gtk_widget_style_get(gToolbarWidget,
  2069                          "wide-separators", &wide_separators,
  2070                          "separator-width", &separator_width,
  2071                          NULL);
  2073     TSOffsetStyleGCs(style, rect->x, rect->y);
  2075     if (wide_separators) {
  2076         if (separator_width > rect->width)
  2077             separator_width = rect->width;
  2079         gtk_paint_box(style, drawable,
  2080                       GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
  2081                       cliprect, gToolbarWidget, "vseparator",
  2082                       rect->x + (rect->width - separator_width) / 2,
  2083                       rect->y + rect->height * start_fraction,
  2084                       separator_width,
  2085                       rect->height * (end_fraction - start_fraction));
  2087     } else {
  2088         paint_width = style->xthickness;
  2090         if (paint_width > rect->width)
  2091             paint_width = rect->width;
  2093         gtk_paint_vline(style, drawable,
  2094                         GTK_STATE_NORMAL, cliprect, gToolbarSeparatorWidget,
  2095                         "toolbar",
  2096                         rect->y + rect->height * start_fraction,
  2097                         rect->y + rect->height * end_fraction,
  2098                         rect->x + (rect->width - paint_width) / 2);
  2101     return MOZ_GTK_SUCCESS;
  2104 static gint
  2105 moz_gtk_tooltip_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2106                       GdkRectangle* cliprect, GtkTextDirection direction)
  2108     GtkStyle* style;
  2110     ensure_tooltip_widget();
  2111     gtk_widget_set_direction(gTooltipWidget, direction);
  2113     style = gtk_rc_get_style_by_paths(gtk_settings_get_default(),
  2114                                       "gtk-tooltips", "GtkWindow",
  2115                                       GTK_TYPE_WINDOW);
  2117     style = gtk_style_attach(style, gTooltipWidget->window);
  2118     TSOffsetStyleGCs(style, rect->x, rect->y);
  2119     gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
  2120                        cliprect, gTooltipWidget, "tooltip",
  2121                        rect->x, rect->y, rect->width, rect->height);
  2123     return MOZ_GTK_SUCCESS;
  2126 static gint
  2127 moz_gtk_resizer_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2128                       GdkRectangle* cliprect, GtkWidgetState* state,
  2129                       GtkTextDirection direction)
  2131     GtkStyle* style;
  2132     GtkStateType state_type = ConvertGtkState(state);
  2134     ensure_frame_widget();
  2135     gtk_widget_set_direction(gStatusbarWidget, direction);
  2137     style = gStatusbarWidget->style;
  2139     TSOffsetStyleGCs(style, rect->x, rect->y);
  2141     gtk_paint_resize_grip(style, drawable, state_type, cliprect, gStatusbarWidget,
  2142                           "statusbar", (direction == GTK_TEXT_DIR_LTR) ?
  2143                           GDK_WINDOW_EDGE_SOUTH_EAST :
  2144                           GDK_WINDOW_EDGE_SOUTH_WEST,
  2145                           rect->x, rect->y, rect->width, rect->height);
  2146     return MOZ_GTK_SUCCESS;
  2149 static gint
  2150 moz_gtk_frame_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2151                     GdkRectangle* cliprect, GtkTextDirection direction)
  2153     GtkStyle* style;
  2154     GtkShadowType shadow_type;
  2156     ensure_frame_widget();
  2157     gtk_widget_set_direction(gFrameWidget, direction);
  2159     style = gFrameWidget->style;
  2161     gtk_widget_style_get(gStatusbarWidget, "shadow-type", &shadow_type, NULL);
  2163     TSOffsetStyleGCs(style, rect->x, rect->y);
  2164     gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, shadow_type,
  2165                      cliprect, gFrameWidget, "frame", rect->x, rect->y,
  2166                      rect->width, rect->height);
  2168     return MOZ_GTK_SUCCESS;
  2171 static gint
  2172 moz_gtk_progressbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2173                           GdkRectangle* cliprect, GtkTextDirection direction)
  2175     GtkStyle* style;
  2177     ensure_progress_widget();
  2178     gtk_widget_set_direction(gProgressWidget, direction);
  2180     style = gProgressWidget->style;
  2182     TSOffsetStyleGCs(style, rect->x, rect->y);
  2183     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
  2184                   cliprect, gProgressWidget, "trough", rect->x, rect->y,
  2185                   rect->width, rect->height);
  2187     return MOZ_GTK_SUCCESS;
  2190 static gint
  2191 moz_gtk_progress_chunk_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2192                              GdkRectangle* cliprect, GtkTextDirection direction,
  2193                              GtkThemeWidgetType widget)
  2195     GtkStyle* style;
  2197     ensure_progress_widget();
  2198     gtk_widget_set_direction(gProgressWidget, direction);
  2200     style = gProgressWidget->style;
  2202     TSOffsetStyleGCs(style, rect->x, rect->y);
  2204     if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
  2205         widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
  2206       /**
  2207        * The bar's size and the bar speed are set depending of the progress'
  2208        * size. These could also be constant for all progress bars easily.
  2209        */
  2210       gboolean vertical = (widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE);
  2212       /* The size of the dimension we are going to use for the animation. */
  2213       const gint progressSize = vertical ? rect->height : rect->width;
  2215       /* The bar is using a fifth of the element size, based on GtkProgressBar
  2216        * activity-blocks property. */
  2217       const gint barSize = MAX(1, progressSize / 5);
  2219       /* Represents the travel that has to be done for a complete cycle. */
  2220       const gint travel = 2 * (progressSize - barSize);
  2222       /* period equals to travel / pixelsPerMillisecond
  2223        * where pixelsPerMillisecond equals progressSize / 1000.0.
  2224        * This is equivalent to 1600. */
  2225       static const guint period = 1600;
  2226       const gint t = PR_IntervalToMilliseconds(PR_IntervalNow()) % period;
  2227       const gint dx = travel * t / period;
  2229       if (vertical) {
  2230         rect->y += (dx < travel / 2) ? dx : travel - dx;
  2231         rect->height = barSize;
  2232       } else {
  2233         rect->x += (dx < travel / 2) ? dx : travel - dx;
  2234         rect->width = barSize;
  2238     gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT,
  2239                   cliprect, gProgressWidget, "bar", rect->x, rect->y,
  2240                   rect->width, rect->height);
  2242     return MOZ_GTK_SUCCESS;
  2245 gint
  2246 moz_gtk_get_tab_thickness(void)
  2248     ensure_tab_widget();
  2249     if (YTHICKNESS(gTabWidget->style) < 2)
  2250         return 2; /* some themes don't set ythickness correctly */
  2252     return YTHICKNESS(gTabWidget->style);
  2255 static gint
  2256 moz_gtk_tab_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2257                   GdkRectangle* cliprect, GtkWidgetState* state,
  2258                   GtkTabFlags flags, GtkTextDirection direction)
  2260     /* When the tab isn't selected, we just draw a notebook extension.
  2261      * When it is selected, we overwrite the adjacent border of the tabpanel
  2262      * touching the tab with a pierced border (called "the gap") to make the
  2263      * tab appear physically attached to the tabpanel; see details below. */
  2265     GtkStyle* style;
  2266     GdkRectangle focusRect;
  2268     ensure_tab_widget();
  2269     gtk_widget_set_direction(gTabWidget, direction);
  2271     style = gTabWidget->style;
  2272     focusRect = *rect;
  2273     TSOffsetStyleGCs(style, rect->x, rect->y);
  2275     if ((flags & MOZ_GTK_TAB_SELECTED) == 0) {
  2276         /* Only draw the tab */
  2277         gtk_paint_extension(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
  2278                             cliprect, gTabWidget, "tab",
  2279                             rect->x, rect->y, rect->width, rect->height,
  2280                             (flags & MOZ_GTK_TAB_BOTTOM) ?
  2281                                 GTK_POS_TOP : GTK_POS_BOTTOM );
  2282     } else {
  2283         /* Draw the tab and the gap
  2284          * We want the gap to be positioned exactly on the tabpanel top
  2285          * border; since tabbox.css may set a negative margin so that the tab
  2286          * frame rect already overlaps the tabpanel frame rect, we need to take
  2287          * that into account when drawing. To that effect, nsNativeThemeGTK
  2288          * passes us this negative margin (bmargin in the graphic below) in the
  2289          * lowest bits of |flags|.  We use it to set gap_voffset, the distance
  2290          * between the top of the gap and the bottom of the tab (resp. the
  2291          * bottom of the gap and the top of the tab when we draw a bottom tab),
  2292          * while ensuring that the gap always touches the border of the tab,
  2293          * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results
  2294          * with big negative or positive margins.
  2295          * Here is a graphical explanation in the case of top tabs:
  2296          *             ___________________________
  2297          *            /                           \
  2298          *           |            T A B            |
  2299          * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel
  2300          *           :    ^       bmargin          :  ^
  2301          *           :    | (-negative margin,     :  |
  2302          *  bottom   :    v  passed in flags)      :  |       gap_height
  2303          *    of  -> :.............................:  |    (the size of the
  2304          * the tab   .       part of the gap       .  |  tabpanel top border)
  2305          *           .      outside of the tab     .  v
  2306          * ----------------------------------------------
  2308          * To draw the gap, we use gtk_paint_box_gap(), see comment in
  2309          * moz_gtk_tabpanels_paint(). This box_gap is made 3 * gap_height tall,
  2310          * which should suffice to ensure that the only visible border is the
  2311          * pierced one.  If the tab is in the middle, we make the box_gap begin
  2312          * a bit to the left of the tab and end a bit to the right, adjusting
  2313          * the gap position so it still is under the tab, because we want the
  2314          * rendering of a gap in the middle of a tabpanel.  This is the role of
  2315          * the gints gap_{l,r}_offset. On the contrary, if the tab is the
  2316          * first, we align the start border of the box_gap with the start
  2317          * border of the tab (left if LTR, right if RTL), by setting the
  2318          * appropriate offset to 0.*/
  2319         gint gap_loffset, gap_roffset, gap_voffset, gap_height;
  2321         /* Get height needed by the gap */
  2322         gap_height = moz_gtk_get_tab_thickness();
  2324         /* Extract gap_voffset from the first bits of flags */
  2325         gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK;
  2326         if (gap_voffset > gap_height)
  2327             gap_voffset = gap_height;
  2329         /* Set gap_{l,r}_offset to appropriate values */
  2330         gap_loffset = gap_roffset = 20; /* should be enough */
  2331         if (flags & MOZ_GTK_TAB_FIRST) {
  2332             if (direction == GTK_TEXT_DIR_RTL)
  2333                 gap_roffset = 0;
  2334             else
  2335                 gap_loffset = 0;
  2338         if (flags & MOZ_GTK_TAB_BOTTOM) {
  2339             /* Draw the tab */
  2340             focusRect.y += gap_voffset;
  2341             focusRect.height -= gap_voffset;
  2342             gtk_paint_extension(style, drawable, GTK_STATE_NORMAL,
  2343                                 GTK_SHADOW_OUT, cliprect, gTabWidget, "tab",
  2344                                 rect->x, rect->y + gap_voffset, rect->width,
  2345                                 rect->height - gap_voffset, GTK_POS_TOP);
  2347             /* Draw the gap; erase with background color before painting in
  2348              * case theme does not */
  2349             gtk_style_apply_default_background(style, drawable, TRUE,
  2350                                                GTK_STATE_NORMAL, cliprect,
  2351                                                rect->x,
  2352                                                rect->y + gap_voffset
  2353                                                        - gap_height,
  2354                                                rect->width, gap_height);
  2355             gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
  2356                               cliprect, gTabWidget, "notebook",
  2357                               rect->x - gap_loffset,
  2358                               rect->y + gap_voffset - 3 * gap_height,
  2359                               rect->width + gap_loffset + gap_roffset,
  2360                               3 * gap_height, GTK_POS_BOTTOM,
  2361                               gap_loffset, rect->width);
  2362         } else {
  2363             /* Draw the tab */
  2364             focusRect.height -= gap_voffset;
  2365             gtk_paint_extension(style, drawable, GTK_STATE_NORMAL,
  2366                                 GTK_SHADOW_OUT, cliprect, gTabWidget, "tab",
  2367                                 rect->x, rect->y, rect->width,
  2368                                 rect->height - gap_voffset, GTK_POS_BOTTOM);
  2370             /* Draw the gap; erase with background color before painting in
  2371              * case theme does not */
  2372             gtk_style_apply_default_background(style, drawable, TRUE,
  2373                                                GTK_STATE_NORMAL, cliprect,
  2374                                                rect->x,
  2375                                                rect->y + rect->height
  2376                                                        - gap_voffset,
  2377                                                rect->width, gap_height);
  2378             gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
  2379                               cliprect, gTabWidget, "notebook",
  2380                               rect->x - gap_loffset,
  2381                               rect->y + rect->height - gap_voffset,
  2382                               rect->width + gap_loffset + gap_roffset,
  2383                               3 * gap_height, GTK_POS_TOP,
  2384                               gap_loffset, rect->width);
  2389     if (state->focused) {
  2390       /* Paint the focus ring */
  2391       focusRect.x += XTHICKNESS(style);
  2392       focusRect.width -= XTHICKNESS(style) * 2;
  2393       focusRect.y += YTHICKNESS(style);
  2394       focusRect.height -= YTHICKNESS(style) * 2;
  2396       gtk_paint_focus(style, drawable,
  2397                       /* Believe it or not, NORMAL means a selected tab and
  2398                          ACTIVE means an unselected tab. */
  2399                       (flags & MOZ_GTK_TAB_SELECTED) ? GTK_STATE_NORMAL
  2400                                                      : GTK_STATE_ACTIVE,
  2401                       cliprect, gTabWidget, "tab",
  2402                       focusRect.x, focusRect.y, focusRect.width, focusRect.height);
  2405     return MOZ_GTK_SUCCESS;
  2408 static gint
  2409 moz_gtk_tabpanels_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2410                         GdkRectangle* cliprect, GtkTextDirection direction)
  2412     /* We have three problems here:
  2413      * - Most engines draw gtk_paint_box differently to gtk_paint_box_gap, the
  2414      *   former implies there are no tabs, eg. Clearlooks.
  2415      * - Wanting a gap of width 0 doesn't actually guarantee a zero-width gap, eg.
  2416      *   Clearlooks.
  2417      * - Our old approach of a negative X position could cause rendering errors
  2418      *   on the box's corner, eg. themes using the Pixbuf engine.
  2419      */
  2420     GtkStyle* style;
  2421     GdkRectangle halfClipRect;
  2423     ensure_tab_widget();
  2424     gtk_widget_set_direction(gTabWidget, direction);
  2426     style = gTabWidget->style;
  2427     TSOffsetStyleGCs(style, rect->x, rect->y);
  2429     /* Our approach is as follows:
  2430      * - Draw the box in two passes. Pass in a clip rect to draw the left half of the
  2431      *   box, with the gap specified to the right outside the clip rect so that it is
  2432      *   not drawn.
  2433      * - The right half is drawn with the gap to the left outside the modified clip rect.
  2434      */
  2435     if (!gdk_rectangle_intersect(rect, cliprect, &halfClipRect))
  2436       return MOZ_GTK_SUCCESS;
  2438     halfClipRect.width = (halfClipRect.width / 2) + 1;
  2439     gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
  2440                       &halfClipRect, gTabWidget, "notebook", rect->x, rect->y,
  2441                       rect->width, rect->height,
  2442                       GTK_POS_TOP, halfClipRect.width + 1, 0);
  2444     halfClipRect.x += halfClipRect.width;
  2445     gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
  2446                       &halfClipRect, gTabWidget, "notebook", rect->x, rect->y,
  2447                       rect->width, rect->height,
  2448                       GTK_POS_TOP, -10, 0);
  2450     return MOZ_GTK_SUCCESS;
  2453 static gint
  2454 moz_gtk_tab_scroll_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2455                                GdkRectangle* cliprect, GtkWidgetState* state,
  2456                                GtkArrowType arrow_type,
  2457                                GtkTextDirection direction)
  2459     GtkStateType state_type = ConvertGtkState(state);
  2460     GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
  2461     GtkStyle* style;
  2462     gint arrow_size = MIN(rect->width, rect->height);
  2463     gint x = rect->x + (rect->width - arrow_size) / 2;
  2464     gint y = rect->y + (rect->height - arrow_size) / 2;
  2466     ensure_tab_widget();
  2468     style = gTabWidget->style;
  2469     TSOffsetStyleGCs(style, rect->x, rect->y);
  2471     if (direction == GTK_TEXT_DIR_RTL) {
  2472         arrow_type = (arrow_type == GTK_ARROW_LEFT) ?
  2473                          GTK_ARROW_RIGHT : GTK_ARROW_LEFT;
  2476     gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL,
  2477                     gTabWidget, "notebook", arrow_type, TRUE,
  2478                     x, y, arrow_size, arrow_size);
  2480     return MOZ_GTK_SUCCESS;
  2483 static gint
  2484 moz_gtk_menu_bar_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2485                        GdkRectangle* cliprect, GtkTextDirection direction)
  2487     GtkStyle* style;
  2488     GtkShadowType shadow_type;
  2489     ensure_menu_bar_widget();
  2490     gtk_widget_set_direction(gMenuBarWidget, direction);
  2492     gtk_widget_style_get(gMenuBarWidget, "shadow-type", &shadow_type, NULL);
  2494     style = gMenuBarWidget->style;
  2496     TSOffsetStyleGCs(style, rect->x, rect->y);
  2497     gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL,
  2498                                        cliprect, rect->x, rect->y,
  2499                                        rect->width, rect->height);
  2501     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, shadow_type,
  2502                   cliprect, gMenuBarWidget, "menubar", rect->x, rect->y,
  2503                   rect->width, rect->height);
  2504     return MOZ_GTK_SUCCESS;
  2507 static gint
  2508 moz_gtk_menu_popup_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2509                          GdkRectangle* cliprect, GtkTextDirection direction)
  2511     GtkStyle* style;
  2512     ensure_menu_popup_widget();
  2513     gtk_widget_set_direction(gMenuPopupWidget, direction);
  2515     style = gMenuPopupWidget->style;
  2517     TSOffsetStyleGCs(style, rect->x, rect->y);
  2518     gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL,
  2519                                        cliprect, rect->x, rect->y,
  2520                                        rect->width, rect->height);
  2521     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, 
  2522                   cliprect, gMenuPopupWidget, "menu",
  2523                   rect->x, rect->y, rect->width, rect->height);
  2525     return MOZ_GTK_SUCCESS;
  2528 static gint
  2529 moz_gtk_menu_separator_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2530                              GdkRectangle* cliprect, GtkTextDirection direction)
  2532     GtkStyle* style;
  2533     gboolean wide_separators;
  2534     gint separator_height;
  2535     guint horizontal_padding;
  2536     gint paint_height;
  2538     ensure_menu_separator_widget();
  2539     gtk_widget_set_direction(gMenuSeparatorWidget, direction);
  2541     style = gMenuSeparatorWidget->style;
  2543     gtk_widget_style_get(gMenuSeparatorWidget,
  2544                          "wide-separators",    &wide_separators,
  2545                          "separator-height",   &separator_height,
  2546                          "horizontal-padding", &horizontal_padding,
  2547                          NULL);
  2549     TSOffsetStyleGCs(style, rect->x, rect->y);
  2551     if (wide_separators) {
  2552         if (separator_height > rect->height)
  2553             separator_height = rect->height;
  2555         gtk_paint_box(style, drawable,
  2556                       GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
  2557                       cliprect, gMenuSeparatorWidget, "hseparator",
  2558                       rect->x + horizontal_padding + style->xthickness,
  2559                       rect->y + (rect->height - separator_height - style->ythickness) / 2,
  2560                       rect->width - 2 * (horizontal_padding + style->xthickness),
  2561                       separator_height);
  2562     } else {
  2563         paint_height = style->ythickness;
  2564         if (paint_height > rect->height)
  2565             paint_height = rect->height;
  2567         gtk_paint_hline(style, drawable,
  2568                         GTK_STATE_NORMAL, cliprect, gMenuSeparatorWidget,
  2569                         "menuitem",
  2570                         rect->x + horizontal_padding + style->xthickness,
  2571                         rect->x + rect->width - horizontal_padding - style->xthickness - 1,
  2572                         rect->y + (rect->height - style->ythickness) / 2);
  2575     return MOZ_GTK_SUCCESS;
  2578 static gint
  2579 moz_gtk_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2580                         GdkRectangle* cliprect, GtkWidgetState* state,
  2581                         gint flags, GtkTextDirection direction)
  2583     GtkStyle* style;
  2584     GtkShadowType shadow_type;
  2585     GtkWidget* item_widget;
  2587     if (state->inHover && !state->disabled) {
  2588         if (flags & MOZ_TOPLEVEL_MENU_ITEM) {
  2589             ensure_menu_bar_item_widget();
  2590             item_widget = gMenuBarItemWidget;
  2591         } else {
  2592             ensure_menu_item_widget();
  2593             item_widget = gMenuItemWidget;
  2595         gtk_widget_set_direction(item_widget, direction);
  2597         style = item_widget->style;
  2598         TSOffsetStyleGCs(style, rect->x, rect->y);
  2600         gtk_widget_style_get(item_widget, "selected-shadow-type",
  2601                              &shadow_type, NULL);
  2603         gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, shadow_type,
  2604                       cliprect, item_widget, "menuitem", rect->x, rect->y,
  2605                       rect->width, rect->height);
  2608     return MOZ_GTK_SUCCESS;
  2611 static gint
  2612 moz_gtk_menu_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2613                          GdkRectangle* cliprect, GtkWidgetState* state,
  2614                          GtkTextDirection direction)
  2616     GtkStyle* style;
  2617     GtkStateType state_type = ConvertGtkState(state);
  2619     ensure_menu_item_widget();
  2620     gtk_widget_set_direction(gMenuItemWidget, direction);
  2622     style = gMenuItemWidget->style;
  2624     TSOffsetStyleGCs(style, rect->x, rect->y);
  2625     gtk_paint_arrow(style, drawable, state_type,
  2626                     (state_type == GTK_STATE_PRELIGHT) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
  2627                     cliprect, gMenuItemWidget, "menuitem",
  2628                     (direction == GTK_TEXT_DIR_LTR) ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT,
  2629                     TRUE, rect->x, rect->y, rect->width, rect->height);
  2631     return MOZ_GTK_SUCCESS;
  2634 static gint
  2635 moz_gtk_check_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2636                               GdkRectangle* cliprect, GtkWidgetState* state,
  2637                               gboolean checked, gboolean isradio,
  2638                               GtkTextDirection direction)
  2640     GtkStateType state_type = ConvertGtkState(state);
  2641     GtkStyle* style;
  2642     GtkShadowType shadow_type = (checked)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
  2643     gint offset;
  2644     gint indicator_size, horizontal_padding;
  2645     gint x, y;
  2647     moz_gtk_menu_item_paint(drawable, rect, cliprect, state, FALSE, direction);
  2649     ensure_check_menu_item_widget();
  2650     gtk_widget_set_direction(gCheckMenuItemWidget, direction);
  2652     gtk_widget_style_get (gCheckMenuItemWidget,
  2653                           "indicator-size", &indicator_size,
  2654                           "horizontal-padding", &horizontal_padding,
  2655                           NULL);
  2657     if (checked || GTK_CHECK_MENU_ITEM(gCheckMenuItemWidget)->always_show_toggle) {
  2658       style = gCheckMenuItemWidget->style;
  2660       offset = GTK_CONTAINER(gCheckMenuItemWidget)->border_width +
  2661              gCheckMenuItemWidget->style->xthickness + 2;
  2663       x = (direction == GTK_TEXT_DIR_RTL) ?
  2664             rect->width - indicator_size - offset - horizontal_padding: rect->x + offset + horizontal_padding;
  2665       y = rect->y + (rect->height - indicator_size) / 2;
  2667       TSOffsetStyleGCs(style, x, y);
  2668       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gCheckMenuItemWidget),
  2669                                      checked);
  2671       if (isradio) {
  2672         gtk_paint_option(style, drawable, state_type, shadow_type, cliprect,
  2673                          gCheckMenuItemWidget, "option",
  2674                          x, y, indicator_size, indicator_size);
  2675       } else {
  2676         gtk_paint_check(style, drawable, state_type, shadow_type, cliprect,
  2677                         gCheckMenuItemWidget, "check",
  2678                         x, y, indicator_size, indicator_size);
  2682     return MOZ_GTK_SUCCESS;
  2685 static gint
  2686 moz_gtk_window_paint(GdkDrawable* drawable, GdkRectangle* rect,
  2687                      GdkRectangle* cliprect, GtkTextDirection direction)
  2689     GtkStyle* style;
  2691     ensure_window_widget();
  2692     gtk_widget_set_direction(gProtoWindow, direction);
  2694     style = gProtoWindow->style;
  2696     TSOffsetStyleGCs(style, rect->x, rect->y);
  2697     gtk_style_apply_default_background(style, drawable, TRUE,
  2698                                        GTK_STATE_NORMAL,
  2699                                        cliprect, rect->x, rect->y,
  2700                                        rect->width, rect->height);
  2701     return MOZ_GTK_SUCCESS;
  2704 gint
  2705 moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top,
  2706                           gint* right, gint* bottom, GtkTextDirection direction,
  2707                           gboolean inhtml)
  2709     GtkWidget* w;
  2711     switch (widget) {
  2712     case MOZ_GTK_BUTTON:
  2714             GtkBorder inner_border;
  2715             gboolean interior_focus;
  2716             gint focus_width, focus_pad;
  2718             ensure_button_widget();
  2719             *left = *top = *right = *bottom = GTK_CONTAINER(gButtonWidget)->border_width;
  2721             /* Don't add this padding in HTML, otherwise the buttons will
  2722                become too big and stuff the layout. */
  2723             if (!inhtml) {
  2724                 moz_gtk_widget_get_focus(gButtonWidget, &interior_focus, &focus_width, &focus_pad);
  2725                 moz_gtk_button_get_inner_border(gButtonWidget, &inner_border);
  2726                 *left += focus_width + focus_pad + inner_border.left;
  2727                 *right += focus_width + focus_pad + inner_border.right;
  2728                 *top += focus_width + focus_pad + inner_border.top;
  2729                 *bottom += focus_width + focus_pad + inner_border.bottom;
  2732             *left += gButtonWidget->style->xthickness;
  2733             *right += gButtonWidget->style->xthickness;
  2734             *top += gButtonWidget->style->ythickness;
  2735             *bottom += gButtonWidget->style->ythickness;
  2736             return MOZ_GTK_SUCCESS;
  2738     case MOZ_GTK_ENTRY:
  2739         ensure_entry_widget();
  2740         w = gEntryWidget;
  2741         break;
  2742     case MOZ_GTK_TREEVIEW:
  2743         ensure_tree_view_widget();
  2744         w = gTreeViewWidget;
  2745         break;
  2746     case MOZ_GTK_TREE_HEADER_CELL:
  2748             /* A Tree Header in GTK is just a different styled button 
  2749              * It must be placed in a TreeView for getting the correct style
  2750              * assigned.
  2751              * That is why the following code is the same as for MOZ_GTK_BUTTON.  
  2752              * */
  2754             GtkBorder inner_border;
  2755             gboolean interior_focus;
  2756             gint focus_width, focus_pad;
  2758             ensure_tree_header_cell_widget();
  2759             *left = *top = *right = *bottom = GTK_CONTAINER(gTreeHeaderCellWidget)->border_width;
  2761             moz_gtk_widget_get_focus(gTreeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad);
  2762             moz_gtk_button_get_inner_border(gTreeHeaderCellWidget, &inner_border);
  2763             *left += focus_width + focus_pad + inner_border.left;
  2764             *right += focus_width + focus_pad + inner_border.right;
  2765             *top += focus_width + focus_pad + inner_border.top;
  2766             *bottom += focus_width + focus_pad + inner_border.bottom;
  2768             *left += gTreeHeaderCellWidget->style->xthickness;
  2769             *right += gTreeHeaderCellWidget->style->xthickness;
  2770             *top += gTreeHeaderCellWidget->style->ythickness;
  2771             *bottom += gTreeHeaderCellWidget->style->ythickness;
  2772             return MOZ_GTK_SUCCESS;
  2774     case MOZ_GTK_TREE_HEADER_SORTARROW:
  2775         ensure_tree_header_cell_widget();
  2776         w = gTreeHeaderSortArrowWidget;
  2777         break;
  2778     case MOZ_GTK_DROPDOWN_ENTRY:
  2779         ensure_combo_box_entry_widgets();
  2780         w = gComboBoxEntryTextareaWidget;
  2781         break;
  2782     case MOZ_GTK_DROPDOWN_ARROW:
  2783         ensure_combo_box_entry_widgets();
  2784         w = gComboBoxEntryButtonWidget;
  2785         break;
  2786     case MOZ_GTK_DROPDOWN:
  2788             /* We need to account for the arrow on the dropdown, so text
  2789              * doesn't come too close to the arrow, or in some cases spill
  2790              * into the arrow. */
  2791             gboolean ignored_interior_focus, wide_separators;
  2792             gint focus_width, focus_pad, separator_width;
  2793             GtkRequisition arrow_req;
  2795             ensure_combo_box_widgets();
  2797             *left = GTK_CONTAINER(gComboBoxButtonWidget)->border_width;
  2799             if (!inhtml) {
  2800                 moz_gtk_widget_get_focus(gComboBoxButtonWidget,
  2801                                          &ignored_interior_focus,
  2802                                          &focus_width, &focus_pad);
  2803                 *left += focus_width + focus_pad;
  2806             *top = *left + gComboBoxButtonWidget->style->ythickness;
  2807             *left += gComboBoxButtonWidget->style->xthickness;
  2809             *right = *left; *bottom = *top;
  2811             /* If there is no separator, don't try to count its width. */
  2812             separator_width = 0;
  2813             if (gComboBoxSeparatorWidget) {
  2814                 gtk_widget_style_get(gComboBoxSeparatorWidget,
  2815                                      "wide-separators", &wide_separators,
  2816                                      "separator-width", &separator_width,
  2817                                      NULL);
  2819                 if (!wide_separators)
  2820                     separator_width =
  2821                         XTHICKNESS(gComboBoxSeparatorWidget->style);
  2824             gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req);
  2826             if (direction == GTK_TEXT_DIR_RTL)
  2827                 *left += separator_width + arrow_req.width;
  2828             else
  2829                 *right += separator_width + arrow_req.width;
  2831             return MOZ_GTK_SUCCESS;
  2833     case MOZ_GTK_TABPANELS:
  2834         ensure_tab_widget();
  2835         w = gTabWidget;
  2836         break;
  2837     case MOZ_GTK_PROGRESSBAR:
  2838         ensure_progress_widget();
  2839         w = gProgressWidget;
  2840         break;
  2841     case MOZ_GTK_SPINBUTTON_ENTRY:
  2842     case MOZ_GTK_SPINBUTTON_UP:
  2843     case MOZ_GTK_SPINBUTTON_DOWN:
  2844         ensure_spin_widget();
  2845         w = gSpinWidget;
  2846         break;
  2847     case MOZ_GTK_SCALE_HORIZONTAL:
  2848         ensure_scale_widget();
  2849         w = gHScaleWidget;
  2850         break;
  2851     case MOZ_GTK_SCALE_VERTICAL:
  2852         ensure_scale_widget();
  2853         w = gVScaleWidget;
  2854         break;
  2855     case MOZ_GTK_FRAME:
  2856         ensure_frame_widget();
  2857         w = gFrameWidget;
  2858         break;
  2859     case MOZ_GTK_CHECKBUTTON_LABEL:
  2860     case MOZ_GTK_RADIOBUTTON_LABEL:
  2862             gboolean interior_focus;
  2863             gint focus_width, focus_pad;
  2865             /* If the focus is interior, then the label has a border of
  2866                (focus_width + focus_pad). */
  2867             if (widget == MOZ_GTK_CHECKBUTTON_LABEL) {
  2868                 ensure_checkbox_widget();
  2869                 moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus,
  2870                                            &focus_width, &focus_pad);
  2872             else {
  2873                 ensure_radiobutton_widget();
  2874                 moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus,
  2875                                         &focus_width, &focus_pad);
  2878             if (interior_focus)
  2879                 *left = *top = *right = *bottom = (focus_width + focus_pad);
  2880             else
  2881                 *left = *top = *right = *bottom = 0;
  2883             return MOZ_GTK_SUCCESS;
  2886     case MOZ_GTK_CHECKBUTTON_CONTAINER:
  2887     case MOZ_GTK_RADIOBUTTON_CONTAINER:
  2889             gboolean interior_focus;
  2890             gint focus_width, focus_pad;
  2892             /* If the focus is _not_ interior, then the container has a border
  2893                of (focus_width + focus_pad). */
  2894             if (widget == MOZ_GTK_CHECKBUTTON_CONTAINER) {
  2895                 ensure_checkbox_widget();
  2896                 moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus,
  2897                                            &focus_width, &focus_pad);
  2898                 w = gCheckboxWidget;
  2899             } else {
  2900                 ensure_radiobutton_widget();
  2901                 moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus,
  2902                                         &focus_width, &focus_pad);
  2903                 w = gRadiobuttonWidget;
  2906             *left = *top = *right = *bottom = GTK_CONTAINER(w)->border_width;
  2908             if (!interior_focus) {
  2909                 *left += (focus_width + focus_pad);
  2910                 *right += (focus_width + focus_pad);
  2911                 *top += (focus_width + focus_pad);
  2912                 *bottom += (focus_width + focus_pad);
  2915             return MOZ_GTK_SUCCESS;
  2917     case MOZ_GTK_MENUPOPUP:
  2918         ensure_menu_popup_widget();
  2919         w = gMenuPopupWidget;
  2920         break;
  2921     case MOZ_GTK_MENUITEM:
  2922         ensure_menu_item_widget();
  2923         ensure_menu_bar_item_widget();
  2924         w = gMenuItemWidget;
  2925         break;
  2926     case MOZ_GTK_CHECKMENUITEM:
  2927     case MOZ_GTK_RADIOMENUITEM:
  2928         ensure_check_menu_item_widget();
  2929         w = gCheckMenuItemWidget;
  2930         break;
  2931     case MOZ_GTK_TAB:
  2932         ensure_tab_widget();
  2933         w = gTabWidget;
  2934         break;
  2935     /* These widgets have no borders, since they are not containers. */
  2936     case MOZ_GTK_SPLITTER_HORIZONTAL:
  2937     case MOZ_GTK_SPLITTER_VERTICAL:
  2938     case MOZ_GTK_CHECKBUTTON:
  2939     case MOZ_GTK_RADIOBUTTON:
  2940     case MOZ_GTK_SCROLLBAR_BUTTON:
  2941     case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
  2942     case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
  2943     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
  2944     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
  2945     case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
  2946     case MOZ_GTK_SCALE_THUMB_VERTICAL:
  2947     case MOZ_GTK_GRIPPER:
  2948     case MOZ_GTK_PROGRESS_CHUNK:
  2949     case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
  2950     case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
  2951     case MOZ_GTK_TREEVIEW_EXPANDER:
  2952     case MOZ_GTK_TOOLBAR_SEPARATOR:
  2953     case MOZ_GTK_MENUSEPARATOR:
  2954     /* These widgets have no borders.*/
  2955     case MOZ_GTK_SPINBUTTON:
  2956     case MOZ_GTK_TOOLTIP:
  2957     case MOZ_GTK_WINDOW:
  2958     case MOZ_GTK_RESIZER:
  2959     case MOZ_GTK_MENUARROW:
  2960     case MOZ_GTK_TOOLBARBUTTON_ARROW:
  2961     case MOZ_GTK_TOOLBAR:
  2962     case MOZ_GTK_MENUBAR:
  2963     case MOZ_GTK_TAB_SCROLLARROW:
  2964         *left = *top = *right = *bottom = 0;
  2965         return MOZ_GTK_SUCCESS;
  2966     default:
  2967         g_warning("Unsupported widget type: %d", widget);
  2968         return MOZ_GTK_UNKNOWN_WIDGET;
  2971     *right = *left = XTHICKNESS(w->style);
  2972     *bottom = *top = YTHICKNESS(w->style);
  2974     return MOZ_GTK_SUCCESS;
  2977 gint
  2978 moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height)
  2980     /*
  2981      * We get the requisition of the drop down button, which includes
  2982      * all padding, border and focus line widths the button uses,
  2983      * as well as the minimum arrow size and its padding
  2984      * */
  2985     GtkRequisition requisition;
  2986     ensure_combo_box_entry_widgets();
  2988     gtk_widget_size_request(gComboBoxEntryButtonWidget, &requisition);
  2989     *width = requisition.width;
  2990     *height = requisition.height;
  2992     return MOZ_GTK_SUCCESS;
  2995 gint
  2996 moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height)
  2998     gint arrow_size;
  3000     ensure_tab_widget();
  3001     gtk_widget_style_get(gTabWidget,
  3002                          "scroll-arrow-hlength", &arrow_size,
  3003                          NULL);
  3005     *height = *width = arrow_size;
  3007     return MOZ_GTK_SUCCESS;
  3010 gint
  3011 moz_gtk_get_arrow_size(gint* width, gint* height)
  3013     GtkRequisition requisition;
  3014     ensure_button_arrow_widget();
  3016     gtk_widget_size_request(gButtonArrowWidget, &requisition);
  3017     *width = requisition.width;
  3018     *height = requisition.height;
  3020     return MOZ_GTK_SUCCESS;
  3023 gint
  3024 moz_gtk_get_toolbar_separator_width(gint* size)
  3026     gboolean wide_separators;
  3027     gint separator_width;
  3028     GtkStyle* style;
  3030     ensure_toolbar_widget();
  3032     style = gToolbarWidget->style;
  3034     gtk_widget_style_get(gToolbarWidget,
  3035                          "space-size", size,
  3036                          "wide-separators",  &wide_separators,
  3037                          "separator-width", &separator_width,
  3038                          NULL);
  3040     /* Just in case... */
  3041     *size = MAX(*size, (wide_separators ? separator_width : style->xthickness));
  3043     return MOZ_GTK_SUCCESS;
  3046 gint
  3047 moz_gtk_get_expander_size(gint* size)
  3049     ensure_expander_widget();
  3050     gtk_widget_style_get(gExpanderWidget,
  3051                          "expander-size", size,
  3052                          NULL);
  3054     return MOZ_GTK_SUCCESS;
  3057 gint
  3058 moz_gtk_get_treeview_expander_size(gint* size)
  3060     ensure_tree_view_widget();
  3061     gtk_widget_style_get(gTreeViewWidget,
  3062                          "expander-size", size,
  3063                          NULL);
  3065     return MOZ_GTK_SUCCESS;
  3068 gint
  3069 moz_gtk_get_menu_separator_height(gint *size)
  3071     gboolean wide_separators;
  3072     gint     separator_height;
  3074     ensure_menu_separator_widget();
  3076     gtk_widget_style_get(gMenuSeparatorWidget,
  3077                           "wide-separators",  &wide_separators,
  3078                           "separator-height", &separator_height,
  3079                           NULL);
  3081     if (wide_separators)
  3082         *size = separator_height + gMenuSeparatorWidget->style->ythickness;
  3083     else
  3084         *size = gMenuSeparatorWidget->style->ythickness * 2;
  3086     return MOZ_GTK_SUCCESS;
  3089 gint
  3090 moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height)
  3092   GtkWidget* widget;
  3094   ensure_scale_widget();
  3095   widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
  3097   gtk_widget_style_get (widget,
  3098                         "slider_length", thumb_length,
  3099                         "slider_width", thumb_height,
  3100                         NULL);
  3102   return MOZ_GTK_SUCCESS;
  3105 gint
  3106 moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
  3108     ensure_scrollbar_widget();
  3110     gtk_widget_style_get (gHorizScrollbarWidget,
  3111                           "slider_width", &metrics->slider_width,
  3112                           "trough_border", &metrics->trough_border,
  3113                           "stepper_size", &metrics->stepper_size,
  3114                           "stepper_spacing", &metrics->stepper_spacing,
  3115                           NULL);
  3117     metrics->min_slider_size =
  3118         GTK_RANGE(gHorizScrollbarWidget)->min_slider_size;
  3120     return MOZ_GTK_SUCCESS;
  3123 gboolean
  3124 moz_gtk_images_in_menus()
  3126     gboolean result;
  3127     GtkSettings* settings;
  3129     ensure_image_menu_item_widget();
  3130     settings = gtk_widget_get_settings(gImageMenuItemWidget);
  3132     g_object_get(settings, "gtk-menu-images", &result, NULL);
  3133     return result;
  3136 gboolean
  3137 moz_gtk_images_in_buttons()
  3139     gboolean result;
  3140     GtkSettings* settings;
  3142     ensure_button_widget();
  3143     settings = gtk_widget_get_settings(gButtonWidget);
  3145     g_object_get(settings, "gtk-button-images", &result, NULL);
  3146     return result;
  3149 gint
  3150 moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable,
  3151                      GdkRectangle* rect, GdkRectangle* cliprect,
  3152                      GtkWidgetState* state, gint flags,
  3153                      GtkTextDirection direction)
  3155     switch (widget) {
  3156     case MOZ_GTK_BUTTON:
  3157         if (state->depressed) {
  3158             ensure_toggle_button_widget();
  3159             return moz_gtk_button_paint(drawable, rect, cliprect, state,
  3160                                         (GtkReliefStyle) flags,
  3161                                         gToggleButtonWidget, direction);
  3163         ensure_button_widget();
  3164         return moz_gtk_button_paint(drawable, rect, cliprect, state,
  3165                                     (GtkReliefStyle) flags, gButtonWidget,
  3166                                     direction);
  3167         break;
  3168     case MOZ_GTK_CHECKBUTTON:
  3169     case MOZ_GTK_RADIOBUTTON:
  3170         return moz_gtk_toggle_paint(drawable, rect, cliprect, state,
  3171                                     !!(flags & MOZ_GTK_WIDGET_CHECKED),
  3172                                     !!(flags & MOZ_GTK_WIDGET_INCONSISTENT),
  3173                                     (widget == MOZ_GTK_RADIOBUTTON),
  3174                                     direction);
  3175         break;
  3176     case MOZ_GTK_SCROLLBAR_BUTTON:
  3177         return moz_gtk_scrollbar_button_paint(drawable, rect, cliprect, state,
  3178                                               (GtkScrollbarButtonFlags) flags,
  3179                                               direction);
  3180         break;
  3181     case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
  3182     case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
  3183         return moz_gtk_scrollbar_trough_paint(widget, drawable, rect,
  3184                                               cliprect, state, direction);
  3185         break;
  3186     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
  3187     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
  3188         return moz_gtk_scrollbar_thumb_paint(widget, drawable, rect,
  3189                                              cliprect, state, direction);
  3190         break;
  3191     case MOZ_GTK_SCALE_HORIZONTAL:
  3192     case MOZ_GTK_SCALE_VERTICAL:
  3193         return moz_gtk_scale_paint(drawable, rect, cliprect, state,
  3194                                    (GtkOrientation) flags, direction);
  3195         break;
  3196     case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
  3197     case MOZ_GTK_SCALE_THUMB_VERTICAL:
  3198         return moz_gtk_scale_thumb_paint(drawable, rect, cliprect, state,
  3199                                          (GtkOrientation) flags, direction);
  3200         break;
  3201     case MOZ_GTK_SPINBUTTON:
  3202         return moz_gtk_spin_paint(drawable, rect, direction);
  3203         break;
  3204     case MOZ_GTK_SPINBUTTON_UP:
  3205     case MOZ_GTK_SPINBUTTON_DOWN:
  3206         return moz_gtk_spin_updown_paint(drawable, rect,
  3207                                          (widget == MOZ_GTK_SPINBUTTON_DOWN),
  3208                                          state, direction);
  3209         break;
  3210     case MOZ_GTK_SPINBUTTON_ENTRY:
  3211         ensure_spin_widget();
  3212         return moz_gtk_entry_paint(drawable, rect, cliprect, state,
  3213                                    gSpinWidget, direction);
  3214         break;
  3215     case MOZ_GTK_GRIPPER:
  3216         return moz_gtk_gripper_paint(drawable, rect, cliprect, state,
  3217                                      direction);
  3218         break;
  3219     case MOZ_GTK_TREEVIEW:
  3220         return moz_gtk_treeview_paint(drawable, rect, cliprect, state,
  3221                                       direction);
  3222         break;
  3223     case MOZ_GTK_TREE_HEADER_CELL:
  3224         return moz_gtk_tree_header_cell_paint(drawable, rect, cliprect, state,
  3225                                               flags, direction);
  3226         break;
  3227     case MOZ_GTK_TREE_HEADER_SORTARROW:
  3228         return moz_gtk_tree_header_sort_arrow_paint(drawable, rect, cliprect,
  3229                                                     state,
  3230                                                     (GtkArrowType) flags,
  3231                                                     direction);
  3232         break;
  3233     case MOZ_GTK_TREEVIEW_EXPANDER:
  3234         return moz_gtk_treeview_expander_paint(drawable, rect, cliprect, state,
  3235                                                (GtkExpanderStyle) flags, direction);
  3236         break;
  3237     case MOZ_GTK_ENTRY:
  3238         ensure_entry_widget();
  3239         return moz_gtk_entry_paint(drawable, rect, cliprect, state,
  3240                                    gEntryWidget, direction);
  3241         break;
  3242     case MOZ_GTK_DROPDOWN:
  3243         return moz_gtk_combo_box_paint(drawable, rect, cliprect, state,
  3244                                        (gboolean) flags, direction);
  3245         break;
  3246     case MOZ_GTK_DROPDOWN_ARROW:
  3247         return moz_gtk_combo_box_entry_button_paint(drawable, rect, cliprect,
  3248                                                     state, flags, direction);
  3249         break;
  3250     case MOZ_GTK_DROPDOWN_ENTRY:
  3251         ensure_combo_box_entry_widgets();
  3252         return moz_gtk_entry_paint(drawable, rect, cliprect, state,
  3253                                    gComboBoxEntryTextareaWidget, direction);
  3254         break;
  3255     case MOZ_GTK_CHECKBUTTON_CONTAINER:
  3256     case MOZ_GTK_RADIOBUTTON_CONTAINER:
  3257         return moz_gtk_container_paint(drawable, rect, cliprect, state,
  3258                                        (widget == MOZ_GTK_RADIOBUTTON_CONTAINER),
  3259                                        direction);
  3260         break;
  3261     case MOZ_GTK_CHECKBUTTON_LABEL:
  3262     case MOZ_GTK_RADIOBUTTON_LABEL:
  3263         return moz_gtk_toggle_label_paint(drawable, rect, cliprect, state,
  3264                                           (widget == MOZ_GTK_RADIOBUTTON_LABEL),
  3265                                           direction);
  3266         break;
  3267     case MOZ_GTK_TOOLBAR:
  3268         return moz_gtk_toolbar_paint(drawable, rect, cliprect, direction);
  3269         break;
  3270     case MOZ_GTK_TOOLBAR_SEPARATOR:
  3271         return moz_gtk_toolbar_separator_paint(drawable, rect, cliprect,
  3272                                                direction);
  3273         break;
  3274     case MOZ_GTK_TOOLTIP:
  3275         return moz_gtk_tooltip_paint(drawable, rect, cliprect, direction);
  3276         break;
  3277     case MOZ_GTK_FRAME:
  3278         return moz_gtk_frame_paint(drawable, rect, cliprect, direction);
  3279         break;
  3280     case MOZ_GTK_RESIZER:
  3281         return moz_gtk_resizer_paint(drawable, rect, cliprect, state,
  3282                                      direction);
  3283         break;
  3284     case MOZ_GTK_PROGRESSBAR:
  3285         return moz_gtk_progressbar_paint(drawable, rect, cliprect, direction);
  3286         break;
  3287     case MOZ_GTK_PROGRESS_CHUNK:
  3288     case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
  3289     case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
  3290         return moz_gtk_progress_chunk_paint(drawable, rect, cliprect,
  3291                                             direction, widget);
  3292         break;
  3293     case MOZ_GTK_TAB:
  3294         return moz_gtk_tab_paint(drawable, rect, cliprect, state,
  3295                                  (GtkTabFlags) flags, direction);
  3296         break;
  3297     case MOZ_GTK_TABPANELS:
  3298         return moz_gtk_tabpanels_paint(drawable, rect, cliprect, direction);
  3299         break;
  3300     case MOZ_GTK_TAB_SCROLLARROW:
  3301         return moz_gtk_tab_scroll_arrow_paint(drawable, rect, cliprect, state,
  3302                                               (GtkArrowType) flags, direction);
  3303         break;
  3304     case MOZ_GTK_MENUBAR:
  3305         return moz_gtk_menu_bar_paint(drawable, rect, cliprect, direction);
  3306         break;
  3307     case MOZ_GTK_MENUPOPUP:
  3308         return moz_gtk_menu_popup_paint(drawable, rect, cliprect, direction);
  3309         break;
  3310     case MOZ_GTK_MENUSEPARATOR:
  3311         return moz_gtk_menu_separator_paint(drawable, rect, cliprect,
  3312                                             direction);
  3313         break;
  3314     case MOZ_GTK_MENUITEM:
  3315         return moz_gtk_menu_item_paint(drawable, rect, cliprect, state, flags,
  3316                                        direction);
  3317         break;
  3318     case MOZ_GTK_MENUARROW:
  3319         return moz_gtk_menu_arrow_paint(drawable, rect, cliprect, state,
  3320                                         direction);
  3321         break;
  3322     case MOZ_GTK_TOOLBARBUTTON_ARROW:
  3323         return moz_gtk_arrow_paint(drawable, rect, cliprect, state,
  3324                                    (GtkArrowType) flags, direction);
  3325         break;
  3326     case MOZ_GTK_CHECKMENUITEM:
  3327     case MOZ_GTK_RADIOMENUITEM:
  3328         return moz_gtk_check_menu_item_paint(drawable, rect, cliprect, state,
  3329                                              (gboolean) flags,
  3330                                              (widget == MOZ_GTK_RADIOMENUITEM),
  3331                                              direction);
  3332         break;
  3333     case MOZ_GTK_SPLITTER_HORIZONTAL:
  3334         return moz_gtk_vpaned_paint(drawable, rect, cliprect, state);
  3335         break;
  3336     case MOZ_GTK_SPLITTER_VERTICAL:
  3337         return moz_gtk_hpaned_paint(drawable, rect, cliprect, state);
  3338         break;
  3339     case MOZ_GTK_WINDOW:
  3340         return moz_gtk_window_paint(drawable, rect, cliprect, direction);
  3341         break;
  3342     default:
  3343         g_warning("Unknown widget type: %d", widget);
  3346     return MOZ_GTK_UNKNOWN_WIDGET;
  3349 GtkWidget* moz_gtk_get_scrollbar_widget(void)
  3351     NS_ASSERTION(is_initialized, "Forgot to call moz_gtk_init()");
  3352     ensure_scrollbar_widget();
  3353     return gHorizScrollbarWidget;
  3356 gint
  3357 moz_gtk_shutdown()
  3359     GtkWidgetClass *entry_class;
  3361     if (gTooltipWidget)
  3362         gtk_widget_destroy(gTooltipWidget);
  3363     /* This will destroy all of our widgets */
  3364     if (gProtoWindow)
  3365         gtk_widget_destroy(gProtoWindow);
  3367     gProtoWindow = NULL;
  3368     gProtoLayout = NULL;
  3369     gButtonWidget = NULL;
  3370     gToggleButtonWidget = NULL;
  3371     gButtonArrowWidget = NULL;
  3372     gCheckboxWidget = NULL;
  3373     gRadiobuttonWidget = NULL;
  3374     gHorizScrollbarWidget = NULL;
  3375     gVertScrollbarWidget = NULL;
  3376     gSpinWidget = NULL;
  3377     gHScaleWidget = NULL;
  3378     gVScaleWidget = NULL;
  3379     gEntryWidget = NULL;
  3380     gComboBoxWidget = NULL;
  3381     gComboBoxButtonWidget = NULL;
  3382     gComboBoxSeparatorWidget = NULL;
  3383     gComboBoxArrowWidget = NULL;
  3384     gComboBoxEntryWidget = NULL;
  3385     gComboBoxEntryButtonWidget = NULL;
  3386     gComboBoxEntryArrowWidget = NULL;
  3387     gComboBoxEntryTextareaWidget = NULL;
  3388     gHandleBoxWidget = NULL;
  3389     gToolbarWidget = NULL;
  3390     gStatusbarWidget = NULL;
  3391     gFrameWidget = NULL;
  3392     gProgressWidget = NULL;
  3393     gTabWidget = NULL;
  3394     gTooltipWidget = NULL;
  3395     gMenuBarWidget = NULL;
  3396     gMenuBarItemWidget = NULL;
  3397     gMenuPopupWidget = NULL;
  3398     gMenuItemWidget = NULL;
  3399     gImageMenuItemWidget = NULL;
  3400     gCheckMenuItemWidget = NULL;
  3401     gTreeViewWidget = NULL;
  3402     gMiddleTreeViewColumn = NULL;
  3403     gTreeHeaderCellWidget = NULL;
  3404     gTreeHeaderSortArrowWidget = NULL;
  3405     gExpanderWidget = NULL;
  3406     gToolbarSeparatorWidget = NULL;
  3407     gMenuSeparatorWidget = NULL;
  3408     gHPanedWidget = NULL;
  3409     gVPanedWidget = NULL;
  3410     gScrolledWindowWidget = NULL;
  3412     entry_class = g_type_class_peek(GTK_TYPE_ENTRY);
  3413     g_type_class_unref(entry_class);
  3415     is_initialized = FALSE;
  3417     return MOZ_GTK_SUCCESS;

mercurial