/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . */ // General info: // http://msdn.microsoft.com/en-us/library/windows/desktop/hh270423%28v=vs.85%29.aspx // http://msdn.microsoft.com/en-us/library/windows/desktop/bb773178%28v=vs.85%29.aspx // Useful tool to explore the themes & their rendering: // http://privat.rejbrand.se/UxExplore.exe // (found at http://stackoverflow.com/questions/4009701/windows-visual-themes-gallery-of-parts-and-states/4009712#4009712) // Theme subclasses: // http://msdn.microsoft.com/en-us/library/windows/desktop/bb773218%28v=vs.85%29.aspx // Drawing in non-client area (general DWM-related info): // http://msdn.microsoft.com/en-us/library/windows/desktop/bb688195%28v=vs.85%29.aspx #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; typedef map< wstring, HTHEME > ThemeMap; static ThemeMap aThemeMap; /**************************************************** wrap visual styles API to avoid linking against it it is not available on all Windows platforms *****************************************************/ class VisualStylesAPI { private: typedef HTHEME (WINAPI * OpenThemeData_Proc_T) ( HWND hwnd, LPCWSTR pszClassList ); typedef HRESULT (WINAPI * CloseThemeData_Proc_T) ( HTHEME hTheme ); typedef HRESULT (WINAPI * GetThemeBackgroundContentRect_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect ); typedef HRESULT (WINAPI * DrawThemeBackground_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect ); typedef HRESULT (WINAPI * DrawThemeText_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect ); typedef HRESULT (WINAPI * GetThemePartSize_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz ); typedef BOOL (WINAPI * IsThemeActive_Proc_T) ( void ); OpenThemeData_Proc_T lpfnOpenThemeData; CloseThemeData_Proc_T lpfnCloseThemeData; GetThemeBackgroundContentRect_Proc_T lpfnGetThemeBackgroundContentRect; DrawThemeBackground_Proc_T lpfnDrawThemeBackground; DrawThemeText_Proc_T lpfnDrawThemeText; GetThemePartSize_Proc_T lpfnGetThemePartSize; IsThemeActive_Proc_T lpfnIsThemeActive; oslModule mhModule; public: VisualStylesAPI(); ~VisualStylesAPI(); HTHEME OpenThemeData( HWND hwnd, LPCWSTR pszClassList ); HRESULT CloseThemeData( HTHEME hTheme ); HRESULT GetThemeBackgroundContentRect( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect ); HRESULT DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect ); HRESULT DrawThemeText( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect ); HRESULT GetThemePartSize( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz ); BOOL IsThemeActive(); }; static VisualStylesAPI vsAPI; VisualStylesAPI::VisualStylesAPI() : lpfnOpenThemeData( nullptr ), lpfnCloseThemeData( nullptr ), lpfnGetThemeBackgroundContentRect( nullptr ), lpfnDrawThemeBackground( nullptr ), lpfnDrawThemeText( nullptr ), lpfnGetThemePartSize( nullptr ), lpfnIsThemeActive( nullptr ) { OUString aLibraryName( "uxtheme.dll" ); mhModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT ); if ( mhModule ) { lpfnOpenThemeData = reinterpret_cast(osl_getAsciiFunctionSymbol( mhModule, "OpenThemeData" )); lpfnCloseThemeData = reinterpret_cast(osl_getAsciiFunctionSymbol( mhModule, "CloseThemeData" )); lpfnGetThemeBackgroundContentRect = reinterpret_cast(osl_getAsciiFunctionSymbol( mhModule, "GetThemeBackgroundContentRect" )); lpfnDrawThemeBackground = reinterpret_cast(osl_getAsciiFunctionSymbol( mhModule, "DrawThemeBackground" )); lpfnDrawThemeText = reinterpret_cast(osl_getAsciiFunctionSymbol( mhModule, "DrawThemeText" )); lpfnGetThemePartSize = reinterpret_cast(osl_getAsciiFunctionSymbol( mhModule, "GetThemePartSize" )); lpfnIsThemeActive = reinterpret_cast(osl_getAsciiFunctionSymbol( mhModule, "IsThemeActive" )); } } VisualStylesAPI::~VisualStylesAPI() { if( mhModule ) osl_unloadModule( mhModule ); } HTHEME VisualStylesAPI::OpenThemeData( HWND hwnd, LPCWSTR pszClassList ) { if(lpfnOpenThemeData) return (*lpfnOpenThemeData) (hwnd, pszClassList); else return nullptr; } HRESULT VisualStylesAPI::CloseThemeData( HTHEME hTheme ) { if(lpfnCloseThemeData) return (*lpfnCloseThemeData) (hTheme); else return S_FALSE; } HRESULT VisualStylesAPI::GetThemeBackgroundContentRect( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect ) { if(lpfnGetThemeBackgroundContentRect) return (*lpfnGetThemeBackgroundContentRect) ( hTheme, hdc, iPartId, iStateId, pBoundingRect, pContentRect ); else return S_FALSE; } HRESULT VisualStylesAPI::DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect ) { if(lpfnDrawThemeBackground) return (*lpfnDrawThemeBackground) (hTheme, hdc, iPartId, iStateId, pRect, pClipRect); else return S_FALSE; } HRESULT VisualStylesAPI::DrawThemeText( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect ) { if(lpfnDrawThemeText) return (*lpfnDrawThemeText) (hTheme, hdc, iPartId, iStateId, pszText, iCharCount, dwTextFlags, dwTextFlags2, pRect); else return S_FALSE; } HRESULT VisualStylesAPI::GetThemePartSize( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz ) { if(lpfnGetThemePartSize) return (*lpfnGetThemePartSize) (hTheme, hdc, iPartId, iStateId, prc, eSize, psz); else return S_FALSE; } BOOL VisualStylesAPI::IsThemeActive() { if(lpfnIsThemeActive) return (*lpfnIsThemeActive) (); else return FALSE; } /********************************************************* * Initialize XP theming and local stuff *********************************************************/ void SalData::initNWF() { ImplSVData* pSVData = ImplGetSVData(); // the menu bar and the top docking area should have a common background (gradient) pSVData->maNWFData.mbMenuBarDockingAreaCommonBG = true; } // ********************************************************* // * Release theming handles // ******************************************************** void SalData::deInitNWF() { ThemeMap::iterator iter = aThemeMap.begin(); while( iter != aThemeMap.end() ) { vsAPI.CloseThemeData(iter->second); ++iter; } aThemeMap.clear(); } static HTHEME getThemeHandle( HWND hWnd, LPCWSTR name ) { if( GetSalData()->mbThemeChanged ) { // throw away invalid theme handles SalData::deInitNWF(); GetSalData()->mbThemeChanged = false; } ThemeMap::iterator iter; if( (iter = aThemeMap.find( name )) != aThemeMap.end() ) return iter->second; // theme not found -> add it to map HTHEME hTheme = vsAPI.OpenThemeData( hWnd, name ); if( hTheme != nullptr ) aThemeMap[name] = hTheme; return hTheme; } bool WinSalGraphics::IsNativeControlSupported( ControlType nType, ControlPart nPart ) { HTHEME hTheme = nullptr; switch( nType ) { case ControlType::Pushbutton: case ControlType::Radiobutton: case ControlType::Checkbox: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Button"); break; case ControlType::Scrollbar: if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert ) return FALSE; // no background painting needed if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Scrollbar"); break; case ControlType::Combobox: if( nPart == ControlPart::HasBackgroundTexture ) return FALSE; // we do not paint the inner part (ie the selection background/focus indication) if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Edit"); else if( nPart == ControlPart::ButtonDown ) hTheme = getThemeHandle( mhWnd, L"Combobox"); break; case ControlType::Spinbox: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Edit"); else if( nPart == ControlPart::AllButtons || nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown || nPart == ControlPart::ButtonLeft|| nPart == ControlPart::ButtonRight ) hTheme = getThemeHandle( mhWnd, L"Spin"); break; case ControlType::SpinButtons: if( nPart == ControlPart::Entire || nPart == ControlPart::AllButtons ) hTheme = getThemeHandle( mhWnd, L"Spin"); break; case ControlType::Editbox: case ControlType::MultilineEditbox: if( nPart == ControlPart::HasBackgroundTexture ) return FALSE; // we do not paint the inner part (ie the selection background/focus indication) //return TRUE; if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Edit"); break; case ControlType::Listbox: if( nPart == ControlPart::HasBackgroundTexture ) return FALSE; // we do not paint the inner part (ie the selection background/focus indication) if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow ) hTheme = getThemeHandle( mhWnd, L"Listview"); else if( nPart == ControlPart::ButtonDown ) hTheme = getThemeHandle( mhWnd, L"Combobox"); break; case ControlType::TabPane: case ControlType::TabBody: case ControlType::TabItem: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Tab"); break; case ControlType::Toolbar: if( nPart == ControlPart::Entire || nPart == ControlPart::Button ) hTheme = getThemeHandle( mhWnd, L"Toolbar"); else // use rebar theme for grip and background hTheme = getThemeHandle( mhWnd, L"Rebar"); break; case ControlType::Menubar: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Rebar"); else if( GetSalData()->mbThemeMenuSupport ) { if( nPart == ControlPart::MenuItem ) hTheme = getThemeHandle( mhWnd, L"Menu" ); } break; case ControlType::MenuPopup: if( GetSalData()->mbThemeMenuSupport ) { if( nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark || nPart == ControlPart::Separator ) hTheme = getThemeHandle( mhWnd, L"Menu" ); } break; case ControlType::Progress: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Progress"); break; case ControlType::Slider: if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea ) hTheme = getThemeHandle( mhWnd, L"Trackbar" ); break; case ControlType::ListNode: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"TreeView" ); break; default: hTheme = nullptr; break; } return (hTheme != nullptr); } bool WinSalGraphics::hitTestNativeControl( ControlType, ControlPart, const tools::Rectangle&, const Point&, bool& ) { return FALSE; } bool ImplDrawTheme( HTHEME hTheme, HDC hDC, int iPart, int iState, RECT rc, const OUString& aStr) { HRESULT hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); if( aStr.getLength() ) { RECT rcContent; hr = vsAPI.GetThemeBackgroundContentRect( hTheme, hDC, iPart, iState, &rc, &rcContent); hr = vsAPI.DrawThemeText( hTheme, hDC, iPart, iState, o3tl::toW(aStr.getStr()), -1, DT_CENTER | DT_VCENTER | DT_SINGLELINE, 0, &rcContent); } return (hr == S_OK); } tools::Rectangle ImplGetThemeRect( HTHEME hTheme, HDC hDC, int iPart, int iState, const tools::Rectangle& /* aRect */, THEMESIZE eTS = TS_TRUE ) { SIZE aSz; HRESULT hr = vsAPI.GetThemePartSize( hTheme, hDC, iPart, iState, nullptr, eTS, &aSz ); // TS_TRUE returns optimal size if( hr == S_OK ) return tools::Rectangle( 0, 0, aSz.cx, aSz.cy ); else return tools::Rectangle(); } // Helper functions void ImplConvertSpinbuttonValues( ControlPart nControlPart, const ControlState& rState, const tools::Rectangle& rRect, int* pLunaPart, int *pLunaState, RECT *pRect ) { if( nControlPart == ControlPart::ButtonDown ) { *pLunaPart = SPNP_DOWN; if( rState & ControlState::PRESSED ) *pLunaState = DNS_PRESSED; else if( !(rState & ControlState::ENABLED) ) *pLunaState = DNS_DISABLED; else if( rState & ControlState::ROLLOVER ) *pLunaState = DNS_HOT; else *pLunaState = DNS_NORMAL; } if( nControlPart == ControlPart::ButtonUp ) { *pLunaPart = SPNP_UP; if( rState & ControlState::PRESSED ) *pLunaState = UPS_PRESSED; else if( !(rState & ControlState::ENABLED) ) *pLunaState = UPS_DISABLED; else if( rState & ControlState::ROLLOVER ) *pLunaState = UPS_HOT; else *pLunaState = UPS_NORMAL; } if( nControlPart == ControlPart::ButtonRight ) { *pLunaPart = SPNP_UPHORZ; if( rState & ControlState::PRESSED ) *pLunaState = DNHZS_PRESSED; else if( !(rState & ControlState::ENABLED) ) *pLunaState = DNHZS_DISABLED; else if( rState & ControlState::ROLLOVER ) *pLunaState = DNHZS_HOT; else *pLunaState = DNHZS_NORMAL; } if( nControlPart == ControlPart::ButtonLeft ) { *pLunaPart = SPNP_DOWNHORZ; if( rState & ControlState::PRESSED ) *pLunaState = UPHZS_PRESSED; else if( !(rState & ControlState::ENABLED) ) *pLunaState = UPHZS_DISABLED; else if( rState & ControlState::ROLLOVER ) *pLunaState = UPHZS_HOT; else *pLunaState = UPHZS_NORMAL; } pRect->left = rRect.Left(); pRect->right = rRect.Right()+1; pRect->top = rRect.Top(); pRect->bottom = rRect.Bottom()+1; } /// Draw an own toolbar style on Windows Vista or later, looks better there static void impl_drawAeroToolbar( HDC hDC, RECT rc, bool bHorizontal ) { if ( rc.top == 0 && bHorizontal ) { const long GRADIENT_HEIGHT = 32; long gradient_break = rc.top; long gradient_bottom = rc.bottom - 1; GRADIENT_RECT g_rect[1] = { { 0, 1 } }; // very slow gradient at the top (if we have space for that) if ( gradient_bottom - rc.top > GRADIENT_HEIGHT ) { gradient_break = gradient_bottom - GRADIENT_HEIGHT; TRIVERTEX vert[2] = { { rc.left, rc.top, 0xff00, 0xff00, 0xff00, 0xff00 }, { rc.right, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 }, }; GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V ); } // gradient at the bottom TRIVERTEX vert[2] = { { rc.left, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 }, { rc.right, gradient_bottom, 0xf000, 0xf000, 0xf000, 0xff00 } }; GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V ); // and a darker horizontal line under that HPEN hpen = CreatePen( PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0 ) ); HPEN hOrigPen = static_cast(SelectObject(hDC, hpen)); MoveToEx( hDC, rc.left, gradient_bottom, nullptr ); LineTo( hDC, rc.right, gradient_bottom ); SelectObject(hDC, hOrigPen); DeleteObject(hpen); } else { HBRUSH hbrush = CreateSolidBrush( RGB( 0xf0, 0xf0, 0xf0 ) ); FillRect( hDC, &rc, hbrush ); DeleteObject( hbrush ); // darker line to distinguish the toolbar and viewshell // it is drawn only for the horizontal toolbars; it did not look well // when done for the vertical ones too if ( bHorizontal ) { long from_x, from_y, to_x, to_y; from_x = rc.left; to_x = rc.right; from_y = to_y = rc.top; HPEN hpen = CreatePen( PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0 ) ); HPEN hOrigPen = static_cast(SelectObject(hDC, hpen)); MoveToEx( hDC, from_x, from_y, nullptr ); LineTo( hDC, to_x, to_y ); SelectObject(hDC, hOrigPen); DeleteObject(hpen); } } } bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc, ControlType nType, ControlPart nPart, ControlState nState, const ImplControlValue& aValue, OUString const & aCaption ) { // a listbox dropdown is actually a combobox dropdown if( nType == ControlType::Listbox ) if( nPart == ControlPart::ButtonDown ) nType = ControlType::Combobox; // draw entire combobox as a large edit box if( nType == ControlType::Combobox ) if( nPart == ControlPart::Entire ) nType = ControlType::Editbox; // draw entire spinbox as a large edit box if( nType == ControlType::Spinbox ) if( nPart == ControlPart::Entire ) nType = ControlType::Editbox; int iPart(0), iState(0); if( nType == ControlType::Scrollbar ) { HRESULT hr; if( nPart == ControlPart::ButtonUp ) { iPart = SBP_ARROWBTN; if( nState & ControlState::PRESSED ) iState = ABS_UPPRESSED; else if( !(nState & ControlState::ENABLED) ) iState = ABS_UPDISABLED; else if( nState & ControlState::ROLLOVER ) iState = ABS_UPHOT; else iState = ABS_UPNORMAL; hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); return (hr == S_OK); } if( nPart == ControlPart::ButtonDown ) { iPart = SBP_ARROWBTN; if( nState & ControlState::PRESSED ) iState = ABS_DOWNPRESSED; else if( !(nState & ControlState::ENABLED) ) iState = ABS_DOWNDISABLED; else if( nState & ControlState::ROLLOVER ) iState = ABS_DOWNHOT; else iState = ABS_DOWNNORMAL; hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); return (hr == S_OK); } if( nPart == ControlPart::ButtonLeft ) { iPart = SBP_ARROWBTN; if( nState & ControlState::PRESSED ) iState = ABS_LEFTPRESSED; else if( !(nState & ControlState::ENABLED) ) iState = ABS_LEFTDISABLED; else if( nState & ControlState::ROLLOVER ) iState = ABS_LEFTHOT; else iState = ABS_LEFTNORMAL; hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); return (hr == S_OK); } if( nPart == ControlPart::ButtonRight ) { iPart = SBP_ARROWBTN; if( nState & ControlState::PRESSED ) iState = ABS_RIGHTPRESSED; else if( !(nState & ControlState::ENABLED) ) iState = ABS_RIGHTDISABLED; else if( nState & ControlState::ROLLOVER ) iState = ABS_RIGHTHOT; else iState = ABS_RIGHTNORMAL; hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); return (hr == S_OK); } if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert ) { iPart = (nPart == ControlPart::ThumbHorz) ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT; if( nState & ControlState::PRESSED ) iState = SCRBS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = SCRBS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = SCRBS_HOT; else iState = SCRBS_NORMAL; SIZE sz; vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_MIN, &sz); vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_TRUE, &sz); vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_DRAW, &sz); hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); // paint gripper on thumb if enough space if( ( (nPart == ControlPart::ThumbVert) && (rc.bottom-rc.top > 12) ) || ( (nPart == ControlPart::ThumbHorz) && (rc.right-rc.left > 12) ) ) { iPart = (nPart == ControlPart::ThumbHorz) ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT; iState = 0; vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); } return (hr == S_OK); } if( nPart == ControlPart::TrackHorzLeft || nPart == ControlPart::TrackHorzRight || nPart == ControlPart::TrackVertUpper || nPart == ControlPart::TrackVertLower ) { switch( nPart ) { case ControlPart::TrackHorzLeft: iPart = SBP_UPPERTRACKHORZ; break; case ControlPart::TrackHorzRight: iPart = SBP_LOWERTRACKHORZ; break; case ControlPart::TrackVertUpper: iPart = SBP_UPPERTRACKVERT; break; case ControlPart::TrackVertLower: iPart = SBP_LOWERTRACKVERT; break; default: break; } if( nState & ControlState::PRESSED ) iState = SCRBS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = SCRBS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = SCRBS_HOT; else iState = SCRBS_NORMAL; hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr); return (hr == S_OK); } } if( nType == ControlType::SpinButtons && nPart == ControlPart::AllButtons ) { if( aValue.getType() == ControlType::SpinButtons ) { const SpinbuttonValue* pValue = (aValue.getType() == ControlType::SpinButtons) ? static_cast(&aValue) : nullptr; RECT rect; ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect ); bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption); if( bOk ) { ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect ); bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption); } return bOk; } } if( nType == ControlType::Spinbox ) { if( nPart == ControlPart::AllButtons ) { if( aValue.getType() == ControlType::SpinButtons ) { const SpinbuttonValue *pValue = static_cast(&aValue); RECT rect; ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect ); bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption); if( bOk ) { ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect ); bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption); } return bOk; } } if( nPart == ControlPart::ButtonDown ) { iPart = SPNP_DOWN; if( nState & ControlState::PRESSED ) iState = DNS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = DNS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = DNS_HOT; else iState = DNS_NORMAL; } if( nPart == ControlPart::ButtonUp ) { iPart = SPNP_UP; if( nState & ControlState::PRESSED ) iState = UPS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = UPS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = UPS_HOT; else iState = UPS_NORMAL; } if( nPart == ControlPart::ButtonRight ) { iPart = SPNP_DOWNHORZ; if( nState & ControlState::PRESSED ) iState = DNHZS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = DNHZS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = DNHZS_HOT; else iState = DNHZS_NORMAL; } if( nPart == ControlPart::ButtonLeft ) { iPart = SPNP_UPHORZ; if( nState & ControlState::PRESSED ) iState = UPHZS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = UPHZS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = UPHZS_HOT; else iState = UPHZS_NORMAL; } if( nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight || nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown ) return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( nType == ControlType::Combobox ) { if( nPart == ControlPart::ButtonDown ) { iPart = CP_DROPDOWNBUTTON; if( nState & ControlState::PRESSED ) iState = CBXS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = CBXS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = CBXS_HOT; else iState = CBXS_NORMAL; return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } } if( nType == ControlType::Pushbutton ) { iPart = BP_PUSHBUTTON; if( nState & ControlState::PRESSED ) iState = PBS_PRESSED; else if( !(nState & ControlState::ENABLED) ) iState = PBS_DISABLED; else if( nState & ControlState::ROLLOVER ) iState = PBS_HOT; else if( nState & ControlState::DEFAULT ) iState = PBS_DEFAULTED; //else if( nState & ControlState::FOCUSED ) // iState = PBS_DEFAULTED; // may need to draw focus rect else iState = PBS_NORMAL; return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( nType == ControlType::Radiobutton ) { iPart = BP_RADIOBUTTON; bool bChecked = ( aValue.getTristateVal() == ButtonValue::On ); if( nState & ControlState::PRESSED ) iState = bChecked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED; else if( !(nState & ControlState::ENABLED) ) iState = bChecked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED; else if( nState & ControlState::ROLLOVER ) iState = bChecked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT; else iState = bChecked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL; //if( nState & ControlState::FOCUSED ) // iState |= PBS_DEFAULTED; // may need to draw focus rect return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( nType == ControlType::Checkbox ) { iPart = BP_CHECKBOX; ButtonValue v = aValue.getTristateVal(); if( nState & ControlState::PRESSED ) iState = (v == ButtonValue::On) ? CBS_CHECKEDPRESSED : ( (v == ButtonValue::Off) ? CBS_UNCHECKEDPRESSED : CBS_MIXEDPRESSED ); else if( !(nState & ControlState::ENABLED) ) iState = (v == ButtonValue::On) ? CBS_CHECKEDDISABLED : ( (v == ButtonValue::Off) ? CBS_UNCHECKEDDISABLED : CBS_MIXEDDISABLED ); else if( nState & ControlState::ROLLOVER ) iState = (v == ButtonValue::On) ? CBS_CHECKEDHOT : ( (v == ButtonValue::Off) ? CBS_UNCHECKEDHOT : CBS_MIXEDHOT ); else iState = (v == ButtonValue::On) ? CBS_CHECKEDNORMAL : ( (v == ButtonValue::Off) ? CBS_UNCHECKEDNORMAL : CBS_MIXEDNORMAL ); //if( nState & ControlState::FOCUSED ) // iState |= PBS_DEFAULTED; // may need to draw focus rect //SIZE sz; //THEMESIZE eSize = TS_DRAW; // TS_MIN, TS_TRUE, TS_DRAW //vsAPI.GetThemePartSize( hTheme, hDC, iPart, iState, &rc, eSize, &sz); return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( ( nType == ControlType::Editbox ) || ( nType == ControlType::MultilineEditbox ) ) { iPart = EP_EDITTEXT; if( !(nState & ControlState::ENABLED) ) iState = ETS_DISABLED; else if( nState & ControlState::FOCUSED ) iState = ETS_FOCUSED; else if( nState & ControlState::ROLLOVER ) iState = ETS_HOT; else iState = ETS_NORMAL; return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( nType == ControlType::Listbox ) { if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow ) { iPart = LVP_EMPTYTEXT; // ??? no idea which part to choose here return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } } if( nType == ControlType::TabPane ) { iPart = TABP_PANE; return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( nType == ControlType::TabBody ) { iPart = TABP_BODY; return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( nType == ControlType::TabItem ) { iPart = TABP_TABITEMLEFTEDGE; rc.bottom--; OSL_ASSERT( aValue.getType() == ControlType::TabItem ); const TabitemValue *pValue = static_cast(&aValue); if( pValue->isBothAligned() ) { iPart = TABP_TABITEMLEFTEDGE; rc.right--; } else if( pValue->isLeftAligned() ) iPart = TABP_TABITEMLEFTEDGE; else if( pValue->isRightAligned() ) iPart = TABP_TABITEMRIGHTEDGE; else iPart = TABP_TABITEM; if( !(nState & ControlState::ENABLED) ) iState = TILES_DISABLED; else if( nState & ControlState::SELECTED ) { iState = TILES_SELECTED; // increase the selected tab rc.left-=2; if( pValue && !pValue->isBothAligned() ) { if( pValue->isLeftAligned() || pValue->isNotAligned() ) rc.right+=2; if( pValue->isRightAligned() ) rc.right+=1; } rc.top-=2; rc.bottom+=2; } else if( nState & ControlState::ROLLOVER ) iState = TILES_HOT; else if( nState & ControlState::FOCUSED ) iState = TILES_FOCUSED; // may need to draw focus rect else iState = TILES_NORMAL; return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } if( nType == ControlType::Toolbar ) { if( nPart == ControlPart::Button ) { iPart = TP_BUTTON; bool bChecked = ( aValue.getTristateVal() == ButtonValue::On ); if( !(nState & ControlState::ENABLED) ) //iState = TS_DISABLED; // disabled buttons are typically not painted at all but we need visual // feedback when travelling by keyboard over disabled entries iState = TS_HOT; else if( nState & ControlState::PRESSED ) iState = TS_PRESSED; else if( nState & ControlState::ROLLOVER ) iState = bChecked ? TS_HOTCHECKED : TS_HOT; else iState = bChecked ? TS_CHECKED : TS_NORMAL; return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } else if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert ) { // the vertical gripper is not supported in most themes and it makes no // sense to only support horizontal gripper //iPart = (nPart == ControlPart::ThumbHorz) ? RP_GRIPPERVERT : RP_GRIPPER; //return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } else if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert ) { if( aValue.getType() == ControlType::Toolbar ) { const ToolbarValue *pValue = static_cast(&aValue); if( pValue->mbIsTopDockingArea ) rc.top = 0; // extend potential gradient to cover menu bar as well } // make it more compatible with Aero if( ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames ) { impl_drawAeroToolbar( hDC, rc, nPart == ControlPart::DrawBackgroundHorz ); return true; } return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } } if( nType == ControlType::Menubar ) { if( nPart == ControlPart::Entire ) { if( aValue.getType() == ControlType::Menubar ) { const MenubarValue *pValue = static_cast(&aValue); rc.bottom += pValue->maTopDockingAreaHeight; // extend potential gradient to cover docking area as well // make it more compatible with Aero if( ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames ) { impl_drawAeroToolbar( hDC, rc, true ); return true; } } return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption); } else if( nPart == ControlPart::MenuItem ) { if( nState & ControlState::ENABLED ) { if( nState & ControlState::SELECTED ) iState = MBI_PUSHED; else if( nState & ControlState::ROLLOVER ) iState = MBI_HOT; else iState = MBI_NORMAL; } else { if( nState & ControlState::SELECTED ) iState = MBI_DISABLEDPUSHED; else if( nState & ControlState::ROLLOVER ) iState = MBI_DISABLEDHOT; else iState = MBI_DISABLED; } return ImplDrawTheme( hTheme, hDC, MENU_BARITEM, iState, rc, aCaption ); } } if( nType == ControlType::Progress ) { if( nPart != ControlPart::Entire ) return FALSE; if( ! ImplDrawTheme( hTheme, hDC, PP_BAR, iState, rc, aCaption) ) return false; RECT aProgressRect = rc; if( vsAPI.GetThemeBackgroundContentRect( hTheme, hDC, PP_BAR, iState, &rc, &aProgressRect) != S_OK ) return false; long nProgressWidth = aValue.getNumericVal(); nProgressWidth *= (aProgressRect.right - aProgressRect.left); nProgressWidth /= (rc.right - rc.left); if( AllSettings::GetLayoutRTL() ) aProgressRect.left = aProgressRect.right - nProgressWidth; else aProgressRect.right = aProgressRect.left + nProgressWidth; return ImplDrawTheme( hTheme, hDC, PP_CHUNK, iState, aProgressRect, aCaption ); } if( nType == ControlType::Slider ) { iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_TRACK : TKP_TRACKVERT; iState = (nPart == ControlPart::TrackHorzArea) ? static_cast(TRS_NORMAL) : static_cast(TRVS_NORMAL); tools::Rectangle aTrackRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() ); RECT aTRect = rc; if( nPart == ControlPart::TrackHorzArea ) { long nH = aTrackRect.GetHeight(); aTRect.top += (rc.bottom - rc.top - nH)/2; aTRect.bottom = aTRect.top + nH; } else { long nW = aTrackRect.GetWidth(); aTRect.left += (rc.right - rc.left - nW)/2; aTRect.right = aTRect.left + nW; } ImplDrawTheme( hTheme, hDC, iPart, iState, aTRect, aCaption ); RECT aThumbRect; OSL_ASSERT( aValue.getType() == ControlType::Slider ); const SliderValue* pVal = static_cast(&aValue); aThumbRect.left = pVal->maThumbRect.Left(); aThumbRect.top = pVal->maThumbRect.Top(); aThumbRect.right = pVal->maThumbRect.Right(); aThumbRect.bottom = pVal->maThumbRect.Bottom(); iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_THUMB : TKP_THUMBVERT; iState = (nState & ControlState::ENABLED) ? TUS_NORMAL : TUS_DISABLED; return ImplDrawTheme( hTheme, hDC, iPart, iState, aThumbRect, aCaption ); } if( nType == ControlType::ListNode ) { if( nPart != ControlPart::Entire ) return FALSE; ButtonValue aButtonValue = aValue.getTristateVal(); iPart = TVP_GLYPH; switch( aButtonValue ) { case ButtonValue::On: iState = GLPS_OPENED; break; case ButtonValue::Off: iState = GLPS_CLOSED; break; default: return FALSE; } return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption ); } if( GetSalData()->mbThemeMenuSupport ) { if( nType == ControlType::MenuPopup ) { if( nPart == ControlPart::Entire ) { RECT aGutterRC = rc; if( AllSettings::GetLayoutRTL() ) { aGutterRC.right -= aValue.getNumericVal()+1; aGutterRC.left = aGutterRC.right-3; } else { aGutterRC.left += aValue.getNumericVal(); aGutterRC.right = aGutterRC.left+3; } return ImplDrawTheme( hTheme, hDC, MENU_POPUPBACKGROUND, 0, rc, aCaption ) && ImplDrawTheme( hTheme, hDC, MENU_POPUPGUTTER, 0, aGutterRC, aCaption ) ; } else if( nPart == ControlPart::MenuItem ) { if( nState & ControlState::ENABLED ) iState = (nState & ControlState::SELECTED) ? MPI_HOT : MPI_NORMAL; else iState = (nState & ControlState::SELECTED) ? MPI_DISABLEDHOT : MPI_DISABLED; return ImplDrawTheme( hTheme, hDC, MENU_POPUPITEM, iState, rc, aCaption ); } else if( nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark ) { if( nState & ControlState::PRESSED ) { RECT aBGRect = rc; if( aValue.getType() == ControlType::MenuPopup ) { const MenupopupValue& rMVal( static_cast(aValue) ); aBGRect.top = rMVal.maItemRect.Top(); aBGRect.bottom = rMVal.maItemRect.Bottom()+1; // see below in drawNativeControl if( AllSettings::GetLayoutRTL() ) { aBGRect.right = rMVal.maItemRect.Right()+1; aBGRect.left = aBGRect.right - (rMVal.getNumericVal()-rMVal.maItemRect.Left()); } else { aBGRect.right = rMVal.getNumericVal(); aBGRect.left = rMVal.maItemRect.Left(); } rc = aBGRect; } iState = (nState & ControlState::ENABLED) ? MCB_NORMAL : MCB_DISABLED; ImplDrawTheme( hTheme, hDC, MENU_POPUPCHECKBACKGROUND, iState, aBGRect, aCaption ); if( nPart == ControlPart::MenuItemCheckMark ) iState = (nState & ControlState::ENABLED) ? MC_CHECKMARKNORMAL : MC_CHECKMARKDISABLED; else iState = (nState & ControlState::ENABLED) ? MC_BULLETNORMAL : MC_BULLETDISABLED; return ImplDrawTheme( hTheme, hDC, MENU_POPUPCHECK, iState, rc, aCaption ); } else return true; // unchecked: do nothing } else if( nPart == ControlPart::Separator ) { // adjust for gutter position if( AllSettings::GetLayoutRTL() ) rc.right -= aValue.getNumericVal()+1; else rc.left += aValue.getNumericVal()+1; tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, MENU_POPUPSEPARATOR, 0, tools::Rectangle( rc.left, rc.top, rc.right, rc.bottom ) ) ); // center the separator inside the passed rectangle long nDY = ((rc.bottom - rc.top + 1) - aRect.GetHeight()) / 2; rc.top += nDY; rc.bottom = rc.top+aRect.GetHeight()-1; return ImplDrawTheme( hTheme, hDC, MENU_POPUPSEPARATOR, 0, rc, aCaption ); } } } return false; } bool WinSalGraphics::drawNativeControl( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, ControlState nState, const ImplControlValue& aValue, const OUString& aCaption ) { bool bOk = false; HTHEME hTheme = nullptr; tools::Rectangle buttonRect = rControlRegion; tools::Rectangle cacheRect = rControlRegion; Size keySize = cacheRect.GetSize(); WinOpenGLSalGraphicsImpl* pImpl = dynamic_cast(mpImpl.get()); // tdf#95618 - A few controls render outside the region they're given. if (pImpl && nType == ControlType::TabItem) { tools::Rectangle rNativeBoundingRegion; tools::Rectangle rNativeContentRegion; if (getNativeControlRegion(nType, nPart, rControlRegion, nState, aValue, aCaption, rNativeBoundingRegion, rNativeContentRegion)) { cacheRect = rNativeBoundingRegion; keySize = rNativeBoundingRegion.GetSize(); } } ControlCacheKey aControlCacheKey(nType, nPart, nState, keySize); if (pImpl != nullptr && pImpl->TryRenderCachedNativeControl(aControlCacheKey, buttonRect.Left(), buttonRect.Top())) { return true; } switch( nType ) { case ControlType::Pushbutton: case ControlType::Radiobutton: case ControlType::Checkbox: hTheme = getThemeHandle( mhWnd, L"Button"); break; case ControlType::Scrollbar: hTheme = getThemeHandle( mhWnd, L"Scrollbar"); break; case ControlType::Combobox: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Edit"); else if( nPart == ControlPart::ButtonDown ) hTheme = getThemeHandle( mhWnd, L"Combobox"); break; case ControlType::Spinbox: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Edit"); else hTheme = getThemeHandle( mhWnd, L"Spin"); break; case ControlType::SpinButtons: hTheme = getThemeHandle( mhWnd, L"Spin"); break; case ControlType::Editbox: case ControlType::MultilineEditbox: hTheme = getThemeHandle( mhWnd, L"Edit"); break; case ControlType::Listbox: if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow ) hTheme = getThemeHandle( mhWnd, L"Listview"); else if( nPart == ControlPart::ButtonDown ) hTheme = getThemeHandle( mhWnd, L"Combobox"); break; case ControlType::TabPane: case ControlType::TabBody: case ControlType::TabItem: hTheme = getThemeHandle( mhWnd, L"Tab"); break; case ControlType::Toolbar: if( nPart == ControlPart::Entire || nPart == ControlPart::Button ) hTheme = getThemeHandle( mhWnd, L"Toolbar"); else // use rebar for grip and background hTheme = getThemeHandle( mhWnd, L"Rebar"); break; case ControlType::Menubar: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Rebar"); else if( GetSalData()->mbThemeMenuSupport ) { if( nPart == ControlPart::MenuItem ) hTheme = getThemeHandle( mhWnd, L"Menu" ); } break; case ControlType::Progress: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"Progress"); break; case ControlType::ListNode: if( nPart == ControlPart::Entire ) hTheme = getThemeHandle( mhWnd, L"TreeView"); break; case ControlType::Slider: if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea ) hTheme = getThemeHandle( mhWnd, L"Trackbar" ); break; case ControlType::MenuPopup: if( GetSalData()->mbThemeMenuSupport ) { if( nPart == ControlPart::Entire || nPart == ControlPart::MenuItem || nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark || nPart == ControlPart::Separator ) hTheme = getThemeHandle( mhWnd, L"Menu" ); } break; default: hTheme = nullptr; break; } if( !hTheme ) return false; RECT rc; rc.left = buttonRect.Left(); rc.right = buttonRect.Right()+1; rc.top = buttonRect.Top(); rc.bottom = buttonRect.Bottom()+1; OUString aCaptionStr(aCaption.replace('~', '&')); // translate mnemonics if (pImpl == nullptr) { // set default text alignment int ta = SetTextAlign(getHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP); bOk = ImplDrawNativeControl(getHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr); // restore alignment SetTextAlign(getHDC(), ta); } else { // We can do OpenGL OpenGLCompatibleDC aBlackDC(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1); SetTextAlign(aBlackDC.getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP); aBlackDC.fill(MAKE_SALCOLOR(0, 0, 0)); OpenGLCompatibleDC aWhiteDC(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1); SetTextAlign(aWhiteDC.getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP); aWhiteDC.fill(MAKE_SALCOLOR(0xff, 0xff, 0xff)); if (ImplDrawNativeControl(aBlackDC.getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr) && ImplDrawNativeControl(aWhiteDC.getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr)) { bOk = pImpl->RenderAndCacheNativeControl(aWhiteDC, aBlackDC, cacheRect.Left(), cacheRect.Top(), aControlCacheKey); } } return bOk; } bool WinSalGraphics::getNativeControlRegion( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, ControlState nState, const ImplControlValue& rControlValue, const OUString&, tools::Rectangle &rNativeBoundingRegion, tools::Rectangle &rNativeContentRegion ) { bool bRet = FALSE; // FIXME: rNativeBoundingRegion has a different origin // depending on which part is used; horrors. HDC hDC = GetDC( mhWnd ); if( nType == ControlType::Toolbar ) { if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert ) { /* // the vertical gripper is not supported in most themes and it makes no // sense to only support horizontal gripper HTHEME hTheme = getThemeHandle( mhWnd, L"Rebar"); if( hTheme ) { tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, nPart == ControlPart::ThumbHorz ? RP_GRIPPERVERT : RP_GRIPPER, 0, rControlRegion.GetBoundRect() ) ); if( nPart == ControlPart::ThumbHorz && !aRect.IsEmpty() ) { tools::Rectangle aVertRect( 0, 0, aRect.getHeight(), aRect.getWidth() ); rNativeContentRegion = aVertRect; } else rNativeContentRegion = aRect; rNativeBoundingRegion = rNativeContentRegion; if( !rNativeContentRegion.IsEmpty() ) bRet = TRUE; } */ } if( nPart == ControlPart::Button ) { HTHEME hTheme = getThemeHandle( mhWnd, L"Toolbar"); if( hTheme ) { tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, TP_SPLITBUTTONDROPDOWN, TS_HOT, rControlRegion ) ); rNativeContentRegion = aRect; rNativeBoundingRegion = rNativeContentRegion; if( !rNativeContentRegion.IsEmpty() ) bRet = TRUE; } } } if( nType == ControlType::Progress && nPart == ControlPart::Entire ) { HTHEME hTheme = getThemeHandle( mhWnd, L"Progress"); if( hTheme ) { tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, PP_BAR, 0, rControlRegion ) ); rNativeContentRegion = aRect; rNativeBoundingRegion = rNativeContentRegion; if( !rNativeContentRegion.IsEmpty() ) bRet = TRUE; } } if( (nType == ControlType::Listbox || nType == ControlType::Combobox ) && nPart == ControlPart::Entire ) { HTHEME hTheme = getThemeHandle( mhWnd, L"Combobox"); if( hTheme ) { tools::Rectangle aBoxRect( rControlRegion ); tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, CP_DROPDOWNBUTTON, CBXS_NORMAL, aBoxRect ) ); if( aRect.GetHeight() > aBoxRect.GetHeight() ) aBoxRect.Bottom() = aBoxRect.Top() + aRect.GetHeight(); if( aRect.GetWidth() > aBoxRect.GetWidth() ) aBoxRect.Right() = aBoxRect.Left() + aRect.GetWidth(); rNativeContentRegion = aBoxRect; rNativeBoundingRegion = rNativeContentRegion; if( !aRect.IsEmpty() ) bRet = TRUE; } } if( (nType == ControlType::Editbox || nType == ControlType::Spinbox) && nPart == ControlPart::Entire ) { HTHEME hTheme = getThemeHandle( mhWnd, L"Edit"); if( hTheme ) { // get border size tools::Rectangle aBoxRect( rControlRegion ); tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, EP_BACKGROUNDWITHBORDER, EBWBS_HOT, aBoxRect ) ); // ad app font height NONCLIENTMETRICSW aNonClientMetrics; aNonClientMetrics.cbSize = sizeof( aNonClientMetrics ); if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) ) { long nFontHeight = aNonClientMetrics.lfMessageFont.lfHeight; if( nFontHeight < 0 ) nFontHeight = -nFontHeight; if( aRect.GetHeight() && nFontHeight ) { aRect.Bottom() += aRect.GetHeight(); aRect.Bottom() += nFontHeight; if( aRect.GetHeight() > aBoxRect.GetHeight() ) aBoxRect.Bottom() = aBoxRect.Top() + aRect.GetHeight(); if( aRect.GetWidth() > aBoxRect.GetWidth() ) aBoxRect.Right() = aBoxRect.Left() + aRect.GetWidth(); rNativeContentRegion = aBoxRect; rNativeBoundingRegion = rNativeContentRegion; bRet = TRUE; } } } } if( GetSalData()->mbThemeMenuSupport ) { if( nType == ControlType::MenuPopup ) { if( nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark ) { HTHEME hTheme = getThemeHandle( mhWnd, L"Menu"); tools::Rectangle aBoxRect( rControlRegion ); tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, aBoxRect ) ); if( aBoxRect.GetWidth() && aBoxRect.GetHeight() ) { rNativeContentRegion = aRect; rNativeBoundingRegion = rNativeContentRegion; bRet = TRUE; } } } } if( nType == ControlType::Slider && ( (nPart == ControlPart::ThumbHorz) || (nPart == ControlPart::ThumbVert) ) ) { HTHEME hTheme = getThemeHandle( mhWnd, L"Trackbar"); if( hTheme ) { int iPart = (nPart == ControlPart::ThumbHorz) ? TKP_THUMB : TKP_THUMBVERT; int iState = (nPart == ControlPart::ThumbHorz) ? static_cast(TUS_NORMAL) : static_cast(TUVS_NORMAL); tools::Rectangle aThumbRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() ); if( nPart == ControlPart::ThumbHorz ) { long nW = aThumbRect.GetWidth(); tools::Rectangle aRect( rControlRegion ); aRect.Right() = aRect.Left() + nW - 1; rNativeContentRegion = aRect; rNativeBoundingRegion = rNativeContentRegion; } else { long nH = aThumbRect.GetHeight(); tools::Rectangle aRect( rControlRegion ); aRect.Bottom() = aRect.Top() + nH - 1; rNativeContentRegion = aRect; rNativeBoundingRegion = rNativeContentRegion; } bRet = TRUE; } } if ( ( nType == ControlType::TabItem ) && ( nPart == ControlPart::Entire ) ) { tools::Rectangle aControlRect( rControlRegion ); rNativeContentRegion = aControlRect; --aControlRect.Bottom(); if( rControlValue.getType() == ControlType::TabItem ) { const TabitemValue *pValue = static_cast(&rControlValue); if ( pValue->isBothAligned() ) --aControlRect.Right(); if ( nState & ControlState::SELECTED ) { aControlRect.Left() -= 2; if ( pValue && !pValue->isBothAligned() ) { if ( pValue->isLeftAligned() || pValue->isNotAligned() ) aControlRect.Right() += 2; if ( pValue->isRightAligned() ) aControlRect.Right() += 1; } aControlRect.Top() -= 2; aControlRect.Bottom() += 2; } } rNativeBoundingRegion = aControlRect; bRet = TRUE; } ReleaseDC( mhWnd, hDC ); return bRet; } void WinSalGraphics::updateSettingsNative( AllSettings& rSettings ) { if ( !vsAPI.IsThemeActive() ) return; StyleSettings aStyleSettings = rSettings.GetStyleSettings(); ImplSVData* pSVData = ImplGetSVData(); // don't draw frame around each and every toolbar pSVData->maNWFData.mbDockingAreaAvoidTBFrames = true; // FIXME get the color directly from the theme, not from the settings Color aMenuBarTextColor = aStyleSettings.GetPersonaMenuBarTextColor().get_value_or( aStyleSettings.GetMenuTextColor() ); // in aero menuitem highlight text is drawn in the same color as normal aStyleSettings.SetMenuHighlightTextColor( aStyleSettings.GetMenuTextColor() ); aStyleSettings.SetMenuBarRolloverTextColor( aMenuBarTextColor ); aStyleSettings.SetMenuBarHighlightTextColor( aMenuBarTextColor ); pSVData->maNWFData.mnMenuFormatBorderX = 2; pSVData->maNWFData.mnMenuFormatBorderY = 2; pSVData->maNWFData.maMenuBarHighlightTextColor = aMenuBarTextColor; GetSalData()->mbThemeMenuSupport = true; rSettings.SetStyleSettings( aStyleSettings ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */