Back to index

lightning-sunbird  0.9+nobinonly
gtkdrawing.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /*
00040  * This file contains painting functions for each of the gtk widgets.
00041  * Adapted from the gtk+ 1.2.10 source.
00042  */
00043 
00044 #include <gtk/gtk.h>
00045 #include <gdk/gdkprivate.h>
00046 #include <string.h>
00047 #include "gtkdrawing.h"
00048 
00049 static GtkWidget* gButtonWidget;
00050 static GtkWidget* gProtoWindow;
00051 static GtkWidget* gCheckboxWidget;
00052 static GtkWidget* gScrollbarWidget;
00053 static GtkWidget* gEntryWidget;
00054 static GtkWidget* gArrowWidget;
00055 static GtkWidget* gDropdownButtonWidget;
00056 static GtkWidget* gHandleBoxWidget;
00057 static GtkWidget* gFrameWidget;
00058 static GtkWidget* gProgressWidget;
00059 static GtkWidget* gTabWidget;
00060 static GtkTooltips* gTooltipWidget;
00061 
00062 static style_prop_t style_prop_func;
00063 
00064 typedef struct GtkThemeEnginePrivate {
00065   GtkThemeEngine engine;
00066   void* library;
00067   char* name;
00068 } GtkThemeEnginePrivate;
00069 
00070 static const char* sDisabledEngines[] = {
00071   "xeno",   /* xenophilia */
00072   NULL
00073 };
00074 
00075 gint
00076 moz_gtk_enable_style_props(style_prop_t styleGetProp)
00077 {
00078   style_prop_func = styleGetProp;
00079   return MOZ_GTK_SUCCESS;
00080 }
00081 
00082 static gint
00083 setup_widget_prototype(GtkWidget* widget)
00084 {
00085   static GtkWidget* protoLayout;
00086 
00087   if (!gProtoWindow) {
00088     gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
00089     protoLayout = gtk_fixed_new();
00090     gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout);
00091   }
00092 
00093   gtk_container_add(GTK_CONTAINER(protoLayout), widget);
00094   gtk_widget_set_rc_style(widget);
00095   gtk_widget_realize(widget);
00096   return MOZ_GTK_SUCCESS;
00097 }
00098 
00099 static gint
00100 ensure_button_widget()
00101 {
00102   if (!gButtonWidget) {
00103     gButtonWidget = gtk_button_new_with_label("M");
00104     setup_widget_prototype(gButtonWidget);
00105   }
00106   return MOZ_GTK_SUCCESS;
00107 }
00108 
00109 static gint
00110 ensure_checkbox_widget()
00111 {
00112   if (!gCheckboxWidget) {
00113     gCheckboxWidget = gtk_check_button_new_with_label("M");
00114     setup_widget_prototype(gCheckboxWidget);
00115   }
00116   return MOZ_GTK_SUCCESS;
00117 }
00118 
00119 static gint
00120 ensure_scrollbar_widget()
00121 {
00122   if (!gScrollbarWidget) {
00123     gScrollbarWidget = gtk_vscrollbar_new(NULL);
00124     setup_widget_prototype(gScrollbarWidget);
00125   }
00126   return MOZ_GTK_SUCCESS;
00127 }
00128 
00129 static gint
00130 ensure_entry_widget()
00131 {
00132   if (!gEntryWidget) {
00133     gEntryWidget = gtk_entry_new();
00134     setup_widget_prototype(gEntryWidget);
00135 #ifdef _AIX
00136     /* On AIX, calling gdk_widget_realize on a gtk entry with an
00137      * input context style of type GDK_IM_STATUS_NOTHING causes a 
00138      * root status window to come up. It remains up until the widget 
00139      * is destroyed (which is never in this case). To work around 
00140      * this problem, we destroy its XIC here to prevent the root 
00141      * status window from remaining on the screen. */
00142     if (GTK_EDITABLE(gEntryWidget)->ic &&
00143         GTK_EDITABLE(gEntryWidget)->ic_attr->style & GDK_IM_STATUS_NOTHING) {
00144       gdk_ic_destroy(GTK_EDITABLE(gEntryWidget)->ic);
00145       GTK_EDITABLE(gEntryWidget)->ic = NULL;
00146     }
00147 #endif
00148   }
00149   return MOZ_GTK_SUCCESS;
00150 }
00151 
00152 static gint
00153 ensure_arrow_widget()
00154 {
00155   if (!gArrowWidget) {
00156     gDropdownButtonWidget = gtk_button_new();
00157     setup_widget_prototype(gDropdownButtonWidget);
00158     gArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
00159     gtk_container_add(GTK_CONTAINER(gDropdownButtonWidget), gArrowWidget);
00160     gtk_widget_set_rc_style(gArrowWidget);
00161     gtk_widget_realize(gArrowWidget);
00162   }
00163   return MOZ_GTK_SUCCESS;
00164 }
00165 
00166 static gint
00167 ensure_handlebox_widget()
00168 {
00169   if (!gHandleBoxWidget) {
00170     gHandleBoxWidget = gtk_handle_box_new();
00171     setup_widget_prototype(gHandleBoxWidget);
00172   }
00173   return MOZ_GTK_SUCCESS;
00174 }
00175 
00176 static gint
00177 ensure_tooltip_widget()
00178 {
00179   if (!gTooltipWidget) {
00180     gTooltipWidget = gtk_tooltips_new();
00181     gtk_tooltips_force_window(gTooltipWidget);
00182     gtk_widget_set_rc_style(gTooltipWidget->tip_window);
00183     gtk_widget_realize(gTooltipWidget->tip_window);
00184   }
00185   return MOZ_GTK_SUCCESS;
00186 }
00187 
00188 static gint
00189 ensure_tab_widget()
00190 {
00191   if (!gTabWidget) {
00192     gTabWidget = gtk_notebook_new();
00193     setup_widget_prototype(gTabWidget);
00194   }
00195   return MOZ_GTK_SUCCESS;
00196 }
00197 
00198 static gint
00199 ensure_progress_widget()
00200 {
00201   if (!gProgressWidget) {
00202     gProgressWidget = gtk_progress_bar_new();
00203     setup_widget_prototype(gProgressWidget);
00204   }
00205   return MOZ_GTK_SUCCESS;
00206 }
00207 
00208 static gint
00209 ensure_frame_widget()
00210 {
00211   if (!gFrameWidget) {
00212     gFrameWidget = gtk_frame_new(NULL);
00213     setup_widget_prototype(gFrameWidget);
00214   }
00215   return MOZ_GTK_SUCCESS;
00216 }
00217 
00218 static GtkStateType
00219 ConvertGtkState(GtkWidgetState* state)
00220 {
00221   if (state->disabled)
00222     return GTK_STATE_INSENSITIVE;
00223   else if (state->inHover)
00224     return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
00225   else
00226     return GTK_STATE_NORMAL;
00227 }
00228 
00229 static gint
00230 TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin)
00231 {
00232   int i;
00233   /* there are 5 gc's in each array, for each of the widget states */
00234   for (i = 0; i < 5; ++i)
00235     gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin);
00236   return MOZ_GTK_SUCCESS;
00237 }
00238 
00239 static gint
00240 TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin)
00241 {
00242   TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin);
00243   TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin);
00244   TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin);
00245   TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin);
00246   TSOffsetStyleGCArray(style->mid_gc, xorigin, yorigin);
00247   TSOffsetStyleGCArray(style->text_gc, xorigin, yorigin);
00248   TSOffsetStyleGCArray(style->base_gc, xorigin, yorigin);
00249   gdk_gc_set_ts_origin(style->black_gc, xorigin, yorigin);
00250   gdk_gc_set_ts_origin(style->white_gc, xorigin, yorigin);
00251   return MOZ_GTK_SUCCESS;
00252 }
00253 
00254 static gint
00255 moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
00256                      GdkRectangle* cliprect, GtkWidgetState* state,
00257                      GtkReliefStyle relief, GtkWidget* widget)
00258 {
00259   GtkShadowType shadow_type;
00260   GtkStyle* style = widget->style;
00261   gint default_spacing = 7; /* xxx fix me */
00262   GtkStateType button_state = ConvertGtkState(state);
00263   gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
00264 
00265   if (((GdkWindowPrivate*)drawable)->mapped) {
00266     gdk_window_set_back_pixmap(drawable, NULL, TRUE);
00267     gdk_window_clear_area(drawable, cliprect->x, cliprect->y, cliprect->width,
00268                           cliprect->height);
00269   }
00270 
00271   gtk_widget_set_state(widget, button_state);
00272   if (state->isDefault) {
00273     TSOffsetStyleGCs(style, x, y);
00274     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, cliprect,
00275                   widget, "buttondefault", x, y, width, height);
00276   }
00277 
00278   if (state->canDefault) {
00279     x += style->klass->xthickness;
00280     y += style->klass->ythickness;
00281     width -= 2 * x + default_spacing;
00282     height -= 2 * y + default_spacing;
00283     x += (1 + default_spacing) / 2;
00284     y += (1 + default_spacing) / 2;
00285   }
00286        
00287   if (state->focused) {
00288     x += 1;
00289     y += 1;
00290     width -= 2;
00291     height -= 2;
00292   }
00293 
00294   shadow_type = (state->active && state->inHover && !state->disabled) ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
00295 
00296   if (relief != GTK_RELIEF_NONE || (button_state != GTK_STATE_NORMAL &&
00297                                     button_state != GTK_STATE_INSENSITIVE)) {
00298     TSOffsetStyleGCs(style, x, y);
00299     gtk_paint_box(style, drawable, button_state, shadow_type, cliprect,
00300                   widget, "button", x, y, width, height);
00301   }
00302 
00303   if (state->focused) {
00304     x -= 1;
00305     y -= 1;
00306     width += 2;
00307     height += 2;
00308     
00309     TSOffsetStyleGCs(style, x, y);
00310     gtk_paint_focus(style, drawable, cliprect, widget, "button", x, y,
00311                     width - 1, height - 1);
00312   }
00313 
00314   return MOZ_GTK_SUCCESS;
00315 }
00316 
00317 gint
00318 moz_gtk_init()
00319 {
00320   /* Check for a blacklisted theme engine */
00321   GtkThemeEngine* gtkThemeEng;
00322   ensure_button_widget();
00323   gtkThemeEng = gButtonWidget->style->engine;
00324   if (gtkThemeEng) {
00325     const char* curEngine = ((GtkThemeEnginePrivate*) gtkThemeEng)->name;
00326     const char* eng;
00327     int i = 0;
00328     while ((eng = sDisabledEngines[i++])) {
00329       if (!strcmp(eng, curEngine)) {
00330         g_warning("gtkdrawing found unsafe theme engine: %s\n", eng);
00331         return MOZ_GTK_UNSAFE_THEME;
00332       }
00333     }
00334   }
00335 
00336   return MOZ_GTK_SUCCESS;
00337 }
00338 
00339 gint
00340 moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing)
00341 {
00342   GtkCheckButtonClass* klass;
00343 
00344   ensure_checkbox_widget();
00345   klass = GTK_CHECK_BUTTON_CLASS(GTK_OBJECT(gCheckboxWidget)->klass);
00346 
00347   if (style_prop_func) {
00348     /*
00349      * This API is supported only in GTK+ >= 1.2.9, and gives per-theme values.
00350      */
00351 
00352     GtkStyle* style = gCheckboxWidget->style;
00353 
00354     *indicator_size = style_prop_func(style,
00355                                       "GtkCheckButton::indicator_size",
00356                                       klass->indicator_size);
00357     *indicator_spacing = style_prop_func(style,
00358                                          "GtkCheckButton::indicator_spacing",
00359                                          klass->indicator_spacing);
00360   } else {
00361     *indicator_size = klass->indicator_size;
00362     *indicator_spacing = klass->indicator_spacing;
00363   }
00364 
00365   return MOZ_GTK_SUCCESS;
00366 }
00367 
00368 gint
00369 moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing)
00370 {
00371   return moz_gtk_checkbox_get_metrics(indicator_size, indicator_spacing);
00372 }
00373 
00374 gint
00375 moz_gtk_checkbox_get_focus(gboolean* interior_focus,
00376                            gint* focus_width, gint* focus_pad)
00377 {
00378   /* These values are hardcoded in gtk1. */
00379   *interior_focus = FALSE;
00380   *focus_width = 1;
00381   *focus_pad = 0;
00382   return MOZ_GTK_SUCCESS;
00383 }
00384 
00385 gint
00386 moz_gtk_radio_get_focus(gboolean* interior_focus,
00387                         gint* focus_width, gint* focus_pad)
00388 {
00389   return moz_gtk_checkbox_get_focus(interior_focus, focus_width, focus_pad);
00390 }
00391 
00392 static gint
00393 moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect,
00394                      GdkRectangle* cliprect, GtkWidgetState* state,
00395                      gboolean selected, gboolean isradio)
00396 {
00397   GtkStateType state_type;
00398   GtkShadowType shadow_type;
00399   gint indicator_size, indicator_spacing;
00400   gint x, y, width, height;
00401   GtkStyle* style;
00402 
00403   moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
00404   style = gCheckboxWidget->style;
00405 
00406   /* offset by indicator_spacing, and centered vertically within the rect */
00407   x = rect->x + indicator_spacing;
00408   y = rect->y + (rect->height - indicator_size) / 2;
00409   width = indicator_size;
00410   height = indicator_size;
00411   
00412   if (selected) {
00413     state_type = GTK_STATE_ACTIVE;
00414     shadow_type = GTK_SHADOW_IN;
00415   }
00416   else {
00417     shadow_type = GTK_SHADOW_OUT;
00418     state_type = ConvertGtkState(state);
00419   }
00420   
00421   TSOffsetStyleGCs(style, x, y);
00422 
00423   /* Some themes check the widget state themselves. */
00424   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gCheckboxWidget), selected);
00425 
00426   if (isradio)
00427     gtk_paint_option(style, drawable, state_type, shadow_type, cliprect,
00428                      gCheckboxWidget, "radiobutton", x, y, width, height);
00429   else
00430     gtk_paint_check(style, drawable, state_type, shadow_type, cliprect, 
00431                      gCheckboxWidget, "checkbutton", x, y, width, height);
00432 
00433   return MOZ_GTK_SUCCESS;
00434 }
00435 
00436 static gint
00437 calculate_arrow_dimensions(GdkRectangle* rect, GdkRectangle* arrow_rect)
00438 {
00439   GtkMisc* misc = GTK_MISC(gArrowWidget);
00440 
00441   gint extent = MIN(rect->width - misc->xpad * 2, rect->height - misc->ypad * 2);
00442   arrow_rect->x = ((rect->x + misc->xpad) * (1.0 - misc->xalign) +
00443                    (rect->x + rect->width - extent - misc->xpad) * misc->xalign);
00444   arrow_rect->y = ((rect->y + misc->ypad) * (1.0 - misc->yalign) +
00445                    (rect->y + rect->height - extent - misc->ypad) * misc->yalign);
00446 
00447   arrow_rect->width = arrow_rect->height = extent;
00448   return MOZ_GTK_SUCCESS;
00449 }
00450 
00451 static gint
00452 moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect,
00453                                GdkRectangle* cliprect, GtkWidgetState* state,
00454                                GtkArrowType type)
00455 {
00456   GtkStateType state_type = ConvertGtkState(state);
00457   GtkShadowType shadow_type = (state->active && !state->disabled) ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
00458   GdkRectangle arrow_rect;
00459   GtkStyle* style;
00460 
00461   ensure_scrollbar_widget();
00462   style = gScrollbarWidget->style;
00463 
00464   ensure_arrow_widget();
00465   calculate_arrow_dimensions(rect, &arrow_rect);
00466   TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y);
00467   gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
00468                   gScrollbarWidget, (type < 2) ? "vscrollbar" : "hscrollbar", 
00469                   type, TRUE, arrow_rect.x, arrow_rect.y, arrow_rect.width,
00470                   arrow_rect.height);
00471   return MOZ_GTK_SUCCESS;
00472 }
00473 
00474 static gint
00475 moz_gtk_scrollbar_trough_paint(GdkDrawable* drawable, GdkRectangle* rect,
00476                                GdkRectangle* cliprect, GtkWidgetState* state)
00477 {
00478   GtkStyle* style;
00479 
00480   ensure_scrollbar_widget();
00481   style = gScrollbarWidget->style;
00482 
00483   TSOffsetStyleGCs(style, rect->x, rect->y);
00484   gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_ACTIVE,
00485                                      cliprect, rect->x, rect->y,
00486                                      rect->width, rect->height);
00487 
00488   gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect,
00489                 gScrollbarWidget, "trough", rect->x, rect->y,  rect->width,
00490                 rect->height);
00491 
00492   if (state->focused)
00493     gtk_paint_focus(style, drawable, cliprect, gScrollbarWidget, "trough",
00494                     rect->x, rect->y, rect->width, rect->height);
00495 
00496   return MOZ_GTK_SUCCESS;
00497 }
00498 
00499 static gint
00500 moz_gtk_scrollbar_thumb_paint(GdkDrawable* drawable, GdkRectangle* rect,
00501                               GdkRectangle* cliprect, GtkWidgetState* state)
00502 {
00503   GtkStateType state_type = (state->inHover || state->active) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
00504   GtkStyle* style;
00505 
00506   ensure_scrollbar_widget();
00507   style = gScrollbarWidget->style;
00508 
00509   TSOffsetStyleGCs(style, rect->x, rect->y);
00510   gtk_paint_box(style, drawable, state_type, GTK_SHADOW_OUT, cliprect,
00511                 gScrollbarWidget, "slider", rect->x, rect->y, rect->width, 
00512                 rect->height);
00513 
00514   return MOZ_GTK_SUCCESS;
00515 }
00516 
00517 static gint
00518 moz_gtk_gripper_paint(GdkDrawable* drawable, GdkRectangle* rect,
00519                       GdkRectangle* cliprect, GtkWidgetState* state)
00520 {
00521   GtkStateType state_type = ConvertGtkState(state);
00522   GtkShadowType shadow_type;
00523   GtkStyle* style;
00524 
00525   ensure_handlebox_widget();
00526   style = gHandleBoxWidget->style;
00527   shadow_type = GTK_HANDLE_BOX(gHandleBoxWidget)->shadow_type;
00528 
00529   TSOffsetStyleGCs(style, rect->x, rect->y);
00530   gtk_paint_box(style, drawable, state_type, shadow_type, cliprect,
00531                 gHandleBoxWidget, "handlebox_bin", rect->x, rect->y,
00532                 rect->width, rect->height);
00533 
00534   return MOZ_GTK_SUCCESS;
00535 }
00536 
00537 static gint
00538 moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect,
00539                     GdkRectangle* cliprect, GtkWidgetState* state)
00540 {
00541   gint x = rect->x, y = rect->y, width = rect->width, height = rect->height;
00542   GtkStyle* style;
00543 
00544   ensure_entry_widget();
00545   style = gEntryWidget->style;
00546 
00547   /*
00548    * gtk+ is able to draw over top of the entry when it gains focus,
00549    * so the non-focused text border is implicitly already drawn when
00550    * the entry is drawn in a focused state.
00551    *
00552    * Gecko doesn't quite work this way, so always draw the non-focused
00553    * shadow, then draw the shadow again, inset, if we're focused.
00554    */
00555 
00556   TSOffsetStyleGCs(style, x, y);
00557   gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, cliprect,
00558                    gEntryWidget, "entry", x, y, width, height);
00559   
00560 
00561   if (state->focused) {
00562       x += 1;
00563       y += 1;
00564       width -= 2;
00565       height -= 2;
00566       TSOffsetStyleGCs(style, x, y);
00567       gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
00568                        cliprect, gEntryWidget, "entry", x, y, width, height);
00569 
00570       TSOffsetStyleGCs(style, rect->x, rect->y);
00571       gtk_paint_focus(style, drawable,  cliprect, gEntryWidget, "entry",
00572                       rect->x, rect->y, rect->width - 1, rect->height - 1);
00573   }
00574 
00575   x = style->klass->xthickness;
00576   y = style->klass->ythickness;
00577 
00578   TSOffsetStyleGCs(style, rect->x + x, rect->y + y);
00579   gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
00580                      cliprect, gEntryWidget, "entry_bg",  rect->x + x,
00581                      rect->y + y, rect->width - 2*x, rect->height - 2*y);
00582 
00583   return MOZ_GTK_SUCCESS;
00584 }
00585 
00586 static gint
00587 moz_gtk_dropdown_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect,
00588                              GdkRectangle* cliprect, GtkWidgetState* state)
00589 {
00590   GdkRectangle arrow_rect, real_arrow_rect;
00591   GtkStateType state_type = ConvertGtkState(state);
00592   GtkShadowType shadow_type = (state->active && !state->disabled) ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
00593   GtkStyle* style;
00594 
00595   ensure_arrow_widget();
00596   moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL,
00597                        gDropdownButtonWidget);
00598 
00599   /* This mirrors gtkbutton's child positioning */
00600   style = gDropdownButtonWidget->style;
00601   arrow_rect.x = rect->x + 1 + gDropdownButtonWidget->style->klass->xthickness;
00602   arrow_rect.y = rect->y + 1 + gDropdownButtonWidget->style->klass->ythickness;
00603   arrow_rect.width = MAX(1, rect->width - (arrow_rect.x - rect->x) * 2);
00604   arrow_rect.height = MAX(1, rect->height - (arrow_rect.y - rect->y) * 2);
00605 
00606   calculate_arrow_dimensions(&arrow_rect, &real_arrow_rect);
00607   style = gArrowWidget->style;
00608   TSOffsetStyleGCs(style, real_arrow_rect.x, real_arrow_rect.y);
00609   gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect,
00610                   gScrollbarWidget, "arrow",  GTK_ARROW_DOWN, TRUE,
00611                   real_arrow_rect.x, real_arrow_rect.y,
00612                   real_arrow_rect.width, real_arrow_rect.height);
00613 
00614   return MOZ_GTK_SUCCESS;
00615 }
00616 
00617 static gint
00618 moz_gtk_container_paint(GdkDrawable* drawable, GdkRectangle* rect,
00619                         GdkRectangle* cliprect, GtkWidgetState* state, 
00620                         gboolean isradio)
00621 {
00622   GtkStateType state_type = ConvertGtkState(state);
00623   GtkStyle* style;
00624 
00625   ensure_checkbox_widget();
00626   style = gCheckboxWidget->style;
00627 
00628   if (state_type != GTK_STATE_NORMAL && state_type != GTK_STATE_PRELIGHT)
00629     state_type = GTK_STATE_NORMAL;
00630   
00631   TSOffsetStyleGCs(style, rect->x, rect->y);
00632 
00633   if (state_type != GTK_STATE_NORMAL) /* this is for drawing a prelight box */
00634     gtk_paint_flat_box(style, drawable, state_type, GTK_SHADOW_ETCHED_OUT,
00635                        cliprect, gCheckboxWidget,
00636                        isradio ? "radiobutton" : "checkbutton",
00637                        rect->x, rect->y, rect->width, rect->height);
00638 
00639   if (state->focused)
00640     gtk_paint_focus(style, drawable, cliprect, gCheckboxWidget, "checkbutton",
00641                     rect->x, rect->y, rect->width - 1, rect->height - 1);
00642 
00643   return MOZ_GTK_SUCCESS;
00644 }
00645 
00646 static gint
00647 moz_gtk_toolbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
00648                       GdkRectangle* cliprect)
00649 {
00650   GtkStyle* style;
00651 
00652   ensure_handlebox_widget();
00653   style = gHandleBoxWidget->style;
00654 
00655   TSOffsetStyleGCs(style, rect->x, rect->y);
00656   if (style->bg_pixmap[GTK_STATE_NORMAL])
00657     gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL,
00658                                        cliprect, rect->x, rect->y,
00659                                        rect->width, rect->height);
00660   else
00661     gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, cliprect,
00662                   gHandleBoxWidget, "dockitem_bin",
00663                   rect->x, rect->y, rect->width, rect->height);
00664 
00665   return MOZ_GTK_SUCCESS;
00666 }
00667 
00668 static gint
00669 moz_gtk_tooltip_paint(GdkDrawable* drawable, GdkRectangle* rect,
00670                       GdkRectangle* cliprect)
00671 {
00672   GtkStyle* style;
00673 
00674   ensure_tooltip_widget();
00675   style = gTooltipWidget->tip_window->style;
00676 
00677   TSOffsetStyleGCs(style, rect->x, rect->y);
00678   gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
00679                      cliprect, gTooltipWidget->tip_window, "tooltip", rect->x,
00680                      rect->y, rect->width, rect->height);
00681 
00682   return MOZ_GTK_SUCCESS;
00683 }
00684 
00685 static gint
00686 moz_gtk_frame_paint(GdkDrawable* drawable, GdkRectangle* rect,
00687                     GdkRectangle* cliprect)
00688 {
00689   GtkStyle* style = gProtoWindow->style;
00690 
00691   TSOffsetStyleGCs(style, rect->x, rect->y);
00692   gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL,
00693                      gProtoWindow, "base", rect->x, rect->y, rect->width,
00694                      rect->height);
00695 
00696   ensure_frame_widget();
00697   style = gFrameWidget->style;
00698 
00699   TSOffsetStyleGCs(style, rect->x, rect->y);
00700   gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, cliprect,
00701                    gFrameWidget, "frame", rect->x, rect->y, rect->width,
00702                    rect->height);
00703 
00704   return MOZ_GTK_SUCCESS;
00705 }
00706 
00707 static gint
00708 moz_gtk_progressbar_paint(GdkDrawable* drawable, GdkRectangle* rect,
00709                           GdkRectangle* cliprect)
00710 {
00711   GtkStyle* style;
00712 
00713   ensure_progress_widget();
00714   style = gProgressWidget->style;
00715 
00716   TSOffsetStyleGCs(style, rect->x, rect->y);
00717   gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN,
00718                 cliprect, gProgressWidget, "trough", rect->x, rect->y,
00719                 rect->width, rect->height);
00720 
00721   return MOZ_GTK_SUCCESS;
00722 }
00723 
00724 static gint
00725 moz_gtk_progress_chunk_paint(GdkDrawable* drawable, GdkRectangle* rect,
00726                              GdkRectangle* cliprect)
00727 {
00728   GtkStyle* style;
00729 
00730   ensure_progress_widget();
00731   style = gProgressWidget->style;
00732 
00733   TSOffsetStyleGCs(style, rect->x, rect->y);
00734   gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT,
00735                 cliprect, gProgressWidget, "bar", rect->x, rect->y,
00736                 rect->width, rect->height);
00737 
00738   return MOZ_GTK_SUCCESS;
00739 }
00740 
00741 static gint
00742 moz_gtk_tab_paint(GdkDrawable* drawable, GdkRectangle* rect,
00743                   GdkRectangle* cliprect, gint flags)
00744 {
00745   /*
00746    * In order to get the correct shadows and highlights, GTK paints tabs
00747    * right-to-left (end-to-beginning, to be generic), leaving out the
00748    * active tab, and then paints the current tab once everything else is
00749    * painted.  In addition, GTK uses a 2-pixel overlap between adjacent tabs
00750    * (this value is hard-coded in gtknotebook.c).  For purposes of mapping to
00751    * gecko's frame positions, we put this overlap on the far edge of the frame
00752    * (i.e., for a horizontal/top tab strip, we shift the left side of each tab
00753    * 2px to the left, into the neighboring tab's frame rect.  The right 2px
00754    * of a tab's frame will be referred to as the "overlap area".
00755    *
00756    * Since we can't guarantee painting order with gecko, we need to manage
00757    * the overlap area manually. There are three types of tab boundaries we need
00758    * to handle:
00759    *
00760    * * two non-active tabs: In this case, we just have both tabs paint
00761    *                        normally.
00762    *
00763    * * non-active to active tab: Here, we need the tab on the left to paint
00764    *                             itself normally, then paint the edge of the
00765    *                             active tab in its overlap area.
00766    *
00767    * * active to non-active tab: In this case, we just have both tabs paint
00768    *                             normally.
00769    *
00770    * We need to make an exception for the first tab - since there is no tab to
00771    * the left to paint the overlap area, we do _not_ shift the tab left by 2px.
00772    */
00773 
00774   GtkStyle* style;
00775   ensure_tab_widget();
00776 
00777   if (!(flags & MOZ_GTK_TAB_FIRST)) {
00778     rect->x -= 2;
00779     rect->width += 2;
00780   }
00781 
00782   style = gTabWidget->style;
00783   TSOffsetStyleGCs(style, rect->x, rect->y);
00784   gtk_paint_extension(style, drawable,
00785                       ((flags & MOZ_GTK_TAB_SELECTED) ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE),
00786                       GTK_SHADOW_OUT, cliprect, gTabWidget, "tab", rect->x,
00787                       rect->y, rect->width, rect->height, GTK_POS_BOTTOM);
00788 
00789   if (flags & MOZ_GTK_TAB_BEFORE_SELECTED) {
00790     gboolean before_selected = ((flags & MOZ_GTK_TAB_BEFORE_SELECTED) != 0);
00791     cliprect->y -= 2;
00792     cliprect->height += 2;
00793     TSOffsetStyleGCs(style, rect->x + rect->width - 2, rect->y - (2 * before_selected));
00794     gtk_paint_extension(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
00795                         cliprect, gTabWidget, "tab", rect->x + rect->width - 2,
00796                         rect->y - (2 * before_selected), rect->width,
00797                         rect->height + (2 * before_selected), GTK_POS_BOTTOM);
00798   }
00799 
00800   return MOZ_GTK_SUCCESS;
00801 }
00802 
00803 static gint
00804 moz_gtk_tabpanels_paint(GdkDrawable* drawable, GdkRectangle* rect,
00805                         GdkRectangle* cliprect)
00806 {
00807   GtkStyle* style;
00808 
00809   ensure_tab_widget();
00810   style = gTabWidget->style;
00811 
00812   TSOffsetStyleGCs(style, rect->x, rect->y);
00813   gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
00814                 cliprect, gTabWidget, "notebook", rect->x, rect->y,
00815                 rect->width, rect->height);
00816 
00817   return MOZ_GTK_SUCCESS;
00818 }
00819 
00820 gint
00821 moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* xthickness,
00822                           gint* ythickness)
00823 {
00824   GtkWidget* w;
00825   switch (widget) {
00826   case MOZ_GTK_BUTTON:
00827     ensure_button_widget();
00828     w = gButtonWidget;
00829     break;
00830   case MOZ_GTK_TOOLBAR:
00831     ensure_handlebox_widget();
00832     w = gHandleBoxWidget;
00833     break;
00834   case MOZ_GTK_ENTRY:
00835     ensure_entry_widget();
00836     w = gEntryWidget;
00837     break;
00838   case MOZ_GTK_DROPDOWN_ARROW:
00839     ensure_arrow_widget();
00840     w = gDropdownButtonWidget;
00841     break;
00842   case MOZ_GTK_TABPANELS:
00843     ensure_tab_widget();
00844     w = gTabWidget;
00845     break;
00846   case MOZ_GTK_PROGRESSBAR:
00847     ensure_progress_widget();
00848     w = gProgressWidget;
00849     break;
00850   case MOZ_GTK_FRAME:
00851     ensure_frame_widget();
00852     w = gFrameWidget;
00853     break;
00854   case MOZ_GTK_CHECKBUTTON_CONTAINER:
00855   case MOZ_GTK_RADIOBUTTON_CONTAINER:
00856     ensure_checkbox_widget();
00857     *xthickness = *ythickness =
00858       GTK_CONTAINER(gCheckboxWidget)->border_width + 1;
00859 
00860     return MOZ_GTK_SUCCESS;
00861 
00862   case MOZ_GTK_CHECKBUTTON_LABEL:
00863   case MOZ_GTK_RADIOBUTTON_LABEL:
00864     *xthickness = *ythickness = 0;
00865     return MOZ_GTK_SUCCESS;
00866 
00867   case MOZ_GTK_CHECKBUTTON:
00868   case MOZ_GTK_RADIOBUTTON:
00869   case MOZ_GTK_SCROLLBAR_BUTTON:
00870   case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
00871   case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
00872   case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
00873   case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
00874   case MOZ_GTK_GRIPPER:
00875   case MOZ_GTK_TOOLTIP:
00876   case MOZ_GTK_PROGRESS_CHUNK:
00877   case MOZ_GTK_TAB:
00878     /* These widgets have no borders, since they are not containers. */
00879     *xthickness = 0;
00880     *ythickness = 0;
00881     return MOZ_GTK_SUCCESS;
00882   default:
00883     g_warning("Unsupported widget type: %d", widget);
00884     return MOZ_GTK_UNKNOWN_WIDGET;
00885   }
00886 
00887   *xthickness = w->style->klass->xthickness;
00888   *ythickness = w->style->klass->ythickness;
00889 
00890   return MOZ_GTK_SUCCESS;
00891 }
00892 
00893 gint
00894 moz_gtk_get_dropdown_arrow_size(gint* width, gint* height)
00895 {
00896   ensure_arrow_widget();
00897 
00898   /*
00899    * First get the border of the dropdown arrow, then add in the requested
00900    * size of the arrow.  Note that the minimum arrow size is fixed at
00901    * 11 pixels.
00902    */
00903 
00904   *width = 2 * (1 + gDropdownButtonWidget->style->klass->xthickness);
00905   *width += 11 + GTK_MISC(gArrowWidget)->xpad * 2;
00906 
00907   *height = 2 * (1 + gDropdownButtonWidget->style->klass->ythickness);
00908   *height += 11 + GTK_MISC(gArrowWidget)->ypad * 2;
00909 
00910   return MOZ_GTK_SUCCESS;
00911 }
00912 
00913 gint
00914 moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
00915 {
00916   GtkRangeClass* klass;
00917   GtkStyle* style;
00918 
00919   ensure_scrollbar_widget();
00920   klass = GTK_RANGE_CLASS(GTK_OBJECT(gScrollbarWidget)->klass);
00921   style = gScrollbarWidget->style;
00922 
00923   if (style_prop_func) {
00924     /*
00925      * This API is supported only in GTK+ >= 1.2.9, and gives per-theme values.
00926      */
00927 
00928     metrics->slider_width = style_prop_func(style, "GtkRange::slider_width",
00929                                             klass->slider_width);
00930 
00931     metrics->trough_border = style_prop_func(style, "GtkRange::trough_border",
00932                                              style->klass->xthickness);
00933 
00934     metrics->stepper_size = style_prop_func(style, "GtkRange::stepper_size",
00935                                             klass->stepper_size);
00936 
00937     metrics->stepper_spacing = style_prop_func(style,
00938                                                "GtkRange::stepper_spacing",
00939                                                klass->stepper_slider_spacing);
00940   } else {
00941     /*
00942      * This is the older method, which gives per-engine values.
00943      */
00944 
00945     metrics->slider_width = klass->slider_width;
00946     metrics->trough_border = style->klass->xthickness;
00947     metrics->stepper_size = klass->stepper_size;
00948     metrics->stepper_spacing = klass->stepper_slider_spacing;
00949   }
00950 
00951   metrics->min_slider_size = klass->min_slider_size;
00952 
00953   return MOZ_GTK_SUCCESS;
00954 }
00955 
00956 gint
00957 moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable,
00958                      GdkRectangle* rect, GdkRectangle* cliprect,
00959                      GtkWidgetState* state, gint flags)
00960 {
00961   switch (widget) {
00962   case MOZ_GTK_BUTTON:
00963     ensure_button_widget();
00964     return moz_gtk_button_paint(drawable, rect, cliprect, state,
00965                                 (GtkReliefStyle) flags, gButtonWidget);
00966     break;
00967   case MOZ_GTK_CHECKBUTTON:
00968   case MOZ_GTK_RADIOBUTTON:
00969     return moz_gtk_toggle_paint(drawable, rect, cliprect, state,
00970                                 (gboolean) flags,
00971                                 (widget == MOZ_GTK_RADIOBUTTON));
00972     break;
00973   case MOZ_GTK_SCROLLBAR_BUTTON:
00974     return moz_gtk_scrollbar_button_paint(drawable, rect, cliprect,
00975                                           state, (GtkArrowType) flags);
00976     break;
00977   case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL:
00978   case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL:
00979     return moz_gtk_scrollbar_trough_paint(drawable, rect, cliprect, state);
00980     break;
00981   case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
00982   case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
00983     return moz_gtk_scrollbar_thumb_paint(drawable, rect, cliprect, state);
00984     break;
00985   case MOZ_GTK_GRIPPER:
00986     return moz_gtk_gripper_paint(drawable, rect, cliprect, state);
00987     break;
00988   case MOZ_GTK_ENTRY:
00989     return moz_gtk_entry_paint(drawable, rect, cliprect, state);
00990     break;
00991   case MOZ_GTK_DROPDOWN_ARROW:
00992     return moz_gtk_dropdown_arrow_paint(drawable, rect, cliprect, state);
00993     break;
00994   case MOZ_GTK_CHECKBUTTON_CONTAINER:
00995   case MOZ_GTK_RADIOBUTTON_CONTAINER:
00996     return moz_gtk_container_paint(drawable, rect, cliprect, state,
00997                                    (widget == MOZ_GTK_RADIOBUTTON_CONTAINER));
00998     break;
00999   case MOZ_GTK_TOOLBAR:
01000     return moz_gtk_toolbar_paint(drawable, rect, cliprect);
01001     break;
01002   case MOZ_GTK_TOOLTIP:
01003     return moz_gtk_tooltip_paint(drawable, rect, cliprect);
01004     break;
01005   case MOZ_GTK_FRAME:
01006     return moz_gtk_frame_paint(drawable, rect, cliprect);
01007     break;
01008   case MOZ_GTK_PROGRESSBAR:
01009     return moz_gtk_progressbar_paint(drawable, rect, cliprect);
01010     break;
01011   case MOZ_GTK_PROGRESS_CHUNK:
01012     return moz_gtk_progress_chunk_paint(drawable, rect, cliprect);
01013     break;
01014   case MOZ_GTK_TAB:
01015     return moz_gtk_tab_paint(drawable, rect, cliprect, flags);
01016     break;
01017   case MOZ_GTK_TABPANELS:
01018     return moz_gtk_tabpanels_paint(drawable, rect, cliprect);
01019     break;
01020   case MOZ_GTK_CHECKBUTTON_LABEL:
01021   case MOZ_GTK_RADIOBUTTON_LABEL:
01022     /* We only support these so that we can prevent the CSS border
01023        from being drawn. */
01024     return MOZ_GTK_SUCCESS;
01025   default:
01026     g_warning("Unknown widget type: %d", widget);
01027   }
01028 
01029   return MOZ_GTK_UNKNOWN_WIDGET;
01030 }
01031 
01032 gint
01033 moz_gtk_shutdown()
01034 {
01035   if (gTooltipWidget)
01036     gtk_object_unref(GTK_OBJECT(gTooltipWidget));
01037   /* This will destroy all of our widgets */
01038   if (gProtoWindow)
01039     gtk_widget_destroy(gProtoWindow);
01040 
01041   gButtonWidget = NULL;
01042   gProtoWindow = NULL;
01043   gCheckboxWidget = NULL;
01044   gScrollbarWidget = NULL;
01045   gEntryWidget = NULL;
01046   gArrowWidget = NULL;
01047   gDropdownButtonWidget = NULL;
01048   gHandleBoxWidget = NULL;
01049   gFrameWidget = NULL;
01050   gProgressWidget = NULL;
01051   gTabWidget = NULL;
01052   gTooltipWidget = NULL;
01053 
01054   return MOZ_GTK_SUCCESS;
01055 }