Bug 1449132 [wpt PR 10194] - [css-grid] Fix resolution of percentage paddings and...
[gecko.git] / widget / gtk / gtk2drawing.c
blobe7f9d32a4d1cefdfcdea3255bf5061204f9c2ea5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * This file contains painting functions for each of the gtk2 widgets.
8 * Adapted from the gtkdrawing.c, and gtk+2.0 source.
9 */
11 #include <gtk/gtk.h>
12 #include <gdk/gdkprivate.h>
13 #include <string.h>
14 #include "gtkdrawing.h"
15 #include "mozilla/Assertions.h"
16 #include "prinrval.h"
18 #include <math.h>
19 #include <stdbool.h> // for MOZ_ASSERT_UNREACHABLE
21 #define XTHICKNESS(style) (style->xthickness)
22 #define YTHICKNESS(style) (style->ythickness)
23 #define WINDOW_IS_MAPPED(window) ((window) && GDK_IS_WINDOW(window) && gdk_window_is_visible(window))
25 static GtkWidget* gProtoWindow;
26 static GtkWidget* gProtoLayout;
27 static GtkWidget* gButtonWidget;
28 static GtkWidget* gToggleButtonWidget;
29 static GtkWidget* gButtonArrowWidget;
30 static GtkWidget* gCheckboxWidget;
31 static GtkWidget* gRadiobuttonWidget;
32 static GtkWidget* gHorizScrollbarWidget;
33 static GtkWidget* gVertScrollbarWidget;
34 static GtkWidget* gSpinWidget;
35 static GtkWidget* gHScaleWidget;
36 static GtkWidget* gVScaleWidget;
37 static GtkWidget* gEntryWidget;
38 static GtkWidget* gComboBoxWidget;
39 static GtkWidget* gComboBoxButtonWidget;
40 static GtkWidget* gComboBoxArrowWidget;
41 static GtkWidget* gComboBoxSeparatorWidget;
42 static GtkWidget* gComboBoxEntryWidget;
43 static GtkWidget* gComboBoxEntryTextareaWidget;
44 static GtkWidget* gComboBoxEntryButtonWidget;
45 static GtkWidget* gComboBoxEntryArrowWidget;
46 static GtkWidget* gHandleBoxWidget;
47 static GtkWidget* gToolbarWidget;
48 static GtkWidget* gFrameWidget;
49 static GtkWidget* gStatusbarWidget;
50 static GtkWidget* gProgressWidget;
51 static GtkWidget* gTabWidget;
52 static GtkWidget* gTooltipWidget;
53 static GtkWidget* gMenuBarWidget;
54 static GtkWidget* gMenuBarItemWidget;
55 static GtkWidget* gMenuPopupWidget;
56 static GtkWidget* gMenuItemWidget;
57 static GtkWidget* gCheckMenuItemWidget;
58 static GtkWidget* gTreeViewWidget;
59 static GtkTreeViewColumn* gMiddleTreeViewColumn;
60 static GtkWidget* gTreeHeaderCellWidget;
61 static GtkWidget* gTreeHeaderSortArrowWidget;
62 static GtkWidget* gExpanderWidget;
63 static GtkWidget* gToolbarSeparatorWidget;
64 static GtkWidget* gMenuSeparatorWidget;
65 static GtkWidget* gHPanedWidget;
66 static GtkWidget* gVPanedWidget;
67 static GtkWidget* gScrolledWindowWidget;
69 static gboolean have_arrow_scaling;
70 static gboolean is_initialized;
72 /* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine
73 that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific
74 things they may want to do. */
75 static void
76 moz_gtk_set_widget_name(GtkWidget* widget)
78 gtk_widget_set_name(widget, "MozillaGtkWidget");
81 static gint
82 ensure_window_widget()
84 if (!gProtoWindow) {
85 gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
86 gtk_widget_realize(gProtoWindow);
87 moz_gtk_set_widget_name(gProtoWindow);
89 return MOZ_GTK_SUCCESS;
92 static gint
93 setup_widget_prototype(GtkWidget* widget)
95 ensure_window_widget();
96 if (!gProtoLayout) {
97 gProtoLayout = gtk_fixed_new();
98 gtk_container_add(GTK_CONTAINER(gProtoWindow), gProtoLayout);
101 gtk_container_add(GTK_CONTAINER(gProtoLayout), widget);
102 gtk_widget_realize(widget);
103 g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
104 return MOZ_GTK_SUCCESS;
107 static gint
108 ensure_button_widget()
110 if (!gButtonWidget) {
111 gButtonWidget = gtk_button_new_with_label("M");
112 setup_widget_prototype(gButtonWidget);
114 return MOZ_GTK_SUCCESS;
117 static gint
118 ensure_hpaned_widget()
120 if (!gHPanedWidget) {
121 gHPanedWidget = gtk_hpaned_new();
122 setup_widget_prototype(gHPanedWidget);
124 return MOZ_GTK_SUCCESS;
127 static gint
128 ensure_vpaned_widget()
130 if (!gVPanedWidget) {
131 gVPanedWidget = gtk_vpaned_new();
132 setup_widget_prototype(gVPanedWidget);
134 return MOZ_GTK_SUCCESS;
137 static gint
138 ensure_toggle_button_widget()
140 if (!gToggleButtonWidget) {
141 gToggleButtonWidget = gtk_toggle_button_new();
142 setup_widget_prototype(gToggleButtonWidget);
143 /* toggle button must be set active to get the right style on hover. */
144 GTK_TOGGLE_BUTTON(gToggleButtonWidget)->active = TRUE;
146 return MOZ_GTK_SUCCESS;
149 static gint
150 ensure_button_arrow_widget()
152 if (!gButtonArrowWidget) {
153 ensure_toggle_button_widget();
155 gButtonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
156 gtk_container_add(GTK_CONTAINER(gToggleButtonWidget), gButtonArrowWidget);
157 gtk_widget_realize(gButtonArrowWidget);
159 return MOZ_GTK_SUCCESS;
162 static gint
163 ensure_checkbox_widget()
165 if (!gCheckboxWidget) {
166 gCheckboxWidget = gtk_check_button_new_with_label("M");
167 setup_widget_prototype(gCheckboxWidget);
169 return MOZ_GTK_SUCCESS;
172 static gint
173 ensure_radiobutton_widget()
175 if (!gRadiobuttonWidget) {
176 gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M");
177 setup_widget_prototype(gRadiobuttonWidget);
179 return MOZ_GTK_SUCCESS;
182 static gint
183 ensure_scrollbar_widget()
185 if (!gVertScrollbarWidget) {
186 gVertScrollbarWidget = gtk_vscrollbar_new(NULL);
187 setup_widget_prototype(gVertScrollbarWidget);
189 if (!gHorizScrollbarWidget) {
190 gHorizScrollbarWidget = gtk_hscrollbar_new(NULL);
191 setup_widget_prototype(gHorizScrollbarWidget);
193 return MOZ_GTK_SUCCESS;
196 static gint
197 ensure_spin_widget()
199 if (!gSpinWidget) {
200 gSpinWidget = gtk_spin_button_new(NULL, 1, 0);
201 setup_widget_prototype(gSpinWidget);
203 return MOZ_GTK_SUCCESS;
206 static gint
207 ensure_scale_widget()
209 if (!gHScaleWidget) {
210 gHScaleWidget = gtk_hscale_new(NULL);
211 setup_widget_prototype(gHScaleWidget);
213 if (!gVScaleWidget) {
214 gVScaleWidget = gtk_vscale_new(NULL);
215 setup_widget_prototype(gVScaleWidget);
217 return MOZ_GTK_SUCCESS;
220 static gint
221 ensure_entry_widget()
223 if (!gEntryWidget) {
224 gEntryWidget = gtk_entry_new();
225 setup_widget_prototype(gEntryWidget);
227 return MOZ_GTK_SUCCESS;
230 /* We need to have pointers to the inner widgets (button, separator, arrow)
231 * of the ComboBox to get the correct rendering from theme engines which
232 * special cases their look. Since the inner layout can change, we ask GTK
233 * to NULL our pointers when they are about to become invalid because the
234 * corresponding widgets don't exist anymore. It's the role of
235 * g_object_add_weak_pointer().
236 * Note that if we don't find the inner widgets (which shouldn't happen), we
237 * fallback to use generic "non-inner" widgets, and they don't need that kind
238 * of weak pointer since they are explicit children of gProtoWindow and as
239 * such GTK holds a strong reference to them. */
240 static void
241 moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data)
243 if (GTK_IS_TOGGLE_BUTTON(widget)) {
244 gComboBoxButtonWidget = widget;
245 g_object_add_weak_pointer(G_OBJECT(widget),
246 (gpointer) &gComboBoxButtonWidget);
247 gtk_widget_realize(widget);
248 g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
252 static void
253 moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget,
254 gpointer client_data)
256 if (GTK_IS_SEPARATOR(widget)) {
257 gComboBoxSeparatorWidget = widget;
258 g_object_add_weak_pointer(G_OBJECT(widget),
259 (gpointer) &gComboBoxSeparatorWidget);
260 } else if (GTK_IS_ARROW(widget)) {
261 gComboBoxArrowWidget = widget;
262 g_object_add_weak_pointer(G_OBJECT(widget),
263 (gpointer) &gComboBoxArrowWidget);
264 } else
265 return;
266 gtk_widget_realize(widget);
267 g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
270 static gint
271 ensure_combo_box_widgets()
273 GtkWidget* buttonChild;
275 if (gComboBoxButtonWidget && gComboBoxArrowWidget)
276 return MOZ_GTK_SUCCESS;
278 /* Create a ComboBox if needed */
279 if (!gComboBoxWidget) {
280 gComboBoxWidget = gtk_combo_box_new();
281 setup_widget_prototype(gComboBoxWidget);
284 /* Get its inner Button */
285 gtk_container_forall(GTK_CONTAINER(gComboBoxWidget),
286 moz_gtk_get_combo_box_inner_button,
287 NULL);
289 if (gComboBoxButtonWidget) {
290 /* Get the widgets inside the Button */
291 buttonChild = GTK_BIN(gComboBoxButtonWidget)->child;
292 if (GTK_IS_HBOX(buttonChild)) {
293 /* appears-as-list = FALSE, cell-view = TRUE; the button
294 * contains an hbox. This hbox is there because the ComboBox
295 * needs to place a cell renderer, a separator, and an arrow in
296 * the button when appears-as-list is FALSE. */
297 gtk_container_forall(GTK_CONTAINER(buttonChild),
298 moz_gtk_get_combo_box_button_inner_widgets,
299 NULL);
300 } else if(GTK_IS_ARROW(buttonChild)) {
301 /* appears-as-list = TRUE, or cell-view = FALSE;
302 * the button only contains an arrow */
303 gComboBoxArrowWidget = buttonChild;
304 g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
305 &gComboBoxArrowWidget);
306 gtk_widget_realize(gComboBoxArrowWidget);
307 g_object_set_data(G_OBJECT(gComboBoxArrowWidget),
308 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
310 } else {
311 /* Shouldn't be reached with current internal gtk implementation; we
312 * use a generic toggle button as last resort fallback to avoid
313 * crashing. */
314 ensure_toggle_button_widget();
315 gComboBoxButtonWidget = gToggleButtonWidget;
318 if (!gComboBoxArrowWidget) {
319 /* Shouldn't be reached with current internal gtk implementation;
320 * we gButtonArrowWidget as last resort fallback to avoid
321 * crashing. */
322 ensure_button_arrow_widget();
323 gComboBoxArrowWidget = gButtonArrowWidget;
326 /* We don't test the validity of gComboBoxSeparatorWidget since there
327 * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it
328 * is invalid we just won't paint it. */
330 return MOZ_GTK_SUCCESS;
333 /* We need to have pointers to the inner widgets (entry, button, arrow) of
334 * the ComboBoxEntry to get the correct rendering from theme engines which
335 * special cases their look. Since the inner layout can change, we ask GTK
336 * to NULL our pointers when they are about to become invalid because the
337 * corresponding widgets don't exist anymore. It's the role of
338 * g_object_add_weak_pointer().
339 * Note that if we don't find the inner widgets (which shouldn't happen), we
340 * fallback to use generic "non-inner" widgets, and they don't need that kind
341 * of weak pointer since they are explicit children of gProtoWindow and as
342 * such GTK holds a strong reference to them. */
343 static void
344 moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget,
345 gpointer client_data)
347 if (GTK_IS_TOGGLE_BUTTON(widget)) {
348 gComboBoxEntryButtonWidget = widget;
349 g_object_add_weak_pointer(G_OBJECT(widget),
350 (gpointer) &gComboBoxEntryButtonWidget);
351 } else if (GTK_IS_ENTRY(widget)) {
352 gComboBoxEntryTextareaWidget = widget;
353 g_object_add_weak_pointer(G_OBJECT(widget),
354 (gpointer) &gComboBoxEntryTextareaWidget);
355 } else
356 return;
357 gtk_widget_realize(widget);
358 g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
361 static void
362 moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data)
364 if (GTK_IS_ARROW(widget)) {
365 gComboBoxEntryArrowWidget = widget;
366 g_object_add_weak_pointer(G_OBJECT(widget),
367 (gpointer) &gComboBoxEntryArrowWidget);
368 gtk_widget_realize(widget);
369 g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
373 static gint
374 ensure_combo_box_entry_widgets()
376 GtkWidget* buttonChild;
378 if (gComboBoxEntryTextareaWidget &&
379 gComboBoxEntryButtonWidget &&
380 gComboBoxEntryArrowWidget)
381 return MOZ_GTK_SUCCESS;
383 /* Create a ComboBoxEntry if needed */
384 if (!gComboBoxEntryWidget) {
385 gComboBoxEntryWidget = gtk_combo_box_entry_new();
386 setup_widget_prototype(gComboBoxEntryWidget);
389 /* Get its inner Entry and Button */
390 gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget),
391 moz_gtk_get_combo_box_entry_inner_widgets,
392 NULL);
394 if (!gComboBoxEntryTextareaWidget) {
395 ensure_entry_widget();
396 gComboBoxEntryTextareaWidget = gEntryWidget;
399 if (gComboBoxEntryButtonWidget) {
400 /* Get the Arrow inside the Button */
401 buttonChild = GTK_BIN(gComboBoxEntryButtonWidget)->child;
402 if (GTK_IS_HBOX(buttonChild)) {
403 /* appears-as-list = FALSE, cell-view = TRUE; the button
404 * contains an hbox. This hbox is there because ComboBoxEntry
405 * inherits from ComboBox which needs to place a cell renderer,
406 * a separator, and an arrow in the button when appears-as-list
407 * is FALSE. Here the hbox should only contain an arrow, since
408 * a ComboBoxEntry doesn't need all those widgets in the
409 * button. */
410 gtk_container_forall(GTK_CONTAINER(buttonChild),
411 moz_gtk_get_combo_box_entry_arrow,
412 NULL);
413 } else if(GTK_IS_ARROW(buttonChild)) {
414 /* appears-as-list = TRUE, or cell-view = FALSE;
415 * the button only contains an arrow */
416 gComboBoxEntryArrowWidget = buttonChild;
417 g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
418 &gComboBoxEntryArrowWidget);
419 gtk_widget_realize(gComboBoxEntryArrowWidget);
420 g_object_set_data(G_OBJECT(gComboBoxEntryArrowWidget),
421 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
423 } else {
424 /* Shouldn't be reached with current internal gtk implementation;
425 * we use a generic toggle button as last resort fallback to avoid
426 * crashing. */
427 ensure_toggle_button_widget();
428 gComboBoxEntryButtonWidget = gToggleButtonWidget;
431 if (!gComboBoxEntryArrowWidget) {
432 /* Shouldn't be reached with current internal gtk implementation;
433 * we gButtonArrowWidget as last resort fallback to avoid
434 * crashing. */
435 ensure_button_arrow_widget();
436 gComboBoxEntryArrowWidget = gButtonArrowWidget;
439 return MOZ_GTK_SUCCESS;
443 static gint
444 ensure_handlebox_widget()
446 if (!gHandleBoxWidget) {
447 gHandleBoxWidget = gtk_handle_box_new();
448 setup_widget_prototype(gHandleBoxWidget);
450 return MOZ_GTK_SUCCESS;
453 static gint
454 ensure_toolbar_widget()
456 if (!gToolbarWidget) {
457 ensure_handlebox_widget();
458 gToolbarWidget = gtk_toolbar_new();
459 gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget);
460 gtk_widget_realize(gToolbarWidget);
461 g_object_set_data(G_OBJECT(gToolbarWidget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
463 return MOZ_GTK_SUCCESS;
466 static gint
467 ensure_toolbar_separator_widget()
469 if (!gToolbarSeparatorWidget) {
470 ensure_toolbar_widget();
471 gToolbarSeparatorWidget = GTK_WIDGET(gtk_separator_tool_item_new());
472 setup_widget_prototype(gToolbarSeparatorWidget);
474 return MOZ_GTK_SUCCESS;
477 static gint
478 ensure_tooltip_widget()
480 if (!gTooltipWidget) {
481 gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP);
482 gtk_widget_realize(gTooltipWidget);
483 moz_gtk_set_widget_name(gTooltipWidget);
485 return MOZ_GTK_SUCCESS;
488 static gint
489 ensure_tab_widget()
491 if (!gTabWidget) {
492 gTabWidget = gtk_notebook_new();
493 setup_widget_prototype(gTabWidget);
495 return MOZ_GTK_SUCCESS;
498 static gint
499 ensure_progress_widget()
501 if (!gProgressWidget) {
502 gProgressWidget = gtk_progress_bar_new();
503 setup_widget_prototype(gProgressWidget);
505 return MOZ_GTK_SUCCESS;
508 static gint
509 ensure_statusbar_widget()
511 if (!gStatusbarWidget) {
512 gStatusbarWidget = gtk_statusbar_new();
513 setup_widget_prototype(gStatusbarWidget);
515 return MOZ_GTK_SUCCESS;
518 static gint
519 ensure_frame_widget()
521 if (!gFrameWidget) {
522 ensure_statusbar_widget();
523 gFrameWidget = gtk_frame_new(NULL);
524 gtk_container_add(GTK_CONTAINER(gStatusbarWidget), gFrameWidget);
525 gtk_widget_realize(gFrameWidget);
527 return MOZ_GTK_SUCCESS;
530 static gint
531 ensure_menu_bar_widget()
533 if (!gMenuBarWidget) {
534 gMenuBarWidget = gtk_menu_bar_new();
535 setup_widget_prototype(gMenuBarWidget);
537 return MOZ_GTK_SUCCESS;
540 static gint
541 ensure_menu_bar_item_widget()
543 if (!gMenuBarItemWidget) {
544 ensure_menu_bar_widget();
545 gMenuBarItemWidget = gtk_menu_item_new();
546 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget),
547 gMenuBarItemWidget);
548 gtk_widget_realize(gMenuBarItemWidget);
549 g_object_set_data(G_OBJECT(gMenuBarItemWidget),
550 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
552 return MOZ_GTK_SUCCESS;
555 static gint
556 ensure_menu_popup_widget()
558 if (!gMenuPopupWidget) {
559 ensure_menu_bar_item_widget();
560 gMenuPopupWidget = gtk_menu_new();
561 gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget),
562 gMenuPopupWidget);
563 gtk_widget_realize(gMenuPopupWidget);
564 g_object_set_data(G_OBJECT(gMenuPopupWidget),
565 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
567 return MOZ_GTK_SUCCESS;
570 static gint
571 ensure_menu_item_widget()
573 if (!gMenuItemWidget) {
574 ensure_menu_popup_widget();
575 gMenuItemWidget = gtk_menu_item_new_with_label("M");
576 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
577 gMenuItemWidget);
578 gtk_widget_realize(gMenuItemWidget);
579 g_object_set_data(G_OBJECT(gMenuItemWidget),
580 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
582 return MOZ_GTK_SUCCESS;
585 static gint
586 ensure_menu_separator_widget()
588 if (!gMenuSeparatorWidget) {
589 ensure_menu_popup_widget();
590 gMenuSeparatorWidget = gtk_separator_menu_item_new();
591 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
592 gMenuSeparatorWidget);
593 gtk_widget_realize(gMenuSeparatorWidget);
594 g_object_set_data(G_OBJECT(gMenuSeparatorWidget),
595 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
597 return MOZ_GTK_SUCCESS;
600 static gint
601 ensure_check_menu_item_widget()
603 if (!gCheckMenuItemWidget) {
604 ensure_menu_popup_widget();
605 gCheckMenuItemWidget = gtk_check_menu_item_new_with_label("M");
606 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
607 gCheckMenuItemWidget);
608 gtk_widget_realize(gCheckMenuItemWidget);
609 g_object_set_data(G_OBJECT(gCheckMenuItemWidget),
610 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
612 return MOZ_GTK_SUCCESS;
615 static gint
616 ensure_tree_view_widget()
618 if (!gTreeViewWidget) {
619 gTreeViewWidget = gtk_tree_view_new();
620 setup_widget_prototype(gTreeViewWidget);
622 return MOZ_GTK_SUCCESS;
625 static gint
626 ensure_tree_header_cell_widget()
628 if(!gTreeHeaderCellWidget) {
630 * Some GTK engines paint the first and last cell
631 * of a TreeView header with a highlight.
632 * Since we do not know where our widget will be relative
633 * to the other buttons in the TreeView header, we must
634 * paint it as a button that is between two others,
635 * thus ensuring it is neither the first or last button
636 * in the header.
637 * GTK doesn't give us a way to do this explicitly,
638 * so we must paint with a button that is between two
639 * others.
642 GtkTreeViewColumn* firstTreeViewColumn;
643 GtkTreeViewColumn* lastTreeViewColumn;
645 ensure_tree_view_widget();
647 /* Create and append our three columns */
648 firstTreeViewColumn = gtk_tree_view_column_new();
649 gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
650 gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn);
652 gMiddleTreeViewColumn = gtk_tree_view_column_new();
653 gtk_tree_view_column_set_title(gMiddleTreeViewColumn, "M");
654 gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget),
655 gMiddleTreeViewColumn);
657 lastTreeViewColumn = gtk_tree_view_column_new();
658 gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
659 gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn);
661 /* Use the middle column's header for our button */
662 gTreeHeaderCellWidget = gMiddleTreeViewColumn->button;
663 gTreeHeaderSortArrowWidget = gMiddleTreeViewColumn->arrow;
664 g_object_set_data(G_OBJECT(gTreeHeaderCellWidget),
665 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
666 g_object_set_data(G_OBJECT(gTreeHeaderSortArrowWidget),
667 "transparent-bg-hint", GINT_TO_POINTER(TRUE));
669 return MOZ_GTK_SUCCESS;
672 static gint
673 ensure_expander_widget()
675 if (!gExpanderWidget) {
676 gExpanderWidget = gtk_expander_new("M");
677 setup_widget_prototype(gExpanderWidget);
679 return MOZ_GTK_SUCCESS;
682 static gint
683 ensure_scrolled_window_widget()
685 if (!gScrolledWindowWidget) {
686 gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL);
687 setup_widget_prototype(gScrolledWindowWidget);
689 return MOZ_GTK_SUCCESS;
692 static GtkStateType
693 ConvertGtkState(GtkWidgetState* state)
695 if (state->disabled)
696 return GTK_STATE_INSENSITIVE;
697 else if (state->depressed)
698 return (state->inHover ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
699 else if (state->inHover)
700 return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
701 else
702 return GTK_STATE_NORMAL;
705 static gint
706 TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin)
708 int i;
709 /* there are 5 gc's in each array, for each of the widget states */
710 for (i = 0; i < 5; ++i)
711 gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin);
712 return MOZ_GTK_SUCCESS;
715 static gint
716 TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin)
718 TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin);
719 TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin);
720 TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin);
721 TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin);
722 TSOffsetStyleGCArray(style->mid_gc, xorigin, yorigin);
723 TSOffsetStyleGCArray(style->text_gc, xorigin, yorigin);
724 TSOffsetStyleGCArray(style->base_gc, xorigin, yorigin);
725 gdk_gc_set_ts_origin(style->black_gc, xorigin, yorigin);
726 gdk_gc_set_ts_origin(style->white_gc, xorigin, yorigin);
727 return MOZ_GTK_SUCCESS;
730 gint
731 moz_gtk_init()
733 GtkWidgetClass *entry_class;
735 if (is_initialized)
736 return MOZ_GTK_SUCCESS;
738 is_initialized = TRUE;
739 have_arrow_scaling = (gtk_major_version > 2 ||
740 (gtk_major_version == 2 && gtk_minor_version >= 12));
742 /* Add style property to GtkEntry.
743 * Adding the style property to the normal GtkEntry class means that it
744 * will work without issues inside GtkComboBox and for Spinbuttons. */
745 entry_class = g_type_class_ref(GTK_TYPE_ENTRY);
746 gtk_widget_class_install_style_property(entry_class,
747 g_param_spec_boolean("honors-transparent-bg-hint",
748 "Transparent BG enabling flag",
749 "If TRUE, the theme is able to draw the GtkEntry on non-prefilled background.",
750 FALSE,
751 G_PARAM_READWRITE));
753 return MOZ_GTK_SUCCESS;
756 GdkColormap*
757 moz_gtk_widget_get_colormap()
759 /* Child widgets inherit the colormap from the GtkWindow. */
760 ensure_window_widget();
761 return gtk_widget_get_colormap(gProtoWindow);
764 gint
765 moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing)
767 ensure_checkbox_widget();
769 gtk_widget_style_get (gCheckboxWidget,
770 "indicator_size", indicator_size,
771 "indicator_spacing", indicator_spacing,
772 NULL);
774 return MOZ_GTK_SUCCESS;
777 gint
778 moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing)
780 ensure_radiobutton_widget();
782 gtk_widget_style_get (gRadiobuttonWidget,
783 "indicator_size", indicator_size,
784 "indicator_spacing", indicator_spacing,
785 NULL);
787 return MOZ_GTK_SUCCESS;
790 gint
791 moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width)
793 gboolean interior_focus;
794 gint focus_width = 0;
796 ensure_entry_widget();
797 gtk_widget_style_get(gEntryWidget,
798 "interior-focus", &interior_focus,
799 "focus-line-width", &focus_width,
800 NULL);
801 if (interior_focus) {
802 *focus_h_width = XTHICKNESS(gEntryWidget->style) + focus_width;
803 *focus_v_width = YTHICKNESS(gEntryWidget->style) + focus_width;
804 } else {
805 *focus_h_width = focus_width;
806 *focus_v_width = focus_width;
808 return MOZ_GTK_SUCCESS;
811 static gint
812 moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus,
813 gint* focus_width, gint* focus_pad)
815 gtk_widget_style_get (widget,
816 "interior-focus", interior_focus,
817 "focus-line-width", focus_width,
818 "focus-padding", focus_pad,
819 NULL);
821 return MOZ_GTK_SUCCESS;
824 gint
825 moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding)
827 ensure_menu_item_widget();
829 gtk_widget_style_get (gMenuItemWidget,
830 "horizontal-padding", horizontal_padding,
831 NULL);
833 return MOZ_GTK_SUCCESS;
836 gint
837 moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding)
839 ensure_check_menu_item_widget();
841 gtk_widget_style_get (gCheckMenuItemWidget,
842 "horizontal-padding", horizontal_padding,
843 NULL);
845 return MOZ_GTK_SUCCESS;
848 gint
849 moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left,
850 gint* border_bottom, gint* border_right)
852 GtkBorder* default_outside_border;
854 ensure_button_widget();
855 gtk_widget_style_get(gButtonWidget,
856 "default-outside-border", &default_outside_border,
857 NULL);
859 if (default_outside_border) {
860 *border_top = default_outside_border->top;
861 *border_left = default_outside_border->left;
862 *border_bottom = default_outside_border->bottom;
863 *border_right = default_outside_border->right;
864 gtk_border_free(default_outside_border);
865 } else {
866 *border_top = *border_left = *border_bottom = *border_right = 0;
868 return MOZ_GTK_SUCCESS;
871 static gint
872 moz_gtk_button_get_default_border(gint* border_top, gint* border_left,
873 gint* border_bottom, gint* border_right)
875 GtkBorder* default_border;
877 ensure_button_widget();
878 gtk_widget_style_get(gButtonWidget,
879 "default-border", &default_border,
880 NULL);
882 if (default_border) {
883 *border_top = default_border->top;
884 *border_left = default_border->left;
885 *border_bottom = default_border->bottom;
886 *border_right = default_border->right;
887 gtk_border_free(default_border);
888 } else {
889 /* see gtkbutton.c */
890 *border_top = *border_left = *border_bottom = *border_right = 1;
892 return MOZ_GTK_SUCCESS;
895 gint
896 moz_gtk_splitter_get_metrics(gint orientation, gint* size)
898 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
899 ensure_hpaned_widget();
900 gtk_widget_style_get(gHPanedWidget, "handle_size", size, NULL);
901 } else {
902 ensure_vpaned_widget();
903 gtk_widget_style_get(gVPanedWidget, "handle_size", size, NULL);
905 return MOZ_GTK_SUCCESS;
908 static gint
909 moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border)
911 static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
912 GtkBorder *tmp_border;
914 gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL);
916 if (tmp_border) {
917 *inner_border = *tmp_border;
918 gtk_border_free(tmp_border);
920 else
921 *inner_border = default_inner_border;
923 return MOZ_GTK_SUCCESS;
926 static gint
927 moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
928 GdkRectangle* cliprect, GtkWidgetState* state,
929 GtkReliefStyle relief, GtkWidget* widget,
930 GtkTextDirection direction)
932 GtkShadowType shadow_type;
933 GtkStyle* style = widget->style;
934 GtkStateType button_state = ConvertGtkState(state);
935 gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
937 gboolean interior_focus;
938 gint focus_width, focus_pad;
940 moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad);
942 if (WINDOW_IS_MAPPED(drawable)) {
943 gdk_window_set_back_pixmap(drawable, NULL, TRUE);
944 gdk_window_clear_area(drawable, cliprect->x, cliprect->y,
945 cliprect->width, cliprect->height);
948 gtk_widget_set_state(widget, button_state);
949 gtk_widget_set_direction(widget, direction);
951 if (state->isDefault)
952 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_DEFAULT);
954 GTK_BUTTON(widget)->relief = relief;
956 /* Some theme engines love to cause us pain in that gtk_paint_focus is a
957 no-op on buttons and button-like widgets. They only listen to this flag. */
958 if (state->focused && !state->disabled)
959 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
961 if (!interior_focus && state->focused) {
962 x += focus_width + focus_pad;
963 y += focus_width + focus_pad;
964 width -= 2 * (focus_width + focus_pad);
965 height -= 2 * (focus_width + focus_pad);
968 shadow_type = button_state == GTK_STATE_ACTIVE ||
969 state->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
971 if (state->isDefault && relief == GTK_RELIEF_NORMAL) {
972 /* handle default borders both outside and inside the button */
973 gint default_top, default_left, default_bottom, default_right;
974 moz_gtk_button_get_default_overflow(&default_top, &default_left,
975 &default_bottom, &default_right);
976 x -= default_left;
977 y -= default_top;
978 width += default_left + default_right;
979 height += default_top + default_bottom;
980 gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, cliprect,
981 widget, "buttondefault", x, y, width, height);
983 moz_gtk_button_get_default_border(&default_top, &default_left,
984 &default_bottom, &default_right);
985 x += default_left;
986 y += default_top;
987 width -= (default_left + default_right);
988 height -= (default_top + default_bottom);
991 if (relief != GTK_RELIEF_NONE || state->depressed ||
992 (button_state != GTK_STATE_NORMAL &&
993 button_state != GTK_STATE_INSENSITIVE)) {
994 TSOffsetStyleGCs(style, x, y);
995 /* the following line can trigger an assertion (Crux theme)
996 file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
997 assertion `GDK_IS_WINDOW (window)' failed */
998 gtk_paint_box(style, drawable, button_state, shadow_type, cliprect,
999 widget, "button", x, y, width, height);
1002 if (state->focused) {
1003 if (interior_focus) {
1004 x += widget->style->xthickness + focus_pad;
1005 y += widget->style->ythickness + focus_pad;
1006 width -= 2 * (widget->style->xthickness + focus_pad);
1007 height -= 2 * (widget->style->ythickness + focus_pad);
1008 } else {
1009 x -= focus_width + focus_pad;
1010 y -= focus_width + focus_pad;
1011 width += 2 * (focus_width + focus_pad);
1012 height += 2 * (focus_width + focus_pad);
1015 TSOffsetStyleGCs(style, x, y);
1016 gtk_paint_focus(style, drawable, button_state, cliprect,
1017 widget, "button", x, y, width, height);
1020 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_DEFAULT);
1021 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
1022 return MOZ_GTK_SUCCESS;
1025 static gint
1026 moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect,
1027 GdkRectangle* cliprect, GtkWidgetState* state,
1028 gboolean selected, gboolean inconsistent,
1029 gboolean isradio, GtkTextDirection direction)
1031 GtkStateType state_type = ConvertGtkState(state);
1032 GtkShadowType shadow_type = (selected)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
1033 gint indicator_size, indicator_spacing;
1034 gint x, y, width, height;
1035 gint focus_x, focus_y, focus_width, focus_height;
1036 GtkWidget *w;
1037 GtkStyle *style;
1039 if (isradio) {
1040 moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
1041 w = gRadiobuttonWidget;
1042 } else {
1043 moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
1044 w = gCheckboxWidget;
1047 // Clamp the rect and paint it center aligned in the rect.
1048 x = rect->x;
1049 y = rect->y;
1050 width = rect->width;
1051 height = rect->height;
1053 if (rect->width < rect->height) {
1054 y = rect->y + (rect->height - rect->width) / 2;
1055 height = rect->width;
1058 if (rect->height < rect->width) {
1059 x = rect->x + (rect->width - rect->height) / 2;
1060 width = rect->height;
1063 focus_x = x - indicator_spacing;
1064 focus_y = y - indicator_spacing;
1065 focus_width = width + 2 * indicator_spacing;
1066 focus_height = height + 2 * indicator_spacing;
1068 style = w->style;
1069 TSOffsetStyleGCs(style, x, y);
1071 gtk_widget_set_sensitive(w, !state->disabled);
1072 gtk_widget_set_direction(w, direction);
1073 GTK_TOGGLE_BUTTON(w)->active = selected;
1075 if (isradio) {
1076 gtk_paint_option(style, drawable, state_type, shadow_type, cliprect,
1077 gRadiobuttonWidget, "radiobutton", x, y,
1078 width, height);
1079 if (state->focused) {
1080 gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect,
1081 gRadiobuttonWidget, "radiobutton", focus_x, focus_y,
1082 focus_width, focus_height);
1085 else {
1087 * 'indeterminate' type on checkboxes. In GTK, the shadow type
1088 * must also be changed for the state to be drawn.
1090 if (inconsistent) {
1091 gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE);
1092 shadow_type = GTK_SHADOW_ETCHED_IN;
1093 } else {
1094 gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE);
1097 gtk_paint_check(style, drawable, state_type, shadow_type, cliprect,
1098 gCheckboxWidget, "checkbutton", x, y, width, height);
1099 if (state->focused) {
1100 gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect,
1101 gCheckboxWidget, "checkbutton", focus_x, focus_y,
1102 focus_width, focus_height);
1106 return MOZ_GTK_SUCCESS;
1109 static gint
1110 calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect,
1111 GdkRectangle* inner_rect,
1112 GtkTextDirection direction,
1113 gboolean ignore_focus)
1115 GtkBorder inner_border;
1116 gboolean interior_focus;
1117 gint focus_width, focus_pad;
1118 GtkStyle* style;
1120 style = button->style;
1122 /* This mirrors gtkbutton's child positioning */
1123 moz_gtk_button_get_inner_border(button, &inner_border);
1124 moz_gtk_widget_get_focus(button, &interior_focus,
1125 &focus_width, &focus_pad);
1127 if (ignore_focus)
1128 focus_width = focus_pad = 0;
1130 inner_rect->x = rect->x + XTHICKNESS(style) + focus_width + focus_pad;
1131 inner_rect->x += direction == GTK_TEXT_DIR_LTR ?
1132 inner_border.left : inner_border.right;
1133 inner_rect->y = rect->y + inner_border.top + YTHICKNESS(style) +
1134 focus_width + focus_pad;
1135 inner_rect->width = MAX(1, rect->width - inner_border.left -
1136 inner_border.right - (XTHICKNESS(style) + focus_pad + focus_width) * 2);
1137 inner_rect->height = MAX(1, rect->height - inner_border.top -
1138 inner_border.bottom - (YTHICKNESS(style) + focus_pad + focus_width) * 2);
1140 return MOZ_GTK_SUCCESS;
1143 static gint
1144 calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect,
1145 GdkRectangle* arrow_rect, GtkTextDirection direction)
1147 /* defined in gtkarrow.c */
1148 gfloat arrow_scaling = 0.7;
1149 gfloat xalign, xpad;
1150 gint extent;
1151 GtkMisc* misc = GTK_MISC(arrow);
1153 if (have_arrow_scaling)
1154 gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL);
1156 extent = MIN((rect->width - misc->xpad * 2),
1157 (rect->height - misc->ypad * 2)) * arrow_scaling;
1159 xalign = direction == GTK_TEXT_DIR_LTR ? misc->xalign : 1.0 - misc->xalign;
1160 xpad = misc->xpad + (rect->width - extent) * xalign;
1162 arrow_rect->x = direction == GTK_TEXT_DIR_LTR ?
1163 floor(rect->x + xpad) : ceil(rect->x + xpad);
1164 arrow_rect->y = floor(rect->y + misc->ypad +
1165 ((rect->height - extent) * misc->yalign));
1167 arrow_rect->width = arrow_rect->height = extent;
1169 return MOZ_GTK_SUCCESS;
1172 static gint
1173 moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
1174 GdkRectangle* cliprect, GtkWidgetState* state,
1175 GtkScrollbarButtonFlags flags,
1176 GtkTextDirection direction)
1178 GtkStateType state_type = ConvertGtkState(state);
1179 GtkShadowType shadow_type = (state->active) ?
1180 GTK_SHADOW_IN : GTK_SHADOW_OUT;
1181 GdkRectangle arrow_rect;
1182 GtkStyle* style;
1183 GtkWidget *scrollbar;
1184 GtkArrowType arrow_type;
1185 gint arrow_displacement_x, arrow_displacement_y;
1186 const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ?
1187 "vscrollbar" : "hscrollbar";
1189 ensure_scrollbar_widget();
1191 if (flags & MOZ_GTK_STEPPER_VERTICAL)
1192 scrollbar = gVertScrollbarWidget;
1193 else
1194 scrollbar = gHorizScrollbarWidget;
1196 gtk_widget_set_direction(scrollbar, direction);
1198 /* Some theme engines (i.e., ClearLooks) check the scrollbar's allocation
1199 to determine where it should paint rounded corners on the buttons.
1200 We need to trick them into drawing the buttons the way we want them. */
1202 scrollbar->allocation.x = rect->x;
1203 scrollbar->allocation.y = rect->y;
1204 scrollbar->allocation.width = rect->width;
1205 scrollbar->allocation.height = rect->height;
1207 if (flags & MOZ_GTK_STEPPER_VERTICAL) {
1208 scrollbar->allocation.height *= 5;
1209 if (flags & MOZ_GTK_STEPPER_DOWN) {
1210 arrow_type = GTK_ARROW_DOWN;
1211 if (flags & MOZ_GTK_STEPPER_BOTTOM)
1212 scrollbar->allocation.y -= 4 * rect->height;
1213 else
1214 scrollbar->allocation.y -= rect->height;
1216 } else {
1217 arrow_type = GTK_ARROW_UP;
1218 if (flags & MOZ_GTK_STEPPER_BOTTOM)
1219 scrollbar->allocation.y -= 3 * rect->height;
1221 } else {
1222 scrollbar->allocation.width *= 5;
1223 if (flags & MOZ_GTK_STEPPER_DOWN) {
1224 arrow_type = GTK_ARROW_RIGHT;
1225 if (flags & MOZ_GTK_STEPPER_BOTTOM)
1226 scrollbar->allocation.x -= 4 * rect->width;
1227 else
1228 scrollbar->allocation.x -= rect->width;
1229 } else {
1230 arrow_type = GTK_ARROW_LEFT;
1231 if (flags & MOZ_GTK_STEPPER_BOTTOM)
1232 scrollbar->allocation.x -= 3 * rect->width;
1236 style = scrollbar->style;
1238 TSOffsetStyleGCs(style, rect->x, rect->y);
1240 gtk_paint_box(style, drawable, state_type, shadow_type, cliprect,
1241 scrollbar, detail, rect->x, rect->y,
1242 rect->width, rect->height);
1244 arrow_rect.width = rect->width / 2;
1245 arrow_rect.height = rect->height / 2;
1246 arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1247 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1249 if (state_type == GTK_STATE_ACTIVE) {
1250 gtk_widget_style_get(scrollbar,
1251 "arrow-displacement-x", &arrow_displacement_x,
1252 "arrow-displacement-y", &arrow_displacement_y,
1253 NULL);
1255 arrow_rect.x += arrow_displacement_x;
1256 arrow_rect.y += arrow_displacement_y;
1259 gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
1260 scrollbar, detail, arrow_type, TRUE, arrow_rect.x,
1261 arrow_rect.y, arrow_rect.width, arrow_rect.height);
1263 return MOZ_GTK_SUCCESS;
1266 static gint
1267 moz_gtk_scrollbar_trough_paint(WidgetNodeType widget,
1268 GdkDrawable* drawable, GdkRectangle* rect,
1269 GdkRectangle* cliprect, GtkWidgetState* state,
1270 GtkTextDirection direction)
1272 GtkStyle* style;
1273 GtkScrollbar *scrollbar;
1275 ensure_scrollbar_widget();
1277 if (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL)
1278 scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
1279 else
1280 scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
1282 gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
1284 style = GTK_WIDGET(scrollbar)->style;
1286 TSOffsetStyleGCs(style, rect->x, rect->y);
1287 gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_ACTIVE,
1288 cliprect, rect->x, rect->y,
1289 rect->width, rect->height);
1291 gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect,
1292 GTK_WIDGET(scrollbar), "trough", rect->x, rect->y,
1293 rect->width, rect->height);
1295 if (state->focused) {
1296 gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect,
1297 GTK_WIDGET(scrollbar), "trough",
1298 rect->x, rect->y, rect->width, rect->height);
1301 return MOZ_GTK_SUCCESS;
1304 static gint
1305 moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget,
1306 GdkDrawable* drawable, GdkRectangle* rect,
1307 GdkRectangle* cliprect, GtkWidgetState* state,
1308 GtkTextDirection direction)
1310 GtkStateType state_type = (state->inHover || state->active) ?
1311 GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
1312 GtkShadowType shadow_type = GTK_SHADOW_OUT;
1313 GtkStyle* style;
1314 GtkScrollbar *scrollbar;
1315 GtkAdjustment *adj;
1316 gboolean activate_slider;
1318 ensure_scrollbar_widget();
1320 if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL)
1321 scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
1322 else
1323 scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
1325 gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
1327 /* Make sure to set the scrollbar range before painting so that
1328 everything is drawn properly. At least the bluecurve (and
1329 maybe other) themes don't draw the top or bottom black line
1330 surrounding the scrollbar if the theme thinks that it's butted
1331 up against the scrollbar arrows. Note the increases of the
1332 clip rect below. */
1333 adj = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
1335 if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) {
1336 adj->page_size = rect->width;
1338 else {
1339 adj->page_size = rect->height;
1342 adj->lower = 0;
1343 adj->value = state->curpos;
1344 adj->upper = state->maxpos;
1345 gtk_adjustment_changed(adj);
1347 style = GTK_WIDGET(scrollbar)->style;
1349 gtk_widget_style_get(GTK_WIDGET(scrollbar), "activate-slider",
1350 &activate_slider, NULL);
1352 if (activate_slider && state->active) {
1353 shadow_type = GTK_SHADOW_IN;
1354 state_type = GTK_STATE_ACTIVE;
1357 TSOffsetStyleGCs(style, rect->x, rect->y);
1359 gtk_paint_slider(style, drawable, state_type, shadow_type, cliprect,
1360 GTK_WIDGET(scrollbar), "slider", rect->x, rect->y,
1361 rect->width, rect->height,
1362 (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
1363 GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1365 return MOZ_GTK_SUCCESS;
1368 static gint
1369 moz_gtk_inner_spin_paint(GdkDrawable* drawable, GdkRectangle* rect,
1370 GtkWidgetState* state,
1371 GtkTextDirection direction)
1373 GdkRectangle arrow_rect;
1374 GtkStateType state_type = ConvertGtkState(state);
1375 GtkShadowType shadow_type = state_type == GTK_STATE_ACTIVE ?
1376 GTK_SHADOW_IN : GTK_SHADOW_OUT;
1377 GtkStyle* style;
1379 ensure_spin_widget();
1380 style = gSpinWidget->style;
1381 gtk_widget_set_direction(gSpinWidget, direction);
1383 TSOffsetStyleGCs(style, rect->x, rect->y);
1384 gtk_paint_box(style, drawable, state_type, shadow_type, NULL, gSpinWidget,
1385 "spinbutton", rect->x, rect->y, rect->width, rect->height);
1387 /* hard code these values */
1388 arrow_rect.width = 6;
1389 arrow_rect.height = 6;
1391 // align spin to the left
1392 arrow_rect.x = rect->x;
1394 // up button
1395 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2 - 3;
1396 gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL,
1397 gSpinWidget, "spinbutton",
1398 GTK_ARROW_UP, TRUE,
1399 arrow_rect.x, arrow_rect.y,
1400 arrow_rect.width, arrow_rect.height);
1402 // down button
1403 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2 + 3;
1404 gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL,
1405 gSpinWidget, "spinbutton",
1406 GTK_ARROW_DOWN,
1407 arrow_rect.x, arrow_rect.y,
1408 arrow_rect.width, arrow_rect.height);
1410 return MOZ_GTK_SUCCESS;
1413 static gint
1414 moz_gtk_spin_paint(GdkDrawable* drawable, GdkRectangle* rect,
1415 GtkTextDirection direction)
1417 GtkStyle* style;
1419 ensure_spin_widget();
1420 gtk_widget_set_direction(gSpinWidget, direction);
1421 style = gSpinWidget->style;
1423 TSOffsetStyleGCs(style, rect->x, rect->y);
1424 gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL,
1425 gSpinWidget, "spinbutton",
1426 rect->x, rect->y, rect->width, rect->height);
1427 return MOZ_GTK_SUCCESS;
1430 static gint
1431 moz_gtk_spin_updown_paint(GdkDrawable* drawable, GdkRectangle* rect,
1432 gboolean isDown, GtkWidgetState* state,
1433 GtkTextDirection direction)
1435 GdkRectangle arrow_rect;
1436 GtkStateType state_type = ConvertGtkState(state);
1437 GtkShadowType shadow_type = state_type == GTK_STATE_ACTIVE ?
1438 GTK_SHADOW_IN : GTK_SHADOW_OUT;
1439 GtkStyle* style;
1441 ensure_spin_widget();
1442 style = gSpinWidget->style;
1443 gtk_widget_set_direction(gSpinWidget, direction);
1445 TSOffsetStyleGCs(style, rect->x, rect->y);
1446 gtk_paint_box(style, drawable, state_type, shadow_type, NULL, gSpinWidget,
1447 isDown ? "spinbutton_down" : "spinbutton_up",
1448 rect->x, rect->y, rect->width, rect->height);
1450 /* hard code these values */
1451 arrow_rect.width = 6;
1452 arrow_rect.height = 6;
1453 arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1454 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1455 arrow_rect.y += isDown ? -1 : 1;
1457 gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL,
1458 gSpinWidget, "spinbutton",
1459 isDown ? GTK_ARROW_DOWN : GTK_ARROW_UP, TRUE,
1460 arrow_rect.x, arrow_rect.y,
1461 arrow_rect.width, arrow_rect.height);
1463 return MOZ_GTK_SUCCESS;
1466 static gint
1467 moz_gtk_scale_paint(GdkDrawable* drawable, GdkRectangle* rect,
1468 GdkRectangle* cliprect, GtkWidgetState* state,
1469 GtkOrientation flags, GtkTextDirection direction)
1471 gint x = 0, y = 0;
1472 GtkStateType state_type = ConvertGtkState(state);
1473 GtkStyle* style;
1474 GtkWidget* widget;
1476 ensure_scale_widget();
1477 widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
1478 gtk_widget_set_direction(widget, direction);
1480 style = widget->style;
1482 if (flags == GTK_ORIENTATION_HORIZONTAL) {
1483 x = XTHICKNESS(style);
1484 y++;
1486 else {
1487 x++;
1488 y = YTHICKNESS(style);
1491 TSOffsetStyleGCs(style, rect->x, rect->y);
1493 gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect,
1494 widget, "trough", rect->x + x, rect->y + y,
1495 rect->width - 2*x, rect->height - 2*y);
1497 if (state->focused)
1498 gtk_paint_focus(style, drawable, state_type, cliprect, widget, "trough",
1499 rect->x, rect->y, rect->width, rect->height);
1501 return MOZ_GTK_SUCCESS;
1504 static gint
1505 moz_gtk_scale_thumb_paint(GdkDrawable* drawable, GdkRectangle* rect,
1506 GdkRectangle* cliprect, GtkWidgetState* state,
1507 GtkOrientation flags, GtkTextDirection direction)
1509 GtkStateType state_type = ConvertGtkState(state);
1510 GtkStyle* style;
1511 GtkWidget* widget;
1512 gint thumb_width, thumb_height, x, y;
1514 ensure_scale_widget();
1515 widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
1516 gtk_widget_set_direction(widget, direction);
1518 style = widget->style;
1520 /* determine the thumb size, and position the thumb in the center in the opposite axis */
1521 if (flags == GTK_ORIENTATION_HORIZONTAL) {
1522 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_width, &thumb_height);
1523 x = rect->x;
1524 y = rect->y + (rect->height - thumb_height) / 2;
1526 else {
1527 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_width);
1528 x = rect->x + (rect->width - thumb_width) / 2;
1529 y = rect->y;
1532 TSOffsetStyleGCs(style, rect->x, rect->y);
1533 gtk_paint_slider(style, drawable, state_type, GTK_SHADOW_OUT, cliprect,
1534 widget, (flags == GTK_ORIENTATION_HORIZONTAL) ? "hscale" : "vscale",
1535 x, y, thumb_width, thumb_height, flags);
1537 return MOZ_GTK_SUCCESS;
1540 static gint
1541 moz_gtk_gripper_paint(GdkDrawable* drawable, GdkRectangle* rect,
1542 GdkRectangle* cliprect, GtkWidgetState* state,
1543 GtkTextDirection direction)
1545 GtkStateType state_type = ConvertGtkState(state);
1546 GtkShadowType shadow_type;
1547 GtkStyle* style;
1549 ensure_handlebox_widget();
1550 gtk_widget_set_direction(gHandleBoxWidget, direction);
1552 style = gHandleBoxWidget->style;
1553 shadow_type = GTK_HANDLE_BOX(gHandleBoxWidget)->shadow_type;
1555 TSOffsetStyleGCs(style, rect->x, rect->y);
1556 gtk_paint_box(style, drawable, state_type, shadow_type, cliprect,
1557 gHandleBoxWidget, "handlebox_bin", rect->x, rect->y,
1558 rect->width, rect->height);
1560 return MOZ_GTK_SUCCESS;
1563 static gint
1564 moz_gtk_hpaned_paint(GdkDrawable* drawable, GdkRectangle* rect,
1565 GdkRectangle* cliprect, GtkWidgetState* state)
1567 GtkStateType hpaned_state = ConvertGtkState(state);
1569 ensure_hpaned_widget();
1570 gtk_paint_handle(gHPanedWidget->style, drawable, hpaned_state,
1571 GTK_SHADOW_NONE, cliprect, gHPanedWidget, "paned",
1572 rect->x, rect->y, rect->width, rect->height,
1573 GTK_ORIENTATION_VERTICAL);
1575 return MOZ_GTK_SUCCESS;
1578 static gint
1579 moz_gtk_vpaned_paint(GdkDrawable* drawable, GdkRectangle* rect,
1580 GdkRectangle* cliprect, GtkWidgetState* state)
1582 GtkStateType vpaned_state = ConvertGtkState(state);
1584 ensure_vpaned_widget();
1585 gtk_paint_handle(gVPanedWidget->style, drawable, vpaned_state,
1586 GTK_SHADOW_NONE, cliprect, gVPanedWidget, "paned",
1587 rect->x, rect->y, rect->width, rect->height,
1588 GTK_ORIENTATION_HORIZONTAL);
1590 return MOZ_GTK_SUCCESS;
1593 static gint
1594 moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect,
1595 GdkRectangle* cliprect, GtkWidgetState* state,
1596 GtkWidget* widget, GtkTextDirection direction)
1598 GtkStateType bg_state = state->disabled ?
1599 GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
1600 gint x, y, width = rect->width, height = rect->height;
1601 GtkStyle* style;
1602 gboolean interior_focus;
1603 gboolean theme_honors_transparency = FALSE;
1604 gint focus_width;
1605 int draw_focus_outline_only = state->depressed; // NS_THEME_FOCUS_OUTLINE
1607 gtk_widget_set_direction(widget, direction);
1609 style = widget->style;
1611 gtk_widget_style_get(widget,
1612 "interior-focus", &interior_focus,
1613 "focus-line-width", &focus_width,
1614 "honors-transparent-bg-hint", &theme_honors_transparency,
1615 NULL);
1617 if (draw_focus_outline_only) {
1618 // Inflate the given 'rect' with the focus outline size.
1619 gint h, v;
1620 moz_gtk_get_focus_outline_size(&h, &v);
1621 rect->x -= h;
1622 rect->width += 2 * h;
1623 rect->y -= v;
1624 rect->height += 2 * v;
1625 width = rect->width;
1626 height = rect->height;
1629 /* gtkentry.c uses two windows, one for the entire widget and one for the
1630 * text area inside it. The background of both windows is set to the "base"
1631 * color of the new state in gtk_entry_state_changed, but only the inner
1632 * textarea window uses gtk_paint_flat_box when exposed */
1634 TSOffsetStyleGCs(style, rect->x, rect->y);
1636 /* This gets us a lovely greyish disabledish look */
1637 gtk_widget_set_sensitive(widget, !state->disabled);
1639 /* GTK fills the outer widget window with the base color before drawing the widget.
1640 * Some older themes rely on this behavior, but many themes nowadays use rounded
1641 * corners on their widgets. While most GTK apps are blissfully unaware of this
1642 * problem due to their use of the default window background, we render widgets on
1643 * many kinds of backgrounds on the web.
1644 * If the theme is able to cope with transparency, then we can skip pre-filling
1645 * and notify the theme it will paint directly on the canvas. */
1646 if (theme_honors_transparency) {
1647 g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
1648 } else {
1649 GdkRectangle clipped_rect;
1650 gdk_rectangle_intersect(rect, cliprect, &clipped_rect);
1651 if (clipped_rect.width != 0) {
1652 gdk_draw_rectangle(drawable, style->base_gc[bg_state], TRUE,
1653 clipped_rect.x, clipped_rect.y,
1654 clipped_rect.width, clipped_rect.height);
1656 g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(FALSE));
1659 if (!draw_focus_outline_only) {
1660 /* Get the position of the inner window, see _gtk_entry_get_borders */
1661 x = XTHICKNESS(style);
1662 y = YTHICKNESS(style);
1664 if (!interior_focus) {
1665 x += focus_width;
1666 y += focus_width;
1669 /* Simulate an expose of the inner window */
1670 gtk_paint_flat_box(style, drawable, bg_state, GTK_SHADOW_NONE,
1671 cliprect, widget, "entry_bg", rect->x + x,
1672 rect->y + y, rect->width - 2*x, rect->height - 2*y);
1675 /* Now paint the shadow and focus border.
1676 * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad
1677 * smaller when focused if the focus is not interior, then the focus. */
1678 x = rect->x;
1679 y = rect->y;
1681 if (state->focused && !state->disabled) {
1682 /* This will get us the lit borders that focused textboxes enjoy on
1683 * some themes. */
1684 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
1686 if (!interior_focus) {
1687 /* Indent the border a little bit if we have exterior focus
1688 (this is what GTK does to draw native entries) */
1689 x += focus_width;
1690 y += focus_width;
1691 width -= 2 * focus_width;
1692 height -= 2 * focus_width;
1696 if (!draw_focus_outline_only || interior_focus) {
1697 gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
1698 cliprect, widget, "entry", x, y, width, height);
1701 if (state->focused && !state->disabled) {
1702 if (!interior_focus) {
1703 gtk_paint_focus(style, drawable, GTK_STATE_NORMAL, cliprect,
1704 widget, "entry",
1705 rect->x, rect->y, rect->width, rect->height);
1708 /* Now unset the focus flag. We don't want other entries to look
1709 * like they're focused too! */
1710 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
1713 return MOZ_GTK_SUCCESS;
1716 static gint
1717 moz_gtk_treeview_paint(GdkDrawable* drawable, GdkRectangle* rect,
1718 GdkRectangle* cliprect, GtkWidgetState* state,
1719 GtkTextDirection direction)
1721 gint xthickness, ythickness;
1723 GtkStyle *style;
1724 GtkStateType state_type;
1726 ensure_tree_view_widget();
1727 ensure_scrolled_window_widget();
1729 gtk_widget_set_direction(gTreeViewWidget, direction);
1730 gtk_widget_set_direction(gScrolledWindowWidget, direction);
1732 /* only handle disabled and normal states, otherwise the whole background
1733 * area will be painted differently with other states */
1734 state_type = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
1736 /* In GTK the treeview sets the background of the window
1737 * which contains the cells to the treeview base color.
1738 * If we don't set it here the background color will not be correct.*/
1739 gtk_widget_modify_bg(gTreeViewWidget, state_type,
1740 &gTreeViewWidget->style->base[state_type]);
1742 style = gScrolledWindowWidget->style;
1743 xthickness = XTHICKNESS(style);
1744 ythickness = YTHICKNESS(style);
1746 TSOffsetStyleGCs(gTreeViewWidget->style, rect->x, rect->y);
1747 TSOffsetStyleGCs(style, rect->x, rect->y);
1749 gtk_paint_flat_box(gTreeViewWidget->style, drawable, state_type,
1750 GTK_SHADOW_NONE, cliprect, gTreeViewWidget, "treeview",
1751 rect->x + xthickness, rect->y + ythickness,
1752 rect->width - 2 * xthickness,
1753 rect->height - 2 * ythickness);
1755 gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
1756 cliprect, gScrolledWindowWidget, "scrolled_window",
1757 rect->x, rect->y, rect->width, rect->height);
1759 return MOZ_GTK_SUCCESS;
1762 static gint
1763 moz_gtk_tree_header_cell_paint(GdkDrawable* drawable, GdkRectangle* rect,
1764 GdkRectangle* cliprect, GtkWidgetState* state,
1765 gboolean isSorted, GtkTextDirection direction)
1767 gtk_tree_view_column_set_sort_indicator(gMiddleTreeViewColumn,
1768 isSorted);
1770 moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
1771 gTreeHeaderCellWidget, direction);
1772 return MOZ_GTK_SUCCESS;
1775 static gint
1776 moz_gtk_tree_header_sort_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
1777 GdkRectangle* cliprect,
1778 GtkWidgetState* state, GtkArrowType flags,
1779 GtkTextDirection direction)
1781 GdkRectangle arrow_rect;
1782 GtkStateType state_type = ConvertGtkState(state);
1783 GtkShadowType shadow_type = GTK_SHADOW_IN;
1784 GtkArrowType arrow_type = flags;
1785 GtkStyle* style;
1787 ensure_tree_header_cell_widget();
1788 gtk_widget_set_direction(gTreeHeaderSortArrowWidget, direction);
1790 /* hard code these values */
1791 arrow_rect.width = 11;
1792 arrow_rect.height = 11;
1793 arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1794 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1796 style = gTreeHeaderSortArrowWidget->style;
1797 TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y);
1799 gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
1800 gTreeHeaderSortArrowWidget, "arrow", arrow_type, TRUE,
1801 arrow_rect.x, arrow_rect.y,
1802 arrow_rect.width, arrow_rect.height);
1804 return MOZ_GTK_SUCCESS;
1807 static gint
1808 moz_gtk_treeview_expander_paint(GdkDrawable* drawable, GdkRectangle* rect,
1809 GdkRectangle* cliprect, GtkWidgetState* state,
1810 GtkExpanderStyle expander_state,
1811 GtkTextDirection direction)
1813 GtkStyle *style;
1814 GtkStateType state_type;
1816 ensure_tree_view_widget();
1817 gtk_widget_set_direction(gTreeViewWidget, direction);
1819 style = gTreeViewWidget->style;
1821 /* Because the frame we get is of the entire treeview, we can't get the precise
1822 * event state of one expander, thus rendering hover and active feedback useless. */
1823 state_type = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
1825 TSOffsetStyleGCs(style, rect->x, rect->y);
1826 gtk_paint_expander(style, drawable, state_type, cliprect, gTreeViewWidget, "treeview",
1827 rect->x + rect->width / 2, rect->y + rect->height / 2, expander_state);
1829 return MOZ_GTK_SUCCESS;
1832 static gint
1833 moz_gtk_combo_box_paint(GdkDrawable* drawable, GdkRectangle* rect,
1834 GdkRectangle* cliprect, GtkWidgetState* state,
1835 gboolean ishtml, GtkTextDirection direction)
1837 GdkRectangle arrow_rect, real_arrow_rect;
1838 gint separator_width;
1839 gboolean wide_separators;
1840 GtkStateType state_type = ConvertGtkState(state);
1841 GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
1842 GtkStyle* style;
1843 GtkRequisition arrow_req;
1845 ensure_combo_box_widgets();
1847 /* Also sets the direction on gComboBoxButtonWidget, which is then
1848 * inherited by the separator and arrow */
1849 moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
1850 gComboBoxButtonWidget, direction);
1852 calculate_button_inner_rect(gComboBoxButtonWidget,
1853 rect, &arrow_rect, direction, ishtml);
1854 /* Now arrow_rect contains the inner rect ; we want to correct the width
1855 * to what the arrow needs (see gtk_combo_box_size_allocate) */
1856 gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req);
1857 if (direction == GTK_TEXT_DIR_LTR)
1858 arrow_rect.x += arrow_rect.width - arrow_req.width;
1859 arrow_rect.width = arrow_req.width;
1861 calculate_arrow_rect(gComboBoxArrowWidget,
1862 &arrow_rect, &real_arrow_rect, direction);
1864 style = gComboBoxArrowWidget->style;
1865 TSOffsetStyleGCs(style, rect->x, rect->y);
1867 gtk_widget_size_allocate(gComboBoxWidget, rect);
1869 gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
1870 gComboBoxArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE,
1871 real_arrow_rect.x, real_arrow_rect.y,
1872 real_arrow_rect.width, real_arrow_rect.height);
1875 /* If there is no separator in the theme, there's nothing left to do. */
1876 if (!gComboBoxSeparatorWidget)
1877 return MOZ_GTK_SUCCESS;
1879 style = gComboBoxSeparatorWidget->style;
1880 TSOffsetStyleGCs(style, rect->x, rect->y);
1882 gtk_widget_style_get(gComboBoxSeparatorWidget,
1883 "wide-separators", &wide_separators,
1884 "separator-width", &separator_width,
1885 NULL);
1887 if (wide_separators) {
1888 if (direction == GTK_TEXT_DIR_LTR)
1889 arrow_rect.x -= separator_width;
1890 else
1891 arrow_rect.x += arrow_rect.width;
1893 gtk_paint_box(style, drawable,
1894 GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
1895 cliprect, gComboBoxSeparatorWidget, "vseparator",
1896 arrow_rect.x, arrow_rect.y,
1897 separator_width, arrow_rect.height);
1898 } else {
1899 if (direction == GTK_TEXT_DIR_LTR)
1900 arrow_rect.x -= XTHICKNESS(style);
1901 else
1902 arrow_rect.x += arrow_rect.width;
1904 gtk_paint_vline(style, drawable, GTK_STATE_NORMAL, cliprect,
1905 gComboBoxSeparatorWidget, "vseparator",
1906 arrow_rect.y, arrow_rect.y + arrow_rect.height,
1907 arrow_rect.x);
1910 return MOZ_GTK_SUCCESS;
1913 static gint
1914 moz_gtk_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
1915 GdkRectangle* cliprect, GtkWidgetState* state,
1916 GtkArrowType arrow_type, GtkTextDirection direction)
1918 GtkStyle* style;
1919 GtkStateType state_type = ConvertGtkState(state);
1920 GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
1921 GdkRectangle arrow_rect;
1923 ensure_button_arrow_widget();
1924 style = gButtonArrowWidget->style;
1925 gtk_widget_set_direction(gButtonArrowWidget, direction);
1927 calculate_arrow_rect(gButtonArrowWidget, rect, &arrow_rect,
1928 direction);
1930 if (direction == GTK_TEXT_DIR_RTL) {
1931 if (arrow_type == GTK_ARROW_LEFT)
1932 arrow_type = GTK_ARROW_RIGHT;
1933 else if (arrow_type == GTK_ARROW_RIGHT)
1934 arrow_type = GTK_ARROW_LEFT;
1937 TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y);
1938 gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
1939 gButtonArrowWidget, "arrow", arrow_type, TRUE,
1940 arrow_rect.x, arrow_rect.y, arrow_rect.width, arrow_rect.height);
1942 return MOZ_GTK_SUCCESS;
1945 static gint
1946 moz_gtk_combo_box_entry_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
1947 GdkRectangle* cliprect,
1948 GtkWidgetState* state,
1949 gboolean input_focus,
1950 GtkTextDirection direction)
1952 gint x_displacement, y_displacement;
1953 GdkRectangle arrow_rect, real_arrow_rect;
1954 GtkStateType state_type = ConvertGtkState(state);
1955 GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
1956 GtkStyle* style;
1958 ensure_combo_box_entry_widgets();
1960 if (input_focus) {
1961 /* Some themes draw a complementary focus ring for the dropdown button
1962 * when the dropdown entry has focus */
1963 GTK_WIDGET_SET_FLAGS(gComboBoxEntryTextareaWidget, GTK_HAS_FOCUS);
1966 moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
1967 gComboBoxEntryButtonWidget, direction);
1969 if (input_focus)
1970 GTK_WIDGET_UNSET_FLAGS(gComboBoxEntryTextareaWidget, GTK_HAS_FOCUS);
1972 calculate_button_inner_rect(gComboBoxEntryButtonWidget,
1973 rect, &arrow_rect, direction, FALSE);
1974 if (state_type == GTK_STATE_ACTIVE) {
1975 gtk_widget_style_get(gComboBoxEntryButtonWidget,
1976 "child-displacement-x", &x_displacement,
1977 "child-displacement-y", &y_displacement,
1978 NULL);
1979 arrow_rect.x += x_displacement;
1980 arrow_rect.y += y_displacement;
1983 calculate_arrow_rect(gComboBoxEntryArrowWidget,
1984 &arrow_rect, &real_arrow_rect, direction);
1986 style = gComboBoxEntryArrowWidget->style;
1987 TSOffsetStyleGCs(style, real_arrow_rect.x, real_arrow_rect.y);
1989 gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
1990 gComboBoxEntryArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE,
1991 real_arrow_rect.x, real_arrow_rect.y,
1992 real_arrow_rect.width, real_arrow_rect.height);
1994 return MOZ_GTK_SUCCESS;
1997 static gint
1998 moz_gtk_container_paint(GdkDrawable* drawable, GdkRectangle* rect,
1999 GdkRectangle* cliprect, GtkWidgetState* state,
2000 gboolean isradio, GtkTextDirection direction)
2002 GtkStateType state_type = ConvertGtkState(state);
2003 GtkStyle* style;
2004 GtkWidget *widget;
2005 gboolean interior_focus;
2006 gint focus_width, focus_pad;
2008 if (isradio) {
2009 ensure_radiobutton_widget();
2010 widget = gRadiobuttonWidget;
2011 } else {
2012 ensure_checkbox_widget();
2013 widget = gCheckboxWidget;
2015 gtk_widget_set_direction(widget, direction);
2017 style = widget->style;
2018 moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width,
2019 &focus_pad);
2021 TSOffsetStyleGCs(style, rect->x, rect->y);
2023 /* The detail argument for the gtk_paint_* calls below are "checkbutton"
2024 even for radio buttons, to match what gtk does. */
2026 /* this is for drawing a prelight box */
2027 if (state_type == GTK_STATE_PRELIGHT || state_type == GTK_STATE_ACTIVE) {
2028 gtk_paint_flat_box(style, drawable, GTK_STATE_PRELIGHT,
2029 GTK_SHADOW_ETCHED_OUT, cliprect, widget,
2030 "checkbutton",
2031 rect->x, rect->y, rect->width, rect->height);
2034 if (state_type != GTK_STATE_NORMAL && state_type != GTK_STATE_PRELIGHT)
2035 state_type = GTK_STATE_NORMAL;
2037 if (state->focused && !interior_focus) {
2038 gtk_paint_focus(style, drawable, state_type, cliprect, widget,
2039 "checkbutton",
2040 rect->x, rect->y, rect->width, rect->height);
2043 return MOZ_GTK_SUCCESS;
2046 static gint
2047 moz_gtk_toggle_label_paint(GdkDrawable* drawable, GdkRectangle* rect,
2048 GdkRectangle* cliprect, GtkWidgetState* state,
2049 gboolean isradio, GtkTextDirection direction)
2051 GtkStateType state_type;
2052 GtkStyle *style;
2053 GtkWidget *widget;
2054 gboolean interior_focus;
2056 if (!state->focused)
2057 return MOZ_GTK_SUCCESS;
2059 if (isradio) {
2060 ensure_radiobutton_widget();
2061 widget = gRadiobuttonWidget;
2062 } else {
2063 ensure_checkbox_widget();
2064 widget = gCheckboxWidget;
2066 gtk_widget_set_direction(widget, direction);
2068 gtk_widget_style_get(widget, "interior-focus", &interior_focus, NULL);
2069 if (!interior_focus)
2070 return MOZ_GTK_SUCCESS;
2072 state_type = ConvertGtkState(state);
2074 style = widget->style;
2075 TSOffsetStyleGCs(style, rect->x, rect->y);
2077 /* Always "checkbutton" to match gtkcheckbutton.c */
2078 gtk_paint_focus(style, drawable, state_type, cliprect, widget,
2079 "checkbutton",
2080 rect->x, rect->y, rect->width, rect->height);
2082 return MOZ_GTK_SUCCESS;
2085 static gint
2086 moz_gtk_toolbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
2087 GdkRectangle* cliprect, GtkTextDirection direction)
2089 GtkStyle* style;
2090 GtkShadowType shadow_type;
2092 ensure_toolbar_widget();
2093 gtk_widget_set_direction(gToolbarWidget, direction);
2095 style = gToolbarWidget->style;
2097 TSOffsetStyleGCs(style, rect->x, rect->y);
2099 gtk_style_apply_default_background(style, drawable, TRUE,
2100 GTK_STATE_NORMAL,
2101 cliprect, rect->x, rect->y,
2102 rect->width, rect->height);
2104 gtk_widget_style_get(gToolbarWidget, "shadow-type", &shadow_type, NULL);
2106 gtk_paint_box (style, drawable, GTK_STATE_NORMAL, shadow_type,
2107 cliprect, gToolbarWidget, "toolbar",
2108 rect->x, rect->y, rect->width, rect->height);
2110 return MOZ_GTK_SUCCESS;
2113 static gint
2114 moz_gtk_toolbar_separator_paint(GdkDrawable* drawable, GdkRectangle* rect,
2115 GdkRectangle* cliprect,
2116 GtkTextDirection direction)
2118 GtkStyle* style;
2119 gint separator_width;
2120 gint paint_width;
2121 gboolean wide_separators;
2123 /* Defined as constants in GTK+ 2.10.14 */
2124 const double start_fraction = 0.2;
2125 const double end_fraction = 0.8;
2127 ensure_toolbar_separator_widget();
2128 gtk_widget_set_direction(gToolbarSeparatorWidget, direction);
2130 style = gToolbarSeparatorWidget->style;
2132 gtk_widget_style_get(gToolbarWidget,
2133 "wide-separators", &wide_separators,
2134 "separator-width", &separator_width,
2135 NULL);
2137 TSOffsetStyleGCs(style, rect->x, rect->y);
2139 if (wide_separators) {
2140 if (separator_width > rect->width)
2141 separator_width = rect->width;
2143 gtk_paint_box(style, drawable,
2144 GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
2145 cliprect, gToolbarWidget, "vseparator",
2146 rect->x + (rect->width - separator_width) / 2,
2147 rect->y + rect->height * start_fraction,
2148 separator_width,
2149 rect->height * (end_fraction - start_fraction));
2151 } else {
2152 paint_width = style->xthickness;
2154 if (paint_width > rect->width)
2155 paint_width = rect->width;
2157 gtk_paint_vline(style, drawable,
2158 GTK_STATE_NORMAL, cliprect, gToolbarSeparatorWidget,
2159 "toolbar",
2160 rect->y + rect->height * start_fraction,
2161 rect->y + rect->height * end_fraction,
2162 rect->x + (rect->width - paint_width) / 2);
2165 return MOZ_GTK_SUCCESS;
2168 static gint
2169 moz_gtk_tooltip_paint(GdkDrawable* drawable, GdkRectangle* rect,
2170 GdkRectangle* cliprect, GtkTextDirection direction)
2172 GtkStyle* style;
2174 ensure_tooltip_widget();
2175 gtk_widget_set_direction(gTooltipWidget, direction);
2177 style = gtk_rc_get_style_by_paths(gtk_settings_get_default(),
2178 "gtk-tooltips", "GtkWindow",
2179 GTK_TYPE_WINDOW);
2181 style = gtk_style_attach(style, gTooltipWidget->window);
2182 TSOffsetStyleGCs(style, rect->x, rect->y);
2183 gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2184 cliprect, gTooltipWidget, "tooltip",
2185 rect->x, rect->y, rect->width, rect->height);
2187 return MOZ_GTK_SUCCESS;
2190 static gint
2191 moz_gtk_resizer_paint(GdkDrawable* drawable, GdkRectangle* rect,
2192 GdkRectangle* cliprect, GtkWidgetState* state,
2193 GtkTextDirection direction)
2195 GtkStyle* style;
2196 GtkStateType state_type = ConvertGtkState(state);
2198 ensure_frame_widget();
2199 gtk_widget_set_direction(gStatusbarWidget, direction);
2201 style = gStatusbarWidget->style;
2203 TSOffsetStyleGCs(style, rect->x, rect->y);
2205 gtk_paint_resize_grip(style, drawable, state_type, cliprect, gStatusbarWidget,
2206 "statusbar", (direction == GTK_TEXT_DIR_LTR) ?
2207 GDK_WINDOW_EDGE_SOUTH_EAST :
2208 GDK_WINDOW_EDGE_SOUTH_WEST,
2209 rect->x, rect->y, rect->width, rect->height);
2210 return MOZ_GTK_SUCCESS;
2213 static gint
2214 moz_gtk_frame_paint(GdkDrawable* drawable, GdkRectangle* rect,
2215 GdkRectangle* cliprect, GtkTextDirection direction)
2217 GtkStyle* style;
2218 GtkShadowType shadow_type;
2220 ensure_frame_widget();
2221 gtk_widget_set_direction(gFrameWidget, direction);
2223 style = gFrameWidget->style;
2225 gtk_widget_style_get(gStatusbarWidget, "shadow-type", &shadow_type, NULL);
2227 TSOffsetStyleGCs(style, rect->x, rect->y);
2228 gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, shadow_type,
2229 cliprect, gFrameWidget, "frame", rect->x, rect->y,
2230 rect->width, rect->height);
2232 return MOZ_GTK_SUCCESS;
2235 static gint
2236 moz_gtk_progressbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
2237 GdkRectangle* cliprect, GtkTextDirection direction)
2239 GtkStyle* style;
2241 ensure_progress_widget();
2242 gtk_widget_set_direction(gProgressWidget, direction);
2244 style = gProgressWidget->style;
2246 TSOffsetStyleGCs(style, rect->x, rect->y);
2247 gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
2248 cliprect, gProgressWidget, "trough", rect->x, rect->y,
2249 rect->width, rect->height);
2251 return MOZ_GTK_SUCCESS;
2254 static gint
2255 moz_gtk_progress_chunk_paint(GdkDrawable* drawable, GdkRectangle* rect,
2256 GdkRectangle* cliprect, GtkTextDirection direction,
2257 WidgetNodeType widget)
2259 GtkStyle* style;
2261 ensure_progress_widget();
2262 gtk_widget_set_direction(gProgressWidget, direction);
2264 style = gProgressWidget->style;
2266 TSOffsetStyleGCs(style, rect->x, rect->y);
2268 if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
2269 widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
2271 * The bar's size and the bar speed are set depending of the progress'
2272 * size. These could also be constant for all progress bars easily.
2274 gboolean vertical = (widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE);
2276 /* The size of the dimension we are going to use for the animation. */
2277 const gint progressSize = vertical ? rect->height : rect->width;
2279 /* The bar is using a fifth of the element size, based on GtkProgressBar
2280 * activity-blocks property. */
2281 const gint barSize = MAX(1, progressSize / 5);
2283 /* Represents the travel that has to be done for a complete cycle. */
2284 const gint travel = 2 * (progressSize - barSize);
2286 /* period equals to travel / pixelsPerMillisecond
2287 * where pixelsPerMillisecond equals progressSize / 1000.0.
2288 * This is equivalent to 1600. */
2289 static const guint period = 1600;
2290 const gint t = PR_IntervalToMilliseconds(PR_IntervalNow()) % period;
2291 const gint dx = travel * t / period;
2293 if (vertical) {
2294 rect->y += (dx < travel / 2) ? dx : travel - dx;
2295 rect->height = barSize;
2296 } else {
2297 rect->x += (dx < travel / 2) ? dx : travel - dx;
2298 rect->width = barSize;
2302 gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT,
2303 cliprect, gProgressWidget, "bar", rect->x, rect->y,
2304 rect->width, rect->height);
2306 return MOZ_GTK_SUCCESS;
2309 gint
2310 moz_gtk_get_tab_thickness(WidgetNodeType aNodeType)
2312 ensure_tab_widget();
2313 if (YTHICKNESS(gTabWidget->style) < 2)
2314 return 2; /* some themes don't set ythickness correctly */
2316 return YTHICKNESS(gTabWidget->style);
2319 static gint
2320 moz_gtk_tab_paint(GdkDrawable* drawable, GdkRectangle* rect,
2321 GdkRectangle* cliprect, GtkWidgetState* state,
2322 GtkTabFlags flags, GtkTextDirection direction,
2323 WidgetNodeType widget)
2325 /* When the tab isn't selected, we just draw a notebook extension.
2326 * When it is selected, we overwrite the adjacent border of the tabpanel
2327 * touching the tab with a pierced border (called "the gap") to make the
2328 * tab appear physically attached to the tabpanel; see details below. */
2330 GtkStyle* style;
2331 GdkRectangle focusRect;
2332 gboolean isBottomTab = (widget == MOZ_GTK_TAB_BOTTOM);
2334 ensure_tab_widget();
2335 gtk_widget_set_direction(gTabWidget, direction);
2337 style = gTabWidget->style;
2338 focusRect = *rect;
2339 TSOffsetStyleGCs(style, rect->x, rect->y);
2341 if ((flags & MOZ_GTK_TAB_SELECTED) == 0) {
2342 /* Only draw the tab */
2343 gtk_paint_extension(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
2344 cliprect, gTabWidget, "tab",
2345 rect->x, rect->y, rect->width, rect->height,
2346 isBottomTab ? GTK_POS_TOP : GTK_POS_BOTTOM );
2347 } else {
2348 /* Draw the tab and the gap
2349 * We want the gap to be positioned exactly on the tabpanel top
2350 * border; since tabbox.css may set a negative margin so that the tab
2351 * frame rect already overlaps the tabpanel frame rect, we need to take
2352 * that into account when drawing. To that effect, nsNativeThemeGTK
2353 * passes us this negative margin (bmargin in the graphic below) in the
2354 * lowest bits of |flags|. We use it to set gap_voffset, the distance
2355 * between the top of the gap and the bottom of the tab (resp. the
2356 * bottom of the gap and the top of the tab when we draw a bottom tab),
2357 * while ensuring that the gap always touches the border of the tab,
2358 * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results
2359 * with big negative or positive margins.
2360 * Here is a graphical explanation in the case of top tabs:
2361 * ___________________________
2362 * / \
2363 * | T A B |
2364 * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel
2365 * : ^ bmargin : ^
2366 * : | (-negative margin, : |
2367 * bottom : v passed in flags) : | gap_height
2368 * of -> :.............................: | (the size of the
2369 * the tab . part of the gap . | tabpanel top border)
2370 * . outside of the tab . v
2371 * ----------------------------------------------
2373 * To draw the gap, we use gtk_paint_box_gap(), see comment in
2374 * moz_gtk_tabpanels_paint(). This box_gap is made 3 * gap_height tall,
2375 * which should suffice to ensure that the only visible border is the
2376 * pierced one. If the tab is in the middle, we make the box_gap begin
2377 * a bit to the left of the tab and end a bit to the right, adjusting
2378 * the gap position so it still is under the tab, because we want the
2379 * rendering of a gap in the middle of a tabpanel. This is the role of
2380 * the gints gap_{l,r}_offset. On the contrary, if the tab is the
2381 * first, we align the start border of the box_gap with the start
2382 * border of the tab (left if LTR, right if RTL), by setting the
2383 * appropriate offset to 0.*/
2384 gint gap_loffset, gap_roffset, gap_voffset, gap_height;
2386 /* Get height needed by the gap */
2387 gap_height = moz_gtk_get_tab_thickness(widget);
2389 /* Extract gap_voffset from the first bits of flags */
2390 gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK;
2391 if (gap_voffset > gap_height)
2392 gap_voffset = gap_height;
2394 /* Set gap_{l,r}_offset to appropriate values */
2395 gap_loffset = gap_roffset = 20; /* should be enough */
2396 if (flags & MOZ_GTK_TAB_FIRST) {
2397 if (direction == GTK_TEXT_DIR_RTL)
2398 gap_roffset = 0;
2399 else
2400 gap_loffset = 0;
2403 if (isBottomTab) {
2404 /* Draw the tab */
2405 focusRect.y += gap_voffset;
2406 focusRect.height -= gap_voffset;
2407 gtk_paint_extension(style, drawable, GTK_STATE_NORMAL,
2408 GTK_SHADOW_OUT, cliprect, gTabWidget, "tab",
2409 rect->x, rect->y + gap_voffset, rect->width,
2410 rect->height - gap_voffset, GTK_POS_TOP);
2412 /* Draw the gap; erase with background color before painting in
2413 * case theme does not */
2414 gtk_style_apply_default_background(style, drawable, TRUE,
2415 GTK_STATE_NORMAL, cliprect,
2416 rect->x,
2417 rect->y + gap_voffset
2418 - gap_height,
2419 rect->width, gap_height);
2420 gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2421 cliprect, gTabWidget, "notebook",
2422 rect->x - gap_loffset,
2423 rect->y + gap_voffset - 3 * gap_height,
2424 rect->width + gap_loffset + gap_roffset,
2425 3 * gap_height, GTK_POS_BOTTOM,
2426 gap_loffset, rect->width);
2427 } else {
2428 /* Draw the tab */
2429 focusRect.height -= gap_voffset;
2430 gtk_paint_extension(style, drawable, GTK_STATE_NORMAL,
2431 GTK_SHADOW_OUT, cliprect, gTabWidget, "tab",
2432 rect->x, rect->y, rect->width,
2433 rect->height - gap_voffset, GTK_POS_BOTTOM);
2435 /* Draw the gap; erase with background color before painting in
2436 * case theme does not */
2437 gtk_style_apply_default_background(style, drawable, TRUE,
2438 GTK_STATE_NORMAL, cliprect,
2439 rect->x,
2440 rect->y + rect->height
2441 - gap_voffset,
2442 rect->width, gap_height);
2443 gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2444 cliprect, gTabWidget, "notebook",
2445 rect->x - gap_loffset,
2446 rect->y + rect->height - gap_voffset,
2447 rect->width + gap_loffset + gap_roffset,
2448 3 * gap_height, GTK_POS_TOP,
2449 gap_loffset, rect->width);
2454 if (state->focused) {
2455 /* Paint the focus ring */
2456 focusRect.x += XTHICKNESS(style);
2457 focusRect.width -= XTHICKNESS(style) * 2;
2458 focusRect.y += YTHICKNESS(style);
2459 focusRect.height -= YTHICKNESS(style) * 2;
2461 gtk_paint_focus(style, drawable,
2462 /* Believe it or not, NORMAL means a selected tab and
2463 ACTIVE means an unselected tab. */
2464 (flags & MOZ_GTK_TAB_SELECTED) ? GTK_STATE_NORMAL
2465 : GTK_STATE_ACTIVE,
2466 cliprect, gTabWidget, "tab",
2467 focusRect.x, focusRect.y, focusRect.width, focusRect.height);
2470 return MOZ_GTK_SUCCESS;
2473 static gint
2474 moz_gtk_tabpanels_paint(GdkDrawable* drawable, GdkRectangle* rect,
2475 GdkRectangle* cliprect, GtkTextDirection direction)
2477 /* We have three problems here:
2478 * - Most engines draw gtk_paint_box differently to gtk_paint_box_gap, the
2479 * former implies there are no tabs, eg. Clearlooks.
2480 * - Wanting a gap of width 0 doesn't actually guarantee a zero-width gap, eg.
2481 * Clearlooks.
2482 * - Our old approach of a negative X position could cause rendering errors
2483 * on the box's corner, eg. themes using the Pixbuf engine.
2485 GtkStyle* style;
2486 GdkRectangle halfClipRect;
2488 ensure_tab_widget();
2489 gtk_widget_set_direction(gTabWidget, direction);
2491 style = gTabWidget->style;
2492 TSOffsetStyleGCs(style, rect->x, rect->y);
2494 /* Our approach is as follows:
2495 * - Draw the box in two passes. Pass in a clip rect to draw the left half of the
2496 * box, with the gap specified to the right outside the clip rect so that it is
2497 * not drawn.
2498 * - The right half is drawn with the gap to the left outside the modified clip rect.
2500 if (!gdk_rectangle_intersect(rect, cliprect, &halfClipRect))
2501 return MOZ_GTK_SUCCESS;
2503 halfClipRect.width = (halfClipRect.width / 2) + 1;
2504 gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2505 &halfClipRect, gTabWidget, "notebook", rect->x, rect->y,
2506 rect->width, rect->height,
2507 GTK_POS_TOP, halfClipRect.width + 1, 0);
2509 halfClipRect.x += halfClipRect.width;
2510 gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2511 &halfClipRect, gTabWidget, "notebook", rect->x, rect->y,
2512 rect->width, rect->height,
2513 GTK_POS_TOP, -10, 0);
2515 return MOZ_GTK_SUCCESS;
2518 static gint
2519 moz_gtk_tab_scroll_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
2520 GdkRectangle* cliprect, GtkWidgetState* state,
2521 GtkArrowType arrow_type,
2522 GtkTextDirection direction)
2524 GtkStateType state_type = ConvertGtkState(state);
2525 GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
2526 GtkStyle* style;
2527 gint arrow_size = MIN(rect->width, rect->height);
2528 gint x = rect->x + (rect->width - arrow_size) / 2;
2529 gint y = rect->y + (rect->height - arrow_size) / 2;
2531 ensure_tab_widget();
2533 style = gTabWidget->style;
2534 TSOffsetStyleGCs(style, rect->x, rect->y);
2536 if (direction == GTK_TEXT_DIR_RTL) {
2537 arrow_type = (arrow_type == GTK_ARROW_LEFT) ?
2538 GTK_ARROW_RIGHT : GTK_ARROW_LEFT;
2541 gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL,
2542 gTabWidget, "notebook", arrow_type, TRUE,
2543 x, y, arrow_size, arrow_size);
2545 return MOZ_GTK_SUCCESS;
2548 static gint
2549 moz_gtk_menu_bar_paint(GdkDrawable* drawable, GdkRectangle* rect,
2550 GdkRectangle* cliprect, GtkTextDirection direction)
2552 GtkStyle* style;
2553 GtkShadowType shadow_type;
2554 ensure_menu_bar_widget();
2555 gtk_widget_set_direction(gMenuBarWidget, direction);
2557 gtk_widget_style_get(gMenuBarWidget, "shadow-type", &shadow_type, NULL);
2559 style = gMenuBarWidget->style;
2561 TSOffsetStyleGCs(style, rect->x, rect->y);
2562 gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL,
2563 cliprect, rect->x, rect->y,
2564 rect->width, rect->height);
2566 gtk_paint_box(style, drawable, GTK_STATE_NORMAL, shadow_type,
2567 cliprect, gMenuBarWidget, "menubar", rect->x, rect->y,
2568 rect->width, rect->height);
2569 return MOZ_GTK_SUCCESS;
2572 static gint
2573 moz_gtk_menu_popup_paint(GdkDrawable* drawable, GdkRectangle* rect,
2574 GdkRectangle* cliprect, GtkTextDirection direction)
2576 GtkStyle* style;
2577 ensure_menu_popup_widget();
2578 gtk_widget_set_direction(gMenuPopupWidget, direction);
2580 style = gMenuPopupWidget->style;
2582 TSOffsetStyleGCs(style, rect->x, rect->y);
2583 gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL,
2584 cliprect, rect->x, rect->y,
2585 rect->width, rect->height);
2586 gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2587 cliprect, gMenuPopupWidget, "menu",
2588 rect->x, rect->y, rect->width, rect->height);
2590 return MOZ_GTK_SUCCESS;
2593 static gint
2594 moz_gtk_menu_separator_paint(GdkDrawable* drawable, GdkRectangle* rect,
2595 GdkRectangle* cliprect, GtkTextDirection direction)
2597 GtkStyle* style;
2598 gboolean wide_separators;
2599 gint separator_height;
2600 guint horizontal_padding;
2601 gint paint_height;
2603 ensure_menu_separator_widget();
2604 gtk_widget_set_direction(gMenuSeparatorWidget, direction);
2606 style = gMenuSeparatorWidget->style;
2608 gtk_widget_style_get(gMenuSeparatorWidget,
2609 "wide-separators", &wide_separators,
2610 "separator-height", &separator_height,
2611 "horizontal-padding", &horizontal_padding,
2612 NULL);
2614 TSOffsetStyleGCs(style, rect->x, rect->y);
2616 if (wide_separators) {
2617 if (separator_height > rect->height)
2618 separator_height = rect->height;
2620 gtk_paint_box(style, drawable,
2621 GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
2622 cliprect, gMenuSeparatorWidget, "hseparator",
2623 rect->x + horizontal_padding + style->xthickness,
2624 rect->y + (rect->height - separator_height - style->ythickness) / 2,
2625 rect->width - 2 * (horizontal_padding + style->xthickness),
2626 separator_height);
2627 } else {
2628 paint_height = style->ythickness;
2629 if (paint_height > rect->height)
2630 paint_height = rect->height;
2632 gtk_paint_hline(style, drawable,
2633 GTK_STATE_NORMAL, cliprect, gMenuSeparatorWidget,
2634 "menuitem",
2635 rect->x + horizontal_padding + style->xthickness,
2636 rect->x + rect->width - horizontal_padding - style->xthickness - 1,
2637 rect->y + (rect->height - style->ythickness) / 2);
2640 return MOZ_GTK_SUCCESS;
2643 static gint
2644 moz_gtk_menu_item_paint(WidgetNodeType widget, GdkDrawable* drawable,
2645 GdkRectangle* rect, GdkRectangle* cliprect,
2646 GtkWidgetState* state, GtkTextDirection direction)
2648 GtkStyle* style;
2649 GtkShadowType shadow_type;
2650 GtkWidget* item_widget;
2652 if (state->inHover && !state->disabled) {
2653 if (widget == MOZ_GTK_MENUBARITEM) {
2654 ensure_menu_bar_item_widget();
2655 item_widget = gMenuBarItemWidget;
2656 } else {
2657 ensure_menu_item_widget();
2658 item_widget = gMenuItemWidget;
2660 gtk_widget_set_direction(item_widget, direction);
2662 style = item_widget->style;
2663 TSOffsetStyleGCs(style, rect->x, rect->y);
2665 gtk_widget_style_get(item_widget, "selected-shadow-type",
2666 &shadow_type, NULL);
2668 gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, shadow_type,
2669 cliprect, item_widget, "menuitem", rect->x, rect->y,
2670 rect->width, rect->height);
2673 return MOZ_GTK_SUCCESS;
2676 static gint
2677 moz_gtk_menu_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
2678 GdkRectangle* cliprect, GtkWidgetState* state,
2679 GtkTextDirection direction)
2681 GtkStyle* style;
2682 GtkStateType state_type = ConvertGtkState(state);
2684 ensure_menu_item_widget();
2685 gtk_widget_set_direction(gMenuItemWidget, direction);
2687 style = gMenuItemWidget->style;
2689 TSOffsetStyleGCs(style, rect->x, rect->y);
2690 gtk_paint_arrow(style, drawable, state_type,
2691 (state_type == GTK_STATE_PRELIGHT) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
2692 cliprect, gMenuItemWidget, "menuitem",
2693 (direction == GTK_TEXT_DIR_LTR) ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT,
2694 TRUE, rect->x, rect->y, rect->width, rect->height);
2696 return MOZ_GTK_SUCCESS;
2699 static gint
2700 moz_gtk_check_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect,
2701 GdkRectangle* cliprect, GtkWidgetState* state,
2702 gboolean checked, gboolean isradio,
2703 GtkTextDirection direction)
2705 GtkStateType state_type = ConvertGtkState(state);
2706 GtkStyle* style;
2707 GtkShadowType shadow_type = (checked)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
2708 gint offset;
2709 gint indicator_size, horizontal_padding;
2710 gint x, y;
2712 moz_gtk_menu_item_paint(MOZ_GTK_MENUITEM, drawable, rect, cliprect, state,
2713 direction);
2715 ensure_check_menu_item_widget();
2716 gtk_widget_set_direction(gCheckMenuItemWidget, direction);
2718 gtk_widget_style_get (gCheckMenuItemWidget,
2719 "indicator-size", &indicator_size,
2720 "horizontal-padding", &horizontal_padding,
2721 NULL);
2723 if (checked || GTK_CHECK_MENU_ITEM(gCheckMenuItemWidget)->always_show_toggle) {
2724 style = gCheckMenuItemWidget->style;
2726 offset = GTK_CONTAINER(gCheckMenuItemWidget)->border_width +
2727 gCheckMenuItemWidget->style->xthickness + 2;
2729 x = (direction == GTK_TEXT_DIR_RTL) ?
2730 rect->width - indicator_size - offset - horizontal_padding: rect->x + offset + horizontal_padding;
2731 y = rect->y + (rect->height - indicator_size) / 2;
2733 TSOffsetStyleGCs(style, x, y);
2734 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gCheckMenuItemWidget),
2735 checked);
2737 if (isradio) {
2738 gtk_paint_option(style, drawable, state_type, shadow_type, cliprect,
2739 gCheckMenuItemWidget, "option",
2740 x, y, indicator_size, indicator_size);
2741 } else {
2742 gtk_paint_check(style, drawable, state_type, shadow_type, cliprect,
2743 gCheckMenuItemWidget, "check",
2744 x, y, indicator_size, indicator_size);
2748 return MOZ_GTK_SUCCESS;
2751 static gint
2752 moz_gtk_window_paint(GdkDrawable* drawable, GdkRectangle* rect,
2753 GdkRectangle* cliprect, GtkTextDirection direction)
2755 GtkStyle* style;
2757 ensure_window_widget();
2758 gtk_widget_set_direction(gProtoWindow, direction);
2760 style = gProtoWindow->style;
2762 TSOffsetStyleGCs(style, rect->x, rect->y);
2763 gtk_style_apply_default_background(style, drawable, TRUE,
2764 GTK_STATE_NORMAL,
2765 cliprect, rect->x, rect->y,
2766 rect->width, rect->height);
2767 return MOZ_GTK_SUCCESS;
2770 gint
2771 moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
2772 gint* right, gint* bottom, GtkTextDirection direction,
2773 gboolean inhtml)
2775 GtkWidget* w;
2777 switch (widget) {
2778 case MOZ_GTK_BUTTON:
2779 case MOZ_GTK_TOOLBAR_BUTTON:
2781 GtkBorder inner_border;
2782 gboolean interior_focus;
2783 gint focus_width, focus_pad;
2785 ensure_button_widget();
2786 *left = *top = *right = *bottom = GTK_CONTAINER(gButtonWidget)->border_width;
2788 /* Don't add this padding in HTML, otherwise the buttons will
2789 become too big and stuff the layout. */
2790 if (!inhtml) {
2791 moz_gtk_widget_get_focus(gButtonWidget, &interior_focus, &focus_width, &focus_pad);
2792 moz_gtk_button_get_inner_border(gButtonWidget, &inner_border);
2793 *left += focus_width + focus_pad + inner_border.left;
2794 *right += focus_width + focus_pad + inner_border.right;
2795 *top += focus_width + focus_pad + inner_border.top;
2796 *bottom += focus_width + focus_pad + inner_border.bottom;
2799 *left += gButtonWidget->style->xthickness;
2800 *right += gButtonWidget->style->xthickness;
2801 *top += gButtonWidget->style->ythickness;
2802 *bottom += gButtonWidget->style->ythickness;
2803 return MOZ_GTK_SUCCESS;
2805 case MOZ_GTK_ENTRY:
2806 ensure_entry_widget();
2807 w = gEntryWidget;
2808 break;
2809 case MOZ_GTK_TREEVIEW:
2810 ensure_tree_view_widget();
2811 w = gTreeViewWidget;
2812 break;
2813 case MOZ_GTK_TREE_HEADER_CELL:
2815 /* A Tree Header in GTK is just a different styled button
2816 * It must be placed in a TreeView for getting the correct style
2817 * assigned.
2818 * That is why the following code is the same as for MOZ_GTK_BUTTON.
2819 * */
2821 GtkBorder inner_border;
2822 gboolean interior_focus;
2823 gint focus_width, focus_pad;
2825 ensure_tree_header_cell_widget();
2826 *left = *top = *right = *bottom = GTK_CONTAINER(gTreeHeaderCellWidget)->border_width;
2828 moz_gtk_widget_get_focus(gTreeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad);
2829 moz_gtk_button_get_inner_border(gTreeHeaderCellWidget, &inner_border);
2830 *left += focus_width + focus_pad + inner_border.left;
2831 *right += focus_width + focus_pad + inner_border.right;
2832 *top += focus_width + focus_pad + inner_border.top;
2833 *bottom += focus_width + focus_pad + inner_border.bottom;
2835 *left += gTreeHeaderCellWidget->style->xthickness;
2836 *right += gTreeHeaderCellWidget->style->xthickness;
2837 *top += gTreeHeaderCellWidget->style->ythickness;
2838 *bottom += gTreeHeaderCellWidget->style->ythickness;
2839 return MOZ_GTK_SUCCESS;
2841 case MOZ_GTK_TREE_HEADER_SORTARROW:
2842 ensure_tree_header_cell_widget();
2843 w = gTreeHeaderSortArrowWidget;
2844 break;
2845 case MOZ_GTK_DROPDOWN_ENTRY:
2846 ensure_combo_box_entry_widgets();
2847 w = gComboBoxEntryTextareaWidget;
2848 break;
2849 case MOZ_GTK_DROPDOWN_ARROW:
2850 ensure_combo_box_entry_widgets();
2851 w = gComboBoxEntryButtonWidget;
2852 break;
2853 case MOZ_GTK_DROPDOWN:
2855 /* We need to account for the arrow on the dropdown, so text
2856 * doesn't come too close to the arrow, or in some cases spill
2857 * into the arrow. */
2858 gboolean ignored_interior_focus, wide_separators;
2859 gint focus_width, focus_pad, separator_width;
2860 GtkRequisition arrow_req;
2862 ensure_combo_box_widgets();
2864 *left = GTK_CONTAINER(gComboBoxButtonWidget)->border_width;
2866 if (!inhtml) {
2867 moz_gtk_widget_get_focus(gComboBoxButtonWidget,
2868 &ignored_interior_focus,
2869 &focus_width, &focus_pad);
2870 *left += focus_width + focus_pad;
2873 *top = *left + gComboBoxButtonWidget->style->ythickness;
2874 *left += gComboBoxButtonWidget->style->xthickness;
2876 *right = *left; *bottom = *top;
2878 /* If there is no separator, don't try to count its width. */
2879 separator_width = 0;
2880 if (gComboBoxSeparatorWidget) {
2881 gtk_widget_style_get(gComboBoxSeparatorWidget,
2882 "wide-separators", &wide_separators,
2883 "separator-width", &separator_width,
2884 NULL);
2886 if (!wide_separators)
2887 separator_width =
2888 XTHICKNESS(gComboBoxSeparatorWidget->style);
2891 gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req);
2893 if (direction == GTK_TEXT_DIR_RTL)
2894 *left += separator_width + arrow_req.width;
2895 else
2896 *right += separator_width + arrow_req.width;
2898 return MOZ_GTK_SUCCESS;
2900 case MOZ_GTK_TABPANELS:
2901 ensure_tab_widget();
2902 w = gTabWidget;
2903 break;
2904 case MOZ_GTK_PROGRESSBAR:
2905 ensure_progress_widget();
2906 w = gProgressWidget;
2907 break;
2908 case MOZ_GTK_SPINBUTTON_ENTRY:
2909 case MOZ_GTK_SPINBUTTON_UP:
2910 case MOZ_GTK_SPINBUTTON_DOWN:
2911 ensure_spin_widget();
2912 w = gSpinWidget;
2913 break;
2914 case MOZ_GTK_SCALE_HORIZONTAL:
2915 ensure_scale_widget();
2916 w = gHScaleWidget;
2917 break;
2918 case MOZ_GTK_SCALE_VERTICAL:
2919 ensure_scale_widget();
2920 w = gVScaleWidget;
2921 break;
2922 case MOZ_GTK_FRAME:
2923 ensure_frame_widget();
2924 w = gFrameWidget;
2925 break;
2926 case MOZ_GTK_CHECKBUTTON_LABEL:
2927 case MOZ_GTK_RADIOBUTTON_LABEL:
2929 gboolean interior_focus;
2930 gint focus_width, focus_pad;
2932 /* If the focus is interior, then the label has a border of
2933 (focus_width + focus_pad). */
2934 if (widget == MOZ_GTK_CHECKBUTTON_LABEL) {
2935 ensure_checkbox_widget();
2936 moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus,
2937 &focus_width, &focus_pad);
2939 else {
2940 ensure_radiobutton_widget();
2941 moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus,
2942 &focus_width, &focus_pad);
2945 if (interior_focus)
2946 *left = *top = *right = *bottom = (focus_width + focus_pad);
2947 else
2948 *left = *top = *right = *bottom = 0;
2950 return MOZ_GTK_SUCCESS;
2953 case MOZ_GTK_CHECKBUTTON_CONTAINER:
2954 case MOZ_GTK_RADIOBUTTON_CONTAINER:
2956 gboolean interior_focus;
2957 gint focus_width, focus_pad;
2959 /* If the focus is _not_ interior, then the container has a border
2960 of (focus_width + focus_pad). */
2961 if (widget == MOZ_GTK_CHECKBUTTON_CONTAINER) {
2962 ensure_checkbox_widget();
2963 moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus,
2964 &focus_width, &focus_pad);
2965 w = gCheckboxWidget;
2966 } else {
2967 ensure_radiobutton_widget();
2968 moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus,
2969 &focus_width, &focus_pad);
2970 w = gRadiobuttonWidget;
2973 *left = *top = *right = *bottom = GTK_CONTAINER(w)->border_width;
2975 if (!interior_focus) {
2976 *left += (focus_width + focus_pad);
2977 *right += (focus_width + focus_pad);
2978 *top += (focus_width + focus_pad);
2979 *bottom += (focus_width + focus_pad);
2982 return MOZ_GTK_SUCCESS;
2984 case MOZ_GTK_MENUPOPUP:
2985 ensure_menu_popup_widget();
2986 w = gMenuPopupWidget;
2987 break;
2988 case MOZ_GTK_MENUBARITEM:
2989 // Bug 1274143 for MOZ_GTK_MENUBARITEM.
2990 // Fall through to MOZ_GTK_MENUITEM for now.
2991 case MOZ_GTK_MENUITEM:
2992 ensure_menu_item_widget();
2993 ensure_menu_bar_item_widget();
2994 w = gMenuItemWidget;
2995 break;
2996 case MOZ_GTK_CHECKMENUITEM:
2997 case MOZ_GTK_RADIOMENUITEM:
2998 ensure_check_menu_item_widget();
2999 w = gCheckMenuItemWidget;
3000 break;
3001 case MOZ_GTK_TAB_TOP:
3002 case MOZ_GTK_TAB_BOTTOM:
3003 ensure_tab_widget();
3004 w = gTabWidget;
3005 break;
3006 case MOZ_GTK_TOOLTIP:
3007 // In GTK 2 the spacing between box is set to 4.
3008 *left = *top = *right = *bottom = 4;
3009 return MOZ_GTK_SUCCESS;
3010 /* These widgets have no borders, since they are not containers. */
3011 case MOZ_GTK_SPLITTER_HORIZONTAL:
3012 case MOZ_GTK_SPLITTER_VERTICAL:
3013 case MOZ_GTK_CHECKBUTTON:
3014 case MOZ_GTK_RADIOBUTTON:
3015 case MOZ_GTK_SCROLLBAR_BUTTON:
3016 case MOZ_GTK_SCROLLBAR_HORIZONTAL:
3017 case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
3018 case MOZ_GTK_SCROLLBAR_VERTICAL:
3019 case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
3020 case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
3021 case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
3022 case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
3023 case MOZ_GTK_SCALE_THUMB_VERTICAL:
3024 case MOZ_GTK_GRIPPER:
3025 case MOZ_GTK_PROGRESS_CHUNK:
3026 case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
3027 case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
3028 case MOZ_GTK_TREEVIEW_EXPANDER:
3029 case MOZ_GTK_TOOLBAR_SEPARATOR:
3030 case MOZ_GTK_MENUSEPARATOR:
3031 /* These widgets have no borders.*/
3032 case MOZ_GTK_INNER_SPIN_BUTTON:
3033 case MOZ_GTK_SPINBUTTON:
3034 case MOZ_GTK_WINDOW:
3035 case MOZ_GTK_RESIZER:
3036 case MOZ_GTK_MENUARROW:
3037 case MOZ_GTK_TOOLBARBUTTON_ARROW:
3038 case MOZ_GTK_TOOLBAR:
3039 case MOZ_GTK_MENUBAR:
3040 case MOZ_GTK_TAB_SCROLLARROW:
3041 *left = *top = *right = *bottom = 0;
3042 return MOZ_GTK_SUCCESS;
3043 default:
3044 g_warning("Unsupported widget type: %d", widget);
3045 return MOZ_GTK_UNKNOWN_WIDGET;
3048 *right = *left = XTHICKNESS(w->style);
3049 *bottom = *top = YTHICKNESS(w->style);
3051 return MOZ_GTK_SUCCESS;
3054 gint
3055 moz_gtk_get_tab_border(gint* left, gint* top, gint* right, gint* bottom,
3056 GtkTextDirection direction, GtkTabFlags flags,
3057 WidgetNodeType widget)
3059 moz_gtk_get_widget_border(widget, left, top,
3060 right, bottom, direction,
3061 FALSE);
3063 // Top tabs have no bottom border, bottom tabs have no top border
3064 if (widget == MOZ_GTK_TAB_BOTTOM) {
3065 *top = 0;
3066 } else {
3067 *bottom = 0;
3070 return MOZ_GTK_SUCCESS;
3073 gint
3074 moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height)
3077 * We get the requisition of the drop down button, which includes
3078 * all padding, border and focus line widths the button uses,
3079 * as well as the minimum arrow size and its padding
3080 * */
3081 GtkRequisition requisition;
3082 ensure_combo_box_entry_widgets();
3084 gtk_widget_size_request(gComboBoxEntryButtonWidget, &requisition);
3085 *width = requisition.width;
3086 *height = requisition.height;
3088 return MOZ_GTK_SUCCESS;
3091 gint
3092 moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height)
3094 gint arrow_size;
3096 ensure_tab_widget();
3097 gtk_widget_style_get(gTabWidget,
3098 "scroll-arrow-hlength", &arrow_size,
3099 NULL);
3101 *height = *width = arrow_size;
3103 return MOZ_GTK_SUCCESS;
3106 void
3107 moz_gtk_get_arrow_size(WidgetNodeType widgetType, gint* width, gint* height)
3109 GtkWidget* widget;
3110 switch (widgetType) {
3111 case MOZ_GTK_DROPDOWN:
3112 ensure_combo_box_widgets();
3113 widget = gComboBoxArrowWidget;
3114 break;
3115 default:
3116 ensure_button_arrow_widget();
3117 widget = gButtonArrowWidget;
3118 break;
3121 GtkRequisition requisition;
3122 gtk_widget_size_request(widget, &requisition);
3123 *width = requisition.width;
3124 *height = requisition.height;
3127 gint
3128 moz_gtk_get_toolbar_separator_width(gint* size)
3130 gboolean wide_separators;
3131 gint separator_width;
3132 GtkStyle* style;
3134 ensure_toolbar_widget();
3136 style = gToolbarWidget->style;
3138 gtk_widget_style_get(gToolbarWidget,
3139 "space-size", size,
3140 "wide-separators", &wide_separators,
3141 "separator-width", &separator_width,
3142 NULL);
3144 /* Just in case... */
3145 *size = MAX(*size, (wide_separators ? separator_width : style->xthickness));
3147 return MOZ_GTK_SUCCESS;
3150 gint
3151 moz_gtk_get_expander_size(gint* size)
3153 ensure_expander_widget();
3154 gtk_widget_style_get(gExpanderWidget,
3155 "expander-size", size,
3156 NULL);
3158 return MOZ_GTK_SUCCESS;
3161 gint
3162 moz_gtk_get_treeview_expander_size(gint* size)
3164 ensure_tree_view_widget();
3165 gtk_widget_style_get(gTreeViewWidget,
3166 "expander-size", size,
3167 NULL);
3169 return MOZ_GTK_SUCCESS;
3172 gint
3173 moz_gtk_get_menu_separator_height(gint *size)
3175 gboolean wide_separators;
3176 gint separator_height;
3178 ensure_menu_separator_widget();
3180 gtk_widget_style_get(gMenuSeparatorWidget,
3181 "wide-separators", &wide_separators,
3182 "separator-height", &separator_height,
3183 NULL);
3185 if (wide_separators)
3186 *size = separator_height + gMenuSeparatorWidget->style->ythickness;
3187 else
3188 *size = gMenuSeparatorWidget->style->ythickness * 2;
3190 return MOZ_GTK_SUCCESS;
3193 void
3194 moz_gtk_get_scale_metrics(GtkOrientation orient, gint* scale_width,
3195 gint* scale_height)
3197 moz_gtk_get_scalethumb_metrics(orient, scale_width, scale_height);
3201 gint
3202 moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height)
3204 GtkWidget* widget;
3206 ensure_scale_widget();
3207 widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
3209 gtk_widget_style_get (widget,
3210 "slider_length", thumb_length,
3211 "slider_width", thumb_height,
3212 NULL);
3214 return MOZ_GTK_SUCCESS;
3217 gint
3218 moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
3220 ensure_scrollbar_widget();
3222 gtk_widget_style_get (gHorizScrollbarWidget,
3223 "slider_width", &metrics->slider_width,
3224 "trough_border", &metrics->trough_border,
3225 "stepper_size", &metrics->stepper_size,
3226 "stepper_spacing", &metrics->stepper_spacing,
3227 NULL);
3229 metrics->min_slider_size =
3230 GTK_RANGE(gHorizScrollbarWidget)->min_slider_size;
3232 return MOZ_GTK_SUCCESS;
3234 void
3235 moz_gtk_get_widget_min_size(WidgetNodeType aGtkWidgetType, int* width, int* height) {
3236 MOZ_ASSERT_UNREACHABLE("get_widget_min_size not available for GTK2");
3239 gint
3240 moz_gtk_widget_paint(WidgetNodeType widget, GdkDrawable* drawable,
3241 GdkRectangle* rect, GdkRectangle* cliprect,
3242 GtkWidgetState* state, gint flags,
3243 GtkTextDirection direction)
3245 switch (widget) {
3246 case MOZ_GTK_BUTTON:
3247 case MOZ_GTK_TOOLBAR_BUTTON:
3248 if (state->depressed) {
3249 ensure_toggle_button_widget();
3250 return moz_gtk_button_paint(drawable, rect, cliprect, state,
3251 (GtkReliefStyle) flags,
3252 gToggleButtonWidget, direction);
3254 ensure_button_widget();
3255 return moz_gtk_button_paint(drawable, rect, cliprect, state,
3256 (GtkReliefStyle) flags, gButtonWidget,
3257 direction);
3258 break;
3259 case MOZ_GTK_CHECKBUTTON:
3260 case MOZ_GTK_RADIOBUTTON:
3261 return moz_gtk_toggle_paint(drawable, rect, cliprect, state,
3262 !!(flags & MOZ_GTK_WIDGET_CHECKED),
3263 !!(flags & MOZ_GTK_WIDGET_INCONSISTENT),
3264 (widget == MOZ_GTK_RADIOBUTTON),
3265 direction);
3266 break;
3267 case MOZ_GTK_SCROLLBAR_BUTTON:
3268 return moz_gtk_scrollbar_button_paint(drawable, rect, cliprect, state,
3269 (GtkScrollbarButtonFlags) flags,
3270 direction);
3271 break;
3272 case MOZ_GTK_SCROLLBAR_HORIZONTAL:
3273 case MOZ_GTK_SCROLLBAR_VERTICAL:
3274 return moz_gtk_scrollbar_trough_paint(widget, drawable, rect,
3275 cliprect, state, direction);
3276 break;
3277 case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
3278 case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
3279 return MOZ_GTK_SUCCESS;
3280 break;
3281 case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
3282 case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
3283 return moz_gtk_scrollbar_thumb_paint(widget, drawable, rect,
3284 cliprect, state, direction);
3285 break;
3286 case MOZ_GTK_SCALE_HORIZONTAL:
3287 case MOZ_GTK_SCALE_VERTICAL:
3288 return moz_gtk_scale_paint(drawable, rect, cliprect, state,
3289 (GtkOrientation) flags, direction);
3290 break;
3291 case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
3292 case MOZ_GTK_SCALE_THUMB_VERTICAL:
3293 return moz_gtk_scale_thumb_paint(drawable, rect, cliprect, state,
3294 (GtkOrientation) flags, direction);
3295 break;
3296 case MOZ_GTK_INNER_SPIN_BUTTON:
3297 return moz_gtk_inner_spin_paint(drawable, rect, state, direction);
3298 break;
3299 case MOZ_GTK_SPINBUTTON:
3300 return moz_gtk_spin_paint(drawable, rect, direction);
3301 break;
3302 case MOZ_GTK_SPINBUTTON_UP:
3303 case MOZ_GTK_SPINBUTTON_DOWN:
3304 return moz_gtk_spin_updown_paint(drawable, rect,
3305 (widget == MOZ_GTK_SPINBUTTON_DOWN),
3306 state, direction);
3307 break;
3308 case MOZ_GTK_SPINBUTTON_ENTRY:
3309 ensure_spin_widget();
3310 return moz_gtk_entry_paint(drawable, rect, cliprect, state,
3311 gSpinWidget, direction);
3312 break;
3313 case MOZ_GTK_GRIPPER:
3314 return moz_gtk_gripper_paint(drawable, rect, cliprect, state,
3315 direction);
3316 break;
3317 case MOZ_GTK_TREEVIEW:
3318 return moz_gtk_treeview_paint(drawable, rect, cliprect, state,
3319 direction);
3320 break;
3321 case MOZ_GTK_TREE_HEADER_CELL:
3322 return moz_gtk_tree_header_cell_paint(drawable, rect, cliprect, state,
3323 flags, direction);
3324 break;
3325 case MOZ_GTK_TREE_HEADER_SORTARROW:
3326 return moz_gtk_tree_header_sort_arrow_paint(drawable, rect, cliprect,
3327 state,
3328 (GtkArrowType) flags,
3329 direction);
3330 break;
3331 case MOZ_GTK_TREEVIEW_EXPANDER:
3332 return moz_gtk_treeview_expander_paint(drawable, rect, cliprect, state,
3333 (GtkExpanderStyle) flags, direction);
3334 break;
3335 case MOZ_GTK_ENTRY:
3336 ensure_entry_widget();
3337 return moz_gtk_entry_paint(drawable, rect, cliprect, state,
3338 gEntryWidget, direction);
3339 break;
3340 case MOZ_GTK_DROPDOWN:
3341 return moz_gtk_combo_box_paint(drawable, rect, cliprect, state,
3342 (gboolean) flags, direction);
3343 break;
3344 case MOZ_GTK_DROPDOWN_ARROW:
3345 return moz_gtk_combo_box_entry_button_paint(drawable, rect, cliprect,
3346 state, flags, direction);
3347 break;
3348 case MOZ_GTK_DROPDOWN_ENTRY:
3349 ensure_combo_box_entry_widgets();
3350 return moz_gtk_entry_paint(drawable, rect, cliprect, state,
3351 gComboBoxEntryTextareaWidget, direction);
3352 break;
3353 case MOZ_GTK_CHECKBUTTON_CONTAINER:
3354 case MOZ_GTK_RADIOBUTTON_CONTAINER:
3355 return moz_gtk_container_paint(drawable, rect, cliprect, state,
3356 (widget == MOZ_GTK_RADIOBUTTON_CONTAINER),
3357 direction);
3358 break;
3359 case MOZ_GTK_CHECKBUTTON_LABEL:
3360 case MOZ_GTK_RADIOBUTTON_LABEL:
3361 return moz_gtk_toggle_label_paint(drawable, rect, cliprect, state,
3362 (widget == MOZ_GTK_RADIOBUTTON_LABEL),
3363 direction);
3364 break;
3365 case MOZ_GTK_TOOLBAR:
3366 return moz_gtk_toolbar_paint(drawable, rect, cliprect, direction);
3367 break;
3368 case MOZ_GTK_TOOLBAR_SEPARATOR:
3369 return moz_gtk_toolbar_separator_paint(drawable, rect, cliprect,
3370 direction);
3371 break;
3372 case MOZ_GTK_TOOLTIP:
3373 return moz_gtk_tooltip_paint(drawable, rect, cliprect, direction);
3374 break;
3375 case MOZ_GTK_FRAME:
3376 return moz_gtk_frame_paint(drawable, rect, cliprect, direction);
3377 break;
3378 case MOZ_GTK_RESIZER:
3379 return moz_gtk_resizer_paint(drawable, rect, cliprect, state,
3380 direction);
3381 break;
3382 case MOZ_GTK_PROGRESSBAR:
3383 return moz_gtk_progressbar_paint(drawable, rect, cliprect, direction);
3384 break;
3385 case MOZ_GTK_PROGRESS_CHUNK:
3386 case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
3387 case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
3388 return moz_gtk_progress_chunk_paint(drawable, rect, cliprect,
3389 direction, widget);
3390 break;
3391 case MOZ_GTK_TAB_TOP:
3392 case MOZ_GTK_TAB_BOTTOM:
3393 return moz_gtk_tab_paint(drawable, rect, cliprect, state,
3394 (GtkTabFlags) flags, direction, widget);
3395 break;
3396 case MOZ_GTK_TABPANELS:
3397 return moz_gtk_tabpanels_paint(drawable, rect, cliprect, direction);
3398 break;
3399 case MOZ_GTK_TAB_SCROLLARROW:
3400 return moz_gtk_tab_scroll_arrow_paint(drawable, rect, cliprect, state,
3401 (GtkArrowType) flags, direction);
3402 break;
3403 case MOZ_GTK_MENUBAR:
3404 return moz_gtk_menu_bar_paint(drawable, rect, cliprect, direction);
3405 break;
3406 case MOZ_GTK_MENUPOPUP:
3407 return moz_gtk_menu_popup_paint(drawable, rect, cliprect, direction);
3408 break;
3409 case MOZ_GTK_MENUSEPARATOR:
3410 return moz_gtk_menu_separator_paint(drawable, rect, cliprect,
3411 direction);
3412 break;
3413 case MOZ_GTK_MENUBARITEM:
3414 case MOZ_GTK_MENUITEM:
3415 return moz_gtk_menu_item_paint(widget, drawable, rect, cliprect, state,
3416 direction);
3417 break;
3418 case MOZ_GTK_MENUARROW:
3419 return moz_gtk_menu_arrow_paint(drawable, rect, cliprect, state,
3420 direction);
3421 break;
3422 case MOZ_GTK_TOOLBARBUTTON_ARROW:
3423 return moz_gtk_arrow_paint(drawable, rect, cliprect, state,
3424 (GtkArrowType) flags, direction);
3425 break;
3426 case MOZ_GTK_CHECKMENUITEM:
3427 case MOZ_GTK_RADIOMENUITEM:
3428 return moz_gtk_check_menu_item_paint(drawable, rect, cliprect, state,
3429 (gboolean) flags,
3430 (widget == MOZ_GTK_RADIOMENUITEM),
3431 direction);
3432 break;
3433 case MOZ_GTK_SPLITTER_HORIZONTAL:
3434 return moz_gtk_vpaned_paint(drawable, rect, cliprect, state);
3435 break;
3436 case MOZ_GTK_SPLITTER_VERTICAL:
3437 return moz_gtk_hpaned_paint(drawable, rect, cliprect, state);
3438 break;
3439 case MOZ_GTK_WINDOW:
3440 return moz_gtk_window_paint(drawable, rect, cliprect, direction);
3441 break;
3442 default:
3443 g_warning("Unknown widget type: %d", widget);
3446 return MOZ_GTK_UNKNOWN_WIDGET;
3449 GtkWidget* moz_gtk_get_scrollbar_widget(void)
3451 MOZ_ASSERT(is_initialized, "Forgot to call moz_gtk_init()");
3452 ensure_scrollbar_widget();
3453 return gHorizScrollbarWidget;
3456 gboolean moz_gtk_has_scrollbar_buttons(void)
3458 gboolean backward, forward, secondary_backward, secondary_forward;
3459 MOZ_ASSERT(is_initialized, "Forgot to call moz_gtk_init()");
3460 ensure_scrollbar_widget();
3461 gtk_widget_style_get (gHorizScrollbarWidget,
3462 "has-backward-stepper", &backward,
3463 "has-forward-stepper", &forward,
3464 "has-secondary-backward-stepper", &secondary_backward,
3465 "has-secondary-forward-stepper", &secondary_forward,
3466 NULL);
3467 return backward | forward | secondary_forward | secondary_forward;
3470 gint
3471 moz_gtk_shutdown()
3473 GtkWidgetClass *entry_class;
3475 if (gTooltipWidget)
3476 gtk_widget_destroy(gTooltipWidget);
3477 /* This will destroy all of our widgets */
3478 if (gProtoWindow)
3479 gtk_widget_destroy(gProtoWindow);
3481 gProtoWindow = NULL;
3482 gProtoLayout = NULL;
3483 gButtonWidget = NULL;
3484 gToggleButtonWidget = NULL;
3485 gButtonArrowWidget = NULL;
3486 gCheckboxWidget = NULL;
3487 gRadiobuttonWidget = NULL;
3488 gHorizScrollbarWidget = NULL;
3489 gVertScrollbarWidget = NULL;
3490 gSpinWidget = NULL;
3491 gHScaleWidget = NULL;
3492 gVScaleWidget = NULL;
3493 gEntryWidget = NULL;
3494 gComboBoxWidget = NULL;
3495 gComboBoxButtonWidget = NULL;
3496 gComboBoxSeparatorWidget = NULL;
3497 gComboBoxArrowWidget = NULL;
3498 gComboBoxEntryWidget = NULL;
3499 gComboBoxEntryButtonWidget = NULL;
3500 gComboBoxEntryArrowWidget = NULL;
3501 gComboBoxEntryTextareaWidget = NULL;
3502 gHandleBoxWidget = NULL;
3503 gToolbarWidget = NULL;
3504 gStatusbarWidget = NULL;
3505 gFrameWidget = NULL;
3506 gProgressWidget = NULL;
3507 gTabWidget = NULL;
3508 gTooltipWidget = NULL;
3509 gMenuBarWidget = NULL;
3510 gMenuBarItemWidget = NULL;
3511 gMenuPopupWidget = NULL;
3512 gMenuItemWidget = NULL;
3513 gCheckMenuItemWidget = NULL;
3514 gTreeViewWidget = NULL;
3515 gMiddleTreeViewColumn = NULL;
3516 gTreeHeaderCellWidget = NULL;
3517 gTreeHeaderSortArrowWidget = NULL;
3518 gExpanderWidget = NULL;
3519 gToolbarSeparatorWidget = NULL;
3520 gMenuSeparatorWidget = NULL;
3521 gHPanedWidget = NULL;
3522 gVPanedWidget = NULL;
3523 gScrolledWindowWidget = NULL;
3525 entry_class = g_type_class_peek(GTK_TYPE_ENTRY);
3526 g_type_class_unref(entry_class);
3528 is_initialized = FALSE;
3530 return MOZ_GTK_SUCCESS;