diff --git a/widget/src/windows/nsNativeThemeWin.cpp b/widget/src/windows/nsNativeThemeWin.cpp new file mode 100644 index 000000000000..f312753e5c3c --- /dev/null +++ b/widget/src/windows/nsNativeThemeWin.cpp @@ -0,0 +1,839 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * David Hyatt (hyatt@netscape.com) + * + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "nsNativeThemeWin.h" +#include "nsRenderingContextWin.h" +#include "nsDeviceContextWin.h" +#include "nsRect.h" +#include "nsSize.h" +#include "nsTransform2D.h" +#include "nsThemeConstants.h" +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIFrame.h" +#include "nsIEventStateManager.h" +#include "nsINameSpaceManager.h" +#include "nsIPresContext.h" +#include "nsILookAndFeel.h" + +#define THEME_COLOR 204 +#define THEME_FONT 210 + +// Button constants +#define TP_BUTTON 1 +#define TP_RADIO 2 +#define TP_CHECKBOX 3 +#define BP_NORMAL 1 +#define BP_HOVER 2 +#define BP_ACTIVE 3 +#define BP_DISABLED 4 +#define BP_DEFAULT 5 + +// Scrollbar constants +#define SP_BUTTON 1 +#define SP_THUMBHOR 2 +#define SP_THUMBVERT 3 +#define SP_TRACKSTARTHOR 4 +#define SP_TRACKENDHOR 5 +#define SP_TRACKSTARTVERT 6 +#define SP_TRACKENDVERT 7 +#define SP_GRIPPERHOR 8 +#define SP_GRIPPERVERT 9 + +// Tab constants +#define TABP_TAB_SELAFTER 2 +#define TABP_TAB_SELBEFORE 3 +#define TABP_TAB 4 +#define TABP_TAB_SELECTED 5 +#define TABP_TAB_PANE 9 + +NS_IMPL_ISUPPORTS1(nsNativeThemeWin, nsITheme) + +typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList); +typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme); +typedef HRESULT (WINAPI*DrawThemeBackgroundPtr)(HANDLE hTheme, HDC hdc, int iPartId, + int iStateId, const RECT *pRect, + const RECT* pClipRect); +typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId, + int iStateId, const RECT* pRect, + RECT* pContentRect); +typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId, + int iStateId, RECT* prc, int ts, + SIZE* psz); +typedef HRESULT (WINAPI*GetThemeFontPtr)(HANDLE hTheme, HDC hdc, int iPartId, + int iStateId, int iPropId, OUT LOGFONT* pFont); +typedef HRESULT (WINAPI*GetThemeSysFontPtr)(HANDLE hTheme, int iFontId, OUT LOGFONT* pFont); +typedef HRESULT (WINAPI*GetThemeColorPtr)(HANDLE hTheme, HDC hdc, int iPartId, + int iStateId, int iPropId, OUT COLORREF* pFont); +typedef HRESULT (WINAPI*GetThemeTextMetricsPtr)(HANDLE hTheme, OPTIONAL HDC hdc, int iPartId, + int iStateId, OUT TEXTMETRIC* ptm); + +static OpenThemeDataPtr openTheme = NULL; +static CloseThemeDataPtr closeTheme = NULL; +static DrawThemeBackgroundPtr drawThemeBG = NULL; +static GetThemeContentRectPtr getThemeContentRect = NULL; +static GetThemePartSizePtr getThemePartSize = NULL; +static GetThemeFontPtr getThemeFont = NULL; +static GetThemeSysFontPtr getThemeSysFont = NULL; +static GetThemeColorPtr getThemeColor = NULL; +static GetThemeTextMetricsPtr getThemeTextMetrics = NULL; + +static const char kThemeLibraryName[] = "uxtheme.dll"; + +nsNativeThemeWin::nsNativeThemeWin() { + NS_INIT_ISUPPORTS(); + mThemeDLL = NULL; + mButtonTheme = NULL; + mToolbarTheme = NULL; + mRebarTheme = NULL; + mScrollbarTheme = NULL; + mStatusbarTheme = NULL; + mTabTheme = NULL; + mTreeViewTheme = NULL; + + mThemeDLL = ::LoadLibrary(kThemeLibraryName); + if (mThemeDLL) { + openTheme = (OpenThemeDataPtr)GetProcAddress(mThemeDLL, "OpenThemeData"); + closeTheme = (CloseThemeDataPtr)GetProcAddress(mThemeDLL, "CloseThemeData"); + drawThemeBG = (DrawThemeBackgroundPtr)GetProcAddress(mThemeDLL, "DrawThemeBackground"); + getThemeContentRect = (GetThemeContentRectPtr)GetProcAddress(mThemeDLL, "GetThemeBackgroundContentRect"); + getThemePartSize = (GetThemePartSizePtr)GetProcAddress(mThemeDLL, "GetThemePartSize"); + getThemeFont = (GetThemeFontPtr)GetProcAddress(mThemeDLL, "GetThemeFont"); + getThemeSysFont = (GetThemeSysFontPtr)GetProcAddress(mThemeDLL, "GetThemeSysFont"); + getThemeTextMetrics = (GetThemeTextMetricsPtr)GetProcAddress(mThemeDLL, "GetThemeTextMetrics"); + getThemeColor = (GetThemeColorPtr)GetProcAddress(mThemeDLL, "GetThemeColor"); + + mCheckedAtom = getter_AddRefs(NS_NewAtom("checked")); + mDisabledAtom = getter_AddRefs(NS_NewAtom("disabled")); + mSBOrientAtom = getter_AddRefs(NS_NewAtom("sborient")); + mSelectedAtom = getter_AddRefs(NS_NewAtom("selected")); + mTypeAtom = getter_AddRefs(NS_NewAtom("type")); + } +} + +nsNativeThemeWin::~nsNativeThemeWin() { + if (!mThemeDLL) + return; + + CloseData(); + + if (mThemeDLL) + ::FreeLibrary(mThemeDLL); +} + +static void GetNativeRect(const nsRect& aSrc, RECT& aDst) +{ + aDst.top = aSrc.y; + aDst.bottom = aSrc.y + aSrc.height; + aDst.left = aSrc.x; + aDst.right = aSrc.x + aSrc.width; +} + +HANDLE +nsNativeThemeWin::GetTheme(PRUint8 aWidgetType) +{ + switch (aWidgetType) { + case NS_THEME_BUTTON: + case NS_THEME_RADIO: + case NS_THEME_CHECKBOX: { + if (!mButtonTheme) + mButtonTheme = openTheme(NULL, L"Button"); + return mButtonTheme; + } + case NS_THEME_TOOLBOX: { + if (!mRebarTheme) + mRebarTheme = openTheme(NULL, L"Rebar"); + return mRebarTheme; + } + case NS_THEME_TOOLBAR: + case NS_THEME_TOOLBAR_BUTTON: { + if (!mToolbarTheme) + mToolbarTheme = openTheme(NULL, L"Toolbar"); + return mToolbarTheme; + } + case NS_THEME_TAB: + case NS_THEME_TAB_PANEL: { + if (!mTabTheme) + mTabTheme = openTheme(NULL, L"Tab"); + return mTabTheme; + } + case NS_THEME_SCROLLBAR: + case NS_THEME_SCROLLBAR_TRACK: + case NS_THEME_SCROLLBAR_BUTTON: + case NS_THEME_SCROLLBAR_THUMB: + case NS_THEME_SCROLLBAR_GRIPPER: { + if (!mScrollbarTheme) + mScrollbarTheme = openTheme(NULL, L"Scrollbar"); + return mScrollbarTheme; + } + case NS_THEME_STATUSBAR: { + if (!mStatusbarTheme) + mStatusbarTheme = openTheme(NULL, L"Status"); + return mStatusbarTheme; + } + } + return NULL; +} + +static void GetPrimaryPresShell(nsIFrame* aFrame, nsIPresShell** aResult) +{ + *aResult = nsnull; + + if (!aFrame) + return; + + nsCOMPtr doc; + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + content->GetDocument(*getter_AddRefs(doc)); + if (doc) + doc->GetShellAt(0, aResult); // Addref happens here. +} + +static PRInt32 GetContentState(nsIFrame* aFrame) +{ + if (!aFrame) + return 0; + + nsCOMPtr shell; + GetPrimaryPresShell(aFrame, getter_AddRefs(shell)); + if (!shell) + return 0; + + nsCOMPtr context; + shell->GetPresContext(getter_AddRefs(context)); + nsCOMPtr esm; + context->GetEventStateManager(getter_AddRefs(esm)); + PRInt32 flags = 0; + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + esm->GetContentState(content, flags); + return flags; +} + +static PRBool HasAttrValue(nsIContent* aContent, nsIAtom* aAtom, const char* aStr) +{ + nsAutoString attr; + aContent->GetAttr(kNameSpaceID_None, aAtom, attr); + return attr.EqualsIgnoreCase(aStr); +} + +static PRBool CheckBooleanAttr(nsIFrame* aFrame, nsIAtom* aAtom) +{ + if (!aFrame) + return PR_FALSE; + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + nsAutoString attr; + nsresult res = content->GetAttr(kNameSpaceID_None, aAtom, attr); + if (res == NS_CONTENT_ATTR_NO_VALUE) + return PR_TRUE; // This handles the HTML case (an attr with no value is like a true val) + return attr.EqualsIgnoreCase("true"); // This handles the XUL case. +} + +PRBool nsNativeThemeWin::IsDisabled(nsIFrame* aFrame) +{ + return CheckBooleanAttr(aFrame, mDisabledAtom); +} + +PRBool nsNativeThemeWin::IsChecked(nsIFrame* aFrame) +{ + if (!aFrame) + return NS_OK; + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + nsAutoString checked; + nsresult res = content->GetAttr(kNameSpaceID_None, mCheckedAtom, checked); + if (res == NS_CONTENT_ATTR_NO_VALUE) + return PR_TRUE; // XXXdwh Can the HTML form control's checked property differ + // from the checked attribute? If so, will need an IsContentofType + // HTML followed by a QI to nsIDOMHTMLInputElement to grab the prop. + return checked.EqualsIgnoreCase("true"); // This handles the XUL case. +} + +PRBool nsNativeThemeWin::IsSelected(nsIFrame* aFrame) +{ + return CheckBooleanAttr(aFrame, mSelectedAtom); +} + +nsresult +nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, + PRInt32& aPart, PRInt32& aState) +{ + switch (aWidgetType) { + case NS_THEME_BUTTON: { + aPart = TP_BUTTON; + if (!aFrame) { + aState = BP_NORMAL; + return NS_OK; + } + + if (IsDisabled(aFrame)) { + aState = BP_DISABLED; + return NS_OK; + } + PRInt32 eventState = GetContentState(aFrame); + if (eventState & NS_EVENT_STATE_HOVER && eventState & NS_EVENT_STATE_ACTIVE) + aState = BP_ACTIVE; + else if (eventState & NS_EVENT_STATE_FOCUS) + aState = BP_DEFAULT; + else if (eventState & NS_EVENT_STATE_HOVER) + aState = BP_HOVER; + else + aState = BP_NORMAL; + + return NS_OK; + } + case NS_THEME_CHECKBOX: + case NS_THEME_RADIO: { + aPart = (aWidgetType == NS_THEME_CHECKBOX) ? TP_CHECKBOX : TP_RADIO; + if (!aFrame) + aState = BP_NORMAL; + else if (IsDisabled(aFrame)) + aState = BP_DISABLED; + else { + PRInt32 eventState = GetContentState(aFrame); + if (eventState & NS_EVENT_STATE_HOVER && eventState & NS_EVENT_STATE_ACTIVE) + aState = BP_ACTIVE; + else if (eventState & NS_EVENT_STATE_HOVER) + aState = BP_HOVER; + else + aState = BP_NORMAL; + } + + // XXXdwh This check will need to be more complicated, since HTML radio groups + // use checked, but XUL radio groups use selected. There will need to be an + // IsContentOfType test for HTML vs. XUL here. + nsIAtom* atom = (aWidgetType == NS_THEME_CHECKBOX) ? mCheckedAtom : mSelectedAtom; + if (CheckBooleanAttr(aFrame, atom)) + aState += 4; // 4 unchecked states, 4 checked states. + return NS_OK; + } + case NS_THEME_TOOLBAR_BUTTON: { + aPart = TP_BUTTON; + if (!aFrame) { + aState = BP_NORMAL; + return NS_OK; + } + + if (IsDisabled(aFrame)) { + aState = BP_DISABLED; + return NS_OK; + } + PRInt32 eventState = GetContentState(aFrame); + if (eventState & NS_EVENT_STATE_HOVER && eventState & NS_EVENT_STATE_ACTIVE) + aState = BP_ACTIVE; + else if (eventState & NS_EVENT_STATE_HOVER) + aState = BP_HOVER; + else + aState = BP_NORMAL; + + return NS_OK; + } + case NS_THEME_SCROLLBAR_BUTTON: { + aPart = SP_BUTTON; + aState = 8; // Assume horizontal by default. + // States are 4 vert up, 4 vert down, 4 horz left, 4 horz right + if (!aFrame) + aState += BP_NORMAL; + else if (IsDisabled(aFrame)) + aState += BP_DISABLED; + else { + PRInt32 eventState = GetContentState(aFrame); + if (eventState & NS_EVENT_STATE_HOVER && eventState & NS_EVENT_STATE_ACTIVE) + aState += BP_ACTIVE; + else if (eventState & NS_EVENT_STATE_HOVER) + aState += BP_HOVER; + else + aState += BP_NORMAL; + } + + if (aFrame) { + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + + // Get our parent frame's orientation. If we are a horizontal scrollbar, + // then subtract 8 from the result to get the correct offset. + if (HasAttrValue(content, mSBOrientAtom, "vertical")) + aState -= 8; // We're vertical instead. + + // Are we the start button or end button? If we are the end button, add 4 + // to the result to get the correct offset. + if (HasAttrValue(content, mTypeAtom, "increment")) + aState += 4; + } + + return NS_OK; + } + case NS_THEME_SCROLLBAR_TRACK: { + aPart = SP_TRACKSTARTHOR; + aState = BP_NORMAL; + if (aFrame) { + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + + // Get our scrollbar frame's orientation. If we are a vertical scrollbar, + // then add 2 to get the correct val + if (HasAttrValue(content, mSBOrientAtom, "vertical")) + aPart += 2; // We're vertical instead. + } + + return NS_OK; + } + case NS_THEME_SCROLLBAR_THUMB: { + aPart = SP_THUMBHOR; + if (!aFrame) + aState = BP_NORMAL; + else if (IsDisabled(aFrame)) + aState = BP_DISABLED; + else { + PRInt32 eventState = GetContentState(aFrame); + if (eventState & NS_EVENT_STATE_ACTIVE) // Hover is not also a requirement for + // the thumb, since the drag is not canceled + // when you move outside the thumb. + aState = BP_ACTIVE; + else if (eventState & NS_EVENT_STATE_HOVER) + aState = BP_HOVER; + else + aState = BP_NORMAL; + } + + if (aFrame) { + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + + // Get our parent frame's orientation. If we are a horizontal scrollbar, + // then subtract 8 from the result to get the correct offset. + if (HasAttrValue(content, mSBOrientAtom, "vertical")) + aPart = SP_THUMBVERT; // We're vertical instead. + } + + return NS_OK; + } + case NS_THEME_SCROLLBAR_GRIPPER: { + aPart = SP_GRIPPERHOR; + if (!aFrame) + aState = BP_NORMAL; + else if (IsDisabled(aFrame)) + aState = BP_DISABLED; + else { + PRInt32 eventState = GetContentState(aFrame); + if (eventState & NS_EVENT_STATE_ACTIVE) // Hover is not also a requirement for + // the gripper, since the drag is not canceled + // when you move outside the gripper. + aState = BP_ACTIVE; + else if (eventState & NS_EVENT_STATE_HOVER) + aState = BP_HOVER; + else + aState = BP_NORMAL; + } + + if (aFrame) { + nsCOMPtr content; + aFrame->GetContent(getter_AddRefs(content)); + + if (HasAttrValue(content, mSBOrientAtom, "vertical")) + aPart = SP_GRIPPERVERT; // We're vertical instead. + } + + return NS_OK; + } + case NS_THEME_TOOLBOX: + case NS_THEME_STATUSBAR: { + aPart = aState = 0; + return NS_OK; // These have no part or state. + } + case NS_THEME_TAB: { + aPart = TABP_TAB; + if (!aFrame) { + aState = BP_NORMAL; + return NS_OK; + } + + if (IsDisabled(aFrame)) { + aState = BP_DISABLED; + return NS_OK; + } + + if (IsSelected(aFrame)) { + aPart = TABP_TAB_SELECTED; + aState = BP_ACTIVE; // The selected tab is always "pressed". + } + else { + PRInt32 eventState = GetContentState(aFrame); + if (eventState & NS_EVENT_STATE_HOVER && eventState & NS_EVENT_STATE_ACTIVE) + aState = BP_ACTIVE; + else if (eventState & NS_EVENT_STATE_FOCUS) + aState = BP_DEFAULT; + else if (eventState & NS_EVENT_STATE_HOVER) + aState = BP_HOVER; + else + aState = BP_NORMAL; + } + + return NS_OK; + } + } + + aPart = 0; + aState = 0; + return NS_ERROR_FAILURE; +} + +nsresult +GetSystemColor(PRUint8 aWidgetType, nsILookAndFeel::nsColorID& aColorID) +{ + switch (aWidgetType) { + case NS_THEME_BUTTON: + case NS_THEME_TOOLBAR_BUTTON: + case NS_THEME_TAB: { + aColorID = nsILookAndFeel::eColor_buttontext; + return NS_OK; + } + } + return NS_ERROR_FAILURE; +} + +nsresult +GetSystemFont(PRUint8 aWidgetType, nsSystemFontID& aFont) +{ + switch (aWidgetType) { + case NS_THEME_BUTTON: + case NS_THEME_TOOLBAR_BUTTON: + case NS_THEME_TAB: { + aFont = eSystemFont_Button; + return NS_OK; + } + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsNativeThemeWin::DrawWidgetBackground(nsIRenderingContext* aContext, + nsIFrame* aFrame, + PRUint8 aWidgetType, + const nsRect& aRect, + const nsRect& aClipRect) +{ + if (!drawThemeBG) + return NS_ERROR_FAILURE; + + HANDLE theme = GetTheme(aWidgetType); + if (!theme) + return NS_ERROR_FAILURE; + + PRInt32 part, state; + nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state); + if (NS_FAILED(rv)) + return rv; + + nsTransform2D* transformMatrix; + aContext->GetCurrentTransform(transformMatrix); + RECT widgetRect; + RECT clipRect; + nsRect tr(aRect); + nsRect cr(aClipRect); + transformMatrix->TransformCoord(&tr.x,&tr.y,&tr.width,&tr.height); + GetNativeRect(tr, widgetRect); + transformMatrix->TransformCoord(&cr.x,&cr.y,&cr.width,&cr.height); + GetNativeRect(cr, clipRect); + HDC hdc = ((nsRenderingContextWin*)aContext)->mDC; + if (!hdc) + return NS_ERROR_FAILURE; + + drawThemeBG(theme, hdc, part, state, &widgetRect, &clipRect); + return NS_OK; +} + +NS_IMETHODIMP +nsNativeThemeWin::GetWidgetPadding(nsIDeviceContext* aContext, + nsIFrame* aFrame, + PRUint8 aWidgetType, + nsMargin* aResult) +{ + if (!getThemeContentRect) + return NS_ERROR_FAILURE; + + if (aWidgetType == NS_THEME_TOOLBOX || aWidgetType == NS_THEME_TOOLBAR || aWidgetType == NS_THEME_STATUSBAR) + return NS_OK; // Don't worry about it. + + HANDLE theme = GetTheme(aWidgetType); + if (!theme) + return NS_ERROR_FAILURE; + + PRInt32 part, state; + nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state); + if (NS_FAILED(rv)) + return rv; + + // Get our info. + RECT outerRect; // Create a fake outer rect. + outerRect.top = outerRect.left = 100; + outerRect.right = outerRect.bottom = 200; + RECT contentRect(outerRect); + HRESULT res = getThemeContentRect(theme, NULL, part, state, &outerRect, &contentRect); + + if (FAILED(res)) + return NS_ERROR_FAILURE; + + // Now compute the delta in each direction and place it in our + // nsMargin struct. + aResult->top = contentRect.top - outerRect.top; + aResult->bottom = outerRect.bottom - contentRect.bottom; + aResult->left = contentRect.left - outerRect.left; + aResult->right = outerRect.right - contentRect.right; + return NS_OK; +} + +NS_IMETHODIMP +nsNativeThemeWin::GetWidgetFont(nsIDeviceContext* aContext, + PRUint8 aWidgetType, + nsFont* aFont) +{ + if (!getThemeFont) + return NS_ERROR_FAILURE; + + HANDLE theme = GetTheme(aWidgetType); + if (!theme) + return NS_ERROR_FAILURE; + + PRInt32 part, state; + nsresult rv = GetThemePartAndState(nsnull, aWidgetType, part, state); + if (NS_FAILED(rv)) + return rv; + + nsDeviceContextWin* dcWin = (nsDeviceContextWin*)aContext; + HWND hwnd; + HDC tdc; + if (!dcWin->mDC) { + hwnd = (HWND)dcWin->mWidget; + tdc = ::GetDC(hwnd); + } + else + tdc = dcWin->mDC; + + // Get our info. + // LOGFONT logFont; + // HRESULT res = getThemeFont(theme, tdc, part, state, THEME_FONT, &logFont); + // if (FAILED(res)) { + // Part didn't define a font. Get the font from the system metrics + // instead. + nsSystemFontID sysID; + rv = GetSystemFont(aWidgetType, sysID); + if (NS_FAILED(rv)) + return rv; + dcWin->GetSysFontInfo(tdc, sysID, aFont); + // } + // else + // dcWin->CopyLogFontToNSFont(&tdc, &logFont, aFont); + + // Release the dc + if (!dcWin->mDC) + ::ReleaseDC(hwnd, tdc); + return NS_OK; +} + +NS_IMETHODIMP +nsNativeThemeWin::GetWidgetColor(nsIPresContext* aPresContext, + nsIRenderingContext* aContext, + nsIFrame* aFrame, + PRUint8 aWidgetType, + nscolor* aColor) +{ + HANDLE theme = GetTheme(aWidgetType); + + PRInt32 part, state; + nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state); + + HDC hdc = ((nsRenderingContextWin*)aContext)->mDC; + if (!hdc) + return NS_ERROR_FAILURE; + + //COLORREF color; + //HRESULT res = getThemeColor(theme, hdc, part, state, THEME_COLOR, &color); + //if (FAILED(res)) { + // Try to get a system color based off the widget type. + nsCOMPtr lf; + aPresContext->GetLookAndFeel(getter_AddRefs(lf)); + nsILookAndFeel::nsColorID colorID; + rv = GetSystemColor(aWidgetType, colorID); + if (NS_FAILED(rv)) + return rv; + lf->GetColor(colorID, *aColor); + // } + + // Copy the colorref into an nscolor. + // *aColor = color; + return NS_OK; +} + +NS_IMETHODIMP +nsNativeThemeWin::GetMinimumWidgetSize(nsIRenderingContext* aContext, nsIFrame* aFrame, + PRUint8 aWidgetType, + nsSize* aResult) +{ + if (!getThemePartSize) + return NS_ERROR_FAILURE; + + if (aWidgetType == NS_THEME_TOOLBOX || aWidgetType == NS_THEME_TOOLBAR || aWidgetType == NS_THEME_STATUSBAR) + return NS_OK; // Don't worry about it. + + HANDLE theme = GetTheme(aWidgetType); + if (!theme) + return NS_ERROR_FAILURE; + + PRInt32 part, state; + nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state); + if (NS_FAILED(rv)) + return rv; + + HDC hdc = ((nsRenderingContextWin*)aContext)->mDC; + if (!hdc) + return NS_ERROR_FAILURE; + + PRInt32 sizeReq = 1; // Best-fit size. + if (aWidgetType == NS_THEME_SCROLLBAR_THUMB) + sizeReq = 0; // Best-fit size for scrollbar thumbs is too large for most themes. + // In our app, we want the thumb to be able to really shrink down, + // so use the min-size request value (of 0). + SIZE sz; + getThemePartSize(theme, hdc, part, state, NULL, sizeReq, &sz); + aResult->width = sz.cx; + aResult->height = sz.cy; + return NS_OK; +} + +NS_IMETHODIMP +nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType, + nsIAtom* aAttribute, PRBool* aShouldRepaint) +{ + // Some widget types just never change state. + if (aWidgetType == NS_THEME_TOOLBOX || aWidgetType == NS_THEME_TOOLBAR || + aWidgetType == NS_THEME_SCROLLBAR_TRACK || aWidgetType == NS_THEME_STATUSBAR) { + *aShouldRepaint = PR_FALSE; + return NS_OK; + } + + // XXXdwh Not sure what can really be done here. Can at least guess for + // specific widgets that they're highly unlikely to have certain states. + // For example, a toolbar doesn't care about any states. + if (!aAttribute) { + // Hover/focus/active changed. Always repaint. + *aShouldRepaint = PR_TRUE; + } + else { + // Check the attribute to see if it's relevant. + // disabled, checked, dlgtype, default, etc. + *aShouldRepaint = PR_FALSE; + if (aAttribute == mDisabledAtom || aAttribute == mCheckedAtom || + aAttribute == mSelectedAtom) + *aShouldRepaint = PR_TRUE; + } + + return NS_OK; +} + +void +nsNativeThemeWin::CloseData() +{ + // XXXdwh Calling closeTheme trashes the this ptr and causes horrible things + // to happen. For now, just drop the stale handles without closing them. + // Is this a bug in the API, or is there something I'm doing wrong? + if (mToolbarTheme) { + closeTheme(mToolbarTheme); + mToolbarTheme = NULL; + } + if (mScrollbarTheme) { + closeTheme(mScrollbarTheme); + mScrollbarTheme = NULL; + } + if (mRebarTheme) { + closeTheme(mRebarTheme); + mRebarTheme = NULL; + } + if (mButtonTheme) { + closeTheme(mButtonTheme); + mButtonTheme = NULL; + } + if (mStatusbarTheme) { + closeTheme(mStatusbarTheme); + mStatusbarTheme = NULL; + } + if (mTabTheme) { + closeTheme(mTabTheme); + mTabTheme = NULL; + } + if (mTreeViewTheme) { + closeTheme(mTreeViewTheme); + mTreeViewTheme = NULL; + } +} + +NS_IMETHODIMP +nsNativeThemeWin::ThemeChanged() +{ + CloseData(); + return NS_OK; +} + +PRBool +nsNativeThemeWin::ThemeSupportsWidget(nsIPresContext* aPresContext, + PRUint8 aWidgetType) +{ + // XXXdwh We can go even further and call the API to ask if support exists. + HANDLE theme = GetTheme(aWidgetType); + return theme != NULL; +} + +/////////////////////////////////////////// +// Creation Routine +/////////////////////////////////////////// +NS_METHOD NS_NewNativeTheme(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + nsNativeThemeWin* theme = new nsNativeThemeWin(); + if (!theme) + return NS_ERROR_OUT_OF_MEMORY; + return theme->QueryInterface(aIID, aResult); +} \ No newline at end of file diff --git a/widget/src/windows/nsNativeThemeWin.h b/widget/src/windows/nsNativeThemeWin.h new file mode 100644 index 000000000000..9f2889fb0b33 --- /dev/null +++ b/widget/src/windows/nsNativeThemeWin.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Mozilla browser. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Original Author: David W. Hyatt (hyatt@netscape.com) + * + * Contributors: + */ + +#include "nsITheme.h" +#include "nsCOMPtr.h" +#include "nsIAtom.h" +#include + +class nsNativeThemeWin: public nsITheme { +public: + NS_DECL_ISUPPORTS + + // The nsITheme interface. + NS_IMETHOD DrawWidgetBackground(nsIRenderingContext* aContext, + nsIFrame* aFrame, + PRUint8 aWidgetType, + const nsRect& aRect, + const nsRect& aClipRect); + + NS_IMETHOD GetWidgetPadding(nsIDeviceContext* aContext, + nsIFrame* aFrame, + PRUint8 aWidgetType, + nsMargin* aResult); + + NS_IMETHOD GetWidgetFont(nsIDeviceContext* aContext, + PRUint8 aWidgetType, + nsFont* aFont); + + NS_IMETHOD GetWidgetColor(nsIPresContext* aPresContext, + nsIRenderingContext* aContext, + nsIFrame* aFrame, + PRUint8 aWidgetType, + nscolor* aFont); + + NS_IMETHOD GetMinimumWidgetSize(nsIRenderingContext* aContext, nsIFrame* aFrame, + PRUint8 aWidgetType, + nsSize* aResult); + + NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType, + nsIAtom* aAttribute, PRBool* aShouldRepaint); + + NS_IMETHOD ThemeChanged(); + + PRBool ThemeSupportsWidget(nsIPresContext* aPresContext, + PRUint8 aWidgetType); + + nsNativeThemeWin(); + virtual ~nsNativeThemeWin(); + +protected: + void CloseData(); + HANDLE GetTheme(PRUint8 aWidgetType); + nsresult GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, + PRInt32& aPart, PRInt32& aState); + PRBool IsDisabled(nsIFrame* aFrame); + PRBool IsChecked(nsIFrame* aFrame); + PRBool IsSelected(nsIFrame* aFrame); + +private: + HMODULE mThemeDLL; + HANDLE mButtonTheme; + HANDLE mToolbarTheme; + HANDLE mRebarTheme; + HANDLE mScrollbarTheme; + HANDLE mStatusbarTheme; + HANDLE mTabTheme; + HANDLE mTreeViewTheme; + + nsCOMPtr mCheckedAtom; + nsCOMPtr mDisabledAtom; + nsCOMPtr mSBOrientAtom; + nsCOMPtr mSelectedAtom; + nsCOMPtr mTypeAtom; +}; + +// Creator function +extern NS_METHOD NS_NewNativeThemeWin(nsISupports *aOuter, REFNSIID aIID, void **aResult);