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