widget/gtk/nsNativeThemeGTK.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsNativeThemeGTK.h"
michael@0 7 #include "nsThemeConstants.h"
michael@0 8 #include "gtkdrawing.h"
michael@0 9
michael@0 10 #include "nsIObserverService.h"
michael@0 11 #include "nsIServiceManager.h"
michael@0 12 #include "nsIFrame.h"
michael@0 13 #include "nsIPresShell.h"
michael@0 14 #include "nsIContent.h"
michael@0 15 #include "nsViewManager.h"
michael@0 16 #include "nsNameSpaceManager.h"
michael@0 17 #include "nsGfxCIID.h"
michael@0 18 #include "nsTransform2D.h"
michael@0 19 #include "nsMenuFrame.h"
michael@0 20 #include "prlink.h"
michael@0 21 #include "nsIDOMHTMLInputElement.h"
michael@0 22 #include "nsRenderingContext.h"
michael@0 23 #include "nsGkAtoms.h"
michael@0 24
michael@0 25 #include "mozilla/EventStates.h"
michael@0 26 #include "mozilla/Services.h"
michael@0 27
michael@0 28 #include <gdk/gdkprivate.h>
michael@0 29 #include <gtk/gtk.h>
michael@0 30
michael@0 31 #include "gfxContext.h"
michael@0 32 #include "gfxPlatformGtk.h"
michael@0 33 #include "gfxGdkNativeRenderer.h"
michael@0 34 #include <algorithm>
michael@0 35
michael@0 36 using namespace mozilla;
michael@0 37
michael@0 38 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
michael@0 39 nsIObserver)
michael@0 40
michael@0 41 static int gLastGdkError;
michael@0 42
michael@0 43 nsNativeThemeGTK::nsNativeThemeGTK()
michael@0 44 {
michael@0 45 if (moz_gtk_init() != MOZ_GTK_SUCCESS) {
michael@0 46 memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes));
michael@0 47 return;
michael@0 48 }
michael@0 49
michael@0 50 // We have to call moz_gtk_shutdown before the event loop stops running.
michael@0 51 nsCOMPtr<nsIObserverService> obsServ =
michael@0 52 mozilla::services::GetObserverService();
michael@0 53 obsServ->AddObserver(this, "xpcom-shutdown", false);
michael@0 54
michael@0 55 memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
michael@0 56 memset(mSafeWidgetStates, 0, sizeof(mSafeWidgetStates));
michael@0 57 }
michael@0 58
michael@0 59 nsNativeThemeGTK::~nsNativeThemeGTK() {
michael@0 60 }
michael@0 61
michael@0 62 NS_IMETHODIMP
michael@0 63 nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic,
michael@0 64 const char16_t *aData)
michael@0 65 {
michael@0 66 if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
michael@0 67 moz_gtk_shutdown();
michael@0 68 } else {
michael@0 69 NS_NOTREACHED("unexpected topic");
michael@0 70 return NS_ERROR_UNEXPECTED;
michael@0 71 }
michael@0 72
michael@0 73 return NS_OK;
michael@0 74 }
michael@0 75
michael@0 76 void
michael@0 77 nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame)
michael@0 78 {
michael@0 79 nsIPresShell *shell = GetPresShell(aFrame);
michael@0 80 if (!shell)
michael@0 81 return;
michael@0 82
michael@0 83 nsViewManager* vm = shell->GetViewManager();
michael@0 84 if (!vm)
michael@0 85 return;
michael@0 86
michael@0 87 vm->InvalidateAllViews();
michael@0 88 }
michael@0 89
michael@0 90 static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, uint32_t aNamespace)
michael@0 91 {
michael@0 92 nsIContent *content = aFrame ? aFrame->GetContent() : nullptr;
michael@0 93 if (!content)
michael@0 94 return false;
michael@0 95 return content->IsInNamespace(aNamespace);
michael@0 96 }
michael@0 97
michael@0 98 static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) {
michael@0 99 return (aDisabledVector[aWidgetType >> 3] & (1 << (aWidgetType & 7))) != 0;
michael@0 100 }
michael@0 101
michael@0 102 static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) {
michael@0 103 aDisabledVector[aWidgetType >> 3] |= (1 << (aWidgetType & 7));
michael@0 104 }
michael@0 105
michael@0 106 static inline uint16_t
michael@0 107 GetWidgetStateKey(uint8_t aWidgetType, GtkWidgetState *aWidgetState)
michael@0 108 {
michael@0 109 return (aWidgetState->active |
michael@0 110 aWidgetState->focused << 1 |
michael@0 111 aWidgetState->inHover << 2 |
michael@0 112 aWidgetState->disabled << 3 |
michael@0 113 aWidgetState->isDefault << 4 |
michael@0 114 aWidgetType << 5);
michael@0 115 }
michael@0 116
michael@0 117 static bool IsWidgetStateSafe(uint8_t* aSafeVector,
michael@0 118 uint8_t aWidgetType,
michael@0 119 GtkWidgetState *aWidgetState)
michael@0 120 {
michael@0 121 uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
michael@0 122 return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0;
michael@0 123 }
michael@0 124
michael@0 125 static void SetWidgetStateSafe(uint8_t *aSafeVector,
michael@0 126 uint8_t aWidgetType,
michael@0 127 GtkWidgetState *aWidgetState)
michael@0 128 {
michael@0 129 uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
michael@0 130 aSafeVector[key >> 3] |= (1 << (key & 7));
michael@0 131 }
michael@0 132
michael@0 133 static GtkTextDirection GetTextDirection(nsIFrame* aFrame)
michael@0 134 {
michael@0 135 if (!aFrame)
michael@0 136 return GTK_TEXT_DIR_NONE;
michael@0 137
michael@0 138 switch (aFrame->StyleVisibility()->mDirection) {
michael@0 139 case NS_STYLE_DIRECTION_RTL:
michael@0 140 return GTK_TEXT_DIR_RTL;
michael@0 141 case NS_STYLE_DIRECTION_LTR:
michael@0 142 return GTK_TEXT_DIR_LTR;
michael@0 143 }
michael@0 144
michael@0 145 return GTK_TEXT_DIR_NONE;
michael@0 146 }
michael@0 147
michael@0 148 // Returns positive for negative margins (otherwise 0).
michael@0 149 gint
michael@0 150 nsNativeThemeGTK::GetTabMarginPixels(nsIFrame* aFrame)
michael@0 151 {
michael@0 152 nscoord margin =
michael@0 153 IsBottomTab(aFrame) ? aFrame->GetUsedMargin().top
michael@0 154 : aFrame->GetUsedMargin().bottom;
michael@0 155
michael@0 156 return std::min<gint>(MOZ_GTK_TAB_MARGIN_MASK,
michael@0 157 std::max(0,
michael@0 158 aFrame->PresContext()->AppUnitsToDevPixels(-margin)));
michael@0 159 }
michael@0 160
michael@0 161 bool
michael@0 162 nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame,
michael@0 163 GtkThemeWidgetType& aGtkWidgetType,
michael@0 164 GtkWidgetState* aState,
michael@0 165 gint* aWidgetFlags)
michael@0 166 {
michael@0 167 if (aState) {
michael@0 168 if (!aFrame) {
michael@0 169 // reset the entire struct to zero
michael@0 170 memset(aState, 0, sizeof(GtkWidgetState));
michael@0 171 } else {
michael@0 172
michael@0 173 // For XUL checkboxes and radio buttons, the state of the parent
michael@0 174 // determines our state.
michael@0 175 nsIFrame *stateFrame = aFrame;
michael@0 176 if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX ||
michael@0 177 aWidgetType == NS_THEME_RADIO)) ||
michael@0 178 aWidgetType == NS_THEME_CHECKBOX_LABEL ||
michael@0 179 aWidgetType == NS_THEME_RADIO_LABEL)) {
michael@0 180
michael@0 181 nsIAtom* atom = nullptr;
michael@0 182 if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
michael@0 183 if (aWidgetType == NS_THEME_CHECKBOX_LABEL ||
michael@0 184 aWidgetType == NS_THEME_RADIO_LABEL) {
michael@0 185 // Adjust stateFrame so GetContentState finds the correct state.
michael@0 186 stateFrame = aFrame = aFrame->GetParent()->GetParent();
michael@0 187 } else {
michael@0 188 // GetContentState knows to look one frame up for radio/checkbox
michael@0 189 // widgets, so don't adjust stateFrame here.
michael@0 190 aFrame = aFrame->GetParent();
michael@0 191 }
michael@0 192 if (aWidgetFlags) {
michael@0 193 if (!atom) {
michael@0 194 atom = (aWidgetType == NS_THEME_CHECKBOX ||
michael@0 195 aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked
michael@0 196 : nsGkAtoms::selected;
michael@0 197 }
michael@0 198 *aWidgetFlags = CheckBooleanAttr(aFrame, atom);
michael@0 199 }
michael@0 200 } else {
michael@0 201 if (aWidgetFlags) {
michael@0 202 nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(aFrame->GetContent()));
michael@0 203 *aWidgetFlags = 0;
michael@0 204 if (inputElt) {
michael@0 205 bool isHTMLChecked;
michael@0 206 inputElt->GetChecked(&isHTMLChecked);
michael@0 207 if (isHTMLChecked)
michael@0 208 *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED;
michael@0 209 }
michael@0 210
michael@0 211 if (GetIndeterminate(aFrame))
michael@0 212 *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT;
michael@0 213 }
michael@0 214 }
michael@0 215 } else if (aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN ||
michael@0 216 aWidgetType == NS_THEME_TREEVIEW_HEADER_SORTARROW ||
michael@0 217 aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS ||
michael@0 218 aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
michael@0 219 aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
michael@0 220 aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) {
michael@0 221 // The state of an arrow comes from its parent.
michael@0 222 stateFrame = aFrame = aFrame->GetParent();
michael@0 223 }
michael@0 224
michael@0 225 EventStates eventState = GetContentState(stateFrame, aWidgetType);
michael@0 226
michael@0 227 aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame);
michael@0 228 aState->active = eventState.HasState(NS_EVENT_STATE_ACTIVE);
michael@0 229 aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS);
michael@0 230 aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER);
michael@0 231 aState->isDefault = IsDefaultButton(aFrame);
michael@0 232 aState->canDefault = FALSE; // XXX fix me
michael@0 233 aState->depressed = FALSE;
michael@0 234
michael@0 235 if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
michael@0 236 // For these widget types, some element (either a child or parent)
michael@0 237 // actually has element focus, so we check the focused attribute
michael@0 238 // to see whether to draw in the focused state.
michael@0 239 if (aWidgetType == NS_THEME_NUMBER_INPUT ||
michael@0 240 aWidgetType == NS_THEME_TEXTFIELD ||
michael@0 241 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
michael@0 242 aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD ||
michael@0 243 aWidgetType == NS_THEME_SPINNER_TEXTFIELD ||
michael@0 244 aWidgetType == NS_THEME_RADIO_CONTAINER ||
michael@0 245 aWidgetType == NS_THEME_RADIO_LABEL) {
michael@0 246 aState->focused = IsFocused(aFrame);
michael@0 247 } else if (aWidgetType == NS_THEME_RADIO ||
michael@0 248 aWidgetType == NS_THEME_CHECKBOX) {
michael@0 249 // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
michael@0 250 aState->focused = FALSE;
michael@0 251 }
michael@0 252
michael@0 253 if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL ||
michael@0 254 aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) {
michael@0 255 // for scrollbars we need to go up two to go from the thumb to
michael@0 256 // the slider to the actual scrollbar object
michael@0 257 nsIFrame *tmpFrame = aFrame->GetParent()->GetParent();
michael@0 258
michael@0 259 aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0);
michael@0 260 aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100);
michael@0 261 }
michael@0 262
michael@0 263 if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
michael@0 264 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
michael@0 265 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT ||
michael@0 266 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) {
michael@0 267 // set the state to disabled when the scrollbar is scrolled to
michael@0 268 // the beginning or the end, depending on the button type.
michael@0 269 int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
michael@0 270 int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
michael@0 271 if ((curpos == 0 && (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
michael@0 272 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT)) ||
michael@0 273 (curpos == maxpos &&
michael@0 274 (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
michael@0 275 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT)))
michael@0 276 aState->disabled = true;
michael@0 277
michael@0 278 // In order to simulate native GTK scrollbar click behavior,
michael@0 279 // we set the active attribute on the element to true if it's
michael@0 280 // pressed with any mouse button.
michael@0 281 // This allows us to show that it's active without setting :active
michael@0 282 else if (CheckBooleanAttr(aFrame, nsGkAtoms::active))
michael@0 283 aState->active = true;
michael@0 284
michael@0 285 if (aWidgetFlags) {
michael@0 286 *aWidgetFlags = GetScrollbarButtonType(aFrame);
michael@0 287 if (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP < 2)
michael@0 288 *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL;
michael@0 289 }
michael@0 290 }
michael@0 291
michael@0 292 // menu item state is determined by the attribute "_moz-menuactive",
michael@0 293 // and not by the mouse hovering (accessibility). as a special case,
michael@0 294 // menus which are children of a menu bar are only marked as prelight
michael@0 295 // if they are open, not on normal hover.
michael@0 296
michael@0 297 if (aWidgetType == NS_THEME_MENUITEM ||
michael@0 298 aWidgetType == NS_THEME_CHECKMENUITEM ||
michael@0 299 aWidgetType == NS_THEME_RADIOMENUITEM ||
michael@0 300 aWidgetType == NS_THEME_MENUSEPARATOR ||
michael@0 301 aWidgetType == NS_THEME_MENUARROW) {
michael@0 302 bool isTopLevel = false;
michael@0 303 nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
michael@0 304 if (menuFrame) {
michael@0 305 isTopLevel = menuFrame->IsOnMenuBar();
michael@0 306 }
michael@0 307
michael@0 308 if (isTopLevel) {
michael@0 309 aState->inHover = menuFrame->IsOpen();
michael@0 310 *aWidgetFlags |= MOZ_TOPLEVEL_MENU_ITEM;
michael@0 311 } else {
michael@0 312 aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
michael@0 313 *aWidgetFlags &= ~MOZ_TOPLEVEL_MENU_ITEM;
michael@0 314 }
michael@0 315
michael@0 316 aState->active = FALSE;
michael@0 317
michael@0 318 if (aWidgetType == NS_THEME_CHECKMENUITEM ||
michael@0 319 aWidgetType == NS_THEME_RADIOMENUITEM) {
michael@0 320 *aWidgetFlags = 0;
michael@0 321 if (aFrame && aFrame->GetContent()) {
michael@0 322 *aWidgetFlags = aFrame->GetContent()->
michael@0 323 AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
michael@0 324 nsGkAtoms::_true, eIgnoreCase);
michael@0 325 }
michael@0 326 }
michael@0 327 }
michael@0 328
michael@0 329 // A button with drop down menu open or an activated toggle button
michael@0 330 // should always appear depressed.
michael@0 331 if (aWidgetType == NS_THEME_BUTTON ||
michael@0 332 aWidgetType == NS_THEME_TOOLBAR_BUTTON ||
michael@0 333 aWidgetType == NS_THEME_TOOLBAR_DUAL_BUTTON ||
michael@0 334 aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN ||
michael@0 335 aWidgetType == NS_THEME_DROPDOWN ||
michael@0 336 aWidgetType == NS_THEME_DROPDOWN_BUTTON) {
michael@0 337 bool menuOpen = IsOpenButton(aFrame);
michael@0 338 aState->depressed = IsCheckedButton(aFrame) || menuOpen;
michael@0 339 // we must not highlight buttons with open drop down menus on hover.
michael@0 340 aState->inHover = aState->inHover && !menuOpen;
michael@0 341 }
michael@0 342
michael@0 343 // When the input field of the drop down button has focus, some themes
michael@0 344 // should draw focus for the drop down button as well.
michael@0 345 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON && aWidgetFlags) {
michael@0 346 *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
michael@0 347 }
michael@0 348 }
michael@0 349 }
michael@0 350 }
michael@0 351
michael@0 352 switch (aWidgetType) {
michael@0 353 case NS_THEME_BUTTON:
michael@0 354 case NS_THEME_TOOLBAR_BUTTON:
michael@0 355 case NS_THEME_TOOLBAR_DUAL_BUTTON:
michael@0 356 if (aWidgetFlags)
michael@0 357 *aWidgetFlags = (aWidgetType == NS_THEME_BUTTON) ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE;
michael@0 358 aGtkWidgetType = MOZ_GTK_BUTTON;
michael@0 359 break;
michael@0 360 case NS_THEME_CHECKBOX:
michael@0 361 case NS_THEME_RADIO:
michael@0 362 aGtkWidgetType = (aWidgetType == NS_THEME_RADIO) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON;
michael@0 363 break;
michael@0 364 case NS_THEME_SCROLLBAR_BUTTON_UP:
michael@0 365 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
michael@0 366 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
michael@0 367 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
michael@0 368 aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON;
michael@0 369 break;
michael@0 370 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
michael@0 371 aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_VERTICAL;
michael@0 372 break;
michael@0 373 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
michael@0 374 aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL;
michael@0 375 break;
michael@0 376 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
michael@0 377 aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
michael@0 378 break;
michael@0 379 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
michael@0 380 aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
michael@0 381 break;
michael@0 382 case NS_THEME_SPINNER:
michael@0 383 aGtkWidgetType = MOZ_GTK_SPINBUTTON;
michael@0 384 break;
michael@0 385 case NS_THEME_SPINNER_UP_BUTTON:
michael@0 386 aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP;
michael@0 387 break;
michael@0 388 case NS_THEME_SPINNER_DOWN_BUTTON:
michael@0 389 aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN;
michael@0 390 break;
michael@0 391 case NS_THEME_SPINNER_TEXTFIELD:
michael@0 392 aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY;
michael@0 393 break;
michael@0 394 case NS_THEME_RANGE:
michael@0 395 {
michael@0 396 if (IsRangeHorizontal(aFrame)) {
michael@0 397 if (aWidgetFlags)
michael@0 398 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
michael@0 399 aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
michael@0 400 } else {
michael@0 401 if (aWidgetFlags)
michael@0 402 *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
michael@0 403 aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
michael@0 404 }
michael@0 405 break;
michael@0 406 }
michael@0 407 case NS_THEME_RANGE_THUMB:
michael@0 408 {
michael@0 409 if (IsRangeHorizontal(aFrame)) {
michael@0 410 if (aWidgetFlags)
michael@0 411 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
michael@0 412 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
michael@0 413 } else {
michael@0 414 if (aWidgetFlags)
michael@0 415 *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
michael@0 416 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
michael@0 417 }
michael@0 418 break;
michael@0 419 }
michael@0 420 case NS_THEME_SCALE_HORIZONTAL:
michael@0 421 if (aWidgetFlags)
michael@0 422 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
michael@0 423 aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
michael@0 424 break;
michael@0 425 case NS_THEME_SCALE_THUMB_HORIZONTAL:
michael@0 426 if (aWidgetFlags)
michael@0 427 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
michael@0 428 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
michael@0 429 break;
michael@0 430 case NS_THEME_SCALE_VERTICAL:
michael@0 431 if (aWidgetFlags)
michael@0 432 *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
michael@0 433 aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
michael@0 434 break;
michael@0 435 case NS_THEME_TOOLBAR_SEPARATOR:
michael@0 436 aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR;
michael@0 437 break;
michael@0 438 case NS_THEME_SCALE_THUMB_VERTICAL:
michael@0 439 if (aWidgetFlags)
michael@0 440 *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
michael@0 441 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
michael@0 442 break;
michael@0 443 case NS_THEME_TOOLBAR_GRIPPER:
michael@0 444 aGtkWidgetType = MOZ_GTK_GRIPPER;
michael@0 445 break;
michael@0 446 case NS_THEME_RESIZER:
michael@0 447 aGtkWidgetType = MOZ_GTK_RESIZER;
michael@0 448 break;
michael@0 449 case NS_THEME_NUMBER_INPUT:
michael@0 450 case NS_THEME_TEXTFIELD:
michael@0 451 case NS_THEME_TEXTFIELD_MULTILINE:
michael@0 452 aGtkWidgetType = MOZ_GTK_ENTRY;
michael@0 453 break;
michael@0 454 case NS_THEME_LISTBOX:
michael@0 455 case NS_THEME_TREEVIEW:
michael@0 456 aGtkWidgetType = MOZ_GTK_TREEVIEW;
michael@0 457 break;
michael@0 458 case NS_THEME_TREEVIEW_HEADER_CELL:
michael@0 459 if (aWidgetFlags) {
michael@0 460 // In this case, the flag denotes whether the header is the sorted one or not
michael@0 461 if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural)
michael@0 462 *aWidgetFlags = false;
michael@0 463 else
michael@0 464 *aWidgetFlags = true;
michael@0 465 }
michael@0 466 aGtkWidgetType = MOZ_GTK_TREE_HEADER_CELL;
michael@0 467 break;
michael@0 468 case NS_THEME_TREEVIEW_HEADER_SORTARROW:
michael@0 469 if (aWidgetFlags) {
michael@0 470 switch (GetTreeSortDirection(aFrame)) {
michael@0 471 case eTreeSortDirection_Ascending:
michael@0 472 *aWidgetFlags = GTK_ARROW_DOWN;
michael@0 473 break;
michael@0 474 case eTreeSortDirection_Descending:
michael@0 475 *aWidgetFlags = GTK_ARROW_UP;
michael@0 476 break;
michael@0 477 case eTreeSortDirection_Natural:
michael@0 478 default:
michael@0 479 /* This prevents the treecolums from getting smaller
michael@0 480 * and wider when switching sort direction off and on
michael@0 481 * */
michael@0 482 *aWidgetFlags = GTK_ARROW_NONE;
michael@0 483 break;
michael@0 484 }
michael@0 485 }
michael@0 486 aGtkWidgetType = MOZ_GTK_TREE_HEADER_SORTARROW;
michael@0 487 break;
michael@0 488 case NS_THEME_TREEVIEW_TWISTY:
michael@0 489 aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
michael@0 490 if (aWidgetFlags)
michael@0 491 *aWidgetFlags = GTK_EXPANDER_COLLAPSED;
michael@0 492 break;
michael@0 493 case NS_THEME_TREEVIEW_TWISTY_OPEN:
michael@0 494 aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
michael@0 495 if (aWidgetFlags)
michael@0 496 *aWidgetFlags = GTK_EXPANDER_EXPANDED;
michael@0 497 break;
michael@0 498 case NS_THEME_DROPDOWN:
michael@0 499 aGtkWidgetType = MOZ_GTK_DROPDOWN;
michael@0 500 if (aWidgetFlags)
michael@0 501 *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
michael@0 502 break;
michael@0 503 case NS_THEME_DROPDOWN_TEXT:
michael@0 504 return false; // nothing to do, but prevents the bg from being drawn
michael@0 505 case NS_THEME_DROPDOWN_TEXTFIELD:
michael@0 506 aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY;
michael@0 507 break;
michael@0 508 case NS_THEME_DROPDOWN_BUTTON:
michael@0 509 aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW;
michael@0 510 break;
michael@0 511 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
michael@0 512 case NS_THEME_BUTTON_ARROW_DOWN:
michael@0 513 case NS_THEME_BUTTON_ARROW_UP:
michael@0 514 case NS_THEME_BUTTON_ARROW_NEXT:
michael@0 515 case NS_THEME_BUTTON_ARROW_PREVIOUS:
michael@0 516 aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
michael@0 517 if (aWidgetFlags) {
michael@0 518 *aWidgetFlags = GTK_ARROW_DOWN;
michael@0 519
michael@0 520 if (aWidgetType == NS_THEME_BUTTON_ARROW_UP)
michael@0 521 *aWidgetFlags = GTK_ARROW_UP;
michael@0 522 else if (aWidgetType == NS_THEME_BUTTON_ARROW_NEXT)
michael@0 523 *aWidgetFlags = GTK_ARROW_RIGHT;
michael@0 524 else if (aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
michael@0 525 *aWidgetFlags = GTK_ARROW_LEFT;
michael@0 526 }
michael@0 527 break;
michael@0 528 case NS_THEME_CHECKBOX_CONTAINER:
michael@0 529 aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER;
michael@0 530 break;
michael@0 531 case NS_THEME_RADIO_CONTAINER:
michael@0 532 aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER;
michael@0 533 break;
michael@0 534 case NS_THEME_CHECKBOX_LABEL:
michael@0 535 aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL;
michael@0 536 break;
michael@0 537 case NS_THEME_RADIO_LABEL:
michael@0 538 aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL;
michael@0 539 break;
michael@0 540 case NS_THEME_TOOLBAR:
michael@0 541 aGtkWidgetType = MOZ_GTK_TOOLBAR;
michael@0 542 break;
michael@0 543 case NS_THEME_TOOLTIP:
michael@0 544 aGtkWidgetType = MOZ_GTK_TOOLTIP;
michael@0 545 break;
michael@0 546 case NS_THEME_STATUSBAR_PANEL:
michael@0 547 case NS_THEME_STATUSBAR_RESIZER_PANEL:
michael@0 548 aGtkWidgetType = MOZ_GTK_FRAME;
michael@0 549 break;
michael@0 550 case NS_THEME_PROGRESSBAR:
michael@0 551 case NS_THEME_PROGRESSBAR_VERTICAL:
michael@0 552 aGtkWidgetType = MOZ_GTK_PROGRESSBAR;
michael@0 553 break;
michael@0 554 case NS_THEME_PROGRESSBAR_CHUNK:
michael@0 555 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
michael@0 556 {
michael@0 557 nsIFrame* stateFrame = aFrame->GetParent();
michael@0 558 EventStates eventStates = GetContentState(stateFrame, aWidgetType);
michael@0 559
michael@0 560 aGtkWidgetType = IsIndeterminateProgress(stateFrame, eventStates)
michael@0 561 ? (stateFrame->StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL)
michael@0 562 ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
michael@0 563 : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
michael@0 564 : MOZ_GTK_PROGRESS_CHUNK;
michael@0 565 }
michael@0 566 break;
michael@0 567 case NS_THEME_TAB_SCROLLARROW_BACK:
michael@0 568 case NS_THEME_TAB_SCROLLARROW_FORWARD:
michael@0 569 if (aWidgetFlags)
michael@0 570 *aWidgetFlags = aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK ?
michael@0 571 GTK_ARROW_LEFT : GTK_ARROW_RIGHT;
michael@0 572 aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW;
michael@0 573 break;
michael@0 574 case NS_THEME_TAB_PANELS:
michael@0 575 aGtkWidgetType = MOZ_GTK_TABPANELS;
michael@0 576 break;
michael@0 577 case NS_THEME_TAB:
michael@0 578 {
michael@0 579 if (aWidgetFlags) {
michael@0 580 /* First bits will be used to store max(0,-bmargin) where bmargin
michael@0 581 * is the bottom margin of the tab in pixels (resp. top margin,
michael@0 582 * for bottom tabs). */
michael@0 583 if (IsBottomTab(aFrame)) {
michael@0 584 *aWidgetFlags = MOZ_GTK_TAB_BOTTOM;
michael@0 585 } else {
michael@0 586 *aWidgetFlags = 0;
michael@0 587 }
michael@0 588
michael@0 589 *aWidgetFlags |= GetTabMarginPixels(aFrame);
michael@0 590
michael@0 591 if (IsSelectedTab(aFrame))
michael@0 592 *aWidgetFlags |= MOZ_GTK_TAB_SELECTED;
michael@0 593
michael@0 594 if (IsFirstTab(aFrame))
michael@0 595 *aWidgetFlags |= MOZ_GTK_TAB_FIRST;
michael@0 596 }
michael@0 597
michael@0 598 aGtkWidgetType = MOZ_GTK_TAB;
michael@0 599 }
michael@0 600 break;
michael@0 601 case NS_THEME_SPLITTER:
michael@0 602 if (IsHorizontal(aFrame))
michael@0 603 aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL;
michael@0 604 else
michael@0 605 aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL;
michael@0 606 break;
michael@0 607 case NS_THEME_MENUBAR:
michael@0 608 aGtkWidgetType = MOZ_GTK_MENUBAR;
michael@0 609 break;
michael@0 610 case NS_THEME_MENUPOPUP:
michael@0 611 aGtkWidgetType = MOZ_GTK_MENUPOPUP;
michael@0 612 break;
michael@0 613 case NS_THEME_MENUITEM:
michael@0 614 aGtkWidgetType = MOZ_GTK_MENUITEM;
michael@0 615 break;
michael@0 616 case NS_THEME_MENUSEPARATOR:
michael@0 617 aGtkWidgetType = MOZ_GTK_MENUSEPARATOR;
michael@0 618 break;
michael@0 619 case NS_THEME_MENUARROW:
michael@0 620 aGtkWidgetType = MOZ_GTK_MENUARROW;
michael@0 621 break;
michael@0 622 case NS_THEME_CHECKMENUITEM:
michael@0 623 aGtkWidgetType = MOZ_GTK_CHECKMENUITEM;
michael@0 624 break;
michael@0 625 case NS_THEME_RADIOMENUITEM:
michael@0 626 aGtkWidgetType = MOZ_GTK_RADIOMENUITEM;
michael@0 627 break;
michael@0 628 case NS_THEME_WINDOW:
michael@0 629 case NS_THEME_DIALOG:
michael@0 630 aGtkWidgetType = MOZ_GTK_WINDOW;
michael@0 631 break;
michael@0 632 default:
michael@0 633 return false;
michael@0 634 }
michael@0 635
michael@0 636 return true;
michael@0 637 }
michael@0 638
michael@0 639 #if (MOZ_WIDGET_GTK == 2)
michael@0 640 class ThemeRenderer : public gfxGdkNativeRenderer {
michael@0 641 public:
michael@0 642 ThemeRenderer(GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType,
michael@0 643 gint aFlags, GtkTextDirection aDirection,
michael@0 644 const GdkRectangle& aGDKRect, const GdkRectangle& aGDKClip)
michael@0 645 : mState(aState), mGTKWidgetType(aGTKWidgetType), mFlags(aFlags),
michael@0 646 mDirection(aDirection), mGDKRect(aGDKRect), mGDKClip(aGDKClip) {}
michael@0 647 nsresult DrawWithGDK(GdkDrawable * drawable, gint offsetX, gint offsetY,
michael@0 648 GdkRectangle * clipRects, uint32_t numClipRects);
michael@0 649 private:
michael@0 650 GtkWidgetState mState;
michael@0 651 GtkThemeWidgetType mGTKWidgetType;
michael@0 652 gint mFlags;
michael@0 653 GtkTextDirection mDirection;
michael@0 654 const GdkRectangle& mGDKRect;
michael@0 655 const GdkRectangle& mGDKClip;
michael@0 656 };
michael@0 657
michael@0 658 nsresult
michael@0 659 ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX,
michael@0 660 gint offsetY, GdkRectangle * clipRects, uint32_t numClipRects)
michael@0 661 {
michael@0 662 GdkRectangle gdk_rect = mGDKRect;
michael@0 663 gdk_rect.x += offsetX;
michael@0 664 gdk_rect.y += offsetY;
michael@0 665
michael@0 666 GdkRectangle gdk_clip = mGDKClip;
michael@0 667 gdk_clip.x += offsetX;
michael@0 668 gdk_clip.y += offsetY;
michael@0 669
michael@0 670 GdkRectangle surfaceRect;
michael@0 671 surfaceRect.x = 0;
michael@0 672 surfaceRect.y = 0;
michael@0 673 gdk_drawable_get_size(drawable, &surfaceRect.width, &surfaceRect.height);
michael@0 674 gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip);
michael@0 675
michael@0 676 NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
michael@0 677 moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip,
michael@0 678 &mState, mFlags, mDirection);
michael@0 679
michael@0 680 return NS_OK;
michael@0 681 }
michael@0 682 #endif
michael@0 683
michael@0 684 bool
michael@0 685 nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
michael@0 686 nsIntMargin* aExtra)
michael@0 687 {
michael@0 688 *aExtra = nsIntMargin(0,0,0,0);
michael@0 689 // Allow an extra one pixel above and below the thumb for certain
michael@0 690 // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
michael@0 691 // We modify the frame's overflow area. See bug 297508.
michael@0 692 switch (aWidgetType) {
michael@0 693 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
michael@0 694 aExtra->top = aExtra->bottom = 1;
michael@0 695 return true;
michael@0 696 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
michael@0 697 aExtra->left = aExtra->right = 1;
michael@0 698 return true;
michael@0 699
michael@0 700 // Include the indicator spacing (the padding around the control).
michael@0 701 case NS_THEME_CHECKBOX:
michael@0 702 case NS_THEME_RADIO:
michael@0 703 {
michael@0 704 gint indicator_size, indicator_spacing;
michael@0 705
michael@0 706 if (aWidgetType == NS_THEME_CHECKBOX) {
michael@0 707 moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
michael@0 708 } else {
michael@0 709 moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
michael@0 710 }
michael@0 711
michael@0 712 aExtra->top = indicator_spacing;
michael@0 713 aExtra->right = indicator_spacing;
michael@0 714 aExtra->bottom = indicator_spacing;
michael@0 715 aExtra->left = indicator_spacing;
michael@0 716 return true;
michael@0 717 }
michael@0 718 case NS_THEME_BUTTON :
michael@0 719 {
michael@0 720 if (IsDefaultButton(aFrame)) {
michael@0 721 // Some themes draw a default indicator outside the widget,
michael@0 722 // include that in overflow
michael@0 723 gint top, left, bottom, right;
michael@0 724 moz_gtk_button_get_default_overflow(&top, &left, &bottom, &right);
michael@0 725 aExtra->top = top;
michael@0 726 aExtra->right = right;
michael@0 727 aExtra->bottom = bottom;
michael@0 728 aExtra->left = left;
michael@0 729 return true;
michael@0 730 }
michael@0 731 }
michael@0 732 case NS_THEME_TAB :
michael@0 733 {
michael@0 734 if (!IsSelectedTab(aFrame))
michael@0 735 return false;
michael@0 736
michael@0 737 gint gap_height = moz_gtk_get_tab_thickness();
michael@0 738
michael@0 739 int32_t extra = gap_height - GetTabMarginPixels(aFrame);
michael@0 740 if (extra <= 0)
michael@0 741 return false;
michael@0 742
michael@0 743 if (IsBottomTab(aFrame)) {
michael@0 744 aExtra->top = extra;
michael@0 745 } else {
michael@0 746 aExtra->bottom = extra;
michael@0 747 }
michael@0 748 }
michael@0 749 default:
michael@0 750 return false;
michael@0 751 }
michael@0 752 }
michael@0 753
michael@0 754 NS_IMETHODIMP
michael@0 755 nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
michael@0 756 nsIFrame* aFrame,
michael@0 757 uint8_t aWidgetType,
michael@0 758 const nsRect& aRect,
michael@0 759 const nsRect& aDirtyRect)
michael@0 760 {
michael@0 761 GtkWidgetState state;
michael@0 762 GtkThemeWidgetType gtkWidgetType;
michael@0 763 GtkTextDirection direction = GetTextDirection(aFrame);
michael@0 764 gint flags;
michael@0 765 if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
michael@0 766 &flags))
michael@0 767 return NS_OK;
michael@0 768
michael@0 769 gfxContext* ctx = aContext->ThebesContext();
michael@0 770 nsPresContext *presContext = aFrame->PresContext();
michael@0 771
michael@0 772 gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
michael@0 773 gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
michael@0 774
michael@0 775 // Align to device pixels where sensible
michael@0 776 // to provide crisper and faster drawing.
michael@0 777 // Don't snap if it's a non-unit scale factor. We're going to have to take
michael@0 778 // slow paths then in any case.
michael@0 779 bool snapXY = ctx->UserToDevicePixelSnapped(rect);
michael@0 780 if (snapXY) {
michael@0 781 // Leave rect in device coords but make dirtyRect consistent.
michael@0 782 dirtyRect = ctx->UserToDevice(dirtyRect);
michael@0 783 }
michael@0 784
michael@0 785 // Translate the dirty rect so that it is wrt the widget top-left.
michael@0 786 dirtyRect.MoveBy(-rect.TopLeft());
michael@0 787 // Round out the dirty rect to gdk pixels to ensure that gtk draws
michael@0 788 // enough pixels for interpolation to device pixels.
michael@0 789 dirtyRect.RoundOut();
michael@0 790
michael@0 791 // GTK themes can only draw an integer number of pixels
michael@0 792 // (even when not snapped).
michael@0 793 nsIntRect widgetRect(0, 0, NS_lround(rect.Width()), NS_lround(rect.Height()));
michael@0 794 nsIntRect overflowRect(widgetRect);
michael@0 795 nsIntMargin extraSize;
michael@0 796 if (GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize)) {
michael@0 797 overflowRect.Inflate(extraSize);
michael@0 798 }
michael@0 799
michael@0 800 // This is the rectangle that will actually be drawn, in gdk pixels
michael@0 801 nsIntRect drawingRect(int32_t(dirtyRect.X()),
michael@0 802 int32_t(dirtyRect.Y()),
michael@0 803 int32_t(dirtyRect.Width()),
michael@0 804 int32_t(dirtyRect.Height()));
michael@0 805 if (widgetRect.IsEmpty()
michael@0 806 || !drawingRect.IntersectRect(overflowRect, drawingRect))
michael@0 807 return NS_OK;
michael@0 808
michael@0 809 // gdk rectangles are wrt the drawing rect.
michael@0 810
michael@0 811 GdkRectangle gdk_rect = {-drawingRect.x, -drawingRect.y,
michael@0 812 widgetRect.width, widgetRect.height};
michael@0 813
michael@0 814 // translate everything so (0,0) is the top left of the drawingRect
michael@0 815 gfxContextAutoSaveRestore autoSR(ctx);
michael@0 816 if (snapXY) {
michael@0 817 // Rects are in device coords.
michael@0 818 ctx->IdentityMatrix();
michael@0 819 }
michael@0 820 ctx->Translate(rect.TopLeft() + gfxPoint(drawingRect.x, drawingRect.y));
michael@0 821
michael@0 822 NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
michael@0 823 "Trying to render an unsafe widget!");
michael@0 824
michael@0 825 bool safeState = IsWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
michael@0 826 if (!safeState) {
michael@0 827 gLastGdkError = 0;
michael@0 828 gdk_error_trap_push ();
michael@0 829 }
michael@0 830
michael@0 831 #if (MOZ_WIDGET_GTK == 2)
michael@0 832 // The gdk_clip is just advisory here, meaning "you don't
michael@0 833 // need to draw outside this rect if you don't feel like it!"
michael@0 834 GdkRectangle gdk_clip = {0, 0, drawingRect.width, drawingRect.height};
michael@0 835
michael@0 836 ThemeRenderer renderer(state, gtkWidgetType, flags, direction,
michael@0 837 gdk_rect, gdk_clip);
michael@0 838
michael@0 839 // Some themes (e.g. Clearlooks) just don't clip properly to any
michael@0 840 // clip rect we provide, so we cannot advertise support for clipping within
michael@0 841 // the widget bounds.
michael@0 842 uint32_t rendererFlags = 0;
michael@0 843 if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) {
michael@0 844 rendererFlags |= gfxGdkNativeRenderer::DRAW_IS_OPAQUE;
michael@0 845 }
michael@0 846
michael@0 847 // GtkStyles (used by the widget drawing backend) are created for a
michael@0 848 // particular colormap/visual.
michael@0 849 GdkColormap* colormap = moz_gtk_widget_get_colormap();
michael@0 850
michael@0 851 renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap);
michael@0 852 #else
michael@0 853 moz_gtk_widget_paint(gtkWidgetType, ctx->GetCairo(), &gdk_rect,
michael@0 854 &state, flags, direction);
michael@0 855 #endif
michael@0 856
michael@0 857 if (!safeState) {
michael@0 858 gdk_flush();
michael@0 859 gLastGdkError = gdk_error_trap_pop ();
michael@0 860
michael@0 861 if (gLastGdkError) {
michael@0 862 #ifdef DEBUG
michael@0 863 printf("GTK theme failed for widget type %d, error was %d, state was "
michael@0 864 "[active=%d,focused=%d,inHover=%d,disabled=%d]\n",
michael@0 865 aWidgetType, gLastGdkError, state.active, state.focused,
michael@0 866 state.inHover, state.disabled);
michael@0 867 #endif
michael@0 868 NS_WARNING("GTK theme failed; disabling unsafe widget");
michael@0 869 SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType);
michael@0 870 // force refresh of the window, because the widget was not
michael@0 871 // successfully drawn it must be redrawn using the default look
michael@0 872 RefreshWidgetWindow(aFrame);
michael@0 873 } else {
michael@0 874 SetWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
michael@0 875 }
michael@0 876 }
michael@0 877
michael@0 878 // Indeterminate progress bar are animated.
michael@0 879 if (gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
michael@0 880 gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
michael@0 881 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
michael@0 882 NS_WARNING("unable to animate widget!");
michael@0 883 }
michael@0 884 }
michael@0 885
michael@0 886 return NS_OK;
michael@0 887 }
michael@0 888
michael@0 889 NS_IMETHODIMP
michael@0 890 nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
michael@0 891 uint8_t aWidgetType, nsIntMargin* aResult)
michael@0 892 {
michael@0 893 GtkTextDirection direction = GetTextDirection(aFrame);
michael@0 894 aResult->top = aResult->left = aResult->right = aResult->bottom = 0;
michael@0 895 switch (aWidgetType) {
michael@0 896 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
michael@0 897 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
michael@0 898 {
michael@0 899 MozGtkScrollbarMetrics metrics;
michael@0 900 moz_gtk_get_scrollbar_metrics(&metrics);
michael@0 901 aResult->top = aResult->left = aResult->right = aResult->bottom = metrics.trough_border;
michael@0 902 }
michael@0 903 break;
michael@0 904 case NS_THEME_TOOLBOX:
michael@0 905 // gtk has no toolbox equivalent. So, although we map toolbox to
michael@0 906 // gtk's 'toolbar' for purposes of painting the widget background,
michael@0 907 // we don't use the toolbar border for toolbox.
michael@0 908 break;
michael@0 909 case NS_THEME_TOOLBAR_DUAL_BUTTON:
michael@0 910 // TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw
michael@0 911 // around the entire button + dropdown, and also an inner border if you're
michael@0 912 // over the button part. But, we want the inner button to be right up
michael@0 913 // against the edge of the outer button so that the borders overlap.
michael@0 914 // To make this happen, we draw a button border for the outer button,
michael@0 915 // but don't reserve any space for it.
michael@0 916 break;
michael@0 917 case NS_THEME_TAB:
michael@0 918 // Top tabs have no bottom border, bottom tabs have no top border
michael@0 919 moz_gtk_get_widget_border(MOZ_GTK_TAB, &aResult->left, &aResult->top,
michael@0 920 &aResult->right, &aResult->bottom, direction,
michael@0 921 FALSE);
michael@0 922 if (IsBottomTab(aFrame))
michael@0 923 aResult->top = 0;
michael@0 924 else
michael@0 925 aResult->bottom = 0;
michael@0 926 break;
michael@0 927 case NS_THEME_MENUITEM:
michael@0 928 case NS_THEME_CHECKMENUITEM:
michael@0 929 case NS_THEME_RADIOMENUITEM:
michael@0 930 // For regular menuitems, we will be using GetWidgetPadding instead of
michael@0 931 // GetWidgetBorder to pad up the widget's internals; other menuitems
michael@0 932 // will need to fall through and use the default case as before.
michael@0 933 if (IsRegularMenuItem(aFrame))
michael@0 934 break;
michael@0 935 default:
michael@0 936 {
michael@0 937 GtkThemeWidgetType gtkWidgetType;
michael@0 938 if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
michael@0 939 nullptr)) {
michael@0 940 moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
michael@0 941 &aResult->right, &aResult->bottom, direction,
michael@0 942 IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML));
michael@0 943 }
michael@0 944 }
michael@0 945 }
michael@0 946 return NS_OK;
michael@0 947 }
michael@0 948
michael@0 949 bool
michael@0 950 nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
michael@0 951 nsIFrame* aFrame, uint8_t aWidgetType,
michael@0 952 nsIntMargin* aResult)
michael@0 953 {
michael@0 954 switch (aWidgetType) {
michael@0 955 case NS_THEME_BUTTON_FOCUS:
michael@0 956 case NS_THEME_TOOLBAR_BUTTON:
michael@0 957 case NS_THEME_TOOLBAR_DUAL_BUTTON:
michael@0 958 case NS_THEME_TAB_SCROLLARROW_BACK:
michael@0 959 case NS_THEME_TAB_SCROLLARROW_FORWARD:
michael@0 960 case NS_THEME_DROPDOWN_BUTTON:
michael@0 961 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
michael@0 962 case NS_THEME_BUTTON_ARROW_UP:
michael@0 963 case NS_THEME_BUTTON_ARROW_DOWN:
michael@0 964 case NS_THEME_BUTTON_ARROW_NEXT:
michael@0 965 case NS_THEME_BUTTON_ARROW_PREVIOUS:
michael@0 966 case NS_THEME_RANGE_THUMB:
michael@0 967 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
michael@0 968 // and have a meaningful baseline, so they can't have
michael@0 969 // author-specified padding.
michael@0 970 case NS_THEME_CHECKBOX:
michael@0 971 case NS_THEME_RADIO:
michael@0 972 aResult->SizeTo(0, 0, 0, 0);
michael@0 973 return true;
michael@0 974 case NS_THEME_MENUITEM:
michael@0 975 case NS_THEME_CHECKMENUITEM:
michael@0 976 case NS_THEME_RADIOMENUITEM:
michael@0 977 {
michael@0 978 // Menubar and menulist have their padding specified in CSS.
michael@0 979 if (!IsRegularMenuItem(aFrame))
michael@0 980 return false;
michael@0 981
michael@0 982 aResult->SizeTo(0, 0, 0, 0);
michael@0 983 GtkThemeWidgetType gtkWidgetType;
michael@0 984 if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
michael@0 985 nullptr)) {
michael@0 986 moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
michael@0 987 &aResult->right, &aResult->bottom, GetTextDirection(aFrame),
michael@0 988 IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML));
michael@0 989 }
michael@0 990
michael@0 991 gint horizontal_padding;
michael@0 992
michael@0 993 if (aWidgetType == NS_THEME_MENUITEM)
michael@0 994 moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
michael@0 995 else
michael@0 996 moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
michael@0 997
michael@0 998 aResult->left += horizontal_padding;
michael@0 999 aResult->right += horizontal_padding;
michael@0 1000
michael@0 1001 return true;
michael@0 1002 }
michael@0 1003 }
michael@0 1004
michael@0 1005 return false;
michael@0 1006 }
michael@0 1007
michael@0 1008 bool
michael@0 1009 nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext,
michael@0 1010 nsIFrame* aFrame, uint8_t aWidgetType,
michael@0 1011 nsRect* aOverflowRect)
michael@0 1012 {
michael@0 1013 nsIntMargin extraSize;
michael@0 1014 if (!GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize))
michael@0 1015 return false;
michael@0 1016
michael@0 1017 int32_t p2a = aContext->AppUnitsPerDevPixel();
michael@0 1018 nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a),
michael@0 1019 NSIntPixelsToAppUnits(extraSize.right, p2a),
michael@0 1020 NSIntPixelsToAppUnits(extraSize.bottom, p2a),
michael@0 1021 NSIntPixelsToAppUnits(extraSize.left, p2a));
michael@0 1022
michael@0 1023 aOverflowRect->Inflate(m);
michael@0 1024 return true;
michael@0 1025 }
michael@0 1026
michael@0 1027 NS_IMETHODIMP
michael@0 1028 nsNativeThemeGTK::GetMinimumWidgetSize(nsRenderingContext* aContext,
michael@0 1029 nsIFrame* aFrame, uint8_t aWidgetType,
michael@0 1030 nsIntSize* aResult, bool* aIsOverridable)
michael@0 1031 {
michael@0 1032 aResult->width = aResult->height = 0;
michael@0 1033 *aIsOverridable = true;
michael@0 1034
michael@0 1035 switch (aWidgetType) {
michael@0 1036 case NS_THEME_SCROLLBAR_BUTTON_UP:
michael@0 1037 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
michael@0 1038 {
michael@0 1039 MozGtkScrollbarMetrics metrics;
michael@0 1040 moz_gtk_get_scrollbar_metrics(&metrics);
michael@0 1041
michael@0 1042 aResult->width = metrics.slider_width;
michael@0 1043 aResult->height = metrics.stepper_size;
michael@0 1044 *aIsOverridable = false;
michael@0 1045 }
michael@0 1046 break;
michael@0 1047 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
michael@0 1048 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
michael@0 1049 {
michael@0 1050 MozGtkScrollbarMetrics metrics;
michael@0 1051 moz_gtk_get_scrollbar_metrics(&metrics);
michael@0 1052
michael@0 1053 aResult->width = metrics.stepper_size;
michael@0 1054 aResult->height = metrics.slider_width;
michael@0 1055 *aIsOverridable = false;
michael@0 1056 }
michael@0 1057 break;
michael@0 1058 case NS_THEME_SPLITTER:
michael@0 1059 {
michael@0 1060 gint metrics;
michael@0 1061 if (IsHorizontal(aFrame)) {
michael@0 1062 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
michael@0 1063 aResult->width = metrics;
michael@0 1064 aResult->height = 0;
michael@0 1065 } else {
michael@0 1066 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &metrics);
michael@0 1067 aResult->width = 0;
michael@0 1068 aResult->height = metrics;
michael@0 1069 }
michael@0 1070 *aIsOverridable = false;
michael@0 1071 }
michael@0 1072 break;
michael@0 1073 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
michael@0 1074 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
michael@0 1075 {
michael@0 1076 /* While we enforce a minimum size for the thumb, this is ignored
michael@0 1077 * for the some scrollbars if buttons are hidden (bug 513006) because
michael@0 1078 * the thumb isn't a direct child of the scrollbar, unlike the buttons
michael@0 1079 * or track. So add a minimum size to the track as well to prevent a
michael@0 1080 * 0-width scrollbar. */
michael@0 1081 MozGtkScrollbarMetrics metrics;
michael@0 1082 moz_gtk_get_scrollbar_metrics(&metrics);
michael@0 1083
michael@0 1084 if (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL)
michael@0 1085 aResult->width = metrics.slider_width + 2 * metrics.trough_border;
michael@0 1086 else
michael@0 1087 aResult->height = metrics.slider_width + 2 * metrics.trough_border;
michael@0 1088
michael@0 1089 *aIsOverridable = false;
michael@0 1090 }
michael@0 1091 break;
michael@0 1092 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
michael@0 1093 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
michael@0 1094 {
michael@0 1095 MozGtkScrollbarMetrics metrics;
michael@0 1096 moz_gtk_get_scrollbar_metrics(&metrics);
michael@0 1097
michael@0 1098 nsRect rect = aFrame->GetParent()->GetRect();
michael@0 1099 int32_t p2a = aFrame->PresContext()->DeviceContext()->
michael@0 1100 AppUnitsPerDevPixel();
michael@0 1101 nsMargin margin;
michael@0 1102
michael@0 1103 /* Get the available space, if that is smaller then the minimum size,
michael@0 1104 * adjust the mininum size to fit into it.
michael@0 1105 * Setting aIsOverridable to true has no effect for thumbs. */
michael@0 1106 aFrame->GetMargin(margin);
michael@0 1107 rect.Deflate(margin);
michael@0 1108 aFrame->GetParent()->GetBorderAndPadding(margin);
michael@0 1109 rect.Deflate(margin);
michael@0 1110
michael@0 1111 if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL) {
michael@0 1112 aResult->width = metrics.slider_width;
michael@0 1113 aResult->height = std::min(NSAppUnitsToIntPixels(rect.height, p2a),
michael@0 1114 metrics.min_slider_size);
michael@0 1115 } else {
michael@0 1116 aResult->height = metrics.slider_width;
michael@0 1117 aResult->width = std::min(NSAppUnitsToIntPixels(rect.width, p2a),
michael@0 1118 metrics.min_slider_size);
michael@0 1119 }
michael@0 1120
michael@0 1121 *aIsOverridable = false;
michael@0 1122 }
michael@0 1123 break;
michael@0 1124 case NS_THEME_RANGE_THUMB:
michael@0 1125 {
michael@0 1126 gint thumb_length, thumb_height;
michael@0 1127
michael@0 1128 if (IsRangeHorizontal(aFrame)) {
michael@0 1129 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
michael@0 1130 } else {
michael@0 1131 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_length);
michael@0 1132 }
michael@0 1133 aResult->width = thumb_length;
michael@0 1134 aResult->height = thumb_height;
michael@0 1135
michael@0 1136 *aIsOverridable = false;
michael@0 1137 }
michael@0 1138 break;
michael@0 1139 case NS_THEME_SCALE_THUMB_HORIZONTAL:
michael@0 1140 case NS_THEME_SCALE_THUMB_VERTICAL:
michael@0 1141 {
michael@0 1142 gint thumb_length, thumb_height;
michael@0 1143
michael@0 1144 if (aWidgetType == NS_THEME_SCALE_THUMB_VERTICAL) {
michael@0 1145 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_length, &thumb_height);
michael@0 1146 aResult->width = thumb_height;
michael@0 1147 aResult->height = thumb_length;
michael@0 1148 } else {
michael@0 1149 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
michael@0 1150 aResult->width = thumb_length;
michael@0 1151 aResult->height = thumb_height;
michael@0 1152 }
michael@0 1153
michael@0 1154 *aIsOverridable = false;
michael@0 1155 }
michael@0 1156 break;
michael@0 1157 case NS_THEME_TAB_SCROLLARROW_BACK:
michael@0 1158 case NS_THEME_TAB_SCROLLARROW_FORWARD:
michael@0 1159 {
michael@0 1160 moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height);
michael@0 1161 *aIsOverridable = false;
michael@0 1162 }
michael@0 1163 break;
michael@0 1164 case NS_THEME_DROPDOWN_BUTTON:
michael@0 1165 {
michael@0 1166 moz_gtk_get_combo_box_entry_button_size(&aResult->width,
michael@0 1167 &aResult->height);
michael@0 1168 *aIsOverridable = false;
michael@0 1169 }
michael@0 1170 break;
michael@0 1171 case NS_THEME_MENUSEPARATOR:
michael@0 1172 {
michael@0 1173 gint separator_height;
michael@0 1174
michael@0 1175 moz_gtk_get_menu_separator_height(&separator_height);
michael@0 1176 aResult->height = separator_height;
michael@0 1177
michael@0 1178 *aIsOverridable = false;
michael@0 1179 }
michael@0 1180 break;
michael@0 1181 case NS_THEME_CHECKBOX:
michael@0 1182 case NS_THEME_RADIO:
michael@0 1183 {
michael@0 1184 gint indicator_size, indicator_spacing;
michael@0 1185
michael@0 1186 if (aWidgetType == NS_THEME_CHECKBOX) {
michael@0 1187 moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
michael@0 1188 } else {
michael@0 1189 moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
michael@0 1190 }
michael@0 1191
michael@0 1192 // Include space for the indicator and the padding around it.
michael@0 1193 aResult->width = indicator_size;
michael@0 1194 aResult->height = indicator_size;
michael@0 1195 }
michael@0 1196 break;
michael@0 1197 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
michael@0 1198 case NS_THEME_BUTTON_ARROW_UP:
michael@0 1199 case NS_THEME_BUTTON_ARROW_DOWN:
michael@0 1200 case NS_THEME_BUTTON_ARROW_NEXT:
michael@0 1201 case NS_THEME_BUTTON_ARROW_PREVIOUS:
michael@0 1202 {
michael@0 1203 moz_gtk_get_arrow_size(&aResult->width, &aResult->height);
michael@0 1204 *aIsOverridable = false;
michael@0 1205 }
michael@0 1206 break;
michael@0 1207 case NS_THEME_CHECKBOX_CONTAINER:
michael@0 1208 case NS_THEME_RADIO_CONTAINER:
michael@0 1209 case NS_THEME_CHECKBOX_LABEL:
michael@0 1210 case NS_THEME_RADIO_LABEL:
michael@0 1211 case NS_THEME_BUTTON:
michael@0 1212 case NS_THEME_DROPDOWN:
michael@0 1213 case NS_THEME_TOOLBAR_BUTTON:
michael@0 1214 case NS_THEME_TREEVIEW_HEADER_CELL:
michael@0 1215 {
michael@0 1216 // Just include our border, and let the box code augment the size.
michael@0 1217 nsIntMargin border;
michael@0 1218 nsNativeThemeGTK::GetWidgetBorder(aContext->DeviceContext(),
michael@0 1219 aFrame, aWidgetType, &border);
michael@0 1220 aResult->width = border.left + border.right;
michael@0 1221 aResult->height = border.top + border.bottom;
michael@0 1222 }
michael@0 1223 break;
michael@0 1224 case NS_THEME_TOOLBAR_SEPARATOR:
michael@0 1225 {
michael@0 1226 gint separator_width;
michael@0 1227
michael@0 1228 moz_gtk_get_toolbar_separator_width(&separator_width);
michael@0 1229
michael@0 1230 aResult->width = separator_width;
michael@0 1231 }
michael@0 1232 break;
michael@0 1233 case NS_THEME_SPINNER:
michael@0 1234 // hard code these sizes
michael@0 1235 aResult->width = 14;
michael@0 1236 aResult->height = 26;
michael@0 1237 break;
michael@0 1238 case NS_THEME_TREEVIEW_HEADER_SORTARROW:
michael@0 1239 case NS_THEME_SPINNER_UP_BUTTON:
michael@0 1240 case NS_THEME_SPINNER_DOWN_BUTTON:
michael@0 1241 // hard code these sizes
michael@0 1242 aResult->width = 14;
michael@0 1243 aResult->height = 13;
michael@0 1244 break;
michael@0 1245 case NS_THEME_RESIZER:
michael@0 1246 // same as Windows to make our lives easier
michael@0 1247 aResult->width = aResult->height = 15;
michael@0 1248 *aIsOverridable = false;
michael@0 1249 break;
michael@0 1250 case NS_THEME_TREEVIEW_TWISTY:
michael@0 1251 case NS_THEME_TREEVIEW_TWISTY_OPEN:
michael@0 1252 {
michael@0 1253 gint expander_size;
michael@0 1254
michael@0 1255 moz_gtk_get_treeview_expander_size(&expander_size);
michael@0 1256 aResult->width = aResult->height = expander_size;
michael@0 1257 *aIsOverridable = false;
michael@0 1258 }
michael@0 1259 break;
michael@0 1260 }
michael@0 1261 return NS_OK;
michael@0 1262 }
michael@0 1263
michael@0 1264 NS_IMETHODIMP
michael@0 1265 nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
michael@0 1266 nsIAtom* aAttribute, bool* aShouldRepaint)
michael@0 1267 {
michael@0 1268 // Some widget types just never change state.
michael@0 1269 if (aWidgetType == NS_THEME_TOOLBOX ||
michael@0 1270 aWidgetType == NS_THEME_TOOLBAR ||
michael@0 1271 aWidgetType == NS_THEME_STATUSBAR ||
michael@0 1272 aWidgetType == NS_THEME_STATUSBAR_PANEL ||
michael@0 1273 aWidgetType == NS_THEME_STATUSBAR_RESIZER_PANEL ||
michael@0 1274 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
michael@0 1275 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
michael@0 1276 aWidgetType == NS_THEME_PROGRESSBAR ||
michael@0 1277 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
michael@0 1278 aWidgetType == NS_THEME_MENUBAR ||
michael@0 1279 aWidgetType == NS_THEME_MENUPOPUP ||
michael@0 1280 aWidgetType == NS_THEME_TOOLTIP ||
michael@0 1281 aWidgetType == NS_THEME_MENUSEPARATOR ||
michael@0 1282 aWidgetType == NS_THEME_WINDOW ||
michael@0 1283 aWidgetType == NS_THEME_DIALOG) {
michael@0 1284 *aShouldRepaint = false;
michael@0 1285 return NS_OK;
michael@0 1286 }
michael@0 1287
michael@0 1288 if ((aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
michael@0 1289 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
michael@0 1290 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT ||
michael@0 1291 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) &&
michael@0 1292 (aAttribute == nsGkAtoms::curpos ||
michael@0 1293 aAttribute == nsGkAtoms::maxpos)) {
michael@0 1294 *aShouldRepaint = true;
michael@0 1295 return NS_OK;
michael@0 1296 }
michael@0 1297
michael@0 1298 // XXXdwh Not sure what can really be done here. Can at least guess for
michael@0 1299 // specific widgets that they're highly unlikely to have certain states.
michael@0 1300 // For example, a toolbar doesn't care about any states.
michael@0 1301 if (!aAttribute) {
michael@0 1302 // Hover/focus/active changed. Always repaint.
michael@0 1303 *aShouldRepaint = true;
michael@0 1304 }
michael@0 1305 else {
michael@0 1306 // Check the attribute to see if it's relevant.
michael@0 1307 // disabled, checked, dlgtype, default, etc.
michael@0 1308 *aShouldRepaint = false;
michael@0 1309 if (aAttribute == nsGkAtoms::disabled ||
michael@0 1310 aAttribute == nsGkAtoms::checked ||
michael@0 1311 aAttribute == nsGkAtoms::selected ||
michael@0 1312 aAttribute == nsGkAtoms::focused ||
michael@0 1313 aAttribute == nsGkAtoms::readonly ||
michael@0 1314 aAttribute == nsGkAtoms::_default ||
michael@0 1315 aAttribute == nsGkAtoms::menuactive ||
michael@0 1316 aAttribute == nsGkAtoms::open ||
michael@0 1317 aAttribute == nsGkAtoms::parentfocused)
michael@0 1318 *aShouldRepaint = true;
michael@0 1319 }
michael@0 1320
michael@0 1321 return NS_OK;
michael@0 1322 }
michael@0 1323
michael@0 1324 NS_IMETHODIMP
michael@0 1325 nsNativeThemeGTK::ThemeChanged()
michael@0 1326 {
michael@0 1327 memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
michael@0 1328 return NS_OK;
michael@0 1329 }
michael@0 1330
michael@0 1331 NS_IMETHODIMP_(bool)
michael@0 1332 nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
michael@0 1333 nsIFrame* aFrame,
michael@0 1334 uint8_t aWidgetType)
michael@0 1335 {
michael@0 1336 if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType))
michael@0 1337 return false;
michael@0 1338
michael@0 1339 switch (aWidgetType) {
michael@0 1340 case NS_THEME_BUTTON:
michael@0 1341 case NS_THEME_BUTTON_FOCUS:
michael@0 1342 case NS_THEME_RADIO:
michael@0 1343 case NS_THEME_CHECKBOX:
michael@0 1344 case NS_THEME_TOOLBOX: // N/A
michael@0 1345 case NS_THEME_TOOLBAR:
michael@0 1346 case NS_THEME_TOOLBAR_BUTTON:
michael@0 1347 case NS_THEME_TOOLBAR_DUAL_BUTTON: // so we can override the border with 0
michael@0 1348 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
michael@0 1349 case NS_THEME_BUTTON_ARROW_UP:
michael@0 1350 case NS_THEME_BUTTON_ARROW_DOWN:
michael@0 1351 case NS_THEME_BUTTON_ARROW_NEXT:
michael@0 1352 case NS_THEME_BUTTON_ARROW_PREVIOUS:
michael@0 1353 case NS_THEME_TOOLBAR_SEPARATOR:
michael@0 1354 case NS_THEME_TOOLBAR_GRIPPER:
michael@0 1355 case NS_THEME_STATUSBAR:
michael@0 1356 case NS_THEME_STATUSBAR_PANEL:
michael@0 1357 case NS_THEME_STATUSBAR_RESIZER_PANEL:
michael@0 1358 case NS_THEME_RESIZER:
michael@0 1359 case NS_THEME_LISTBOX:
michael@0 1360 // case NS_THEME_LISTBOX_LISTITEM:
michael@0 1361 case NS_THEME_TREEVIEW:
michael@0 1362 // case NS_THEME_TREEVIEW_TREEITEM:
michael@0 1363 case NS_THEME_TREEVIEW_TWISTY:
michael@0 1364 // case NS_THEME_TREEVIEW_LINE:
michael@0 1365 // case NS_THEME_TREEVIEW_HEADER:
michael@0 1366 case NS_THEME_TREEVIEW_HEADER_CELL:
michael@0 1367 case NS_THEME_TREEVIEW_HEADER_SORTARROW:
michael@0 1368 case NS_THEME_TREEVIEW_TWISTY_OPEN:
michael@0 1369 case NS_THEME_PROGRESSBAR:
michael@0 1370 case NS_THEME_PROGRESSBAR_CHUNK:
michael@0 1371 case NS_THEME_PROGRESSBAR_VERTICAL:
michael@0 1372 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
michael@0 1373 case NS_THEME_TAB:
michael@0 1374 // case NS_THEME_TAB_PANEL:
michael@0 1375 case NS_THEME_TAB_PANELS:
michael@0 1376 case NS_THEME_TAB_SCROLLARROW_BACK:
michael@0 1377 case NS_THEME_TAB_SCROLLARROW_FORWARD:
michael@0 1378 case NS_THEME_TOOLTIP:
michael@0 1379 case NS_THEME_SPINNER:
michael@0 1380 case NS_THEME_SPINNER_UP_BUTTON:
michael@0 1381 case NS_THEME_SPINNER_DOWN_BUTTON:
michael@0 1382 case NS_THEME_SPINNER_TEXTFIELD:
michael@0 1383 // case NS_THEME_SCROLLBAR: (n/a for gtk)
michael@0 1384 // case NS_THEME_SCROLLBAR_SMALL: (n/a for gtk)
michael@0 1385 case NS_THEME_SCROLLBAR_BUTTON_UP:
michael@0 1386 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
michael@0 1387 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
michael@0 1388 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
michael@0 1389 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
michael@0 1390 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
michael@0 1391 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
michael@0 1392 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
michael@0 1393 case NS_THEME_NUMBER_INPUT:
michael@0 1394 case NS_THEME_TEXTFIELD:
michael@0 1395 case NS_THEME_TEXTFIELD_MULTILINE:
michael@0 1396 case NS_THEME_DROPDOWN_TEXTFIELD:
michael@0 1397 case NS_THEME_RANGE:
michael@0 1398 case NS_THEME_RANGE_THUMB:
michael@0 1399 case NS_THEME_SCALE_HORIZONTAL:
michael@0 1400 case NS_THEME_SCALE_THUMB_HORIZONTAL:
michael@0 1401 case NS_THEME_SCALE_VERTICAL:
michael@0 1402 case NS_THEME_SCALE_THUMB_VERTICAL:
michael@0 1403 // case NS_THEME_SCALE_THUMB_START:
michael@0 1404 // case NS_THEME_SCALE_THUMB_END:
michael@0 1405 // case NS_THEME_SCALE_TICK:
michael@0 1406 case NS_THEME_CHECKBOX_CONTAINER:
michael@0 1407 case NS_THEME_RADIO_CONTAINER:
michael@0 1408 case NS_THEME_CHECKBOX_LABEL:
michael@0 1409 case NS_THEME_RADIO_LABEL:
michael@0 1410 case NS_THEME_MENUBAR:
michael@0 1411 case NS_THEME_MENUPOPUP:
michael@0 1412 case NS_THEME_MENUITEM:
michael@0 1413 case NS_THEME_MENUARROW:
michael@0 1414 case NS_THEME_MENUSEPARATOR:
michael@0 1415 case NS_THEME_CHECKMENUITEM:
michael@0 1416 case NS_THEME_RADIOMENUITEM:
michael@0 1417 case NS_THEME_SPLITTER:
michael@0 1418 case NS_THEME_WINDOW:
michael@0 1419 case NS_THEME_DIALOG:
michael@0 1420 case NS_THEME_DROPDOWN:
michael@0 1421 case NS_THEME_DROPDOWN_TEXT:
michael@0 1422 return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
michael@0 1423
michael@0 1424 case NS_THEME_DROPDOWN_BUTTON:
michael@0 1425 // "Native" dropdown buttons cause padding and margin problems, but only
michael@0 1426 // in HTML so allow them in XUL.
michael@0 1427 return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
michael@0 1428 !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
michael@0 1429
michael@0 1430 }
michael@0 1431
michael@0 1432 return false;
michael@0 1433 }
michael@0 1434
michael@0 1435 NS_IMETHODIMP_(bool)
michael@0 1436 nsNativeThemeGTK::WidgetIsContainer(uint8_t aWidgetType)
michael@0 1437 {
michael@0 1438 // XXXdwh At some point flesh all of this out.
michael@0 1439 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON ||
michael@0 1440 aWidgetType == NS_THEME_RADIO ||
michael@0 1441 aWidgetType == NS_THEME_RANGE_THUMB ||
michael@0 1442 aWidgetType == NS_THEME_CHECKBOX ||
michael@0 1443 aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK ||
michael@0 1444 aWidgetType == NS_THEME_TAB_SCROLLARROW_FORWARD ||
michael@0 1445 aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
michael@0 1446 aWidgetType == NS_THEME_BUTTON_ARROW_DOWN ||
michael@0 1447 aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
michael@0 1448 aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
michael@0 1449 return false;
michael@0 1450 return true;
michael@0 1451 }
michael@0 1452
michael@0 1453 bool
michael@0 1454 nsNativeThemeGTK::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
michael@0 1455 {
michael@0 1456 if (aWidgetType == NS_THEME_DROPDOWN ||
michael@0 1457 aWidgetType == NS_THEME_BUTTON ||
michael@0 1458 aWidgetType == NS_THEME_TREEVIEW_HEADER_CELL)
michael@0 1459 return true;
michael@0 1460
michael@0 1461 return false;
michael@0 1462 }
michael@0 1463
michael@0 1464 bool
michael@0 1465 nsNativeThemeGTK::ThemeNeedsComboboxDropmarker()
michael@0 1466 {
michael@0 1467 return false;
michael@0 1468 }
michael@0 1469
michael@0 1470 nsITheme::Transparency
michael@0 1471 nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
michael@0 1472 {
michael@0 1473 switch (aWidgetType) {
michael@0 1474 // These widgets always draw a default background.
michael@0 1475 #if (MOZ_WIDGET_GTK == 2)
michael@0 1476 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
michael@0 1477 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
michael@0 1478 case NS_THEME_TOOLBAR:
michael@0 1479 case NS_THEME_MENUBAR:
michael@0 1480 #endif
michael@0 1481 case NS_THEME_MENUPOPUP:
michael@0 1482 case NS_THEME_WINDOW:
michael@0 1483 case NS_THEME_DIALOG:
michael@0 1484 // Tooltips use gtk_paint_flat_box().
michael@0 1485 case NS_THEME_TOOLTIP:
michael@0 1486 return eOpaque;
michael@0 1487 }
michael@0 1488
michael@0 1489 return eUnknownTransparency;
michael@0 1490 }

mercurial