| 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|---|
| 2 | /* ***** BEGIN LICENSE BLOCK ***** |
|---|
| 3 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
|---|
| 4 | * |
|---|
| 5 | * The contents of this file are subject to the Mozilla Public License Version |
|---|
| 6 | * 1.1 (the "License"); you may not use this file except in compliance with |
|---|
| 7 | * the License. You may obtain a copy of the License at |
|---|
| 8 | * http://www.mozilla.org/MPL/ |
|---|
| 9 | * |
|---|
| 10 | * Software distributed under the License is distributed on an "AS IS" basis, |
|---|
| 11 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
|---|
| 12 | * for the specific language governing rights and limitations under the |
|---|
| 13 | * License. |
|---|
| 14 | * |
|---|
| 15 | * The Original Code is mozilla.org code. |
|---|
| 16 | * |
|---|
| 17 | * The Initial Developer of the Original Code is |
|---|
| 18 | * Netscape Communications Corporation. |
|---|
| 19 | * Portions created by the Initial Developer are Copyright (C) 2002 |
|---|
| 20 | * the Initial Developer. All Rights Reserved. |
|---|
| 21 | * |
|---|
| 22 | * Contributor(s): |
|---|
| 23 | * Brian Ryner <bryner@brianryner.com> (Original Author) |
|---|
| 24 | * Pierre Chanial <p_ch@verizon.net> |
|---|
| 25 | * Michael Ventnor <m.ventnor@gmail.com> |
|---|
| 26 | * Alp Toker <alp@nuanti.com> |
|---|
| 27 | * |
|---|
| 28 | * Alternatively, the contents of this file may be used under the terms of |
|---|
| 29 | * either the GNU General Public License Version 2 or later (the "GPL"), or |
|---|
| 30 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
|---|
| 31 | * in which case the provisions of the GPL or the LGPL are applicable instead |
|---|
| 32 | * of those above. If you wish to allow use of your version of this file only |
|---|
| 33 | * under the terms of either the GPL or the LGPL, and not to allow others to |
|---|
| 34 | * use your version of this file under the terms of the MPL, indicate your |
|---|
| 35 | * decision by deleting the provisions above and replace them with the notice |
|---|
| 36 | * and other provisions required by the GPL or the LGPL. If you do not delete |
|---|
| 37 | * the provisions above, a recipient may use your version of this file under |
|---|
| 38 | * the terms of any one of the MPL, the GPL or the LGPL. |
|---|
| 39 | * |
|---|
| 40 | * ***** END LICENSE BLOCK ***** */ |
|---|
| 41 | |
|---|
| 42 | /* |
|---|
| 43 | * This file contains painting functions for each of the gtk2 widgets. |
|---|
| 44 | * Adapted from the gtkdrawing.c, and gtk+2.0 source. |
|---|
| 45 | */ |
|---|
| 46 | |
|---|
| 47 | #include <gtk/gtk.h> |
|---|
| 48 | #include <gdk/gdkprivate.h> |
|---|
| 49 | #include <string.h> |
|---|
| 50 | #include "gtkdrawing.h" |
|---|
| 51 | |
|---|
| 52 | #include <math.h> |
|---|
| 53 | |
|---|
| 54 | #define XTHICKNESS(style) (style->xthickness) |
|---|
| 55 | #define YTHICKNESS(style) (style->ythickness) |
|---|
| 56 | #define WINDOW_IS_MAPPED(window) ((window) && GDK_IS_WINDOW(window) && gdk_window_is_visible(window)) |
|---|
| 57 | |
|---|
| 58 | static GtkWidget* gProtoWindow; |
|---|
| 59 | static GtkWidget* gButtonWidget; |
|---|
| 60 | static GtkWidget* gToggleButtonWidget; |
|---|
| 61 | static GtkWidget* gButtonArrowWidget; |
|---|
| 62 | static GtkWidget* gCheckboxWidget; |
|---|
| 63 | static GtkWidget* gRadiobuttonWidget; |
|---|
| 64 | static GtkWidget* gHorizScrollbarWidget; |
|---|
| 65 | static GtkWidget* gVertScrollbarWidget; |
|---|
| 66 | static GtkWidget* gSpinWidget; |
|---|
| 67 | static GtkWidget* gHScaleWidget; |
|---|
| 68 | static GtkWidget* gVScaleWidget; |
|---|
| 69 | static GtkWidget* gEntryWidget; |
|---|
| 70 | static GtkWidget* gComboBoxWidget; |
|---|
| 71 | static GtkWidget* gComboBoxButtonWidget; |
|---|
| 72 | static GtkWidget* gComboBoxArrowWidget; |
|---|
| 73 | static GtkWidget* gComboBoxSeparatorWidget; |
|---|
| 74 | static GtkWidget* gComboBoxEntryWidget; |
|---|
| 75 | static GtkWidget* gComboBoxEntryTextareaWidget; |
|---|
| 76 | static GtkWidget* gComboBoxEntryButtonWidget; |
|---|
| 77 | static GtkWidget* gComboBoxEntryArrowWidget; |
|---|
| 78 | static GtkWidget* gHandleBoxWidget; |
|---|
| 79 | static GtkWidget* gToolbarWidget; |
|---|
| 80 | static GtkWidget* gFrameWidget; |
|---|
| 81 | static GtkWidget* gStatusbarWidget; |
|---|
| 82 | static GtkWidget* gProgressWidget; |
|---|
| 83 | static GtkWidget* gTabWidget; |
|---|
| 84 | static GtkWidget* gTooltipWidget; |
|---|
| 85 | static GtkWidget* gMenuBarWidget; |
|---|
| 86 | static GtkWidget* gMenuBarItemWidget; |
|---|
| 87 | static GtkWidget* gMenuPopupWidget; |
|---|
| 88 | static GtkWidget* gMenuItemWidget; |
|---|
| 89 | static GtkWidget* gImageMenuItemWidget; |
|---|
| 90 | static GtkWidget* gCheckMenuItemWidget; |
|---|
| 91 | static GtkWidget* gTreeViewWidget; |
|---|
| 92 | static GtkWidget* gMiddleTreeViewColumn; |
|---|
| 93 | static GtkWidget* gTreeHeaderCellWidget; |
|---|
| 94 | static GtkWidget* gTreeHeaderSortArrowWidget; |
|---|
| 95 | static GtkWidget* gExpanderWidget; |
|---|
| 96 | static GtkWidget* gToolbarSeparatorWidget; |
|---|
| 97 | static GtkWidget* gMenuSeparatorWidget; |
|---|
| 98 | static GtkWidget* gHPanedWidget; |
|---|
| 99 | static GtkWidget* gVPanedWidget; |
|---|
| 100 | static GtkWidget* gScrolledWindowWidget; |
|---|
| 101 | |
|---|
| 102 | static style_prop_t style_prop_func; |
|---|
| 103 | static gboolean have_arrow_scaling; |
|---|
| 104 | static gboolean have_2_10; |
|---|
| 105 | static gboolean is_initialized; |
|---|
| 106 | |
|---|
| 107 | /* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine |
|---|
| 108 | that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific |
|---|
| 109 | things they may want to do. */ |
|---|
| 110 | static void |
|---|
| 111 | moz_gtk_set_widget_name(GtkWidget* widget) |
|---|
| 112 | { |
|---|
| 113 | gtk_widget_set_name(widget, "MozillaGtkWidget"); |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | gint |
|---|
| 117 | moz_gtk_enable_style_props(style_prop_t styleGetProp) |
|---|
| 118 | { |
|---|
| 119 | style_prop_func = styleGetProp; |
|---|
| 120 | return MOZ_GTK_SUCCESS; |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | static gint |
|---|
| 124 | ensure_window_widget() |
|---|
| 125 | { |
|---|
| 126 | if (!gProtoWindow) { |
|---|
| 127 | gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP); |
|---|
| 128 | gtk_widget_realize(gProtoWindow); |
|---|
| 129 | moz_gtk_set_widget_name(gProtoWindow); |
|---|
| 130 | } |
|---|
| 131 | return MOZ_GTK_SUCCESS; |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | static gint |
|---|
| 135 | setup_widget_prototype(GtkWidget* widget) |
|---|
| 136 | { |
|---|
| 137 | static GtkWidget* protoLayout; |
|---|
| 138 | ensure_window_widget(); |
|---|
| 139 | if (!protoLayout) { |
|---|
| 140 | protoLayout = gtk_fixed_new(); |
|---|
| 141 | gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout); |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | gtk_container_add(GTK_CONTAINER(protoLayout), widget); |
|---|
| 145 | gtk_widget_realize(widget); |
|---|
| 146 | g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); |
|---|
| 147 | return MOZ_GTK_SUCCESS; |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | static gint |
|---|
| 151 | ensure_button_widget() |
|---|
| 152 | { |
|---|
| 153 | if (!gButtonWidget) { |
|---|
| 154 | gButtonWidget = gtk_button_new_with_label("M"); |
|---|
| 155 | setup_widget_prototype(gButtonWidget); |
|---|
| 156 | } |
|---|
| 157 | return MOZ_GTK_SUCCESS; |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | static gint |
|---|
| 161 | ensure_hpaned_widget() |
|---|
| 162 | { |
|---|
| 163 | if (!gHPanedWidget) { |
|---|
| 164 | gHPanedWidget = gtk_hpaned_new(); |
|---|
| 165 | setup_widget_prototype(gHPanedWidget); |
|---|
| 166 | } |
|---|
| 167 | return MOZ_GTK_SUCCESS; |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | static gint |
|---|
| 171 | ensure_vpaned_widget() |
|---|
| 172 | { |
|---|
| 173 | if (!gVPanedWidget) { |
|---|
| 174 | gVPanedWidget = gtk_vpaned_new(); |
|---|
| 175 | setup_widget_prototype(gVPanedWidget); |
|---|
| 176 | } |
|---|
| 177 | return MOZ_GTK_SUCCESS; |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | static gint |
|---|
| 181 | ensure_toggle_button_widget() |
|---|
| 182 | { |
|---|
| 183 | if (!gToggleButtonWidget) { |
|---|
| 184 | gToggleButtonWidget = gtk_toggle_button_new(); |
|---|
| 185 | setup_widget_prototype(gToggleButtonWidget); |
|---|
| 186 | /* toggle button must be set active to get the right style on hover. */ |
|---|
| 187 | GTK_TOGGLE_BUTTON(gToggleButtonWidget)->active = TRUE; |
|---|
| 188 | } |
|---|
| 189 | return MOZ_GTK_SUCCESS; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | static gint |
|---|
| 193 | ensure_button_arrow_widget() |
|---|
| 194 | { |
|---|
| 195 | if (!gButtonArrowWidget) { |
|---|
| 196 | ensure_toggle_button_widget(); |
|---|
| 197 | |
|---|
| 198 | gButtonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); |
|---|
| 199 | gtk_container_add(GTK_CONTAINER(gToggleButtonWidget), gButtonArrowWidget); |
|---|
| 200 | gtk_widget_realize(gButtonArrowWidget); |
|---|
| 201 | } |
|---|
| 202 | return MOZ_GTK_SUCCESS; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | static gint |
|---|
| 206 | ensure_checkbox_widget() |
|---|
| 207 | { |
|---|
| 208 | if (!gCheckboxWidget) { |
|---|
| 209 | gCheckboxWidget = gtk_check_button_new_with_label("M"); |
|---|
| 210 | setup_widget_prototype(gCheckboxWidget); |
|---|
| 211 | } |
|---|
| 212 | return MOZ_GTK_SUCCESS; |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | static gint |
|---|
| 216 | ensure_radiobutton_widget() |
|---|
| 217 | { |
|---|
| 218 | if (!gRadiobuttonWidget) { |
|---|
| 219 | gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M"); |
|---|
| 220 | setup_widget_prototype(gRadiobuttonWidget); |
|---|
| 221 | } |
|---|
| 222 | return MOZ_GTK_SUCCESS; |
|---|
| 223 | } |
|---|
| 224 | |
|---|
| 225 | static gint |
|---|
| 226 | ensure_scrollbar_widget() |
|---|
| 227 | { |
|---|
| 228 | if (!gVertScrollbarWidget) { |
|---|
| 229 | gVertScrollbarWidget = gtk_vscrollbar_new(NULL); |
|---|
| 230 | setup_widget_prototype(gVertScrollbarWidget); |
|---|
| 231 | } |
|---|
| 232 | if (!gHorizScrollbarWidget) { |
|---|
| 233 | gHorizScrollbarWidget = gtk_hscrollbar_new(NULL); |
|---|
| 234 | setup_widget_prototype(gHorizScrollbarWidget); |
|---|
| 235 | } |
|---|
| 236 | return MOZ_GTK_SUCCESS; |
|---|
| 237 | } |
|---|
| 238 | |
|---|
| 239 | static gint |
|---|
| 240 | ensure_spin_widget() |
|---|
| 241 | { |
|---|
| 242 | if (!gSpinWidget) { |
|---|
| 243 | gSpinWidget = gtk_spin_button_new(NULL, 1, 0); |
|---|
| 244 | setup_widget_prototype(gSpinWidget); |
|---|
| 245 | } |
|---|
| 246 | return MOZ_GTK_SUCCESS; |
|---|
| 247 | } |
|---|
| 248 | |
|---|
| 249 | static gint |
|---|
| 250 | ensure_scale_widget() |
|---|
| 251 | { |
|---|
| 252 | if (!gHScaleWidget) { |
|---|
| 253 | gHScaleWidget = gtk_hscale_new(NULL); |
|---|
| 254 | setup_widget_prototype(gHScaleWidget); |
|---|
| 255 | } |
|---|
| 256 | if (!gVScaleWidget) { |
|---|
| 257 | gVScaleWidget = gtk_vscale_new(NULL); |
|---|
| 258 | setup_widget_prototype(gVScaleWidget); |
|---|
| 259 | } |
|---|
| 260 | return MOZ_GTK_SUCCESS; |
|---|
| 261 | } |
|---|
| 262 | |
|---|
| 263 | static gint |
|---|
| 264 | ensure_entry_widget() |
|---|
| 265 | { |
|---|
| 266 | if (!gEntryWidget) { |
|---|
| 267 | gEntryWidget = gtk_entry_new(); |
|---|
| 268 | setup_widget_prototype(gEntryWidget); |
|---|
| 269 | } |
|---|
| 270 | return MOZ_GTK_SUCCESS; |
|---|
| 271 | } |
|---|
| 272 | |
|---|
| 273 | /* We need to have pointers to the inner widgets (button, separator, arrow) |
|---|
| 274 | * of the ComboBox to get the correct rendering from theme engines which |
|---|
| 275 | * special cases their look. Since the inner layout can change, we ask GTK |
|---|
| 276 | * to NULL our pointers when they are about to become invalid because the |
|---|
| 277 | * corresponding widgets don't exist anymore. It's the role of |
|---|
| 278 | * g_object_add_weak_pointer(). |
|---|
| 279 | * Note that if we don't find the inner widgets (which shouldn't happen), we |
|---|
| 280 | * fallback to use generic "non-inner" widgets, and they don't need that kind |
|---|
| 281 | * of weak pointer since they are explicit children of gProtoWindow and as |
|---|
| 282 | * such GTK holds a strong reference to them. */ |
|---|
| 283 | static void |
|---|
| 284 | moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) |
|---|
| 285 | { |
|---|
| 286 | if (GTK_IS_TOGGLE_BUTTON(widget)) { |
|---|
| 287 | gComboBoxButtonWidget = widget; |
|---|
| 288 | g_object_add_weak_pointer(G_OBJECT(widget), |
|---|
| 289 | (gpointer) &gComboBoxButtonWidget); |
|---|
| 290 | gtk_widget_realize(widget); |
|---|
| 291 | g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); |
|---|
| 292 | } |
|---|
| 293 | } |
|---|
| 294 | |
|---|
| 295 | static void |
|---|
| 296 | moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, |
|---|
| 297 | gpointer client_data) |
|---|
| 298 | { |
|---|
| 299 | if (GTK_IS_SEPARATOR(widget)) { |
|---|
| 300 | gComboBoxSeparatorWidget = widget; |
|---|
| 301 | g_object_add_weak_pointer(G_OBJECT(widget), |
|---|
| 302 | (gpointer) &gComboBoxSeparatorWidget); |
|---|
| 303 | } else if (GTK_IS_ARROW(widget)) { |
|---|
| 304 | gComboBoxArrowWidget = widget; |
|---|
| 305 | g_object_add_weak_pointer(G_OBJECT(widget), |
|---|
| 306 | (gpointer) &gComboBoxArrowWidget); |
|---|
| 307 | } else |
|---|
| 308 | return; |
|---|
| 309 | gtk_widget_realize(widget); |
|---|
| 310 | g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | static gint |
|---|
| 314 | ensure_combo_box_widgets() |
|---|
| 315 | { |
|---|
| 316 | GtkWidget* buttonChild; |
|---|
| 317 | |
|---|
| 318 | if (gComboBoxButtonWidget && gComboBoxArrowWidget) |
|---|
| 319 | return MOZ_GTK_SUCCESS; |
|---|
| 320 | |
|---|
| 321 | /* Create a ComboBox if needed */ |
|---|
| 322 | if (!gComboBoxWidget) { |
|---|
| 323 | gComboBoxWidget = gtk_combo_box_new(); |
|---|
| 324 | setup_widget_prototype(gComboBoxWidget); |
|---|
| 325 | } |
|---|
| 326 | |
|---|
| 327 | /* Get its inner Button */ |
|---|
| 328 | gtk_container_forall(GTK_CONTAINER(gComboBoxWidget), |
|---|
| 329 | moz_gtk_get_combo_box_inner_button, |
|---|
| 330 | NULL); |
|---|
| 331 | |
|---|
| 332 | if (gComboBoxButtonWidget) { |
|---|
| 333 | /* Get the widgets inside the Button */ |
|---|
| 334 | buttonChild = GTK_BIN(gComboBoxButtonWidget)->child; |
|---|
| 335 | if (GTK_IS_HBOX(buttonChild)) { |
|---|
| 336 | /* appears-as-list = FALSE, cell-view = TRUE; the button |
|---|
| 337 | * contains an hbox. This hbox is there because the ComboBox |
|---|
| 338 | * needs to place a cell renderer, a separator, and an arrow in |
|---|
| 339 | * the button when appears-as-list is FALSE. */ |
|---|
| 340 | gtk_container_forall(GTK_CONTAINER(buttonChild), |
|---|
| 341 | moz_gtk_get_combo_box_button_inner_widgets, |
|---|
| 342 | NULL); |
|---|
| 343 | } else if(GTK_IS_ARROW(buttonChild)) { |
|---|
| 344 | /* appears-as-list = TRUE, or cell-view = FALSE; |
|---|
| 345 | * the button only contains an arrow */ |
|---|
| 346 | gComboBoxArrowWidget = buttonChild; |
|---|
| 347 | g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) |
|---|
| 348 | &gComboBoxArrowWidget); |
|---|
| 349 | gtk_widget_realize(gComboBoxArrowWidget); |
|---|
| 350 | g_object_set_data(G_OBJECT(gComboBoxArrowWidget), |
|---|
| 351 | "transparent-bg-hint", TRUE); |
|---|
| 352 | } |
|---|
| 353 | } else { |
|---|
| 354 | /* Shouldn't be reached with current internal gtk implementation; we |
|---|
| 355 | * use a generic toggle button as last resort fallback to avoid |
|---|
| 356 | * crashing. */ |
|---|
| 357 | ensure_toggle_button_widget(); |
|---|
| 358 | gComboBoxButtonWidget = gToggleButtonWidget; |
|---|
| 359 | } |
|---|
| 360 | |
|---|
| 361 | if (!gComboBoxArrowWidget) { |
|---|
| 362 | /* Shouldn't be reached with current internal gtk implementation; |
|---|
| 363 | * we gButtonArrowWidget as last resort fallback to avoid |
|---|
| 364 | * crashing. */ |
|---|
| 365 | ensure_button_arrow_widget(); |
|---|
| 366 | gComboBoxArrowWidget = gButtonArrowWidget; |
|---|
| 367 | } |
|---|
| 368 | |
|---|
| 369 | /* We don't test the validity of gComboBoxSeparatorWidget since there |
|---|
| 370 | * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it |
|---|
| 371 | * is invalid we just won't paint it. */ |
|---|
| 372 | |
|---|
| 373 | return MOZ_GTK_SUCCESS; |
|---|
| 374 | } |
|---|
| 375 | |
|---|
| 376 | /* We need to have pointers to the inner widgets (entry, button, arrow) of |
|---|
| 377 | * the ComboBoxEntry to get the correct rendering from theme engines which |
|---|
| 378 | * special cases their look. Since the inner layout can change, we ask GTK |
|---|
| 379 | * to NULL our pointers when they are about to become invalid because the |
|---|
| 380 | * corresponding widgets don't exist anymore. It's the role of |
|---|
| 381 | * g_object_add_weak_pointer(). |
|---|
| 382 | * Note that if we don't find the inner widgets (which shouldn't happen), we |
|---|
| 383 | * fallback to use generic "non-inner" widgets, and they don't need that kind |
|---|
| 384 | * of weak pointer since they are explicit children of gProtoWindow and as |
|---|
| 385 | * such GTK holds a strong reference to them. */ |
|---|
| 386 | static void |
|---|
| 387 | moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, |
|---|
| 388 | gpointer client_data) |
|---|
| 389 | { |
|---|
| 390 | if (GTK_IS_TOGGLE_BUTTON(widget)) { |
|---|
| 391 | gComboBoxEntryButtonWidget = widget; |
|---|
| 392 | g_object_add_weak_pointer(G_OBJECT(widget), |
|---|
| 393 | (gpointer) &gComboBoxEntryButtonWidget); |
|---|
| 394 | } else if (GTK_IS_ENTRY(widget)) { |
|---|
| 395 | gComboBoxEntryTextareaWidget = widget; |
|---|
| 396 | g_object_add_weak_pointer(G_OBJECT(widget), |
|---|
| 397 | (gpointer) &gComboBoxEntryTextareaWidget); |
|---|
| 398 | } else |
|---|
| 399 | return; |
|---|
| 400 | gtk_widget_realize(widget); |
|---|
| 401 | g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); |
|---|
| 402 | } |
|---|
| 403 | |
|---|
| 404 | static void |
|---|
| 405 | moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) |
|---|
| 406 | { |
|---|
| 407 | if (GTK_IS_ARROW(widget)) { |
|---|
| 408 | gComboBoxEntryArrowWidget = widget; |
|---|
| 409 | g_object_add_weak_pointer(G_OBJECT(widget), |
|---|
| 410 | (gpointer) &gComboBoxEntryArrowWidget); |
|---|
| 411 | gtk_widget_realize(widget); |
|---|
| 412 | g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); |
|---|
| 413 | } |
|---|
| 414 | } |
|---|
| 415 | |
|---|
| 416 | static gint |
|---|
| 417 | ensure_combo_box_entry_widgets() |
|---|
| 418 | { |
|---|
| 419 | GtkWidget* buttonChild; |
|---|
| 420 | |
|---|
| 421 | if (gComboBoxEntryTextareaWidget && |
|---|
| 422 | gComboBoxEntryButtonWidget && |
|---|
| 423 | gComboBoxEntryArrowWidget) |
|---|
| 424 | return MOZ_GTK_SUCCESS; |
|---|
| 425 | |
|---|
| 426 | /* Create a ComboBoxEntry if needed */ |
|---|
| 427 | if (!gComboBoxEntryWidget) { |
|---|
| 428 | gComboBoxEntryWidget = gtk_combo_box_entry_new(); |
|---|
| 429 | setup_widget_prototype(gComboBoxEntryWidget); |
|---|
| 430 | } |
|---|
| 431 | |
|---|
| 432 | /* Get its inner Entry and Button */ |
|---|
| 433 | gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), |
|---|
| 434 | moz_gtk_get_combo_box_entry_inner_widgets, |
|---|
| 435 | NULL); |
|---|
| 436 | |
|---|
| 437 | if (!gComboBoxEntryTextareaWidget) { |
|---|
| 438 | ensure_entry_widget(); |
|---|
| 439 | gComboBoxEntryTextareaWidget = gEntryWidget; |
|---|
| 440 | } |
|---|
| 441 | |
|---|
| 442 | if (gComboBoxEntryButtonWidget) { |
|---|
| 443 | /* Get the Arrow inside the Button */ |
|---|
| 444 | buttonChild = GTK_BIN(gComboBoxEntryButtonWidget)->child; |
|---|
| 445 | if (GTK_IS_HBOX(buttonChild)) { |
|---|
| 446 | /* appears-as-list = FALSE, cell-view = TRUE; the button |
|---|
| 447 | * contains an hbox. This hbox is there because ComboBoxEntry |
|---|
| 448 | * inherits from ComboBox which needs to place a cell renderer, |
|---|
| 449 | * a separator, and an arrow in the button when appears-as-list |
|---|
| 450 | * is FALSE. Here the hbox should only contain an arrow, since |
|---|
| 451 | * a ComboBoxEntry doesn't need all those widgets in the |
|---|
| 452 | * button. */ |
|---|
| 453 | gtk_container_forall(GTK_CONTAINER(buttonChild), |
|---|
| 454 | moz_gtk_get_combo_box_entry_arrow, |
|---|
| 455 | NULL); |
|---|
| 456 | } else if(GTK_IS_ARROW(buttonChild)) { |
|---|
| 457 | /* appears-as-list = TRUE, or cell-view = FALSE; |
|---|
| 458 | * the button only contains an arrow */ |
|---|
| 459 | gComboBoxEntryArrowWidget = buttonChild; |
|---|
| 460 | g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) |
|---|
| 461 | &gComboBoxEntryArrowWidget); |
|---|
| 462 | gtk_widget_realize(gComboBoxEntryArrowWidget); |
|---|
| 463 | g_object_set_data(G_OBJECT(gComboBoxEntryArrowWidget), |
|---|
| 464 | "transparent-bg-hint", TRUE); |
|---|
| 465 | } |
|---|
| 466 | } else { |
|---|
| 467 | /* Shouldn't be reached with current internal gtk implementation; |
|---|
| 468 | * we use a generic toggle button as last resort fallback to avoid |
|---|
| 469 | * crashing. */ |
|---|
| 470 | ensure_toggle_button_widget(); |
|---|
| 471 | gComboBoxEntryButtonWidget = gToggleButtonWidget; |
|---|
| 472 | } |
|---|
| 473 | |
|---|
| 474 | if (!gComboBoxEntryArrowWidget) { |
|---|
| 475 | /* Shouldn't be reached with current internal gtk implementation; |
|---|
| 476 | * we gButtonArrowWidget as last resort fallback to avoid |
|---|
| 477 | * crashing. */ |
|---|
| 478 | ensure_button_arrow_widget(); |
|---|
| 479 | gComboBoxEntryArrowWidget = gButtonArrowWidget; |
|---|
| 480 | } |
|---|
| 481 | |
|---|
| 482 | return MOZ_GTK_SUCCESS; |
|---|
| 483 | } |
|---|
| 484 | |
|---|
| 485 | |
|---|
| 486 | static gint |
|---|
| 487 | ensure_handlebox_widget() |
|---|
| 488 | { |
|---|
| 489 | if (!gHandleBoxWidget) { |
|---|
| 490 | gHandleBoxWidget = gtk_handle_box_new(); |
|---|
| 491 | setup_widget_prototype(gHandleBoxWidget); |
|---|
| 492 | } |
|---|
| 493 | return MOZ_GTK_SUCCESS; |
|---|
| 494 | } |
|---|
| 495 | |
|---|
| 496 | static gint |
|---|
| 497 | ensure_toolbar_widget() |
|---|
| 498 | { |
|---|
| 499 | if (!gToolbarWidget) { |
|---|
| 500 | ensure_handlebox_widget(); |
|---|
| 501 | gToolbarWidget = gtk_toolbar_new(); |
|---|
| 502 | gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget); |
|---|
| 503 | gtk_widget_realize(gToolbarWidget); |
|---|
| 504 | g_object_set_data(G_OBJECT(gToolbarWidget), "transparent-bg-hint", TRUE); |
|---|
| 505 | } |
|---|
| 506 | return MOZ_GTK_SUCCESS; |
|---|
| 507 | } |
|---|
| 508 | |
|---|
| 509 | static gint |
|---|
| 510 | ensure_toolbar_separator_widget() |
|---|
| 511 | { |
|---|
| 512 | if (!gToolbarSeparatorWidget) { |
|---|
| 513 | ensure_toolbar_widget(); |
|---|
| 514 | gToolbarSeparatorWidget = GTK_WIDGET(gtk_separator_tool_item_new()); |
|---|
| 515 | setup_widget_prototype(gToolbarSeparatorWidget); |
|---|
| 516 | } |
|---|
| 517 | return MOZ_GTK_SUCCESS; |
|---|
| 518 | } |
|---|
| 519 | |
|---|
| 520 | static gint |
|---|
| 521 | ensure_tooltip_widget() |
|---|
| 522 | { |
|---|
| 523 | if (!gTooltipWidget) { |
|---|
| 524 | gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP); |
|---|
| 525 | gtk_widget_realize(gTooltipWidget); |
|---|
| 526 | moz_gtk_set_widget_name(gTooltipWidget); |
|---|
| 527 | } |
|---|
| 528 | return MOZ_GTK_SUCCESS; |
|---|
| 529 | } |
|---|
| 530 | |
|---|
| 531 | static gint |
|---|
| 532 | ensure_tab_widget() |
|---|
| 533 | { |
|---|
| 534 | if (!gTabWidget) { |
|---|
| 535 | gTabWidget = gtk_notebook_new(); |
|---|
| 536 | setup_widget_prototype(gTabWidget); |
|---|
| 537 | } |
|---|
| 538 | return MOZ_GTK_SUCCESS; |
|---|
| 539 | } |
|---|
| 540 | |
|---|
| 541 | static gint |
|---|
| 542 | ensure_progress_widget() |
|---|
| 543 | { |
|---|
| 544 | if (!gProgressWidget) { |
|---|
| 545 | gProgressWidget = gtk_progress_bar_new(); |
|---|
| 546 | setup_widget_prototype(gProgressWidget); |
|---|
| 547 | } |
|---|
| 548 | return MOZ_GTK_SUCCESS; |
|---|
| 549 | } |
|---|
| 550 | |
|---|
| 551 | static gint |
|---|
| 552 | ensure_statusbar_widget() |
|---|
| 553 | { |
|---|
| 554 | if (!gStatusbarWidget) { |
|---|
| 555 | gStatusbarWidget = gtk_statusbar_new(); |
|---|
| 556 | setup_widget_prototype(gStatusbarWidget); |
|---|
| 557 | } |
|---|
| 558 | return MOZ_GTK_SUCCESS; |
|---|
| 559 | } |
|---|
| 560 | |
|---|
| 561 | static gint |
|---|
| 562 | ensure_frame_widget() |
|---|
| 563 | { |
|---|
| 564 | if (!gFrameWidget) { |
|---|
| 565 | ensure_statusbar_widget(); |
|---|
| 566 | gFrameWidget = gtk_frame_new(NULL); |
|---|
| 567 | gtk_container_add(GTK_CONTAINER(gStatusbarWidget), gFrameWidget); |
|---|
| 568 | gtk_widget_realize(gFrameWidget); |
|---|
| 569 | } |
|---|
| 570 | return MOZ_GTK_SUCCESS; |
|---|
| 571 | } |
|---|
| 572 | |
|---|
| 573 | static gint |
|---|
| 574 | ensure_menu_bar_widget() |
|---|
| 575 | { |
|---|
| 576 | if (!gMenuBarWidget) { |
|---|
| 577 | gMenuBarWidget = gtk_menu_bar_new(); |
|---|
| 578 | setup_widget_prototype(gMenuBarWidget); |
|---|
| 579 | } |
|---|
| 580 | return MOZ_GTK_SUCCESS; |
|---|
| 581 | } |
|---|
| 582 | |
|---|
| 583 | static gint |
|---|
| 584 | ensure_menu_bar_item_widget() |
|---|
| 585 | { |
|---|
| 586 | if (!gMenuBarItemWidget) { |
|---|
| 587 | ensure_menu_bar_widget(); |
|---|
| 588 | gMenuBarItemWidget = gtk_menu_item_new(); |
|---|
| 589 | gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget), |
|---|
| 590 | gMenuBarItemWidget); |
|---|
| 591 | gtk_widget_realize(gMenuBarItemWidget); |
|---|
| 592 | g_object_set_data(G_OBJECT(gMenuBarItemWidget), |
|---|
| 593 | "transparent-bg-hint", TRUE); |
|---|
| 594 | } |
|---|
| 595 | return MOZ_GTK_SUCCESS; |
|---|
| 596 | } |
|---|
| 597 | |
|---|
| 598 | static gint |
|---|
| 599 | ensure_menu_popup_widget() |
|---|
| 600 | { |
|---|
| 601 | if (!gMenuPopupWidget) { |
|---|
| 602 | ensure_menu_bar_item_widget(); |
|---|
| 603 | gMenuPopupWidget = gtk_menu_new(); |
|---|
| 604 | gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget), |
|---|
| 605 | gMenuPopupWidget); |
|---|
| 606 | gtk_widget_realize(gMenuPopupWidget); |
|---|
| 607 | g_object_set_data(G_OBJECT(gMenuPopupWidget), |
|---|
| 608 | "transparent-bg-hint", TRUE); |
|---|
| 609 | } |
|---|
| 610 | return MOZ_GTK_SUCCESS; |
|---|
| 611 | } |
|---|
| 612 | |
|---|
| 613 | static gint |
|---|
| 614 | ensure_menu_item_widget() |
|---|
| 615 | { |
|---|
| 616 | if (!gMenuItemWidget) { |
|---|
| 617 | ensure_menu_popup_widget(); |
|---|
| 618 | gMenuItemWidget = gtk_menu_item_new_with_label("M"); |
|---|
| 619 | gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), |
|---|
| 620 | gMenuItemWidget); |
|---|
| 621 | gtk_widget_realize(gMenuItemWidget); |
|---|
| 622 | g_object_set_data(G_OBJECT(gMenuItemWidget), |
|---|
| 623 | "transparent-bg-hint", TRUE); |
|---|
| 624 | } |
|---|
| 625 | return MOZ_GTK_SUCCESS; |
|---|
| 626 | } |
|---|
| 627 | |
|---|
| 628 | static gint |
|---|
| 629 | ensure_image_menu_item_widget() |
|---|
| 630 | { |
|---|
| 631 | if (!gImageMenuItemWidget) { |
|---|
| 632 | ensure_menu_popup_widget(); |
|---|
| 633 | gImageMenuItemWidget = gtk_image_menu_item_new(); |
|---|
| 634 | gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), |
|---|
| 635 | gImageMenuItemWidget); |
|---|
| 636 | gtk_widget_realize(gImageMenuItemWidget); |
|---|
| 637 | g_object_set_data(G_OBJECT(gImageMenuItemWidget), |
|---|
| 638 | "transparent-bg-hint", TRUE); |
|---|
| 639 | } |
|---|
| 640 | return MOZ_GTK_SUCCESS; |
|---|
| 641 | } |
|---|
| 642 | |
|---|
| 643 | static gint |
|---|
| 644 | ensure_menu_separator_widget() |
|---|
| 645 | { |
|---|
| 646 | if (!gMenuSeparatorWidget) { |
|---|
| 647 | ensure_menu_popup_widget(); |
|---|
| 648 | gMenuSeparatorWidget = gtk_separator_menu_item_new(); |
|---|
| 649 | gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), |
|---|
| 650 | gMenuSeparatorWidget); |
|---|
| 651 | gtk_widget_realize(gMenuSeparatorWidget); |
|---|
| 652 | g_object_set_data(G_OBJECT(gMenuSeparatorWidget), |
|---|
| 653 | "transparent-bg-hint", TRUE); |
|---|
| 654 | } |
|---|
| 655 | return MOZ_GTK_SUCCESS; |
|---|
| 656 | } |
|---|
| 657 | |
|---|
| 658 | static gint |
|---|
| 659 | ensure_check_menu_item_widget() |
|---|
| 660 | { |
|---|
| 661 | if (!gCheckMenuItemWidget) { |
|---|
| 662 | ensure_menu_popup_widget(); |
|---|
| 663 | gCheckMenuItemWidget = gtk_check_menu_item_new_with_label("M"); |
|---|
| 664 | gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), |
|---|
| 665 | gCheckMenuItemWidget); |
|---|
| 666 | gtk_widget_realize(gCheckMenuItemWidget); |
|---|
| 667 | g_object_set_data(G_OBJECT(gCheckMenuItemWidget), |
|---|
| 668 | "transparent-bg-hint", TRUE); |
|---|
| 669 | } |
|---|
| 670 | return MOZ_GTK_SUCCESS; |
|---|
| 671 | } |
|---|
| 672 | |
|---|
| 673 | static gint |
|---|
| 674 | ensure_tree_view_widget() |
|---|
| 675 | { |
|---|
| 676 | if (!gTreeViewWidget) { |
|---|
| 677 | gTreeViewWidget = gtk_tree_view_new(); |
|---|
| 678 | setup_widget_prototype(gTreeViewWidget); |
|---|
| 679 | } |
|---|
| 680 | return MOZ_GTK_SUCCESS; |
|---|
| 681 | } |
|---|
| 682 | |
|---|
| 683 | static gint |
|---|
| 684 | ensure_tree_header_cell_widget() |
|---|
| 685 | { |
|---|
| 686 | if(!gTreeHeaderCellWidget) { |
|---|
| 687 | /* |
|---|
| 688 | * Some GTK engines paint the first and last cell |
|---|
| 689 | * of a TreeView header with a highlight. |
|---|
| 690 | * Since we do not know where our widget will be relative |
|---|
| 691 | * to the other buttons in the TreeView header, we must |
|---|
| 692 | * paint it as a button that is between two others, |
|---|
| 693 | * thus ensuring it is neither the first or last button |
|---|
| 694 | * in the header. |
|---|
| 695 | * GTK doesn't give us a way to do this explicitly, |
|---|
| 696 | * so we must paint with a button that is between two |
|---|
| 697 | * others. |
|---|
| 698 | */ |
|---|
| 699 | |
|---|
| 700 | GtkTreeViewColumn* firstTreeViewColumn; |
|---|
| 701 | GtkTreeViewColumn* lastTreeViewColumn; |
|---|
| 702 | |
|---|
| 703 | ensure_tree_view_widget(); |
|---|
| 704 | |
|---|
| 705 | /* Create and append our three columns */ |
|---|
| 706 | firstTreeViewColumn = gtk_tree_view_column_new(); |
|---|
| 707 | gtk_tree_view_column_set_title(firstTreeViewColumn, "M"); |
|---|
| 708 | gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn); |
|---|
| 709 | |
|---|
| 710 | gMiddleTreeViewColumn = gtk_tree_view_column_new(); |
|---|
| 711 | gtk_tree_view_column_set_title(GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn), "M"); |
|---|
| 712 | gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), |
|---|
| 713 | GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn)); |
|---|
| 714 | |
|---|
| 715 | lastTreeViewColumn = gtk_tree_view_column_new(); |
|---|
| 716 | gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); |
|---|
| 717 | gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn); |
|---|
| 718 | |
|---|
| 719 | /* Use the middle column's header for our button */ |
|---|
| 720 | gTreeHeaderCellWidget = GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn)->button; |
|---|
| 721 | gTreeHeaderSortArrowWidget = GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn)->arrow; |
|---|
| 722 | g_object_set_data(G_OBJECT(gTreeHeaderCellWidget), |
|---|
| 723 | "transparent-bg-hint", TRUE); |
|---|
| 724 | g_object_set_data(G_OBJECT(gTreeHeaderSortArrowWidget), |
|---|
| 725 | "transparent-bg-hint", TRUE); |
|---|
| 726 | } |
|---|
| 727 | return MOZ_GTK_SUCCESS; |
|---|
| 728 | } |
|---|
| 729 | |
|---|
| 730 | static gint |
|---|
| 731 | ensure_expander_widget() |
|---|
| 732 | { |
|---|
| 733 | if (!gExpanderWidget) { |
|---|
| 734 | gExpanderWidget = gtk_expander_new("M"); |
|---|
| 735 | setup_widget_prototype(gExpanderWidget); |
|---|
| 736 | } |
|---|
| 737 | return MOZ_GTK_SUCCESS; |
|---|
| 738 | } |
|---|
| 739 | |
|---|
| 740 | static gint |
|---|
| 741 | ensure_scrolled_window_widget() |
|---|
| 742 | { |
|---|
| 743 | if (!gScrolledWindowWidget) { |
|---|
| 744 | gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL); |
|---|
| 745 | setup_widget_prototype(gScrolledWindowWidget); |
|---|
| 746 | } |
|---|
| 747 | return MOZ_GTK_SUCCESS; |
|---|
| 748 | } |
|---|
| 749 | |
|---|
| 750 | static GtkStateType |
|---|
| 751 | ConvertGtkState(GtkWidgetState* state) |
|---|
| 752 | { |
|---|
| 753 | if (state->disabled) |
|---|
| 754 | return GTK_STATE_INSENSITIVE; |
|---|
| 755 | else if (state->depressed) |
|---|
| 756 | return (state->inHover ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE); |
|---|
| 757 | else if (state->inHover) |
|---|
| 758 | return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT); |
|---|
| 759 | else |
|---|
| 760 | return GTK_STATE_NORMAL; |
|---|
| 761 | } |
|---|
| 762 | |
|---|
| 763 | static gint |
|---|
| 764 | TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin) |
|---|
| 765 | { |
|---|
| 766 | int i; |
|---|
| 767 | /* there are 5 gc's in each array, for each of the widget states */ |
|---|
| 768 | for (i = 0; i < 5; ++i) |
|---|
| 769 | gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin); |
|---|
| 770 | return MOZ_GTK_SUCCESS; |
|---|
| 771 | } |
|---|
| 772 | |
|---|
| 773 | static gint |
|---|
| 774 | TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin) |
|---|
| 775 | { |
|---|
| 776 | TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin); |
|---|
| 777 | TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin); |
|---|
| 778 | TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin); |
|---|
| 779 | TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin); |
|---|
| 780 | TSOffsetStyleGCArray( |
|---|