michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * This file contains painting functions for each of the gtk2 widgets. michael@0: * Adapted from the gtkdrawing.c, and gtk+2.0 source. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include "gtkdrawing.h" michael@0: #include "nsDebug.h" michael@0: #include "prinrval.h" michael@0: michael@0: #include michael@0: michael@0: static GtkWidget* gProtoWindow; michael@0: static GtkWidget* gProtoLayout; michael@0: static GtkWidget* gButtonWidget; michael@0: static GtkWidget* gToggleMenuButtonWidget; michael@0: static GtkWidget* gButtonArrowWidget; michael@0: static GtkWidget* gCheckboxWidget; michael@0: static GtkWidget* gRadiobuttonWidget; michael@0: static GtkWidget* gHorizScrollbarWidget; michael@0: static GtkWidget* gVertScrollbarWidget; michael@0: static GtkWidget* gSpinWidget; michael@0: static GtkWidget* gHScaleWidget; michael@0: static GtkWidget* gVScaleWidget; michael@0: static GtkWidget* gEntryWidget; michael@0: static GtkWidget* gComboBoxWidget; michael@0: static GtkWidget* gComboBoxButtonWidget; michael@0: static GtkWidget* gComboBoxArrowWidget; michael@0: static GtkWidget* gComboBoxSeparatorWidget; michael@0: static GtkWidget* gComboBoxEntryWidget; michael@0: static GtkWidget* gComboBoxEntryTextareaWidget; michael@0: static GtkWidget* gComboBoxEntryButtonWidget; michael@0: static GtkWidget* gComboBoxEntryArrowWidget; michael@0: static GtkWidget* gHandleBoxWidget; michael@0: static GtkWidget* gToolbarWidget; michael@0: static GtkWidget* gFrameWidget; michael@0: static GtkWidget* gStatusbarWidget; michael@0: static GtkWidget* gProgressWidget; michael@0: static GtkWidget* gTabWidget; michael@0: static GtkWidget* gTooltipWidget; michael@0: static GtkWidget* gMenuBarWidget; michael@0: static GtkWidget* gMenuBarItemWidget; michael@0: static GtkWidget* gMenuPopupWidget; michael@0: static GtkWidget* gMenuItemWidget; michael@0: static GtkWidget* gImageMenuItemWidget; michael@0: static GtkWidget* gCheckMenuItemWidget; michael@0: static GtkWidget* gTreeViewWidget; michael@0: static GtkTreeViewColumn* gMiddleTreeViewColumn; michael@0: static GtkWidget* gTreeHeaderCellWidget; michael@0: static GtkWidget* gTreeHeaderSortArrowWidget; michael@0: static GtkWidget* gExpanderWidget; michael@0: static GtkWidget* gToolbarSeparatorWidget; michael@0: static GtkWidget* gMenuSeparatorWidget; michael@0: static GtkWidget* gHPanedWidget; michael@0: static GtkWidget* gVPanedWidget; michael@0: static GtkWidget* gScrolledWindowWidget; michael@0: michael@0: static style_prop_t style_prop_func; michael@0: static gboolean have_arrow_scaling; michael@0: static gboolean is_initialized; michael@0: michael@0: #define ARROW_UP 0 michael@0: #define ARROW_DOWN G_PI michael@0: #define ARROW_RIGHT G_PI_2 michael@0: #define ARROW_LEFT (G_PI+G_PI_2) michael@0: michael@0: static GtkStateFlags michael@0: GetStateFlagsFromGtkWidgetState(GtkWidgetState* state) michael@0: { michael@0: GtkStateFlags stateFlags = GTK_STATE_FLAG_NORMAL; michael@0: michael@0: if (state->disabled) michael@0: stateFlags = GTK_STATE_FLAG_INSENSITIVE; michael@0: else { michael@0: if (state->depressed || state->active) michael@0: stateFlags |= GTK_STATE_FLAG_ACTIVE; michael@0: if (state->inHover) michael@0: stateFlags |= GTK_STATE_FLAG_PRELIGHT; michael@0: if (state->focused) michael@0: stateFlags |= GTK_STATE_FLAG_FOCUSED; michael@0: } michael@0: michael@0: return stateFlags; michael@0: } michael@0: michael@0: /* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine michael@0: that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific michael@0: things they may want to do. */ michael@0: static void michael@0: moz_gtk_set_widget_name(GtkWidget* widget) michael@0: { michael@0: gtk_widget_set_name(widget, "MozillaGtkWidget"); michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_enable_style_props(style_prop_t styleGetProp) michael@0: { michael@0: style_prop_func = styleGetProp; michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_window_widget() michael@0: { michael@0: if (!gProtoWindow) { michael@0: gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP); michael@0: gtk_widget_realize(gProtoWindow); michael@0: moz_gtk_set_widget_name(gProtoWindow); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: setup_widget_prototype(GtkWidget* widget) michael@0: { michael@0: ensure_window_widget(); michael@0: if (!gProtoLayout) { michael@0: gProtoLayout = gtk_fixed_new(); michael@0: gtk_container_add(GTK_CONTAINER(gProtoWindow), gProtoLayout); michael@0: } michael@0: michael@0: gtk_container_add(GTK_CONTAINER(gProtoLayout), widget); michael@0: gtk_widget_realize(widget); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_button_widget() michael@0: { michael@0: if (!gButtonWidget) { michael@0: gButtonWidget = gtk_button_new_with_label("M"); michael@0: setup_widget_prototype(gButtonWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_hpaned_widget() michael@0: { michael@0: if (!gHPanedWidget) { michael@0: gHPanedWidget = gtk_hpaned_new(); michael@0: setup_widget_prototype(gHPanedWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_vpaned_widget() michael@0: { michael@0: if (!gVPanedWidget) { michael@0: gVPanedWidget = gtk_vpaned_new(); michael@0: setup_widget_prototype(gVPanedWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_toggle_menu_button_widget() michael@0: { michael@0: if (!gToggleMenuButtonWidget) { michael@0: gToggleMenuButtonWidget = gtk_menu_button_new(); michael@0: setup_widget_prototype(gToggleMenuButtonWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_button_arrow_widget() michael@0: { michael@0: if (!gButtonArrowWidget) { michael@0: ensure_toggle_menu_button_widget(); michael@0: gButtonArrowWidget = gtk_bin_get_child(GTK_BIN(gToggleMenuButtonWidget)); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_checkbox_widget() michael@0: { michael@0: if (!gCheckboxWidget) { michael@0: gCheckboxWidget = gtk_check_button_new_with_label("M"); michael@0: setup_widget_prototype(gCheckboxWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_radiobutton_widget() michael@0: { michael@0: if (!gRadiobuttonWidget) { michael@0: gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M"); michael@0: setup_widget_prototype(gRadiobuttonWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_scrollbar_widget() michael@0: { michael@0: if (!gVertScrollbarWidget) { michael@0: gVertScrollbarWidget = gtk_vscrollbar_new(NULL); michael@0: setup_widget_prototype(gVertScrollbarWidget); michael@0: } michael@0: if (!gHorizScrollbarWidget) { michael@0: gHorizScrollbarWidget = gtk_hscrollbar_new(NULL); michael@0: setup_widget_prototype(gHorizScrollbarWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_spin_widget() michael@0: { michael@0: if (!gSpinWidget) { michael@0: gSpinWidget = gtk_spin_button_new(NULL, 1, 0); michael@0: setup_widget_prototype(gSpinWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_scale_widget() michael@0: { michael@0: if (!gHScaleWidget) { michael@0: gHScaleWidget = gtk_hscale_new(NULL); michael@0: setup_widget_prototype(gHScaleWidget); michael@0: } michael@0: if (!gVScaleWidget) { michael@0: gVScaleWidget = gtk_vscale_new(NULL); michael@0: setup_widget_prototype(gVScaleWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_entry_widget() michael@0: { michael@0: if (!gEntryWidget) { michael@0: gEntryWidget = gtk_entry_new(); michael@0: setup_widget_prototype(gEntryWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: /* We need to have pointers to the inner widgets (button, separator, arrow) michael@0: * of the ComboBox to get the correct rendering from theme engines which michael@0: * special cases their look. Since the inner layout can change, we ask GTK michael@0: * to NULL our pointers when they are about to become invalid because the michael@0: * corresponding widgets don't exist anymore. It's the role of michael@0: * g_object_add_weak_pointer(). michael@0: * Note that if we don't find the inner widgets (which shouldn't happen), we michael@0: * fallback to use generic "non-inner" widgets, and they don't need that kind michael@0: * of weak pointer since they are explicit children of gProtoWindow and as michael@0: * such GTK holds a strong reference to them. */ michael@0: static void michael@0: moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) michael@0: { michael@0: if (GTK_IS_TOGGLE_BUTTON(widget)) { michael@0: gComboBoxButtonWidget = widget; michael@0: g_object_add_weak_pointer(G_OBJECT(widget), michael@0: (gpointer) &gComboBoxButtonWidget); michael@0: gtk_widget_realize(widget); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, michael@0: gpointer client_data) michael@0: { michael@0: if (GTK_IS_SEPARATOR(widget)) { michael@0: gComboBoxSeparatorWidget = widget; michael@0: g_object_add_weak_pointer(G_OBJECT(widget), michael@0: (gpointer) &gComboBoxSeparatorWidget); michael@0: } else if (GTK_IS_ARROW(widget)) { michael@0: gComboBoxArrowWidget = widget; michael@0: g_object_add_weak_pointer(G_OBJECT(widget), michael@0: (gpointer) &gComboBoxArrowWidget); michael@0: } else michael@0: return; michael@0: gtk_widget_realize(widget); michael@0: } michael@0: michael@0: static gint michael@0: ensure_combo_box_widgets() michael@0: { michael@0: GtkWidget* buttonChild; michael@0: michael@0: if (gComboBoxButtonWidget && gComboBoxArrowWidget) michael@0: return MOZ_GTK_SUCCESS; michael@0: michael@0: /* Create a ComboBox if needed */ michael@0: if (!gComboBoxWidget) { michael@0: gComboBoxWidget = gtk_combo_box_new(); michael@0: setup_widget_prototype(gComboBoxWidget); michael@0: } michael@0: michael@0: /* Get its inner Button */ michael@0: gtk_container_forall(GTK_CONTAINER(gComboBoxWidget), michael@0: moz_gtk_get_combo_box_inner_button, michael@0: NULL); michael@0: michael@0: if (gComboBoxButtonWidget) { michael@0: /* Get the widgets inside the Button */ michael@0: buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxButtonWidget)); michael@0: if (GTK_IS_BOX(buttonChild)) { michael@0: /* appears-as-list = FALSE, cell-view = TRUE; the button michael@0: * contains an hbox. This hbox is there because the ComboBox michael@0: * needs to place a cell renderer, a separator, and an arrow in michael@0: * the button when appears-as-list is FALSE. */ michael@0: gtk_container_forall(GTK_CONTAINER(buttonChild), michael@0: moz_gtk_get_combo_box_button_inner_widgets, michael@0: NULL); michael@0: } else if(GTK_IS_ARROW(buttonChild)) { michael@0: /* appears-as-list = TRUE, or cell-view = FALSE; michael@0: * the button only contains an arrow */ michael@0: gComboBoxArrowWidget = buttonChild; michael@0: g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) michael@0: &gComboBoxArrowWidget); michael@0: gtk_widget_realize(gComboBoxArrowWidget); michael@0: } michael@0: } else { michael@0: /* Shouldn't be reached with current internal gtk implementation; we michael@0: * use a generic toggle button as last resort fallback to avoid michael@0: * crashing. */ michael@0: ensure_toggle_menu_button_widget(); michael@0: gComboBoxButtonWidget = gToggleMenuButtonWidget; michael@0: } michael@0: michael@0: if (!gComboBoxArrowWidget) { michael@0: /* Shouldn't be reached with current internal gtk implementation; michael@0: * we gButtonArrowWidget as last resort fallback to avoid michael@0: * crashing. */ michael@0: ensure_button_arrow_widget(); michael@0: gComboBoxArrowWidget = gButtonArrowWidget; michael@0: } michael@0: michael@0: /* We don't test the validity of gComboBoxSeparatorWidget since there michael@0: * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it michael@0: * is invalid we just won't paint it. */ michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: /* We need to have pointers to the inner widgets (entry, button, arrow) of michael@0: * the ComboBoxEntry to get the correct rendering from theme engines which michael@0: * special cases their look. Since the inner layout can change, we ask GTK michael@0: * to NULL our pointers when they are about to become invalid because the michael@0: * corresponding widgets don't exist anymore. It's the role of michael@0: * g_object_add_weak_pointer(). michael@0: * Note that if we don't find the inner widgets (which shouldn't happen), we michael@0: * fallback to use generic "non-inner" widgets, and they don't need that kind michael@0: * of weak pointer since they are explicit children of gProtoWindow and as michael@0: * such GTK holds a strong reference to them. */ michael@0: static void michael@0: moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, michael@0: gpointer client_data) michael@0: { michael@0: if (GTK_IS_TOGGLE_BUTTON(widget)) { michael@0: gComboBoxEntryButtonWidget = widget; michael@0: g_object_add_weak_pointer(G_OBJECT(widget), michael@0: (gpointer) &gComboBoxEntryButtonWidget); michael@0: } else if (GTK_IS_ENTRY(widget)) { michael@0: gComboBoxEntryTextareaWidget = widget; michael@0: g_object_add_weak_pointer(G_OBJECT(widget), michael@0: (gpointer) &gComboBoxEntryTextareaWidget); michael@0: } else michael@0: return; michael@0: gtk_widget_realize(widget); michael@0: } michael@0: michael@0: static void michael@0: moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) michael@0: { michael@0: if (GTK_IS_ARROW(widget)) { michael@0: gComboBoxEntryArrowWidget = widget; michael@0: g_object_add_weak_pointer(G_OBJECT(widget), michael@0: (gpointer) &gComboBoxEntryArrowWidget); michael@0: gtk_widget_realize(widget); michael@0: } michael@0: } michael@0: michael@0: static gint michael@0: ensure_combo_box_entry_widgets() michael@0: { michael@0: GtkWidget* buttonChild; michael@0: michael@0: if (gComboBoxEntryTextareaWidget && michael@0: gComboBoxEntryButtonWidget && michael@0: gComboBoxEntryArrowWidget) michael@0: return MOZ_GTK_SUCCESS; michael@0: michael@0: /* Create a ComboBoxEntry if needed */ michael@0: if (!gComboBoxEntryWidget) { michael@0: gComboBoxEntryWidget = gtk_combo_box_new_with_entry(); michael@0: setup_widget_prototype(gComboBoxEntryWidget); michael@0: } michael@0: michael@0: /* Get its inner Entry and Button */ michael@0: gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), michael@0: moz_gtk_get_combo_box_entry_inner_widgets, michael@0: NULL); michael@0: michael@0: if (!gComboBoxEntryTextareaWidget) { michael@0: ensure_entry_widget(); michael@0: gComboBoxEntryTextareaWidget = gEntryWidget; michael@0: } michael@0: michael@0: if (gComboBoxEntryButtonWidget) { michael@0: /* Get the Arrow inside the Button */ michael@0: buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxEntryButtonWidget)); michael@0: if (GTK_IS_BOX(buttonChild)) { michael@0: /* appears-as-list = FALSE, cell-view = TRUE; the button michael@0: * contains an hbox. This hbox is there because the ComboBox michael@0: * needs to place a cell renderer, a separator, and an arrow in michael@0: * the button when appears-as-list is FALSE. */ michael@0: gtk_container_forall(GTK_CONTAINER(buttonChild), michael@0: moz_gtk_get_combo_box_entry_arrow, michael@0: NULL); michael@0: } else if(GTK_IS_ARROW(buttonChild)) { michael@0: /* appears-as-list = TRUE, or cell-view = FALSE; michael@0: * the button only contains an arrow */ michael@0: gComboBoxEntryArrowWidget = buttonChild; michael@0: g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) michael@0: &gComboBoxEntryArrowWidget); michael@0: gtk_widget_realize(gComboBoxEntryArrowWidget); michael@0: } michael@0: } else { michael@0: /* Shouldn't be reached with current internal gtk implementation; michael@0: * we use a generic toggle button as last resort fallback to avoid michael@0: * crashing. */ michael@0: ensure_toggle_menu_button_widget(); michael@0: gComboBoxEntryButtonWidget = gToggleMenuButtonWidget; michael@0: } michael@0: michael@0: if (!gComboBoxEntryArrowWidget) { michael@0: /* Shouldn't be reached with current internal gtk implementation; michael@0: * we gButtonArrowWidget as last resort fallback to avoid michael@0: * crashing. */ michael@0: ensure_button_arrow_widget(); michael@0: gComboBoxEntryArrowWidget = gButtonArrowWidget; michael@0: } michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: michael@0: static gint michael@0: ensure_handlebox_widget() michael@0: { michael@0: if (!gHandleBoxWidget) { michael@0: gHandleBoxWidget = gtk_handle_box_new(); michael@0: setup_widget_prototype(gHandleBoxWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_toolbar_widget() michael@0: { michael@0: if (!gToolbarWidget) { michael@0: ensure_handlebox_widget(); michael@0: gToolbarWidget = gtk_toolbar_new(); michael@0: gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget); michael@0: gtk_widget_realize(gToolbarWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_toolbar_separator_widget() michael@0: { michael@0: if (!gToolbarSeparatorWidget) { michael@0: ensure_toolbar_widget(); michael@0: gToolbarSeparatorWidget = GTK_WIDGET(gtk_separator_tool_item_new()); michael@0: setup_widget_prototype(gToolbarSeparatorWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_tooltip_widget() michael@0: { michael@0: if (!gTooltipWidget) { michael@0: gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP); michael@0: gtk_widget_realize(gTooltipWidget); michael@0: moz_gtk_set_widget_name(gTooltipWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_tab_widget() michael@0: { michael@0: if (!gTabWidget) { michael@0: gTabWidget = gtk_notebook_new(); michael@0: setup_widget_prototype(gTabWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_progress_widget() michael@0: { michael@0: if (!gProgressWidget) { michael@0: gProgressWidget = gtk_progress_bar_new(); michael@0: setup_widget_prototype(gProgressWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_statusbar_widget() michael@0: { michael@0: if (!gStatusbarWidget) { michael@0: gStatusbarWidget = gtk_statusbar_new(); michael@0: setup_widget_prototype(gStatusbarWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_frame_widget() michael@0: { michael@0: if (!gFrameWidget) { michael@0: ensure_statusbar_widget(); michael@0: gFrameWidget = gtk_frame_new(NULL); michael@0: gtk_container_add(GTK_CONTAINER(gStatusbarWidget), gFrameWidget); michael@0: gtk_widget_realize(gFrameWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_menu_bar_widget() michael@0: { michael@0: if (!gMenuBarWidget) { michael@0: gMenuBarWidget = gtk_menu_bar_new(); michael@0: setup_widget_prototype(gMenuBarWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_menu_bar_item_widget() michael@0: { michael@0: if (!gMenuBarItemWidget) { michael@0: ensure_menu_bar_widget(); michael@0: gMenuBarItemWidget = gtk_menu_item_new(); michael@0: gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget), michael@0: gMenuBarItemWidget); michael@0: gtk_widget_realize(gMenuBarItemWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_menu_popup_widget() michael@0: { michael@0: if (!gMenuPopupWidget) { michael@0: ensure_menu_bar_item_widget(); michael@0: gMenuPopupWidget = gtk_menu_new(); michael@0: gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget), michael@0: gMenuPopupWidget); michael@0: gtk_widget_realize(gMenuPopupWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_menu_item_widget() michael@0: { michael@0: if (!gMenuItemWidget) { michael@0: ensure_menu_popup_widget(); michael@0: gMenuItemWidget = gtk_menu_item_new_with_label("M"); michael@0: gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), michael@0: gMenuItemWidget); michael@0: gtk_widget_realize(gMenuItemWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_image_menu_item_widget() michael@0: { michael@0: if (!gImageMenuItemWidget) { michael@0: ensure_menu_popup_widget(); michael@0: gImageMenuItemWidget = gtk_image_menu_item_new(); michael@0: gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), michael@0: gImageMenuItemWidget); michael@0: gtk_widget_realize(gImageMenuItemWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_menu_separator_widget() michael@0: { michael@0: if (!gMenuSeparatorWidget) { michael@0: ensure_menu_popup_widget(); michael@0: gMenuSeparatorWidget = gtk_separator_menu_item_new(); michael@0: gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), michael@0: gMenuSeparatorWidget); michael@0: gtk_widget_realize(gMenuSeparatorWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_check_menu_item_widget() michael@0: { michael@0: if (!gCheckMenuItemWidget) { michael@0: ensure_menu_popup_widget(); michael@0: gCheckMenuItemWidget = gtk_check_menu_item_new_with_label("M"); michael@0: gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), michael@0: gCheckMenuItemWidget); michael@0: gtk_widget_realize(gCheckMenuItemWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_tree_view_widget() michael@0: { michael@0: if (!gTreeViewWidget) { michael@0: gTreeViewWidget = gtk_tree_view_new(); michael@0: setup_widget_prototype(gTreeViewWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_tree_header_cell_widget() michael@0: { michael@0: if(!gTreeHeaderCellWidget) { michael@0: /* michael@0: * Some GTK engines paint the first and last cell michael@0: * of a TreeView header with a highlight. michael@0: * Since we do not know where our widget will be relative michael@0: * to the other buttons in the TreeView header, we must michael@0: * paint it as a button that is between two others, michael@0: * thus ensuring it is neither the first or last button michael@0: * in the header. michael@0: * GTK doesn't give us a way to do this explicitly, michael@0: * so we must paint with a button that is between two michael@0: * others. michael@0: */ michael@0: michael@0: GtkTreeViewColumn* firstTreeViewColumn; michael@0: GtkTreeViewColumn* lastTreeViewColumn; michael@0: michael@0: ensure_tree_view_widget(); michael@0: michael@0: /* Create and append our three columns */ michael@0: firstTreeViewColumn = gtk_tree_view_column_new(); michael@0: gtk_tree_view_column_set_title(firstTreeViewColumn, "M"); michael@0: gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn); michael@0: michael@0: gMiddleTreeViewColumn = gtk_tree_view_column_new(); michael@0: gtk_tree_view_column_set_title(gMiddleTreeViewColumn, "M"); michael@0: gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), michael@0: gMiddleTreeViewColumn); michael@0: michael@0: lastTreeViewColumn = gtk_tree_view_column_new(); michael@0: gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); michael@0: gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn); michael@0: michael@0: /* Use the middle column's header for our button */ michael@0: gTreeHeaderCellWidget = gtk_tree_view_column_get_button(gMiddleTreeViewColumn); michael@0: /* TODO, but it can't be NULL */ michael@0: gTreeHeaderSortArrowWidget = gtk_button_new(); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_expander_widget() michael@0: { michael@0: if (!gExpanderWidget) { michael@0: gExpanderWidget = gtk_expander_new("M"); michael@0: setup_widget_prototype(gExpanderWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: ensure_scrolled_window_widget() michael@0: { michael@0: if (!gScrolledWindowWidget) { michael@0: gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL); michael@0: setup_widget_prototype(gScrolledWindowWidget); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_init() michael@0: { michael@0: GtkWidgetClass *entry_class; michael@0: michael@0: if (is_initialized) michael@0: return MOZ_GTK_SUCCESS; michael@0: michael@0: is_initialized = TRUE; michael@0: have_arrow_scaling = (gtk_major_version > 2 || michael@0: (gtk_major_version == 2 && gtk_minor_version >= 12)); michael@0: michael@0: /* Add style property to GtkEntry. michael@0: * Adding the style property to the normal GtkEntry class means that it michael@0: * will work without issues inside GtkComboBox and for Spinbuttons. */ michael@0: entry_class = g_type_class_ref(GTK_TYPE_ENTRY); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing) michael@0: { michael@0: ensure_checkbox_widget(); michael@0: michael@0: gtk_widget_style_get (gCheckboxWidget, michael@0: "indicator_size", indicator_size, michael@0: "indicator_spacing", indicator_spacing, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing) michael@0: { michael@0: ensure_radiobutton_widget(); michael@0: michael@0: gtk_widget_style_get (gRadiobuttonWidget, michael@0: "indicator_size", indicator_size, michael@0: "indicator_spacing", indicator_spacing, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, michael@0: gint* focus_width, gint* focus_pad) michael@0: { michael@0: gtk_widget_style_get (widget, michael@0: "interior-focus", interior_focus, michael@0: "focus-line-width", focus_width, michael@0: "focus-padding", focus_pad, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding) michael@0: { michael@0: ensure_menu_item_widget(); michael@0: michael@0: gtk_widget_style_get (gMenuItemWidget, michael@0: "horizontal-padding", horizontal_padding, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding) michael@0: { michael@0: ensure_check_menu_item_widget(); michael@0: michael@0: gtk_widget_style_get (gCheckMenuItemWidget, michael@0: "horizontal-padding", horizontal_padding, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left, michael@0: gint* border_bottom, gint* border_right) michael@0: { michael@0: GtkBorder* default_outside_border; michael@0: michael@0: ensure_button_widget(); michael@0: gtk_widget_style_get(gButtonWidget, michael@0: "default-outside-border", &default_outside_border, michael@0: NULL); michael@0: michael@0: if (default_outside_border) { michael@0: *border_top = default_outside_border->top; michael@0: *border_left = default_outside_border->left; michael@0: *border_bottom = default_outside_border->bottom; michael@0: *border_right = default_outside_border->right; michael@0: gtk_border_free(default_outside_border); michael@0: } else { michael@0: *border_top = *border_left = *border_bottom = *border_right = 0; michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_button_get_default_border(gint* border_top, gint* border_left, michael@0: gint* border_bottom, gint* border_right) michael@0: { michael@0: GtkBorder* default_border; michael@0: michael@0: ensure_button_widget(); michael@0: gtk_widget_style_get(gButtonWidget, michael@0: "default-border", &default_border, michael@0: NULL); michael@0: michael@0: if (default_border) { michael@0: *border_top = default_border->top; michael@0: *border_left = default_border->left; michael@0: *border_bottom = default_border->bottom; michael@0: *border_right = default_border->right; michael@0: gtk_border_free(default_border); michael@0: } else { michael@0: /* see gtkbutton.c */ michael@0: *border_top = *border_left = *border_bottom = *border_right = 1; michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_splitter_get_metrics(gint orientation, gint* size) michael@0: { michael@0: if (orientation == GTK_ORIENTATION_HORIZONTAL) { michael@0: ensure_hpaned_widget(); michael@0: gtk_widget_style_get(gHPanedWidget, "handle_size", size, NULL); michael@0: } else { michael@0: ensure_vpaned_widget(); michael@0: gtk_widget_style_get(gVPanedWidget, "handle_size", size, NULL); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border) michael@0: { michael@0: static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; michael@0: GtkBorder *tmp_border; michael@0: michael@0: gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL); michael@0: michael@0: if (tmp_border) { michael@0: *inner_border = *tmp_border; michael@0: gtk_border_free(tmp_border); michael@0: } michael@0: else michael@0: *inner_border = default_inner_border; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkReliefStyle relief, GtkWidget* widget, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStyleContext* style = gtk_widget_get_style_context(widget); michael@0: gint x = rect->x, y=rect->y, width=rect->width, height=rect->height; michael@0: michael@0: gboolean interior_focus; michael@0: gint focus_width, focus_pad; michael@0: michael@0: moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); michael@0: gtk_widget_set_direction(widget, direction); michael@0: michael@0: if (!interior_focus && state->focused) { michael@0: x += focus_width + focus_pad; michael@0: y += focus_width + focus_pad; michael@0: width -= 2 * (focus_width + focus_pad); michael@0: height -= 2 * (focus_width + focus_pad); michael@0: } michael@0: michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_set_state(style, state_flags); michael@0: michael@0: if (state->isDefault && relief == GTK_RELIEF_NORMAL) { michael@0: /* handle default borders both outside and inside the button */ michael@0: gint default_top, default_left, default_bottom, default_right; michael@0: moz_gtk_button_get_default_overflow(&default_top, &default_left, michael@0: &default_bottom, &default_right); michael@0: x -= default_left; michael@0: y -= default_top; michael@0: width += default_left + default_right; michael@0: height += default_top + default_bottom; michael@0: gtk_render_background(style, cr, x, y, width, height); michael@0: gtk_render_frame(style, cr, x, y, width, height); michael@0: moz_gtk_button_get_default_border(&default_top, &default_left, michael@0: &default_bottom, &default_right); michael@0: x += default_left; michael@0: y += default_top; michael@0: width -= (default_left + default_right); michael@0: height -= (default_top + default_bottom); michael@0: } michael@0: michael@0: if (relief != GTK_RELIEF_NONE || state->depressed || michael@0: (state_flags & GTK_STATE_FLAG_PRELIGHT)) { michael@0: /* the following line can trigger an assertion (Crux theme) michael@0: file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area): michael@0: assertion `GDK_IS_WINDOW (window)' failed */ michael@0: gtk_render_background(style, cr, x, y, width, height); michael@0: gtk_render_frame(style, cr, x, y, width, height); michael@0: } michael@0: michael@0: if (state->focused) { michael@0: if (interior_focus) { michael@0: GtkBorder border; michael@0: gtk_style_context_get_border(style, state_flags, &border); michael@0: x += border.left + focus_pad; michael@0: y += border.top + focus_pad; michael@0: width -= 2 * (border.left + focus_pad); michael@0: height -= 2 * (border.top + focus_pad); michael@0: } else { michael@0: x -= focus_width + focus_pad; michael@0: y -= focus_width + focus_pad; michael@0: width += 2 * (focus_width + focus_pad); michael@0: height += 2 * (focus_width + focus_pad); michael@0: } michael@0: michael@0: gtk_render_focus(style, cr, x, y, width, height); michael@0: } michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gboolean selected, gboolean inconsistent, michael@0: gboolean isradio, GtkTextDirection direction) michael@0: { michael@0: gint indicator_size, indicator_spacing; michael@0: gint x, y, width, height; michael@0: gint focus_x, focus_y, focus_width, focus_height; michael@0: GtkWidget *w; michael@0: GtkStyleContext *style; michael@0: michael@0: if (isradio) { michael@0: moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing); michael@0: w = gRadiobuttonWidget; michael@0: } else { michael@0: moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing); michael@0: w = gCheckboxWidget; michael@0: } michael@0: michael@0: // XXX we should assert rect->height >= indicator_size too michael@0: // after bug 369581 is fixed. michael@0: NS_ASSERTION(rect->width >= indicator_size, michael@0: "GetMinimumWidgetSize was ignored"); michael@0: michael@0: // Paint it center aligned in the rect. michael@0: x = rect->x + (rect->width - indicator_size) / 2; michael@0: y = rect->y + (rect->height - indicator_size) / 2; michael@0: width = indicator_size; michael@0: height = indicator_size; michael@0: michael@0: focus_x = x - indicator_spacing; michael@0: focus_y = y - indicator_spacing; michael@0: focus_width = width + 2 * indicator_spacing; michael@0: focus_height = height + 2 * indicator_spacing; michael@0: michael@0: style = gtk_widget_get_style_context(w); michael@0: michael@0: gtk_widget_set_sensitive(w, !state->disabled); michael@0: gtk_widget_set_direction(w, direction); michael@0: gtk_style_context_save(style); michael@0: michael@0: if (isradio) { michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); michael@0: gtk_style_context_set_state(style, selected ? GTK_STATE_FLAG_ACTIVE : michael@0: GTK_STATE_FLAG_NORMAL); michael@0: gtk_render_option(style, cr, x, y, width, height); michael@0: if (state->focused) { michael@0: gtk_render_focus(style, cr, focus_x, focus_y, michael@0: focus_width, focus_height); michael@0: } michael@0: } michael@0: else { michael@0: /* michael@0: * 'indeterminate' type on checkboxes. In GTK, the shadow type michael@0: * must also be changed for the state to be drawn. michael@0: */ michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); michael@0: if (inconsistent) { michael@0: gtk_style_context_set_state(style, GTK_STATE_FLAG_INCONSISTENT); michael@0: gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE); michael@0: } else if (selected) { michael@0: gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE); michael@0: } else { michael@0: gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE); michael@0: } michael@0: gtk_render_check(style, cr, x, y, width, height); michael@0: if (state->focused) { michael@0: gtk_render_focus(style, cr, michael@0: focus_x, focus_y, focus_width, focus_height); michael@0: } michael@0: } michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect, michael@0: GdkRectangle* inner_rect, michael@0: GtkTextDirection direction, michael@0: gboolean ignore_focus) michael@0: { michael@0: GtkBorder inner_border; michael@0: gboolean interior_focus; michael@0: gint focus_width, focus_pad; michael@0: GtkStyleContext* style; michael@0: GtkBorder border; michael@0: michael@0: style = gtk_widget_get_style_context(button); michael@0: michael@0: /* This mirrors gtkbutton's child positioning */ michael@0: moz_gtk_button_get_inner_border(button, &inner_border); michael@0: moz_gtk_widget_get_focus(button, &interior_focus, michael@0: &focus_width, &focus_pad); michael@0: michael@0: if (ignore_focus) michael@0: focus_width = focus_pad = 0; michael@0: michael@0: gtk_style_context_get_border(style, 0, &border); michael@0: michael@0: inner_rect->x = rect->x + border.left + focus_width + focus_pad; michael@0: inner_rect->x += direction == GTK_TEXT_DIR_LTR ? michael@0: inner_border.left : inner_border.right; michael@0: inner_rect->y = rect->y + inner_border.top + border.top + michael@0: focus_width + focus_pad; michael@0: inner_rect->width = MAX(1, rect->width - inner_border.left - michael@0: inner_border.right - (border.left + focus_pad + focus_width) * 2); michael@0: inner_rect->height = MAX(1, rect->height - inner_border.top - michael@0: inner_border.bottom - (border.top + focus_pad + focus_width) * 2); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: michael@0: static gint michael@0: calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect, michael@0: GdkRectangle* arrow_rect, GtkTextDirection direction) michael@0: { michael@0: /* defined in gtkarrow.c */ michael@0: gfloat arrow_scaling = 0.7; michael@0: gfloat xalign, xpad; michael@0: gint extent; michael@0: gint mxpad, mypad; michael@0: gfloat mxalign, myalign; michael@0: GtkMisc* misc = GTK_MISC(arrow); michael@0: michael@0: if (have_arrow_scaling) michael@0: gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL); michael@0: michael@0: gtk_misc_get_padding(misc, &mxpad, &mypad); michael@0: extent = MIN((rect->width - mxpad * 2), michael@0: (rect->height - mypad * 2)) * arrow_scaling; michael@0: michael@0: gtk_misc_get_alignment(misc, &mxalign, &myalign); michael@0: michael@0: xalign = direction == GTK_TEXT_DIR_LTR ? mxalign : 1.0 - mxalign; michael@0: xpad = mxpad + (rect->width - extent) * xalign; michael@0: michael@0: arrow_rect->x = direction == GTK_TEXT_DIR_LTR ? michael@0: floor(rect->x + xpad) : ceil(rect->x + xpad); michael@0: arrow_rect->y = floor(rect->y + mypad + michael@0: ((rect->height - extent) * myalign)); michael@0: michael@0: arrow_rect->width = arrow_rect->height = extent; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkScrollbarButtonFlags flags, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStateType saved_state; michael@0: GdkRectangle arrow_rect; michael@0: gdouble arrow_angle; michael@0: GtkStyleContext* style; michael@0: GtkWidget *scrollbar; michael@0: gint arrow_displacement_x, arrow_displacement_y; michael@0: const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ? michael@0: "vscrollbar" : "hscrollbar"; michael@0: michael@0: ensure_scrollbar_widget(); michael@0: michael@0: if (flags & MOZ_GTK_STEPPER_VERTICAL) michael@0: scrollbar = gVertScrollbarWidget; michael@0: else michael@0: scrollbar = gHorizScrollbarWidget; michael@0: michael@0: gtk_widget_set_direction(scrollbar, direction); michael@0: michael@0: if (flags & MOZ_GTK_STEPPER_VERTICAL) { michael@0: arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_DOWN : ARROW_UP; michael@0: } else { michael@0: arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_RIGHT : ARROW_LEFT; michael@0: } michael@0: michael@0: style = gtk_widget_get_style_context(scrollbar); michael@0: michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCROLLBAR); michael@0: gtk_style_context_set_state(style, state_flags); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: michael@0: arrow_rect.width = rect->width / 2; michael@0: arrow_rect.height = rect->height / 2; michael@0: arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; michael@0: arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; michael@0: michael@0: if (state_flags & GTK_STATE_FLAG_ACTIVE) { michael@0: gtk_widget_style_get(scrollbar, michael@0: "arrow-displacement-x", &arrow_displacement_x, michael@0: "arrow-displacement-y", &arrow_displacement_y, michael@0: NULL); michael@0: michael@0: arrow_rect.x += arrow_displacement_x; michael@0: arrow_rect.y += arrow_displacement_y; michael@0: } michael@0: michael@0: gtk_render_arrow(style, cr, arrow_angle, michael@0: arrow_rect.x, michael@0: arrow_rect.y, michael@0: arrow_rect.width); michael@0: michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget, michael@0: cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: GtkScrollbar *scrollbar; michael@0: michael@0: ensure_scrollbar_widget(); michael@0: michael@0: if (widget == MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL) michael@0: scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget); michael@0: else michael@0: scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget); michael@0: michael@0: gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); michael@0: michael@0: style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar)); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: michael@0: if (state->focused) { michael@0: gtk_render_focus(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: } michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, michael@0: cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStyleContext* style; michael@0: GtkScrollbar *scrollbar; michael@0: GtkAdjustment *adj; michael@0: michael@0: ensure_scrollbar_widget(); michael@0: michael@0: if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) michael@0: scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget); michael@0: else michael@0: scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget); michael@0: michael@0: gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); michael@0: michael@0: style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar)); michael@0: gtk_style_context_save(style); michael@0: michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER); michael@0: gtk_style_context_set_state(style, state_flags); michael@0: michael@0: gtk_render_slider(style, cr, rect->x, rect->y, michael@0: rect->width, rect->height, michael@0: (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? michael@0: GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); michael@0: michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_spin_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_spin_widget(); michael@0: gtk_widget_set_direction(gSpinWidget, direction); michael@0: style = gtk_widget_get_style_context(gSpinWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_SPINBUTTON); michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_spin_updown_paint(cairo_t *cr, GdkRectangle* rect, michael@0: gboolean isDown, GtkWidgetState* state, michael@0: GtkTextDirection direction) michael@0: { michael@0: GdkRectangle arrow_rect; michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_spin_widget(); michael@0: style = gtk_widget_get_style_context(gSpinWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_SPINBUTTON); michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: gtk_widget_set_direction(gSpinWidget, direction); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: michael@0: michael@0: /* hard code these values */ michael@0: arrow_rect.width = 6; michael@0: arrow_rect.height = 6; michael@0: arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; michael@0: arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; michael@0: arrow_rect.y += isDown ? -1 : 1; michael@0: michael@0: gtk_render_arrow(style, cr, michael@0: isDown ? ARROW_DOWN : ARROW_UP, michael@0: arrow_rect.x, arrow_rect.y, michael@0: arrow_rect.width); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: /* See gtk_range_draw() for reference. michael@0: */ michael@0: static gint michael@0: moz_gtk_scale_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkOrientation flags, GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: gint x = 0, y = 0; michael@0: GtkStyleContext* style; michael@0: GtkWidget* widget; michael@0: GtkBorder margin; michael@0: michael@0: ensure_scale_widget(); michael@0: widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); michael@0: gtk_widget_set_direction(widget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(widget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCALE); michael@0: gtk_style_context_get_margin(style, state_flags, &margin); michael@0: michael@0: if (flags == GTK_ORIENTATION_HORIZONTAL) { michael@0: x = margin.left; michael@0: y++; michael@0: } michael@0: else { michael@0: x++; michael@0: y = margin.top; michael@0: } michael@0: michael@0: gtk_render_frame(style, cr, rect->x + x, rect->y + y, michael@0: rect->width - 2*x, rect->height - 2*y); michael@0: michael@0: if (state->focused) michael@0: gtk_render_focus(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_scale_thumb_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkOrientation flags, GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStyleContext* style; michael@0: GtkWidget* widget; michael@0: gint thumb_width, thumb_height, x, y; michael@0: michael@0: ensure_scale_widget(); michael@0: widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); michael@0: gtk_widget_set_direction(widget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(widget); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_set_state(style, state_flags); michael@0: /* determine the thumb size, and position the thumb in the center in the opposite axis michael@0: */ michael@0: if (flags == GTK_ORIENTATION_HORIZONTAL) { michael@0: moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_width, &thumb_height); michael@0: x = rect->x; michael@0: y = rect->y + (rect->height - thumb_height) / 2; michael@0: } michael@0: else { michael@0: moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_width); michael@0: x = rect->x + (rect->width - thumb_width) / 2; michael@0: y = rect->y; michael@0: } michael@0: michael@0: gtk_render_slider(style, cr, x, y, thumb_width, thumb_height, flags); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_gripper_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_handlebox_widget(); michael@0: gtk_widget_set_direction(gHandleBoxWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gHandleBoxWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_hpaned_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_hpaned_widget(); michael@0: style = gtk_widget_get_style_context(gHPanedWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: gtk_render_handle(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_vpaned_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_vpaned_widget(); michael@0: style = gtk_widget_get_style_context(gVPanedWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: gtk_render_handle(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: // See gtk_entry_draw() for reference. michael@0: static gint michael@0: moz_gtk_entry_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkWidget* widget, GtkTextDirection direction) michael@0: { michael@0: gint x = rect->x, y = rect->y, width = rect->width, height = rect->height; michael@0: GtkStyleContext* style; michael@0: gboolean interior_focus; michael@0: gint focus_width; michael@0: michael@0: gtk_widget_set_direction(widget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(widget); michael@0: michael@0: gtk_widget_style_get(widget, michael@0: "interior-focus", &interior_focus, michael@0: "focus-line-width", &focus_width, michael@0: NULL); michael@0: michael@0: /* gtkentry.c uses two windows, one for the entire widget and one for the michael@0: * text area inside it. The background of both windows is set to the "base" michael@0: * color of the new state in gtk_entry_state_changed, but only the inner michael@0: * textarea window uses gtk_paint_flat_box when exposed */ michael@0: michael@0: /* This gets us a lovely greyish disabledish look */ michael@0: gtk_widget_set_sensitive(widget, !state->disabled); michael@0: michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_ENTRY); michael@0: michael@0: /* Now paint the shadow and focus border. michael@0: * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad michael@0: * smaller when focused if the focus is not interior, then the focus. */ michael@0: michael@0: if (state->focused && !state->disabled) { michael@0: /* This will get us the lit borders that focused textboxes enjoy on michael@0: * some themes. */ michael@0: gtk_style_context_set_state(style, GTK_STATE_FLAG_FOCUSED); michael@0: if (!interior_focus) { michael@0: /* Indent the border a little bit if we have exterior focus michael@0: (this is what GTK does to draw native entries) */ michael@0: x += focus_width; michael@0: y += focus_width; michael@0: width -= 2 * focus_width; michael@0: height -= 2 * focus_width; michael@0: } michael@0: } michael@0: michael@0: if (state->disabled) { michael@0: gtk_style_context_set_state(style, GTK_STATE_FLAG_INSENSITIVE); michael@0: } michael@0: michael@0: gtk_render_background(style, cr, x, y, width, height); michael@0: gtk_render_frame(style, cr, x, y, width, height); michael@0: michael@0: if (state->focused && !state->disabled) { michael@0: if (!interior_focus) { michael@0: gtk_render_focus(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: } michael@0: } michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_treeview_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkTextDirection direction) michael@0: { michael@0: gint xthickness, ythickness; michael@0: GtkStyleContext *style; michael@0: GtkStyleContext *style_tree; michael@0: GtkStateFlags state_flags; michael@0: GtkBorder border; michael@0: michael@0: ensure_tree_view_widget(); michael@0: ensure_scrolled_window_widget(); michael@0: michael@0: gtk_widget_set_direction(gTreeViewWidget, direction); michael@0: gtk_widget_set_direction(gScrolledWindowWidget, direction); michael@0: michael@0: /* only handle disabled and normal states, otherwise the whole background michael@0: * area will be painted differently with other states */ michael@0: state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL; michael@0: michael@0: style = gtk_widget_get_style_context(gScrolledWindowWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME); michael@0: gtk_style_context_get_border(style, state_flags, &border); michael@0: xthickness = border.left; michael@0: ythickness = border.top; michael@0: michael@0: style_tree = gtk_widget_get_style_context(gTreeViewWidget); michael@0: gtk_style_context_save(style_tree); michael@0: gtk_style_context_add_class(style_tree, GTK_STYLE_CLASS_VIEW); michael@0: michael@0: gtk_render_background(style_tree, cr, michael@0: rect->x + xthickness, rect->y + ythickness, michael@0: rect->width - 2 * xthickness, michael@0: rect->height - 2 * ythickness); michael@0: gtk_render_frame(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: gtk_style_context_restore(style_tree); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_tree_header_cell_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gboolean isSorted, GtkTextDirection direction) michael@0: { michael@0: moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, michael@0: gTreeHeaderCellWidget, direction); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_tree_header_sort_arrow_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, GtkArrowType arrow_type, michael@0: GtkTextDirection direction) michael@0: { michael@0: GdkRectangle arrow_rect; michael@0: gdouble arrow_angle; michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_tree_header_cell_widget(); michael@0: gtk_widget_set_direction(gTreeHeaderSortArrowWidget, direction); michael@0: michael@0: /* hard code these values */ michael@0: arrow_rect.width = 11; michael@0: arrow_rect.height = 11; michael@0: arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; michael@0: arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; michael@0: style = gtk_widget_get_style_context(gTreeHeaderSortArrowWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: michael@0: switch (arrow_type) { michael@0: case GTK_ARROW_LEFT: michael@0: arrow_angle = ARROW_LEFT; michael@0: break; michael@0: case GTK_ARROW_RIGHT: michael@0: arrow_angle = ARROW_RIGHT; michael@0: break; michael@0: case GTK_ARROW_DOWN: michael@0: arrow_angle = ARROW_DOWN; michael@0: break; michael@0: default: michael@0: arrow_angle = ARROW_UP; michael@0: break; michael@0: } michael@0: if (arrow_type != GTK_ARROW_NONE) michael@0: gtk_render_arrow(style, cr, arrow_angle, michael@0: arrow_rect.x, arrow_rect.y, michael@0: arrow_rect.width); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: /* See gtk_expander_paint() for reference. michael@0: */ michael@0: static gint michael@0: moz_gtk_treeview_expander_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkExpanderStyle expander_state, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext *style; michael@0: GtkStateFlags state_flags; michael@0: michael@0: ensure_tree_view_widget(); michael@0: gtk_widget_set_direction(gTreeViewWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gTreeViewWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_EXPANDER); michael@0: michael@0: /* Because the frame we get is of the entire treeview, we can't get the precise michael@0: * event state of one expander, thus rendering hover and active feedback useless. */ michael@0: state_flags = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; michael@0: michael@0: /* GTK_STATE_FLAG_ACTIVE controls expanded/colapsed state rendering michael@0: * in gtk_render_expander() michael@0: */ michael@0: if (expander_state == GTK_EXPANDER_EXPANDED) michael@0: state_flags |= GTK_STATE_FLAG_ACTIVE; michael@0: else michael@0: state_flags &= ~(GTK_STATE_FLAG_ACTIVE); michael@0: michael@0: gtk_style_context_set_state(style, state_flags); michael@0: michael@0: gtk_render_expander(style, cr, michael@0: rect->x, michael@0: rect->y, michael@0: rect->width, michael@0: rect->height); michael@0: michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: /* See gtk_separator_draw() for reference. michael@0: */ michael@0: static gint michael@0: moz_gtk_combo_box_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gboolean ishtml, GtkTextDirection direction) michael@0: { michael@0: GdkRectangle arrow_rect, real_arrow_rect; michael@0: gint arrow_size, separator_width; michael@0: gboolean wide_separators; michael@0: GtkStyleContext* style; michael@0: GtkRequisition arrow_req; michael@0: michael@0: ensure_combo_box_widgets(); michael@0: michael@0: /* Also sets the direction on gComboBoxButtonWidget, which is then michael@0: * inherited by the separator and arrow */ michael@0: moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, michael@0: gComboBoxButtonWidget, direction); michael@0: michael@0: calculate_button_inner_rect(gComboBoxButtonWidget, michael@0: rect, &arrow_rect, direction, ishtml); michael@0: /* Now arrow_rect contains the inner rect ; we want to correct the width michael@0: * to what the arrow needs (see gtk_combo_box_size_allocate) */ michael@0: gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req); michael@0: if (direction == GTK_TEXT_DIR_LTR) michael@0: arrow_rect.x += arrow_rect.width - arrow_req.width; michael@0: arrow_rect.width = arrow_req.width; michael@0: michael@0: calculate_arrow_rect(gComboBoxArrowWidget, michael@0: &arrow_rect, &real_arrow_rect, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gComboBoxArrowWidget); michael@0: gtk_render_arrow(style, cr, ARROW_DOWN, michael@0: real_arrow_rect.x, real_arrow_rect.y, michael@0: real_arrow_rect.width); michael@0: michael@0: /* If there is no separator in the theme, there's nothing left to do. */ michael@0: if (!gComboBoxSeparatorWidget) michael@0: return MOZ_GTK_SUCCESS; michael@0: style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); michael@0: gtk_widget_style_get(gComboBoxSeparatorWidget, michael@0: "wide-separators", &wide_separators, michael@0: "separator-width", &separator_width, michael@0: NULL); michael@0: michael@0: if (wide_separators) { michael@0: if (direction == GTK_TEXT_DIR_LTR) michael@0: arrow_rect.x -= separator_width; michael@0: else michael@0: arrow_rect.x += arrow_rect.width; michael@0: michael@0: gtk_render_frame(style, cr, arrow_rect.x, arrow_rect.y, separator_width, arrow_rect.height); michael@0: } else { michael@0: if (direction == GTK_TEXT_DIR_LTR) { michael@0: GtkBorder padding; michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: gtk_style_context_get_padding(style, state_flags, &padding); michael@0: arrow_rect.x -= padding.left; michael@0: } michael@0: else michael@0: arrow_rect.x += arrow_rect.width; michael@0: michael@0: gtk_render_line(style, cr, michael@0: arrow_rect.x, arrow_rect.y, michael@0: arrow_rect.x, arrow_rect.y + arrow_rect.height); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_arrow_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkArrowType arrow_type, GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GdkRectangle arrow_rect; michael@0: gdouble arrow_angle = ARROW_UP; michael@0: michael@0: ensure_button_arrow_widget(); michael@0: style = gtk_widget_get_style_context(gButtonArrowWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_set_state(style, state_flags); michael@0: gtk_widget_set_direction(gButtonArrowWidget, direction); michael@0: michael@0: calculate_arrow_rect(gButtonArrowWidget, rect, &arrow_rect, michael@0: direction); michael@0: michael@0: if (direction == GTK_TEXT_DIR_RTL) { michael@0: if (arrow_type == GTK_ARROW_LEFT) michael@0: arrow_angle = ARROW_RIGHT; michael@0: else if (arrow_type == GTK_ARROW_RIGHT) michael@0: arrow_angle = ARROW_LEFT; michael@0: } else if (arrow_type == GTK_ARROW_DOWN) { michael@0: arrow_angle = ARROW_DOWN; michael@0: } michael@0: if (arrow_type != GTK_ARROW_NONE) michael@0: gtk_render_arrow(style, cr, arrow_angle, michael@0: arrow_rect.x, arrow_rect.y, arrow_rect.width); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_combo_box_entry_button_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gboolean input_focus, michael@0: GtkTextDirection direction) michael@0: { michael@0: gint x_displacement, y_displacement; michael@0: GdkRectangle arrow_rect, real_arrow_rect; michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_combo_box_entry_widgets(); michael@0: michael@0: moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, michael@0: gComboBoxEntryButtonWidget, direction); michael@0: michael@0: calculate_button_inner_rect(gComboBoxEntryButtonWidget, michael@0: rect, &arrow_rect, direction, FALSE); michael@0: if (state_flags & GTK_STATE_FLAG_ACTIVE) { michael@0: gtk_widget_style_get(gComboBoxEntryButtonWidget, michael@0: "child-displacement-x", &x_displacement, michael@0: "child-displacement-y", &y_displacement, michael@0: NULL); michael@0: arrow_rect.x += x_displacement; michael@0: arrow_rect.y += y_displacement; michael@0: } michael@0: michael@0: calculate_arrow_rect(gComboBoxEntryArrowWidget, michael@0: &arrow_rect, &real_arrow_rect, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gComboBoxEntryArrowWidget); michael@0: michael@0: gtk_render_arrow(style, cr, ARROW_DOWN, michael@0: real_arrow_rect.x, real_arrow_rect.y, michael@0: real_arrow_rect.width); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_container_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gboolean isradio, GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStyleContext* style; michael@0: GtkWidget *widget; michael@0: gboolean interior_focus; michael@0: gint focus_width, focus_pad; michael@0: michael@0: if (isradio) { michael@0: ensure_radiobutton_widget(); michael@0: widget = gRadiobuttonWidget; michael@0: } else { michael@0: ensure_checkbox_widget(); michael@0: widget = gCheckboxWidget; michael@0: } michael@0: gtk_widget_set_direction(widget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(widget); michael@0: gtk_style_context_save(style); michael@0: moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); michael@0: gtk_style_context_set_state(style, state_flags); michael@0: michael@0: /* this is for drawing a prelight box */ michael@0: if (state_flags & GTK_STATE_FLAG_PRELIGHT) { michael@0: gtk_render_background(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: } michael@0: michael@0: if (state->focused && !interior_focus) { michael@0: gtk_render_focus(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: } michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_toggle_label_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gboolean isradio, GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext *style; michael@0: GtkWidget *widget; michael@0: gboolean interior_focus; michael@0: michael@0: if (!state->focused) michael@0: return MOZ_GTK_SUCCESS; michael@0: michael@0: if (isradio) { michael@0: ensure_radiobutton_widget(); michael@0: widget = gRadiobuttonWidget; michael@0: } else { michael@0: ensure_checkbox_widget(); michael@0: widget = gCheckboxWidget; michael@0: } michael@0: style = gtk_widget_get_style_context(widget); michael@0: gtk_style_context_save(style); michael@0: if (isradio) { michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); michael@0: } else { michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); michael@0: } michael@0: gtk_widget_set_direction(widget, direction); michael@0: michael@0: gtk_widget_style_get(widget, "interior-focus", &interior_focus, NULL); michael@0: if (!interior_focus) michael@0: return MOZ_GTK_SUCCESS; michael@0: michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: gtk_render_focus(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_toolbar_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_toolbar_widget(); michael@0: gtk_widget_set_direction(gToolbarWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gToolbarWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLBAR); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: /* See _gtk_toolbar_paint_space_line() for reference. michael@0: */ michael@0: static gint michael@0: moz_gtk_toolbar_separator_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: gint separator_width; michael@0: gint paint_width; michael@0: gboolean wide_separators; michael@0: michael@0: /* Defined as constants in GTK+ 2.10.14 */ michael@0: const double start_fraction = 0.2; michael@0: const double end_fraction = 0.8; michael@0: michael@0: ensure_toolbar_separator_widget(); michael@0: gtk_widget_set_direction(gToolbarSeparatorWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gToolbarSeparatorWidget); michael@0: michael@0: gtk_widget_style_get(gToolbarWidget, michael@0: "wide-separators", &wide_separators, michael@0: "separator-width", &separator_width, michael@0: NULL); michael@0: michael@0: if (wide_separators) { michael@0: if (separator_width > rect->width) michael@0: separator_width = rect->width; michael@0: michael@0: gtk_render_frame(style, cr, michael@0: rect->x + (rect->width - separator_width) / 2, michael@0: rect->y + rect->height * start_fraction, michael@0: separator_width, michael@0: rect->height * (end_fraction - start_fraction)); michael@0: } else { michael@0: GtkBorder padding; michael@0: gtk_style_context_get_padding(style, 0, &padding); michael@0: michael@0: paint_width = padding.left; michael@0: if (paint_width > rect->width) michael@0: paint_width = rect->width; michael@0: michael@0: gtk_render_line(style, cr, michael@0: rect->x + (rect->width - paint_width) / 2, michael@0: rect->y + rect->height * start_fraction, michael@0: rect->x + (rect->width - paint_width) / 2, michael@0: rect->y + rect->height * end_fraction); michael@0: } michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_tooltip_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_tooltip_widget(); michael@0: gtk_widget_set_direction(gTooltipWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gTooltipWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP); michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_resizer_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_frame_widget(); michael@0: gtk_widget_set_direction(gStatusbarWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gStatusbarWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: michael@0: gtk_render_handle(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_frame_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_frame_widget(); michael@0: gtk_widget_set_direction(gFrameWidget, direction); michael@0: style = gtk_widget_get_style_context(gFrameWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME); michael@0: michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_progressbar_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_progress_widget(); michael@0: gtk_widget_set_direction(gProgressWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gProgressWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_progress_chunk_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction, michael@0: GtkThemeWidgetType widget) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_progress_widget(); michael@0: gtk_widget_set_direction(gProgressWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gProgressWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_PROGRESSBAR); michael@0: michael@0: if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE || michael@0: widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) { michael@0: /** michael@0: * The bar's size and the bar speed are set depending of the progress' michael@0: * size. These could also be constant for all progress bars easily. michael@0: */ michael@0: gboolean vertical = (widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE); michael@0: michael@0: /* The size of the dimension we are going to use for the animation. */ michael@0: const gint progressSize = vertical ? rect->height : rect->width; michael@0: michael@0: /* The bar is using a fifth of the element size, based on GtkProgressBar michael@0: * activity-blocks property. */ michael@0: const gint barSize = MAX(1, progressSize / 5); michael@0: michael@0: /* Represents the travel that has to be done for a complete cycle. */ michael@0: const gint travel = 2 * (progressSize - barSize); michael@0: michael@0: /* period equals to travel / pixelsPerMillisecond michael@0: * where pixelsPerMillisecond equals progressSize / 1000.0. michael@0: * This is equivalent to 1600. */ michael@0: static const guint period = 1600; michael@0: const gint t = PR_IntervalToMilliseconds(PR_IntervalNow()) % period; michael@0: const gint dx = travel * t / period; michael@0: michael@0: if (vertical) { michael@0: rect->y += (dx < travel / 2) ? dx : travel - dx; michael@0: rect->height = barSize; michael@0: } else { michael@0: rect->x += (dx < travel / 2) ? dx : travel - dx; michael@0: rect->width = barSize; michael@0: } michael@0: } michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_activity(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_tab_thickness(void) michael@0: { michael@0: GtkBorder border; michael@0: michael@0: ensure_tab_widget(); michael@0: GtkStyleContext * style = gtk_widget_get_style_context(gTabWidget); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); michael@0: gtk_style_context_get_border(style, 0, &border); michael@0: michael@0: if (border.top < 2) michael@0: return 2; /* some themes don't set ythickness correctly */ michael@0: michael@0: return border.top; michael@0: } michael@0: michael@0: /* actual small tabs */ michael@0: static gint michael@0: moz_gtk_tab_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkTabFlags flags, GtkTextDirection direction) michael@0: { michael@0: /* When the tab isn't selected, we just draw a notebook extension. michael@0: * When it is selected, we overwrite the adjacent border of the tabpanel michael@0: * touching the tab with a pierced border (called "the gap") to make the michael@0: * tab appear physically attached to the tabpanel; see details below. */ michael@0: michael@0: GtkStyleContext* style; michael@0: GdkRectangle focusRect; michael@0: GdkRectangle backRect; michael@0: michael@0: ensure_tab_widget(); michael@0: gtk_widget_set_direction(gTabWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gTabWidget); michael@0: backRect = focusRect = *rect; michael@0: michael@0: gtk_style_context_save(style); michael@0: michael@0: if ((flags & MOZ_GTK_TAB_SELECTED) == 0) { michael@0: /* Only draw the tab */ michael@0: gtk_style_context_set_state(style, GTK_STATE_FLAG_NORMAL); michael@0: gtk_render_extension(style, cr, michael@0: rect->x, rect->y, rect->width, rect->height, michael@0: (flags & MOZ_GTK_TAB_BOTTOM) ? michael@0: GTK_POS_TOP : GTK_POS_BOTTOM ); michael@0: } else { michael@0: /* Draw the tab and the gap michael@0: * We want the gap to be positioned exactly on the tabpanel top michael@0: * border; since tabbox.css may set a negative margin so that the tab michael@0: * frame rect already overlaps the tabpanel frame rect, we need to take michael@0: * that into account when drawing. To that effect, nsNativeThemeGTK michael@0: * passes us this negative margin (bmargin in the graphic below) in the michael@0: * lowest bits of |flags|. We use it to set gap_voffset, the distance michael@0: * between the top of the gap and the bottom of the tab (resp. the michael@0: * bottom of the gap and the top of the tab when we draw a bottom tab), michael@0: * while ensuring that the gap always touches the border of the tab, michael@0: * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results michael@0: * with big negative or positive margins. michael@0: * Here is a graphical explanation in the case of top tabs: michael@0: * ___________________________ michael@0: * / \ michael@0: * | T A B | michael@0: * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel michael@0: * : ^ bmargin : ^ michael@0: * : | (-negative margin, : | michael@0: * bottom : v passed in flags) : | gap_height michael@0: * of -> :.............................: | (the size of the michael@0: * the tab . part of the gap . | tabpanel top border) michael@0: * . outside of the tab . v michael@0: * ---------------------------------------------- michael@0: * michael@0: * To draw the gap, we use gtk_paint_box_gap(), see comment in michael@0: * moz_gtk_tabpanels_paint(). This box_gap is made 3 * gap_height tall, michael@0: * which should suffice to ensure that the only visible border is the michael@0: * pierced one. If the tab is in the middle, we make the box_gap begin michael@0: * a bit to the left of the tab and end a bit to the right, adjusting michael@0: * the gap position so it still is under the tab, because we want the michael@0: * rendering of a gap in the middle of a tabpanel. This is the role of michael@0: * the gints gap_{l,r}_offset. On the contrary, if the tab is the michael@0: * first, we align the start border of the box_gap with the start michael@0: * border of the tab (left if LTR, right if RTL), by setting the michael@0: * appropriate offset to 0.*/ michael@0: gint gap_loffset, gap_roffset, gap_voffset, gap_height; michael@0: michael@0: /* Get height needed by the gap */ michael@0: gap_height = moz_gtk_get_tab_thickness(); michael@0: michael@0: /* Extract gap_voffset from the first bits of flags */ michael@0: gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK; michael@0: if (gap_voffset > gap_height) michael@0: gap_voffset = gap_height; michael@0: michael@0: /* Set gap_{l,r}_offset to appropriate values */ michael@0: gap_loffset = gap_roffset = 20; /* should be enough */ michael@0: if (flags & MOZ_GTK_TAB_FIRST) { michael@0: if (direction == GTK_TEXT_DIR_RTL) michael@0: gap_roffset = 0; michael@0: else michael@0: gap_loffset = 0; michael@0: } michael@0: michael@0: gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE); michael@0: michael@0: /* Adwaita theme engine crashes without it (rhbz#713764) */ michael@0: gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, 0); michael@0: michael@0: if (flags & MOZ_GTK_TAB_BOTTOM) { michael@0: /* Draw the tab on bottom */ michael@0: focusRect.y += gap_voffset; michael@0: focusRect.height -= gap_voffset; michael@0: michael@0: gtk_render_extension(style, cr, michael@0: rect->x, rect->y + gap_voffset, rect->width, michael@0: rect->height - gap_voffset, GTK_POS_TOP); michael@0: michael@0: gtk_style_context_remove_region(style, GTK_STYLE_REGION_TAB); michael@0: michael@0: backRect.y += (gap_voffset - gap_height); michael@0: backRect.height = gap_height; michael@0: michael@0: /* Draw the gap; erase with background color before painting in michael@0: * case theme does not */ michael@0: gtk_render_background(style, cr, backRect.x, backRect.y, michael@0: backRect.width, backRect.height); michael@0: cairo_save(cr); michael@0: cairo_rectangle(cr, backRect.x, backRect.y, backRect.width, backRect.height); michael@0: cairo_clip(cr); michael@0: michael@0: gtk_render_frame_gap(style, cr, michael@0: rect->x - gap_loffset, michael@0: rect->y + gap_voffset - 3 * gap_height, michael@0: rect->width + gap_loffset + gap_roffset, michael@0: 3 * gap_height, GTK_POS_BOTTOM, michael@0: gap_loffset, gap_loffset + rect->width); michael@0: cairo_restore(cr); michael@0: } else { michael@0: /* Draw the tab on top */ michael@0: focusRect.height -= gap_voffset; michael@0: gtk_render_extension(style, cr, michael@0: rect->x, rect->y, rect->width, michael@0: rect->height - gap_voffset, GTK_POS_BOTTOM); michael@0: michael@0: gtk_style_context_remove_region(style, GTK_STYLE_REGION_TAB); michael@0: michael@0: backRect.y += (rect->height - gap_voffset); michael@0: backRect.height = gap_height; michael@0: michael@0: /* Draw the gap; erase with background color before painting in michael@0: * case theme does not */ michael@0: gtk_render_background(style, cr, backRect.x, backRect.y, michael@0: backRect.width, backRect.height); michael@0: cairo_save(cr); michael@0: cairo_rectangle(cr, backRect.x, backRect.y, backRect.width, backRect.height); michael@0: cairo_clip(cr); michael@0: michael@0: gtk_render_frame_gap(style, cr, michael@0: rect->x - gap_loffset, michael@0: rect->y + rect->height - gap_voffset, michael@0: rect->width + gap_loffset + gap_roffset, michael@0: 3 * gap_height, GTK_POS_TOP, michael@0: gap_loffset, gap_loffset + rect->width); michael@0: cairo_restore(cr); michael@0: } michael@0: } michael@0: michael@0: if (state->focused) { michael@0: /* Paint the focus ring */ michael@0: GtkBorder border; michael@0: gtk_style_context_get_border(style, GetStateFlagsFromGtkWidgetState(state), &border); michael@0: michael@0: focusRect.x += border.left; michael@0: focusRect.width -= (border.left + border.right); michael@0: focusRect.y += border.top; michael@0: focusRect.height -= (border.top + border.bottom); michael@0: michael@0: gtk_render_focus(style, cr, michael@0: focusRect.x, focusRect.y, focusRect.width, focusRect.height); michael@0: } michael@0: michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: /* tab area*/ michael@0: static gint michael@0: moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_tab_widget(); michael@0: gtk_widget_set_direction(gTabWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gTabWidget); michael@0: gtk_style_context_save(style); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, michael@0: rect->width, rect->height); michael@0: /* michael@0: * The gap size is not needed in moz_gtk_tabpanels_paint because michael@0: * the gap will be painted with the foreground tab in moz_gtk_tab_paint. michael@0: * michael@0: * However, if moz_gtk_tabpanels_paint just uses gtk_render_frame(), michael@0: * the theme will think that there are no tabs and may draw something michael@0: * different.Hence the trick of using two clip regions, and drawing the michael@0: * gap outside each clip region, to get the correct frame for michael@0: * a tabpanel with tabs. michael@0: */ michael@0: /* left side */ michael@0: cairo_save(cr); michael@0: cairo_rectangle(cr, rect->x, rect->y, michael@0: rect->x + rect->width / 2, michael@0: rect->y + rect->height); michael@0: cairo_clip(cr); michael@0: gtk_render_frame_gap(style, cr, michael@0: rect->x, rect->y, michael@0: rect->width, rect->height, michael@0: GTK_POS_TOP, rect->width - 1, rect->width); michael@0: cairo_restore(cr); michael@0: michael@0: /* right side */ michael@0: cairo_save(cr); michael@0: cairo_rectangle(cr, rect->x + rect->width / 2, rect->y, michael@0: rect->x + rect->width, michael@0: rect->y + rect->height); michael@0: cairo_clip(cr); michael@0: gtk_render_frame_gap(style, cr, michael@0: rect->x, rect->y, michael@0: rect->width, rect->height, michael@0: GTK_POS_TOP, 0, 1); michael@0: cairo_restore(cr); michael@0: michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_tab_scroll_arrow_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkArrowType arrow_type, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStyleContext* style; michael@0: gdouble arrow_angle; michael@0: gint arrow_size = MIN(rect->width, rect->height); michael@0: gint x = rect->x + (rect->width - arrow_size) / 2; michael@0: gint y = rect->y + (rect->height - arrow_size) / 2; michael@0: michael@0: ensure_tab_widget(); michael@0: michael@0: style = gtk_widget_get_style_context(gTabWidget); michael@0: gtk_style_context_save(style); michael@0: if (direction == GTK_TEXT_DIR_RTL) { michael@0: arrow_type = (arrow_type == GTK_ARROW_LEFT) ? michael@0: GTK_ARROW_RIGHT : GTK_ARROW_LEFT; michael@0: } michael@0: switch (arrow_type) { michael@0: case GTK_ARROW_LEFT: michael@0: arrow_angle = ARROW_LEFT; michael@0: break; michael@0: case GTK_ARROW_RIGHT: michael@0: arrow_angle = ARROW_RIGHT; michael@0: break; michael@0: case GTK_ARROW_DOWN: michael@0: arrow_angle = ARROW_DOWN; michael@0: break; michael@0: default: michael@0: arrow_angle = ARROW_UP; michael@0: break; michael@0: } michael@0: if (arrow_type != GTK_ARROW_NONE) { michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); /* TODO TEST */ michael@0: gtk_style_context_set_state(style, state_flags); michael@0: gtk_render_arrow(style, cr, arrow_angle, michael@0: x, y, arrow_size); michael@0: } michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_menu_bar_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_menu_bar_widget(); michael@0: gtk_widget_set_direction(gMenuBarWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gMenuBarWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_menu_popup_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_menu_popup_widget(); michael@0: gtk_widget_set_direction(gMenuPopupWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gMenuPopupWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENU); michael@0: michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: // See gtk_menu_item_draw() for reference. michael@0: static gint michael@0: moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: gboolean wide_separators; michael@0: gint separator_height; michael@0: gint paint_height; michael@0: guint border_width; michael@0: gint x, y, w, h; michael@0: GtkBorder padding; michael@0: michael@0: ensure_menu_separator_widget(); michael@0: gtk_widget_set_direction(gMenuSeparatorWidget, direction); michael@0: michael@0: border_width = gtk_container_get_border_width(gMenuSeparatorWidget); michael@0: gtk_widget_style_get(gMenuSeparatorWidget, michael@0: "wide-separators", &wide_separators, michael@0: "separator-height", &separator_height, michael@0: NULL); michael@0: michael@0: style = gtk_widget_get_style_context(gMenuSeparatorWidget); michael@0: gtk_style_context_get_padding(style, 0, &padding); michael@0: michael@0: x = rect->x + border_width; michael@0: y = rect->y + border_width; michael@0: w = rect->width - border_width * 2; michael@0: h = rect->height - border_width * 2; michael@0: michael@0: if (wide_separators) { michael@0: gtk_render_frame(style, cr, michael@0: x + padding.left, michael@0: y + padding.top, michael@0: w - padding.left - padding.right, michael@0: separator_height); michael@0: } else { michael@0: gtk_render_line(style, cr, michael@0: x + padding.left, michael@0: y + padding.top, michael@0: x + w - padding.right - 1, michael@0: y + padding.top); michael@0: } michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: // See gtk_menu_item_draw() for reference. michael@0: static gint michael@0: moz_gtk_menu_item_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gint flags, GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: GtkWidget* item_widget; michael@0: guint border_width; michael@0: gint x, y, w, h; michael@0: michael@0: if (state->inHover && !state->disabled) { michael@0: if (flags & MOZ_TOPLEVEL_MENU_ITEM) { michael@0: ensure_menu_bar_item_widget(); michael@0: item_widget = gMenuBarItemWidget; michael@0: } else { michael@0: ensure_menu_item_widget(); michael@0: item_widget = gMenuItemWidget; michael@0: } michael@0: style = gtk_widget_get_style_context(item_widget); michael@0: gtk_style_context_save(style); michael@0: michael@0: if (flags & MOZ_TOPLEVEL_MENU_ITEM) { michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); michael@0: } michael@0: michael@0: gtk_widget_set_direction(item_widget, direction); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUITEM); michael@0: gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); michael@0: michael@0: border_width = gtk_container_get_border_width(GTK_CONTAINER(item_widget)); michael@0: michael@0: x = rect->x + border_width; michael@0: y = rect->y + border_width; michael@0: w = rect->width - border_width * 2; michael@0: h = rect->height - border_width * 2; michael@0: michael@0: gtk_render_background(style, cr, x, y, w, h); michael@0: gtk_render_frame(style, cr, x, y, w, h); michael@0: gtk_style_context_restore(style); michael@0: } michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_menu_arrow_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: michael@0: ensure_menu_item_widget(); michael@0: gtk_widget_set_direction(gMenuItemWidget, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gMenuItemWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUITEM); michael@0: gtk_style_context_set_state(style, state_flags); michael@0: gtk_render_arrow(style, cr, michael@0: (direction == GTK_TEXT_DIR_LTR) ? ARROW_RIGHT : ARROW_LEFT, michael@0: rect->x, rect->y, rect->width); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: // See gtk_real_check_menu_item_draw_indicator() for reference. michael@0: static gint michael@0: moz_gtk_check_menu_item_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkWidgetState* state, michael@0: gboolean checked, gboolean isradio, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); michael@0: GtkStyleContext* style; michael@0: GtkBorder padding; michael@0: gint offset; michael@0: gint indicator_size, horizontal_padding; michael@0: gint x, y; michael@0: michael@0: moz_gtk_menu_item_paint(cr, rect, state, FALSE, direction); michael@0: michael@0: ensure_check_menu_item_widget(); michael@0: gtk_widget_set_direction(gCheckMenuItemWidget, direction); michael@0: michael@0: gtk_widget_style_get (gCheckMenuItemWidget, michael@0: "indicator-size", &indicator_size, michael@0: "horizontal-padding", &horizontal_padding, michael@0: NULL); michael@0: michael@0: style = gtk_widget_get_style_context(gCheckMenuItemWidget); michael@0: gtk_style_context_save(style); michael@0: if (isradio) { michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); michael@0: } else { michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); michael@0: } michael@0: michael@0: if (checked) michael@0: state_flags |= GTK_STATE_FLAG_ACTIVE; michael@0: michael@0: gtk_style_context_set_state(style, state_flags); michael@0: gtk_style_context_get_padding(style, state_flags, &padding); michael@0: michael@0: offset = gtk_container_get_border_width(GTK_CONTAINER(gCheckMenuItemWidget)) + michael@0: padding.left + 2; michael@0: michael@0: if (direction == GTK_TEXT_DIR_RTL) { michael@0: x = rect->width - indicator_size - offset - horizontal_padding; michael@0: } michael@0: else { michael@0: x = rect->x + offset + horizontal_padding; michael@0: } michael@0: y = rect->y + (rect->height - indicator_size) / 2; michael@0: michael@0: if (isradio) { michael@0: gtk_render_option(style, cr, x, y, indicator_size, indicator_size); michael@0: } else { michael@0: gtk_render_check(style, cr, x, y, indicator_size, indicator_size); michael@0: } michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static gint michael@0: moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect, michael@0: GtkTextDirection direction) michael@0: { michael@0: GtkStyleContext* style; michael@0: michael@0: ensure_window_widget(); michael@0: gtk_widget_set_direction(gProtoWindow, direction); michael@0: michael@0: style = gtk_widget_get_style_context(gProtoWindow); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND); michael@0: gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); michael@0: gtk_style_context_restore(style); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: static void michael@0: moz_gtk_add_style_border(GtkStyleContext* style, michael@0: gint* left, gint* top, gint* right, gint* bottom) michael@0: { michael@0: GtkBorder border; michael@0: michael@0: gtk_style_context_get_border(style, 0, &border); michael@0: michael@0: *left += border.left; michael@0: *right += border.right; michael@0: *top += border.top; michael@0: *bottom += border.bottom; michael@0: } michael@0: michael@0: static void michael@0: moz_gtk_add_style_padding(GtkStyleContext* style, michael@0: gint* left, gint* top, gint* right, gint* bottom) michael@0: { michael@0: GtkBorder padding; michael@0: michael@0: gtk_style_context_get_padding(style, 0, &padding); michael@0: michael@0: *left += padding.left; michael@0: *right += padding.right; michael@0: *top += padding.top; michael@0: *bottom += padding.bottom; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, michael@0: gint* right, gint* bottom, GtkTextDirection direction, michael@0: gboolean inhtml) michael@0: { michael@0: GtkWidget* w; michael@0: GtkStyleContext* style; michael@0: *left = *top = *right = *bottom = 0; michael@0: michael@0: switch (widget) { michael@0: case MOZ_GTK_BUTTON: michael@0: { michael@0: GtkBorder inner_border; michael@0: gboolean interior_focus; michael@0: gint focus_width, focus_pad; michael@0: michael@0: ensure_button_widget(); michael@0: *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gButtonWidget)); michael@0: michael@0: /* Don't add this padding in HTML, otherwise the buttons will michael@0: become too big and stuff the layout. */ michael@0: if (!inhtml) { michael@0: moz_gtk_widget_get_focus(gButtonWidget, &interior_focus, &focus_width, &focus_pad); michael@0: moz_gtk_button_get_inner_border(gButtonWidget, &inner_border); michael@0: *left += focus_width + focus_pad + inner_border.left; michael@0: *right += focus_width + focus_pad + inner_border.right; michael@0: *top += focus_width + focus_pad + inner_border.top; michael@0: *bottom += focus_width + focus_pad + inner_border.bottom; michael@0: } michael@0: michael@0: moz_gtk_add_style_border(gtk_widget_get_style_context(gButtonWidget), michael@0: left, top, right, bottom); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: case MOZ_GTK_ENTRY: michael@0: { michael@0: ensure_entry_widget(); michael@0: style = gtk_widget_get_style_context(gEntryWidget); michael@0: moz_gtk_add_style_border(style, left, top, right, bottom); michael@0: moz_gtk_add_style_padding(style, left, top, right, bottom); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: case MOZ_GTK_TREEVIEW: michael@0: { michael@0: ensure_scrolled_window_widget(); michael@0: style = gtk_widget_get_style_context(gScrolledWindowWidget); michael@0: gtk_style_context_save(style); michael@0: gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME); michael@0: moz_gtk_add_style_border(style, left, top, right, bottom); michael@0: gtk_style_context_restore(style); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: case MOZ_GTK_TREE_HEADER_CELL: michael@0: { michael@0: /* A Tree Header in GTK is just a different styled button michael@0: * It must be placed in a TreeView for getting the correct style michael@0: * assigned. michael@0: * That is why the following code is the same as for MOZ_GTK_BUTTON. michael@0: * */ michael@0: michael@0: GtkBorder inner_border; michael@0: gboolean interior_focus; michael@0: gint focus_width, focus_pad; michael@0: michael@0: ensure_tree_header_cell_widget(); michael@0: *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gTreeHeaderCellWidget)); michael@0: michael@0: moz_gtk_widget_get_focus(gTreeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad); michael@0: moz_gtk_button_get_inner_border(gTreeHeaderCellWidget, &inner_border); michael@0: *left += focus_width + focus_pad + inner_border.left; michael@0: *right += focus_width + focus_pad + inner_border.right; michael@0: *top += focus_width + focus_pad + inner_border.top; michael@0: *bottom += focus_width + focus_pad + inner_border.bottom; michael@0: michael@0: moz_gtk_add_style_border(gtk_widget_get_style_context(gTreeHeaderCellWidget), michael@0: left, top, right, bottom); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: case MOZ_GTK_TREE_HEADER_SORTARROW: michael@0: ensure_tree_header_cell_widget(); michael@0: w = gTreeHeaderSortArrowWidget; michael@0: break; michael@0: case MOZ_GTK_DROPDOWN_ENTRY: michael@0: ensure_combo_box_entry_widgets(); michael@0: w = gComboBoxEntryTextareaWidget; michael@0: break; michael@0: case MOZ_GTK_DROPDOWN_ARROW: michael@0: ensure_combo_box_entry_widgets(); michael@0: w = gComboBoxEntryButtonWidget; michael@0: break; michael@0: case MOZ_GTK_DROPDOWN: michael@0: { michael@0: /* We need to account for the arrow on the dropdown, so text michael@0: * doesn't come too close to the arrow, or in some cases spill michael@0: * into the arrow. */ michael@0: gboolean ignored_interior_focus, wide_separators; michael@0: gint focus_width, focus_pad, separator_width; michael@0: GtkRequisition arrow_req; michael@0: GtkBorder border; michael@0: michael@0: ensure_combo_box_widgets(); michael@0: michael@0: *left = gtk_container_get_border_width(GTK_CONTAINER(gComboBoxButtonWidget)); michael@0: michael@0: if (!inhtml) { michael@0: moz_gtk_widget_get_focus(gComboBoxButtonWidget, michael@0: &ignored_interior_focus, michael@0: &focus_width, &focus_pad); michael@0: *left += focus_width + focus_pad; michael@0: } michael@0: michael@0: style = gtk_widget_get_style_context(gComboBoxButtonWidget); michael@0: gtk_style_context_get_border(style, 0, &border); michael@0: michael@0: *top = *left + border.top; michael@0: *left += border.left; michael@0: michael@0: *right = *left; *bottom = *top; michael@0: michael@0: /* If there is no separator, don't try to count its width. */ michael@0: separator_width = 0; michael@0: if (gComboBoxSeparatorWidget) { michael@0: gtk_widget_style_get(gComboBoxSeparatorWidget, michael@0: "wide-separators", &wide_separators, michael@0: "separator-width", &separator_width, michael@0: NULL); michael@0: michael@0: if (!wide_separators) { michael@0: style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); michael@0: gtk_style_context_get_border(style, 0, &border); michael@0: separator_width = border.left; michael@0: } michael@0: } michael@0: michael@0: gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req); michael@0: michael@0: if (direction == GTK_TEXT_DIR_RTL) michael@0: *left += separator_width + arrow_req.width; michael@0: else michael@0: *right += separator_width + arrow_req.width; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: case MOZ_GTK_TABPANELS: michael@0: ensure_tab_widget(); michael@0: w = gTabWidget; michael@0: break; michael@0: case MOZ_GTK_PROGRESSBAR: michael@0: ensure_progress_widget(); michael@0: w = gProgressWidget; michael@0: break; michael@0: case MOZ_GTK_SPINBUTTON_ENTRY: michael@0: case MOZ_GTK_SPINBUTTON_UP: michael@0: case MOZ_GTK_SPINBUTTON_DOWN: michael@0: ensure_spin_widget(); michael@0: w = gSpinWidget; michael@0: break; michael@0: case MOZ_GTK_SCALE_HORIZONTAL: michael@0: ensure_scale_widget(); michael@0: w = gHScaleWidget; michael@0: break; michael@0: case MOZ_GTK_SCALE_VERTICAL: michael@0: ensure_scale_widget(); michael@0: w = gVScaleWidget; michael@0: break; michael@0: case MOZ_GTK_FRAME: michael@0: ensure_frame_widget(); michael@0: w = gFrameWidget; michael@0: break; michael@0: case MOZ_GTK_CHECKBUTTON_LABEL: michael@0: case MOZ_GTK_RADIOBUTTON_LABEL: michael@0: { michael@0: gboolean interior_focus; michael@0: gint focus_width, focus_pad; michael@0: michael@0: /* If the focus is interior, then the label has a border of michael@0: (focus_width + focus_pad). */ michael@0: if (widget == MOZ_GTK_CHECKBUTTON_LABEL) { michael@0: ensure_checkbox_widget(); michael@0: moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus, michael@0: &focus_width, &focus_pad); michael@0: } michael@0: else { michael@0: ensure_radiobutton_widget(); michael@0: moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus, michael@0: &focus_width, &focus_pad); michael@0: } michael@0: michael@0: if (interior_focus) michael@0: *left = *top = *right = *bottom = (focus_width + focus_pad); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: case MOZ_GTK_CHECKBUTTON_CONTAINER: michael@0: case MOZ_GTK_RADIOBUTTON_CONTAINER: michael@0: { michael@0: gboolean interior_focus; michael@0: gint focus_width, focus_pad; michael@0: michael@0: /* If the focus is _not_ interior, then the container has a border michael@0: of (focus_width + focus_pad). */ michael@0: if (widget == MOZ_GTK_CHECKBUTTON_CONTAINER) { michael@0: ensure_checkbox_widget(); michael@0: moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus, michael@0: &focus_width, &focus_pad); michael@0: w = gCheckboxWidget; michael@0: } else { michael@0: ensure_radiobutton_widget(); michael@0: moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus, michael@0: &focus_width, &focus_pad); michael@0: w = gRadiobuttonWidget; michael@0: } michael@0: michael@0: *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w)); michael@0: michael@0: if (!interior_focus) { michael@0: *left += (focus_width + focus_pad); michael@0: *right += (focus_width + focus_pad); michael@0: *top += (focus_width + focus_pad); michael@0: *bottom += (focus_width + focus_pad); michael@0: } michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: case MOZ_GTK_MENUPOPUP: michael@0: ensure_menu_popup_widget(); michael@0: w = gMenuPopupWidget; michael@0: break; michael@0: case MOZ_GTK_MENUITEM: michael@0: case MOZ_GTK_CHECKMENUITEM: michael@0: case MOZ_GTK_RADIOMENUITEM: michael@0: { michael@0: if (widget == MOZ_GTK_MENUITEM) { michael@0: ensure_menu_item_widget(); michael@0: ensure_menu_bar_item_widget(); michael@0: w = gMenuItemWidget; michael@0: } michael@0: else { michael@0: ensure_check_menu_item_widget(); michael@0: w = gCheckMenuItemWidget; michael@0: } michael@0: michael@0: *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w)); michael@0: moz_gtk_add_style_padding(gtk_widget_get_style_context(w), michael@0: left, top, right, bottom); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: case MOZ_GTK_TAB: michael@0: ensure_tab_widget(); michael@0: w = gTabWidget; michael@0: break; michael@0: /* These widgets have no borders, since they are not containers. */ michael@0: case MOZ_GTK_SPLITTER_HORIZONTAL: michael@0: case MOZ_GTK_SPLITTER_VERTICAL: michael@0: case MOZ_GTK_CHECKBUTTON: michael@0: case MOZ_GTK_RADIOBUTTON: michael@0: case MOZ_GTK_SCROLLBAR_BUTTON: michael@0: case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: michael@0: case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: michael@0: case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: michael@0: case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: michael@0: case MOZ_GTK_SCALE_THUMB_HORIZONTAL: michael@0: case MOZ_GTK_SCALE_THUMB_VERTICAL: michael@0: case MOZ_GTK_GRIPPER: michael@0: case MOZ_GTK_PROGRESS_CHUNK: michael@0: case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE: michael@0: case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE: michael@0: case MOZ_GTK_TREEVIEW_EXPANDER: michael@0: case MOZ_GTK_TOOLBAR_SEPARATOR: michael@0: case MOZ_GTK_MENUSEPARATOR: michael@0: /* These widgets have no borders.*/ michael@0: case MOZ_GTK_SPINBUTTON: michael@0: case MOZ_GTK_TOOLTIP: michael@0: case MOZ_GTK_WINDOW: michael@0: case MOZ_GTK_RESIZER: michael@0: case MOZ_GTK_MENUARROW: michael@0: case MOZ_GTK_TOOLBARBUTTON_ARROW: michael@0: case MOZ_GTK_TOOLBAR: michael@0: case MOZ_GTK_MENUBAR: michael@0: case MOZ_GTK_TAB_SCROLLARROW: michael@0: return MOZ_GTK_SUCCESS; michael@0: default: michael@0: g_warning("Unsupported widget type: %d", widget); michael@0: return MOZ_GTK_UNKNOWN_WIDGET; michael@0: } michael@0: /* TODO - we're still missing some widget implementations */ michael@0: if (w) { michael@0: moz_gtk_add_style_border(gtk_widget_get_style_context(w), michael@0: left, top, right, bottom); michael@0: } michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height) michael@0: { michael@0: /* michael@0: * We get the requisition of the drop down button, which includes michael@0: * all padding, border and focus line widths the button uses, michael@0: * as well as the minimum arrow size and its padding michael@0: * */ michael@0: GtkRequisition requisition; michael@0: ensure_combo_box_entry_widgets(); michael@0: michael@0: gtk_widget_size_request(gComboBoxEntryButtonWidget, &requisition); michael@0: *width = requisition.width; michael@0: *height = requisition.height; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height) michael@0: { michael@0: gint arrow_size; michael@0: michael@0: ensure_tab_widget(); michael@0: gtk_widget_style_get(gTabWidget, michael@0: "scroll-arrow-hlength", &arrow_size, michael@0: NULL); michael@0: michael@0: *height = *width = arrow_size; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_arrow_size(gint* width, gint* height) michael@0: { michael@0: GtkRequisition requisition; michael@0: ensure_button_arrow_widget(); michael@0: michael@0: gtk_widget_size_request(gButtonArrowWidget, &requisition); michael@0: *width = requisition.width; michael@0: *height = requisition.height; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_toolbar_separator_width(gint* size) michael@0: { michael@0: gboolean wide_separators; michael@0: gint separator_width; michael@0: GtkStyleContext* style; michael@0: GtkBorder border; michael@0: michael@0: ensure_toolbar_widget(); michael@0: style = gtk_widget_get_style_context(gToolbarWidget); michael@0: michael@0: gtk_widget_style_get(gToolbarWidget, michael@0: "space-size", size, michael@0: "wide-separators", &wide_separators, michael@0: "separator-width", &separator_width, michael@0: NULL); michael@0: /* Just in case... */ michael@0: gtk_style_context_get_border(style, 0, &border); michael@0: *size = MAX(*size, (wide_separators ? separator_width : border.left)); michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_expander_size(gint* size) michael@0: { michael@0: ensure_expander_widget(); michael@0: gtk_widget_style_get(gExpanderWidget, michael@0: "expander-size", size, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_treeview_expander_size(gint* size) michael@0: { michael@0: ensure_tree_view_widget(); michael@0: gtk_widget_style_get(gTreeViewWidget, michael@0: "expander-size", size, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: // See gtk_menu_item_draw() for reference. michael@0: gint michael@0: moz_gtk_get_menu_separator_height(gint *size) michael@0: { michael@0: gboolean wide_separators; michael@0: gint separator_height; michael@0: GtkBorder padding; michael@0: GtkStyleContext* style; michael@0: guint border_width; michael@0: michael@0: ensure_menu_separator_widget(); michael@0: michael@0: gtk_widget_style_get(gMenuSeparatorWidget, michael@0: "wide-separators", &wide_separators, michael@0: "separator-height", &separator_height, michael@0: NULL); michael@0: michael@0: border_width = gtk_container_get_border_width(GTK_CONTAINER(gMenuSeparatorWidget)); michael@0: michael@0: style = gtk_widget_get_style_context(gMenuSeparatorWidget); michael@0: gtk_style_context_get_padding(style, 0, &padding); michael@0: michael@0: *size = padding.top + padding.bottom + border_width*2; michael@0: *size += (wide_separators) ? separator_height : 1; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height) michael@0: { michael@0: GtkWidget* widget; michael@0: michael@0: ensure_scale_widget(); michael@0: widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); michael@0: michael@0: gtk_widget_style_get (widget, michael@0: "slider_length", thumb_length, michael@0: "slider_width", thumb_height, michael@0: NULL); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) michael@0: { michael@0: ensure_scrollbar_widget(); michael@0: michael@0: gtk_widget_style_get (gHorizScrollbarWidget, michael@0: "slider_width", &metrics->slider_width, michael@0: "trough_border", &metrics->trough_border, michael@0: "stepper_size", &metrics->stepper_size, michael@0: "stepper_spacing", &metrics->stepper_spacing, michael@0: NULL); michael@0: michael@0: metrics->min_slider_size = michael@0: gtk_range_get_min_slider_size(GTK_RANGE(gHorizScrollbarWidget)); michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: } michael@0: michael@0: gboolean michael@0: moz_gtk_images_in_menus() michael@0: { michael@0: gboolean result; michael@0: GtkSettings* settings; michael@0: michael@0: ensure_image_menu_item_widget(); michael@0: settings = gtk_widget_get_settings(gImageMenuItemWidget); michael@0: michael@0: g_object_get(settings, "gtk-menu-images", &result, NULL); michael@0: return result; michael@0: } michael@0: michael@0: gboolean michael@0: moz_gtk_images_in_buttons() michael@0: { michael@0: gboolean result; michael@0: GtkSettings* settings; michael@0: michael@0: ensure_button_widget(); michael@0: settings = gtk_widget_get_settings(gButtonWidget); michael@0: michael@0: g_object_get(settings, "gtk-button-images", &result, NULL); michael@0: return result; michael@0: } michael@0: michael@0: /* cairo_t *cr argument has to be a system-cairo. */ michael@0: gint michael@0: moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t *cr, michael@0: GdkRectangle* rect, michael@0: GtkWidgetState* state, gint flags, michael@0: GtkTextDirection direction) michael@0: { michael@0: /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086 michael@0: */ michael@0: cairo_new_path(cr); michael@0: michael@0: switch (widget) { michael@0: case MOZ_GTK_BUTTON: michael@0: if (state->depressed) { michael@0: ensure_toggle_menu_button_widget(); michael@0: return moz_gtk_button_paint(cr, rect, state, michael@0: (GtkReliefStyle) flags, michael@0: gToggleMenuButtonWidget, direction); michael@0: } michael@0: ensure_button_widget(); michael@0: return moz_gtk_button_paint(cr, rect, state, michael@0: (GtkReliefStyle) flags, gButtonWidget, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_CHECKBUTTON: michael@0: case MOZ_GTK_RADIOBUTTON: michael@0: return moz_gtk_toggle_paint(cr, rect, state, michael@0: !!(flags & MOZ_GTK_WIDGET_CHECKED), michael@0: !!(flags & MOZ_GTK_WIDGET_INCONSISTENT), michael@0: (widget == MOZ_GTK_RADIOBUTTON), michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_SCROLLBAR_BUTTON: michael@0: return moz_gtk_scrollbar_button_paint(cr, rect, state, michael@0: (GtkScrollbarButtonFlags) flags, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: michael@0: case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: michael@0: return moz_gtk_scrollbar_trough_paint(widget, cr, rect, michael@0: state, direction); michael@0: break; michael@0: case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: michael@0: case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: michael@0: return moz_gtk_scrollbar_thumb_paint(widget, cr, rect, michael@0: state, direction); michael@0: break; michael@0: case MOZ_GTK_SCALE_HORIZONTAL: michael@0: case MOZ_GTK_SCALE_VERTICAL: michael@0: return moz_gtk_scale_paint(cr, rect, state, michael@0: (GtkOrientation) flags, direction); michael@0: break; michael@0: case MOZ_GTK_SCALE_THUMB_HORIZONTAL: michael@0: case MOZ_GTK_SCALE_THUMB_VERTICAL: michael@0: return moz_gtk_scale_thumb_paint(cr, rect, state, michael@0: (GtkOrientation) flags, direction); michael@0: break; michael@0: case MOZ_GTK_SPINBUTTON: michael@0: return moz_gtk_spin_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_SPINBUTTON_UP: michael@0: case MOZ_GTK_SPINBUTTON_DOWN: michael@0: return moz_gtk_spin_updown_paint(cr, rect, michael@0: (widget == MOZ_GTK_SPINBUTTON_DOWN), michael@0: state, direction); michael@0: break; michael@0: case MOZ_GTK_SPINBUTTON_ENTRY: michael@0: ensure_spin_widget(); michael@0: return moz_gtk_entry_paint(cr, rect, state, michael@0: gSpinWidget, direction); michael@0: break; michael@0: case MOZ_GTK_GRIPPER: michael@0: return moz_gtk_gripper_paint(cr, rect, state, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_TREEVIEW: michael@0: return moz_gtk_treeview_paint(cr, rect, state, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_TREE_HEADER_CELL: michael@0: return moz_gtk_tree_header_cell_paint(cr, rect, state, michael@0: flags, direction); michael@0: break; michael@0: case MOZ_GTK_TREE_HEADER_SORTARROW: michael@0: return moz_gtk_tree_header_sort_arrow_paint(cr, rect, michael@0: state, michael@0: (GtkArrowType) flags, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_TREEVIEW_EXPANDER: michael@0: return moz_gtk_treeview_expander_paint(cr, rect, state, michael@0: (GtkExpanderStyle) flags, direction); michael@0: break; michael@0: case MOZ_GTK_ENTRY: michael@0: ensure_entry_widget(); michael@0: return moz_gtk_entry_paint(cr, rect, state, michael@0: gEntryWidget, direction); michael@0: break; michael@0: case MOZ_GTK_DROPDOWN: michael@0: return moz_gtk_combo_box_paint(cr, rect, state, michael@0: (gboolean) flags, direction); michael@0: break; michael@0: case MOZ_GTK_DROPDOWN_ARROW: michael@0: return moz_gtk_combo_box_entry_button_paint(cr, rect, michael@0: state, flags, direction); michael@0: break; michael@0: case MOZ_GTK_DROPDOWN_ENTRY: michael@0: ensure_combo_box_entry_widgets(); michael@0: return moz_gtk_entry_paint(cr, rect, state, michael@0: gComboBoxEntryTextareaWidget, direction); michael@0: break; michael@0: case MOZ_GTK_CHECKBUTTON_CONTAINER: michael@0: case MOZ_GTK_RADIOBUTTON_CONTAINER: michael@0: return moz_gtk_container_paint(cr, rect, state, michael@0: (widget == MOZ_GTK_RADIOBUTTON_CONTAINER), michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_CHECKBUTTON_LABEL: michael@0: case MOZ_GTK_RADIOBUTTON_LABEL: michael@0: return moz_gtk_toggle_label_paint(cr, rect, state, michael@0: (widget == MOZ_GTK_RADIOBUTTON_LABEL), michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_TOOLBAR: michael@0: return moz_gtk_toolbar_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_TOOLBAR_SEPARATOR: michael@0: return moz_gtk_toolbar_separator_paint(cr, rect, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_TOOLTIP: michael@0: return moz_gtk_tooltip_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_FRAME: michael@0: return moz_gtk_frame_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_RESIZER: michael@0: return moz_gtk_resizer_paint(cr, rect, state, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_PROGRESSBAR: michael@0: return moz_gtk_progressbar_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_PROGRESS_CHUNK: michael@0: case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE: michael@0: case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE: michael@0: return moz_gtk_progress_chunk_paint(cr, rect, michael@0: direction, widget); michael@0: break; michael@0: case MOZ_GTK_TAB: michael@0: return moz_gtk_tab_paint(cr, rect, state, michael@0: (GtkTabFlags) flags, direction); michael@0: break; michael@0: case MOZ_GTK_TABPANELS: michael@0: return moz_gtk_tabpanels_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_TAB_SCROLLARROW: michael@0: return moz_gtk_tab_scroll_arrow_paint(cr, rect, state, michael@0: (GtkArrowType) flags, direction); michael@0: break; michael@0: case MOZ_GTK_MENUBAR: michael@0: return moz_gtk_menu_bar_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_MENUPOPUP: michael@0: return moz_gtk_menu_popup_paint(cr, rect, direction); michael@0: break; michael@0: case MOZ_GTK_MENUSEPARATOR: michael@0: return moz_gtk_menu_separator_paint(cr, rect, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_MENUITEM: michael@0: return moz_gtk_menu_item_paint(cr, rect, state, flags, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_MENUARROW: michael@0: return moz_gtk_menu_arrow_paint(cr, rect, state, michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_TOOLBARBUTTON_ARROW: michael@0: return moz_gtk_arrow_paint(cr, rect, state, michael@0: (GtkArrowType) flags, direction); michael@0: break; michael@0: case MOZ_GTK_CHECKMENUITEM: michael@0: case MOZ_GTK_RADIOMENUITEM: michael@0: return moz_gtk_check_menu_item_paint(cr, rect, state, michael@0: (gboolean) flags, michael@0: (widget == MOZ_GTK_RADIOMENUITEM), michael@0: direction); michael@0: break; michael@0: case MOZ_GTK_SPLITTER_HORIZONTAL: michael@0: return moz_gtk_vpaned_paint(cr, rect, state); michael@0: break; michael@0: case MOZ_GTK_SPLITTER_VERTICAL: michael@0: return moz_gtk_hpaned_paint(cr, rect, state); michael@0: break; michael@0: case MOZ_GTK_WINDOW: michael@0: return moz_gtk_window_paint(cr, rect, direction); michael@0: break; michael@0: default: michael@0: g_warning("Unknown widget type: %d", widget); michael@0: } michael@0: michael@0: return MOZ_GTK_UNKNOWN_WIDGET; michael@0: } michael@0: michael@0: GtkWidget* moz_gtk_get_scrollbar_widget(void) michael@0: { michael@0: NS_ASSERTION(is_initialized, "Forgot to call moz_gtk_init()"); michael@0: ensure_scrollbar_widget(); michael@0: return gHorizScrollbarWidget; michael@0: } michael@0: michael@0: gint michael@0: moz_gtk_shutdown() michael@0: { michael@0: GtkWidgetClass *entry_class; michael@0: michael@0: if (gTooltipWidget) michael@0: gtk_widget_destroy(gTooltipWidget); michael@0: /* This will destroy all of our widgets */ michael@0: if (gProtoWindow) michael@0: gtk_widget_destroy(gProtoWindow); michael@0: michael@0: /* TODO - replace it with appropriate widget */ michael@0: if (gTreeHeaderSortArrowWidget) michael@0: gtk_widget_destroy(gTreeHeaderSortArrowWidget); michael@0: michael@0: gProtoWindow = NULL; michael@0: gProtoLayout = NULL; michael@0: gButtonWidget = NULL; michael@0: gToggleMenuButtonWidget = NULL; michael@0: gButtonArrowWidget = NULL; michael@0: gCheckboxWidget = NULL; michael@0: gRadiobuttonWidget = NULL; michael@0: gHorizScrollbarWidget = NULL; michael@0: gVertScrollbarWidget = NULL; michael@0: gSpinWidget = NULL; michael@0: gHScaleWidget = NULL; michael@0: gVScaleWidget = NULL; michael@0: gEntryWidget = NULL; michael@0: gComboBoxWidget = NULL; michael@0: gComboBoxButtonWidget = NULL; michael@0: gComboBoxSeparatorWidget = NULL; michael@0: gComboBoxArrowWidget = NULL; michael@0: gComboBoxEntryWidget = NULL; michael@0: gComboBoxEntryButtonWidget = NULL; michael@0: gComboBoxEntryArrowWidget = NULL; michael@0: gComboBoxEntryTextareaWidget = NULL; michael@0: gHandleBoxWidget = NULL; michael@0: gToolbarWidget = NULL; michael@0: gStatusbarWidget = NULL; michael@0: gFrameWidget = NULL; michael@0: gProgressWidget = NULL; michael@0: gTabWidget = NULL; michael@0: gTooltipWidget = NULL; michael@0: gMenuBarWidget = NULL; michael@0: gMenuBarItemWidget = NULL; michael@0: gMenuPopupWidget = NULL; michael@0: gMenuItemWidget = NULL; michael@0: gImageMenuItemWidget = NULL; michael@0: gCheckMenuItemWidget = NULL; michael@0: gTreeViewWidget = NULL; michael@0: gMiddleTreeViewColumn = NULL; michael@0: gTreeHeaderCellWidget = NULL; michael@0: gTreeHeaderSortArrowWidget = NULL; michael@0: gExpanderWidget = NULL; michael@0: gToolbarSeparatorWidget = NULL; michael@0: gMenuSeparatorWidget = NULL; michael@0: gHPanedWidget = NULL; michael@0: gVPanedWidget = NULL; michael@0: gScrolledWindowWidget = NULL; michael@0: michael@0: entry_class = g_type_class_peek(GTK_TYPE_ENTRY); michael@0: g_type_class_unref(entry_class); michael@0: michael@0: is_initialized = FALSE; michael@0: michael@0: return MOZ_GTK_SUCCESS; michael@0: }