Back to index

lightning-sunbird  0.9+nobinonly
gtk2drawing.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Brian Ryner <bryner@brianryner.com>  (Original Author)
00024  *  Pierre Chanial <p_ch@verizon.net>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 /*
00041  * This file contains painting functions for each of the gtk2 widgets.
00042  * Adapted from the gtkdrawing.c, and gtk+2.0 source.
00043  */
00044 
00045 #include <gtk/gtk.h>
00046 #include <gdk/gdkprivate.h>
00047 #include <string.h>
00048 #include "gtkdrawing.h"
00049 
00050 #include <math.h>
00051 
00052 #define XTHICKNESS(style) (style->xthickness)
00053 #define YTHICKNESS(style) (style->ythickness)
00054 #define WINDOW_IS_MAPPED(window) ((window) && GDK_IS_WINDOW(window) && gdk_window_is_visible(window))
00055 
00056 static GtkWidget* gProtoWindow;
00057 static GtkWidget* gButtonWidget;
00058 static GtkWidget* gCheckboxWidget;
00059 static GtkWidget* gRadiobuttonWidget;
00060 static GtkWidget* gHorizScrollbarWidget;
00061 static GtkWidget* gVertScrollbarWidget;
00062 static GtkWidget* gEntryWidget;
00063 static GtkWidget* gArrowWidget;
00064 static GtkWidget* gOptionMenuWidget;
00065 static GtkWidget* gDropdownButtonWidget;
00066 static GtkWidget* gHandleBoxWidget;
00067 static GtkWidget* gToolbarWidget;
00068 static GtkWidget* gFrameWidget;
00069 static GtkWidget* gProgressWidget;
00070 static GtkWidget* gTabWidget;
00071 static GtkWidget* gTooltipWidget;
00072 static GtkWidget* gMenuBarWidget;
00073 static GtkWidget* gMenuBarItemWidget;
00074 static GtkWidget* gMenuPopupWidget;
00075 static GtkWidget* gMenuItemWidget;
00076 static GtkWidget* gCheckMenuItemWidget;
00077 
00078 static GtkShadowType gMenuBarShadowType;
00079 static GtkShadowType gToolbarShadowType;
00080 
00081 static style_prop_t style_prop_func;
00082 static gboolean have_menu_shadow_type;
00083 
00084 gint
00085 moz_gtk_enable_style_props(style_prop_t styleGetProp)
00086 {
00087     style_prop_func = styleGetProp;
00088     return MOZ_GTK_SUCCESS;
00089 }
00090 
00091 static gint
00092 ensure_window_widget()
00093 {
00094     if (!gProtoWindow) {
00095         gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
00096         gtk_widget_realize(gProtoWindow);
00097     }
00098     return MOZ_GTK_SUCCESS;
00099 }
00100 
00101 static gint
00102 setup_widget_prototype(GtkWidget* widget)
00103 {
00104     static GtkWidget* protoLayout;
00105     ensure_window_widget();
00106     if (!protoLayout) {
00107         protoLayout = gtk_fixed_new();
00108         gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout);
00109     }
00110 
00111     gtk_container_add(GTK_CONTAINER(protoLayout), widget);
00112     gtk_widget_realize(widget);
00113     return MOZ_GTK_SUCCESS;
00114 }
00115 
00116 static gint
00117 ensure_button_widget()
00118 {
00119     if (!gButtonWidget) {
00120         gButtonWidget = gtk_button_new_with_label("M");
00121         setup_widget_prototype(gButtonWidget);
00122     }
00123     return MOZ_GTK_SUCCESS;
00124 }
00125 
00126 static gint
00127 ensure_checkbox_widget()
00128 {
00129     if (!gCheckboxWidget) {
00130         gCheckboxWidget = gtk_check_button_new_with_label("M");
00131         setup_widget_prototype(gCheckboxWidget);
00132     }
00133     return MOZ_GTK_SUCCESS;
00134 }
00135 
00136 static gint
00137 ensure_radiobutton_widget()
00138 {
00139     if (!gRadiobuttonWidget) {
00140         gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M");
00141         setup_widget_prototype(gRadiobuttonWidget);
00142     }
00143     return MOZ_GTK_SUCCESS;
00144 }
00145 
00146 static gint
00147 ensure_scrollbar_widget()
00148 {
00149     if (!gVertScrollbarWidget) {
00150         gVertScrollbarWidget = gtk_vscrollbar_new(NULL);
00151         setup_widget_prototype(gVertScrollbarWidget);
00152     }
00153     if (!gHorizScrollbarWidget) {
00154         gHorizScrollbarWidget = gtk_hscrollbar_new(NULL);
00155         setup_widget_prototype(gHorizScrollbarWidget);
00156     }
00157     return MOZ_GTK_SUCCESS;
00158 }
00159 
00160 static gint
00161 ensure_entry_widget()
00162 {
00163     if (!gEntryWidget) {
00164         gEntryWidget = gtk_entry_new();
00165         setup_widget_prototype(gEntryWidget);
00166     }
00167     return MOZ_GTK_SUCCESS;
00168 }
00169 
00170 static gint
00171 ensure_option_menu_widget()
00172 {
00173     if (!gOptionMenuWidget) {
00174         gOptionMenuWidget = gtk_option_menu_new();
00175         setup_widget_prototype(gOptionMenuWidget);        
00176     }
00177     return MOZ_GTK_SUCCESS;
00178 }
00179 
00180 static gint
00181 ensure_arrow_widget()
00182 {
00183     if (!gArrowWidget) {
00184         gDropdownButtonWidget = gtk_button_new();
00185         setup_widget_prototype(gDropdownButtonWidget);
00186         gArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
00187         gtk_container_add(GTK_CONTAINER(gDropdownButtonWidget), gArrowWidget);
00188         gtk_widget_realize(gArrowWidget);
00189     }
00190     return MOZ_GTK_SUCCESS;
00191 }
00192 
00193 static gint
00194 ensure_handlebox_widget()
00195 {
00196     if (!gHandleBoxWidget) {
00197         gHandleBoxWidget = gtk_handle_box_new();
00198         setup_widget_prototype(gHandleBoxWidget);
00199     }
00200     return MOZ_GTK_SUCCESS;
00201 }
00202 
00203 static gint
00204 ensure_toolbar_widget()
00205 {
00206     if (!gToolbarWidget) {
00207         ensure_handlebox_widget();
00208         gToolbarWidget = gtk_toolbar_new();
00209         gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget);
00210         gtk_widget_realize(gToolbarWidget);
00211         gtk_widget_style_get(gToolbarWidget, "shadow_type", &gToolbarShadowType,
00212                              NULL);
00213     }
00214     return MOZ_GTK_SUCCESS;
00215 }
00216 
00217 static gint
00218 ensure_tooltip_widget()
00219 {
00220     if (!gTooltipWidget) {
00221         gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP);
00222         gtk_widget_realize(gTooltipWidget);
00223     }
00224     return MOZ_GTK_SUCCESS;
00225 }
00226 
00227 static gint
00228 ensure_tab_widget()
00229 {
00230     if (!gTabWidget) {
00231         gTabWidget = gtk_notebook_new();
00232         setup_widget_prototype(gTabWidget);
00233     }
00234     return MOZ_GTK_SUCCESS;
00235 }
00236 
00237 static gint
00238 ensure_progress_widget()
00239 {
00240     if (!gProgressWidget) {
00241         gProgressWidget = gtk_progress_bar_new();
00242         setup_widget_prototype(gProgressWidget);
00243     }
00244     return MOZ_GTK_SUCCESS;
00245 }
00246 
00247 static gint
00248 ensure_frame_widget()
00249 {
00250     if (!gFrameWidget) {
00251         gFrameWidget = gtk_frame_new(NULL);
00252         setup_widget_prototype(gFrameWidget);
00253     }
00254     return MOZ_GTK_SUCCESS;
00255 }
00256 
00257 static gint
00258 ensure_menu_bar_widget()
00259 {
00260     if (!gMenuBarWidget) {
00261         gMenuBarWidget = gtk_menu_bar_new();
00262         setup_widget_prototype(gMenuBarWidget);
00263        gtk_widget_style_get(gMenuBarWidget, "shadow_type", &gMenuBarShadowType,
00264                             NULL);
00265     }
00266     return MOZ_GTK_SUCCESS;
00267 }
00268 
00269 static gint
00270 ensure_menu_bar_item_widget()
00271 {
00272     if (!gMenuBarItemWidget) {
00273         ensure_menu_bar_widget();
00274         gMenuBarItemWidget = gtk_menu_item_new();
00275         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget),
00276                               gMenuBarItemWidget);
00277         gtk_widget_realize(gMenuBarItemWidget);
00278     }
00279     return MOZ_GTK_SUCCESS;
00280 }
00281 
00282 static gint
00283 ensure_menu_popup_widget()
00284 {
00285     if (!gMenuPopupWidget) {
00286         ensure_menu_bar_item_widget();
00287         gMenuPopupWidget = gtk_menu_new();
00288         gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget),
00289                                   gMenuPopupWidget);
00290         gtk_widget_realize(gMenuPopupWidget);
00291     }
00292     return MOZ_GTK_SUCCESS;
00293 }
00294 
00295 static gint
00296 ensure_menu_item_widget()
00297 {
00298     if (!gMenuItemWidget) {
00299         ensure_menu_popup_widget();
00300         gMenuItemWidget = gtk_menu_item_new_with_label("M");
00301         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
00302                               gMenuItemWidget);
00303         gtk_widget_realize(gMenuItemWidget);
00304     }
00305     return MOZ_GTK_SUCCESS;
00306 }
00307 
00308 static gint
00309 ensure_check_menu_item_widget()
00310 {
00311     if (!gCheckMenuItemWidget) {
00312         ensure_menu_popup_widget();
00313         gCheckMenuItemWidget = gtk_check_menu_item_new_with_label("M");
00314         gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget),
00315                               gCheckMenuItemWidget);
00316         gtk_widget_realize(gCheckMenuItemWidget);
00317     }
00318     return MOZ_GTK_SUCCESS;
00319 }
00320 
00321 static GtkStateType
00322 ConvertGtkState(GtkWidgetState* state)
00323 {
00324     if (state->disabled)
00325         return GTK_STATE_INSENSITIVE;
00326     else if (state->inHover)
00327         return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
00328     else
00329         return GTK_STATE_NORMAL;
00330 }
00331 
00332 static gint
00333 TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin)
00334 {
00335     int i;
00336     /* there are 5 gc's in each array, for each of the widget states */
00337     for (i = 0; i < 5; ++i)
00338         gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin);
00339     return MOZ_GTK_SUCCESS;
00340 }
00341 
00342 static gint
00343 TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin)
00344 {
00345     TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin);
00346     TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin);
00347     TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin);
00348     TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin);
00349     TSOffsetStyleGCArray(style->mid_gc, xorigin, yorigin);
00350     TSOffsetStyleGCArray(style->text_gc, xorigin, yorigin);
00351     TSOffsetStyleGCArray(style->base_gc, xorigin, yorigin);
00352     gdk_gc_set_ts_origin(style->black_gc, xorigin, yorigin);
00353     gdk_gc_set_ts_origin(style->white_gc, xorigin, yorigin);
00354     return MOZ_GTK_SUCCESS;
00355 }
00356 
00357 static gint
00358 moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
00359                      GdkRectangle* cliprect, GtkWidgetState* state,
00360                      GtkReliefStyle relief, GtkWidget* widget)
00361 {
00362     GtkShadowType shadow_type;
00363     GtkStyle* style = widget->style;
00364     GtkStateType button_state = ConvertGtkState(state);
00365     gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
00366 
00367     gboolean interior_focus;
00368     gint focus_width, focus_pad;
00369 
00370     moz_gtk_button_get_focus(&interior_focus, &focus_width, &focus_pad);
00371 
00372     if (WINDOW_IS_MAPPED(drawable)) {
00373         gdk_window_set_back_pixmap(drawable, NULL, TRUE);
00374         gdk_window_clear_area(drawable, cliprect->x, cliprect->y,
00375                               cliprect->width, cliprect->height);
00376     }
00377 
00378     gtk_widget_set_state(widget, button_state);
00379 
00380     /*
00381      * XXX fix this code when we have default state working.
00382     if (state->isDefault) {
00383         TSOffsetStyleGCs(style, x, y);
00384         gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
00385                       cliprect, widget, "buttondefault", x, y, width, height);
00386     }
00387 
00388     if (state->canDefault) {
00389         x += XTHICKNESS(style);
00390         y += YTHICKNESS(style);
00391 
00392         width -= 2 * x + default_spacing;
00393         height -= 2 * y + default_spacing;
00394 
00395         x += (1 + default_spacing) / 2;
00396         y += (1 + default_spacing) / 2;
00397     }
00398     */
00399 
00400     if (!interior_focus && state->focused) {
00401         x += focus_width + focus_pad;
00402         y += focus_width + focus_pad;
00403         width -= 2 * (focus_width + focus_pad);
00404         height -= 2 * (focus_width + focus_pad);
00405     }
00406 
00407     shadow_type = button_state == GTK_STATE_ACTIVE ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
00408 
00409     if (relief != GTK_RELIEF_NONE || (button_state != GTK_STATE_NORMAL &&
00410                                       button_state != GTK_STATE_INSENSITIVE)) {
00411         TSOffsetStyleGCs(style, x, y);
00412         /* the following line can trigger an assertion (Crux theme)
00413            file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
00414            assertion `GDK_IS_WINDOW (window)' failed */
00415         gtk_paint_box(style, drawable, button_state, shadow_type, cliprect,
00416                       widget, "button", x, y, width, height);
00417     }
00418 
00419     if (state->focused) {
00420         if (interior_focus) {
00421             x += widget->style->xthickness + focus_pad;
00422             y += widget->style->ythickness + focus_pad;
00423             width -= 2 * (widget->style->xthickness + focus_pad);
00424             height -= 2 * (widget->style->ythickness + focus_pad);
00425         } else {
00426             x -= focus_width + focus_pad;
00427             y -= focus_width + focus_pad;
00428             width += 2 * (focus_width + focus_pad);
00429             height += 2 * (focus_width + focus_pad);
00430         }
00431 
00432         TSOffsetStyleGCs(style, x, y);
00433         gtk_paint_focus(style, drawable, button_state, cliprect,
00434                         widget, "button", x, y, width, height);
00435     }
00436 
00437     return MOZ_GTK_SUCCESS;
00438 }
00439 
00440 gint
00441 moz_gtk_init()
00442 {
00443     have_menu_shadow_type =
00444         (gtk_major_version > 2 ||
00445          (gtk_major_version == 2 && gtk_minor_version >= 1));
00446 
00447     return MOZ_GTK_SUCCESS;
00448 }
00449 
00450 gint
00451 moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing)
00452 {
00453     ensure_checkbox_widget();
00454 
00455     gtk_widget_style_get (gCheckboxWidget,
00456                           "indicator_size", indicator_size,
00457                           "indicator_spacing", indicator_spacing,
00458                           NULL);
00459 
00460     return MOZ_GTK_SUCCESS;
00461 }
00462 
00463 gint
00464 moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing)
00465 {
00466     ensure_radiobutton_widget();
00467 
00468     gtk_widget_style_get (gRadiobuttonWidget,
00469                           "indicator_size", indicator_size,
00470                           "indicator_spacing", indicator_spacing,
00471                           NULL);
00472 
00473     return MOZ_GTK_SUCCESS;
00474 }
00475 
00476 gint
00477 moz_gtk_checkbox_get_focus(gboolean* interior_focus,
00478                            gint* focus_width, gint* focus_pad)
00479 {
00480     ensure_checkbox_widget();
00481 
00482     gtk_widget_style_get (gCheckboxWidget,
00483                           "interior-focus", interior_focus,
00484                           "focus-line-width", focus_width,
00485                           "focus-padding", focus_pad,
00486                           NULL);
00487 
00488     return MOZ_GTK_SUCCESS;
00489 }
00490 
00491 gint
00492 moz_gtk_radio_get_focus(gboolean* interior_focus,
00493                         gint* focus_width, gint* focus_pad)
00494 {
00495     ensure_radiobutton_widget();
00496 
00497     gtk_widget_style_get (gRadiobuttonWidget,
00498                           "interior-focus", interior_focus,
00499                           "focus-line-width", focus_width,
00500                           "focus-padding", focus_pad,
00501                           NULL);
00502 
00503     return MOZ_GTK_SUCCESS;
00504 }
00505 
00506 gint
00507 moz_gtk_button_get_focus(gboolean* interior_focus,
00508                          gint* focus_width, gint* focus_pad)
00509 {
00510     ensure_button_widget();
00511 
00512     gtk_widget_style_get (gButtonWidget,
00513                           "interior-focus", interior_focus,
00514                           "focus-line-width", focus_width,
00515                           "focus-padding", focus_pad,
00516                           NULL);
00517 
00518     return MOZ_GTK_SUCCESS;
00519 }
00520 
00521 static gint
00522 moz_gtk_option_menu_get_metrics(gboolean* interior_focus,
00523                                 GtkRequisition* indicator_size,
00524                                 GtkBorder* indicator_spacing,
00525                                 gint* focus_width,
00526                                 gint* focus_pad)
00527 {
00528     static const GtkRequisition default_indicator_size = { 7, 13 };
00529     static const GtkBorder default_indicator_spacing = { 7, 5, 2, 2 };
00530     /* these default values are not used in gtkoptionmenu.c
00531     static const gboolean default_interior_focus = TRUE;
00532     static const gint default_focus_width = 1;
00533     static const gint default_focus_pad = 0; */
00534     GtkRequisition *tmp_indicator_size;
00535     GtkBorder *tmp_indicator_spacing;
00536 
00537     gtk_widget_style_get(gOptionMenuWidget,
00538                          "interior_focus", interior_focus,
00539                          "indicator_size", &tmp_indicator_size,
00540                          "indicator_spacing", &tmp_indicator_spacing,
00541                          "focus_line_width", focus_width,
00542                          "focus_padding", focus_pad,
00543                          NULL);
00544 
00545     if (tmp_indicator_size)
00546         *indicator_size = *tmp_indicator_size;
00547     else
00548         *indicator_size = default_indicator_size;
00549     if (tmp_indicator_spacing)
00550         *indicator_spacing = *tmp_indicator_spacing;
00551     else
00552         *indicator_spacing = default_indicator_spacing;
00553 
00554     gtk_requisition_free(tmp_indicator_size);
00555     gtk_border_free(tmp_indicator_spacing);
00556  
00557     return MOZ_GTK_SUCCESS;
00558 }
00559 
00560 static gint
00561 moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect,
00562                      GdkRectangle* cliprect, GtkWidgetState* state,
00563                      gboolean selected, gboolean isradio)
00564 {
00565     GtkStateType state_type = ConvertGtkState(state);
00566     GtkShadowType shadow_type = (selected)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
00567     gint indicator_size, indicator_spacing;
00568     gint x, y, width, height;
00569     GtkWidget *w;
00570     GtkStyle *style;
00571 
00572     if (isradio) {
00573         moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
00574         w = gRadiobuttonWidget;
00575     } else {
00576         moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
00577         w = gCheckboxWidget;
00578     }
00579 
00580     /* offset by indicator_spacing, and centered vertically within the rect */
00581     x = rect->x + indicator_spacing;
00582     y = rect->y + (rect->height - indicator_size) / 2;
00583     width = indicator_size;
00584     height = indicator_size;
00585   
00586     style = w->style;
00587     TSOffsetStyleGCs(style, x, y);
00588 
00589     /* Some themes check the widget state themselves. */
00590     if (state->focused)
00591         GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
00592     else
00593         GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
00594 
00595     if (state_type != GTK_STATE_INSENSITIVE)
00596          GTK_WIDGET_SET_FLAGS(w, GTK_SENSITIVE);
00597     else
00598          GTK_WIDGET_UNSET_FLAGS(w, GTK_SENSITIVE);
00599 
00600     GTK_TOGGLE_BUTTON(w)->active = selected;
00601       
00602     if (isradio) {
00603         gtk_paint_option(style, drawable, state_type, shadow_type, cliprect,
00604                          gRadiobuttonWidget, "radiobutton", x, y,
00605                          width, height);
00606     }
00607     else {
00608         gtk_paint_check(style, drawable, state_type, shadow_type, cliprect, 
00609                         gCheckboxWidget, "checkbutton", x, y, width, height);
00610     }
00611 
00612     return MOZ_GTK_SUCCESS;
00613 }
00614 
00615 static gint
00616 calculate_arrow_dimensions(GdkRectangle* rect, GdkRectangle* arrow_rect)
00617 {
00618     GtkMisc* misc = GTK_MISC(gArrowWidget);
00619 
00620     gint extent = MIN(rect->width - misc->xpad * 2,
00621                       rect->height - misc->ypad * 2);
00622 
00623     arrow_rect->x = ((rect->x + misc->xpad) * (1.0 - misc->xalign) +
00624                      (rect->x + rect->width - extent - misc->xpad) *
00625                      misc->xalign);
00626 
00627     arrow_rect->y = ((rect->y + misc->ypad) * (1.0 - misc->yalign) +
00628                      (rect->y + rect->height - extent - misc->ypad) *
00629                      misc->yalign);
00630 
00631     arrow_rect->width = arrow_rect->height = extent;
00632 
00633     return MOZ_GTK_SUCCESS;
00634 }
00635 
00636 static gint
00637 moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
00638                                GdkRectangle* cliprect, GtkWidgetState* state,
00639                                GtkArrowType type)
00640 {
00641     GtkStateType state_type = ConvertGtkState(state);
00642     GtkShadowType shadow_type = (state->active) ?
00643         GTK_SHADOW_IN : GTK_SHADOW_OUT;
00644     GdkRectangle button_rect;
00645     GdkRectangle arrow_rect;
00646     GtkStyle* style;
00647     GtkAdjustment *adj;
00648     GtkScrollbar *scrollbar;
00649 
00650     ensure_scrollbar_widget();
00651 
00652     if (type < 2)
00653         scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
00654     else
00655         scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
00656 
00657     style = GTK_WIDGET(scrollbar)->style;
00658 
00659     ensure_arrow_widget();
00660   
00661     calculate_arrow_dimensions(rect, &button_rect);
00662     TSOffsetStyleGCs(style, button_rect.x, button_rect.y);
00663 
00664     gtk_paint_box(style, drawable, state_type, shadow_type, cliprect,
00665                   GTK_WIDGET(scrollbar),
00666                   (type < 2) ? "vscrollbar" : "hscrollbar",
00667                   button_rect.x, button_rect.y, button_rect.width,
00668                   button_rect.height);
00669 
00670     arrow_rect.width = button_rect.width / 2;
00671     arrow_rect.height = button_rect.height / 2;
00672     arrow_rect.x = button_rect.x + (button_rect.width - arrow_rect.width) / 2;
00673     arrow_rect.y = button_rect.y +
00674         (button_rect.height - arrow_rect.height) / 2;  
00675 
00676     gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
00677                     GTK_WIDGET(scrollbar), (type < 2) ?
00678                     "vscrollbar" : "hscrollbar", 
00679                     type, TRUE, arrow_rect.x, arrow_rect.y, arrow_rect.width,
00680                     arrow_rect.height);
00681 
00682     return MOZ_GTK_SUCCESS;
00683 }
00684 
00685 static gint
00686 moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget,
00687                                GdkDrawable* drawable, GdkRectangle* rect,
00688                                GdkRectangle* cliprect, GtkWidgetState* state)
00689 {
00690     GtkStyle* style;
00691     GtkScrollbar *scrollbar;
00692 
00693     ensure_scrollbar_widget();
00694 
00695     if (widget ==  MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL)
00696         scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
00697     else
00698         scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
00699 
00700     style = GTK_WIDGET(scrollbar)->style;
00701 
00702     TSOffsetStyleGCs(style, rect->x, rect->y);
00703     gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_ACTIVE,
00704                                        cliprect, rect->x, rect->y,
00705                                        rect->width, rect->height);
00706 
00707     gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect,
00708                   GTK_WIDGET(scrollbar), "trough", rect->x, rect->y,
00709                   rect->width, rect->height);
00710 
00711     if (state->focused) {
00712         gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect,
00713                         GTK_WIDGET(scrollbar), "trough",
00714                         rect->x, rect->y, rect->width, rect->height);
00715     }
00716 
00717     return MOZ_GTK_SUCCESS;
00718 }
00719 
00720 static gint
00721 moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget,
00722                               GdkDrawable* drawable, GdkRectangle* rect,
00723                               GdkRectangle* cliprect, GtkWidgetState* state)
00724 {
00725     GtkStateType state_type = (state->inHover || state->active) ?
00726         GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
00727     GtkStyle* style;
00728     GtkScrollbar *scrollbar;
00729     GtkAdjustment *adj;
00730 
00731     ensure_scrollbar_widget();
00732 
00733     if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL)
00734         scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
00735     else
00736         scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
00737 
00738     /* Make sure to set the scrollbar range before painting so that
00739        everything is drawn properly.  At least the bluecurve (and
00740        maybe other) themes don't draw the top or bottom black line
00741        surrounding the scrollbar if the theme thinks that it's butted
00742        up against the scrollbar arrows.  Note the increases of the
00743        clip rect below. */
00744     /* Changing the cliprect is pretty bogus. This lets themes draw
00745        outside the frame, which means we don't invalidate them
00746        correctly. See bug 297508. But some themes do seem to need
00747        it. So we modify the frame's overflow area to account for what
00748        we're doing here; see nsNativeThemeGTK::GetWidgetOverflow. */
00749     adj = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
00750 
00751     if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) {
00752         cliprect->x -= 1;
00753         cliprect->width += 2;
00754         adj->page_size = rect->width;
00755     }
00756     else {
00757         cliprect->y -= 1;
00758         cliprect->height += 2;
00759         adj->page_size = rect->height;
00760     }
00761 
00762     adj->lower = 0;
00763     adj->value = state->curpos;
00764     adj->upper = state->maxpos;
00765     gtk_adjustment_changed(adj);
00766 
00767     style = GTK_WIDGET(scrollbar)->style;
00768 
00769     TSOffsetStyleGCs(style, rect->x, rect->y);
00770 
00771     gtk_paint_slider(style, drawable, state_type, GTK_SHADOW_OUT, cliprect,
00772                      GTK_WIDGET(scrollbar), "slider", rect->x, rect->y,
00773                      rect->width,  rect->height,
00774                      (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
00775                      GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
00776 
00777     return MOZ_GTK_SUCCESS;
00778 }
00779 
00780 static gint
00781 moz_gtk_gripper_paint(GdkDrawable* drawable, GdkRectangle* rect,
00782                       GdkRectangle* cliprect, GtkWidgetState* state)
00783 {
00784     GtkStateType state_type = ConvertGtkState(state);
00785     GtkShadowType shadow_type;
00786     GtkStyle* style;
00787 
00788     ensure_handlebox_widget();
00789     style = gHandleBoxWidget->style;
00790     shadow_type = GTK_HANDLE_BOX(gHandleBoxWidget)->shadow_type;
00791 
00792     TSOffsetStyleGCs(style, rect->x, rect->y);
00793     gtk_paint_box(style, drawable, state_type, shadow_type, cliprect,
00794                   gHandleBoxWidget, "handlebox_bin", rect->x, rect->y,
00795                   rect->width, rect->height);
00796 
00797     return MOZ_GTK_SUCCESS;
00798 }
00799 
00800 static gint
00801 moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect,
00802                     GdkRectangle* cliprect, GtkWidgetState* state)
00803 {
00804     gint x, y, width = rect->width, height = rect->height;
00805     GtkStyle* style;
00806     gboolean interior_focus;
00807     gint focus_width;
00808 
00809     ensure_entry_widget();
00810     style = gEntryWidget->style;
00811 
00812     /* paint the background first */
00813     x = XTHICKNESS(style);
00814     y = YTHICKNESS(style);
00815 
00816     TSOffsetStyleGCs(style, rect->x + x, rect->y + y);
00817     gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
00818                        cliprect, gEntryWidget, "entry_bg",  rect->x + x,
00819                        rect->y + y, rect->width - 2*x, rect->height - 2*y);
00820 
00821     gtk_widget_style_get(gEntryWidget,
00822                          "interior-focus", &interior_focus,
00823                          "focus-line-width", &focus_width,
00824                          NULL);
00825 
00826     /*
00827      * Now paint the shadow and focus border.
00828      *
00829      * gtk+ is able to draw over top of the entry when it gains focus,
00830      * so the non-focused text border is implicitly already drawn when
00831      * the entry is drawn in a focused state.
00832      *
00833      * Gecko doesn't quite work this way, so always draw the non-focused
00834      * shadow, then draw the shadow again, inset, if we're focused.
00835      */
00836 
00837     x = rect->x;
00838     y = rect->y;
00839 
00840     TSOffsetStyleGCs(style, x, y);
00841     gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
00842                      cliprect, gEntryWidget, "entry", x, y, width, height);
00843   
00844 
00845     if (state->focused && !interior_focus) {
00846         x += focus_width;
00847         y += focus_width;
00848         width -= 2 * focus_width;
00849         height -= 2 * focus_width;
00850 
00851         TSOffsetStyleGCs(style, x, y);
00852         gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
00853                          cliprect, gEntryWidget, "entry",
00854                          x, y, width, height);
00855 
00856         TSOffsetStyleGCs(style, rect->x, rect->y);
00857         gtk_paint_focus(style, drawable,  GTK_STATE_NORMAL, cliprect,
00858                         gEntryWidget, "entry",
00859                         rect->x, rect->y, rect->width, rect->height);
00860     }
00861 
00862     return MOZ_GTK_SUCCESS;
00863 }
00864 
00865 static gint
00866 moz_gtk_option_menu_paint(GdkDrawable* drawable, GdkRectangle* rect,
00867                           GdkRectangle* cliprect, GtkWidgetState* state)
00868 {
00869     GtkStyle* style;
00870     GtkStateType state_type = ConvertGtkState(state);
00871     gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
00872     gint tab_x, tab_y;
00873     gboolean interior_focus;
00874     GtkRequisition indicator_size;
00875     GtkBorder indicator_spacing;
00876     gint focus_width;
00877     gint focus_pad;
00878 
00879     ensure_option_menu_widget();
00880     moz_gtk_option_menu_get_metrics(&interior_focus, &indicator_size,
00881                                     &indicator_spacing, &focus_width,
00882                                     &focus_pad);
00883 
00884     style = gOptionMenuWidget->style;
00885 
00886     if (!interior_focus && state->focused) {
00887         x += focus_width + focus_pad;
00888         y += focus_width + focus_pad;
00889         width -= 2 * (focus_width + focus_pad);
00890         height -= 2 * (focus_width + focus_pad);
00891     }
00892 
00893     TSOffsetStyleGCs(style, x, y);
00894     gtk_paint_box(style, drawable, state_type, GTK_SHADOW_OUT,
00895                   cliprect, gOptionMenuWidget, "optionmenu",
00896                   x, y, width, height);
00897       
00898     if (gtk_widget_get_direction(gOptionMenuWidget) == GTK_TEXT_DIR_RTL) {
00899         tab_x = x + indicator_spacing.right + XTHICKNESS(style);
00900     } else {
00901         tab_x = x + width - indicator_size.width - indicator_spacing.right -
00902                 XTHICKNESS(style);
00903     }
00904     tab_y = y + (height - indicator_size.height) / 2;
00905 
00906     TSOffsetStyleGCs(style, tab_x, tab_y);
00907     gtk_paint_tab(style, drawable, state_type, GTK_SHADOW_OUT, cliprect,
00908                   gOptionMenuWidget, "optionmenutab", tab_x, tab_y, 
00909                   indicator_size.width, indicator_size.height);
00910       
00911     if (state->focused) {
00912       if (interior_focus) {
00913           x += XTHICKNESS(style) + focus_pad;
00914           y += YTHICKNESS(style) + focus_pad;
00915           width -= 2 * (XTHICKNESS(style) + focus_pad) +
00916                    indicator_spacing.left + indicator_spacing.right +
00917                    indicator_size.width;
00918           height -= 2 * (YTHICKNESS(style) + focus_pad);
00919           if (gtk_widget_get_direction(gOptionMenuWidget) == GTK_TEXT_DIR_RTL) 
00920               x += indicator_spacing.left + indicator_spacing.right +
00921                    indicator_size.width;
00922       } else {
00923           x -= focus_width + focus_pad;
00924           y -= focus_width + focus_pad;
00925           width += 2 * (focus_width + focus_pad);
00926           height += 2 * (focus_width + focus_pad);
00927       }
00928         
00929       TSOffsetStyleGCs(style, x, y);
00930       gtk_paint_focus (style, drawable, state_type, cliprect, gOptionMenuWidget,
00931                        "button", x, y,  width, height);
00932     }
00933     
00934     return MOZ_GTK_SUCCESS;
00935 }
00936 
00937 static gint
00938 moz_gtk_dropdown_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
00939                              GdkRectangle* cliprect, GtkWidgetState* state)
00940 {
00941     GdkRectangle arrow_rect, real_arrow_rect;
00942     GtkStateType state_type = ConvertGtkState(state);
00943     GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
00944     GtkStyle* style;
00945 
00946     ensure_arrow_widget();
00947     moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
00948                          gDropdownButtonWidget);
00949 
00950     /* This mirrors gtkbutton's child positioning */
00951     style = gDropdownButtonWidget->style;
00952     arrow_rect.x = rect->x + 1 + XTHICKNESS(gDropdownButtonWidget->style);
00953     arrow_rect.y = rect->y + 1 + YTHICKNESS(gDropdownButtonWidget->style);
00954     arrow_rect.width = MAX(1, rect->width - (arrow_rect.x - rect->x) * 2);
00955     arrow_rect.height = MAX(1, rect->height - (arrow_rect.y - rect->y) * 2);
00956 
00957     calculate_arrow_dimensions(&arrow_rect, &real_arrow_rect);
00958     style = gArrowWidget->style;
00959     TSOffsetStyleGCs(style, real_arrow_rect.x, real_arrow_rect.y);
00960 
00961     real_arrow_rect.width = real_arrow_rect.height =
00962         MIN (real_arrow_rect.width, real_arrow_rect.height) * 0.9; 
00963 
00964     real_arrow_rect.x = floor (arrow_rect.x + ((arrow_rect.width - real_arrow_rect.width) / 2) + 0.5);
00965     real_arrow_rect.y = floor (arrow_rect.y + ((arrow_rect.height - real_arrow_rect.height) / 2) + 0.5);
00966 
00967     gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
00968                     gHorizScrollbarWidget, "arrow",  GTK_ARROW_DOWN, TRUE,
00969                     real_arrow_rect.x, real_arrow_rect.y,
00970                     real_arrow_rect.width, real_arrow_rect.height);
00971 
00972     return MOZ_GTK_SUCCESS;
00973 }
00974 
00975 static gint
00976 moz_gtk_container_paint(GdkDrawable* drawable, GdkRectangle* rect,
00977                         GdkRectangle* cliprect, GtkWidgetState* state, 
00978                         gboolean isradio)
00979 {
00980     GtkStateType state_type = ConvertGtkState(state);
00981     GtkStyle* style;
00982     gboolean interior_focus;
00983     gint focus_width, focus_pad;
00984 
00985     if (isradio) {
00986         ensure_radiobutton_widget();
00987         style = gRadiobuttonWidget->style;  
00988         moz_gtk_radio_get_focus(&interior_focus, &focus_width, &focus_pad);
00989     } else {
00990         ensure_checkbox_widget();
00991         style = gCheckboxWidget->style;
00992         moz_gtk_checkbox_get_focus(&interior_focus, &focus_width, &focus_pad);
00993     }
00994 
00995     TSOffsetStyleGCs(style, rect->x, rect->y);
00996 
00997     /* The detail argument for the gtk_paint_* calls below are "checkbutton"
00998        even for radio buttons, to match what gtk does. */
00999 
01000     /* this is for drawing a prelight box */
01001     if (state_type == GTK_STATE_PRELIGHT || state_type == GTK_STATE_ACTIVE) {
01002         gtk_paint_flat_box(style, drawable, GTK_STATE_PRELIGHT, GTK_SHADOW_ETCHED_OUT,
01003                            cliprect, gCheckboxWidget,
01004                            "checkbutton",
01005                            rect->x, rect->y, rect->width, rect->height);
01006     }
01007 
01008     if (state_type != GTK_STATE_NORMAL && state_type != GTK_STATE_PRELIGHT)
01009         state_type = GTK_STATE_NORMAL;
01010 
01011     if (state->focused && !interior_focus) {
01012         gtk_paint_focus(style, drawable, state_type, cliprect, gCheckboxWidget,
01013                         "checkbutton",
01014                         rect->x, rect->y, rect->width, rect->height);
01015     }
01016 
01017     return MOZ_GTK_SUCCESS;
01018 }
01019 
01020 static gint
01021 moz_gtk_toggle_label_paint(GdkDrawable* drawable, GdkRectangle* rect,
01022                            GdkRectangle* cliprect, GtkWidgetState* state, 
01023                            gboolean isradio)
01024 {
01025     GtkStateType state_type;
01026     GtkStyle *style;
01027     GtkWidget *widget;
01028     gboolean interior_focus;
01029 
01030     if (!state->focused)
01031         return MOZ_GTK_SUCCESS;
01032 
01033     if (isradio) {
01034         ensure_radiobutton_widget();
01035         widget = gRadiobuttonWidget;
01036     } else {
01037         ensure_checkbox_widget();
01038         widget = gCheckboxWidget;
01039     }
01040 
01041     gtk_widget_style_get(widget, "interior-focus", &interior_focus, NULL);
01042     if (!interior_focus)
01043         return MOZ_GTK_SUCCESS;
01044 
01045     state_type = ConvertGtkState(state);
01046 
01047     style = widget->style;
01048     TSOffsetStyleGCs(style, rect->x, rect->y);
01049 
01050     /* Always "checkbutton" to match gtkcheckbutton.c */
01051     gtk_paint_focus(style, drawable, state_type, cliprect, widget,
01052                     "checkbutton",
01053                     rect->x, rect->y, rect->width, rect->height);
01054 
01055     return MOZ_GTK_SUCCESS;
01056 }
01057 
01058 static gint
01059 moz_gtk_toolbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
01060                       GdkRectangle* cliprect)
01061 {
01062     GtkStyle* style;
01063     GtkShadowType shadow_type;
01064 
01065     ensure_toolbar_widget();
01066     style = gToolbarWidget->style;
01067 
01068     TSOffsetStyleGCs(style, rect->x, rect->y);
01069 
01070     gtk_style_apply_default_background(style, drawable, TRUE,
01071                                        GTK_STATE_NORMAL,
01072                                        cliprect, rect->x, rect->y,
01073                                        rect->width, rect->height);
01074 
01075     gtk_paint_box (style, drawable, GTK_STATE_NORMAL, gToolbarShadowType,
01076                    cliprect, gToolbarWidget, "toolbar",
01077                    rect->x, rect->y, rect->width, rect->height);
01078 
01079     return MOZ_GTK_SUCCESS;
01080 }
01081 
01082 static gint
01083 moz_gtk_tooltip_paint(GdkDrawable* drawable, GdkRectangle* rect,
01084                       GdkRectangle* cliprect)
01085 {
01086     GtkStyle* style;
01087 
01088     ensure_tooltip_widget();
01089     style = gtk_rc_get_style_by_paths(gtk_settings_get_default(),
01090                                       "gtk-tooltips", "GtkWindow",
01091                                       GTK_TYPE_WINDOW);
01092 
01093     gtk_style_attach(style, gTooltipWidget->window);
01094     TSOffsetStyleGCs(style, rect->x, rect->y);
01095     gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
01096                        cliprect, gTooltipWidget, "tooltip",
01097                        rect->x, rect->y, rect->width, rect->height);
01098 
01099     return MOZ_GTK_SUCCESS;
01100 }
01101 
01102 static gint
01103 moz_gtk_frame_paint(GdkDrawable* drawable, GdkRectangle* rect,
01104                     GdkRectangle* cliprect)
01105 {
01106     GtkStyle* style = gProtoWindow->style;
01107 
01108     TSOffsetStyleGCs(style, rect->x, rect->y);
01109     gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
01110                        NULL, gProtoWindow, "base", rect->x, rect->y,
01111                        rect->width, rect->height);
01112 
01113     ensure_frame_widget();
01114     style = gFrameWidget->style;
01115 
01116     TSOffsetStyleGCs(style, rect->x, rect->y);
01117     gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
01118                      cliprect, gFrameWidget, "frame", rect->x, rect->y,
01119                      rect->width, rect->height);
01120 
01121     return MOZ_GTK_SUCCESS;
01122 }
01123 
01124 static gint
01125 moz_gtk_progressbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
01126                           GdkRectangle* cliprect)
01127 {
01128     GtkStyle* style;
01129 
01130     ensure_progress_widget();
01131     style = gProgressWidget->style;
01132 
01133     TSOffsetStyleGCs(style, rect->x, rect->y);
01134     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
01135                   cliprect, gProgressWidget, "trough", rect->x, rect->y,
01136                   rect->width, rect->height);
01137 
01138     return MOZ_GTK_SUCCESS;
01139 }
01140 
01141 static gint
01142 moz_gtk_progress_chunk_paint(GdkDrawable* drawable, GdkRectangle* rect,
01143                              GdkRectangle* cliprect)
01144 {
01145     GtkStyle* style;
01146 
01147     ensure_progress_widget();
01148     style = gProgressWidget->style;
01149 
01150     TSOffsetStyleGCs(style, rect->x, rect->y);
01151     gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT,
01152                   cliprect, gProgressWidget, "bar", rect->x, rect->y,
01153                   rect->width, rect->height);
01154 
01155     return MOZ_GTK_SUCCESS;
01156 }
01157 
01158 static gint
01159 moz_gtk_tab_paint(GdkDrawable* drawable, GdkRectangle* rect,
01160                   GdkRectangle* cliprect, gint flags)
01161 {
01162     /*
01163      * In order to get the correct shadows and highlights, GTK paints
01164      * tabs right-to-left (end-to-beginning, to be generic), leaving
01165      * out the active tab, and then paints the current tab once
01166      * everything else is painted.  In addition, GTK uses a 2-pixel
01167      * overlap between adjacent tabs (this value is hard-coded in
01168      * gtknotebook.c).  For purposes of mapping to gecko's frame
01169      * positions, we put this overlap on the far edge of the frame
01170      * (i.e., for a horizontal/top tab strip, we shift the left side
01171      * of each tab 2px to the left, into the neighboring tab's frame
01172      * rect.  The right 2px * of a tab's frame will be referred to as
01173      * the "overlap area".
01174      *
01175      * Since we can't guarantee painting order with gecko, we need to
01176      * manage the overlap area manually. There are three types of tab
01177      * boundaries we need to handle:
01178      *
01179      * * two non-active tabs: In this case, we just have both tabs
01180      *   paint normally.
01181      *
01182      * * non-active to active tab: Here, we need the tab on the left to paint
01183      *                             itself normally, then paint the edge of the
01184      *                             active tab in its overlap area.
01185      *
01186      * * active to non-active tab: In this case, we just have both tabs paint
01187      *                             normally.
01188      *
01189      * We need to make an exception for the first tab - since there is
01190      * no tab to the left to paint the overlap area, we do _not_ shift
01191      * the tab left by 2px.
01192      */
01193 
01194     GtkStyle* style;
01195     ensure_tab_widget();
01196 
01197     if (!(flags & MOZ_GTK_TAB_FIRST)) {
01198         rect->x -= 2;
01199         rect->width += 2;
01200     }
01201 
01202     style = gTabWidget->style;
01203     TSOffsetStyleGCs(style, rect->x, rect->y);
01204     gtk_paint_extension(style, drawable,
01205                         ((flags & MOZ_GTK_TAB_SELECTED) ?
01206                          GTK_STATE_NORMAL : GTK_STATE_ACTIVE),
01207                         GTK_SHADOW_OUT, cliprect, gTabWidget, "tab", rect->x,
01208                         rect->y, rect->width, rect->height, GTK_POS_BOTTOM);
01209 
01210     if (flags & MOZ_GTK_TAB_BEFORE_SELECTED) {
01211         gboolean before_selected = ((flags & MOZ_GTK_TAB_BEFORE_SELECTED)!=0);
01212         cliprect->y -= 2;
01213         cliprect->height += 2;
01214 
01215         TSOffsetStyleGCs(style, rect->x + rect->width - 2,
01216                          rect->y - (2 * before_selected));
01217 
01218         gtk_paint_extension(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
01219                             cliprect, gTabWidget, "tab",
01220                             rect->x + rect->width - 2,
01221                             rect->y - (2 * before_selected), rect->width,
01222                             rect->height + (2 * before_selected),
01223                             GTK_POS_BOTTOM);
01224     }
01225 
01226     return MOZ_GTK_SUCCESS;
01227 }
01228 
01229 static gint
01230 moz_gtk_tabpanels_paint(GdkDrawable* drawable, GdkRectangle* rect,
01231                         GdkRectangle* cliprect)
01232 {
01233     GtkStyle* style;
01234 
01235     ensure_tab_widget();
01236     style = gTabWidget->style;
01237 
01238     TSOffsetStyleGCs(style, rect->x, rect->y);
01239     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
01240                   cliprect, gTabWidget, "notebook", rect->x, rect->y,
01241                   rect->width, rect->height);
01242 
01243     return MOZ_GTK_SUCCESS;
01244 }
01245 
01246 static gint
01247 moz_gtk_menu_bar_paint(GdkDrawable* drawable, GdkRectangle* rect,
01248                        GdkRectangle* cliprect)
01249 {
01250     GtkStyle* style;
01251     GtkShadowType shadow_type;
01252     ensure_menu_bar_widget();
01253     style = gMenuBarWidget->style;
01254 
01255     TSOffsetStyleGCs(style, rect->x, rect->y);
01256     gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL,
01257                                        cliprect, rect->x, rect->y,
01258                                        rect->width, rect->height);
01259     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, gMenuBarShadowType,
01260                   cliprect, gMenuBarWidget, "menubar", rect->x, rect->y,
01261                   rect->width, rect->height);
01262     return MOZ_GTK_SUCCESS;
01263 }
01264 
01265 static gint
01266 moz_gtk_menu_popup_paint(GdkDrawable* drawable, GdkRectangle* rect,
01267                          GdkRectangle* cliprect)
01268 {
01269     GtkStyle* style;
01270     ensure_menu_popup_widget();
01271     style = gMenuPopupWidget->style;
01272 
01273     TSOffsetStyleGCs(style, rect->x, rect->y);
01274     gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL,
01275                                        cliprect, rect->x, rect->y,
01276                                        rect->width, rect->height);
01277     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, 
01278                   cliprect, gMenuPopupWidget, "menu",
01279                   rect->x, rect->y, rect->width, rect->height);
01280 
01281     return MOZ_GTK_SUCCESS;
01282 }
01283 
01284 static gint
01285 moz_gtk_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect,
01286                         GdkRectangle* cliprect, GtkWidgetState* state)
01287 {
01288     GtkStyle* style;
01289     GtkShadowType shadow_type;
01290 
01291     if (state->inHover && !state->disabled) {
01292         ensure_menu_item_widget();
01293 
01294         style = gMenuItemWidget->style;
01295         TSOffsetStyleGCs(style, rect->x, rect->y);
01296         if (have_menu_shadow_type) {
01297             gtk_widget_style_get(gMenuItemWidget, "selected_shadow_type",
01298                                  &shadow_type, NULL);
01299         } else {
01300             shadow_type = GTK_SHADOW_OUT;
01301         }
01302 
01303         gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, shadow_type,
01304                       cliprect, gMenuItemWidget, "menuitem", rect->x, rect->y,
01305                       rect->width, rect->height);
01306     }
01307 
01308     return MOZ_GTK_SUCCESS;
01309 }
01310 
01311 static gint
01312 moz_gtk_check_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect,
01313                               GdkRectangle* cliprect, GtkWidgetState* state,
01314                               gboolean checked, gboolean isradio)
01315 {
01316     GtkStateType state_type;
01317     GtkStyle* style;
01318     GtkShadowType shadow_type = (checked)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
01319     gint offset;
01320     gint indicator_size = 8; /* it's a fixed value in gtk 2.2 */
01321     gint x, y;
01322     
01323     moz_gtk_menu_item_paint(drawable, rect, cliprect, state);
01324     
01325     ensure_check_menu_item_widget();
01326 
01327     if (checked || GTK_CHECK_MENU_ITEM(gCheckMenuItemWidget)->always_show_toggle) {
01328       style = gCheckMenuItemWidget->style;
01329       
01330       if (state->inHover && !state->disabled) {
01331         state_type = GTK_STATE_PRELIGHT;
01332       } else {
01333         state_type = GTK_STATE_NORMAL;
01334       }
01335       
01336       offset = GTK_CONTAINER(gCheckMenuItemWidget)->border_width + 
01337              gCheckMenuItemWidget->style->xthickness + 2;
01338       
01339       x = rect->x + offset;
01340       y = rect->y + (rect->height - indicator_size) / 2;
01341                                                                                 
01342       TSOffsetStyleGCs(style, x, y);
01343       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gCheckMenuItemWidget),
01344                                      checked);
01345       
01346       if (isradio) {
01347         gtk_paint_option(style, drawable, state_type, shadow_type, cliprect,
01348                          gCheckMenuItemWidget, "option",
01349                          x, y, indicator_size, indicator_size);
01350       } else {
01351         gtk_paint_check(style, drawable, state_type, shadow_type, cliprect,
01352                         gCheckMenuItemWidget, "check",
01353                         x, y, indicator_size, indicator_size);
01354       }
01355     }
01356     
01357     return MOZ_GTK_SUCCESS;
01358 }
01359 
01360 static gint
01361 moz_gtk_window_paint(GdkDrawable* drawable, GdkRectangle* rect,
01362                      GdkRectangle* cliprect)
01363 {
01364     GtkStyle* style;
01365 
01366     ensure_window_widget();
01367     style = gProtoWindow->style;
01368 
01369     TSOffsetStyleGCs(style, rect->x, rect->y);
01370     gtk_style_apply_default_background(style, drawable, TRUE,
01371                                        GTK_STATE_NORMAL,
01372                                        cliprect, rect->x, rect->y,
01373                                        rect->width, rect->height);
01374     return MOZ_GTK_SUCCESS;
01375 }
01376 
01377 gint
01378 moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* xthickness,
01379                           gint* ythickness)
01380 {
01381     GtkWidget* w;
01382 
01383     switch (widget) {
01384     case MOZ_GTK_BUTTON:
01385         {
01386             /* Constant in gtkbutton.c */
01387             static const gint child_spacing = 1;
01388             gboolean interior_focus;
01389             gint focus_width, focus_pad;
01390 
01391             ensure_button_widget();
01392 
01393             moz_gtk_button_get_focus(&interior_focus,
01394                                      &focus_width, &focus_pad);
01395 
01396             *xthickness = *ythickness =
01397                 GTK_CONTAINER(gButtonWidget)->border_width + child_spacing
01398                 + focus_width + focus_pad;
01399 
01400             *xthickness += gButtonWidget->style->xthickness;
01401             *ythickness += gButtonWidget->style->ythickness;
01402             return MOZ_GTK_SUCCESS;
01403         }
01404 
01405     case MOZ_GTK_TOOLBAR:
01406         ensure_toolbar_widget();
01407         w = gToolbarWidget;
01408         break;
01409     case MOZ_GTK_ENTRY:
01410         ensure_entry_widget();
01411         w = gEntryWidget;
01412         break;
01413     case MOZ_GTK_DROPDOWN_ARROW:
01414         ensure_arrow_widget();
01415         w = gDropdownButtonWidget;
01416         break;
01417     case MOZ_GTK_DROPDOWN:
01418         ensure_option_menu_widget();
01419         w = gOptionMenuWidget;
01420         break;
01421     case MOZ_GTK_TABPANELS:
01422         ensure_tab_widget();
01423         w = gTabWidget;
01424         break;
01425     case MOZ_GTK_PROGRESSBAR:
01426         ensure_progress_widget();
01427         w = gProgressWidget;
01428         break;
01429     case MOZ_GTK_FRAME:
01430         ensure_frame_widget();
01431         w = gFrameWidget;
01432         break;
01433     case MOZ_GTK_CHECKBUTTON_LABEL:
01434     case MOZ_GTK_RADIOBUTTON_LABEL:
01435         {
01436             gboolean interior_focus;
01437             gint focus_width, focus_pad;
01438 
01439             /* If the focus is interior, then the label has a border of
01440                (focus_width + focus_pad). */
01441             if (widget == MOZ_GTK_CHECKBUTTON_LABEL)
01442                 moz_gtk_checkbox_get_focus(&interior_focus,
01443                                            &focus_width, &focus_pad);
01444             else
01445                 moz_gtk_radio_get_focus(&interior_focus,
01446                                         &focus_width, &focus_pad);
01447 
01448             if (interior_focus)
01449                 *xthickness = *ythickness = (focus_width + focus_pad);
01450             else
01451                 *xthickness = *ythickness = 0;
01452 
01453             return MOZ_GTK_SUCCESS;
01454         }
01455 
01456     case MOZ_GTK_CHECKBUTTON_CONTAINER:
01457     case MOZ_GTK_RADIOBUTTON_CONTAINER:
01458         {
01459             gboolean interior_focus;
01460             gint focus_width, focus_pad;
01461 
01462             /* If the focus is _not_ interior, then the container has a border
01463                of (focus_width + focus_pad). */
01464             if (widget == MOZ_GTK_CHECKBUTTON_CONTAINER) {
01465                 moz_gtk_checkbox_get_focus(&interior_focus,
01466                                            &focus_width, &focus_pad);
01467                 w = gCheckboxWidget;
01468             } else {
01469                 moz_gtk_radio_get_focus(&interior_focus,
01470                                         &focus_width, &focus_pad);
01471                 w = gRadiobuttonWidget;
01472             }
01473 
01474             *xthickness = *ythickness = GTK_CONTAINER(w)->border_width;
01475 
01476             if (!interior_focus) {
01477                 *xthickness += (focus_width + focus_pad);
01478                 *ythickness += (focus_width + focus_pad);
01479             }
01480 
01481             return MOZ_GTK_SUCCESS;
01482         }
01483 
01484     case MOZ_GTK_MENUBAR:
01485         ensure_menu_bar_widget();
01486         w = gMenuBarWidget;
01487         break;
01488     case MOZ_GTK_MENUPOPUP:
01489         ensure_menu_popup_widget();
01490         w = gMenuPopupWidget;
01491         break;
01492     case MOZ_GTK_MENUITEM:
01493         ensure_menu_item_widget();
01494         w = gMenuItemWidget;
01495         break;
01496     case MOZ_GTK_CHECKMENUITEM:
01497     case MOZ_GTK_RADIOMENUITEM:
01498         ensure_check_menu_item_widget();
01499         w = gCheckMenuItemWidget;
01500         break;
01501     /* These widgets have no borders, since they are not containers. */
01502     case MOZ_GTK_CHECKBUTTON:
01503     case MOZ_GTK_RADIOBUTTON:
01504     case MOZ_GTK_SCROLLBAR_BUTTON:
01505     case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
01506     case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
01507     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
01508     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
01509     case MOZ_GTK_GRIPPER:
01510     case MOZ_GTK_PROGRESS_CHUNK:
01511     case MOZ_GTK_TAB:
01512     /* These widgets have no borders.*/
01513     case MOZ_GTK_TOOLTIP:
01514     case MOZ_GTK_WINDOW:
01515         *xthickness = *ythickness = 0;
01516         return MOZ_GTK_SUCCESS;
01517     default:
01518         g_warning("Unsupported widget type: %d", widget);
01519         return MOZ_GTK_UNKNOWN_WIDGET;
01520     }
01521 
01522     *xthickness = XTHICKNESS(w->style);
01523     *ythickness = YTHICKNESS(w->style);
01524 
01525     return MOZ_GTK_SUCCESS;
01526 }
01527 
01528 gint
01529 moz_gtk_get_dropdown_arrow_size(gint* width, gint* height)
01530 {
01531     ensure_arrow_widget();
01532 
01533     /*
01534      * First get the border of the dropdown arrow, then add in the requested
01535      * size of the arrow.  Note that the minimum arrow size is fixed at
01536      * 11 pixels.
01537      */
01538 
01539     *width = 2 * (1 + XTHICKNESS(gDropdownButtonWidget->style));
01540     *width += 11 + GTK_MISC(gArrowWidget)->xpad * 2;
01541 
01542     *height = 2 * (1 + YTHICKNESS(gDropdownButtonWidget->style));
01543     *height += 11 + GTK_MISC(gArrowWidget)->ypad * 2;
01544 
01545     return MOZ_GTK_SUCCESS;
01546 }
01547 
01548 gint
01549 moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
01550 {
01551     ensure_scrollbar_widget();
01552 
01553     gtk_widget_style_get (gHorizScrollbarWidget,
01554                           "slider_width", &metrics->slider_width,
01555                           "trough_border", &metrics->trough_border,
01556                           "stepper_size", &metrics->stepper_size,
01557                           "stepper_spacing", &metrics->stepper_spacing,
01558                           NULL);
01559 
01560     metrics->min_slider_size =
01561         GTK_RANGE(gHorizScrollbarWidget)->min_slider_size;
01562 
01563     return MOZ_GTK_SUCCESS;
01564 }
01565 
01566 gint
01567 moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable,
01568                      GdkRectangle* rect, GdkRectangle* cliprect,
01569                      GtkWidgetState* state, gint flags)
01570 {
01571     switch (widget) {
01572     case MOZ_GTK_BUTTON:
01573         ensure_button_widget();
01574         return moz_gtk_button_paint(drawable, rect, cliprect, state,
01575                                     (GtkReliefStyle) flags, gButtonWidget);
01576         break;
01577     case MOZ_GTK_CHECKBUTTON:
01578     case MOZ_GTK_RADIOBUTTON:
01579         return moz_gtk_toggle_paint(drawable, rect, cliprect, state,
01580                                     (gboolean) flags,
01581                                     (widget == MOZ_GTK_RADIOBUTTON));
01582         break;
01583     case MOZ_GTK_SCROLLBAR_BUTTON:
01584         return moz_gtk_scrollbar_button_paint(drawable, rect, cliprect,
01585                                               state, (GtkArrowType) flags);
01586         break;
01587     case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
01588     case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
01589         return moz_gtk_scrollbar_trough_paint(widget, drawable, rect,
01590                                               cliprect, state);
01591         break;
01592     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
01593     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
01594         return moz_gtk_scrollbar_thumb_paint(widget, drawable, rect,
01595                                              cliprect, state);
01596         break;
01597     case MOZ_GTK_GRIPPER:
01598         return moz_gtk_gripper_paint(drawable, rect, cliprect, state);
01599         break;
01600     case MOZ_GTK_ENTRY:
01601         return moz_gtk_entry_paint(drawable, rect, cliprect, state);
01602         break;
01603     case MOZ_GTK_DROPDOWN:
01604         return moz_gtk_option_menu_paint(drawable, rect, cliprect, state);
01605         break;
01606     case MOZ_GTK_DROPDOWN_ARROW:
01607         return moz_gtk_dropdown_arrow_paint(drawable, rect, cliprect, state);
01608         break;
01609     case MOZ_GTK_CHECKBUTTON_CONTAINER:
01610     case MOZ_GTK_RADIOBUTTON_CONTAINER:
01611         return moz_gtk_container_paint(drawable, rect, cliprect, state,
01612                                        (widget == MOZ_GTK_RADIOBUTTON_CONTAINER));
01613         break;
01614     case MOZ_GTK_CHECKBUTTON_LABEL:
01615     case MOZ_GTK_RADIOBUTTON_LABEL:
01616         return moz_gtk_toggle_label_paint(drawable, rect, cliprect, state,
01617                                           (widget == MOZ_GTK_RADIOBUTTON_LABEL));
01618         break;
01619     case MOZ_GTK_TOOLBAR:
01620         return moz_gtk_toolbar_paint(drawable, rect, cliprect);
01621         break;
01622     case MOZ_GTK_TOOLTIP:
01623         return moz_gtk_tooltip_paint(drawable, rect, cliprect);
01624         break;
01625     case MOZ_GTK_FRAME:
01626         return moz_gtk_frame_paint(drawable, rect, cliprect);
01627         break;
01628     case MOZ_GTK_PROGRESSBAR:
01629         return moz_gtk_progressbar_paint(drawable, rect, cliprect);
01630         break;
01631     case MOZ_GTK_PROGRESS_CHUNK:
01632         return moz_gtk_progress_chunk_paint(drawable, rect, cliprect);
01633         break;
01634     case MOZ_GTK_TAB:
01635         return moz_gtk_tab_paint(drawable, rect, cliprect, flags);
01636         break;
01637     case MOZ_GTK_TABPANELS:
01638         return moz_gtk_tabpanels_paint(drawable, rect, cliprect);
01639         break;
01640     case MOZ_GTK_MENUBAR:
01641         return moz_gtk_menu_bar_paint(drawable, rect, cliprect);
01642         break;
01643     case MOZ_GTK_MENUPOPUP:
01644         return moz_gtk_menu_popup_paint(drawable, rect, cliprect);
01645         break;
01646     case MOZ_GTK_MENUITEM:
01647         return moz_gtk_menu_item_paint(drawable, rect, cliprect, state);
01648         break;
01649     case MOZ_GTK_CHECKMENUITEM:
01650     case MOZ_GTK_RADIOMENUITEM:
01651         return moz_gtk_check_menu_item_paint(drawable, rect, cliprect, state,
01652                                              (gboolean) flags,
01653                                              (widget == MOZ_GTK_RADIOMENUITEM));
01654         break;
01655     case MOZ_GTK_WINDOW:
01656         return moz_gtk_window_paint(drawable, rect, cliprect);
01657         break;
01658     default:
01659         g_warning("Unknown widget type: %d", widget);
01660     }
01661 
01662     return MOZ_GTK_UNKNOWN_WIDGET;
01663 }
01664 
01665 gint
01666 moz_gtk_shutdown()
01667 {
01668     if (gTooltipWidget)
01669         gtk_widget_destroy(gTooltipWidget);
01670     /* This will destroy all of our widgets */
01671     if (gProtoWindow)
01672         gtk_widget_destroy(gProtoWindow);
01673 
01674     gProtoWindow = NULL;
01675     gButtonWidget = NULL;
01676     gCheckboxWidget = NULL;
01677     gRadiobuttonWidget = NULL;
01678     gHorizScrollbarWidget = NULL;
01679     gVertScrollbarWidget = NULL;
01680     gEntryWidget = NULL;
01681     gArrowWidget = NULL;
01682     gDropdownButtonWidget = NULL;
01683     gHandleBoxWidget = NULL;
01684     gToolbarWidget = NULL;
01685     gFrameWidget = NULL;
01686     gProgressWidget = NULL;
01687     gTabWidget = NULL;
01688     gTooltipWidget = NULL;
01689     gMenuBarWidget = NULL;
01690     gMenuBarItemWidget = NULL;
01691     gMenuPopupWidget = NULL;
01692     gMenuItemWidget = NULL;
01693     gCheckMenuItemWidget = NULL;
01694 
01695     return MOZ_GTK_SUCCESS;
01696 }