widget/gtk/gtk3drawing.c

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:d9984a2f0f9f
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/. */
5
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 */
10
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"
17
18 #include <math.h>
19
20 static GtkWidget* gProtoWindow;
21 static GtkWidget* gProtoLayout;
22 static GtkWidget* gButtonWidget;
23 static GtkWidget* gToggleMenuButtonWidget;
24 static GtkWidget* gButtonArrowWidget;
25 static GtkWidget* gCheckboxWidget;
26 static GtkWidget* gRadiobuttonWidget;
27 static GtkWidget* gHorizScrollbarWidget;
28 static GtkWidget* gVertScrollbarWidget;
29 static GtkWidget* gSpinWidget;
30 static GtkWidget* gHScaleWidget;
31 static GtkWidget* gVScaleWidget;
32 static GtkWidget* gEntryWidget;
33 static GtkWidget* gComboBoxWidget;
34 static GtkWidget* gComboBoxButtonWidget;
35 static GtkWidget* gComboBoxArrowWidget;
36 static GtkWidget* gComboBoxSeparatorWidget;
37 static GtkWidget* gComboBoxEntryWidget;
38 static GtkWidget* gComboBoxEntryTextareaWidget;
39 static GtkWidget* gComboBoxEntryButtonWidget;
40 static GtkWidget* gComboBoxEntryArrowWidget;
41 static GtkWidget* gHandleBoxWidget;
42 static GtkWidget* gToolbarWidget;
43 static GtkWidget* gFrameWidget;
44 static GtkWidget* gStatusbarWidget;
45 static GtkWidget* gProgressWidget;
46 static GtkWidget* gTabWidget;
47 static GtkWidget* gTooltipWidget;
48 static GtkWidget* gMenuBarWidget;
49 static GtkWidget* gMenuBarItemWidget;
50 static GtkWidget* gMenuPopupWidget;
51 static GtkWidget* gMenuItemWidget;
52 static GtkWidget* gImageMenuItemWidget;
53 static GtkWidget* gCheckMenuItemWidget;
54 static GtkWidget* gTreeViewWidget;
55 static GtkTreeViewColumn* gMiddleTreeViewColumn;
56 static GtkWidget* gTreeHeaderCellWidget;
57 static GtkWidget* gTreeHeaderSortArrowWidget;
58 static GtkWidget* gExpanderWidget;
59 static GtkWidget* gToolbarSeparatorWidget;
60 static GtkWidget* gMenuSeparatorWidget;
61 static GtkWidget* gHPanedWidget;
62 static GtkWidget* gVPanedWidget;
63 static GtkWidget* gScrolledWindowWidget;
64
65 static style_prop_t style_prop_func;
66 static gboolean have_arrow_scaling;
67 static gboolean is_initialized;
68
69 #define ARROW_UP 0
70 #define ARROW_DOWN G_PI
71 #define ARROW_RIGHT G_PI_2
72 #define ARROW_LEFT (G_PI+G_PI_2)
73
74 static GtkStateFlags
75 GetStateFlagsFromGtkWidgetState(GtkWidgetState* state)
76 {
77 GtkStateFlags stateFlags = GTK_STATE_FLAG_NORMAL;
78
79 if (state->disabled)
80 stateFlags = GTK_STATE_FLAG_INSENSITIVE;
81 else {
82 if (state->depressed || state->active)
83 stateFlags |= GTK_STATE_FLAG_ACTIVE;
84 if (state->inHover)
85 stateFlags |= GTK_STATE_FLAG_PRELIGHT;
86 if (state->focused)
87 stateFlags |= GTK_STATE_FLAG_FOCUSED;
88 }
89
90 return stateFlags;
91 }
92
93 /* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine
94 that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific
95 things they may want to do. */
96 static void
97 moz_gtk_set_widget_name(GtkWidget* widget)
98 {
99 gtk_widget_set_name(widget, "MozillaGtkWidget");
100 }
101
102 gint
103 moz_gtk_enable_style_props(style_prop_t styleGetProp)
104 {
105 style_prop_func = styleGetProp;
106 return MOZ_GTK_SUCCESS;
107 }
108
109 static gint
110 ensure_window_widget()
111 {
112 if (!gProtoWindow) {
113 gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
114 gtk_widget_realize(gProtoWindow);
115 moz_gtk_set_widget_name(gProtoWindow);
116 }
117 return MOZ_GTK_SUCCESS;
118 }
119
120 static gint
121 setup_widget_prototype(GtkWidget* widget)
122 {
123 ensure_window_widget();
124 if (!gProtoLayout) {
125 gProtoLayout = gtk_fixed_new();
126 gtk_container_add(GTK_CONTAINER(gProtoWindow), gProtoLayout);
127 }
128
129 gtk_container_add(GTK_CONTAINER(gProtoLayout), widget);
130 gtk_widget_realize(widget);
131 return MOZ_GTK_SUCCESS;
132 }
133
134 static gint
135 ensure_button_widget()
136 {
137 if (!gButtonWidget) {
138 gButtonWidget = gtk_button_new_with_label("M");
139 setup_widget_prototype(gButtonWidget);
140 }
141 return MOZ_GTK_SUCCESS;
142 }
143
144 static gint
145 ensure_hpaned_widget()
146 {
147 if (!gHPanedWidget) {
148 gHPanedWidget = gtk_hpaned_new();
149 setup_widget_prototype(gHPanedWidget);
150 }
151 return MOZ_GTK_SUCCESS;
152 }
153
154 static gint
155 ensure_vpaned_widget()
156 {
157 if (!gVPanedWidget) {
158 gVPanedWidget = gtk_vpaned_new();
159 setup_widget_prototype(gVPanedWidget);
160 }
161 return MOZ_GTK_SUCCESS;
162 }
163
164 static gint
165 ensure_toggle_menu_button_widget()
166 {
167 if (!gToggleMenuButtonWidget) {
168 gToggleMenuButtonWidget = gtk_menu_button_new();
169 setup_widget_prototype(gToggleMenuButtonWidget);
170 }
171 return MOZ_GTK_SUCCESS;
172 }
173
174 static gint
175 ensure_button_arrow_widget()
176 {
177 if (!gButtonArrowWidget) {
178 ensure_toggle_menu_button_widget();
179 gButtonArrowWidget = gtk_bin_get_child(GTK_BIN(gToggleMenuButtonWidget));
180 }
181 return MOZ_GTK_SUCCESS;
182 }
183
184 static gint
185 ensure_checkbox_widget()
186 {
187 if (!gCheckboxWidget) {
188 gCheckboxWidget = gtk_check_button_new_with_label("M");
189 setup_widget_prototype(gCheckboxWidget);
190 }
191 return MOZ_GTK_SUCCESS;
192 }
193
194 static gint
195 ensure_radiobutton_widget()
196 {
197 if (!gRadiobuttonWidget) {
198 gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M");
199 setup_widget_prototype(gRadiobuttonWidget);
200 }
201 return MOZ_GTK_SUCCESS;
202 }
203
204 static gint
205 ensure_scrollbar_widget()
206 {
207 if (!gVertScrollbarWidget) {
208 gVertScrollbarWidget = gtk_vscrollbar_new(NULL);
209 setup_widget_prototype(gVertScrollbarWidget);
210 }
211 if (!gHorizScrollbarWidget) {
212 gHorizScrollbarWidget = gtk_hscrollbar_new(NULL);
213 setup_widget_prototype(gHorizScrollbarWidget);
214 }
215 return MOZ_GTK_SUCCESS;
216 }
217
218 static gint
219 ensure_spin_widget()
220 {
221 if (!gSpinWidget) {
222 gSpinWidget = gtk_spin_button_new(NULL, 1, 0);
223 setup_widget_prototype(gSpinWidget);
224 }
225 return MOZ_GTK_SUCCESS;
226 }
227
228 static gint
229 ensure_scale_widget()
230 {
231 if (!gHScaleWidget) {
232 gHScaleWidget = gtk_hscale_new(NULL);
233 setup_widget_prototype(gHScaleWidget);
234 }
235 if (!gVScaleWidget) {
236 gVScaleWidget = gtk_vscale_new(NULL);
237 setup_widget_prototype(gVScaleWidget);
238 }
239 return MOZ_GTK_SUCCESS;
240 }
241
242 static gint
243 ensure_entry_widget()
244 {
245 if (!gEntryWidget) {
246 gEntryWidget = gtk_entry_new();
247 setup_widget_prototype(gEntryWidget);
248 }
249 return MOZ_GTK_SUCCESS;
250 }
251
252 /* We need to have pointers to the inner widgets (button, separator, arrow)
253 * of the ComboBox to get the correct rendering from theme engines which
254 * special cases their look. Since the inner layout can change, we ask GTK
255 * to NULL our pointers when they are about to become invalid because the
256 * corresponding widgets don't exist anymore. It's the role of
257 * g_object_add_weak_pointer().
258 * Note that if we don't find the inner widgets (which shouldn't happen), we
259 * fallback to use generic "non-inner" widgets, and they don't need that kind
260 * of weak pointer since they are explicit children of gProtoWindow and as
261 * such GTK holds a strong reference to them. */
262 static void
263 moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data)
264 {
265 if (GTK_IS_TOGGLE_BUTTON(widget)) {
266 gComboBoxButtonWidget = widget;
267 g_object_add_weak_pointer(G_OBJECT(widget),
268 (gpointer) &gComboBoxButtonWidget);
269 gtk_widget_realize(widget);
270 }
271 }
272
273 static void
274 moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget,
275 gpointer client_data)
276 {
277 if (GTK_IS_SEPARATOR(widget)) {
278 gComboBoxSeparatorWidget = widget;
279 g_object_add_weak_pointer(G_OBJECT(widget),
280 (gpointer) &gComboBoxSeparatorWidget);
281 } else if (GTK_IS_ARROW(widget)) {
282 gComboBoxArrowWidget = widget;
283 g_object_add_weak_pointer(G_OBJECT(widget),
284 (gpointer) &gComboBoxArrowWidget);
285 } else
286 return;
287 gtk_widget_realize(widget);
288 }
289
290 static gint
291 ensure_combo_box_widgets()
292 {
293 GtkWidget* buttonChild;
294
295 if (gComboBoxButtonWidget && gComboBoxArrowWidget)
296 return MOZ_GTK_SUCCESS;
297
298 /* Create a ComboBox if needed */
299 if (!gComboBoxWidget) {
300 gComboBoxWidget = gtk_combo_box_new();
301 setup_widget_prototype(gComboBoxWidget);
302 }
303
304 /* Get its inner Button */
305 gtk_container_forall(GTK_CONTAINER(gComboBoxWidget),
306 moz_gtk_get_combo_box_inner_button,
307 NULL);
308
309 if (gComboBoxButtonWidget) {
310 /* Get the widgets inside the Button */
311 buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxButtonWidget));
312 if (GTK_IS_BOX(buttonChild)) {
313 /* appears-as-list = FALSE, cell-view = TRUE; the button
314 * contains an hbox. This hbox is there because the ComboBox
315 * needs to place a cell renderer, a separator, and an arrow in
316 * the button when appears-as-list is FALSE. */
317 gtk_container_forall(GTK_CONTAINER(buttonChild),
318 moz_gtk_get_combo_box_button_inner_widgets,
319 NULL);
320 } else if(GTK_IS_ARROW(buttonChild)) {
321 /* appears-as-list = TRUE, or cell-view = FALSE;
322 * the button only contains an arrow */
323 gComboBoxArrowWidget = buttonChild;
324 g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
325 &gComboBoxArrowWidget);
326 gtk_widget_realize(gComboBoxArrowWidget);
327 }
328 } else {
329 /* Shouldn't be reached with current internal gtk implementation; we
330 * use a generic toggle button as last resort fallback to avoid
331 * crashing. */
332 ensure_toggle_menu_button_widget();
333 gComboBoxButtonWidget = gToggleMenuButtonWidget;
334 }
335
336 if (!gComboBoxArrowWidget) {
337 /* Shouldn't be reached with current internal gtk implementation;
338 * we gButtonArrowWidget as last resort fallback to avoid
339 * crashing. */
340 ensure_button_arrow_widget();
341 gComboBoxArrowWidget = gButtonArrowWidget;
342 }
343
344 /* We don't test the validity of gComboBoxSeparatorWidget since there
345 * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it
346 * is invalid we just won't paint it. */
347
348 return MOZ_GTK_SUCCESS;
349 }
350
351 /* We need to have pointers to the inner widgets (entry, button, arrow) of
352 * the ComboBoxEntry to get the correct rendering from theme engines which
353 * special cases their look. Since the inner layout can change, we ask GTK
354 * to NULL our pointers when they are about to become invalid because the
355 * corresponding widgets don't exist anymore. It's the role of
356 * g_object_add_weak_pointer().
357 * Note that if we don't find the inner widgets (which shouldn't happen), we
358 * fallback to use generic "non-inner" widgets, and they don't need that kind
359 * of weak pointer since they are explicit children of gProtoWindow and as
360 * such GTK holds a strong reference to them. */
361 static void
362 moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget,
363 gpointer client_data)
364 {
365 if (GTK_IS_TOGGLE_BUTTON(widget)) {
366 gComboBoxEntryButtonWidget = widget;
367 g_object_add_weak_pointer(G_OBJECT(widget),
368 (gpointer) &gComboBoxEntryButtonWidget);
369 } else if (GTK_IS_ENTRY(widget)) {
370 gComboBoxEntryTextareaWidget = widget;
371 g_object_add_weak_pointer(G_OBJECT(widget),
372 (gpointer) &gComboBoxEntryTextareaWidget);
373 } else
374 return;
375 gtk_widget_realize(widget);
376 }
377
378 static void
379 moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data)
380 {
381 if (GTK_IS_ARROW(widget)) {
382 gComboBoxEntryArrowWidget = widget;
383 g_object_add_weak_pointer(G_OBJECT(widget),
384 (gpointer) &gComboBoxEntryArrowWidget);
385 gtk_widget_realize(widget);
386 }
387 }
388
389 static gint
390 ensure_combo_box_entry_widgets()
391 {
392 GtkWidget* buttonChild;
393
394 if (gComboBoxEntryTextareaWidget &&
395 gComboBoxEntryButtonWidget &&
396 gComboBoxEntryArrowWidget)
397 return MOZ_GTK_SUCCESS;
398
399 /* Create a ComboBoxEntry if needed */
400 if (!gComboBoxEntryWidget) {
401 gComboBoxEntryWidget = gtk_combo_box_new_with_entry();
402 setup_widget_prototype(gComboBoxEntryWidget);
403 }
404
405 /* Get its inner Entry and Button */
406 gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget),
407 moz_gtk_get_combo_box_entry_inner_widgets,
408 NULL);
409
410 if (!gComboBoxEntryTextareaWidget) {
411 ensure_entry_widget();
412 gComboBoxEntryTextareaWidget = gEntryWidget;
413 }
414
415 if (gComboBoxEntryButtonWidget) {
416 /* Get the Arrow inside the Button */
417 buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxEntryButtonWidget));
418 if (GTK_IS_BOX(buttonChild)) {
419 /* appears-as-list = FALSE, cell-view = TRUE; the button
420 * contains an hbox. This hbox is there because the ComboBox
421 * needs to place a cell renderer, a separator, and an arrow in
422 * the button when appears-as-list is FALSE. */
423 gtk_container_forall(GTK_CONTAINER(buttonChild),
424 moz_gtk_get_combo_box_entry_arrow,
425 NULL);
426 } else if(GTK_IS_ARROW(buttonChild)) {
427 /* appears-as-list = TRUE, or cell-view = FALSE;
428 * the button only contains an arrow */
429 gComboBoxEntryArrowWidget = buttonChild;
430 g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
431 &gComboBoxEntryArrowWidget);
432 gtk_widget_realize(gComboBoxEntryArrowWidget);
433 }
434 } else {
435 /* Shouldn't be reached with current internal gtk implementation;
436 * we use a generic toggle button as last resort fallback to avoid
437 * crashing. */
438 ensure_toggle_menu_button_widget();
439 gComboBoxEntryButtonWidget = gToggleMenuButtonWidget;
440 }
441
442 if (!gComboBoxEntryArrowWidget) {
443 /* Shouldn't be reached with current internal gtk implementation;
444 * we gButtonArrowWidget as last resort fallback to avoid
445 * crashing. */
446 ensure_button_arrow_widget();
447 gComboBoxEntryArrowWidget = gButtonArrowWidget;
448 }
449
450 return MOZ_GTK_SUCCESS;
451 }
452
453
454 static gint
455 ensure_handlebox_widget()
456 {
457 if (!gHandleBoxWidget) {
458 gHandleBoxWidget = gtk_handle_box_new();
459 setup_widget_prototype(gHandleBoxWidget);
460 }
461 return MOZ_GTK_SUCCESS;
462 }
463
464 static gint
465 ensure_toolbar_widget()
466 {
467 if (!gToolbarWidget) {
468 ensure_handlebox_widget();
469 gToolbarWidget = gtk_toolbar_new();
470 gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget);
471 gtk_widget_realize(gToolbarWidget);
472 }
473 return MOZ_GTK_SUCCESS;
474 }
475
476 static gint
477 ensure_toolbar_separator_widget()
478 {
479 if (!gToolbarSeparatorWidget) {
480 ensure_toolbar_widget();
481 gToolbarSeparatorWidget = GTK_WIDGET(gtk_separator_tool_item_new());
482 setup_widget_prototype(gToolbarSeparatorWidget);
483 }
484 return MOZ_GTK_SUCCESS;
485 }
486
487 static gint
488 ensure_tooltip_widget()
489 {
490 if (!gTooltipWidget) {
491 gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP);
492 gtk_widget_realize(gTooltipWidget);
493 moz_gtk_set_widget_name(gTooltipWidget);
494 }
495 return MOZ_GTK_SUCCESS;
496 }
497
498 static gint
499 ensure_tab_widget()
500 {
501 if (!gTabWidget) {
502 gTabWidget = gtk_notebook_new();
503 setup_widget_prototype(gTabWidget);
504 }
505 return MOZ_GTK_SUCCESS;
506 }
507
508 static gint
509 ensure_progress_widget()
510 {
511 if (!gProgressWidget) {
512 gProgressWidget = gtk_progress_bar_new();
513 setup_widget_prototype(gProgressWidget);
514 }
515 return MOZ_GTK_SUCCESS;
516 }
517
518 static gint
519 ensure_statusbar_widget()
520 {
521 if (!gStatusbarWidget) {
522 gStatusbarWidget = gtk_statusbar_new();
523 setup_widget_prototype(gStatusbarWidget);
524 }
525 return MOZ_GTK_SUCCESS;
526 }
527
528 static gint
529 ensure_frame_widget()
530 {
531 if (!gFrameWidget) {
532 ensure_statusbar_widget();
533 gFrameWidget = gtk_frame_new(NULL);
534 gtk_container_add(GTK_CONTAINER(gStatusbarWidget), gFrameWidget);
535 gtk_widget_realize(gFrameWidget);
536 }
537 return MOZ_GTK_SUCCESS;
538 }
539
540 static gint
541 ensure_menu_bar_widget()
542 {
543 if (!gMenuBarWidget) {
544 gMenuBarWidget = gtk_menu_bar_new();
545 setup_widget_prototype(gMenuBarWidget);
546 }
547 return MOZ_GTK_SUCCESS;
548 }
549
550 static gint
551 ensure_menu_bar_item_widget()
552 {
553 if (!gMenuBarItemWidget) {
554 ensure_menu_bar_widget();
555 gMenuBarItemWidget = gtk_menu_item_new();
556 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget),
557 gMenuBarItemWidget);
558 gtk_widget_realize(gMenuBarItemWidget);
559 }
560 return MOZ_GTK_SUCCESS;
561 }
562
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 }
573 return MOZ_GTK_SUCCESS;
574 }
575
576 static gint
577 ensure_menu_item_widget()
578 {
579 if (!gMenuItemWidget) {
580 ensure_menu_popup_widget();
581 gMenuItemWidget = gtk_menu_item_new_with_label("M");
582 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
583 gMenuItemWidget);
584 gtk_widget_realize(gMenuItemWidget);
585 }
586 return MOZ_GTK_SUCCESS;
587 }
588
589 static gint
590 ensure_image_menu_item_widget()
591 {
592 if (!gImageMenuItemWidget) {
593 ensure_menu_popup_widget();
594 gImageMenuItemWidget = gtk_image_menu_item_new();
595 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
596 gImageMenuItemWidget);
597 gtk_widget_realize(gImageMenuItemWidget);
598 }
599 return MOZ_GTK_SUCCESS;
600 }
601
602 static gint
603 ensure_menu_separator_widget()
604 {
605 if (!gMenuSeparatorWidget) {
606 ensure_menu_popup_widget();
607 gMenuSeparatorWidget = gtk_separator_menu_item_new();
608 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
609 gMenuSeparatorWidget);
610 gtk_widget_realize(gMenuSeparatorWidget);
611 }
612 return MOZ_GTK_SUCCESS;
613 }
614
615 static gint
616 ensure_check_menu_item_widget()
617 {
618 if (!gCheckMenuItemWidget) {
619 ensure_menu_popup_widget();
620 gCheckMenuItemWidget = gtk_check_menu_item_new_with_label("M");
621 gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
622 gCheckMenuItemWidget);
623 gtk_widget_realize(gCheckMenuItemWidget);
624 }
625 return MOZ_GTK_SUCCESS;
626 }
627
628 static gint
629 ensure_tree_view_widget()
630 {
631 if (!gTreeViewWidget) {
632 gTreeViewWidget = gtk_tree_view_new();
633 setup_widget_prototype(gTreeViewWidget);
634 }
635 return MOZ_GTK_SUCCESS;
636 }
637
638 static gint
639 ensure_tree_header_cell_widget()
640 {
641 if(!gTreeHeaderCellWidget) {
642 /*
643 * Some GTK engines paint the first and last cell
644 * of a TreeView header with a highlight.
645 * Since we do not know where our widget will be relative
646 * to the other buttons in the TreeView header, we must
647 * paint it as a button that is between two others,
648 * thus ensuring it is neither the first or last button
649 * in the header.
650 * GTK doesn't give us a way to do this explicitly,
651 * so we must paint with a button that is between two
652 * others.
653 */
654
655 GtkTreeViewColumn* firstTreeViewColumn;
656 GtkTreeViewColumn* lastTreeViewColumn;
657
658 ensure_tree_view_widget();
659
660 /* Create and append our three columns */
661 firstTreeViewColumn = gtk_tree_view_column_new();
662 gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
663 gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn);
664
665 gMiddleTreeViewColumn = gtk_tree_view_column_new();
666 gtk_tree_view_column_set_title(gMiddleTreeViewColumn, "M");
667 gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget),
668 gMiddleTreeViewColumn);
669
670 lastTreeViewColumn = gtk_tree_view_column_new();
671 gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
672 gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn);
673
674 /* Use the middle column's header for our button */
675 gTreeHeaderCellWidget = gtk_tree_view_column_get_button(gMiddleTreeViewColumn);
676 /* TODO, but it can't be NULL */
677 gTreeHeaderSortArrowWidget = gtk_button_new();
678 }
679 return MOZ_GTK_SUCCESS;
680 }
681
682 static gint
683 ensure_expander_widget()
684 {
685 if (!gExpanderWidget) {
686 gExpanderWidget = gtk_expander_new("M");
687 setup_widget_prototype(gExpanderWidget);
688 }
689 return MOZ_GTK_SUCCESS;
690 }
691
692 static gint
693 ensure_scrolled_window_widget()
694 {
695 if (!gScrolledWindowWidget) {
696 gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL);
697 setup_widget_prototype(gScrolledWindowWidget);
698 }
699 return MOZ_GTK_SUCCESS;
700 }
701
702 gint
703 moz_gtk_init()
704 {
705 GtkWidgetClass *entry_class;
706
707 if (is_initialized)
708 return MOZ_GTK_SUCCESS;
709
710 is_initialized = TRUE;
711 have_arrow_scaling = (gtk_major_version > 2 ||
712 (gtk_major_version == 2 && gtk_minor_version >= 12));
713
714 /* Add style property to GtkEntry.
715 * Adding the style property to the normal GtkEntry class means that it
716 * will work without issues inside GtkComboBox and for Spinbuttons. */
717 entry_class = g_type_class_ref(GTK_TYPE_ENTRY);
718
719 return MOZ_GTK_SUCCESS;
720 }
721
722 gint
723 moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing)
724 {
725 ensure_checkbox_widget();
726
727 gtk_widget_style_get (gCheckboxWidget,
728 "indicator_size", indicator_size,
729 "indicator_spacing", indicator_spacing,
730 NULL);
731
732 return MOZ_GTK_SUCCESS;
733 }
734
735 gint
736 moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing)
737 {
738 ensure_radiobutton_widget();
739
740 gtk_widget_style_get (gRadiobuttonWidget,
741 "indicator_size", indicator_size,
742 "indicator_spacing", indicator_spacing,
743 NULL);
744
745 return MOZ_GTK_SUCCESS;
746 }
747
748 gint
749 moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus,
750 gint* focus_width, gint* focus_pad)
751 {
752 gtk_widget_style_get (widget,
753 "interior-focus", interior_focus,
754 "focus-line-width", focus_width,
755 "focus-padding", focus_pad,
756 NULL);
757
758 return MOZ_GTK_SUCCESS;
759 }
760
761 gint
762 moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding)
763 {
764 ensure_menu_item_widget();
765
766 gtk_widget_style_get (gMenuItemWidget,
767 "horizontal-padding", horizontal_padding,
768 NULL);
769
770 return MOZ_GTK_SUCCESS;
771 }
772
773 gint
774 moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding)
775 {
776 ensure_check_menu_item_widget();
777
778 gtk_widget_style_get (gCheckMenuItemWidget,
779 "horizontal-padding", horizontal_padding,
780 NULL);
781
782 return MOZ_GTK_SUCCESS;
783 }
784
785 gint
786 moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left,
787 gint* border_bottom, gint* border_right)
788 {
789 GtkBorder* default_outside_border;
790
791 ensure_button_widget();
792 gtk_widget_style_get(gButtonWidget,
793 "default-outside-border", &default_outside_border,
794 NULL);
795
796 if (default_outside_border) {
797 *border_top = default_outside_border->top;
798 *border_left = default_outside_border->left;
799 *border_bottom = default_outside_border->bottom;
800 *border_right = default_outside_border->right;
801 gtk_border_free(default_outside_border);
802 } else {
803 *border_top = *border_left = *border_bottom = *border_right = 0;
804 }
805 return MOZ_GTK_SUCCESS;
806 }
807
808 static gint
809 moz_gtk_button_get_default_border(gint* border_top, gint* border_left,
810 gint* border_bottom, gint* border_right)
811 {
812 GtkBorder* default_border;
813
814 ensure_button_widget();
815 gtk_widget_style_get(gButtonWidget,
816 "default-border", &default_border,
817 NULL);
818
819 if (default_border) {
820 *border_top = default_border->top;
821 *border_left = default_border->left;
822 *border_bottom = default_border->bottom;
823 *border_right = default_border->right;
824 gtk_border_free(default_border);
825 } else {
826 /* see gtkbutton.c */
827 *border_top = *border_left = *border_bottom = *border_right = 1;
828 }
829 return MOZ_GTK_SUCCESS;
830 }
831
832 gint
833 moz_gtk_splitter_get_metrics(gint orientation, gint* size)
834 {
835 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
836 ensure_hpaned_widget();
837 gtk_widget_style_get(gHPanedWidget, "handle_size", size, NULL);
838 } else {
839 ensure_vpaned_widget();
840 gtk_widget_style_get(gVPanedWidget, "handle_size", size, NULL);
841 }
842 return MOZ_GTK_SUCCESS;
843 }
844
845 gint
846 moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border)
847 {
848 static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
849 GtkBorder *tmp_border;
850
851 gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL);
852
853 if (tmp_border) {
854 *inner_border = *tmp_border;
855 gtk_border_free(tmp_border);
856 }
857 else
858 *inner_border = default_inner_border;
859
860 return MOZ_GTK_SUCCESS;
861 }
862
863 static gint
864 moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect,
865 GtkWidgetState* state,
866 GtkReliefStyle relief, GtkWidget* widget,
867 GtkTextDirection direction)
868 {
869 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
870 GtkStyleContext* style = gtk_widget_get_style_context(widget);
871 gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
872
873 gboolean interior_focus;
874 gint focus_width, focus_pad;
875
876 moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad);
877 gtk_widget_set_direction(widget, direction);
878
879 if (!interior_focus && state->focused) {
880 x += focus_width + focus_pad;
881 y += focus_width + focus_pad;
882 width -= 2 * (focus_width + focus_pad);
883 height -= 2 * (focus_width + focus_pad);
884 }
885
886 gtk_style_context_save(style);
887 gtk_style_context_set_state(style, state_flags);
888
889 if (state->isDefault && relief == GTK_RELIEF_NORMAL) {
890 /* handle default borders both outside and inside the button */
891 gint default_top, default_left, default_bottom, default_right;
892 moz_gtk_button_get_default_overflow(&default_top, &default_left,
893 &default_bottom, &default_right);
894 x -= default_left;
895 y -= default_top;
896 width += default_left + default_right;
897 height += default_top + default_bottom;
898 gtk_render_background(style, cr, x, y, width, height);
899 gtk_render_frame(style, cr, x, y, width, height);
900 moz_gtk_button_get_default_border(&default_top, &default_left,
901 &default_bottom, &default_right);
902 x += default_left;
903 y += default_top;
904 width -= (default_left + default_right);
905 height -= (default_top + default_bottom);
906 }
907
908 if (relief != GTK_RELIEF_NONE || state->depressed ||
909 (state_flags & GTK_STATE_FLAG_PRELIGHT)) {
910 /* the following line can trigger an assertion (Crux theme)
911 file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
912 assertion `GDK_IS_WINDOW (window)' failed */
913 gtk_render_background(style, cr, x, y, width, height);
914 gtk_render_frame(style, cr, x, y, width, height);
915 }
916
917 if (state->focused) {
918 if (interior_focus) {
919 GtkBorder border;
920 gtk_style_context_get_border(style, state_flags, &border);
921 x += border.left + focus_pad;
922 y += border.top + focus_pad;
923 width -= 2 * (border.left + focus_pad);
924 height -= 2 * (border.top + focus_pad);
925 } else {
926 x -= focus_width + focus_pad;
927 y -= focus_width + focus_pad;
928 width += 2 * (focus_width + focus_pad);
929 height += 2 * (focus_width + focus_pad);
930 }
931
932 gtk_render_focus(style, cr, x, y, width, height);
933 }
934 gtk_style_context_restore(style);
935 return MOZ_GTK_SUCCESS;
936 }
937
938 static gint
939 moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect,
940 GtkWidgetState* state,
941 gboolean selected, gboolean inconsistent,
942 gboolean isradio, GtkTextDirection direction)
943 {
944 gint indicator_size, indicator_spacing;
945 gint x, y, width, height;
946 gint focus_x, focus_y, focus_width, focus_height;
947 GtkWidget *w;
948 GtkStyleContext *style;
949
950 if (isradio) {
951 moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
952 w = gRadiobuttonWidget;
953 } else {
954 moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
955 w = gCheckboxWidget;
956 }
957
958 // XXX we should assert rect->height >= indicator_size too
959 // after bug 369581 is fixed.
960 NS_ASSERTION(rect->width >= indicator_size,
961 "GetMinimumWidgetSize was ignored");
962
963 // Paint it center aligned in the rect.
964 x = rect->x + (rect->width - indicator_size) / 2;
965 y = rect->y + (rect->height - indicator_size) / 2;
966 width = indicator_size;
967 height = indicator_size;
968
969 focus_x = x - indicator_spacing;
970 focus_y = y - indicator_spacing;
971 focus_width = width + 2 * indicator_spacing;
972 focus_height = height + 2 * indicator_spacing;
973
974 style = gtk_widget_get_style_context(w);
975
976 gtk_widget_set_sensitive(w, !state->disabled);
977 gtk_widget_set_direction(w, direction);
978 gtk_style_context_save(style);
979
980 if (isradio) {
981 gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO);
982 gtk_style_context_set_state(style, selected ? GTK_STATE_FLAG_ACTIVE :
983 GTK_STATE_FLAG_NORMAL);
984 gtk_render_option(style, cr, x, y, width, height);
985 if (state->focused) {
986 gtk_render_focus(style, cr, focus_x, focus_y,
987 focus_width, focus_height);
988 }
989 }
990 else {
991 /*
992 * 'indeterminate' type on checkboxes. In GTK, the shadow type
993 * must also be changed for the state to be drawn.
994 */
995 gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK);
996 if (inconsistent) {
997 gtk_style_context_set_state(style, GTK_STATE_FLAG_INCONSISTENT);
998 gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE);
999 } else if (selected) {
1000 gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE);
1001 } else {
1002 gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE);
1003 }
1004 gtk_render_check(style, cr, x, y, width, height);
1005 if (state->focused) {
1006 gtk_render_focus(style, cr,
1007 focus_x, focus_y, focus_width, focus_height);
1008 }
1009 }
1010 gtk_style_context_restore(style);
1011
1012 return MOZ_GTK_SUCCESS;
1013 }
1014
1015 static gint
1016 calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect,
1017 GdkRectangle* inner_rect,
1018 GtkTextDirection direction,
1019 gboolean ignore_focus)
1020 {
1021 GtkBorder inner_border;
1022 gboolean interior_focus;
1023 gint focus_width, focus_pad;
1024 GtkStyleContext* style;
1025 GtkBorder border;
1026
1027 style = gtk_widget_get_style_context(button);
1028
1029 /* This mirrors gtkbutton's child positioning */
1030 moz_gtk_button_get_inner_border(button, &inner_border);
1031 moz_gtk_widget_get_focus(button, &interior_focus,
1032 &focus_width, &focus_pad);
1033
1034 if (ignore_focus)
1035 focus_width = focus_pad = 0;
1036
1037 gtk_style_context_get_border(style, 0, &border);
1038
1039 inner_rect->x = rect->x + border.left + focus_width + focus_pad;
1040 inner_rect->x += direction == GTK_TEXT_DIR_LTR ?
1041 inner_border.left : inner_border.right;
1042 inner_rect->y = rect->y + inner_border.top + border.top +
1043 focus_width + focus_pad;
1044 inner_rect->width = MAX(1, rect->width - inner_border.left -
1045 inner_border.right - (border.left + focus_pad + focus_width) * 2);
1046 inner_rect->height = MAX(1, rect->height - inner_border.top -
1047 inner_border.bottom - (border.top + focus_pad + focus_width) * 2);
1048
1049 return MOZ_GTK_SUCCESS;
1050 }
1051
1052
1053 static gint
1054 calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect,
1055 GdkRectangle* arrow_rect, GtkTextDirection direction)
1056 {
1057 /* defined in gtkarrow.c */
1058 gfloat arrow_scaling = 0.7;
1059 gfloat xalign, xpad;
1060 gint extent;
1061 gint mxpad, mypad;
1062 gfloat mxalign, myalign;
1063 GtkMisc* misc = GTK_MISC(arrow);
1064
1065 if (have_arrow_scaling)
1066 gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL);
1067
1068 gtk_misc_get_padding(misc, &mxpad, &mypad);
1069 extent = MIN((rect->width - mxpad * 2),
1070 (rect->height - mypad * 2)) * arrow_scaling;
1071
1072 gtk_misc_get_alignment(misc, &mxalign, &myalign);
1073
1074 xalign = direction == GTK_TEXT_DIR_LTR ? mxalign : 1.0 - mxalign;
1075 xpad = mxpad + (rect->width - extent) * xalign;
1076
1077 arrow_rect->x = direction == GTK_TEXT_DIR_LTR ?
1078 floor(rect->x + xpad) : ceil(rect->x + xpad);
1079 arrow_rect->y = floor(rect->y + mypad +
1080 ((rect->height - extent) * myalign));
1081
1082 arrow_rect->width = arrow_rect->height = extent;
1083
1084 return MOZ_GTK_SUCCESS;
1085 }
1086
1087 static gint
1088 moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect,
1089 GtkWidgetState* state,
1090 GtkScrollbarButtonFlags flags,
1091 GtkTextDirection direction)
1092 {
1093 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1094 GtkStateType saved_state;
1095 GdkRectangle arrow_rect;
1096 gdouble arrow_angle;
1097 GtkStyleContext* style;
1098 GtkWidget *scrollbar;
1099 gint arrow_displacement_x, arrow_displacement_y;
1100 const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ?
1101 "vscrollbar" : "hscrollbar";
1102
1103 ensure_scrollbar_widget();
1104
1105 if (flags & MOZ_GTK_STEPPER_VERTICAL)
1106 scrollbar = gVertScrollbarWidget;
1107 else
1108 scrollbar = gHorizScrollbarWidget;
1109
1110 gtk_widget_set_direction(scrollbar, direction);
1111
1112 if (flags & MOZ_GTK_STEPPER_VERTICAL) {
1113 arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_DOWN : ARROW_UP;
1114 } else {
1115 arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_RIGHT : ARROW_LEFT;
1116 }
1117
1118 style = gtk_widget_get_style_context(scrollbar);
1119
1120 gtk_style_context_save(style);
1121 gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCROLLBAR);
1122 gtk_style_context_set_state(style, state_flags);
1123
1124 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1125 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1126
1127 arrow_rect.width = rect->width / 2;
1128 arrow_rect.height = rect->height / 2;
1129 arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1130 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1131
1132 if (state_flags & GTK_STATE_FLAG_ACTIVE) {
1133 gtk_widget_style_get(scrollbar,
1134 "arrow-displacement-x", &arrow_displacement_x,
1135 "arrow-displacement-y", &arrow_displacement_y,
1136 NULL);
1137
1138 arrow_rect.x += arrow_displacement_x;
1139 arrow_rect.y += arrow_displacement_y;
1140 }
1141
1142 gtk_render_arrow(style, cr, arrow_angle,
1143 arrow_rect.x,
1144 arrow_rect.y,
1145 arrow_rect.width);
1146
1147 gtk_style_context_restore(style);
1148
1149 return MOZ_GTK_SUCCESS;
1150 }
1151
1152 static gint
1153 moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget,
1154 cairo_t *cr, GdkRectangle* rect,
1155 GtkWidgetState* state,
1156 GtkTextDirection direction)
1157 {
1158 GtkStyleContext* style;
1159 GtkScrollbar *scrollbar;
1160
1161 ensure_scrollbar_widget();
1162
1163 if (widget == MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL)
1164 scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
1165 else
1166 scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
1167
1168 gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
1169
1170 style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar));
1171 gtk_style_context_save(style);
1172 gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH);
1173
1174 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1175 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1176
1177 if (state->focused) {
1178 gtk_render_focus(style, cr,
1179 rect->x, rect->y, rect->width, rect->height);
1180 }
1181 gtk_style_context_restore(style);
1182 return MOZ_GTK_SUCCESS;
1183 }
1184
1185 static gint
1186 moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget,
1187 cairo_t *cr, GdkRectangle* rect,
1188 GtkWidgetState* state,
1189 GtkTextDirection direction)
1190 {
1191 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1192 GtkStyleContext* style;
1193 GtkScrollbar *scrollbar;
1194 GtkAdjustment *adj;
1195
1196 ensure_scrollbar_widget();
1197
1198 if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL)
1199 scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
1200 else
1201 scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
1202
1203 gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
1204
1205 style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar));
1206 gtk_style_context_save(style);
1207
1208 gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER);
1209 gtk_style_context_set_state(style, state_flags);
1210
1211 gtk_render_slider(style, cr, rect->x, rect->y,
1212 rect->width, rect->height,
1213 (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
1214 GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1215
1216 gtk_style_context_restore(style);
1217
1218 return MOZ_GTK_SUCCESS;
1219 }
1220
1221 static gint
1222 moz_gtk_spin_paint(cairo_t *cr, GdkRectangle* rect,
1223 GtkTextDirection direction)
1224 {
1225 GtkStyleContext* style;
1226
1227 ensure_spin_widget();
1228 gtk_widget_set_direction(gSpinWidget, direction);
1229 style = gtk_widget_get_style_context(gSpinWidget);
1230 gtk_style_context_save(style);
1231 gtk_style_context_add_class(style, GTK_STYLE_CLASS_SPINBUTTON);
1232 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1233 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1234 gtk_style_context_restore(style);
1235
1236 return MOZ_GTK_SUCCESS;
1237 }
1238
1239 static gint
1240 moz_gtk_spin_updown_paint(cairo_t *cr, GdkRectangle* rect,
1241 gboolean isDown, GtkWidgetState* state,
1242 GtkTextDirection direction)
1243 {
1244 GdkRectangle arrow_rect;
1245 GtkStyleContext* style;
1246
1247 ensure_spin_widget();
1248 style = gtk_widget_get_style_context(gSpinWidget);
1249 gtk_style_context_save(style);
1250 gtk_style_context_add_class(style, GTK_STYLE_CLASS_SPINBUTTON);
1251 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
1252 gtk_widget_set_direction(gSpinWidget, direction);
1253
1254 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1255 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1256
1257
1258 /* hard code these values */
1259 arrow_rect.width = 6;
1260 arrow_rect.height = 6;
1261 arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1262 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1263 arrow_rect.y += isDown ? -1 : 1;
1264
1265 gtk_render_arrow(style, cr,
1266 isDown ? ARROW_DOWN : ARROW_UP,
1267 arrow_rect.x, arrow_rect.y,
1268 arrow_rect.width);
1269 gtk_style_context_restore(style);
1270 return MOZ_GTK_SUCCESS;
1271 }
1272
1273 /* See gtk_range_draw() for reference.
1274 */
1275 static gint
1276 moz_gtk_scale_paint(cairo_t *cr, GdkRectangle* rect,
1277 GtkWidgetState* state,
1278 GtkOrientation flags, GtkTextDirection direction)
1279 {
1280 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1281 gint x = 0, y = 0;
1282 GtkStyleContext* style;
1283 GtkWidget* widget;
1284 GtkBorder margin;
1285
1286 ensure_scale_widget();
1287 widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
1288 gtk_widget_set_direction(widget, direction);
1289
1290 style = gtk_widget_get_style_context(widget);
1291 gtk_style_context_save(style);
1292 gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCALE);
1293 gtk_style_context_get_margin(style, state_flags, &margin);
1294
1295 if (flags == GTK_ORIENTATION_HORIZONTAL) {
1296 x = margin.left;
1297 y++;
1298 }
1299 else {
1300 x++;
1301 y = margin.top;
1302 }
1303
1304 gtk_render_frame(style, cr, rect->x + x, rect->y + y,
1305 rect->width - 2*x, rect->height - 2*y);
1306
1307 if (state->focused)
1308 gtk_render_focus(style, cr,
1309 rect->x, rect->y, rect->width, rect->height);
1310 gtk_style_context_restore(style);
1311 return MOZ_GTK_SUCCESS;
1312 }
1313
1314 static gint
1315 moz_gtk_scale_thumb_paint(cairo_t *cr, GdkRectangle* rect,
1316 GtkWidgetState* state,
1317 GtkOrientation flags, GtkTextDirection direction)
1318 {
1319 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1320 GtkStyleContext* style;
1321 GtkWidget* widget;
1322 gint thumb_width, thumb_height, x, y;
1323
1324 ensure_scale_widget();
1325 widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
1326 gtk_widget_set_direction(widget, direction);
1327
1328 style = gtk_widget_get_style_context(widget);
1329 gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER);
1330 gtk_style_context_save(style);
1331 gtk_style_context_set_state(style, state_flags);
1332 /* determine the thumb size, and position the thumb in the center in the opposite axis
1333 */
1334 if (flags == GTK_ORIENTATION_HORIZONTAL) {
1335 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_width, &thumb_height);
1336 x = rect->x;
1337 y = rect->y + (rect->height - thumb_height) / 2;
1338 }
1339 else {
1340 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_width);
1341 x = rect->x + (rect->width - thumb_width) / 2;
1342 y = rect->y;
1343 }
1344
1345 gtk_render_slider(style, cr, x, y, thumb_width, thumb_height, flags);
1346 gtk_style_context_restore(style);
1347 return MOZ_GTK_SUCCESS;
1348 }
1349
1350 static gint
1351 moz_gtk_gripper_paint(cairo_t *cr, GdkRectangle* rect,
1352 GtkWidgetState* state,
1353 GtkTextDirection direction)
1354 {
1355 GtkStyleContext* style;
1356
1357 ensure_handlebox_widget();
1358 gtk_widget_set_direction(gHandleBoxWidget, direction);
1359
1360 style = gtk_widget_get_style_context(gHandleBoxWidget);
1361 gtk_style_context_save(style);
1362 gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
1363 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
1364
1365 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1366 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1367 gtk_style_context_restore(style);
1368
1369 return MOZ_GTK_SUCCESS;
1370 }
1371
1372 static gint
1373 moz_gtk_hpaned_paint(cairo_t *cr, GdkRectangle* rect,
1374 GtkWidgetState* state)
1375 {
1376 GtkStyleContext* style;
1377
1378 ensure_hpaned_widget();
1379 style = gtk_widget_get_style_context(gHPanedWidget);
1380 gtk_style_context_save(style);
1381 gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR);
1382 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
1383 gtk_render_handle(style, cr,
1384 rect->x, rect->y, rect->width, rect->height);
1385 gtk_style_context_restore(style);
1386
1387 return MOZ_GTK_SUCCESS;
1388 }
1389
1390 static gint
1391 moz_gtk_vpaned_paint(cairo_t *cr, GdkRectangle* rect,
1392 GtkWidgetState* state)
1393 {
1394 GtkStyleContext* style;
1395
1396 ensure_vpaned_widget();
1397 style = gtk_widget_get_style_context(gVPanedWidget);
1398 gtk_style_context_save(style);
1399 gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR);
1400 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
1401 gtk_render_handle(style, cr,
1402 rect->x, rect->y, rect->width, rect->height);
1403 gtk_style_context_restore(style);
1404
1405 return MOZ_GTK_SUCCESS;
1406 }
1407
1408 // See gtk_entry_draw() for reference.
1409 static gint
1410 moz_gtk_entry_paint(cairo_t *cr, GdkRectangle* rect,
1411 GtkWidgetState* state,
1412 GtkWidget* widget, GtkTextDirection direction)
1413 {
1414 gint x = rect->x, y = rect->y, width = rect->width, height = rect->height;
1415 GtkStyleContext* style;
1416 gboolean interior_focus;
1417 gint focus_width;
1418
1419 gtk_widget_set_direction(widget, direction);
1420
1421 style = gtk_widget_get_style_context(widget);
1422
1423 gtk_widget_style_get(widget,
1424 "interior-focus", &interior_focus,
1425 "focus-line-width", &focus_width,
1426 NULL);
1427
1428 /* gtkentry.c uses two windows, one for the entire widget and one for the
1429 * text area inside it. The background of both windows is set to the "base"
1430 * color of the new state in gtk_entry_state_changed, but only the inner
1431 * textarea window uses gtk_paint_flat_box when exposed */
1432
1433 /* This gets us a lovely greyish disabledish look */
1434 gtk_widget_set_sensitive(widget, !state->disabled);
1435
1436 gtk_style_context_save(style);
1437 gtk_style_context_add_class(style, GTK_STYLE_CLASS_ENTRY);
1438
1439 /* Now paint the shadow and focus border.
1440 * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad
1441 * smaller when focused if the focus is not interior, then the focus. */
1442
1443 if (state->focused && !state->disabled) {
1444 /* This will get us the lit borders that focused textboxes enjoy on
1445 * some themes. */
1446 gtk_style_context_set_state(style, GTK_STATE_FLAG_FOCUSED);
1447 if (!interior_focus) {
1448 /* Indent the border a little bit if we have exterior focus
1449 (this is what GTK does to draw native entries) */
1450 x += focus_width;
1451 y += focus_width;
1452 width -= 2 * focus_width;
1453 height -= 2 * focus_width;
1454 }
1455 }
1456
1457 if (state->disabled) {
1458 gtk_style_context_set_state(style, GTK_STATE_FLAG_INSENSITIVE);
1459 }
1460
1461 gtk_render_background(style, cr, x, y, width, height);
1462 gtk_render_frame(style, cr, x, y, width, height);
1463
1464 if (state->focused && !state->disabled) {
1465 if (!interior_focus) {
1466 gtk_render_focus(style, cr, rect->x, rect->y, rect->width, rect->height);
1467 }
1468 }
1469 gtk_style_context_restore(style);
1470
1471 return MOZ_GTK_SUCCESS;
1472 }
1473
1474 static gint
1475 moz_gtk_treeview_paint(cairo_t *cr, GdkRectangle* rect,
1476 GtkWidgetState* state,
1477 GtkTextDirection direction)
1478 {
1479 gint xthickness, ythickness;
1480 GtkStyleContext *style;
1481 GtkStyleContext *style_tree;
1482 GtkStateFlags state_flags;
1483 GtkBorder border;
1484
1485 ensure_tree_view_widget();
1486 ensure_scrolled_window_widget();
1487
1488 gtk_widget_set_direction(gTreeViewWidget, direction);
1489 gtk_widget_set_direction(gScrolledWindowWidget, direction);
1490
1491 /* only handle disabled and normal states, otherwise the whole background
1492 * area will be painted differently with other states */
1493 state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL;
1494
1495 style = gtk_widget_get_style_context(gScrolledWindowWidget);
1496 gtk_style_context_save(style);
1497 gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME);
1498 gtk_style_context_get_border(style, state_flags, &border);
1499 xthickness = border.left;
1500 ythickness = border.top;
1501
1502 style_tree = gtk_widget_get_style_context(gTreeViewWidget);
1503 gtk_style_context_save(style_tree);
1504 gtk_style_context_add_class(style_tree, GTK_STYLE_CLASS_VIEW);
1505
1506 gtk_render_background(style_tree, cr,
1507 rect->x + xthickness, rect->y + ythickness,
1508 rect->width - 2 * xthickness,
1509 rect->height - 2 * ythickness);
1510 gtk_render_frame(style, cr,
1511 rect->x, rect->y, rect->width, rect->height);
1512 gtk_style_context_restore(style);
1513 gtk_style_context_restore(style_tree);
1514 return MOZ_GTK_SUCCESS;
1515 }
1516
1517 static gint
1518 moz_gtk_tree_header_cell_paint(cairo_t *cr, GdkRectangle* rect,
1519 GtkWidgetState* state,
1520 gboolean isSorted, GtkTextDirection direction)
1521 {
1522 moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
1523 gTreeHeaderCellWidget, direction);
1524 return MOZ_GTK_SUCCESS;
1525 }
1526
1527 static gint
1528 moz_gtk_tree_header_sort_arrow_paint(cairo_t *cr, GdkRectangle* rect,
1529 GtkWidgetState* state, GtkArrowType arrow_type,
1530 GtkTextDirection direction)
1531 {
1532 GdkRectangle arrow_rect;
1533 gdouble arrow_angle;
1534 GtkStyleContext* style;
1535
1536 ensure_tree_header_cell_widget();
1537 gtk_widget_set_direction(gTreeHeaderSortArrowWidget, direction);
1538
1539 /* hard code these values */
1540 arrow_rect.width = 11;
1541 arrow_rect.height = 11;
1542 arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1543 arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1544 style = gtk_widget_get_style_context(gTreeHeaderSortArrowWidget);
1545 gtk_style_context_save(style);
1546 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
1547
1548 switch (arrow_type) {
1549 case GTK_ARROW_LEFT:
1550 arrow_angle = ARROW_LEFT;
1551 break;
1552 case GTK_ARROW_RIGHT:
1553 arrow_angle = ARROW_RIGHT;
1554 break;
1555 case GTK_ARROW_DOWN:
1556 arrow_angle = ARROW_DOWN;
1557 break;
1558 default:
1559 arrow_angle = ARROW_UP;
1560 break;
1561 }
1562 if (arrow_type != GTK_ARROW_NONE)
1563 gtk_render_arrow(style, cr, arrow_angle,
1564 arrow_rect.x, arrow_rect.y,
1565 arrow_rect.width);
1566 gtk_style_context_restore(style);
1567 return MOZ_GTK_SUCCESS;
1568 }
1569
1570 /* See gtk_expander_paint() for reference.
1571 */
1572 static gint
1573 moz_gtk_treeview_expander_paint(cairo_t *cr, GdkRectangle* rect,
1574 GtkWidgetState* state,
1575 GtkExpanderStyle expander_state,
1576 GtkTextDirection direction)
1577 {
1578 GtkStyleContext *style;
1579 GtkStateFlags state_flags;
1580
1581 ensure_tree_view_widget();
1582 gtk_widget_set_direction(gTreeViewWidget, direction);
1583
1584 style = gtk_widget_get_style_context(gTreeViewWidget);
1585 gtk_style_context_save(style);
1586 gtk_style_context_add_class(style, GTK_STYLE_CLASS_EXPANDER);
1587
1588 /* Because the frame we get is of the entire treeview, we can't get the precise
1589 * event state of one expander, thus rendering hover and active feedback useless. */
1590 state_flags = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
1591
1592 /* GTK_STATE_FLAG_ACTIVE controls expanded/colapsed state rendering
1593 * in gtk_render_expander()
1594 */
1595 if (expander_state == GTK_EXPANDER_EXPANDED)
1596 state_flags |= GTK_STATE_FLAG_ACTIVE;
1597 else
1598 state_flags &= ~(GTK_STATE_FLAG_ACTIVE);
1599
1600 gtk_style_context_set_state(style, state_flags);
1601
1602 gtk_render_expander(style, cr,
1603 rect->x,
1604 rect->y,
1605 rect->width,
1606 rect->height);
1607
1608 gtk_style_context_restore(style);
1609 return MOZ_GTK_SUCCESS;
1610 }
1611
1612 /* See gtk_separator_draw() for reference.
1613 */
1614 static gint
1615 moz_gtk_combo_box_paint(cairo_t *cr, GdkRectangle* rect,
1616 GtkWidgetState* state,
1617 gboolean ishtml, GtkTextDirection direction)
1618 {
1619 GdkRectangle arrow_rect, real_arrow_rect;
1620 gint arrow_size, separator_width;
1621 gboolean wide_separators;
1622 GtkStyleContext* style;
1623 GtkRequisition arrow_req;
1624
1625 ensure_combo_box_widgets();
1626
1627 /* Also sets the direction on gComboBoxButtonWidget, which is then
1628 * inherited by the separator and arrow */
1629 moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
1630 gComboBoxButtonWidget, direction);
1631
1632 calculate_button_inner_rect(gComboBoxButtonWidget,
1633 rect, &arrow_rect, direction, ishtml);
1634 /* Now arrow_rect contains the inner rect ; we want to correct the width
1635 * to what the arrow needs (see gtk_combo_box_size_allocate) */
1636 gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req);
1637 if (direction == GTK_TEXT_DIR_LTR)
1638 arrow_rect.x += arrow_rect.width - arrow_req.width;
1639 arrow_rect.width = arrow_req.width;
1640
1641 calculate_arrow_rect(gComboBoxArrowWidget,
1642 &arrow_rect, &real_arrow_rect, direction);
1643
1644 style = gtk_widget_get_style_context(gComboBoxArrowWidget);
1645 gtk_render_arrow(style, cr, ARROW_DOWN,
1646 real_arrow_rect.x, real_arrow_rect.y,
1647 real_arrow_rect.width);
1648
1649 /* If there is no separator in the theme, there's nothing left to do. */
1650 if (!gComboBoxSeparatorWidget)
1651 return MOZ_GTK_SUCCESS;
1652 style = gtk_widget_get_style_context(gComboBoxSeparatorWidget);
1653 gtk_widget_style_get(gComboBoxSeparatorWidget,
1654 "wide-separators", &wide_separators,
1655 "separator-width", &separator_width,
1656 NULL);
1657
1658 if (wide_separators) {
1659 if (direction == GTK_TEXT_DIR_LTR)
1660 arrow_rect.x -= separator_width;
1661 else
1662 arrow_rect.x += arrow_rect.width;
1663
1664 gtk_render_frame(style, cr, arrow_rect.x, arrow_rect.y, separator_width, arrow_rect.height);
1665 } else {
1666 if (direction == GTK_TEXT_DIR_LTR) {
1667 GtkBorder padding;
1668 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1669 gtk_style_context_get_padding(style, state_flags, &padding);
1670 arrow_rect.x -= padding.left;
1671 }
1672 else
1673 arrow_rect.x += arrow_rect.width;
1674
1675 gtk_render_line(style, cr,
1676 arrow_rect.x, arrow_rect.y,
1677 arrow_rect.x, arrow_rect.y + arrow_rect.height);
1678 }
1679 return MOZ_GTK_SUCCESS;
1680 }
1681
1682 static gint
1683 moz_gtk_arrow_paint(cairo_t *cr, GdkRectangle* rect,
1684 GtkWidgetState* state,
1685 GtkArrowType arrow_type, GtkTextDirection direction)
1686 {
1687 GtkStyleContext* style;
1688 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1689 GdkRectangle arrow_rect;
1690 gdouble arrow_angle = ARROW_UP;
1691
1692 ensure_button_arrow_widget();
1693 style = gtk_widget_get_style_context(gButtonArrowWidget);
1694 gtk_style_context_save(style);
1695 gtk_style_context_set_state(style, state_flags);
1696 gtk_widget_set_direction(gButtonArrowWidget, direction);
1697
1698 calculate_arrow_rect(gButtonArrowWidget, rect, &arrow_rect,
1699 direction);
1700
1701 if (direction == GTK_TEXT_DIR_RTL) {
1702 if (arrow_type == GTK_ARROW_LEFT)
1703 arrow_angle = ARROW_RIGHT;
1704 else if (arrow_type == GTK_ARROW_RIGHT)
1705 arrow_angle = ARROW_LEFT;
1706 } else if (arrow_type == GTK_ARROW_DOWN) {
1707 arrow_angle = ARROW_DOWN;
1708 }
1709 if (arrow_type != GTK_ARROW_NONE)
1710 gtk_render_arrow(style, cr, arrow_angle,
1711 arrow_rect.x, arrow_rect.y, arrow_rect.width);
1712 gtk_style_context_restore(style);
1713 return MOZ_GTK_SUCCESS;
1714 }
1715
1716 static gint
1717 moz_gtk_combo_box_entry_button_paint(cairo_t *cr, GdkRectangle* rect,
1718 GtkWidgetState* state,
1719 gboolean input_focus,
1720 GtkTextDirection direction)
1721 {
1722 gint x_displacement, y_displacement;
1723 GdkRectangle arrow_rect, real_arrow_rect;
1724 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1725 GtkStyleContext* style;
1726
1727 ensure_combo_box_entry_widgets();
1728
1729 moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
1730 gComboBoxEntryButtonWidget, direction);
1731
1732 calculate_button_inner_rect(gComboBoxEntryButtonWidget,
1733 rect, &arrow_rect, direction, FALSE);
1734 if (state_flags & GTK_STATE_FLAG_ACTIVE) {
1735 gtk_widget_style_get(gComboBoxEntryButtonWidget,
1736 "child-displacement-x", &x_displacement,
1737 "child-displacement-y", &y_displacement,
1738 NULL);
1739 arrow_rect.x += x_displacement;
1740 arrow_rect.y += y_displacement;
1741 }
1742
1743 calculate_arrow_rect(gComboBoxEntryArrowWidget,
1744 &arrow_rect, &real_arrow_rect, direction);
1745
1746 style = gtk_widget_get_style_context(gComboBoxEntryArrowWidget);
1747
1748 gtk_render_arrow(style, cr, ARROW_DOWN,
1749 real_arrow_rect.x, real_arrow_rect.y,
1750 real_arrow_rect.width);
1751
1752 return MOZ_GTK_SUCCESS;
1753 }
1754
1755 static gint
1756 moz_gtk_container_paint(cairo_t *cr, GdkRectangle* rect,
1757 GtkWidgetState* state,
1758 gboolean isradio, GtkTextDirection direction)
1759 {
1760 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1761 GtkStyleContext* style;
1762 GtkWidget *widget;
1763 gboolean interior_focus;
1764 gint focus_width, focus_pad;
1765
1766 if (isradio) {
1767 ensure_radiobutton_widget();
1768 widget = gRadiobuttonWidget;
1769 } else {
1770 ensure_checkbox_widget();
1771 widget = gCheckboxWidget;
1772 }
1773 gtk_widget_set_direction(widget, direction);
1774
1775 style = gtk_widget_get_style_context(widget);
1776 gtk_style_context_save(style);
1777 moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad);
1778 gtk_style_context_set_state(style, state_flags);
1779
1780 /* this is for drawing a prelight box */
1781 if (state_flags & GTK_STATE_FLAG_PRELIGHT) {
1782 gtk_render_background(style, cr,
1783 rect->x, rect->y, rect->width, rect->height);
1784 }
1785
1786 if (state->focused && !interior_focus) {
1787 gtk_render_focus(style, cr,
1788 rect->x, rect->y, rect->width, rect->height);
1789 }
1790 gtk_style_context_restore(style);
1791
1792 return MOZ_GTK_SUCCESS;
1793 }
1794
1795 static gint
1796 moz_gtk_toggle_label_paint(cairo_t *cr, GdkRectangle* rect,
1797 GtkWidgetState* state,
1798 gboolean isradio, GtkTextDirection direction)
1799 {
1800 GtkStyleContext *style;
1801 GtkWidget *widget;
1802 gboolean interior_focus;
1803
1804 if (!state->focused)
1805 return MOZ_GTK_SUCCESS;
1806
1807 if (isradio) {
1808 ensure_radiobutton_widget();
1809 widget = gRadiobuttonWidget;
1810 } else {
1811 ensure_checkbox_widget();
1812 widget = gCheckboxWidget;
1813 }
1814 style = gtk_widget_get_style_context(widget);
1815 gtk_style_context_save(style);
1816 if (isradio) {
1817 gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO);
1818 } else {
1819 gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK);
1820 }
1821 gtk_widget_set_direction(widget, direction);
1822
1823 gtk_widget_style_get(widget, "interior-focus", &interior_focus, NULL);
1824 if (!interior_focus)
1825 return MOZ_GTK_SUCCESS;
1826
1827 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
1828 gtk_render_focus(style, cr,
1829 rect->x, rect->y, rect->width, rect->height);
1830 gtk_style_context_restore(style);
1831
1832 return MOZ_GTK_SUCCESS;
1833 }
1834
1835 static gint
1836 moz_gtk_toolbar_paint(cairo_t *cr, GdkRectangle* rect,
1837 GtkTextDirection direction)
1838 {
1839 GtkStyleContext* style;
1840
1841 ensure_toolbar_widget();
1842 gtk_widget_set_direction(gToolbarWidget, direction);
1843
1844 style = gtk_widget_get_style_context(gToolbarWidget);
1845 gtk_style_context_save(style);
1846 gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLBAR);
1847
1848 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1849 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1850 gtk_style_context_restore(style);
1851
1852 return MOZ_GTK_SUCCESS;
1853 }
1854
1855 /* See _gtk_toolbar_paint_space_line() for reference.
1856 */
1857 static gint
1858 moz_gtk_toolbar_separator_paint(cairo_t *cr, GdkRectangle* rect,
1859 GtkTextDirection direction)
1860 {
1861 GtkStyleContext* style;
1862 gint separator_width;
1863 gint paint_width;
1864 gboolean wide_separators;
1865
1866 /* Defined as constants in GTK+ 2.10.14 */
1867 const double start_fraction = 0.2;
1868 const double end_fraction = 0.8;
1869
1870 ensure_toolbar_separator_widget();
1871 gtk_widget_set_direction(gToolbarSeparatorWidget, direction);
1872
1873 style = gtk_widget_get_style_context(gToolbarSeparatorWidget);
1874
1875 gtk_widget_style_get(gToolbarWidget,
1876 "wide-separators", &wide_separators,
1877 "separator-width", &separator_width,
1878 NULL);
1879
1880 if (wide_separators) {
1881 if (separator_width > rect->width)
1882 separator_width = rect->width;
1883
1884 gtk_render_frame(style, cr,
1885 rect->x + (rect->width - separator_width) / 2,
1886 rect->y + rect->height * start_fraction,
1887 separator_width,
1888 rect->height * (end_fraction - start_fraction));
1889 } else {
1890 GtkBorder padding;
1891 gtk_style_context_get_padding(style, 0, &padding);
1892
1893 paint_width = padding.left;
1894 if (paint_width > rect->width)
1895 paint_width = rect->width;
1896
1897 gtk_render_line(style, cr,
1898 rect->x + (rect->width - paint_width) / 2,
1899 rect->y + rect->height * start_fraction,
1900 rect->x + (rect->width - paint_width) / 2,
1901 rect->y + rect->height * end_fraction);
1902 }
1903
1904 return MOZ_GTK_SUCCESS;
1905 }
1906
1907 static gint
1908 moz_gtk_tooltip_paint(cairo_t *cr, GdkRectangle* rect,
1909 GtkTextDirection direction)
1910 {
1911 GtkStyleContext* style;
1912
1913 ensure_tooltip_widget();
1914 gtk_widget_set_direction(gTooltipWidget, direction);
1915
1916 style = gtk_widget_get_style_context(gTooltipWidget);
1917 gtk_style_context_save(style);
1918 gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
1919 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1920 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1921 gtk_style_context_restore(style);
1922 return MOZ_GTK_SUCCESS;
1923 }
1924
1925 static gint
1926 moz_gtk_resizer_paint(cairo_t *cr, GdkRectangle* rect,
1927 GtkWidgetState* state,
1928 GtkTextDirection direction)
1929 {
1930 GtkStyleContext* style;
1931
1932 ensure_frame_widget();
1933 gtk_widget_set_direction(gStatusbarWidget, direction);
1934
1935 style = gtk_widget_get_style_context(gStatusbarWidget);
1936 gtk_style_context_save(style);
1937 gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
1938 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
1939
1940 gtk_render_handle(style, cr, rect->x, rect->y, rect->width, rect->height);
1941 gtk_style_context_restore(style);
1942
1943 return MOZ_GTK_SUCCESS;
1944 }
1945
1946 static gint
1947 moz_gtk_frame_paint(cairo_t *cr, GdkRectangle* rect,
1948 GtkTextDirection direction)
1949 {
1950 GtkStyleContext* style;
1951
1952 ensure_frame_widget();
1953 gtk_widget_set_direction(gFrameWidget, direction);
1954 style = gtk_widget_get_style_context(gFrameWidget);
1955 gtk_style_context_save(style);
1956 gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME);
1957
1958 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1959 gtk_style_context_restore(style);
1960 return MOZ_GTK_SUCCESS;
1961 }
1962
1963 static gint
1964 moz_gtk_progressbar_paint(cairo_t *cr, GdkRectangle* rect,
1965 GtkTextDirection direction)
1966 {
1967 GtkStyleContext* style;
1968
1969 ensure_progress_widget();
1970 gtk_widget_set_direction(gProgressWidget, direction);
1971
1972 style = gtk_widget_get_style_context(gProgressWidget);
1973 gtk_style_context_save(style);
1974 gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH);
1975
1976 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1977 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1978 gtk_style_context_restore(style);
1979
1980 return MOZ_GTK_SUCCESS;
1981 }
1982
1983 static gint
1984 moz_gtk_progress_chunk_paint(cairo_t *cr, GdkRectangle* rect,
1985 GtkTextDirection direction,
1986 GtkThemeWidgetType widget)
1987 {
1988 GtkStyleContext* style;
1989
1990 ensure_progress_widget();
1991 gtk_widget_set_direction(gProgressWidget, direction);
1992
1993 style = gtk_widget_get_style_context(gProgressWidget);
1994 gtk_style_context_save(style);
1995 gtk_style_context_add_class(style, GTK_STYLE_CLASS_PROGRESSBAR);
1996
1997 if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
1998 widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
1999 /**
2000 * The bar's size and the bar speed are set depending of the progress'
2001 * size. These could also be constant for all progress bars easily.
2002 */
2003 gboolean vertical = (widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE);
2004
2005 /* The size of the dimension we are going to use for the animation. */
2006 const gint progressSize = vertical ? rect->height : rect->width;
2007
2008 /* The bar is using a fifth of the element size, based on GtkProgressBar
2009 * activity-blocks property. */
2010 const gint barSize = MAX(1, progressSize / 5);
2011
2012 /* Represents the travel that has to be done for a complete cycle. */
2013 const gint travel = 2 * (progressSize - barSize);
2014
2015 /* period equals to travel / pixelsPerMillisecond
2016 * where pixelsPerMillisecond equals progressSize / 1000.0.
2017 * This is equivalent to 1600. */
2018 static const guint period = 1600;
2019 const gint t = PR_IntervalToMilliseconds(PR_IntervalNow()) % period;
2020 const gint dx = travel * t / period;
2021
2022 if (vertical) {
2023 rect->y += (dx < travel / 2) ? dx : travel - dx;
2024 rect->height = barSize;
2025 } else {
2026 rect->x += (dx < travel / 2) ? dx : travel - dx;
2027 rect->width = barSize;
2028 }
2029 }
2030
2031 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
2032 gtk_render_activity(style, cr, rect->x, rect->y, rect->width, rect->height);
2033 gtk_style_context_restore(style);
2034
2035 return MOZ_GTK_SUCCESS;
2036 }
2037
2038 gint
2039 moz_gtk_get_tab_thickness(void)
2040 {
2041 GtkBorder border;
2042
2043 ensure_tab_widget();
2044 GtkStyleContext * style = gtk_widget_get_style_context(gTabWidget);
2045 gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK);
2046 gtk_style_context_get_border(style, 0, &border);
2047
2048 if (border.top < 2)
2049 return 2; /* some themes don't set ythickness correctly */
2050
2051 return border.top;
2052 }
2053
2054 /* actual small tabs */
2055 static gint
2056 moz_gtk_tab_paint(cairo_t *cr, GdkRectangle* rect,
2057 GtkWidgetState* state,
2058 GtkTabFlags flags, GtkTextDirection direction)
2059 {
2060 /* When the tab isn't selected, we just draw a notebook extension.
2061 * When it is selected, we overwrite the adjacent border of the tabpanel
2062 * touching the tab with a pierced border (called "the gap") to make the
2063 * tab appear physically attached to the tabpanel; see details below. */
2064
2065 GtkStyleContext* style;
2066 GdkRectangle focusRect;
2067 GdkRectangle backRect;
2068
2069 ensure_tab_widget();
2070 gtk_widget_set_direction(gTabWidget, direction);
2071
2072 style = gtk_widget_get_style_context(gTabWidget);
2073 backRect = focusRect = *rect;
2074
2075 gtk_style_context_save(style);
2076
2077 if ((flags & MOZ_GTK_TAB_SELECTED) == 0) {
2078 /* Only draw the tab */
2079 gtk_style_context_set_state(style, GTK_STATE_FLAG_NORMAL);
2080 gtk_render_extension(style, cr,
2081 rect->x, rect->y, rect->width, rect->height,
2082 (flags & MOZ_GTK_TAB_BOTTOM) ?
2083 GTK_POS_TOP : GTK_POS_BOTTOM );
2084 } else {
2085 /* Draw the tab and the gap
2086 * We want the gap to be positioned exactly on the tabpanel top
2087 * border; since tabbox.css may set a negative margin so that the tab
2088 * frame rect already overlaps the tabpanel frame rect, we need to take
2089 * that into account when drawing. To that effect, nsNativeThemeGTK
2090 * passes us this negative margin (bmargin in the graphic below) in the
2091 * lowest bits of |flags|. We use it to set gap_voffset, the distance
2092 * between the top of the gap and the bottom of the tab (resp. the
2093 * bottom of the gap and the top of the tab when we draw a bottom tab),
2094 * while ensuring that the gap always touches the border of the tab,
2095 * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results
2096 * with big negative or positive margins.
2097 * Here is a graphical explanation in the case of top tabs:
2098 * ___________________________
2099 * / \
2100 * | T A B |
2101 * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel
2102 * : ^ bmargin : ^
2103 * : | (-negative margin, : |
2104 * bottom : v passed in flags) : | gap_height
2105 * of -> :.............................: | (the size of the
2106 * the tab . part of the gap . | tabpanel top border)
2107 * . outside of the tab . v
2108 * ----------------------------------------------
2109 *
2110 * To draw the gap, we use gtk_paint_box_gap(), see comment in
2111 * moz_gtk_tabpanels_paint(). This box_gap is made 3 * gap_height tall,
2112 * which should suffice to ensure that the only visible border is the
2113 * pierced one. If the tab is in the middle, we make the box_gap begin
2114 * a bit to the left of the tab and end a bit to the right, adjusting
2115 * the gap position so it still is under the tab, because we want the
2116 * rendering of a gap in the middle of a tabpanel. This is the role of
2117 * the gints gap_{l,r}_offset. On the contrary, if the tab is the
2118 * first, we align the start border of the box_gap with the start
2119 * border of the tab (left if LTR, right if RTL), by setting the
2120 * appropriate offset to 0.*/
2121 gint gap_loffset, gap_roffset, gap_voffset, gap_height;
2122
2123 /* Get height needed by the gap */
2124 gap_height = moz_gtk_get_tab_thickness();
2125
2126 /* Extract gap_voffset from the first bits of flags */
2127 gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK;
2128 if (gap_voffset > gap_height)
2129 gap_voffset = gap_height;
2130
2131 /* Set gap_{l,r}_offset to appropriate values */
2132 gap_loffset = gap_roffset = 20; /* should be enough */
2133 if (flags & MOZ_GTK_TAB_FIRST) {
2134 if (direction == GTK_TEXT_DIR_RTL)
2135 gap_roffset = 0;
2136 else
2137 gap_loffset = 0;
2138 }
2139
2140 gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE);
2141
2142 /* Adwaita theme engine crashes without it (rhbz#713764) */
2143 gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, 0);
2144
2145 if (flags & MOZ_GTK_TAB_BOTTOM) {
2146 /* Draw the tab on bottom */
2147 focusRect.y += gap_voffset;
2148 focusRect.height -= gap_voffset;
2149
2150 gtk_render_extension(style, cr,
2151 rect->x, rect->y + gap_voffset, rect->width,
2152 rect->height - gap_voffset, GTK_POS_TOP);
2153
2154 gtk_style_context_remove_region(style, GTK_STYLE_REGION_TAB);
2155
2156 backRect.y += (gap_voffset - gap_height);
2157 backRect.height = gap_height;
2158
2159 /* Draw the gap; erase with background color before painting in
2160 * case theme does not */
2161 gtk_render_background(style, cr, backRect.x, backRect.y,
2162 backRect.width, backRect.height);
2163 cairo_save(cr);
2164 cairo_rectangle(cr, backRect.x, backRect.y, backRect.width, backRect.height);
2165 cairo_clip(cr);
2166
2167 gtk_render_frame_gap(style, cr,
2168 rect->x - gap_loffset,
2169 rect->y + gap_voffset - 3 * gap_height,
2170 rect->width + gap_loffset + gap_roffset,
2171 3 * gap_height, GTK_POS_BOTTOM,
2172 gap_loffset, gap_loffset + rect->width);
2173 cairo_restore(cr);
2174 } else {
2175 /* Draw the tab on top */
2176 focusRect.height -= gap_voffset;
2177 gtk_render_extension(style, cr,
2178 rect->x, rect->y, rect->width,
2179 rect->height - gap_voffset, GTK_POS_BOTTOM);
2180
2181 gtk_style_context_remove_region(style, GTK_STYLE_REGION_TAB);
2182
2183 backRect.y += (rect->height - gap_voffset);
2184 backRect.height = gap_height;
2185
2186 /* Draw the gap; erase with background color before painting in
2187 * case theme does not */
2188 gtk_render_background(style, cr, backRect.x, backRect.y,
2189 backRect.width, backRect.height);
2190 cairo_save(cr);
2191 cairo_rectangle(cr, backRect.x, backRect.y, backRect.width, backRect.height);
2192 cairo_clip(cr);
2193
2194 gtk_render_frame_gap(style, cr,
2195 rect->x - gap_loffset,
2196 rect->y + rect->height - gap_voffset,
2197 rect->width + gap_loffset + gap_roffset,
2198 3 * gap_height, GTK_POS_TOP,
2199 gap_loffset, gap_loffset + rect->width);
2200 cairo_restore(cr);
2201 }
2202 }
2203
2204 if (state->focused) {
2205 /* Paint the focus ring */
2206 GtkBorder border;
2207 gtk_style_context_get_border(style, GetStateFlagsFromGtkWidgetState(state), &border);
2208
2209 focusRect.x += border.left;
2210 focusRect.width -= (border.left + border.right);
2211 focusRect.y += border.top;
2212 focusRect.height -= (border.top + border.bottom);
2213
2214 gtk_render_focus(style, cr,
2215 focusRect.x, focusRect.y, focusRect.width, focusRect.height);
2216 }
2217
2218 gtk_style_context_restore(style);
2219
2220 return MOZ_GTK_SUCCESS;
2221 }
2222
2223 /* tab area*/
2224 static gint
2225 moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect,
2226 GtkTextDirection direction)
2227 {
2228 GtkStyleContext* style;
2229
2230 ensure_tab_widget();
2231 gtk_widget_set_direction(gTabWidget, direction);
2232
2233 style = gtk_widget_get_style_context(gTabWidget);
2234 gtk_style_context_save(style);
2235
2236 gtk_render_background(style, cr, rect->x, rect->y,
2237 rect->width, rect->height);
2238 /*
2239 * The gap size is not needed in moz_gtk_tabpanels_paint because
2240 * the gap will be painted with the foreground tab in moz_gtk_tab_paint.
2241 *
2242 * However, if moz_gtk_tabpanels_paint just uses gtk_render_frame(),
2243 * the theme will think that there are no tabs and may draw something
2244 * different.Hence the trick of using two clip regions, and drawing the
2245 * gap outside each clip region, to get the correct frame for
2246 * a tabpanel with tabs.
2247 */
2248 /* left side */
2249 cairo_save(cr);
2250 cairo_rectangle(cr, rect->x, rect->y,
2251 rect->x + rect->width / 2,
2252 rect->y + rect->height);
2253 cairo_clip(cr);
2254 gtk_render_frame_gap(style, cr,
2255 rect->x, rect->y,
2256 rect->width, rect->height,
2257 GTK_POS_TOP, rect->width - 1, rect->width);
2258 cairo_restore(cr);
2259
2260 /* right side */
2261 cairo_save(cr);
2262 cairo_rectangle(cr, rect->x + rect->width / 2, rect->y,
2263 rect->x + rect->width,
2264 rect->y + rect->height);
2265 cairo_clip(cr);
2266 gtk_render_frame_gap(style, cr,
2267 rect->x, rect->y,
2268 rect->width, rect->height,
2269 GTK_POS_TOP, 0, 1);
2270 cairo_restore(cr);
2271
2272 gtk_style_context_restore(style);
2273 return MOZ_GTK_SUCCESS;
2274 }
2275
2276 static gint
2277 moz_gtk_tab_scroll_arrow_paint(cairo_t *cr, GdkRectangle* rect,
2278 GtkWidgetState* state,
2279 GtkArrowType arrow_type,
2280 GtkTextDirection direction)
2281 {
2282 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
2283 GtkStyleContext* style;
2284 gdouble arrow_angle;
2285 gint arrow_size = MIN(rect->width, rect->height);
2286 gint x = rect->x + (rect->width - arrow_size) / 2;
2287 gint y = rect->y + (rect->height - arrow_size) / 2;
2288
2289 ensure_tab_widget();
2290
2291 style = gtk_widget_get_style_context(gTabWidget);
2292 gtk_style_context_save(style);
2293 if (direction == GTK_TEXT_DIR_RTL) {
2294 arrow_type = (arrow_type == GTK_ARROW_LEFT) ?
2295 GTK_ARROW_RIGHT : GTK_ARROW_LEFT;
2296 }
2297 switch (arrow_type) {
2298 case GTK_ARROW_LEFT:
2299 arrow_angle = ARROW_LEFT;
2300 break;
2301 case GTK_ARROW_RIGHT:
2302 arrow_angle = ARROW_RIGHT;
2303 break;
2304 case GTK_ARROW_DOWN:
2305 arrow_angle = ARROW_DOWN;
2306 break;
2307 default:
2308 arrow_angle = ARROW_UP;
2309 break;
2310 }
2311 if (arrow_type != GTK_ARROW_NONE) {
2312 gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); /* TODO TEST */
2313 gtk_style_context_set_state(style, state_flags);
2314 gtk_render_arrow(style, cr, arrow_angle,
2315 x, y, arrow_size);
2316 }
2317 gtk_style_context_restore(style);
2318 return MOZ_GTK_SUCCESS;
2319 }
2320
2321 static gint
2322 moz_gtk_menu_bar_paint(cairo_t *cr, GdkRectangle* rect,
2323 GtkTextDirection direction)
2324 {
2325 GtkStyleContext* style;
2326
2327 ensure_menu_bar_widget();
2328 gtk_widget_set_direction(gMenuBarWidget, direction);
2329
2330 style = gtk_widget_get_style_context(gMenuBarWidget);
2331 gtk_style_context_save(style);
2332 gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR);
2333 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
2334 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
2335 gtk_style_context_restore(style);
2336
2337 return MOZ_GTK_SUCCESS;
2338 }
2339
2340 static gint
2341 moz_gtk_menu_popup_paint(cairo_t *cr, GdkRectangle* rect,
2342 GtkTextDirection direction)
2343 {
2344 GtkStyleContext* style;
2345
2346 ensure_menu_popup_widget();
2347 gtk_widget_set_direction(gMenuPopupWidget, direction);
2348
2349 style = gtk_widget_get_style_context(gMenuPopupWidget);
2350 gtk_style_context_save(style);
2351 gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENU);
2352
2353 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
2354 gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
2355 gtk_style_context_restore(style);
2356
2357 return MOZ_GTK_SUCCESS;
2358 }
2359
2360 // See gtk_menu_item_draw() for reference.
2361 static gint
2362 moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect,
2363 GtkTextDirection direction)
2364 {
2365 GtkStyleContext* style;
2366 gboolean wide_separators;
2367 gint separator_height;
2368 gint paint_height;
2369 guint border_width;
2370 gint x, y, w, h;
2371 GtkBorder padding;
2372
2373 ensure_menu_separator_widget();
2374 gtk_widget_set_direction(gMenuSeparatorWidget, direction);
2375
2376 border_width = gtk_container_get_border_width(gMenuSeparatorWidget);
2377 gtk_widget_style_get(gMenuSeparatorWidget,
2378 "wide-separators", &wide_separators,
2379 "separator-height", &separator_height,
2380 NULL);
2381
2382 style = gtk_widget_get_style_context(gMenuSeparatorWidget);
2383 gtk_style_context_get_padding(style, 0, &padding);
2384
2385 x = rect->x + border_width;
2386 y = rect->y + border_width;
2387 w = rect->width - border_width * 2;
2388 h = rect->height - border_width * 2;
2389
2390 if (wide_separators) {
2391 gtk_render_frame(style, cr,
2392 x + padding.left,
2393 y + padding.top,
2394 w - padding.left - padding.right,
2395 separator_height);
2396 } else {
2397 gtk_render_line(style, cr,
2398 x + padding.left,
2399 y + padding.top,
2400 x + w - padding.right - 1,
2401 y + padding.top);
2402 }
2403
2404 return MOZ_GTK_SUCCESS;
2405 }
2406
2407 // See gtk_menu_item_draw() for reference.
2408 static gint
2409 moz_gtk_menu_item_paint(cairo_t *cr, GdkRectangle* rect,
2410 GtkWidgetState* state,
2411 gint flags, GtkTextDirection direction)
2412 {
2413 GtkStyleContext* style;
2414 GtkWidget* item_widget;
2415 guint border_width;
2416 gint x, y, w, h;
2417
2418 if (state->inHover && !state->disabled) {
2419 if (flags & MOZ_TOPLEVEL_MENU_ITEM) {
2420 ensure_menu_bar_item_widget();
2421 item_widget = gMenuBarItemWidget;
2422 } else {
2423 ensure_menu_item_widget();
2424 item_widget = gMenuItemWidget;
2425 }
2426 style = gtk_widget_get_style_context(item_widget);
2427 gtk_style_context_save(style);
2428
2429 if (flags & MOZ_TOPLEVEL_MENU_ITEM) {
2430 gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR);
2431 }
2432
2433 gtk_widget_set_direction(item_widget, direction);
2434 gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUITEM);
2435 gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state));
2436
2437 border_width = gtk_container_get_border_width(GTK_CONTAINER(item_widget));
2438
2439 x = rect->x + border_width;
2440 y = rect->y + border_width;
2441 w = rect->width - border_width * 2;
2442 h = rect->height - border_width * 2;
2443
2444 gtk_render_background(style, cr, x, y, w, h);
2445 gtk_render_frame(style, cr, x, y, w, h);
2446 gtk_style_context_restore(style);
2447 }
2448
2449 return MOZ_GTK_SUCCESS;
2450 }
2451
2452 static gint
2453 moz_gtk_menu_arrow_paint(cairo_t *cr, GdkRectangle* rect,
2454 GtkWidgetState* state,
2455 GtkTextDirection direction)
2456 {
2457 GtkStyleContext* style;
2458 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
2459
2460 ensure_menu_item_widget();
2461 gtk_widget_set_direction(gMenuItemWidget, direction);
2462
2463 style = gtk_widget_get_style_context(gMenuItemWidget);
2464 gtk_style_context_save(style);
2465 gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUITEM);
2466 gtk_style_context_set_state(style, state_flags);
2467 gtk_render_arrow(style, cr,
2468 (direction == GTK_TEXT_DIR_LTR) ? ARROW_RIGHT : ARROW_LEFT,
2469 rect->x, rect->y, rect->width);
2470 gtk_style_context_restore(style);
2471
2472 return MOZ_GTK_SUCCESS;
2473 }
2474
2475 // See gtk_real_check_menu_item_draw_indicator() for reference.
2476 static gint
2477 moz_gtk_check_menu_item_paint(cairo_t *cr, GdkRectangle* rect,
2478 GtkWidgetState* state,
2479 gboolean checked, gboolean isradio,
2480 GtkTextDirection direction)
2481 {
2482 GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
2483 GtkStyleContext* style;
2484 GtkBorder padding;
2485 gint offset;
2486 gint indicator_size, horizontal_padding;
2487 gint x, y;
2488
2489 moz_gtk_menu_item_paint(cr, rect, state, FALSE, direction);
2490
2491 ensure_check_menu_item_widget();
2492 gtk_widget_set_direction(gCheckMenuItemWidget, direction);
2493
2494 gtk_widget_style_get (gCheckMenuItemWidget,
2495 "indicator-size", &indicator_size,
2496 "horizontal-padding", &horizontal_padding,
2497 NULL);
2498
2499 style = gtk_widget_get_style_context(gCheckMenuItemWidget);
2500 gtk_style_context_save(style);
2501 if (isradio) {
2502 gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO);
2503 } else {
2504 gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK);
2505 }
2506
2507 if (checked)
2508 state_flags |= GTK_STATE_FLAG_ACTIVE;
2509
2510 gtk_style_context_set_state(style, state_flags);
2511 gtk_style_context_get_padding(style, state_flags, &padding);
2512
2513 offset = gtk_container_get_border_width(GTK_CONTAINER(gCheckMenuItemWidget)) +
2514 padding.left + 2;
2515
2516 if (direction == GTK_TEXT_DIR_RTL) {
2517 x = rect->width - indicator_size - offset - horizontal_padding;
2518 }
2519 else {
2520 x = rect->x + offset + horizontal_padding;
2521 }
2522 y = rect->y + (rect->height - indicator_size) / 2;
2523
2524 if (isradio) {
2525 gtk_render_option(style, cr, x, y, indicator_size, indicator_size);
2526 } else {
2527 gtk_render_check(style, cr, x, y, indicator_size, indicator_size);
2528 }
2529 gtk_style_context_restore(style);
2530
2531 return MOZ_GTK_SUCCESS;
2532 }
2533
2534 static gint
2535 moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect,
2536 GtkTextDirection direction)
2537 {
2538 GtkStyleContext* style;
2539
2540 ensure_window_widget();
2541 gtk_widget_set_direction(gProtoWindow, direction);
2542
2543 style = gtk_widget_get_style_context(gProtoWindow);
2544 gtk_style_context_save(style);
2545 gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
2546 gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
2547 gtk_style_context_restore(style);
2548
2549 return MOZ_GTK_SUCCESS;
2550 }
2551
2552 static void
2553 moz_gtk_add_style_border(GtkStyleContext* style,
2554 gint* left, gint* top, gint* right, gint* bottom)
2555 {
2556 GtkBorder border;
2557
2558 gtk_style_context_get_border(style, 0, &border);
2559
2560 *left += border.left;
2561 *right += border.right;
2562 *top += border.top;
2563 *bottom += border.bottom;
2564 }
2565
2566 static void
2567 moz_gtk_add_style_padding(GtkStyleContext* style,
2568 gint* left, gint* top, gint* right, gint* bottom)
2569 {
2570 GtkBorder padding;
2571
2572 gtk_style_context_get_padding(style, 0, &padding);
2573
2574 *left += padding.left;
2575 *right += padding.right;
2576 *top += padding.top;
2577 *bottom += padding.bottom;
2578 }
2579
2580 gint
2581 moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top,
2582 gint* right, gint* bottom, GtkTextDirection direction,
2583 gboolean inhtml)
2584 {
2585 GtkWidget* w;
2586 GtkStyleContext* style;
2587 *left = *top = *right = *bottom = 0;
2588
2589 switch (widget) {
2590 case MOZ_GTK_BUTTON:
2591 {
2592 GtkBorder inner_border;
2593 gboolean interior_focus;
2594 gint focus_width, focus_pad;
2595
2596 ensure_button_widget();
2597 *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gButtonWidget));
2598
2599 /* Don't add this padding in HTML, otherwise the buttons will
2600 become too big and stuff the layout. */
2601 if (!inhtml) {
2602 moz_gtk_widget_get_focus(gButtonWidget, &interior_focus, &focus_width, &focus_pad);
2603 moz_gtk_button_get_inner_border(gButtonWidget, &inner_border);
2604 *left += focus_width + focus_pad + inner_border.left;
2605 *right += focus_width + focus_pad + inner_border.right;
2606 *top += focus_width + focus_pad + inner_border.top;
2607 *bottom += focus_width + focus_pad + inner_border.bottom;
2608 }
2609
2610 moz_gtk_add_style_border(gtk_widget_get_style_context(gButtonWidget),
2611 left, top, right, bottom);
2612 return MOZ_GTK_SUCCESS;
2613 }
2614 case MOZ_GTK_ENTRY:
2615 {
2616 ensure_entry_widget();
2617 style = gtk_widget_get_style_context(gEntryWidget);
2618 moz_gtk_add_style_border(style, left, top, right, bottom);
2619 moz_gtk_add_style_padding(style, left, top, right, bottom);
2620 return MOZ_GTK_SUCCESS;
2621 }
2622 case MOZ_GTK_TREEVIEW:
2623 {
2624 ensure_scrolled_window_widget();
2625 style = gtk_widget_get_style_context(gScrolledWindowWidget);
2626 gtk_style_context_save(style);
2627 gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME);
2628 moz_gtk_add_style_border(style, left, top, right, bottom);
2629 gtk_style_context_restore(style);
2630 return MOZ_GTK_SUCCESS;
2631 }
2632 case MOZ_GTK_TREE_HEADER_CELL:
2633 {
2634 /* A Tree Header in GTK is just a different styled button
2635 * It must be placed in a TreeView for getting the correct style
2636 * assigned.
2637 * That is why the following code is the same as for MOZ_GTK_BUTTON.
2638 * */
2639
2640 GtkBorder inner_border;
2641 gboolean interior_focus;
2642 gint focus_width, focus_pad;
2643
2644 ensure_tree_header_cell_widget();
2645 *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gTreeHeaderCellWidget));
2646
2647 moz_gtk_widget_get_focus(gTreeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad);
2648 moz_gtk_button_get_inner_border(gTreeHeaderCellWidget, &inner_border);
2649 *left += focus_width + focus_pad + inner_border.left;
2650 *right += focus_width + focus_pad + inner_border.right;
2651 *top += focus_width + focus_pad + inner_border.top;
2652 *bottom += focus_width + focus_pad + inner_border.bottom;
2653
2654 moz_gtk_add_style_border(gtk_widget_get_style_context(gTreeHeaderCellWidget),
2655 left, top, right, bottom);
2656 return MOZ_GTK_SUCCESS;
2657 }
2658 case MOZ_GTK_TREE_HEADER_SORTARROW:
2659 ensure_tree_header_cell_widget();
2660 w = gTreeHeaderSortArrowWidget;
2661 break;
2662 case MOZ_GTK_DROPDOWN_ENTRY:
2663 ensure_combo_box_entry_widgets();
2664 w = gComboBoxEntryTextareaWidget;
2665 break;
2666 case MOZ_GTK_DROPDOWN_ARROW:
2667 ensure_combo_box_entry_widgets();
2668 w = gComboBoxEntryButtonWidget;
2669 break;
2670 case MOZ_GTK_DROPDOWN:
2671 {
2672 /* We need to account for the arrow on the dropdown, so text
2673 * doesn't come too close to the arrow, or in some cases spill
2674 * into the arrow. */
2675 gboolean ignored_interior_focus, wide_separators;
2676 gint focus_width, focus_pad, separator_width;
2677 GtkRequisition arrow_req;
2678 GtkBorder border;
2679
2680 ensure_combo_box_widgets();
2681
2682 *left = gtk_container_get_border_width(GTK_CONTAINER(gComboBoxButtonWidget));
2683
2684 if (!inhtml) {
2685 moz_gtk_widget_get_focus(gComboBoxButtonWidget,
2686 &ignored_interior_focus,
2687 &focus_width, &focus_pad);
2688 *left += focus_width + focus_pad;
2689 }
2690
2691 style = gtk_widget_get_style_context(gComboBoxButtonWidget);
2692 gtk_style_context_get_border(style, 0, &border);
2693
2694 *top = *left + border.top;
2695 *left += border.left;
2696
2697 *right = *left; *bottom = *top;
2698
2699 /* If there is no separator, don't try to count its width. */
2700 separator_width = 0;
2701 if (gComboBoxSeparatorWidget) {
2702 gtk_widget_style_get(gComboBoxSeparatorWidget,
2703 "wide-separators", &wide_separators,
2704 "separator-width", &separator_width,
2705 NULL);
2706
2707 if (!wide_separators) {
2708 style = gtk_widget_get_style_context(gComboBoxSeparatorWidget);
2709 gtk_style_context_get_border(style, 0, &border);
2710 separator_width = border.left;
2711 }
2712 }
2713
2714 gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req);
2715
2716 if (direction == GTK_TEXT_DIR_RTL)
2717 *left += separator_width + arrow_req.width;
2718 else
2719 *right += separator_width + arrow_req.width;
2720
2721 return MOZ_GTK_SUCCESS;
2722 }
2723 case MOZ_GTK_TABPANELS:
2724 ensure_tab_widget();
2725 w = gTabWidget;
2726 break;
2727 case MOZ_GTK_PROGRESSBAR:
2728 ensure_progress_widget();
2729 w = gProgressWidget;
2730 break;
2731 case MOZ_GTK_SPINBUTTON_ENTRY:
2732 case MOZ_GTK_SPINBUTTON_UP:
2733 case MOZ_GTK_SPINBUTTON_DOWN:
2734 ensure_spin_widget();
2735 w = gSpinWidget;
2736 break;
2737 case MOZ_GTK_SCALE_HORIZONTAL:
2738 ensure_scale_widget();
2739 w = gHScaleWidget;
2740 break;
2741 case MOZ_GTK_SCALE_VERTICAL:
2742 ensure_scale_widget();
2743 w = gVScaleWidget;
2744 break;
2745 case MOZ_GTK_FRAME:
2746 ensure_frame_widget();
2747 w = gFrameWidget;
2748 break;
2749 case MOZ_GTK_CHECKBUTTON_LABEL:
2750 case MOZ_GTK_RADIOBUTTON_LABEL:
2751 {
2752 gboolean interior_focus;
2753 gint focus_width, focus_pad;
2754
2755 /* If the focus is interior, then the label has a border of
2756 (focus_width + focus_pad). */
2757 if (widget == MOZ_GTK_CHECKBUTTON_LABEL) {
2758 ensure_checkbox_widget();
2759 moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus,
2760 &focus_width, &focus_pad);
2761 }
2762 else {
2763 ensure_radiobutton_widget();
2764 moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus,
2765 &focus_width, &focus_pad);
2766 }
2767
2768 if (interior_focus)
2769 *left = *top = *right = *bottom = (focus_width + focus_pad);
2770
2771 return MOZ_GTK_SUCCESS;
2772 }
2773
2774 case MOZ_GTK_CHECKBUTTON_CONTAINER:
2775 case MOZ_GTK_RADIOBUTTON_CONTAINER:
2776 {
2777 gboolean interior_focus;
2778 gint focus_width, focus_pad;
2779
2780 /* If the focus is _not_ interior, then the container has a border
2781 of (focus_width + focus_pad). */
2782 if (widget == MOZ_GTK_CHECKBUTTON_CONTAINER) {
2783 ensure_checkbox_widget();
2784 moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus,
2785 &focus_width, &focus_pad);
2786 w = gCheckboxWidget;
2787 } else {
2788 ensure_radiobutton_widget();
2789 moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus,
2790 &focus_width, &focus_pad);
2791 w = gRadiobuttonWidget;
2792 }
2793
2794 *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w));
2795
2796 if (!interior_focus) {
2797 *left += (focus_width + focus_pad);
2798 *right += (focus_width + focus_pad);
2799 *top += (focus_width + focus_pad);
2800 *bottom += (focus_width + focus_pad);
2801 }
2802
2803 return MOZ_GTK_SUCCESS;
2804 }
2805 case MOZ_GTK_MENUPOPUP:
2806 ensure_menu_popup_widget();
2807 w = gMenuPopupWidget;
2808 break;
2809 case MOZ_GTK_MENUITEM:
2810 case MOZ_GTK_CHECKMENUITEM:
2811 case MOZ_GTK_RADIOMENUITEM:
2812 {
2813 if (widget == MOZ_GTK_MENUITEM) {
2814 ensure_menu_item_widget();
2815 ensure_menu_bar_item_widget();
2816 w = gMenuItemWidget;
2817 }
2818 else {
2819 ensure_check_menu_item_widget();
2820 w = gCheckMenuItemWidget;
2821 }
2822
2823 *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w));
2824 moz_gtk_add_style_padding(gtk_widget_get_style_context(w),
2825 left, top, right, bottom);
2826 return MOZ_GTK_SUCCESS;
2827 }
2828 case MOZ_GTK_TAB:
2829 ensure_tab_widget();
2830 w = gTabWidget;
2831 break;
2832 /* These widgets have no borders, since they are not containers. */
2833 case MOZ_GTK_SPLITTER_HORIZONTAL:
2834 case MOZ_GTK_SPLITTER_VERTICAL:
2835 case MOZ_GTK_CHECKBUTTON:
2836 case MOZ_GTK_RADIOBUTTON:
2837 case MOZ_GTK_SCROLLBAR_BUTTON:
2838 case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
2839 case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
2840 case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
2841 case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
2842 case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
2843 case MOZ_GTK_SCALE_THUMB_VERTICAL:
2844 case MOZ_GTK_GRIPPER:
2845 case MOZ_GTK_PROGRESS_CHUNK:
2846 case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
2847 case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
2848 case MOZ_GTK_TREEVIEW_EXPANDER:
2849 case MOZ_GTK_TOOLBAR_SEPARATOR:
2850 case MOZ_GTK_MENUSEPARATOR:
2851 /* These widgets have no borders.*/
2852 case MOZ_GTK_SPINBUTTON:
2853 case MOZ_GTK_TOOLTIP:
2854 case MOZ_GTK_WINDOW:
2855 case MOZ_GTK_RESIZER:
2856 case MOZ_GTK_MENUARROW:
2857 case MOZ_GTK_TOOLBARBUTTON_ARROW:
2858 case MOZ_GTK_TOOLBAR:
2859 case MOZ_GTK_MENUBAR:
2860 case MOZ_GTK_TAB_SCROLLARROW:
2861 return MOZ_GTK_SUCCESS;
2862 default:
2863 g_warning("Unsupported widget type: %d", widget);
2864 return MOZ_GTK_UNKNOWN_WIDGET;
2865 }
2866 /* TODO - we're still missing some widget implementations */
2867 if (w) {
2868 moz_gtk_add_style_border(gtk_widget_get_style_context(w),
2869 left, top, right, bottom);
2870 }
2871 return MOZ_GTK_SUCCESS;
2872 }
2873
2874 gint
2875 moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height)
2876 {
2877 /*
2878 * We get the requisition of the drop down button, which includes
2879 * all padding, border and focus line widths the button uses,
2880 * as well as the minimum arrow size and its padding
2881 * */
2882 GtkRequisition requisition;
2883 ensure_combo_box_entry_widgets();
2884
2885 gtk_widget_size_request(gComboBoxEntryButtonWidget, &requisition);
2886 *width = requisition.width;
2887 *height = requisition.height;
2888
2889 return MOZ_GTK_SUCCESS;
2890 }
2891
2892 gint
2893 moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height)
2894 {
2895 gint arrow_size;
2896
2897 ensure_tab_widget();
2898 gtk_widget_style_get(gTabWidget,
2899 "scroll-arrow-hlength", &arrow_size,
2900 NULL);
2901
2902 *height = *width = arrow_size;
2903
2904 return MOZ_GTK_SUCCESS;
2905 }
2906
2907 gint
2908 moz_gtk_get_arrow_size(gint* width, gint* height)
2909 {
2910 GtkRequisition requisition;
2911 ensure_button_arrow_widget();
2912
2913 gtk_widget_size_request(gButtonArrowWidget, &requisition);
2914 *width = requisition.width;
2915 *height = requisition.height;
2916
2917 return MOZ_GTK_SUCCESS;
2918 }
2919
2920 gint
2921 moz_gtk_get_toolbar_separator_width(gint* size)
2922 {
2923 gboolean wide_separators;
2924 gint separator_width;
2925 GtkStyleContext* style;
2926 GtkBorder border;
2927
2928 ensure_toolbar_widget();
2929 style = gtk_widget_get_style_context(gToolbarWidget);
2930
2931 gtk_widget_style_get(gToolbarWidget,
2932 "space-size", size,
2933 "wide-separators", &wide_separators,
2934 "separator-width", &separator_width,
2935 NULL);
2936 /* Just in case... */
2937 gtk_style_context_get_border(style, 0, &border);
2938 *size = MAX(*size, (wide_separators ? separator_width : border.left));
2939 return MOZ_GTK_SUCCESS;
2940 }
2941
2942 gint
2943 moz_gtk_get_expander_size(gint* size)
2944 {
2945 ensure_expander_widget();
2946 gtk_widget_style_get(gExpanderWidget,
2947 "expander-size", size,
2948 NULL);
2949
2950 return MOZ_GTK_SUCCESS;
2951 }
2952
2953 gint
2954 moz_gtk_get_treeview_expander_size(gint* size)
2955 {
2956 ensure_tree_view_widget();
2957 gtk_widget_style_get(gTreeViewWidget,
2958 "expander-size", size,
2959 NULL);
2960
2961 return MOZ_GTK_SUCCESS;
2962 }
2963
2964 // See gtk_menu_item_draw() for reference.
2965 gint
2966 moz_gtk_get_menu_separator_height(gint *size)
2967 {
2968 gboolean wide_separators;
2969 gint separator_height;
2970 GtkBorder padding;
2971 GtkStyleContext* style;
2972 guint border_width;
2973
2974 ensure_menu_separator_widget();
2975
2976 gtk_widget_style_get(gMenuSeparatorWidget,
2977 "wide-separators", &wide_separators,
2978 "separator-height", &separator_height,
2979 NULL);
2980
2981 border_width = gtk_container_get_border_width(GTK_CONTAINER(gMenuSeparatorWidget));
2982
2983 style = gtk_widget_get_style_context(gMenuSeparatorWidget);
2984 gtk_style_context_get_padding(style, 0, &padding);
2985
2986 *size = padding.top + padding.bottom + border_width*2;
2987 *size += (wide_separators) ? separator_height : 1;
2988
2989 return MOZ_GTK_SUCCESS;
2990 }
2991
2992 gint
2993 moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height)
2994 {
2995 GtkWidget* widget;
2996
2997 ensure_scale_widget();
2998 widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget);
2999
3000 gtk_widget_style_get (widget,
3001 "slider_length", thumb_length,
3002 "slider_width", thumb_height,
3003 NULL);
3004
3005 return MOZ_GTK_SUCCESS;
3006 }
3007
3008 gint
3009 moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
3010 {
3011 ensure_scrollbar_widget();
3012
3013 gtk_widget_style_get (gHorizScrollbarWidget,
3014 "slider_width", &metrics->slider_width,
3015 "trough_border", &metrics->trough_border,
3016 "stepper_size", &metrics->stepper_size,
3017 "stepper_spacing", &metrics->stepper_spacing,
3018 NULL);
3019
3020 metrics->min_slider_size =
3021 gtk_range_get_min_slider_size(GTK_RANGE(gHorizScrollbarWidget));
3022
3023 return MOZ_GTK_SUCCESS;
3024 }
3025
3026 gboolean
3027 moz_gtk_images_in_menus()
3028 {
3029 gboolean result;
3030 GtkSettings* settings;
3031
3032 ensure_image_menu_item_widget();
3033 settings = gtk_widget_get_settings(gImageMenuItemWidget);
3034
3035 g_object_get(settings, "gtk-menu-images", &result, NULL);
3036 return result;
3037 }
3038
3039 gboolean
3040 moz_gtk_images_in_buttons()
3041 {
3042 gboolean result;
3043 GtkSettings* settings;
3044
3045 ensure_button_widget();
3046 settings = gtk_widget_get_settings(gButtonWidget);
3047
3048 g_object_get(settings, "gtk-button-images", &result, NULL);
3049 return result;
3050 }
3051
3052 /* cairo_t *cr argument has to be a system-cairo. */
3053 gint
3054 moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t *cr,
3055 GdkRectangle* rect,
3056 GtkWidgetState* state, gint flags,
3057 GtkTextDirection direction)
3058 {
3059 /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
3060 */
3061 cairo_new_path(cr);
3062
3063 switch (widget) {
3064 case MOZ_GTK_BUTTON:
3065 if (state->depressed) {
3066 ensure_toggle_menu_button_widget();
3067 return moz_gtk_button_paint(cr, rect, state,
3068 (GtkReliefStyle) flags,
3069 gToggleMenuButtonWidget, direction);
3070 }
3071 ensure_button_widget();
3072 return moz_gtk_button_paint(cr, rect, state,
3073 (GtkReliefStyle) flags, gButtonWidget,
3074 direction);
3075 break;
3076 case MOZ_GTK_CHECKBUTTON:
3077 case MOZ_GTK_RADIOBUTTON:
3078 return moz_gtk_toggle_paint(cr, rect, state,
3079 !!(flags & MOZ_GTK_WIDGET_CHECKED),
3080 !!(flags & MOZ_GTK_WIDGET_INCONSISTENT),
3081 (widget == MOZ_GTK_RADIOBUTTON),
3082 direction);
3083 break;
3084 case MOZ_GTK_SCROLLBAR_BUTTON:
3085 return moz_gtk_scrollbar_button_paint(cr, rect, state,
3086 (GtkScrollbarButtonFlags) flags,
3087 direction);
3088 break;
3089 case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
3090 case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
3091 return moz_gtk_scrollbar_trough_paint(widget, cr, rect,
3092 state, direction);
3093 break;
3094 case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
3095 case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
3096 return moz_gtk_scrollbar_thumb_paint(widget, cr, rect,
3097 state, direction);
3098 break;
3099 case MOZ_GTK_SCALE_HORIZONTAL:
3100 case MOZ_GTK_SCALE_VERTICAL:
3101 return moz_gtk_scale_paint(cr, rect, state,
3102 (GtkOrientation) flags, direction);
3103 break;
3104 case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
3105 case MOZ_GTK_SCALE_THUMB_VERTICAL:
3106 return moz_gtk_scale_thumb_paint(cr, rect, state,
3107 (GtkOrientation) flags, direction);
3108 break;
3109 case MOZ_GTK_SPINBUTTON:
3110 return moz_gtk_spin_paint(cr, rect, direction);
3111 break;
3112 case MOZ_GTK_SPINBUTTON_UP:
3113 case MOZ_GTK_SPINBUTTON_DOWN:
3114 return moz_gtk_spin_updown_paint(cr, rect,
3115 (widget == MOZ_GTK_SPINBUTTON_DOWN),
3116 state, direction);
3117 break;
3118 case MOZ_GTK_SPINBUTTON_ENTRY:
3119 ensure_spin_widget();
3120 return moz_gtk_entry_paint(cr, rect, state,
3121 gSpinWidget, direction);
3122 break;
3123 case MOZ_GTK_GRIPPER:
3124 return moz_gtk_gripper_paint(cr, rect, state,
3125 direction);
3126 break;
3127 case MOZ_GTK_TREEVIEW:
3128 return moz_gtk_treeview_paint(cr, rect, state,
3129 direction);
3130 break;
3131 case MOZ_GTK_TREE_HEADER_CELL:
3132 return moz_gtk_tree_header_cell_paint(cr, rect, state,
3133 flags, direction);
3134 break;
3135 case MOZ_GTK_TREE_HEADER_SORTARROW:
3136 return moz_gtk_tree_header_sort_arrow_paint(cr, rect,
3137 state,
3138 (GtkArrowType) flags,
3139 direction);
3140 break;
3141 case MOZ_GTK_TREEVIEW_EXPANDER:
3142 return moz_gtk_treeview_expander_paint(cr, rect, state,
3143 (GtkExpanderStyle) flags, direction);
3144 break;
3145 case MOZ_GTK_ENTRY:
3146 ensure_entry_widget();
3147 return moz_gtk_entry_paint(cr, rect, state,
3148 gEntryWidget, direction);
3149 break;
3150 case MOZ_GTK_DROPDOWN:
3151 return moz_gtk_combo_box_paint(cr, rect, state,
3152 (gboolean) flags, direction);
3153 break;
3154 case MOZ_GTK_DROPDOWN_ARROW:
3155 return moz_gtk_combo_box_entry_button_paint(cr, rect,
3156 state, flags, direction);
3157 break;
3158 case MOZ_GTK_DROPDOWN_ENTRY:
3159 ensure_combo_box_entry_widgets();
3160 return moz_gtk_entry_paint(cr, rect, state,
3161 gComboBoxEntryTextareaWidget, direction);
3162 break;
3163 case MOZ_GTK_CHECKBUTTON_CONTAINER:
3164 case MOZ_GTK_RADIOBUTTON_CONTAINER:
3165 return moz_gtk_container_paint(cr, rect, state,
3166 (widget == MOZ_GTK_RADIOBUTTON_CONTAINER),
3167 direction);
3168 break;
3169 case MOZ_GTK_CHECKBUTTON_LABEL:
3170 case MOZ_GTK_RADIOBUTTON_LABEL:
3171 return moz_gtk_toggle_label_paint(cr, rect, state,
3172 (widget == MOZ_GTK_RADIOBUTTON_LABEL),
3173 direction);
3174 break;
3175 case MOZ_GTK_TOOLBAR:
3176 return moz_gtk_toolbar_paint(cr, rect, direction);
3177 break;
3178 case MOZ_GTK_TOOLBAR_SEPARATOR:
3179 return moz_gtk_toolbar_separator_paint(cr, rect,
3180 direction);
3181 break;
3182 case MOZ_GTK_TOOLTIP:
3183 return moz_gtk_tooltip_paint(cr, rect, direction);
3184 break;
3185 case MOZ_GTK_FRAME:
3186 return moz_gtk_frame_paint(cr, rect, direction);
3187 break;
3188 case MOZ_GTK_RESIZER:
3189 return moz_gtk_resizer_paint(cr, rect, state,
3190 direction);
3191 break;
3192 case MOZ_GTK_PROGRESSBAR:
3193 return moz_gtk_progressbar_paint(cr, rect, direction);
3194 break;
3195 case MOZ_GTK_PROGRESS_CHUNK:
3196 case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
3197 case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
3198 return moz_gtk_progress_chunk_paint(cr, rect,
3199 direction, widget);
3200 break;
3201 case MOZ_GTK_TAB:
3202 return moz_gtk_tab_paint(cr, rect, state,
3203 (GtkTabFlags) flags, direction);
3204 break;
3205 case MOZ_GTK_TABPANELS:
3206 return moz_gtk_tabpanels_paint(cr, rect, direction);
3207 break;
3208 case MOZ_GTK_TAB_SCROLLARROW:
3209 return moz_gtk_tab_scroll_arrow_paint(cr, rect, state,
3210 (GtkArrowType) flags, direction);
3211 break;
3212 case MOZ_GTK_MENUBAR:
3213 return moz_gtk_menu_bar_paint(cr, rect, direction);
3214 break;
3215 case MOZ_GTK_MENUPOPUP:
3216 return moz_gtk_menu_popup_paint(cr, rect, direction);
3217 break;
3218 case MOZ_GTK_MENUSEPARATOR:
3219 return moz_gtk_menu_separator_paint(cr, rect,
3220 direction);
3221 break;
3222 case MOZ_GTK_MENUITEM:
3223 return moz_gtk_menu_item_paint(cr, rect, state, flags,
3224 direction);
3225 break;
3226 case MOZ_GTK_MENUARROW:
3227 return moz_gtk_menu_arrow_paint(cr, rect, state,
3228 direction);
3229 break;
3230 case MOZ_GTK_TOOLBARBUTTON_ARROW:
3231 return moz_gtk_arrow_paint(cr, rect, state,
3232 (GtkArrowType) flags, direction);
3233 break;
3234 case MOZ_GTK_CHECKMENUITEM:
3235 case MOZ_GTK_RADIOMENUITEM:
3236 return moz_gtk_check_menu_item_paint(cr, rect, state,
3237 (gboolean) flags,
3238 (widget == MOZ_GTK_RADIOMENUITEM),
3239 direction);
3240 break;
3241 case MOZ_GTK_SPLITTER_HORIZONTAL:
3242 return moz_gtk_vpaned_paint(cr, rect, state);
3243 break;
3244 case MOZ_GTK_SPLITTER_VERTICAL:
3245 return moz_gtk_hpaned_paint(cr, rect, state);
3246 break;
3247 case MOZ_GTK_WINDOW:
3248 return moz_gtk_window_paint(cr, rect, direction);
3249 break;
3250 default:
3251 g_warning("Unknown widget type: %d", widget);
3252 }
3253
3254 return MOZ_GTK_UNKNOWN_WIDGET;
3255 }
3256
3257 GtkWidget* moz_gtk_get_scrollbar_widget(void)
3258 {
3259 NS_ASSERTION(is_initialized, "Forgot to call moz_gtk_init()");
3260 ensure_scrollbar_widget();
3261 return gHorizScrollbarWidget;
3262 }
3263
3264 gint
3265 moz_gtk_shutdown()
3266 {
3267 GtkWidgetClass *entry_class;
3268
3269 if (gTooltipWidget)
3270 gtk_widget_destroy(gTooltipWidget);
3271 /* This will destroy all of our widgets */
3272 if (gProtoWindow)
3273 gtk_widget_destroy(gProtoWindow);
3274
3275 /* TODO - replace it with appropriate widget */
3276 if (gTreeHeaderSortArrowWidget)
3277 gtk_widget_destroy(gTreeHeaderSortArrowWidget);
3278
3279 gProtoWindow = NULL;
3280 gProtoLayout = NULL;
3281 gButtonWidget = NULL;
3282 gToggleMenuButtonWidget = NULL;
3283 gButtonArrowWidget = NULL;
3284 gCheckboxWidget = NULL;
3285 gRadiobuttonWidget = NULL;
3286 gHorizScrollbarWidget = NULL;
3287 gVertScrollbarWidget = NULL;
3288 gSpinWidget = NULL;
3289 gHScaleWidget = NULL;
3290 gVScaleWidget = NULL;
3291 gEntryWidget = NULL;
3292 gComboBoxWidget = NULL;
3293 gComboBoxButtonWidget = NULL;
3294 gComboBoxSeparatorWidget = NULL;
3295 gComboBoxArrowWidget = NULL;
3296 gComboBoxEntryWidget = NULL;
3297 gComboBoxEntryButtonWidget = NULL;
3298 gComboBoxEntryArrowWidget = NULL;
3299 gComboBoxEntryTextareaWidget = NULL;
3300 gHandleBoxWidget = NULL;
3301 gToolbarWidget = NULL;
3302 gStatusbarWidget = NULL;
3303 gFrameWidget = NULL;
3304 gProgressWidget = NULL;
3305 gTabWidget = NULL;
3306 gTooltipWidget = NULL;
3307 gMenuBarWidget = NULL;
3308 gMenuBarItemWidget = NULL;
3309 gMenuPopupWidget = NULL;
3310 gMenuItemWidget = NULL;
3311 gImageMenuItemWidget = NULL;
3312 gCheckMenuItemWidget = NULL;
3313 gTreeViewWidget = NULL;
3314 gMiddleTreeViewColumn = NULL;
3315 gTreeHeaderCellWidget = NULL;
3316 gTreeHeaderSortArrowWidget = NULL;
3317 gExpanderWidget = NULL;
3318 gToolbarSeparatorWidget = NULL;
3319 gMenuSeparatorWidget = NULL;
3320 gHPanedWidget = NULL;
3321 gVPanedWidget = NULL;
3322 gScrolledWindowWidget = NULL;
3323
3324 entry_class = g_type_class_peek(GTK_TYPE_ENTRY);
3325 g_type_class_unref(entry_class);
3326
3327 is_initialized = FALSE;
3328
3329 return MOZ_GTK_SUCCESS;
3330 }

mercurial