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