Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsNativeThemeWin.h"
7 #include "mozilla/EventStates.h"
8 #include "mozilla/WindowsVersion.h"
9 #include "nsRenderingContext.h"
10 #include "nsRect.h"
11 #include "nsSize.h"
12 #include "nsTransform2D.h"
13 #include "nsThemeConstants.h"
14 #include "nsIPresShell.h"
15 #include "nsPresContext.h"
16 #include "nsIContent.h"
17 #include "nsIFrame.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsIDOMHTMLInputElement.h"
20 #include "nsLookAndFeel.h"
21 #include "nsMenuFrame.h"
22 #include "nsGkAtoms.h"
23 #include <malloc.h>
24 #include "nsWindow.h"
25 #include "nsIComboboxControlFrame.h"
26 #include "prinrval.h"
27 #include "WinUtils.h"
29 #include "gfxPlatform.h"
30 #include "gfxContext.h"
31 #include "gfxWindowsPlatform.h"
32 #include "gfxWindowsSurface.h"
33 #include "gfxWindowsNativeDrawing.h"
35 #include "nsUXThemeData.h"
36 #include "nsUXThemeConstants.h"
37 #include <algorithm>
39 using mozilla::IsVistaOrLater;
40 using namespace mozilla;
41 using namespace mozilla::widget;
43 #ifdef PR_LOGGING
44 extern PRLogModuleInfo* gWindowsLog;
45 #endif
47 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeWin, nsNativeTheme, nsITheme)
49 nsNativeThemeWin::nsNativeThemeWin() :
50 mProgressDeterminateTimeStamp(TimeStamp::Now()),
51 mProgressIndeterminateTimeStamp(TimeStamp::Now())
52 {
53 // If there is a relevant change in forms.css for windows platform,
54 // static widget style variables (e.g. sButtonBorderSize) should be
55 // reinitialized here.
56 }
58 nsNativeThemeWin::~nsNativeThemeWin()
59 {
60 nsUXThemeData::Invalidate();
61 }
63 static int32_t
64 GetTopLevelWindowActiveState(nsIFrame *aFrame)
65 {
66 // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
67 // until it finds a real window.
68 nsIWidget* widget = aFrame->GetNearestWidget();
69 nsWindowBase * window = static_cast<nsWindowBase*>(widget);
70 if (!window)
71 return mozilla::widget::themeconst::FS_INACTIVE;
72 if (widget && !window->IsTopLevelWidget() &&
73 !(window = window->GetParentWindowBase(false)))
74 return mozilla::widget::themeconst::FS_INACTIVE;
76 if (window->GetWindowHandle() == ::GetActiveWindow())
77 return mozilla::widget::themeconst::FS_ACTIVE;
78 return mozilla::widget::themeconst::FS_INACTIVE;
79 }
81 static int32_t
82 GetWindowFrameButtonState(nsIFrame* aFrame, EventStates eventState)
83 {
84 if (GetTopLevelWindowActiveState(aFrame) ==
85 mozilla::widget::themeconst::FS_INACTIVE) {
86 if (eventState.HasState(NS_EVENT_STATE_HOVER))
87 return mozilla::widget::themeconst::BS_HOT;
88 return mozilla::widget::themeconst::BS_INACTIVE;
89 }
91 if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
92 if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
93 return mozilla::widget::themeconst::BS_PUSHED;
94 return mozilla::widget::themeconst::BS_HOT;
95 }
96 return mozilla::widget::themeconst::BS_NORMAL;
97 }
99 static int32_t
100 GetClassicWindowFrameButtonState(EventStates eventState)
101 {
102 if (eventState.HasState(NS_EVENT_STATE_ACTIVE) &&
103 eventState.HasState(NS_EVENT_STATE_HOVER))
104 return DFCS_BUTTONPUSH|DFCS_PUSHED;
105 return DFCS_BUTTONPUSH;
106 }
108 static bool
109 IsTopLevelMenu(nsIFrame *aFrame)
110 {
111 bool isTopLevel(false);
112 nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
113 if (menuFrame) {
114 isTopLevel = menuFrame->IsOnMenuBar();
115 }
116 return isTopLevel;
117 }
119 static MARGINS
120 GetCheckboxMargins(HANDLE theme, HDC hdc)
121 {
122 MARGINS checkboxContent = {0};
123 GetThemeMargins(theme, hdc, MENU_POPUPCHECK, MCB_NORMAL,
124 TMT_CONTENTMARGINS, nullptr, &checkboxContent);
125 return checkboxContent;
126 }
128 static SIZE
129 GetCheckboxBGSize(HANDLE theme, HDC hdc)
130 {
131 SIZE checkboxSize;
132 GetThemePartSize(theme, hdc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL,
133 nullptr, TS_TRUE, &checkboxSize);
135 MARGINS checkboxMargins = GetCheckboxMargins(theme, hdc);
137 int leftMargin = checkboxMargins.cxLeftWidth;
138 int rightMargin = checkboxMargins.cxRightWidth;
139 int topMargin = checkboxMargins.cyTopHeight;
140 int bottomMargin = checkboxMargins.cyBottomHeight;
142 int width = leftMargin + checkboxSize.cx + rightMargin;
143 int height = topMargin + checkboxSize.cy + bottomMargin;
144 SIZE ret;
145 ret.cx = width;
146 ret.cy = height;
147 return ret;
148 }
150 static SIZE
151 GetCheckboxBGBounds(HANDLE theme, HDC hdc)
152 {
153 MARGINS checkboxBGSizing = {0};
154 MARGINS checkboxBGContent = {0};
155 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
156 TMT_SIZINGMARGINS, nullptr, &checkboxBGSizing);
157 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
158 TMT_CONTENTMARGINS, nullptr, &checkboxBGContent);
160 #define posdx(d) ((d) > 0 ? d : 0)
162 int dx = posdx(checkboxBGContent.cxRightWidth -
163 checkboxBGSizing.cxRightWidth) +
164 posdx(checkboxBGContent.cxLeftWidth -
165 checkboxBGSizing.cxLeftWidth);
166 int dy = posdx(checkboxBGContent.cyTopHeight -
167 checkboxBGSizing.cyTopHeight) +
168 posdx(checkboxBGContent.cyBottomHeight -
169 checkboxBGSizing.cyBottomHeight);
171 #undef posdx
173 SIZE ret(GetCheckboxBGSize(theme, hdc));
174 ret.cx += dx;
175 ret.cy += dy;
176 return ret;
177 }
179 static SIZE
180 GetGutterSize(HANDLE theme, HDC hdc)
181 {
182 SIZE gutterSize;
183 GetThemePartSize(theme, hdc, MENU_POPUPGUTTER, 0, nullptr, TS_TRUE, &gutterSize);
185 SIZE checkboxBGSize(GetCheckboxBGBounds(theme, hdc));
187 SIZE itemSize;
188 GetThemePartSize(theme, hdc, MENU_POPUPITEM, MPI_NORMAL, nullptr, TS_TRUE, &itemSize);
190 // Figure out how big the menuitem's icon will be (if present) at current DPI
191 double scaleFactor = nsIWidget::DefaultScaleOverride();
192 if (scaleFactor <= 0.0) {
193 scaleFactor = gfxWindowsPlatform::GetPlatform()->GetDPIScale();
194 }
195 int iconDevicePixels = NSToIntRound(16 * scaleFactor);
196 SIZE iconSize = {
197 iconDevicePixels, iconDevicePixels
198 };
199 // Not really sure what margins should be used here, but this seems to work in practice...
200 MARGINS margins = {0};
201 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
202 TMT_CONTENTMARGINS, nullptr, &margins);
203 iconSize.cx += margins.cxLeftWidth + margins.cxRightWidth;
204 iconSize.cy += margins.cyTopHeight + margins.cyBottomHeight;
206 int width = std::max(itemSize.cx, std::max(iconSize.cx, checkboxBGSize.cx) + gutterSize.cx);
207 int height = std::max(itemSize.cy, std::max(iconSize.cy, checkboxBGSize.cy));
209 SIZE ret;
210 ret.cx = width;
211 ret.cy = height;
212 return ret;
213 }
215 /* DrawThemeBGRTLAware - render a theme part based on rtl state.
216 * Some widgets are not direction-neutral and need to be drawn reversed for
217 * RTL. Windows provides a way to do this with SetLayout, but this reverses
218 * the entire drawing area of a given device context, which means that its
219 * use will also affect the positioning of the widget. There are two ways
220 * to work around this:
221 *
222 * Option 1: Alter the position of the rect that we send so that we cancel
223 * out the positioning effects of SetLayout
224 * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto
225 * that, and then transfer the results back to our DC
226 *
227 * This function tries to implement option 1, under the assumption that the
228 * correct way to reverse the effects of SetLayout is to translate the rect
229 * such that the offset from the DC bitmap's left edge to the old rect's
230 * left edge is equal to the offset from the DC bitmap's right edge to the
231 * new rect's right edge. In other words,
232 * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right)
233 */
234 static HRESULT
235 DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart, int aState,
236 const RECT *aWidgetRect, const RECT *aClipRect,
237 bool aIsRtl)
238 {
239 NS_ASSERTION(aTheme, "Bad theme handle.");
240 NS_ASSERTION(aHdc, "Bad hdc.");
241 NS_ASSERTION(aWidgetRect, "Bad rect.");
242 NS_ASSERTION(aClipRect, "Bad clip rect.");
244 if (!aIsRtl) {
245 return DrawThemeBackground(aTheme, aHdc, aPart, aState,
246 aWidgetRect, aClipRect);
247 }
249 HGDIOBJ hObj = GetCurrentObject(aHdc, OBJ_BITMAP);
250 BITMAP bitmap;
251 POINT vpOrg;
253 if (hObj && GetObject(hObj, sizeof(bitmap), &bitmap) &&
254 GetViewportOrgEx(aHdc, &vpOrg)) {
255 RECT newWRect(*aWidgetRect);
256 newWRect.left = bitmap.bmWidth - (aWidgetRect->right + 2*vpOrg.x);
257 newWRect.right = bitmap.bmWidth - (aWidgetRect->left + 2*vpOrg.x);
259 RECT newCRect;
260 RECT *newCRectPtr = nullptr;
262 if (aClipRect) {
263 newCRect.top = aClipRect->top;
264 newCRect.bottom = aClipRect->bottom;
265 newCRect.left = bitmap.bmWidth - (aClipRect->right + 2*vpOrg.x);
266 newCRect.right = bitmap.bmWidth - (aClipRect->left + 2*vpOrg.x);
267 newCRectPtr = &newCRect;
268 }
270 SetLayout(aHdc, LAYOUT_RTL);
271 HRESULT hr = DrawThemeBackground(aTheme, aHdc, aPart, aState, &newWRect,
272 newCRectPtr);
273 SetLayout(aHdc, 0);
274 if (SUCCEEDED(hr)) {
275 return hr;
276 }
277 }
278 return DrawThemeBackground(aTheme, aHdc, aPart, aState,
279 aWidgetRect, aClipRect);
280 }
282 /*
283 * Caption button padding data - 'hot' button padding.
284 * These areas are considered hot, in that they activate
285 * a button when hovered or clicked. The button graphic
286 * is drawn inside the padding border. Unrecognized themes
287 * are treated as their recognized counterparts for now.
288 * left top right bottom
289 * classic min 1 2 0 1
290 * classic max 0 2 1 1
291 * classic close 1 2 2 1
292 *
293 * aero basic min 1 2 0 2
294 * aero basic max 0 2 1 2
295 * aero basic close 1 2 1 2
296 *
297 * xp theme min 0 2 0 2
298 * xp theme max 0 2 1 2
299 * xp theme close 1 2 2 2
300 *
301 * 'cold' button padding - generic button padding, should
302 * be handled in css.
303 * left top right bottom
304 * classic min 0 0 0 0
305 * classic max 0 0 0 0
306 * classic close 0 0 0 0
307 *
308 * aero basic min 0 0 1 0
309 * aero basic max 1 0 0 0
310 * aero basic close 0 0 0 0
311 *
312 * xp theme min 0 0 1 0
313 * xp theme max 1 0 0 0
314 * xp theme close 0 0 0 0
315 */
317 enum CaptionDesktopTheme {
318 CAPTION_CLASSIC = 0,
319 CAPTION_BASIC,
320 CAPTION_XPTHEME,
321 };
323 enum CaptionButton {
324 CAPTIONBUTTON_MINIMIZE = 0,
325 CAPTIONBUTTON_RESTORE,
326 CAPTIONBUTTON_CLOSE,
327 };
329 struct CaptionButtonPadding {
330 RECT hotPadding[3];
331 };
333 // RECT: left, top, right, bottom
334 static CaptionButtonPadding buttonData[3] = {
335 {
336 { { 1, 2, 0, 1 }, { 0, 2, 1, 1 }, { 1, 2, 2, 1 } }
337 },
338 {
339 { { 1, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
340 },
341 {
342 { { 0, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
343 }
344 };
346 // Adds "hot" caption button padding to minimum widget size.
347 static void
348 AddPaddingRect(nsIntSize* aSize, CaptionButton button) {
349 if (!aSize)
350 return;
351 RECT offset;
352 if (!IsAppThemed())
353 offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
354 else if (!IsVistaOrLater())
355 offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
356 else
357 offset = buttonData[CAPTION_BASIC].hotPadding[button];
358 aSize->width += offset.left + offset.right;
359 aSize->height += offset.top + offset.bottom;
360 }
362 // If we've added padding to the minimum widget size, offset
363 // the area we draw into to compensate.
364 static void
365 OffsetBackgroundRect(RECT& rect, CaptionButton button) {
366 RECT offset;
367 if (!IsAppThemed())
368 offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
369 else if (!IsVistaOrLater())
370 offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
371 else
372 offset = buttonData[CAPTION_BASIC].hotPadding[button];
373 rect.left += offset.left;
374 rect.top += offset.top;
375 rect.right -= offset.right;
376 rect.bottom -= offset.bottom;
377 }
379 /*
380 * Notes on progress track and meter part constants:
381 * xp and up:
382 * PP_BAR(_VERT) - base progress track
383 * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if
384 * the underlying surface supports alpha. otherwise
385 * theme lib's DrawThemeBackground falls back on
386 * opaque PP_BAR. we currently don't use this.
387 * PP_CHUNK(_VERT) - xp progress meter. this does not draw an xp style
388 * progress w/chunks, it draws fill using the chunk
389 * graphic.
390 * vista and up:
391 * PP_FILL(_VERT) - progress meter. these have four states/colors.
392 * PP_PULSEOVERLAY(_VERT) - white reflection - an overlay, not sure what this
393 * is used for.
394 * PP_MOVEOVERLAY(_VERT) - green pulse - the pulse effect overlay on
395 * determined progress bars. we also use this for
396 * indeterminate chunk.
397 *
398 * Notes on state constants:
399 * PBBS_NORMAL - green progress
400 * PBBVS_PARTIAL/PBFVS_ERROR - red error progress
401 * PBFS_PAUSED - yellow paused progress
402 *
403 * There is no common controls style indeterminate part on vista and up.
404 */
406 /*
407 * Progress bar related constants. These values are found by experimenting and
408 * comparing against native widgets used by the system. They are very unlikely
409 * exact but try to not be too wrong.
410 */
411 // The amount of time we animate progress meters parts across the frame.
412 static const double kProgressDeterminateTimeSpan = 3.0;
413 static const double kProgressIndeterminateTimeSpan = 5.0;
414 // The width of the overlay used to animate the horizontal progress bar (Vista and later).
415 static const int32_t kProgressHorizontalVistaOverlaySize = 120;
416 // The width of the overlay used for the horizontal indeterminate progress bars on XP.
417 static const int32_t kProgressHorizontalXPOverlaySize = 55;
418 // The height of the overlay used to animate the vertical progress bar (Vista and later).
419 static const int32_t kProgressVerticalOverlaySize = 45;
420 // The height of the overlay used for the vertical indeterminate progress bar (Vista and later).
421 static const int32_t kProgressVerticalIndeterminateOverlaySize = 60;
422 // The width of the overlay used to animate the indeterminate progress bar (Windows Classic).
423 static const int32_t kProgressClassicOverlaySize = 40;
425 /*
426 * GetProgressOverlayStyle - returns the proper overlay part for themed
427 * progress bars based on os and orientation.
428 */
429 static int32_t
430 GetProgressOverlayStyle(bool aIsVertical)
431 {
432 if (aIsVertical) {
433 if (IsVistaOrLater()) {
434 return PP_MOVEOVERLAYVERT;
435 }
436 return PP_CHUNKVERT;
437 } else {
438 if (IsVistaOrLater()) {
439 return PP_MOVEOVERLAY;
440 }
441 return PP_CHUNK;
442 }
443 }
445 /*
446 * GetProgressOverlaySize - returns the minimum width or height for themed
447 * progress bar overlays. This includes the width of indeterminate chunks
448 * and vista pulse overlays.
449 */
450 static int32_t
451 GetProgressOverlaySize(bool aIsVertical, bool aIsIndeterminate)
452 {
453 if (IsVistaOrLater()) {
454 if (aIsVertical) {
455 return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
456 : kProgressVerticalOverlaySize;
457 }
458 return kProgressHorizontalVistaOverlaySize;
459 }
460 return kProgressHorizontalXPOverlaySize;
461 }
463 /*
464 * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based
465 * on a comparison of the current value and maximum.
466 */
467 static bool
468 IsProgressMeterFilled(nsIFrame* aFrame)
469 {
470 NS_ENSURE_TRUE(aFrame, false);
471 nsIFrame* parentFrame = aFrame->GetParent();
472 NS_ENSURE_TRUE(parentFrame, false);
473 return nsNativeTheme::GetProgressValue(parentFrame) ==
474 nsNativeTheme::GetProgressMaxValue(parentFrame);
475 }
477 /*
478 * CalculateProgressOverlayRect - returns the padded overlay animation rect
479 * used in rendering progress bars. Resulting rects are used in rendering
480 * vista+ pulse overlays and indeterminate progress meters. Graphics should
481 * be rendered at the origin.
482 */
483 RECT
484 nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame,
485 RECT* aWidgetRect,
486 bool aIsVertical,
487 bool aIsIndeterminate,
488 bool aIsClassic)
489 {
490 NS_ASSERTION(aFrame, "bad frame pointer");
491 NS_ASSERTION(aWidgetRect, "bad rect pointer");
493 int32_t frameSize = aIsVertical ? aWidgetRect->bottom - aWidgetRect->top
494 : aWidgetRect->right - aWidgetRect->left;
496 // Recycle a set of progress pulse timers - these timers control the position
497 // of all progress overlays and indeterminate chunks that get rendered.
498 double span = aIsIndeterminate ? kProgressIndeterminateTimeSpan
499 : kProgressDeterminateTimeSpan;
500 TimeDuration period;
501 if (!aIsIndeterminate) {
502 if (TimeStamp::Now() > (mProgressDeterminateTimeStamp +
503 TimeDuration::FromSeconds(span))) {
504 mProgressDeterminateTimeStamp = TimeStamp::Now();
505 }
506 period = TimeStamp::Now() - mProgressDeterminateTimeStamp;
507 } else {
508 if (TimeStamp::Now() > (mProgressIndeterminateTimeStamp +
509 TimeDuration::FromSeconds(span))) {
510 mProgressIndeterminateTimeStamp = TimeStamp::Now();
511 }
512 period = TimeStamp::Now() - mProgressIndeterminateTimeStamp;
513 }
515 double percent = period / TimeDuration::FromSeconds(span);
517 if (!aIsVertical && IsFrameRTL(aFrame))
518 percent = 1 - percent;
520 RECT overlayRect = *aWidgetRect;
521 int32_t overlaySize;
522 if (!aIsClassic) {
523 overlaySize = GetProgressOverlaySize(aIsVertical, aIsIndeterminate);
524 } else {
525 overlaySize = kProgressClassicOverlaySize;
526 }
528 // Calculate a bounds that is larger than the meters frame such that the
529 // overlay starts and ends completely off the edge of the frame:
530 // [overlay][frame][overlay]
531 // This also yields a nice delay on rotation. Use overlaySize as the minimum
532 // size for [overlay] based on the graphics dims. If [frame] is larger, use
533 // the frame size instead.
534 int trackWidth = frameSize > overlaySize ? frameSize : overlaySize;
535 if (!aIsVertical) {
536 int xPos = aWidgetRect->left - trackWidth;
537 xPos += (int)ceil(((double)(trackWidth*2) * percent));
538 overlayRect.left = xPos;
539 overlayRect.right = xPos + overlaySize;
540 } else {
541 int yPos = aWidgetRect->bottom + trackWidth;
542 yPos -= (int)ceil(((double)(trackWidth*2) * percent));
543 overlayRect.bottom = yPos;
544 overlayRect.top = yPos - overlaySize;
545 }
546 return overlayRect;
547 }
549 /*
550 * DrawChunkProgressMeter - renders an xp style chunked progress meter. Called
551 * by DrawProgressMeter.
552 *
553 * @param aTheme progress theme handle
554 * @param aHdc hdc returned by gfxWindowsNativeDrawing
555 * @param aPart the PP_X progress part
556 * @param aState the theme state
557 * @param aFrame the elements frame
558 * @param aWidgetRect bounding rect for the widget
559 * @param aClipRect dirty rect that needs drawing.
560 * @param aAppUnits app units per device pixel
561 * @param aIsIndeterm is an indeterminate progress?
562 * @param aIsVertical render a vertical progress?
563 * @param aIsRtl direction is rtl
564 */
565 static void
566 DrawChunkProgressMeter(HTHEME aTheme, HDC aHdc, int aPart,
567 int aState, nsIFrame* aFrame, RECT* aWidgetRect,
568 RECT* aClipRect, gfxFloat aAppUnits, bool aIsIndeterm,
569 bool aIsVertical, bool aIsRtl)
570 {
571 NS_ASSERTION(aTheme, "Bad theme.");
572 NS_ASSERTION(aHdc, "Bad hdc.");
573 NS_ASSERTION(aWidgetRect, "Bad rect.");
574 NS_ASSERTION(aClipRect, "Bad clip rect.");
575 NS_ASSERTION(aFrame, "Bad frame.");
577 // For horizontal meters, the theme lib paints the right graphic but doesn't
578 // paint the chunks, so we do that manually. For vertical meters, the theme
579 // library draws everything correctly.
580 if (aIsVertical) {
581 DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
582 return;
583 }
585 // query for the proper chunk metrics
586 int chunkSize, spaceSize;
587 if (FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
588 TMT_PROGRESSCHUNKSIZE, &chunkSize)) ||
589 FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
590 TMT_PROGRESSSPACESIZE, &spaceSize))) {
591 DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
592 return;
593 }
595 // render chunks
596 if (!aIsRtl || aIsIndeterm) {
597 for (int chunk = aWidgetRect->left; chunk <= aWidgetRect->right;
598 chunk += (chunkSize+spaceSize)) {
599 if (!aIsIndeterm && ((chunk + chunkSize) > aWidgetRect->right)) {
600 // aWidgetRect->right represents the end of the meter. Partial blocks
601 // don't get rendered with one exception, so exit here if we don't have
602 // a full chunk to draw.
603 // The above is true *except* when the meter is at 100% fill, in which
604 // case Windows renders any remaining partial block. Query the parent
605 // frame to find out if we're at 100%.
606 if (!IsProgressMeterFilled(aFrame)) {
607 break;
608 }
609 }
610 RECT bounds =
611 { chunk, aWidgetRect->top, chunk + chunkSize, aWidgetRect->bottom };
612 DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
613 }
614 } else {
615 // rtl needs to grow in the opposite direction to look right.
616 for (int chunk = aWidgetRect->right; chunk >= aWidgetRect->left;
617 chunk -= (chunkSize+spaceSize)) {
618 if ((chunk - chunkSize) < aWidgetRect->left) {
619 if (!IsProgressMeterFilled(aFrame)) {
620 break;
621 }
622 }
623 RECT bounds =
624 { chunk - chunkSize, aWidgetRect->top, chunk, aWidgetRect->bottom };
625 DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
626 }
627 }
628 }
630 /*
631 * DrawProgressMeter - render an appropriate progress meter based on progress
632 * meter style, orientation, and os. Note, this does not render the underlying
633 * progress track.
634 *
635 * @param aFrame the widget frame
636 * @param aWidgetType type of widget
637 * @param aTheme progress theme handle
638 * @param aHdc hdc returned by gfxWindowsNativeDrawing
639 * @param aPart the PP_X progress part
640 * @param aState the theme state
641 * @param aWidgetRect bounding rect for the widget
642 * @param aClipRect dirty rect that needs drawing.
643 * @param aAppUnits app units per device pixel
644 */
645 void
646 nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
647 HANDLE aTheme, HDC aHdc,
648 int aPart, int aState,
649 RECT* aWidgetRect, RECT* aClipRect,
650 gfxFloat aAppUnits)
651 {
652 if (!aFrame || !aTheme || !aHdc)
653 return;
655 NS_ASSERTION(aWidgetRect, "bad rect pointer");
656 NS_ASSERTION(aClipRect, "bad clip rect pointer");
658 RECT adjWidgetRect, adjClipRect;
659 adjWidgetRect = *aWidgetRect;
660 adjClipRect = *aClipRect;
661 if (!IsVistaOrLater()) {
662 // Adjust clipping out by one pixel. XP progress meters are inset,
663 // Vista+ are not.
664 InflateRect(&adjWidgetRect, 1, 1);
665 InflateRect(&adjClipRect, 1, 1);
666 }
668 nsIFrame* parentFrame = aFrame->GetParent();
669 if (!parentFrame) {
670 // We have no parent to work with, just bail.
671 NS_WARNING("No parent frame for progress rendering. Can't paint.");
672 return;
673 }
675 EventStates eventStates = GetContentState(parentFrame, aWidgetType);
676 bool vertical = IsVerticalProgress(parentFrame) ||
677 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL;
678 bool indeterminate = IsIndeterminateProgress(parentFrame, eventStates);
679 bool animate = indeterminate;
681 if (IsVistaOrLater()) {
682 // Vista and up progress meter is fill style, rendered here. We render
683 // the pulse overlay in the follow up section below.
684 DrawThemeBackground(aTheme, aHdc, aPart, aState,
685 &adjWidgetRect, &adjClipRect);
686 if (!IsProgressMeterFilled(aFrame)) {
687 animate = true;
688 }
689 } else if (!indeterminate) {
690 // XP progress meters are 'chunk' style.
691 DrawChunkProgressMeter(aTheme, aHdc, aPart, aState, aFrame,
692 &adjWidgetRect, &adjClipRect, aAppUnits,
693 indeterminate, vertical, IsFrameRTL(aFrame));
694 }
696 if (animate) {
697 // Indeterminate rendering
698 int32_t overlayPart = GetProgressOverlayStyle(vertical);
699 RECT overlayRect =
700 CalculateProgressOverlayRect(aFrame, &adjWidgetRect, vertical,
701 indeterminate, false);
702 if (IsVistaOrLater()) {
703 DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
704 &adjClipRect);
705 } else {
706 DrawChunkProgressMeter(aTheme, aHdc, overlayPart, aState, aFrame,
707 &overlayRect, &adjClipRect, aAppUnits,
708 indeterminate, vertical, IsFrameRTL(aFrame));
709 }
711 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
712 NS_WARNING("unable to animate progress widget!");
713 }
714 }
715 }
717 HANDLE
718 nsNativeThemeWin::GetTheme(uint8_t aWidgetType)
719 {
720 if (!IsVistaOrLater()) {
721 // On XP or earlier, render dropdowns as textfields;
722 // doing it the right way works fine with the MS themes,
723 // but breaks on a lot of custom themes (presumably because MS
724 // apps do the textfield border business as well).
725 if (aWidgetType == NS_THEME_DROPDOWN)
726 aWidgetType = NS_THEME_TEXTFIELD;
727 }
729 switch (aWidgetType) {
730 case NS_THEME_BUTTON:
731 case NS_THEME_RADIO:
732 case NS_THEME_CHECKBOX:
733 case NS_THEME_GROUPBOX:
734 return nsUXThemeData::GetTheme(eUXButton);
735 case NS_THEME_NUMBER_INPUT:
736 case NS_THEME_TEXTFIELD:
737 case NS_THEME_TEXTFIELD_MULTILINE:
738 return nsUXThemeData::GetTheme(eUXEdit);
739 case NS_THEME_TOOLTIP:
740 // XP/2K3 should force a classic treatment of tooltips
741 return !IsVistaOrLater() ?
742 nullptr : nsUXThemeData::GetTheme(eUXTooltip);
743 case NS_THEME_TOOLBOX:
744 return nsUXThemeData::GetTheme(eUXRebar);
745 case NS_THEME_WIN_MEDIA_TOOLBOX:
746 return nsUXThemeData::GetTheme(eUXMediaRebar);
747 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
748 return nsUXThemeData::GetTheme(eUXCommunicationsRebar);
749 case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
750 return nsUXThemeData::GetTheme(eUXBrowserTabBarRebar);
751 case NS_THEME_TOOLBAR:
752 case NS_THEME_TOOLBAR_BUTTON:
753 case NS_THEME_TOOLBAR_SEPARATOR:
754 return nsUXThemeData::GetTheme(eUXToolbar);
755 case NS_THEME_PROGRESSBAR:
756 case NS_THEME_PROGRESSBAR_VERTICAL:
757 case NS_THEME_PROGRESSBAR_CHUNK:
758 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
759 return nsUXThemeData::GetTheme(eUXProgress);
760 case NS_THEME_TAB:
761 case NS_THEME_TAB_PANEL:
762 case NS_THEME_TAB_PANELS:
763 return nsUXThemeData::GetTheme(eUXTab);
764 case NS_THEME_SCROLLBAR:
765 case NS_THEME_SCROLLBAR_SMALL:
766 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
767 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
768 case NS_THEME_SCROLLBAR_BUTTON_UP:
769 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
770 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
771 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
772 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
773 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
774 return nsUXThemeData::GetTheme(eUXScrollbar);
775 case NS_THEME_RANGE:
776 case NS_THEME_RANGE_THUMB:
777 case NS_THEME_SCALE_HORIZONTAL:
778 case NS_THEME_SCALE_VERTICAL:
779 case NS_THEME_SCALE_THUMB_HORIZONTAL:
780 case NS_THEME_SCALE_THUMB_VERTICAL:
781 return nsUXThemeData::GetTheme(eUXTrackbar);
782 case NS_THEME_SPINNER_UP_BUTTON:
783 case NS_THEME_SPINNER_DOWN_BUTTON:
784 return nsUXThemeData::GetTheme(eUXSpin);
785 case NS_THEME_STATUSBAR:
786 case NS_THEME_STATUSBAR_PANEL:
787 case NS_THEME_STATUSBAR_RESIZER_PANEL:
788 case NS_THEME_RESIZER:
789 return nsUXThemeData::GetTheme(eUXStatus);
790 case NS_THEME_DROPDOWN:
791 case NS_THEME_DROPDOWN_BUTTON:
792 return nsUXThemeData::GetTheme(eUXCombobox);
793 case NS_THEME_TREEVIEW_HEADER_CELL:
794 case NS_THEME_TREEVIEW_HEADER_SORTARROW:
795 return nsUXThemeData::GetTheme(eUXHeader);
796 case NS_THEME_LISTBOX:
797 case NS_THEME_LISTBOX_LISTITEM:
798 case NS_THEME_TREEVIEW:
799 case NS_THEME_TREEVIEW_TWISTY_OPEN:
800 case NS_THEME_TREEVIEW_TREEITEM:
801 return nsUXThemeData::GetTheme(eUXListview);
802 case NS_THEME_MENUBAR:
803 case NS_THEME_MENUPOPUP:
804 case NS_THEME_MENUITEM:
805 case NS_THEME_CHECKMENUITEM:
806 case NS_THEME_RADIOMENUITEM:
807 case NS_THEME_MENUCHECKBOX:
808 case NS_THEME_MENURADIO:
809 case NS_THEME_MENUSEPARATOR:
810 case NS_THEME_MENUARROW:
811 case NS_THEME_MENUIMAGE:
812 case NS_THEME_MENUITEMTEXT:
813 return nsUXThemeData::GetTheme(eUXMenu);
814 case NS_THEME_WINDOW_TITLEBAR:
815 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
816 case NS_THEME_WINDOW_FRAME_LEFT:
817 case NS_THEME_WINDOW_FRAME_RIGHT:
818 case NS_THEME_WINDOW_FRAME_BOTTOM:
819 case NS_THEME_WINDOW_BUTTON_CLOSE:
820 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
821 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
822 case NS_THEME_WINDOW_BUTTON_RESTORE:
823 case NS_THEME_WINDOW_BUTTON_BOX:
824 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
825 case NS_THEME_WIN_GLASS:
826 case NS_THEME_WIN_BORDERLESS_GLASS:
827 return nsUXThemeData::GetTheme(eUXWindowFrame);
828 }
829 return nullptr;
830 }
832 int32_t
833 nsNativeThemeWin::StandardGetState(nsIFrame* aFrame, uint8_t aWidgetType,
834 bool wantFocused)
835 {
836 EventStates eventState = GetContentState(aFrame, aWidgetType);
837 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
838 return TS_ACTIVE;
839 if (eventState.HasState(NS_EVENT_STATE_HOVER))
840 return TS_HOVER;
841 if (wantFocused && eventState.HasState(NS_EVENT_STATE_FOCUS))
842 return TS_FOCUSED;
844 return TS_NORMAL;
845 }
847 bool
848 nsNativeThemeWin::IsMenuActive(nsIFrame *aFrame, uint8_t aWidgetType)
849 {
850 nsIContent* content = aFrame->GetContent();
851 if (content->IsXUL() &&
852 content->NodeInfo()->Equals(nsGkAtoms::richlistitem))
853 return CheckBooleanAttr(aFrame, nsGkAtoms::selected);
855 return CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
856 }
858 /**
859 * aPart is filled in with the UXTheme part code. On return, values > 0
860 * are the actual UXTheme part code; -1 means the widget will be drawn by
861 * us; 0 means that we should use part code 0, which isn't a real part code
862 * but elicits some kind of default behaviour from UXTheme when drawing
863 * (but isThemeBackgroundPartiallyTransparent may not work).
864 */
865 nsresult
866 nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
867 int32_t& aPart, int32_t& aState)
868 {
869 if (!IsVistaOrLater()) {
870 // See GetTheme
871 if (aWidgetType == NS_THEME_DROPDOWN)
872 aWidgetType = NS_THEME_TEXTFIELD;
873 }
875 switch (aWidgetType) {
876 case NS_THEME_BUTTON: {
877 aPart = BP_BUTTON;
878 if (!aFrame) {
879 aState = TS_NORMAL;
880 return NS_OK;
881 }
883 EventStates eventState = GetContentState(aFrame, aWidgetType);
884 if (IsDisabled(aFrame, eventState)) {
885 aState = TS_DISABLED;
886 return NS_OK;
887 } else if (IsOpenButton(aFrame) ||
888 IsCheckedButton(aFrame)) {
889 aState = TS_ACTIVE;
890 return NS_OK;
891 }
893 aState = StandardGetState(aFrame, aWidgetType, true);
895 // Check for default dialog buttons. These buttons should always look
896 // focused.
897 if (aState == TS_NORMAL && IsDefaultButton(aFrame))
898 aState = TS_FOCUSED;
899 return NS_OK;
900 }
901 case NS_THEME_CHECKBOX:
902 case NS_THEME_RADIO: {
903 bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
904 aPart = isCheckbox ? BP_CHECKBOX : BP_RADIO;
906 enum InputState {
907 UNCHECKED = 0, CHECKED, INDETERMINATE
908 };
909 InputState inputState = UNCHECKED;
910 bool isXULCheckboxRadio = false;
912 if (!aFrame) {
913 aState = TS_NORMAL;
914 } else {
915 if (GetCheckedOrSelected(aFrame, !isCheckbox)) {
916 inputState = CHECKED;
917 } if (isCheckbox && GetIndeterminate(aFrame)) {
918 inputState = INDETERMINATE;
919 }
921 EventStates eventState =
922 GetContentState(isXULCheckboxRadio ? aFrame->GetParent() : aFrame,
923 aWidgetType);
924 if (IsDisabled(aFrame, eventState)) {
925 aState = TS_DISABLED;
926 } else {
927 aState = StandardGetState(aFrame, aWidgetType, false);
928 }
929 }
931 // 4 unchecked states, 4 checked states, 4 indeterminate states.
932 aState += inputState * 4;
933 return NS_OK;
934 }
935 case NS_THEME_GROUPBOX: {
936 aPart = BP_GROUPBOX;
937 aState = TS_NORMAL;
938 // Since we don't support groupbox disabled and GBS_DISABLED looks the
939 // same as GBS_NORMAL don't bother supporting GBS_DISABLED.
940 return NS_OK;
941 }
942 case NS_THEME_NUMBER_INPUT:
943 case NS_THEME_TEXTFIELD:
944 case NS_THEME_TEXTFIELD_MULTILINE: {
945 EventStates eventState = GetContentState(aFrame, aWidgetType);
947 if (IsVistaOrLater()) {
948 /* Note: the NOSCROLL type has a rounded corner in each
949 * corner. The more specific HSCROLL, VSCROLL, HVSCROLL types
950 * have side and/or top/bottom edges rendered as straight
951 * horizontal lines with sharp corners to accommodate a
952 * scrollbar. However, the scrollbar gets rendered on top of
953 * this for us, so we don't care, and can just use NOSCROLL
954 * here.
955 */
956 aPart = TFP_EDITBORDER_NOSCROLL;
958 if (!aFrame) {
959 aState = TFS_EDITBORDER_NORMAL;
960 } else if (IsDisabled(aFrame, eventState)) {
961 aState = TFS_EDITBORDER_DISABLED;
962 } else if (IsReadOnly(aFrame)) {
963 /* no special read-only state */
964 aState = TFS_EDITBORDER_NORMAL;
965 } else {
966 nsIContent* content = aFrame->GetContent();
968 /* XUL textboxes don't get focused themselves, because they have child
969 * html:input.. but we can check the XUL focused attributes on them
970 */
971 if (content && content->IsXUL() && IsFocused(aFrame))
972 aState = TFS_EDITBORDER_FOCUSED;
973 else if (eventState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS))
974 aState = TFS_EDITBORDER_FOCUSED;
975 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
976 aState = TFS_EDITBORDER_HOVER;
977 else
978 aState = TFS_EDITBORDER_NORMAL;
979 }
980 } else {
981 aPart = TFP_TEXTFIELD;
983 if (!aFrame)
984 aState = TS_NORMAL;
985 else if (IsDisabled(aFrame, eventState))
986 aState = TS_DISABLED;
987 else if (IsReadOnly(aFrame))
988 aState = TFS_READONLY;
989 else
990 aState = StandardGetState(aFrame, aWidgetType, true);
991 }
993 return NS_OK;
994 }
995 case NS_THEME_TOOLTIP: {
996 aPart = TTP_STANDARD;
997 aState = TS_NORMAL;
998 return NS_OK;
999 }
1000 case NS_THEME_PROGRESSBAR:
1001 case NS_THEME_PROGRESSBAR_VERTICAL: {
1002 // Note IsVerticalProgress only tests for orient css attrribute,
1003 // NS_THEME_PROGRESSBAR_VERTICAL is dedicated to -moz-appearance:
1004 // progressbar-vertical.
1005 bool vertical = IsVerticalProgress(aFrame) ||
1006 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL;
1007 aPart = vertical ? PP_BARVERT : PP_BAR;
1008 aState = PBBS_NORMAL;
1009 return NS_OK;
1010 }
1011 case NS_THEME_PROGRESSBAR_CHUNK:
1012 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: {
1013 nsIFrame* parentFrame = aFrame->GetParent();
1014 EventStates eventStates = GetContentState(parentFrame, aWidgetType);
1015 if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
1016 IsVerticalProgress(parentFrame)) {
1017 aPart = IsVistaOrLater() ?
1018 PP_FILLVERT : PP_CHUNKVERT;
1019 } else {
1020 aPart = IsVistaOrLater() ?
1021 PP_FILL : PP_CHUNK;
1022 }
1024 aState = PBBVS_NORMAL;
1025 return NS_OK;
1026 }
1027 case NS_THEME_TOOLBAR_BUTTON: {
1028 aPart = BP_BUTTON;
1029 if (!aFrame) {
1030 aState = TS_NORMAL;
1031 return NS_OK;
1032 }
1034 EventStates eventState = GetContentState(aFrame, aWidgetType);
1035 if (IsDisabled(aFrame, eventState)) {
1036 aState = TS_DISABLED;
1037 return NS_OK;
1038 }
1039 if (IsOpenButton(aFrame)) {
1040 aState = TS_ACTIVE;
1041 return NS_OK;
1042 }
1044 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
1045 aState = TS_ACTIVE;
1046 else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
1047 if (IsCheckedButton(aFrame))
1048 aState = TB_HOVER_CHECKED;
1049 else
1050 aState = TS_HOVER;
1051 }
1052 else {
1053 if (IsCheckedButton(aFrame))
1054 aState = TB_CHECKED;
1055 else
1056 aState = TS_NORMAL;
1057 }
1059 return NS_OK;
1060 }
1061 case NS_THEME_TOOLBAR_SEPARATOR: {
1062 aPart = TP_SEPARATOR;
1063 aState = TS_NORMAL;
1064 return NS_OK;
1065 }
1066 case NS_THEME_SCROLLBAR_BUTTON_UP:
1067 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
1068 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
1069 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: {
1070 aPart = SP_BUTTON;
1071 aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP)*4;
1072 EventStates eventState = GetContentState(aFrame, aWidgetType);
1073 if (!aFrame)
1074 aState += TS_NORMAL;
1075 else if (IsDisabled(aFrame, eventState))
1076 aState += TS_DISABLED;
1077 else {
1078 nsIFrame *parent = aFrame->GetParent();
1079 EventStates parentState =
1080 GetContentState(parent, parent->StyleDisplay()->mAppearance);
1081 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
1082 aState += TS_ACTIVE;
1083 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1084 aState += TS_HOVER;
1085 else if (IsVistaOrLater() &&
1086 parentState.HasState(NS_EVENT_STATE_HOVER))
1087 aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP) + SP_BUTTON_IMPLICIT_HOVER_BASE;
1088 else
1089 aState += TS_NORMAL;
1090 }
1091 return NS_OK;
1092 }
1093 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
1094 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: {
1095 aPart = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL) ?
1096 SP_TRACKSTARTHOR : SP_TRACKSTARTVERT;
1097 aState = TS_NORMAL;
1098 return NS_OK;
1099 }
1100 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
1101 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: {
1102 aPart = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) ?
1103 SP_THUMBHOR : SP_THUMBVERT;
1104 EventStates eventState = GetContentState(aFrame, aWidgetType);
1105 if (!aFrame)
1106 aState = TS_NORMAL;
1107 else if (IsDisabled(aFrame, eventState))
1108 aState = TS_DISABLED;
1109 else {
1110 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a requirement for
1111 // the thumb, since the drag is not canceled
1112 // when you move outside the thumb.
1113 aState = TS_ACTIVE;
1114 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1115 aState = TS_HOVER;
1116 else
1117 aState = TS_NORMAL;
1118 }
1119 return NS_OK;
1120 }
1121 case NS_THEME_RANGE:
1122 case NS_THEME_SCALE_HORIZONTAL:
1123 case NS_THEME_SCALE_VERTICAL: {
1124 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1125 (aWidgetType == NS_THEME_RANGE &&
1126 IsRangeHorizontal(aFrame))) {
1127 aPart = TKP_TRACK;
1128 aState = TRS_NORMAL;
1129 } else {
1130 aPart = TKP_TRACKVERT;
1131 aState = TRVS_NORMAL;
1132 }
1133 return NS_OK;
1134 }
1135 case NS_THEME_RANGE_THUMB:
1136 case NS_THEME_SCALE_THUMB_HORIZONTAL:
1137 case NS_THEME_SCALE_THUMB_VERTICAL: {
1138 if (aWidgetType == NS_THEME_RANGE_THUMB) {
1139 if (IsRangeHorizontal(aFrame)) {
1140 aPart = TKP_THUMBBOTTOM;
1141 } else {
1142 aPart = IsFrameRTL(aFrame) ? TKP_THUMBLEFT : TKP_THUMBRIGHT;
1143 }
1144 } else {
1145 aPart = (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL) ?
1146 TKP_THUMB : TKP_THUMBVERT;
1147 }
1148 EventStates eventState = GetContentState(aFrame, aWidgetType);
1149 if (!aFrame)
1150 aState = TS_NORMAL;
1151 else if (IsDisabled(aFrame, eventState)) {
1152 aState = TKP_DISABLED;
1153 }
1154 else {
1155 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a requirement for
1156 // the thumb, since the drag is not canceled
1157 // when you move outside the thumb.
1158 aState = TS_ACTIVE;
1159 else if (eventState.HasState(NS_EVENT_STATE_FOCUS))
1160 aState = TKP_FOCUSED;
1161 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1162 aState = TS_HOVER;
1163 else
1164 aState = TS_NORMAL;
1165 }
1166 return NS_OK;
1167 }
1168 case NS_THEME_SPINNER_UP_BUTTON:
1169 case NS_THEME_SPINNER_DOWN_BUTTON: {
1170 aPart = (aWidgetType == NS_THEME_SPINNER_UP_BUTTON) ?
1171 SPNP_UP : SPNP_DOWN;
1172 EventStates eventState = GetContentState(aFrame, aWidgetType);
1173 if (!aFrame)
1174 aState = TS_NORMAL;
1175 else if (IsDisabled(aFrame, eventState))
1176 aState = TS_DISABLED;
1177 else
1178 aState = StandardGetState(aFrame, aWidgetType, false);
1179 return NS_OK;
1180 }
1181 case NS_THEME_TOOLBOX:
1182 case NS_THEME_WIN_MEDIA_TOOLBOX:
1183 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
1184 case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
1185 case NS_THEME_STATUSBAR:
1186 case NS_THEME_SCROLLBAR:
1187 case NS_THEME_SCROLLBAR_SMALL: {
1188 aState = 0;
1189 if (IsVistaOrLater()) {
1190 // On vista, they have a part
1191 aPart = RP_BACKGROUND;
1192 } else {
1193 // Otherwise, they don't. (But I bet
1194 // RP_BACKGROUND would work here, too);
1195 aPart = 0;
1196 }
1197 return NS_OK;
1198 }
1199 case NS_THEME_TOOLBAR: {
1200 // Use -1 to indicate we don't wish to have the theme background drawn
1201 // for this item. We will pass any nessessary information via aState,
1202 // and will render the item using separate code.
1203 aPart = -1;
1204 aState = 0;
1205 if (aFrame) {
1206 nsIContent* content = aFrame->GetContent();
1207 nsIContent* parent = content->GetParent();
1208 // XXXzeniko hiding the first toolbar will result in an unwanted margin
1209 if (parent && parent->GetFirstChild() == content) {
1210 aState = 1;
1211 }
1212 }
1213 return NS_OK;
1214 }
1215 case NS_THEME_STATUSBAR_PANEL:
1216 case NS_THEME_STATUSBAR_RESIZER_PANEL:
1217 case NS_THEME_RESIZER: {
1218 aPart = (aWidgetType - NS_THEME_STATUSBAR_PANEL) + 1;
1219 aState = TS_NORMAL;
1220 return NS_OK;
1221 }
1222 case NS_THEME_TREEVIEW:
1223 case NS_THEME_LISTBOX: {
1224 aPart = TREEVIEW_BODY;
1225 aState = TS_NORMAL;
1226 return NS_OK;
1227 }
1228 case NS_THEME_TAB_PANELS: {
1229 aPart = TABP_PANELS;
1230 aState = TS_NORMAL;
1231 return NS_OK;
1232 }
1233 case NS_THEME_TAB_PANEL: {
1234 aPart = TABP_PANEL;
1235 aState = TS_NORMAL;
1236 return NS_OK;
1237 }
1238 case NS_THEME_TAB: {
1239 aPart = TABP_TAB;
1240 if (!aFrame) {
1241 aState = TS_NORMAL;
1242 return NS_OK;
1243 }
1245 EventStates eventState = GetContentState(aFrame, aWidgetType);
1246 if (IsDisabled(aFrame, eventState)) {
1247 aState = TS_DISABLED;
1248 return NS_OK;
1249 }
1251 if (IsSelectedTab(aFrame)) {
1252 aPart = TABP_TAB_SELECTED;
1253 aState = TS_ACTIVE; // The selected tab is always "pressed".
1254 }
1255 else
1256 aState = StandardGetState(aFrame, aWidgetType, true);
1258 return NS_OK;
1259 }
1260 case NS_THEME_TREEVIEW_HEADER_SORTARROW: {
1261 // XXX Probably will never work due to a bug in the Luna theme.
1262 aPart = 4;
1263 aState = 1;
1264 return NS_OK;
1265 }
1266 case NS_THEME_TREEVIEW_HEADER_CELL: {
1267 aPart = 1;
1268 if (!aFrame) {
1269 aState = TS_NORMAL;
1270 return NS_OK;
1271 }
1273 aState = StandardGetState(aFrame, aWidgetType, true);
1275 return NS_OK;
1276 }
1277 case NS_THEME_DROPDOWN: {
1278 nsIContent* content = aFrame->GetContent();
1279 bool isHTML = content && content->IsHTML();
1280 bool useDropBorder = isHTML || IsMenuListEditable(aFrame);
1281 EventStates eventState = GetContentState(aFrame, aWidgetType);
1283 /* On Vista/Win7, we use CBP_DROPBORDER instead of DROPFRAME for HTML
1284 * content or for editable menulists; this gives us the thin outline,
1285 * instead of the gradient-filled background */
1286 if (useDropBorder)
1287 aPart = CBP_DROPBORDER;
1288 else
1289 aPart = CBP_DROPFRAME;
1291 if (IsDisabled(aFrame, eventState)) {
1292 aState = TS_DISABLED;
1293 } else if (IsReadOnly(aFrame)) {
1294 aState = TS_NORMAL;
1295 } else if (IsOpenButton(aFrame)) {
1296 aState = TS_ACTIVE;
1297 } else {
1298 if (useDropBorder && (eventState.HasState(NS_EVENT_STATE_FOCUS) || IsFocused(aFrame)))
1299 aState = TS_ACTIVE;
1300 else if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
1301 aState = TS_ACTIVE;
1302 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1303 aState = TS_HOVER;
1304 else
1305 aState = TS_NORMAL;
1306 }
1308 return NS_OK;
1309 }
1310 case NS_THEME_DROPDOWN_BUTTON: {
1311 bool isHTML = IsHTMLContent(aFrame);
1312 nsIFrame* parentFrame = aFrame->GetParent();
1313 bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
1314 bool isOpen = false;
1316 // HTML select and XUL menulist dropdown buttons get state from the parent.
1317 if (isHTML || isMenulist)
1318 aFrame = parentFrame;
1320 EventStates eventState = GetContentState(aFrame, aWidgetType);
1321 aPart = IsVistaOrLater() ?
1322 CBP_DROPMARKER_VISTA : CBP_DROPMARKER;
1324 // For HTML controls with author styling, we should fall
1325 // back to the old dropmarker style to avoid clashes with
1326 // author-specified backgrounds and borders (bug #441034)
1327 if (isHTML && IsWidgetStyled(aFrame->PresContext(), aFrame, NS_THEME_DROPDOWN))
1328 aPart = CBP_DROPMARKER;
1330 if (IsDisabled(aFrame, eventState)) {
1331 aState = TS_DISABLED;
1332 return NS_OK;
1333 }
1335 if (isHTML) {
1336 nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
1337 isOpen = (ccf && ccf->IsDroppedDown());
1338 }
1339 else
1340 isOpen = IsOpenButton(aFrame);
1342 if (IsVistaOrLater()) {
1343 if (isHTML || IsMenuListEditable(aFrame)) {
1344 if (isOpen) {
1345 /* Hover is propagated, but we need to know whether we're
1346 * hovering just the combobox frame, not the dropdown frame.
1347 * But, we can't get that information, since hover is on the
1348 * content node, and they share the same content node. So,
1349 * instead, we cheat -- if the dropdown is open, we always
1350 * show the hover state. This looks fine in practice.
1351 */
1352 aState = TS_HOVER;
1353 return NS_OK;
1354 }
1355 } else {
1356 /* On Vista, the dropdown indicator on a menulist button in
1357 * chrome is not given a hover effect. When the frame isn't
1358 * isn't HTML content, we cheat and force the dropdown state
1359 * to be normal. (Bug 430434)
1360 */
1361 aState = TS_NORMAL;
1362 return NS_OK;
1363 }
1364 }
1366 aState = TS_NORMAL;
1368 // Dropdown button active state doesn't need :hover.
1369 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) {
1370 if (isOpen && (isHTML || isMenulist)) {
1371 // XXX Button should look active until the mouse is released, but
1372 // without making it look active when the popup is clicked.
1373 return NS_OK;
1374 }
1375 aState = TS_ACTIVE;
1376 }
1377 else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
1378 // No hover effect for XUL menulists and autocomplete dropdown buttons
1379 // while the dropdown menu is open.
1380 if (isOpen) {
1381 // XXX HTML select dropdown buttons should have the hover effect when
1382 // hovering the combobox frame, but not the popup frame.
1383 return NS_OK;
1384 }
1385 aState = TS_HOVER;
1386 }
1387 return NS_OK;
1388 }
1389 case NS_THEME_MENUPOPUP: {
1390 aPart = MENU_POPUPBACKGROUND;
1391 aState = MB_ACTIVE;
1392 return NS_OK;
1393 }
1394 case NS_THEME_MENUITEM:
1395 case NS_THEME_CHECKMENUITEM:
1396 case NS_THEME_RADIOMENUITEM: {
1397 bool isTopLevel = false;
1398 bool isOpen = false;
1399 bool isHover = false;
1400 nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
1401 EventStates eventState = GetContentState(aFrame, aWidgetType);
1403 isTopLevel = IsTopLevelMenu(aFrame);
1405 if (menuFrame)
1406 isOpen = menuFrame->IsOpen();
1408 isHover = IsMenuActive(aFrame, aWidgetType);
1410 if (isTopLevel) {
1411 aPart = MENU_BARITEM;
1413 if (isOpen)
1414 aState = MBI_PUSHED;
1415 else if (isHover)
1416 aState = MBI_HOT;
1417 else
1418 aState = MBI_NORMAL;
1420 // the disabled states are offset by 3
1421 if (IsDisabled(aFrame, eventState))
1422 aState += 3;
1423 } else {
1424 aPart = MENU_POPUPITEM;
1426 if (isHover)
1427 aState = MPI_HOT;
1428 else
1429 aState = MPI_NORMAL;
1431 // the disabled states are offset by 2
1432 if (IsDisabled(aFrame, eventState))
1433 aState += 2;
1434 }
1436 return NS_OK;
1437 }
1438 case NS_THEME_MENUSEPARATOR:
1439 aPart = MENU_POPUPSEPARATOR;
1440 aState = 0;
1441 return NS_OK;
1442 case NS_THEME_MENUARROW:
1443 {
1444 aPart = MENU_POPUPSUBMENU;
1445 EventStates eventState = GetContentState(aFrame, aWidgetType);
1446 aState = IsDisabled(aFrame, eventState) ? MSM_DISABLED : MSM_NORMAL;
1447 return NS_OK;
1448 }
1449 case NS_THEME_MENUCHECKBOX:
1450 case NS_THEME_MENURADIO:
1451 {
1452 bool isChecked;
1453 EventStates eventState = GetContentState(aFrame, aWidgetType);
1455 // NOTE: we can probably use NS_EVENT_STATE_CHECKED
1456 isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
1458 aPart = MENU_POPUPCHECK;
1459 aState = MC_CHECKMARKNORMAL;
1461 // Radio states are offset by 2
1462 if (aWidgetType == NS_THEME_MENURADIO)
1463 aState += 2;
1465 // the disabled states are offset by 1
1466 if (IsDisabled(aFrame, eventState))
1467 aState += 1;
1469 return NS_OK;
1470 }
1471 case NS_THEME_MENUITEMTEXT:
1472 case NS_THEME_MENUIMAGE:
1473 aPart = -1;
1474 aState = 0;
1475 return NS_OK;
1477 case NS_THEME_WINDOW_TITLEBAR:
1478 aPart = mozilla::widget::themeconst::WP_CAPTION;
1479 aState = GetTopLevelWindowActiveState(aFrame);
1480 return NS_OK;
1481 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
1482 aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
1483 aState = GetTopLevelWindowActiveState(aFrame);
1484 return NS_OK;
1485 case NS_THEME_WINDOW_FRAME_LEFT:
1486 aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
1487 aState = GetTopLevelWindowActiveState(aFrame);
1488 return NS_OK;
1489 case NS_THEME_WINDOW_FRAME_RIGHT:
1490 aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
1491 aState = GetTopLevelWindowActiveState(aFrame);
1492 return NS_OK;
1493 case NS_THEME_WINDOW_FRAME_BOTTOM:
1494 aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
1495 aState = GetTopLevelWindowActiveState(aFrame);
1496 return NS_OK;
1497 case NS_THEME_WINDOW_BUTTON_CLOSE:
1498 aPart = mozilla::widget::themeconst::WP_CLOSEBUTTON;
1499 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1500 return NS_OK;
1501 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
1502 aPart = mozilla::widget::themeconst::WP_MINBUTTON;
1503 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1504 return NS_OK;
1505 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
1506 aPart = mozilla::widget::themeconst::WP_MAXBUTTON;
1507 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1508 return NS_OK;
1509 case NS_THEME_WINDOW_BUTTON_RESTORE:
1510 aPart = mozilla::widget::themeconst::WP_RESTOREBUTTON;
1511 aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
1512 return NS_OK;
1513 case NS_THEME_WINDOW_BUTTON_BOX:
1514 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
1515 case NS_THEME_WIN_GLASS:
1516 case NS_THEME_WIN_BORDERLESS_GLASS:
1517 aPart = -1;
1518 aState = 0;
1519 return NS_OK;
1520 }
1522 aPart = 0;
1523 aState = 0;
1524 return NS_ERROR_FAILURE;
1525 }
1527 static bool
1528 AssumeThemePartAndStateAreTransparent(int32_t aPart, int32_t aState)
1529 {
1530 if (aPart == MENU_POPUPITEM && aState == MBI_NORMAL) {
1531 return true;
1532 }
1533 return false;
1534 }
1536 NS_IMETHODIMP
1537 nsNativeThemeWin::DrawWidgetBackground(nsRenderingContext* aContext,
1538 nsIFrame* aFrame,
1539 uint8_t aWidgetType,
1540 const nsRect& aRect,
1541 const nsRect& aDirtyRect)
1542 {
1543 HANDLE theme = GetTheme(aWidgetType);
1544 if (!theme)
1545 return ClassicDrawWidgetBackground(aContext, aFrame, aWidgetType, aRect, aDirtyRect);
1547 // ^^ without the right sdk, assume xp theming and fall through.
1548 if (nsUXThemeData::CheckForCompositor()) {
1549 switch (aWidgetType) {
1550 case NS_THEME_WINDOW_TITLEBAR:
1551 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
1552 case NS_THEME_WINDOW_FRAME_LEFT:
1553 case NS_THEME_WINDOW_FRAME_RIGHT:
1554 case NS_THEME_WINDOW_FRAME_BOTTOM:
1555 // Nothing to draw, these areas are glass. Minimum dimensions
1556 // should be set, so xul content should be layed out correctly.
1557 return NS_OK;
1558 break;
1559 case NS_THEME_WINDOW_BUTTON_CLOSE:
1560 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
1561 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
1562 case NS_THEME_WINDOW_BUTTON_RESTORE:
1563 // Not conventional bitmaps, can't be retrieved. If we fall
1564 // through here and call the theme library we'll get aero
1565 // basic bitmaps.
1566 return NS_OK;
1567 break;
1568 case NS_THEME_WIN_GLASS:
1569 case NS_THEME_WIN_BORDERLESS_GLASS:
1570 // Nothing to draw, this is the glass background.
1571 return NS_OK;
1572 case NS_THEME_WINDOW_BUTTON_BOX:
1573 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
1574 // We handle these through nsIWidget::UpdateThemeGeometries
1575 return NS_OK;
1576 break;
1577 }
1578 }
1580 int32_t part, state;
1581 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
1582 if (NS_FAILED(rv))
1583 return rv;
1585 if (AssumeThemePartAndStateAreTransparent(part, state)) {
1586 return NS_OK;
1587 }
1589 gfxFloat p2a = gfxFloat(aContext->AppUnitsPerDevPixel());
1590 RECT widgetRect;
1591 RECT clipRect;
1592 gfxRect tr(aRect.x, aRect.y, aRect.width, aRect.height),
1593 dr(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
1595 tr.ScaleInverse(p2a);
1596 dr.ScaleInverse(p2a);
1598 nsRefPtr<gfxContext> ctx = aContext->ThebesContext();
1600 gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
1602 RENDER_AGAIN:
1604 HDC hdc = nativeDrawing.BeginNativeDrawing();
1605 if (!hdc)
1606 return NS_ERROR_FAILURE;
1608 nativeDrawing.TransformToNativeRect(tr, widgetRect);
1609 nativeDrawing.TransformToNativeRect(dr, clipRect);
1611 #if 0
1612 {
1613 PR_LOG(gWindowsLog, PR_LOG_ERROR,
1614 (stderr, "xform: %f %f %f %f [%f %f]\n", m.xx, m.yx, m.xy, m.yy,
1615 m.x0, m.y0));
1616 PR_LOG(gWindowsLog, PR_LOG_ERROR,
1617 (stderr, "tr: [%d %d %d %d]\ndr: [%d %d %d %d]\noff: [%f %f]\n",
1618 tr.x, tr.y, tr.width, tr.height, dr.x, dr.y, dr.width, dr.height,
1619 offset.x, offset.y));
1620 }
1621 #endif
1623 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
1624 // Clip out the left and right corners of the frame, all we want in
1625 // is the middle section.
1626 widgetRect.left -= GetSystemMetrics(SM_CXFRAME);
1627 widgetRect.right += GetSystemMetrics(SM_CXFRAME);
1628 } else if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
1629 // The origin of the window is off screen when maximized and windows
1630 // doesn't compensate for this in rendering the background. Push the
1631 // top of the bitmap down by SM_CYFRAME so we get the full graphic.
1632 widgetRect.top += GetSystemMetrics(SM_CYFRAME);
1633 } else if (aWidgetType == NS_THEME_TAB) {
1634 // For left edge and right edge tabs, we need to adjust the widget
1635 // rects and clip rects so that the edges don't get drawn.
1636 bool isLeft = IsLeftToSelectedTab(aFrame);
1637 bool isRight = !isLeft && IsRightToSelectedTab(aFrame);
1639 if (isLeft || isRight) {
1640 // HACK ALERT: There appears to be no way to really obtain this value, so we're forced
1641 // to just use the default value for Luna (which also happens to be correct for
1642 // all the other skins I've tried).
1643 int32_t edgeSize = 2;
1645 // Armed with the size of the edge, we now need to either shift to the left or to the
1646 // right. The clip rect won't include this extra area, so we know that we're
1647 // effectively shifting the edge out of view (such that it won't be painted).
1648 if (isLeft)
1649 // The right edge should not be drawn. Extend our rect by the edge size.
1650 widgetRect.right += edgeSize;
1651 else
1652 // The left edge should not be drawn. Move the widget rect's left coord back.
1653 widgetRect.left -= edgeSize;
1654 }
1655 }
1656 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
1657 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
1658 }
1659 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
1660 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
1661 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
1662 }
1663 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
1664 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
1665 }
1667 // widgetRect is the bounding box for a widget, yet the scale track is only
1668 // a small portion of this size, so the edges of the scale need to be
1669 // adjusted to the real size of the track.
1670 if (aWidgetType == NS_THEME_RANGE ||
1671 aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1672 aWidgetType == NS_THEME_SCALE_VERTICAL) {
1673 RECT contentRect;
1674 GetThemeBackgroundContentRect(theme, hdc, part, state, &widgetRect, &contentRect);
1676 SIZE siz;
1677 GetThemePartSize(theme, hdc, part, state, &widgetRect, TS_TRUE, &siz);
1679 // When rounding is necessary, we round the position of the track
1680 // away from the chevron of the thumb to make it look better.
1681 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1682 (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
1683 contentRect.top += (contentRect.bottom - contentRect.top - siz.cy) / 2;
1684 contentRect.bottom = contentRect.top + siz.cy;
1685 }
1686 else {
1687 if (!IsFrameRTL(aFrame)) {
1688 contentRect.left += (contentRect.right - contentRect.left - siz.cx) / 2;
1689 contentRect.right = contentRect.left + siz.cx;
1690 } else {
1691 contentRect.right -= (contentRect.right - contentRect.left - siz.cx) / 2;
1692 contentRect.left = contentRect.right - siz.cx;
1693 }
1694 }
1696 DrawThemeBackground(theme, hdc, part, state, &contentRect, &clipRect);
1697 }
1698 else if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO)
1699 {
1700 bool isChecked = false;
1701 isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
1703 if (isChecked)
1704 {
1705 int bgState = MCB_NORMAL;
1706 EventStates eventState = GetContentState(aFrame, aWidgetType);
1708 // the disabled states are offset by 1
1709 if (IsDisabled(aFrame, eventState))
1710 bgState += 1;
1712 SIZE checkboxBGSize(GetCheckboxBGSize(theme, hdc));
1714 RECT checkBGRect = widgetRect;
1715 if (IsFrameRTL(aFrame)) {
1716 checkBGRect.left = checkBGRect.right-checkboxBGSize.cx;
1717 } else {
1718 checkBGRect.right = checkBGRect.left+checkboxBGSize.cx;
1719 }
1721 // Center the checkbox background vertically in the menuitem
1722 checkBGRect.top += (checkBGRect.bottom - checkBGRect.top)/2 - checkboxBGSize.cy/2;
1723 checkBGRect.bottom = checkBGRect.top + checkboxBGSize.cy;
1725 DrawThemeBackground(theme, hdc, MENU_POPUPCHECKBACKGROUND, bgState, &checkBGRect, &clipRect);
1727 MARGINS checkMargins = GetCheckboxMargins(theme, hdc);
1728 RECT checkRect = checkBGRect;
1729 checkRect.left += checkMargins.cxLeftWidth;
1730 checkRect.right -= checkMargins.cxRightWidth;
1731 checkRect.top += checkMargins.cyTopHeight;
1732 checkRect.bottom -= checkMargins.cyBottomHeight;
1733 DrawThemeBackground(theme, hdc, MENU_POPUPCHECK, state, &checkRect, &clipRect);
1734 }
1735 }
1736 else if (aWidgetType == NS_THEME_MENUPOPUP)
1737 {
1738 DrawThemeBackground(theme, hdc, MENU_POPUPBORDERS, /* state */ 0, &widgetRect, &clipRect);
1739 SIZE borderSize;
1740 GetThemePartSize(theme, hdc, MENU_POPUPBORDERS, 0, nullptr, TS_TRUE, &borderSize);
1742 RECT bgRect = widgetRect;
1743 bgRect.top += borderSize.cy;
1744 bgRect.bottom -= borderSize.cy;
1745 bgRect.left += borderSize.cx;
1746 bgRect.right -= borderSize.cx;
1748 DrawThemeBackground(theme, hdc, MENU_POPUPBACKGROUND, /* state */ 0, &bgRect, &clipRect);
1750 SIZE gutterSize(GetGutterSize(theme, hdc));
1752 RECT gutterRect;
1753 gutterRect.top = bgRect.top;
1754 gutterRect.bottom = bgRect.bottom;
1755 if (IsFrameRTL(aFrame)) {
1756 gutterRect.right = bgRect.right;
1757 gutterRect.left = gutterRect.right-gutterSize.cx;
1758 } else {
1759 gutterRect.left = bgRect.left;
1760 gutterRect.right = gutterRect.left+gutterSize.cx;
1761 }
1763 DrawThemeBGRTLAware(theme, hdc, MENU_POPUPGUTTER, /* state */ 0,
1764 &gutterRect, &clipRect, IsFrameRTL(aFrame));
1765 }
1766 else if (aWidgetType == NS_THEME_MENUSEPARATOR)
1767 {
1768 SIZE gutterSize(GetGutterSize(theme,hdc));
1770 RECT sepRect = widgetRect;
1771 if (IsFrameRTL(aFrame))
1772 sepRect.right -= gutterSize.cx;
1773 else
1774 sepRect.left += gutterSize.cx;
1776 DrawThemeBackground(theme, hdc, MENU_POPUPSEPARATOR, /* state */ 0, &sepRect, &clipRect);
1777 }
1778 else if (aWidgetType == NS_THEME_MENUARROW)
1779 {
1780 // We're dpi aware and as such on systems that have dpi > 96 set, the
1781 // theme library expects us to do proper positioning and scaling of glyphs.
1782 // For NS_THEME_MENUARROW, layout may hand us a widget rect larger than the
1783 // glyph rect we request in GetMinimumWidgetSize. To prevent distortion we
1784 // have to position and scale what we draw.
1786 SIZE glyphSize;
1787 GetThemePartSize(theme, hdc, part, state, nullptr, TS_TRUE, &glyphSize);
1789 int32_t widgetHeight = widgetRect.bottom - widgetRect.top;
1791 RECT renderRect = widgetRect;
1793 // We request (glyph width * 2, glyph height) in GetMinimumWidgetSize. In
1794 // Firefox some menu items provide the full height of the item to us, in
1795 // others our widget rect is the exact dims of our arrow glyph. Adjust the
1796 // vertical position by the added space, if any exists.
1797 renderRect.top += ((widgetHeight - glyphSize.cy) / 2);
1798 renderRect.bottom = renderRect.top + glyphSize.cy;
1799 // I'm using the width of the arrow glyph for the arrow-side padding.
1800 // AFAICT there doesn't appear to be a theme constant we can query
1801 // for this value. Generally this looks correct, and has the added
1802 // benefit of being a dpi adjusted value.
1803 if (!IsFrameRTL(aFrame)) {
1804 renderRect.right = widgetRect.right - glyphSize.cx;
1805 renderRect.left = renderRect.right - glyphSize.cx;
1806 } else {
1807 renderRect.left = glyphSize.cx;
1808 renderRect.right = renderRect.left + glyphSize.cx;
1809 }
1810 DrawThemeBGRTLAware(theme, hdc, part, state, &renderRect, &clipRect,
1811 IsFrameRTL(aFrame));
1812 }
1813 // The following widgets need to be RTL-aware
1814 else if (aWidgetType == NS_THEME_RESIZER ||
1815 aWidgetType == NS_THEME_DROPDOWN_BUTTON)
1816 {
1817 DrawThemeBGRTLAware(theme, hdc, part, state,
1818 &widgetRect, &clipRect, IsFrameRTL(aFrame));
1819 }
1820 else if (aWidgetType == NS_THEME_PROGRESSBAR ||
1821 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL) {
1822 // DrawThemeBackground renders each corner with a solid white pixel.
1823 // Restore these pixels to the underlying color. Tracks are rendered
1824 // using alpha recovery, so this makes the corners transparent.
1825 COLORREF color;
1826 color = GetPixel(hdc, widgetRect.left, widgetRect.top);
1827 DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
1828 SetPixel(hdc, widgetRect.left, widgetRect.top, color);
1829 SetPixel(hdc, widgetRect.right-1, widgetRect.top, color);
1830 SetPixel(hdc, widgetRect.right-1, widgetRect.bottom-1, color);
1831 SetPixel(hdc, widgetRect.left, widgetRect.bottom-1, color);
1832 }
1833 else if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
1834 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL) {
1835 DrawThemedProgressMeter(aFrame, aWidgetType, theme, hdc, part, state,
1836 &widgetRect, &clipRect, p2a);
1837 }
1838 // If part is negative, the element wishes us to not render a themed
1839 // background, instead opting to be drawn specially below.
1840 else if (part >= 0) {
1841 DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
1842 }
1844 // Draw focus rectangles for XP HTML checkboxes and radio buttons
1845 // XXX it'd be nice to draw these outside of the frame
1846 if (((aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_RADIO) &&
1847 aFrame->GetContent()->IsHTML()) ||
1848 aWidgetType == NS_THEME_RANGE ||
1849 aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1850 aWidgetType == NS_THEME_SCALE_VERTICAL) {
1851 EventStates contentState = GetContentState(aFrame, aWidgetType);
1853 if (contentState.HasState(NS_EVENT_STATE_FOCUS)) {
1854 POINT vpOrg;
1855 HPEN hPen = nullptr;
1857 uint8_t id = SaveDC(hdc);
1859 ::SelectClipRgn(hdc, nullptr);
1860 ::GetViewportOrgEx(hdc, &vpOrg);
1861 ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, nullptr);
1863 // On vista, choose our own colors and draw an XP style half focus rect
1864 // for focused checkboxes and a full rect when active.
1865 if (IsVistaOrLater() &&
1866 aWidgetType == NS_THEME_CHECKBOX) {
1867 LOGBRUSH lb;
1868 lb.lbStyle = BS_SOLID;
1869 lb.lbColor = RGB(255,255,255);
1870 lb.lbHatch = 0;
1872 hPen = ::ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, nullptr);
1873 ::SelectObject(hdc, hPen);
1875 // If pressed, draw the upper left corner of the dotted rect.
1876 if (contentState.HasState(NS_EVENT_STATE_ACTIVE)) {
1877 ::MoveToEx(hdc, widgetRect.left, widgetRect.bottom-1, nullptr);
1878 ::LineTo(hdc, widgetRect.left, widgetRect.top);
1879 ::LineTo(hdc, widgetRect.right-1, widgetRect.top);
1880 }
1882 // Draw the lower right corner of the dotted rect.
1883 ::MoveToEx(hdc, widgetRect.right-1, widgetRect.top, nullptr);
1884 ::LineTo(hdc, widgetRect.right-1, widgetRect.bottom-1);
1885 ::LineTo(hdc, widgetRect.left, widgetRect.bottom-1);
1886 } else {
1887 ::SetTextColor(hdc, 0);
1888 ::DrawFocusRect(hdc, &widgetRect);
1889 }
1890 ::RestoreDC(hdc, id);
1891 if (hPen) {
1892 ::DeleteObject(hPen);
1893 }
1894 }
1895 }
1896 else if (aWidgetType == NS_THEME_TOOLBAR && state == 0) {
1897 // Draw toolbar separator lines above all toolbars except the first one.
1898 // The lines are part of the Rebar theme, which is loaded for NS_THEME_TOOLBOX.
1899 theme = GetTheme(NS_THEME_TOOLBOX);
1900 if (!theme)
1901 return NS_ERROR_FAILURE;
1903 widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT;
1904 DrawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP, nullptr);
1905 }
1906 else if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL ||
1907 aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL)
1908 {
1909 // Draw the decorative gripper for the scrollbar thumb button, if it fits
1911 SIZE gripSize;
1912 MARGINS thumbMgns;
1913 int gripPart = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) ?
1914 SP_GRIPPERHOR : SP_GRIPPERVERT;
1916 if (GetThemePartSize(theme, hdc, gripPart, state, nullptr, TS_TRUE, &gripSize) == S_OK &&
1917 GetThemeMargins(theme, hdc, part, state, TMT_CONTENTMARGINS, nullptr, &thumbMgns) == S_OK &&
1918 gripSize.cx + thumbMgns.cxLeftWidth + thumbMgns.cxRightWidth <= widgetRect.right - widgetRect.left &&
1919 gripSize.cy + thumbMgns.cyTopHeight + thumbMgns.cyBottomHeight <= widgetRect.bottom - widgetRect.top)
1920 {
1921 DrawThemeBackground(theme, hdc, gripPart, state, &widgetRect, &clipRect);
1922 }
1923 }
1925 nativeDrawing.EndNativeDrawing();
1927 if (nativeDrawing.ShouldRenderAgain())
1928 goto RENDER_AGAIN;
1930 nativeDrawing.PaintToContext();
1932 return NS_OK;
1933 }
1935 NS_IMETHODIMP
1936 nsNativeThemeWin::GetWidgetBorder(nsDeviceContext* aContext,
1937 nsIFrame* aFrame,
1938 uint8_t aWidgetType,
1939 nsIntMargin* aResult)
1940 {
1941 HANDLE theme = GetTheme(aWidgetType);
1942 if (!theme)
1943 return ClassicGetWidgetBorder(aContext, aFrame, aWidgetType, aResult);
1945 (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
1947 if (!WidgetIsContainer(aWidgetType) ||
1948 aWidgetType == NS_THEME_TOOLBOX ||
1949 aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
1950 aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
1951 aWidgetType == NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX ||
1952 aWidgetType == NS_THEME_STATUSBAR ||
1953 aWidgetType == NS_THEME_RESIZER || aWidgetType == NS_THEME_TAB_PANEL ||
1954 aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL ||
1955 aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL ||
1956 aWidgetType == NS_THEME_MENUITEM || aWidgetType == NS_THEME_CHECKMENUITEM ||
1957 aWidgetType == NS_THEME_RADIOMENUITEM || aWidgetType == NS_THEME_MENUPOPUP ||
1958 aWidgetType == NS_THEME_MENUIMAGE || aWidgetType == NS_THEME_MENUITEMTEXT ||
1959 aWidgetType == NS_THEME_TOOLBAR_SEPARATOR ||
1960 aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
1961 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
1962 aWidgetType == NS_THEME_WIN_GLASS || aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS)
1963 return NS_OK; // Don't worry about it.
1965 int32_t part, state;
1966 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
1967 if (NS_FAILED(rv))
1968 return rv;
1970 if (aWidgetType == NS_THEME_TOOLBAR) {
1971 // make space for the separator line above all toolbars but the first
1972 if (state == 0)
1973 aResult->top = TB_SEPARATOR_HEIGHT;
1974 return NS_OK;
1975 }
1977 // Get our info.
1978 RECT outerRect; // Create a fake outer rect.
1979 outerRect.top = outerRect.left = 100;
1980 outerRect.right = outerRect.bottom = 200;
1981 RECT contentRect(outerRect);
1982 HRESULT res = GetThemeBackgroundContentRect(theme, nullptr, part, state, &outerRect, &contentRect);
1984 if (FAILED(res))
1985 return NS_ERROR_FAILURE;
1987 // Now compute the delta in each direction and place it in our
1988 // nsIntMargin struct.
1989 aResult->top = contentRect.top - outerRect.top;
1990 aResult->bottom = outerRect.bottom - contentRect.bottom;
1991 aResult->left = contentRect.left - outerRect.left;
1992 aResult->right = outerRect.right - contentRect.right;
1994 // Remove the edges for tabs that are before or after the selected tab,
1995 if (aWidgetType == NS_THEME_TAB) {
1996 if (IsLeftToSelectedTab(aFrame))
1997 // Remove the right edge, since we won't be drawing it.
1998 aResult->right = 0;
1999 else if (IsRightToSelectedTab(aFrame))
2000 // Remove the left edge, since we won't be drawing it.
2001 aResult->left = 0;
2002 }
2004 if (aFrame && (aWidgetType == NS_THEME_NUMBER_INPUT ||
2005 aWidgetType == NS_THEME_TEXTFIELD ||
2006 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE)) {
2007 nsIContent* content = aFrame->GetContent();
2008 if (content && content->IsHTML()) {
2009 // We need to pad textfields by 1 pixel, since the caret will draw
2010 // flush against the edge by default if we don't.
2011 aResult->top++;
2012 aResult->left++;
2013 aResult->bottom++;
2014 aResult->right++;
2015 }
2016 }
2018 return NS_OK;
2019 }
2021 bool
2022 nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext,
2023 nsIFrame* aFrame,
2024 uint8_t aWidgetType,
2025 nsIntMargin* aResult)
2026 {
2027 switch (aWidgetType) {
2028 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
2029 // and have a meaningful baseline, so they can't have
2030 // author-specified padding.
2031 case NS_THEME_CHECKBOX:
2032 case NS_THEME_RADIO:
2033 aResult->SizeTo(0, 0, 0, 0);
2034 return true;
2035 }
2037 HANDLE theme = GetTheme(aWidgetType);
2039 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX ||
2040 aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
2041 aResult->SizeTo(0, 0, 0, 0);
2043 // aero glass doesn't display custom buttons
2044 if (nsUXThemeData::CheckForCompositor())
2045 return true;
2047 // button padding for standard windows
2048 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX) {
2049 aResult->top = GetSystemMetrics(SM_CXFRAME);
2050 }
2051 return true;
2052 }
2054 // Content padding
2055 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
2056 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
2057 aResult->SizeTo(0, 0, 0, 0);
2058 // XXX Maximized windows have an offscreen offset equal to
2059 // the border padding. This should be addressed in nsWindow,
2060 // but currently can't be, see UpdateNonClientMargins.
2061 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED)
2062 aResult->top = GetSystemMetrics(SM_CXFRAME)
2063 + GetSystemMetrics(SM_CXPADDEDBORDER);
2064 return true;
2065 }
2067 if (!theme)
2068 return ClassicGetWidgetPadding(aContext, aFrame, aWidgetType, aResult);
2070 if (aWidgetType == NS_THEME_MENUPOPUP)
2071 {
2072 SIZE popupSize;
2073 GetThemePartSize(theme, nullptr, MENU_POPUPBORDERS, /* state */ 0, nullptr, TS_TRUE, &popupSize);
2074 aResult->top = aResult->bottom = popupSize.cy;
2075 aResult->left = aResult->right = popupSize.cx;
2076 return true;
2077 }
2079 if (IsVistaOrLater()) {
2080 if (aWidgetType == NS_THEME_NUMBER_INPUT ||
2081 aWidgetType == NS_THEME_TEXTFIELD ||
2082 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
2083 aWidgetType == NS_THEME_DROPDOWN)
2084 {
2085 /* If we have author-specified padding for these elements, don't do the fixups below */
2086 if (aFrame->PresContext()->HasAuthorSpecifiedRules(aFrame, NS_AUTHOR_SPECIFIED_PADDING))
2087 return false;
2088 }
2090 /* textfields need extra pixels on all sides, otherwise they
2091 * wrap their content too tightly. The actual border is drawn 1px
2092 * inside the specified rectangle, so Gecko will end up making the
2093 * contents look too small. Instead, we add 2px padding for the
2094 * contents and fix this. (Used to be 1px added, see bug 430212)
2095 */
2096 if (aWidgetType == NS_THEME_NUMBER_INPUT ||
2097 aWidgetType == NS_THEME_TEXTFIELD ||
2098 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
2099 aResult->top = aResult->bottom = 2;
2100 aResult->left = aResult->right = 2;
2101 return true;
2102 } else if (IsHTMLContent(aFrame) && aWidgetType == NS_THEME_DROPDOWN) {
2103 /* For content menulist controls, we need an extra pixel so
2104 * that we have room to draw our focus rectangle stuff.
2105 * Otherwise, the focus rect might overlap the control's
2106 * border.
2107 */
2108 aResult->top = aResult->bottom = 1;
2109 aResult->left = aResult->right = 1;
2110 return true;
2111 }
2112 }
2114 int32_t right, left, top, bottom;
2115 right = left = top = bottom = 0;
2116 switch (aWidgetType)
2117 {
2118 case NS_THEME_MENUIMAGE:
2119 right = 8;
2120 left = 3;
2121 break;
2122 case NS_THEME_MENUCHECKBOX:
2123 case NS_THEME_MENURADIO:
2124 right = 8;
2125 left = 0;
2126 break;
2127 case NS_THEME_MENUITEMTEXT:
2128 // There seem to be exactly 4 pixels from the edge
2129 // of the gutter to the text: 2px margin (CSS) + 2px padding (here)
2130 {
2131 SIZE size(GetGutterSize(theme, nullptr));
2132 left = size.cx + 2;
2133 }
2134 break;
2135 case NS_THEME_MENUSEPARATOR:
2136 {
2137 SIZE size(GetGutterSize(theme, nullptr));
2138 left = size.cx + 5;
2139 top = 10;
2140 bottom = 7;
2141 }
2142 break;
2143 default:
2144 return false;
2145 }
2147 if (IsFrameRTL(aFrame))
2148 {
2149 aResult->right = left;
2150 aResult->left = right;
2151 }
2152 else
2153 {
2154 aResult->right = right;
2155 aResult->left = left;
2156 }
2158 return true;
2159 }
2161 bool
2162 nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext,
2163 nsIFrame* aFrame,
2164 uint8_t aOverflowRect,
2165 nsRect* aResult)
2166 {
2167 /* This is disabled for now, because it causes invalidation problems --
2168 * see bug 420381. The effect of not updating the overflow area is that
2169 * for dropdown buttons in content areas, there is a 1px border on 3 sides
2170 * where, if invalidated, the dropdown control probably won't be repainted.
2171 * This is fairly minor, as by default there is nothing in that area, and
2172 * a border only shows up if the widget is being hovered.
2173 */
2174 #if 0
2175 if (IsVistaOrLater()) {
2176 /* We explicitly draw dropdown buttons in HTML content 1px bigger
2177 * up, right, and bottom so that they overlap the dropdown's border
2178 * like they're supposed to.
2179 */
2180 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON &&
2181 IsHTMLContent(aFrame) &&
2182 !IsWidgetStyled(aFrame->GetParent()->PresContext(),
2183 aFrame->GetParent(),
2184 NS_THEME_DROPDOWN))
2185 {
2186 int32_t p2a = aContext->AppUnitsPerDevPixel();
2187 /* Note: no overflow on the left */
2188 nsMargin m(p2a, p2a, p2a, 0);
2189 aOverflowRect->Inflate (m);
2190 return true;
2191 }
2192 }
2193 #endif
2195 return false;
2196 }
2198 NS_IMETHODIMP
2199 nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* aFrame,
2200 uint8_t aWidgetType,
2201 nsIntSize* aResult, bool* aIsOverridable)
2202 {
2203 (*aResult).width = (*aResult).height = 0;
2204 *aIsOverridable = true;
2206 HANDLE theme = GetTheme(aWidgetType);
2207 if (!theme)
2208 return ClassicGetMinimumWidgetSize(aContext, aFrame, aWidgetType, aResult, aIsOverridable);
2210 switch (aWidgetType) {
2211 case NS_THEME_GROUPBOX:
2212 case NS_THEME_NUMBER_INPUT:
2213 case NS_THEME_TEXTFIELD:
2214 case NS_THEME_TOOLBOX:
2215 case NS_THEME_WIN_MEDIA_TOOLBOX:
2216 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
2217 case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
2218 case NS_THEME_TOOLBAR:
2219 case NS_THEME_STATUSBAR:
2220 case NS_THEME_PROGRESSBAR_CHUNK:
2221 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2222 case NS_THEME_TAB_PANELS:
2223 case NS_THEME_TAB_PANEL:
2224 case NS_THEME_LISTBOX:
2225 case NS_THEME_TREEVIEW:
2226 case NS_THEME_MENUITEMTEXT:
2227 case NS_THEME_WIN_GLASS:
2228 case NS_THEME_WIN_BORDERLESS_GLASS:
2229 return NS_OK; // Don't worry about it.
2230 }
2232 if (aWidgetType == NS_THEME_MENUITEM && IsTopLevelMenu(aFrame))
2233 return NS_OK; // Don't worry about it for top level menus
2235 // Call GetSystemMetrics to determine size for WinXP scrollbars
2236 // (GetThemeSysSize API returns the optimal size for the theme, but
2237 // Windows appears to always use metrics when drawing standard scrollbars)
2238 THEMESIZE sizeReq = TS_TRUE; // Best-fit size
2239 switch (aWidgetType) {
2240 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2241 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2242 case NS_THEME_SCROLLBAR_BUTTON_UP:
2243 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2244 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2245 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2246 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2247 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2248 case NS_THEME_DROPDOWN_BUTTON:
2249 return ClassicGetMinimumWidgetSize(aContext, aFrame, aWidgetType, aResult, aIsOverridable);
2251 case NS_THEME_MENUITEM:
2252 case NS_THEME_CHECKMENUITEM:
2253 case NS_THEME_RADIOMENUITEM:
2254 if(!IsTopLevelMenu(aFrame))
2255 {
2256 SIZE gutterSize(GetGutterSize(theme, nullptr));
2257 aResult->width = gutterSize.cx;
2258 aResult->height = gutterSize.cy;
2259 return NS_OK;
2260 }
2261 break;
2263 case NS_THEME_MENUIMAGE:
2264 case NS_THEME_MENUCHECKBOX:
2265 case NS_THEME_MENURADIO:
2266 {
2267 SIZE boxSize(GetGutterSize(theme, nullptr));
2268 aResult->width = boxSize.cx+2;
2269 aResult->height = boxSize.cy;
2270 *aIsOverridable = false;
2271 }
2273 case NS_THEME_MENUITEMTEXT:
2274 return NS_OK;
2276 case NS_THEME_PROGRESSBAR:
2277 case NS_THEME_PROGRESSBAR_VERTICAL:
2278 // Best-fit size for progress meters is too large for most
2279 // themes. We want these widgets to be able to really shrink
2280 // down, so use the min-size request value (of 0).
2281 sizeReq = TS_MIN;
2282 break;
2284 case NS_THEME_RESIZER:
2285 *aIsOverridable = false;
2286 break;
2288 case NS_THEME_RANGE_THUMB:
2289 case NS_THEME_SCALE_THUMB_HORIZONTAL:
2290 case NS_THEME_SCALE_THUMB_VERTICAL:
2291 {
2292 *aIsOverridable = false;
2293 // on Vista, GetThemePartAndState returns odd values for
2294 // scale thumbs, so use a hardcoded size instead.
2295 if (IsVistaOrLater()) {
2296 if (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL ||
2297 (aWidgetType == NS_THEME_RANGE_THUMB && IsRangeHorizontal(aFrame))) {
2298 aResult->width = 12;
2299 aResult->height = 20;
2300 }
2301 else {
2302 aResult->width = 20;
2303 aResult->height = 12;
2304 }
2305 return NS_OK;
2306 }
2307 break;
2308 }
2310 case NS_THEME_SCROLLBAR:
2311 {
2312 if (nsLookAndFeel::GetInt(
2313 nsLookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
2314 aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
2315 ::GetSystemMetrics(SM_CYVSCROLL));
2316 return NS_OK;
2317 }
2318 break;
2319 }
2321 case NS_THEME_TOOLBAR_SEPARATOR:
2322 // that's 2px left margin, 2px right margin and 2px separator
2323 // (the margin is drawn as part of the separator, though)
2324 aResult->width = 6;
2325 return NS_OK;
2327 case NS_THEME_BUTTON:
2328 // We should let HTML buttons shrink to their min size.
2329 // FIXME bug 403934: We should probably really separate
2330 // GetPreferredWidgetSize from GetMinimumWidgetSize, so callers can
2331 // use the one they want.
2332 if (aFrame->GetContent()->IsHTML()) {
2333 sizeReq = TS_MIN;
2334 }
2335 break;
2337 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2338 case NS_THEME_WINDOW_BUTTON_RESTORE:
2339 // The only way to get accurate titlebar button info is to query a
2340 // window w/buttons when it's visible. nsWindow takes care of this and
2341 // stores that info in nsUXThemeData.
2342 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cx;
2343 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cy;
2344 // For XP, subtract 4 from system metrics dimensions.
2345 if (!IsVistaOrLater()) {
2346 aResult->width -= 4;
2347 aResult->height -= 4;
2348 }
2349 AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
2350 *aIsOverridable = false;
2351 return NS_OK;
2353 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2354 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cx;
2355 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cy;
2356 if (!IsVistaOrLater()) {
2357 aResult->width -= 4;
2358 aResult->height -= 4;
2359 }
2360 AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
2361 *aIsOverridable = false;
2362 return NS_OK;
2364 case NS_THEME_WINDOW_BUTTON_CLOSE:
2365 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cx;
2366 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cy;
2367 if (!IsVistaOrLater()) {
2368 aResult->width -= 4;
2369 aResult->height -= 4;
2370 }
2371 AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
2372 *aIsOverridable = false;
2373 return NS_OK;
2375 case NS_THEME_WINDOW_TITLEBAR:
2376 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2377 aResult->height = GetSystemMetrics(SM_CYCAPTION);
2378 aResult->height += GetSystemMetrics(SM_CYFRAME);
2379 aResult->height += GetSystemMetrics(SM_CXPADDEDBORDER);
2380 *aIsOverridable = false;
2381 return NS_OK;
2383 case NS_THEME_WINDOW_BUTTON_BOX:
2384 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2385 if (nsUXThemeData::CheckForCompositor()) {
2386 aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx;
2387 aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy
2388 - GetSystemMetrics(SM_CYFRAME)
2389 - GetSystemMetrics(SM_CXPADDEDBORDER);
2390 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
2391 aResult->width += 1;
2392 aResult->height -= 2;
2393 }
2394 *aIsOverridable = false;
2395 return NS_OK;
2396 }
2397 break;
2399 case NS_THEME_WINDOW_FRAME_LEFT:
2400 case NS_THEME_WINDOW_FRAME_RIGHT:
2401 case NS_THEME_WINDOW_FRAME_BOTTOM:
2402 aResult->width = GetSystemMetrics(SM_CXFRAME);
2403 aResult->height = GetSystemMetrics(SM_CYFRAME);
2404 *aIsOverridable = false;
2405 return NS_OK;
2406 }
2408 int32_t part, state;
2409 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
2410 if (NS_FAILED(rv))
2411 return rv;
2413 HDC hdc = ::GetDC(nullptr);
2414 if (!hdc)
2415 return NS_ERROR_FAILURE;
2417 SIZE sz;
2418 GetThemePartSize(theme, hdc, part, state, nullptr, sizeReq, &sz);
2419 aResult->width = sz.cx;
2420 aResult->height = sz.cy;
2422 switch(aWidgetType) {
2423 case NS_THEME_SPINNER_UP_BUTTON:
2424 case NS_THEME_SPINNER_DOWN_BUTTON:
2425 aResult->width++;
2426 aResult->height = aResult->height / 2 + 1;
2427 break;
2429 case NS_THEME_MENUSEPARATOR:
2430 {
2431 SIZE gutterSize(GetGutterSize(theme, hdc));
2432 aResult->width += gutterSize.cx;
2433 break;
2434 }
2436 case NS_THEME_MENUARROW:
2437 {
2438 // Use the width of the arrow glyph as padding. See the drawing
2439 // code for details.
2440 aResult->width *= 2;
2441 break;
2442 }
2443 }
2445 ::ReleaseDC(nullptr, hdc);
2446 return NS_OK;
2447 }
2449 NS_IMETHODIMP
2450 nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
2451 nsIAtom* aAttribute, bool* aShouldRepaint)
2452 {
2453 // Some widget types just never change state.
2454 if (aWidgetType == NS_THEME_TOOLBOX ||
2455 aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
2456 aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
2457 aWidgetType == NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX ||
2458 aWidgetType == NS_THEME_TOOLBAR ||
2459 aWidgetType == NS_THEME_STATUSBAR || aWidgetType == NS_THEME_STATUSBAR_PANEL ||
2460 aWidgetType == NS_THEME_STATUSBAR_RESIZER_PANEL ||
2461 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
2462 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
2463 aWidgetType == NS_THEME_PROGRESSBAR ||
2464 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
2465 aWidgetType == NS_THEME_TOOLTIP ||
2466 aWidgetType == NS_THEME_TAB_PANELS ||
2467 aWidgetType == NS_THEME_TAB_PANEL ||
2468 aWidgetType == NS_THEME_TOOLBAR_SEPARATOR ||
2469 aWidgetType == NS_THEME_WIN_GLASS ||
2470 aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS) {
2471 *aShouldRepaint = false;
2472 return NS_OK;
2473 }
2475 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
2476 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
2477 aWidgetType == NS_THEME_WINDOW_FRAME_LEFT ||
2478 aWidgetType == NS_THEME_WINDOW_FRAME_RIGHT ||
2479 aWidgetType == NS_THEME_WINDOW_FRAME_BOTTOM ||
2480 aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE ||
2481 aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
2482 aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
2483 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
2484 *aShouldRepaint = true;
2485 return NS_OK;
2486 }
2488 // On Vista, the scrollbar buttons need to change state when the track has/doesn't have hover
2489 if (!IsVistaOrLater() &&
2490 (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL ||
2491 aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL)) {
2492 *aShouldRepaint = false;
2493 return NS_OK;
2494 }
2496 // We need to repaint the dropdown arrow in vista HTML combobox controls when
2497 // the control is closed to get rid of the hover effect.
2498 if (IsVistaOrLater() &&
2499 (aWidgetType == NS_THEME_DROPDOWN || aWidgetType == NS_THEME_DROPDOWN_BUTTON) &&
2500 IsHTMLContent(aFrame))
2501 {
2502 *aShouldRepaint = true;
2503 return NS_OK;
2504 }
2506 // XXXdwh Not sure what can really be done here. Can at least guess for
2507 // specific widgets that they're highly unlikely to have certain states.
2508 // For example, a toolbar doesn't care about any states.
2509 if (!aAttribute) {
2510 // Hover/focus/active changed. Always repaint.
2511 *aShouldRepaint = true;
2512 }
2513 else {
2514 // Check the attribute to see if it's relevant.
2515 // disabled, checked, dlgtype, default, etc.
2516 *aShouldRepaint = false;
2517 if (aAttribute == nsGkAtoms::disabled ||
2518 aAttribute == nsGkAtoms::checked ||
2519 aAttribute == nsGkAtoms::selected ||
2520 aAttribute == nsGkAtoms::readonly ||
2521 aAttribute == nsGkAtoms::open ||
2522 aAttribute == nsGkAtoms::menuactive ||
2523 aAttribute == nsGkAtoms::focused)
2524 *aShouldRepaint = true;
2525 }
2527 return NS_OK;
2528 }
2530 NS_IMETHODIMP
2531 nsNativeThemeWin::ThemeChanged()
2532 {
2533 nsUXThemeData::Invalidate();
2534 return NS_OK;
2535 }
2537 bool
2538 nsNativeThemeWin::ThemeSupportsWidget(nsPresContext* aPresContext,
2539 nsIFrame* aFrame,
2540 uint8_t aWidgetType)
2541 {
2542 // XXXdwh We can go even further and call the API to ask if support exists for
2543 // specific widgets.
2545 if (aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled())
2546 return false;
2548 HANDLE theme = nullptr;
2549 if (aWidgetType == NS_THEME_CHECKBOX_CONTAINER)
2550 theme = GetTheme(NS_THEME_CHECKBOX);
2551 else if (aWidgetType == NS_THEME_RADIO_CONTAINER)
2552 theme = GetTheme(NS_THEME_RADIO);
2553 else
2554 theme = GetTheme(aWidgetType);
2556 if ((theme) || (!theme && ClassicThemeSupportsWidget(aPresContext, aFrame, aWidgetType)))
2557 // turn off theming for some HTML widgets styled by the page
2558 return (!IsWidgetStyled(aPresContext, aFrame, aWidgetType));
2560 return false;
2561 }
2563 bool
2564 nsNativeThemeWin::WidgetIsContainer(uint8_t aWidgetType)
2565 {
2566 // XXXdwh At some point flesh all of this out.
2567 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON ||
2568 aWidgetType == NS_THEME_RADIO ||
2569 aWidgetType == NS_THEME_CHECKBOX)
2570 return false;
2571 return true;
2572 }
2574 bool
2575 nsNativeThemeWin::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
2576 {
2577 return false;
2578 }
2580 bool
2581 nsNativeThemeWin::ThemeNeedsComboboxDropmarker()
2582 {
2583 return true;
2584 }
2586 bool
2587 nsNativeThemeWin::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
2588 {
2589 switch (aWidgetType) {
2590 case NS_THEME_WINDOW_TITLEBAR:
2591 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2592 case NS_THEME_WINDOW_FRAME_LEFT:
2593 case NS_THEME_WINDOW_FRAME_RIGHT:
2594 case NS_THEME_WINDOW_FRAME_BOTTOM:
2595 case NS_THEME_WINDOW_BUTTON_CLOSE:
2596 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2597 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2598 case NS_THEME_WINDOW_BUTTON_RESTORE:
2599 return true;
2600 default:
2601 return false;
2602 }
2603 }
2605 bool
2606 nsNativeThemeWin::ShouldHideScrollbars()
2607 {
2608 return WinUtils::ShouldHideScrollbars();
2609 }
2611 nsITheme::Transparency
2612 nsNativeThemeWin::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
2613 {
2614 switch (aWidgetType) {
2615 case NS_THEME_SCROLLBAR_SMALL:
2616 case NS_THEME_SCROLLBAR:
2617 case NS_THEME_STATUSBAR:
2618 // Knowing that scrollbars and statusbars are opaque improves
2619 // performance, because we create layers for them. This better be
2620 // true across all Windows themes! If it's not true, we should
2621 // paint an opaque background for them to make it true!
2622 return eOpaque;
2623 case NS_THEME_WIN_GLASS:
2624 case NS_THEME_WIN_BORDERLESS_GLASS:
2625 case NS_THEME_SCALE_HORIZONTAL:
2626 case NS_THEME_SCALE_VERTICAL:
2627 case NS_THEME_PROGRESSBAR:
2628 case NS_THEME_PROGRESSBAR_VERTICAL:
2629 case NS_THEME_PROGRESSBAR_CHUNK:
2630 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2631 case NS_THEME_RANGE:
2632 return eTransparent;
2633 }
2635 HANDLE theme = GetTheme(aWidgetType);
2636 // For the classic theme we don't really have a way of knowing
2637 if (!theme) {
2638 // menu backgrounds and tooltips which can't be themed are opaque
2639 if (aWidgetType == NS_THEME_MENUPOPUP || aWidgetType == NS_THEME_TOOLTIP) {
2640 return eOpaque;
2641 }
2642 return eUnknownTransparency;
2643 }
2645 int32_t part, state;
2646 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
2647 // Fail conservatively
2648 NS_ENSURE_SUCCESS(rv, eUnknownTransparency);
2650 if (part <= 0) {
2651 // Not a real part code, so IsThemeBackgroundPartiallyTransparent may
2652 // not work, so don't call it.
2653 return eUnknownTransparency;
2654 }
2656 if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
2657 return eTransparent;
2658 return eOpaque;
2659 }
2661 /* Windows 9x/NT/2000/Classic XP Theme Support */
2663 bool
2664 nsNativeThemeWin::ClassicThemeSupportsWidget(nsPresContext* aPresContext,
2665 nsIFrame* aFrame,
2666 uint8_t aWidgetType)
2667 {
2668 switch (aWidgetType) {
2669 case NS_THEME_RESIZER:
2670 {
2671 // The classic native resizer has an opaque grey background which doesn't
2672 // match the usually white background of the scrollable container, so
2673 // only support the native resizer if not in a scrollframe.
2674 nsIFrame* parentFrame = aFrame->GetParent();
2675 return (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame);
2676 }
2677 case NS_THEME_MENUBAR:
2678 case NS_THEME_MENUPOPUP:
2679 // Classic non-flat menus are handled almost entirely through CSS.
2680 if (!nsUXThemeData::sFlatMenus)
2681 return false;
2682 case NS_THEME_BUTTON:
2683 case NS_THEME_NUMBER_INPUT:
2684 case NS_THEME_TEXTFIELD:
2685 case NS_THEME_TEXTFIELD_MULTILINE:
2686 case NS_THEME_CHECKBOX:
2687 case NS_THEME_RADIO:
2688 case NS_THEME_RANGE:
2689 case NS_THEME_RANGE_THUMB:
2690 case NS_THEME_GROUPBOX:
2691 case NS_THEME_SCROLLBAR_BUTTON_UP:
2692 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2693 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2694 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2695 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2696 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2697 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2698 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2699 case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
2700 case NS_THEME_SCALE_HORIZONTAL:
2701 case NS_THEME_SCALE_VERTICAL:
2702 case NS_THEME_SCALE_THUMB_HORIZONTAL:
2703 case NS_THEME_SCALE_THUMB_VERTICAL:
2704 case NS_THEME_DROPDOWN_BUTTON:
2705 case NS_THEME_SPINNER_UP_BUTTON:
2706 case NS_THEME_SPINNER_DOWN_BUTTON:
2707 case NS_THEME_LISTBOX:
2708 case NS_THEME_TREEVIEW:
2709 case NS_THEME_DROPDOWN_TEXTFIELD:
2710 case NS_THEME_DROPDOWN:
2711 case NS_THEME_TOOLTIP:
2712 case NS_THEME_STATUSBAR:
2713 case NS_THEME_STATUSBAR_PANEL:
2714 case NS_THEME_STATUSBAR_RESIZER_PANEL:
2715 case NS_THEME_PROGRESSBAR:
2716 case NS_THEME_PROGRESSBAR_VERTICAL:
2717 case NS_THEME_PROGRESSBAR_CHUNK:
2718 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2719 case NS_THEME_TAB:
2720 case NS_THEME_TAB_PANEL:
2721 case NS_THEME_TAB_PANELS:
2722 case NS_THEME_MENUITEM:
2723 case NS_THEME_CHECKMENUITEM:
2724 case NS_THEME_RADIOMENUITEM:
2725 case NS_THEME_MENUCHECKBOX:
2726 case NS_THEME_MENURADIO:
2727 case NS_THEME_MENUARROW:
2728 case NS_THEME_MENUSEPARATOR:
2729 case NS_THEME_MENUITEMTEXT:
2730 case NS_THEME_WINDOW_TITLEBAR:
2731 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2732 case NS_THEME_WINDOW_FRAME_LEFT:
2733 case NS_THEME_WINDOW_FRAME_RIGHT:
2734 case NS_THEME_WINDOW_FRAME_BOTTOM:
2735 case NS_THEME_WINDOW_BUTTON_CLOSE:
2736 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2737 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2738 case NS_THEME_WINDOW_BUTTON_RESTORE:
2739 case NS_THEME_WINDOW_BUTTON_BOX:
2740 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2741 return true;
2742 }
2743 return false;
2744 }
2746 nsresult
2747 nsNativeThemeWin::ClassicGetWidgetBorder(nsDeviceContext* aContext,
2748 nsIFrame* aFrame,
2749 uint8_t aWidgetType,
2750 nsIntMargin* aResult)
2751 {
2752 switch (aWidgetType) {
2753 case NS_THEME_GROUPBOX:
2754 case NS_THEME_BUTTON:
2755 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
2756 break;
2757 case NS_THEME_STATUSBAR:
2758 (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
2759 (*aResult).top = 2;
2760 break;
2761 case NS_THEME_LISTBOX:
2762 case NS_THEME_TREEVIEW:
2763 case NS_THEME_DROPDOWN:
2764 case NS_THEME_DROPDOWN_TEXTFIELD:
2765 case NS_THEME_TAB:
2766 case NS_THEME_NUMBER_INPUT:
2767 case NS_THEME_TEXTFIELD:
2768 case NS_THEME_TEXTFIELD_MULTILINE:
2769 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
2770 break;
2771 case NS_THEME_STATUSBAR_PANEL:
2772 case NS_THEME_STATUSBAR_RESIZER_PANEL: {
2773 (*aResult).top = 1;
2774 (*aResult).left = 1;
2775 (*aResult).bottom = 1;
2776 (*aResult).right = aFrame->GetNextSibling() ? 3 : 1;
2777 break;
2778 }
2779 case NS_THEME_TOOLTIP:
2780 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
2781 break;
2782 case NS_THEME_PROGRESSBAR:
2783 case NS_THEME_PROGRESSBAR_VERTICAL:
2784 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
2785 break;
2786 case NS_THEME_MENUBAR:
2787 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 0;
2788 break;
2789 case NS_THEME_MENUPOPUP:
2790 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 3;
2791 break;
2792 default:
2793 (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
2794 break;
2795 }
2796 return NS_OK;
2797 }
2799 bool
2800 nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext* aContext,
2801 nsIFrame* aFrame,
2802 uint8_t aWidgetType,
2803 nsIntMargin* aResult)
2804 {
2805 switch (aWidgetType) {
2806 case NS_THEME_MENUITEM:
2807 case NS_THEME_CHECKMENUITEM:
2808 case NS_THEME_RADIOMENUITEM: {
2809 int32_t part, state;
2810 bool focused;
2812 if (NS_FAILED(ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused)))
2813 return false;
2815 if (part == 1) { // top-level menu
2816 if (nsUXThemeData::sFlatMenus || !(state & DFCS_PUSHED)) {
2817 (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
2818 }
2819 else {
2820 // make top-level menus look sunken when pushed in the Classic look
2821 (*aResult).top = (*aResult).left = 3;
2822 (*aResult).bottom = (*aResult).right = 1;
2823 }
2824 }
2825 else {
2826 (*aResult).top = 0;
2827 (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
2828 }
2829 return true;
2830 }
2831 case NS_THEME_PROGRESSBAR:
2832 case NS_THEME_PROGRESSBAR_VERTICAL:
2833 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
2834 return true;
2835 default:
2836 return false;
2837 }
2838 }
2840 nsresult
2841 nsNativeThemeWin::ClassicGetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* aFrame,
2842 uint8_t aWidgetType,
2843 nsIntSize* aResult, bool* aIsOverridable)
2844 {
2845 (*aResult).width = (*aResult).height = 0;
2846 *aIsOverridable = true;
2847 switch (aWidgetType) {
2848 case NS_THEME_RADIO:
2849 case NS_THEME_CHECKBOX:
2850 (*aResult).width = (*aResult).height = 13;
2851 break;
2852 case NS_THEME_MENUCHECKBOX:
2853 case NS_THEME_MENURADIO:
2854 case NS_THEME_MENUARROW:
2855 (*aResult).width = ::GetSystemMetrics(SM_CXMENUCHECK);
2856 (*aResult).height = ::GetSystemMetrics(SM_CYMENUCHECK);
2857 break;
2858 case NS_THEME_SPINNER_UP_BUTTON:
2859 case NS_THEME_SPINNER_DOWN_BUTTON:
2860 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2861 (*aResult).height = 8; // No good metrics available for this
2862 *aIsOverridable = false;
2863 break;
2864 case NS_THEME_SCROLLBAR_BUTTON_UP:
2865 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2866 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2867 (*aResult).height = ::GetSystemMetrics(SM_CYVSCROLL);
2868 *aIsOverridable = false;
2869 break;
2870 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2871 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2872 (*aResult).width = ::GetSystemMetrics(SM_CXHSCROLL);
2873 (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
2874 *aIsOverridable = false;
2875 break;
2876 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2877 // XXX HACK We should be able to have a minimum height for the scrollbar
2878 // track. However, this causes problems when uncollapsing a scrollbar
2879 // inside a tree. See bug 201379 for details.
2881 // (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB) << 1;
2882 break;
2883 case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
2884 {
2885 aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
2886 ::GetSystemMetrics(SM_CYVSCROLL));
2887 break;
2888 }
2889 case NS_THEME_RANGE_THUMB: {
2890 if (IsRangeHorizontal(aFrame)) {
2891 (*aResult).width = 12;
2892 (*aResult).height = 20;
2893 } else {
2894 (*aResult).width = 20;
2895 (*aResult).height = 12;
2896 }
2897 *aIsOverridable = false;
2898 break;
2899 }
2900 case NS_THEME_SCALE_THUMB_HORIZONTAL:
2901 (*aResult).width = 12;
2902 (*aResult).height = 20;
2903 *aIsOverridable = false;
2904 break;
2905 case NS_THEME_SCALE_THUMB_VERTICAL:
2906 (*aResult).width = 20;
2907 (*aResult).height = 12;
2908 *aIsOverridable = false;
2909 break;
2910 case NS_THEME_DROPDOWN_BUTTON:
2911 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2912 break;
2913 case NS_THEME_DROPDOWN:
2914 case NS_THEME_BUTTON:
2915 case NS_THEME_GROUPBOX:
2916 case NS_THEME_LISTBOX:
2917 case NS_THEME_TREEVIEW:
2918 case NS_THEME_NUMBER_INPUT:
2919 case NS_THEME_TEXTFIELD:
2920 case NS_THEME_TEXTFIELD_MULTILINE:
2921 case NS_THEME_DROPDOWN_TEXTFIELD:
2922 case NS_THEME_STATUSBAR:
2923 case NS_THEME_STATUSBAR_PANEL:
2924 case NS_THEME_STATUSBAR_RESIZER_PANEL:
2925 case NS_THEME_PROGRESSBAR_CHUNK:
2926 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2927 case NS_THEME_TOOLTIP:
2928 case NS_THEME_PROGRESSBAR:
2929 case NS_THEME_PROGRESSBAR_VERTICAL:
2930 case NS_THEME_TAB:
2931 case NS_THEME_TAB_PANEL:
2932 case NS_THEME_TAB_PANELS:
2933 // no minimum widget size
2934 break;
2935 case NS_THEME_RESIZER: {
2936 NONCLIENTMETRICS nc;
2937 nc.cbSize = sizeof(nc);
2938 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nc), &nc, 0))
2939 (*aResult).width = (*aResult).height = abs(nc.lfStatusFont.lfHeight) + 4;
2940 else
2941 (*aResult).width = (*aResult).height = 15;
2942 *aIsOverridable = false;
2943 break;
2944 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2945 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2946 (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB);
2947 // Without theming, divide the thumb size by two in order to look more
2948 // native
2949 if (!GetTheme(aWidgetType))
2950 (*aResult).height >>= 1;
2951 *aIsOverridable = false;
2952 break;
2953 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2954 (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB);
2955 (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
2956 // Without theming, divide the thumb size by two in order to look more
2957 // native
2958 if (!GetTheme(aWidgetType))
2959 (*aResult).width >>= 1;
2960 *aIsOverridable = false;
2961 break;
2962 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2963 (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB) << 1;
2964 break;
2965 }
2966 case NS_THEME_MENUSEPARATOR:
2967 {
2968 aResult->width = 0;
2969 aResult->height = 10;
2970 break;
2971 }
2973 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2974 case NS_THEME_WINDOW_TITLEBAR:
2975 aResult->height = GetSystemMetrics(SM_CYCAPTION);
2976 aResult->height += GetSystemMetrics(SM_CYFRAME);
2977 aResult->width = 0;
2978 break;
2979 case NS_THEME_WINDOW_FRAME_LEFT:
2980 case NS_THEME_WINDOW_FRAME_RIGHT:
2981 aResult->width = GetSystemMetrics(SM_CXFRAME);
2982 aResult->height = 0;
2983 break;
2985 case NS_THEME_WINDOW_FRAME_BOTTOM:
2986 aResult->height = GetSystemMetrics(SM_CYFRAME);
2987 aResult->width = 0;
2988 break;
2990 case NS_THEME_WINDOW_BUTTON_CLOSE:
2991 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2992 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2993 case NS_THEME_WINDOW_BUTTON_RESTORE:
2994 aResult->width = GetSystemMetrics(SM_CXSIZE);
2995 aResult->height = GetSystemMetrics(SM_CYSIZE);
2996 // XXX I have no idea why these caption metrics are always off,
2997 // but they are.
2998 aResult->width -= 2;
2999 aResult->height -= 4;
3000 if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
3001 AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
3002 }
3003 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
3004 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
3005 AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
3006 }
3007 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
3008 AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
3009 }
3010 break;
3012 default:
3013 return NS_ERROR_FAILURE;
3014 }
3015 return NS_OK;
3016 }
3019 nsresult nsNativeThemeWin::ClassicGetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
3020 int32_t& aPart, int32_t& aState, bool& aFocused)
3021 {
3022 aFocused = false;
3023 switch (aWidgetType) {
3024 case NS_THEME_BUTTON: {
3025 EventStates contentState;
3027 aPart = DFC_BUTTON;
3028 aState = DFCS_BUTTONPUSH;
3029 aFocused = false;
3031 contentState = GetContentState(aFrame, aWidgetType);
3032 if (IsDisabled(aFrame, contentState))
3033 aState |= DFCS_INACTIVE;
3034 else if (IsOpenButton(aFrame))
3035 aState |= DFCS_PUSHED;
3036 else if (IsCheckedButton(aFrame))
3037 aState |= DFCS_CHECKED;
3038 else {
3039 if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) {
3040 aState |= DFCS_PUSHED;
3041 const nsStyleUserInterface *uiData = aFrame->StyleUserInterface();
3042 // The down state is flat if the button is focusable
3043 if (uiData->mUserFocus == NS_STYLE_USER_FOCUS_NORMAL) {
3044 if (!aFrame->GetContent()->IsHTML())
3045 aState |= DFCS_FLAT;
3047 aFocused = true;
3048 }
3049 }
3050 if (contentState.HasState(NS_EVENT_STATE_FOCUS) ||
3051 (aState == DFCS_BUTTONPUSH && IsDefaultButton(aFrame))) {
3052 aFocused = true;
3053 }
3055 }
3057 return NS_OK;
3058 }
3059 case NS_THEME_CHECKBOX:
3060 case NS_THEME_RADIO: {
3061 EventStates contentState;
3062 aFocused = false;
3064 aPart = DFC_BUTTON;
3065 aState = 0;
3066 nsIContent* content = aFrame->GetContent();
3067 bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
3068 bool isChecked = GetCheckedOrSelected(aFrame, !isCheckbox);
3069 bool isIndeterminate = isCheckbox && GetIndeterminate(aFrame);
3071 if (isCheckbox) {
3072 // indeterminate state takes precedence over checkedness.
3073 if (isIndeterminate) {
3074 aState = DFCS_BUTTON3STATE | DFCS_CHECKED;
3075 } else {
3076 aState = DFCS_BUTTONCHECK;
3077 }
3078 } else {
3079 aState = DFCS_BUTTONRADIO;
3080 }
3081 if (isChecked) {
3082 aState |= DFCS_CHECKED;
3083 }
3085 contentState = GetContentState(aFrame, aWidgetType);
3086 if (!content->IsXUL() &&
3087 contentState.HasState(NS_EVENT_STATE_FOCUS)) {
3088 aFocused = true;
3089 }
3091 if (IsDisabled(aFrame, contentState)) {
3092 aState |= DFCS_INACTIVE;
3093 } else if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE |
3094 NS_EVENT_STATE_HOVER)) {
3095 aState |= DFCS_PUSHED;
3096 }
3098 return NS_OK;
3099 }
3100 case NS_THEME_MENUITEM:
3101 case NS_THEME_CHECKMENUITEM:
3102 case NS_THEME_RADIOMENUITEM: {
3103 bool isTopLevel = false;
3104 bool isOpen = false;
3105 bool isContainer = false;
3106 nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
3107 EventStates eventState = GetContentState(aFrame, aWidgetType);
3109 // We indicate top-level-ness using aPart. 0 is a normal menu item,
3110 // 1 is a top-level menu item. The state of the item is composed of
3111 // DFCS_* flags only.
3112 aPart = 0;
3113 aState = 0;
3115 if (menuFrame) {
3116 // If this is a real menu item, we should check if it is part of the
3117 // main menu bar or not, and if it is a container, as these affect
3118 // rendering.
3119 isTopLevel = menuFrame->IsOnMenuBar();
3120 isOpen = menuFrame->IsOpen();
3121 isContainer = menuFrame->IsMenu();
3122 }
3124 if (IsDisabled(aFrame, eventState))
3125 aState |= DFCS_INACTIVE;
3127 if (isTopLevel) {
3128 aPart = 1;
3129 if (isOpen)
3130 aState |= DFCS_PUSHED;
3131 }
3133 if (IsMenuActive(aFrame, aWidgetType))
3134 aState |= DFCS_HOT;
3136 return NS_OK;
3137 }
3138 case NS_THEME_MENUCHECKBOX:
3139 case NS_THEME_MENURADIO:
3140 case NS_THEME_MENUARROW: {
3141 aState = 0;
3142 EventStates eventState = GetContentState(aFrame, aWidgetType);
3144 if (IsDisabled(aFrame, eventState))
3145 aState |= DFCS_INACTIVE;
3146 if (IsMenuActive(aFrame, aWidgetType))
3147 aState |= DFCS_HOT;
3149 if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO) {
3150 if (IsCheckedButton(aFrame))
3151 aState |= DFCS_CHECKED;
3152 } else if (IsFrameRTL(aFrame)) {
3153 aState |= DFCS_RTL;
3154 }
3155 return NS_OK;
3156 }
3157 case NS_THEME_LISTBOX:
3158 case NS_THEME_TREEVIEW:
3159 case NS_THEME_NUMBER_INPUT:
3160 case NS_THEME_TEXTFIELD:
3161 case NS_THEME_TEXTFIELD_MULTILINE:
3162 case NS_THEME_DROPDOWN:
3163 case NS_THEME_DROPDOWN_TEXTFIELD:
3164 case NS_THEME_RANGE:
3165 case NS_THEME_RANGE_THUMB:
3166 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
3167 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
3168 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
3169 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
3170 case NS_THEME_SCALE_HORIZONTAL:
3171 case NS_THEME_SCALE_VERTICAL:
3172 case NS_THEME_SCALE_THUMB_HORIZONTAL:
3173 case NS_THEME_SCALE_THUMB_VERTICAL:
3174 case NS_THEME_STATUSBAR:
3175 case NS_THEME_STATUSBAR_PANEL:
3176 case NS_THEME_STATUSBAR_RESIZER_PANEL:
3177 case NS_THEME_PROGRESSBAR_CHUNK:
3178 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
3179 case NS_THEME_TOOLTIP:
3180 case NS_THEME_PROGRESSBAR:
3181 case NS_THEME_PROGRESSBAR_VERTICAL:
3182 case NS_THEME_TAB:
3183 case NS_THEME_TAB_PANEL:
3184 case NS_THEME_TAB_PANELS:
3185 case NS_THEME_MENUBAR:
3186 case NS_THEME_MENUPOPUP:
3187 case NS_THEME_GROUPBOX:
3188 // these don't use DrawFrameControl
3189 return NS_OK;
3190 case NS_THEME_DROPDOWN_BUTTON: {
3192 aPart = DFC_SCROLL;
3193 aState = DFCS_SCROLLCOMBOBOX;
3195 nsIFrame* parentFrame = aFrame->GetParent();
3196 bool isHTML = IsHTMLContent(aFrame);
3197 bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
3198 bool isOpen = false;
3200 // HTML select and XUL menulist dropdown buttons get state from the parent.
3201 if (isHTML || isMenulist)
3202 aFrame = parentFrame;
3204 EventStates eventState = GetContentState(aFrame, aWidgetType);
3206 if (IsDisabled(aFrame, eventState)) {
3207 aState |= DFCS_INACTIVE;
3208 return NS_OK;
3209 }
3211 if (isHTML) {
3212 nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
3213 isOpen = (ccf && ccf->IsDroppedDown());
3214 }
3215 else
3216 isOpen = IsOpenButton(aFrame);
3218 // XXX Button should look active until the mouse is released, but
3219 // without making it look active when the popup is clicked.
3220 if (isOpen && (isHTML || isMenulist))
3221 return NS_OK;
3223 // Dropdown button active state doesn't need :hover.
3224 if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
3225 aState |= DFCS_PUSHED | DFCS_FLAT;
3227 return NS_OK;
3228 }
3229 case NS_THEME_SCROLLBAR_BUTTON_UP:
3230 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3231 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3232 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: {
3233 EventStates contentState = GetContentState(aFrame, aWidgetType);
3235 aPart = DFC_SCROLL;
3236 switch (aWidgetType) {
3237 case NS_THEME_SCROLLBAR_BUTTON_UP:
3238 aState = DFCS_SCROLLUP;
3239 break;
3240 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3241 aState = DFCS_SCROLLDOWN;
3242 break;
3243 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3244 aState = DFCS_SCROLLLEFT;
3245 break;
3246 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
3247 aState = DFCS_SCROLLRIGHT;
3248 break;
3249 }
3251 if (IsDisabled(aFrame, contentState))
3252 aState |= DFCS_INACTIVE;
3253 else {
3254 if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
3255 aState |= DFCS_PUSHED | DFCS_FLAT;
3256 }
3258 return NS_OK;
3259 }
3260 case NS_THEME_SPINNER_UP_BUTTON:
3261 case NS_THEME_SPINNER_DOWN_BUTTON: {
3262 EventStates contentState = GetContentState(aFrame, aWidgetType);
3264 aPart = DFC_SCROLL;
3265 switch (aWidgetType) {
3266 case NS_THEME_SPINNER_UP_BUTTON:
3267 aState = DFCS_SCROLLUP;
3268 break;
3269 case NS_THEME_SPINNER_DOWN_BUTTON:
3270 aState = DFCS_SCROLLDOWN;
3271 break;
3272 }
3274 if (IsDisabled(aFrame, contentState))
3275 aState |= DFCS_INACTIVE;
3276 else {
3277 if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
3278 aState |= DFCS_PUSHED;
3279 }
3281 return NS_OK;
3282 }
3283 case NS_THEME_RESIZER:
3284 aPart = DFC_SCROLL;
3285 aState = (IsFrameRTL(aFrame)) ?
3286 DFCS_SCROLLSIZEGRIPRIGHT : DFCS_SCROLLSIZEGRIP;
3287 return NS_OK;
3288 case NS_THEME_MENUSEPARATOR:
3289 aPart = 0;
3290 aState = 0;
3291 return NS_OK;
3292 case NS_THEME_WINDOW_TITLEBAR:
3293 aPart = mozilla::widget::themeconst::WP_CAPTION;
3294 aState = GetTopLevelWindowActiveState(aFrame);
3295 return NS_OK;
3296 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
3297 aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
3298 aState = GetTopLevelWindowActiveState(aFrame);
3299 return NS_OK;
3300 case NS_THEME_WINDOW_FRAME_LEFT:
3301 aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
3302 aState = GetTopLevelWindowActiveState(aFrame);
3303 return NS_OK;
3304 case NS_THEME_WINDOW_FRAME_RIGHT:
3305 aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
3306 aState = GetTopLevelWindowActiveState(aFrame);
3307 return NS_OK;
3308 case NS_THEME_WINDOW_FRAME_BOTTOM:
3309 aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
3310 aState = GetTopLevelWindowActiveState(aFrame);
3311 return NS_OK;
3312 case NS_THEME_WINDOW_BUTTON_CLOSE:
3313 aPart = DFC_CAPTION;
3314 aState = DFCS_CAPTIONCLOSE |
3315 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3316 aWidgetType));
3317 return NS_OK;
3318 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
3319 aPart = DFC_CAPTION;
3320 aState = DFCS_CAPTIONMIN |
3321 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3322 aWidgetType));
3323 return NS_OK;
3324 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
3325 aPart = DFC_CAPTION;
3326 aState = DFCS_CAPTIONMAX |
3327 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3328 aWidgetType));
3329 return NS_OK;
3330 case NS_THEME_WINDOW_BUTTON_RESTORE:
3331 aPart = DFC_CAPTION;
3332 aState = DFCS_CAPTIONRESTORE |
3333 GetClassicWindowFrameButtonState(GetContentState(aFrame,
3334 aWidgetType));
3335 return NS_OK;
3336 }
3337 return NS_ERROR_FAILURE;
3338 }
3340 // Draw classic Windows tab
3341 // (no system API for this, but DrawEdge can draw all the parts of a tab)
3342 static void DrawTab(HDC hdc, const RECT& R, int32_t aPosition, bool aSelected,
3343 bool aDrawLeft, bool aDrawRight)
3344 {
3345 int32_t leftFlag, topFlag, rightFlag, lightFlag, shadeFlag;
3346 RECT topRect, sideRect, bottomRect, lightRect, shadeRect;
3347 int32_t selectedOffset, lOffset, rOffset;
3349 selectedOffset = aSelected ? 1 : 0;
3350 lOffset = aDrawLeft ? 2 : 0;
3351 rOffset = aDrawRight ? 2 : 0;
3353 // Get info for tab orientation/position (Left, Top, Right, Bottom)
3354 switch (aPosition) {
3355 case BF_LEFT:
3356 leftFlag = BF_TOP; topFlag = BF_LEFT;
3357 rightFlag = BF_BOTTOM;
3358 lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
3359 shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
3361 ::SetRect(&topRect, R.left, R.top+lOffset, R.right, R.bottom-rOffset);
3362 ::SetRect(&sideRect, R.left+2, R.top, R.right-2+selectedOffset, R.bottom);
3363 ::SetRect(&bottomRect, R.right-2, R.top, R.right, R.bottom);
3364 ::SetRect(&lightRect, R.left, R.top, R.left+3, R.top+3);
3365 ::SetRect(&shadeRect, R.left+1, R.bottom-2, R.left+2, R.bottom-1);
3366 break;
3367 case BF_TOP:
3368 leftFlag = BF_LEFT; topFlag = BF_TOP;
3369 rightFlag = BF_RIGHT;
3370 lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
3371 shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
3373 ::SetRect(&topRect, R.left+lOffset, R.top, R.right-rOffset, R.bottom);
3374 ::SetRect(&sideRect, R.left, R.top+2, R.right, R.bottom-1+selectedOffset);
3375 ::SetRect(&bottomRect, R.left, R.bottom-1, R.right, R.bottom);
3376 ::SetRect(&lightRect, R.left, R.top, R.left+3, R.top+3);
3377 ::SetRect(&shadeRect, R.right-2, R.top+1, R.right-1, R.top+2);
3378 break;
3379 case BF_RIGHT:
3380 leftFlag = BF_TOP; topFlag = BF_RIGHT;
3381 rightFlag = BF_BOTTOM;
3382 lightFlag = BF_DIAGONAL_ENDTOPLEFT;
3383 shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
3385 ::SetRect(&topRect, R.left, R.top+lOffset, R.right, R.bottom-rOffset);
3386 ::SetRect(&sideRect, R.left+2-selectedOffset, R.top, R.right-2, R.bottom);
3387 ::SetRect(&bottomRect, R.left, R.top, R.left+2, R.bottom);
3388 ::SetRect(&lightRect, R.right-3, R.top, R.right-1, R.top+2);
3389 ::SetRect(&shadeRect, R.right-2, R.bottom-3, R.right, R.bottom-1);
3390 break;
3391 case BF_BOTTOM:
3392 leftFlag = BF_LEFT; topFlag = BF_BOTTOM;
3393 rightFlag = BF_RIGHT;
3394 lightFlag = BF_DIAGONAL_ENDTOPLEFT;
3395 shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
3397 ::SetRect(&topRect, R.left+lOffset, R.top, R.right-rOffset, R.bottom);
3398 ::SetRect(&sideRect, R.left, R.top+2-selectedOffset, R.right, R.bottom-2);
3399 ::SetRect(&bottomRect, R.left, R.top, R.right, R.top+2);
3400 ::SetRect(&lightRect, R.left, R.bottom-3, R.left+2, R.bottom-1);
3401 ::SetRect(&shadeRect, R.right-2, R.bottom-3, R.right, R.bottom-1);
3402 break;
3403 }
3405 // Background
3406 ::FillRect(hdc, &R, (HBRUSH) (COLOR_3DFACE+1) );
3408 // Tab "Top"
3409 ::DrawEdge(hdc, &topRect, EDGE_RAISED, BF_SOFT | topFlag);
3411 // Tab "Bottom"
3412 if (!aSelected)
3413 ::DrawEdge(hdc, &bottomRect, EDGE_RAISED, BF_SOFT | topFlag);
3415 // Tab "Sides"
3416 if (!aDrawLeft)
3417 leftFlag = 0;
3418 if (!aDrawRight)
3419 rightFlag = 0;
3420 ::DrawEdge(hdc, &sideRect, EDGE_RAISED, BF_SOFT | leftFlag | rightFlag);
3422 // Tab Diagonal Corners
3423 if (aDrawLeft)
3424 ::DrawEdge(hdc, &lightRect, EDGE_RAISED, BF_SOFT | lightFlag);
3426 if (aDrawRight)
3427 ::DrawEdge(hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag);
3428 }
3430 static void DrawMenuImage(HDC hdc, const RECT& rc, int32_t aComponent, uint32_t aColor)
3431 {
3432 // This procedure creates a memory bitmap to contain the check mark, draws
3433 // it into the bitmap (it is a mask image), then composes it onto the menu
3434 // item in appropriate colors.
3435 HDC hMemoryDC = ::CreateCompatibleDC(hdc);
3436 if (hMemoryDC) {
3437 // XXXjgr We should ideally be caching these, but we wont be notified when
3438 // they change currently, so we can't do so easily. Same for the bitmap.
3439 int checkW = ::GetSystemMetrics(SM_CXMENUCHECK);
3440 int checkH = ::GetSystemMetrics(SM_CYMENUCHECK);
3442 HBITMAP hMonoBitmap = ::CreateBitmap(checkW, checkH, 1, 1, nullptr);
3443 if (hMonoBitmap) {
3445 HBITMAP hPrevBitmap = (HBITMAP) ::SelectObject(hMemoryDC, hMonoBitmap);
3446 if (hPrevBitmap) {
3448 // XXXjgr This will go pear-shaped if the image is bigger than the
3449 // provided rect. What should we do?
3450 RECT imgRect = { 0, 0, checkW, checkH };
3451 POINT imgPos = {
3452 rc.left + (rc.right - rc.left - checkW) / 2,
3453 rc.top + (rc.bottom - rc.top - checkH) / 2
3454 };
3456 // XXXzeniko Windows renders these 1px lower than you'd expect
3457 if (aComponent == DFCS_MENUCHECK || aComponent == DFCS_MENUBULLET)
3458 imgPos.y++;
3460 ::DrawFrameControl(hMemoryDC, &imgRect, DFC_MENU, aComponent);
3461 COLORREF oldTextCol = ::SetTextColor(hdc, 0x00000000);
3462 COLORREF oldBackCol = ::SetBkColor(hdc, 0x00FFFFFF);
3463 ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCAND);
3464 ::SetTextColor(hdc, ::GetSysColor(aColor));
3465 ::SetBkColor(hdc, 0x00000000);
3466 ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCPAINT);
3467 ::SetTextColor(hdc, oldTextCol);
3468 ::SetBkColor(hdc, oldBackCol);
3469 ::SelectObject(hMemoryDC, hPrevBitmap);
3470 }
3471 ::DeleteObject(hMonoBitmap);
3472 }
3473 ::DeleteDC(hMemoryDC);
3474 }
3475 }
3477 void nsNativeThemeWin::DrawCheckedRect(HDC hdc, const RECT& rc, int32_t fore, int32_t back,
3478 HBRUSH defaultBack)
3479 {
3480 static WORD patBits[8] = {
3481 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
3482 };
3484 HBITMAP patBmp = ::CreateBitmap(8, 8, 1, 1, patBits);
3485 if (patBmp) {
3486 HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patBmp);
3487 if (brush) {
3488 COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(fore));
3489 COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(back));
3490 POINT vpOrg;
3492 ::UnrealizeObject(brush);
3493 ::GetViewportOrgEx(hdc, &vpOrg);
3494 ::SetBrushOrgEx(hdc, vpOrg.x + rc.left, vpOrg.y + rc.top, nullptr);
3495 HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush);
3496 ::FillRect(hdc, &rc, brush);
3497 ::SetTextColor(hdc, oldForeColor);
3498 ::SetBkColor(hdc, oldBackColor);
3499 ::SelectObject(hdc, oldBrush);
3500 ::DeleteObject(brush);
3501 }
3502 else
3503 ::FillRect(hdc, &rc, defaultBack);
3505 ::DeleteObject(patBmp);
3506 }
3507 }
3509 nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(nsRenderingContext* aContext,
3510 nsIFrame* aFrame,
3511 uint8_t aWidgetType,
3512 const nsRect& aRect,
3513 const nsRect& aDirtyRect)
3514 {
3515 int32_t part, state;
3516 bool focused;
3517 nsresult rv;
3518 rv = ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused);
3519 if (NS_FAILED(rv))
3520 return rv;
3522 if (AssumeThemePartAndStateAreTransparent(part, state)) {
3523 return NS_OK;
3524 }
3526 gfxFloat p2a = gfxFloat(aContext->AppUnitsPerDevPixel());
3527 RECT widgetRect;
3528 gfxRect tr(aRect.x, aRect.y, aRect.width, aRect.height),
3529 dr(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
3531 tr.ScaleInverse(p2a);
3532 dr.ScaleInverse(p2a);
3534 nsRefPtr<gfxContext> ctx = aContext->ThebesContext();
3536 gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
3538 RENDER_AGAIN:
3540 HDC hdc = nativeDrawing.BeginNativeDrawing();
3541 if (!hdc)
3542 return NS_ERROR_FAILURE;
3544 nativeDrawing.TransformToNativeRect(tr, widgetRect);
3546 rv = NS_OK;
3547 switch (aWidgetType) {
3548 // Draw button
3549 case NS_THEME_BUTTON: {
3550 if (focused) {
3551 // draw dark button focus border first
3552 HBRUSH brush;
3553 brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
3554 if (brush)
3555 ::FrameRect(hdc, &widgetRect, brush);
3556 InflateRect(&widgetRect, -1, -1);
3557 }
3558 // fall-through...
3559 }
3560 // Draw controls supported by DrawFrameControl
3561 case NS_THEME_CHECKBOX:
3562 case NS_THEME_RADIO:
3563 case NS_THEME_SCROLLBAR_BUTTON_UP:
3564 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3565 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3566 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
3567 case NS_THEME_SPINNER_UP_BUTTON:
3568 case NS_THEME_SPINNER_DOWN_BUTTON:
3569 case NS_THEME_DROPDOWN_BUTTON:
3570 case NS_THEME_RESIZER: {
3571 int32_t oldTA;
3572 // setup DC to make DrawFrameControl draw correctly
3573 oldTA = ::SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
3574 ::DrawFrameControl(hdc, &widgetRect, part, state);
3575 ::SetTextAlign(hdc, oldTA);
3577 // Draw focus rectangles for HTML checkboxes and radio buttons
3578 // XXX it'd be nice to draw these outside of the frame
3579 if (focused && (aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_RADIO)) {
3580 // setup DC to make DrawFocusRect draw correctly
3581 POINT vpOrg;
3582 ::GetViewportOrgEx(hdc, &vpOrg);
3583 ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, nullptr);
3584 int32_t oldColor;
3585 oldColor = ::SetTextColor(hdc, 0);
3586 // draw focus rectangle
3587 ::DrawFocusRect(hdc, &widgetRect);
3588 ::SetTextColor(hdc, oldColor);
3589 }
3590 break;
3591 }
3592 // Draw controls with 2px 3D inset border
3593 case NS_THEME_NUMBER_INPUT:
3594 case NS_THEME_TEXTFIELD:
3595 case NS_THEME_TEXTFIELD_MULTILINE:
3596 case NS_THEME_LISTBOX:
3597 case NS_THEME_DROPDOWN:
3598 case NS_THEME_DROPDOWN_TEXTFIELD: {
3599 // Draw inset edge
3600 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3601 EventStates eventState = GetContentState(aFrame, aWidgetType);
3603 // Fill in background
3604 if (IsDisabled(aFrame, eventState) ||
3605 (aFrame->GetContent()->IsXUL() &&
3606 IsReadOnly(aFrame)))
3607 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
3608 else
3609 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
3611 break;
3612 }
3613 case NS_THEME_TREEVIEW: {
3614 // Draw inset edge
3615 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3617 // Fill in window color background
3618 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
3620 break;
3621 }
3622 // Draw ToolTip background
3623 case NS_THEME_TOOLTIP:
3624 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_WINDOWFRAME));
3625 InflateRect(&widgetRect, -1, -1);
3626 ::FillRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_INFOBK));
3628 break;
3629 case NS_THEME_GROUPBOX:
3630 ::DrawEdge(hdc, &widgetRect, EDGE_ETCHED, BF_RECT | BF_ADJUST);
3631 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
3632 break;
3633 // Draw 3D face background controls
3634 case NS_THEME_PROGRESSBAR:
3635 case NS_THEME_PROGRESSBAR_VERTICAL:
3636 // Draw 3D border
3637 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
3638 InflateRect(&widgetRect, -1, -1);
3639 // fall through
3640 case NS_THEME_TAB_PANEL:
3641 case NS_THEME_STATUSBAR:
3642 case NS_THEME_STATUSBAR_RESIZER_PANEL: {
3643 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
3645 break;
3646 }
3647 // Draw 3D inset statusbar panel
3648 case NS_THEME_STATUSBAR_PANEL: {
3649 if (aFrame->GetNextSibling())
3650 widgetRect.right -= 2; // space between sibling status panels
3652 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
3654 break;
3655 }
3656 // Draw scrollbar thumb
3657 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
3658 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
3659 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
3661 break;
3662 case NS_THEME_RANGE_THUMB:
3663 case NS_THEME_SCALE_THUMB_VERTICAL:
3664 case NS_THEME_SCALE_THUMB_HORIZONTAL: {
3665 EventStates eventState = GetContentState(aFrame, aWidgetType);
3667 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
3668 if (IsDisabled(aFrame, eventState)) {
3669 DrawCheckedRect(hdc, widgetRect, COLOR_3DFACE, COLOR_3DHILIGHT,
3670 (HBRUSH) COLOR_3DHILIGHT);
3671 }
3673 break;
3674 }
3675 // Draw scrollbar track background
3676 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
3677 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: {
3679 // Windows fills in the scrollbar track differently
3680 // depending on whether these are equal
3681 DWORD color3D, colorScrollbar, colorWindow;
3683 color3D = ::GetSysColor(COLOR_3DFACE);
3684 colorWindow = ::GetSysColor(COLOR_WINDOW);
3685 colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR);
3687 if ((color3D != colorScrollbar) && (colorWindow != colorScrollbar))
3688 // Use solid brush
3689 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_SCROLLBAR+1));
3690 else
3691 {
3692 DrawCheckedRect(hdc, widgetRect, COLOR_3DHILIGHT, COLOR_3DFACE,
3693 (HBRUSH) COLOR_SCROLLBAR+1);
3694 }
3695 // XXX should invert the part of the track being clicked here
3696 // but the track is never :active
3698 break;
3699 }
3700 // Draw scale track background
3701 case NS_THEME_RANGE:
3702 case NS_THEME_SCALE_VERTICAL:
3703 case NS_THEME_SCALE_HORIZONTAL: {
3704 const int32_t trackWidth = 4;
3705 // When rounding is necessary, we round the position of the track
3706 // away from the chevron of the thumb to make it look better.
3707 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
3708 (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
3709 widgetRect.top += (widgetRect.bottom - widgetRect.top - trackWidth) / 2;
3710 widgetRect.bottom = widgetRect.top + trackWidth;
3711 }
3712 else {
3713 if (!IsFrameRTL(aFrame)) {
3714 widgetRect.left += (widgetRect.right - widgetRect.left - trackWidth) / 2;
3715 widgetRect.right = widgetRect.left + trackWidth;
3716 } else {
3717 widgetRect.right -= (widgetRect.right - widgetRect.left - trackWidth) / 2;
3718 widgetRect.left = widgetRect.right - trackWidth;
3719 }
3720 }
3722 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3723 ::FillRect(hdc, &widgetRect, (HBRUSH) GetStockObject(GRAY_BRUSH));
3725 break;
3726 }
3727 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
3728 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3729 break;
3731 case NS_THEME_PROGRESSBAR_CHUNK: {
3732 nsIFrame* stateFrame = aFrame->GetParent();
3733 EventStates eventStates = GetContentState(stateFrame, aWidgetType);
3735 bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates);
3736 bool vertical = IsVerticalProgress(stateFrame) ||
3737 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL;
3738 int32_t overlayPart = GetProgressOverlayStyle(vertical);
3740 nsIContent* content = aFrame->GetContent();
3741 if (!indeterminate || !content) {
3742 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3743 break;
3744 }
3746 RECT overlayRect =
3747 CalculateProgressOverlayRect(aFrame, &widgetRect, vertical,
3748 indeterminate, true);
3750 ::FillRect(hdc, &overlayRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3752 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
3753 NS_WARNING("unable to animate progress widget!");
3754 }
3755 break;
3756 }
3758 // Draw Tab
3759 case NS_THEME_TAB: {
3760 DrawTab(hdc, widgetRect,
3761 IsBottomTab(aFrame) ? BF_BOTTOM : BF_TOP,
3762 IsSelectedTab(aFrame),
3763 !IsRightToSelectedTab(aFrame),
3764 !IsLeftToSelectedTab(aFrame));
3766 break;
3767 }
3768 case NS_THEME_TAB_PANELS:
3769 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_SOFT | BF_MIDDLE |
3770 BF_LEFT | BF_RIGHT | BF_BOTTOM);
3772 break;
3773 case NS_THEME_MENUBAR:
3774 break;
3775 case NS_THEME_MENUPOPUP:
3776 NS_ASSERTION(nsUXThemeData::sFlatMenus, "Classic menus are styled entirely through CSS");
3777 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
3778 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
3779 break;
3780 case NS_THEME_MENUITEM:
3781 case NS_THEME_CHECKMENUITEM:
3782 case NS_THEME_RADIOMENUITEM:
3783 // part == 0 for normal items
3784 // part == 1 for top-level menu items
3785 if (nsUXThemeData::sFlatMenus) {
3786 // Not disabled and hot/pushed.
3787 if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
3788 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENUHILIGHT+1));
3789 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_HIGHLIGHT));
3790 }
3791 } else {
3792 if (part == 1) {
3793 if ((state & DFCS_INACTIVE) == 0) {
3794 if ((state & DFCS_PUSHED) != 0) {
3795 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT);
3796 } else if ((state & DFCS_HOT) != 0) {
3797 ::DrawEdge(hdc, &widgetRect, BDR_RAISEDINNER, BF_RECT);
3798 }
3799 }
3800 } else {
3801 if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
3802 ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
3803 }
3804 }
3805 }
3806 break;
3807 case NS_THEME_MENUCHECKBOX:
3808 case NS_THEME_MENURADIO:
3809 if (!(state & DFCS_CHECKED))
3810 break; // nothin' to do
3811 case NS_THEME_MENUARROW: {
3812 uint32_t color = COLOR_MENUTEXT;
3813 if ((state & DFCS_INACTIVE))
3814 color = COLOR_GRAYTEXT;
3815 else if ((state & DFCS_HOT))
3816 color = COLOR_HIGHLIGHTTEXT;
3818 if (aWidgetType == NS_THEME_MENUCHECKBOX)
3819 DrawMenuImage(hdc, widgetRect, DFCS_MENUCHECK, color);
3820 else if (aWidgetType == NS_THEME_MENURADIO)
3821 DrawMenuImage(hdc, widgetRect, DFCS_MENUBULLET, color);
3822 else if (aWidgetType == NS_THEME_MENUARROW)
3823 DrawMenuImage(hdc, widgetRect,
3824 (state & DFCS_RTL) ? DFCS_MENUARROWRIGHT : DFCS_MENUARROW,
3825 color);
3826 break;
3827 }
3828 case NS_THEME_MENUSEPARATOR: {
3829 // separators are offset by a bit (see menu.css)
3830 widgetRect.left++;
3831 widgetRect.right--;
3833 // This magic number is brought to you by the value in menu.css
3834 widgetRect.top += 4;
3835 // Our rectangles are 1 pixel high (see border size in menu.css)
3836 widgetRect.bottom = widgetRect.top+1;
3837 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DSHADOW+1));
3838 widgetRect.top++;
3839 widgetRect.bottom++;
3840 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DHILIGHT+1));
3841 break;
3842 }
3844 case NS_THEME_WINDOW_TITLEBAR:
3845 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
3846 {
3847 RECT rect = widgetRect;
3848 int32_t offset = GetSystemMetrics(SM_CXFRAME);
3849 rect.bottom -= 1;
3851 // first fill the area to the color of the window background
3852 FillRect(hdc, &rect, (HBRUSH)(COLOR_3DFACE+1));
3854 // inset the caption area so it doesn't overflow.
3855 rect.top += offset;
3856 // if enabled, draw a gradient titlebar background, otherwise
3857 // fill with a solid color.
3858 BOOL bFlag = TRUE;
3859 SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &bFlag, 0);
3860 if (!bFlag) {
3861 if (state == mozilla::widget::themeconst::FS_ACTIVE)
3862 FillRect(hdc, &rect, (HBRUSH)(COLOR_ACTIVECAPTION+1));
3863 else
3864 FillRect(hdc, &rect, (HBRUSH)(COLOR_INACTIVECAPTION+1));
3865 } else {
3866 DWORD startColor, endColor;
3867 if (state == mozilla::widget::themeconst::FS_ACTIVE) {
3868 startColor = GetSysColor(COLOR_ACTIVECAPTION);
3869 endColor = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
3870 } else {
3871 startColor = GetSysColor(COLOR_INACTIVECAPTION);
3872 endColor = GetSysColor(COLOR_GRADIENTINACTIVECAPTION);
3873 }
3875 TRIVERTEX vertex[2];
3876 vertex[0].x = rect.left;
3877 vertex[0].y = rect.top;
3878 vertex[0].Red = GetRValue(startColor) << 8;
3879 vertex[0].Green = GetGValue(startColor) << 8;
3880 vertex[0].Blue = GetBValue(startColor) << 8;
3881 vertex[0].Alpha = 0;
3883 vertex[1].x = rect.right;
3884 vertex[1].y = rect.bottom;
3885 vertex[1].Red = GetRValue(endColor) << 8;
3886 vertex[1].Green = GetGValue(endColor) << 8;
3887 vertex[1].Blue = GetBValue(endColor) << 8;
3888 vertex[1].Alpha = 0;
3890 GRADIENT_RECT gRect;
3891 gRect.UpperLeft = 0;
3892 gRect.LowerRight = 1;
3893 // available on win2k & up
3894 GradientFill(hdc, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H);
3895 }
3897 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
3898 // frame things up with a top raised border.
3899 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_TOP);
3900 }
3901 break;
3902 }
3904 case NS_THEME_WINDOW_FRAME_LEFT:
3905 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_LEFT);
3906 break;
3908 case NS_THEME_WINDOW_FRAME_RIGHT:
3909 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RIGHT);
3910 break;
3912 case NS_THEME_WINDOW_FRAME_BOTTOM:
3913 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_BOTTOM);
3914 break;
3916 case NS_THEME_WINDOW_BUTTON_CLOSE:
3917 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
3918 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
3919 case NS_THEME_WINDOW_BUTTON_RESTORE:
3920 {
3921 if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
3922 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
3923 }
3924 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
3925 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
3926 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
3927 }
3928 else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
3929 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
3930 }
3931 int32_t oldTA = SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
3932 DrawFrameControl(hdc, &widgetRect, part, state);
3933 SetTextAlign(hdc, oldTA);
3934 break;
3935 }
3937 default:
3938 rv = NS_ERROR_FAILURE;
3939 break;
3940 }
3942 nativeDrawing.EndNativeDrawing();
3944 if (NS_FAILED(rv))
3945 return rv;
3947 if (nativeDrawing.ShouldRenderAgain())
3948 goto RENDER_AGAIN;
3950 nativeDrawing.PaintToContext();
3952 return rv;
3953 }
3955 uint32_t
3956 nsNativeThemeWin::GetWidgetNativeDrawingFlags(uint8_t aWidgetType)
3957 {
3958 switch (aWidgetType) {
3959 case NS_THEME_BUTTON:
3960 case NS_THEME_NUMBER_INPUT:
3961 case NS_THEME_TEXTFIELD:
3962 case NS_THEME_TEXTFIELD_MULTILINE:
3964 case NS_THEME_DROPDOWN:
3965 case NS_THEME_DROPDOWN_TEXTFIELD:
3966 return
3967 gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
3968 gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE |
3969 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
3971 // need to check these others
3972 case NS_THEME_RANGE:
3973 case NS_THEME_RANGE_THUMB:
3974 case NS_THEME_SCROLLBAR_BUTTON_UP:
3975 case NS_THEME_SCROLLBAR_BUTTON_DOWN:
3976 case NS_THEME_SCROLLBAR_BUTTON_LEFT:
3977 case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
3978 case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
3979 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
3980 case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
3981 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
3982 case NS_THEME_SCALE_HORIZONTAL:
3983 case NS_THEME_SCALE_VERTICAL:
3984 case NS_THEME_SCALE_THUMB_HORIZONTAL:
3985 case NS_THEME_SCALE_THUMB_VERTICAL:
3986 case NS_THEME_SPINNER_UP_BUTTON:
3987 case NS_THEME_SPINNER_DOWN_BUTTON:
3988 case NS_THEME_LISTBOX:
3989 case NS_THEME_TREEVIEW:
3990 case NS_THEME_TOOLTIP:
3991 case NS_THEME_STATUSBAR:
3992 case NS_THEME_STATUSBAR_PANEL:
3993 case NS_THEME_STATUSBAR_RESIZER_PANEL:
3994 case NS_THEME_RESIZER:
3995 case NS_THEME_PROGRESSBAR:
3996 case NS_THEME_PROGRESSBAR_VERTICAL:
3997 case NS_THEME_PROGRESSBAR_CHUNK:
3998 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
3999 case NS_THEME_TAB:
4000 case NS_THEME_TAB_PANEL:
4001 case NS_THEME_TAB_PANELS:
4002 case NS_THEME_MENUBAR:
4003 case NS_THEME_MENUPOPUP:
4004 case NS_THEME_MENUITEM:
4005 break;
4007 // the dropdown button /almost/ renders correctly with scaling,
4008 // except that the graphic in the dropdown button (the downward arrow)
4009 // doesn't get scaled up.
4010 case NS_THEME_DROPDOWN_BUTTON:
4011 // these are definitely no; they're all graphics that don't get scaled up
4012 case NS_THEME_CHECKBOX:
4013 case NS_THEME_RADIO:
4014 case NS_THEME_GROUPBOX:
4015 case NS_THEME_CHECKMENUITEM:
4016 case NS_THEME_RADIOMENUITEM:
4017 case NS_THEME_MENUCHECKBOX:
4018 case NS_THEME_MENURADIO:
4019 case NS_THEME_MENUARROW:
4020 return
4021 gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
4022 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
4023 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
4024 }
4026 return
4027 gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
4028 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
4029 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
4030 }
4032 ///////////////////////////////////////////
4033 // Creation Routine
4034 ///////////////////////////////////////////
4036 // from nsWindow.cpp
4037 extern bool gDisableNativeTheme;
4039 nsresult NS_NewNativeTheme(nsISupports *aOuter, REFNSIID aIID, void **aResult)
4040 {
4041 if (gDisableNativeTheme)
4042 return NS_ERROR_NO_INTERFACE;
4044 if (aOuter)
4045 return NS_ERROR_NO_AGGREGATION;
4047 nsNativeThemeWin* theme = new nsNativeThemeWin();
4048 if (!theme)
4049 return NS_ERROR_OUT_OF_MEMORY;
4050 return theme->QueryInterface(aIID, aResult);
4051 }