// Classic Shell (c) 2009-2017, Ivo Beltchev // Open-Shell (c) 2017-2018, The Open-Shell Team // Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author #include "stdafx.h" #include "resource.h" #include "SkinManager.h" #include "ItemManager.h" #include "LogManager.h" #include "SettingsParser.h" #include "Settings.h" #include "SettingsUI.h" #include "Translations.h" #include "ResourceHelper.h" #include "FNVHash.h" #include "dllmain.h" #include "IatHookHelper.h" #include "MenuContainer.h" #include "ProgramsTree.h" #include #include //#define SIMULATE_METRO_COLORS "win8_blue" #ifdef BUILD_SETUP #undef SIMULATE_METRO_COLORS #endif static struct { const wchar_t *name; int code; } g_SystemColors[]= { {L"SystemScrollbar",COLOR_SCROLLBAR}, {L"SystemBackground",COLOR_BACKGROUND}, {L"SystemActiveCaption",COLOR_ACTIVECAPTION}, {L"SystemInactiveCaption",COLOR_INACTIVECAPTION}, {L"SystemMenu",COLOR_MENU}, {L"SystemWindow",COLOR_WINDOW}, {L"SystemWindowFrame",COLOR_WINDOWFRAME}, {L"SystemMenuText",COLOR_MENUTEXT}, {L"SystemWindowText",COLOR_WINDOWTEXT}, {L"SystemCaptionText",COLOR_CAPTIONTEXT}, {L"SystemActiveBorder",COLOR_ACTIVEBORDER}, {L"SystemInactiveBorder",COLOR_INACTIVEBORDER}, {L"SystemAppWorkspace",COLOR_APPWORKSPACE}, {L"SystemHighlight",COLOR_HIGHLIGHT}, {L"SystemHighlightText",COLOR_HIGHLIGHTTEXT}, {L"SystemBtnFace",COLOR_BTNFACE}, {L"SystemBtnShadow",COLOR_BTNSHADOW}, {L"SystemGrayText",COLOR_GRAYTEXT}, {L"SystemBtnText",COLOR_BTNTEXT}, {L"SystemInactiveCaptionText",COLOR_INACTIVECAPTIONTEXT}, {L"SystemBtnHighlight",COLOR_BTNHIGHLIGHT}, {L"System3DDKShadow",COLOR_3DDKSHADOW}, {L"System3DLight",COLOR_3DLIGHT}, {L"SystemInfoText",COLOR_INFOTEXT}, {L"SystemInfoBK",COLOR_INFOBK}, {L"SystemHotLight",COLOR_HOTLIGHT}, {L"SystemGradientActiveCaption",COLOR_GRADIENTACTIVECAPTION}, {L"SystemGradientInactiveCaption",COLOR_GRADIENTINACTIVECAPTION}, {L"SystemMenuHilight",COLOR_MENUHILIGHT}, {L"SystemMenuBar",COLOR_MENUBAR}, }; wchar_t MenuSkin::s_SkinError[1024]; const RECT DEFAULT_ICON_PADDING={3,3,3,3}; const RECT DEFAULT_TEXT_PADDING={1,2,8,2}; const SIZE DEFAULT_ARROW_PADDING={5,7}; const int DEFAULT_SEPARATOR_WIDTH=4; const int DEFAULT_SEPARATOR_HEIGHT=8; const POINT DEFAULT_USER_IMAGE_PADDING={-4,8}; const RECT DEFAULT_SEARCH_PADDING={4,4,4,4}; static int g_GlassColorR, g_GlassColorG, g_GlassColorB, g_GlassColorA; #ifdef SIMULATE_METRO_COLORS struct SimMetroColor { unsigned int nameHash; COLORREF color; }; static std::vector g_MetroColors; static int GetImmersiveUserColorSetPreference( bool bForceCheckRegistry, bool bSkipCheckOnFail ) { return 0; } static COLORREF GetImmersiveColorFromColorSetEx( UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode ) { return g_MetroColors[dwImmersiveColorType].color; } static int GetImmersiveColorTypeFromName( const wchar_t *name ) { if (wcsncmp(name,L"Immersive",9)==0) { unsigned int crc=CalcFNVHash(name+9); for (int i=0;i<(int)g_MetroColors.size();i++) if (g_MetroColors[i].nameHash==crc) return i; } return -1; } #else typedef int (WINAPI *TGetImmersiveUserColorSetPreference)(bool bForceCheckRegistry, bool bSkipCheckOnFail); typedef COLORREF (WINAPI *TGetImmersiveColorFromColorSetEx)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode); typedef int (WINAPI *TGetImmersiveColorTypeFromName)(const wchar_t *name); static TGetImmersiveUserColorSetPreference GetImmersiveUserColorSetPreference; static TGetImmersiveColorFromColorSetEx GetImmersiveColorFromColorSetEx; static TGetImmersiveColorTypeFromName GetImmersiveColorTypeFromName; #endif static int g_CurrentMetroColorSet; void MenuBitmap::Init( bool bIsColor ) { bIsBitmap=!bIsColor; bIsOwned=false; bitmap=NULL; } void MenuBitmap::Reset( bool bIsColor ) { if (bIsOwned && GetBitmap()) { BOOL res=DeleteObject(bitmap); Assert(res); } Init(bIsColor); } MenuSkin::MenuSkin( void ) { Hash=0; AboutIcon=NULL; BOwnHintFont=false; BHasScrollbar=false; BHasMetroColors=false; Main_bitmap.Init(); Main_bitmap_search.Init(); Main_bitmap_jump.Init(); Caption_font=NULL; User_font=NULL; Main_separatorV.Init(); Main_pager.Init(); Main_pager_arrows.Init(); User_bitmap.Init(); User_mask.Init(); Programs_icon.Init(); Programs_icon_selected.Init(); Scrollbar_button.Init(); Scrollbar_arrows.Init(); Scrollbar_background.Init(); Scrollbar_thumb.Init(); Scrollbar_gripper.Init(); Submenu_bitmap.Init(); Submenu_separatorV.Init(); Submenu_pager.Init(); Submenu_pager_arrows.Init(); Pin_bitmap.Init(); More_bitmap.Init(); Shutdown_bitmap.Init(); Search_bitmap.Init(); Search_arrow.Init(); Search_background.Init(); Search_background_search.Init(); Search_background_jump.Init(); for (int i=0;i<_countof(Main_emblems);i++) Main_emblems[i].Init(); Main_emblem_mask.Init(); Main_emblem_search_mask.Init(); Main_emblem_jump_mask.Init(); Search_emblem_mask.Init(); Search_emblem_search_mask.Init(); Search_emblem_jump_mask.Init(); for (int i=0;i<_countof(Patterns);i++) Patterns[i].Init(); Main_pattern_mask.Init(); Main_pattern_search_mask.Init(); Main_pattern_jump_mask.Init(); Search_pattern_mask.Init(); Search_pattern_search_mask.Init(); Search_pattern_jump_mask.Init(); Search_hint_font=NULL; Search_underline_font=NULL; for (int i=0;i<_countof(ItemSettings);i++) ItemSettings[i].Init(); } void MenuSkin::ItemDrawSettings::Init( void ) { font=NULL; bOwnFont=true; glowSize=0; memset(textColors,0,sizeof(textColors)); memset(textShadowColors,-1,sizeof(textShadowColors)); memset(&textPadding,0,sizeof(textPadding)); memset(selSlicesX,0,sizeof(selSlicesX)); memset(selSlicesY,0,sizeof(selSlicesY)); arrSize.cx=arrSize.cy=0; memset(arrColors,0,sizeof(arrColors)); arrPadding.cx=arrPadding.cy=0; memset(frameSlicesX,0,sizeof(frameSlicesX)); memset(frameSlicesY,0,sizeof(frameSlicesY)); iconFrameOffset.x=iconFrameOffset.y=0; memset(&iconPadding,0,sizeof(iconPadding)); memset(sepSlicesX,0,sizeof(sepSlicesX)); sepHeight=0; opacity=MenuSkin::OPACITY_SOLID; textTopOffset=iconTopOffset=0; bmpSelection.Init(); bmpArrow.Init(); bmpIconFrame.Init(); bmpSeparator.Init(); } void MenuSkin::ItemDrawSettings::Reset( void ) { if (font && bOwnFont) { DeleteObject(font); font=NULL; bOwnFont=true; } bmpSelection.Reset(); bmpArrow.Reset(); bmpIconFrame.Reset(); bmpSeparator.Reset(); } MenuSkin::~MenuSkin( void ) { Reset(); } void MenuSkin::Reset( void ) { if (AboutIcon) DestroyIcon(AboutIcon); AboutIcon=NULL; Main_bitmap.Reset(); Main_bitmap_search.Reset(); Main_bitmap_jump.Reset(); if (Caption_font) DeleteObject(Caption_font); Caption_font=NULL; if (User_font) DeleteObject(User_font); User_font=NULL; Main_separatorV.Reset(); User_bitmap.Reset(); User_mask.Reset(); Programs_icon.Reset(); Programs_icon_selected.Reset(); Scrollbar_button.Reset(); Scrollbar_arrows.Reset(); Scrollbar_background.Reset(); Scrollbar_thumb.Reset(); Scrollbar_gripper.Reset(); Submenu_bitmap.Reset(); Submenu_separatorV.Reset(); Main_pager.Reset(); Main_pager_arrows.Reset(); Submenu_pager.Reset(); Submenu_pager_arrows.Reset(); Pin_bitmap.Reset(); More_bitmap.Reset(); Shutdown_bitmap.Reset(); Search_bitmap.Reset(); Search_arrow.Reset(); Search_background.Reset(); Search_background_search.Reset(); Search_background_jump.Reset(); for (int i=0;i<_countof(Main_emblems);i++) Main_emblems[i].Reset(); Main_emblem_mask.Reset(); Main_emblem_search_mask.Reset(); Main_emblem_jump_mask.Reset(); Search_emblem_mask.Reset(); Search_emblem_search_mask.Reset(); Search_emblem_jump_mask.Reset(); for (int i=0;i<_countof(Patterns);i++) { Patterns[i].Reset(); PatternBits[i].clear(); } Main_pattern_mask.Reset(); Main_pattern_search_mask.Reset(); Main_pattern_jump_mask.Reset(); Search_pattern_mask.Reset(); Search_pattern_search_mask.Reset(); Search_pattern_jump_mask.Reset(); PatternPretileWidth=0; if (Search_hint_font && BOwnHintFont) DeleteObject(Search_hint_font); BOwnHintFont=false; BHasScrollbar=false; BHasMetroColors=false; Search_hint_font=NULL; if (Search_underline_font) DeleteObject(Search_underline_font); Search_underline_font=NULL; for (int i=0;i<_countof(ItemSettings);i++) ItemSettings[i].Reset(); for (std::vector::const_iterator it=CustomBitmaps.begin();it!=CustomBitmaps.end();++it) DeleteObject(it->bitmap); CustomBitmaps.clear(); Options.clear(); Variations.clear(); Hash=0; MetroColorHash=0; MetroColors.clear(); } static void GetErrorMessage( wchar_t *err, int size, DWORD code ) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,NULL,code,0,err,size,NULL); } int MenuSkin::ScaleSkinElement( int num, int scale ) const { int ddpi=Dpi-96; if (ddpi<=0) return num; if (num<0) return (num*(9600+ddpi*scale)-4800)/9600; else return (num*(9600+ddpi*scale)+4800)/9600; } COLORREF MenuSkin::GetMetroColor( const wchar_t *names ) const { unsigned int crc=CalcFNVHash(names); std::map::const_iterator it=MetroColors.find(crc); if (it!=MetroColors.end()) return it->second.color; MetroColor data={0}; wchar_t name[100]; while (*names) { if (names[0]=='#') { wchar_t *end; data.color=wcstoul(names+1,&end,16); break; } if (names[0]=='$') names++; names=GetToken(names,name,_countof(name),L"|"); bool bFound=false; if (wcsncmp(name,L"System",6)==0) { for (int i=0;i<_countof(g_SystemColors);i++) { if (wcscmp(name,g_SystemColors[i].name)==0) { data.colorType=-2-g_SystemColors[i].code; COLORREF color=GetSysColor(g_SystemColors[i].code); data.color=((color&0xFF)<<16)|(color&0xFF00)|((color>>16)&0xFF); bFound=true; break; } } } if (!bFound) { BHasMetroColors=true; if (GetImmersiveUserColorSetPreference!=NULL) { wchar_t text[256]; if (wcsncmp(name,L"Immersive",9)==0) wcscpy_s(text,name); else Sprintf(text,_countof(text),L"Immersive%s",name); int type=GetImmersiveColorTypeFromName(text); data.colorType=type<0?-1:type; if (type>=0) { DWORD color=GetImmersiveColorFromColorSetEx(g_CurrentMetroColorSet,type,true,0); data.color=((color&0xFF)<<16)|(color&0xFF00)|((color>>16)&0xFF); bFound=true; } } } if (bFound) break; } data.color|=0xFF000000; MetroColors[crc]=data; return data.color; } unsigned int MenuSkin::CalcMetroColorHash( int set ) const { // if set is -1 returns the currently cached colors unsigned int hash=FNV_HASH0; for (std::map::const_iterator it=MetroColors.begin();it!=MetroColors.end();++it) { COLORREF color=0; if (set==-1) color=it->second.color; else if (it->second.colorType<-1) { color=GetSysColor(-2-it->second.colorType); color=((color&0xFF)<<16)|(color&0xFF00)|((color>>16)&0xFF); } else if (it->second.colorType>=0) { color=GetImmersiveColorFromColorSetEx(set,it->second.colorType,true,0); color=((color&0xFF)<<16)|(color&0xFF00)|((color>>16)&0xFF); } color|=0xFF000000; hash=CalcFNVHash(&color,sizeof(color),hash); } return hash; } int MenuSkin::LoadSkinNumbers( const wchar_t *str, int *numbers, int count, TSkinNumberType type ) const { memset(numbers,0,count*4); for (int i=0;i>16)&0xFF); else if (type==NUMBERS_COLORS_ABGR) { if (Strlen(token+1)==6) num|=0xFF000000; numbers[i]=((num&0xFF)<<16)|(num&0xFF00FF00)|((num>>16)&0xFF); } else numbers[i]=num; } if (type==NUMBERS_PADDING || type==NUMBERS_SIZE) { int scale=0; for (int i=0;i100) scale=100; } numbers[i]=ScaleSkinElement(numbers[i],scale); } } return count; } RECT MenuSkin::ScaleSkinElement( const RECT &rect ) const { RECT res; res.left=ScaleSkinElement(rect.left); res.right=ScaleSkinElement(rect.right); res.top=ScaleSkinElement(rect.top); res.bottom=ScaleSkinElement(rect.bottom); return res; } POINT MenuSkin::ScaleSkinElement( const POINT &point ) const { POINT res; res.x=ScaleSkinElement(point.x); res.y=ScaleSkinElement(point.y); return res; } SIZE MenuSkin::ScaleSkinElement( const SIZE &size ) const { SIZE res; res.cx=ScaleSkinElement(size.cx); res.cy=ScaleSkinElement(size.cy); return res; } _Success_(return != FALSE) BOOL WINAPI SystemParametersInfoForDpi(_In_ UINT uiAction, _In_ UINT uiParam, _Pre_maybenull_ _Post_valid_ PVOID pvParam, _In_ UINT fWinIni, _In_ UINT dpi) { static auto p = static_cast((void*)GetProcAddress(GetModuleHandle(L"user32.dll"), "SystemParametersInfoForDpi")); if (p) return p(uiAction, uiParam, pvParam, fWinIni, dpi); // fall-back for older systems return SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni); } HFONT MenuSkin::LoadSkinFont( const wchar_t *str, const wchar_t *name, int weight, float size, bool bScale ) const { DWORD quality=DEFAULT_QUALITY; int smoothing=GetSettingInt(L"FontSmoothing"); if (smoothing==1) quality=NONANTIALIASED_QUALITY; else if (smoothing==2) quality=ANTIALIASED_QUALITY; if (smoothing==3) quality=CLEARTYPE_QUALITY; wchar_t token[256]; bool bItalic=false; int scale=bScale?100:0; if (str) { if (_wcsicmp(str,L"none")==0) return NULL; str=GetToken(str,token,_countof(token),L", \t"); name=token; wchar_t token2[256]; str=GetToken(str,token2,_countof(token2),L", \t"); weight=FW_NORMAL; if (_wcsicmp(token2,L"bold")==0) weight=FW_BOLD; else if (_wcsicmp(token2,L"italic")==0) bItalic=true; else if (_wcsicmp(token2,L"bold_italic")==0) weight=FW_BOLD, bItalic=true; str=GetToken(str,token2,_countof(token2),L", \t"); size=(float)_wtof(token2); str=GetToken(str,token2,_countof(token2),L", \t"); if (token2[0] && token2[Strlen(token2)-1]=='%') { scale=_wtol(token2); if (scale<0) scale=0; if (scale>100) scale=100; } } else if (!name) { // get the default menu font NONCLIENTMETRICS metrics={sizeof(metrics)}; SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS,sizeof(metrics),&metrics,0,Dpi); metrics.lfMenuFont.lfQuality=(BYTE)quality; return CreateFontIndirect(&metrics.lfMenuFont); } size=ScaleSkinElement((int)(size*96),scale)/72.f; return CreateFont((int)size,0,0,0,weight,bItalic?1:0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,quality,DEFAULT_PITCH,name); } static HICON LoadSkinIcon( HMODULE hMod, int index ) { if (hMod) { return (HICON)LoadImage(hMod,MAKEINTRESOURCE(index),IMAGE_ICON,0,0,LR_DEFAULTSIZE); } else { wchar_t path[_MAX_PATH]; GetSkinsPath(path); wchar_t fname[_MAX_PATH]; Sprintf(fname,_countof(fname),L"%s%d.ico",path,index); return (HICON)LoadImage(NULL,fname,IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); } } struct DWMCOLORIZATIONPARAMS { DWORD ColorizationColor; DWORD ColorizationAfterglow; DWORD ColorizationColorBalance; DWORD ColorizationAfterglowBalance; DWORD ColorizationBlurBalance; DWORD ColorizationGlassReflectionIntensity; DWORD ColorizationOpaqueBlend; DWORD extra; // Win8 has extra parameter }; static bool GetSkinColorizationParameters( DWMCOLORIZATIONPARAMS *params ) { typedef HRESULT (WINAPI *tGetColorizationParameters)(DWMCOLORIZATIONPARAMS *params); // HACK: the system function DwmGetColorizationColor is buggy on Win 7. its calculations can overflow and return a totally wrong value // (try orange color with full intensity and no transparency - you'll get alpha=0 and green color). so here we use the undocumented // function GetColorizationParameters exported by dwmapi.dll, ordinal 127 and then compute the colors manually using integer math HMODULE hMod=GetModuleHandle(L"dwmapi.dll"); if (!hMod) return false; tGetColorizationParameters GetColorizationParameters=(tGetColorizationParameters)GetProcAddress(hMod,MAKEINTRESOURCEA(127)); if (!GetColorizationParameters || FAILED(GetColorizationParameters(params))) return false; if (GetWinVersion()>=WIN_VER_WIN8) params->ColorizationOpaqueBlend=0; // Win8 has no transparency return true; } COLORREF GetSystemGlassColor8( void ) { COLORREF color=0; DWMCOLORIZATIONPARAMS params; if (GetSkinColorizationParameters(¶ms)) { int r=(params.ColorizationColor>>16)&255; int g=(params.ColorizationColor>>8)&255; int b=(params.ColorizationColor)&255; int gray=217*(100-params.ColorizationColorBalance)+50; r=(r*params.ColorizationColorBalance+gray)/100; g=(g*params.ColorizationColorBalance+gray)/100; b=(b*params.ColorizationColorBalance+gray)/100; r=(r*200+127)/255; g=(g*200+127)/255; b=(b*200+127)/255; color=(r<<16)|(g<<8)|b|(params.ColorizationColor&0xFF000000); } return color; } void GetSystemGlassColor( int &dr, int &dg, int &db, int &da, int &dc ) { dr=dg=db=da=dc=0; DWMCOLORIZATIONPARAMS params; if (GetSkinColorizationParameters(¶ms)) { // boost the color balance to better match the Windows 7 menu params.ColorizationColorBalance=(int)(100.f*powf(params.ColorizationColorBalance/100.f,0.5f)); int ir=(params.ColorizationColor>>16)&255; int ig=(params.ColorizationColor>>8)&255; int ib=(params.ColorizationColor)&255; int ir2=(params.ColorizationAfterglow>>16)&255; int ig2=(params.ColorizationAfterglow>>8)&255; int ib2=(params.ColorizationAfterglow)&255; int brightness=(ir*21+ig*72+ib*7)/255; // [0..100] int glowBalance=(brightness*params.ColorizationAfterglowBalance)/100; // [0..100] dr=MulDiv(ir2*glowBalance+ir*100,params.ColorizationColorBalance*255,10000); dg=MulDiv(ig2*glowBalance+ig*100,params.ColorizationColorBalance*255,10000); db=MulDiv(ib2*glowBalance+ib*100,params.ColorizationColorBalance*255,10000); dc=(glowBalance+100)*params.ColorizationColorBalance*255/10000; da=(100-params.ColorizationAfterglowBalance-params.ColorizationBlurBalance)*255/100; if (params.ColorizationOpaqueBlend || da>=255) da=255; else if (da<=0) dr=dg=db=da=0; } } void GetMetroGlassColor( int &dr, int &dg, int &db ) { if (GetImmersiveUserColorSetPreference!=NULL) { int type=GetImmersiveColorTypeFromName(L"ImmersiveStartBackground"); if (type>=0) { int set=GetImmersiveUserColorSetPreference(false,false); DWORD color=GetImmersiveColorFromColorSetEx(set,type,true,0); db=((color>>16)&255); dg=((color>>8)&255); dr=((color)&255); return; } } dr=dg=db=0; } COLORREF GetMetroTaskbarColor( bool &bTransparent ) { bTransparent=true; if (GetImmersiveUserColorSetPreference!=NULL) { CRegKey regTheme; if (regTheme.Open(HKEY_CURRENT_USER,L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",KEY_READ)==ERROR_SUCCESS) { DWORD trans=1; bTransparent=regTheme.QueryDWORDValue(L"EnableTransparency",trans)!=ERROR_SUCCESS || trans; } int type=GetImmersiveColorTypeFromName(bTransparent?L"ImmersiveSystemAccentDark3":L"ImmersiveSystemAccentDark2"); if (type>=0) { int set=GetImmersiveUserColorSetPreference(false,false); return GetImmersiveColorFromColorSetEx(set,type,true,0); } } return 0; } COLORREF GetSystemAccentColor( void ) { if (GetImmersiveUserColorSetPreference!=NULL && GetWinVersion()>=WIN_VER_WIN10) { int type=GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"); if (type>=0) { int set=GetImmersiveUserColorSetPreference(false,false); return GetImmersiveColorFromColorSetEx(set,type,true,0); } } return 0; } static void GetMenuGlassColor( int &dr, int &dg, int &db, int &da ) { if (GetWinVersion()>=WIN_VER_WIN10) { bool bDef=true; if (GetSettingBool(L"GlassOverride")) { COLORREF color=GetSettingInt(L"GlassColor",bDef); db=((color>>16)&255); dg=((color>>8)&255); dr=((color)&255); } if (bDef) GetMetroGlassColor(dr,dg,db); dr*=255; dg*=255; db*=255; da=255; } else { bool bDef=true; if (GetSettingBool(L"GlassOverride")) { bool bDef1, bDef2, bDef3; COLORREF color=GetSettingInt(L"GlassColor",bDef1); int intensity=GetSettingInt(L"GlassIntensity",bDef2); da=GetSettingInt(L"GlassBlending",bDef3); bDef=bDef1 && bDef2 && bDef3; if (!bDef) { db=((color>>16)&255); dg=((color>>8)&255); dr=((color)&255); if (intensity<0) intensity=0; if (intensity>100) intensity=100; intensity=(intensity*255)/100; dr*=intensity; dg*=intensity; db*=intensity; if (da<0) da=0; if (da>100) da=100; da=((100-da)*255)/100; } } if (bDef) { int dc; GetSystemGlassColor(dr,dg,db,da,dc); } } } const COLORREF DEFAULT_GLASS_COLOR=0x00000000; const COLORREF DEFAULT_GLASS_CONFLICT=0x00000001; static void BlendColor( int &r, int &g, int &b, int a1, int a2, int a3, COLORREF tintColor1, COLORREF tintColor2, COLORREF tintColor3 ) { int tr1, tg1, tb1; int ta2=tintColor2>>24, tr2=(tintColor2)&255, tg2=(tintColor2>>8)&255, tb2=(tintColor2>>16)&255; int ta3=tintColor3>>24, tr3=(tintColor3)&255, tg3=(tintColor3>>8)&255, tb3=(tintColor3>>16)&255; int a1m=1; if (tintColor1==DEFAULT_GLASS_COLOR) { a1m=g_GlassColorA; tr1=g_GlassColorR; tg1=g_GlassColorG; tb1=g_GlassColorB; } else { a1*=tintColor1>>24; tr1=(tintColor1)&255; tg1=(tintColor1>>8)&255; tb1=(tintColor1>>16)&255; } a2*=ta2; a3*=ta3; int a4=255*255-a1*a1m-a2-a3; if (a4<0) a4=0; r=(tr1*a1+tr2*a2+tr3*a3+r*a4)/(255*255); g=(tg1*a1+tg2*a2+tg3*a3+g*a4)/(255*255); b=(tb1*a1+tb2*a2+tb3*a3+b*a4)/(255*255); if (r>255) r=255; if (g>255) g=255; if (b>255) b=255; } static void BlendColor( COLORREF &color, COLORREF mask, COLORREF tintColor1, COLORREF tintColor2, COLORREF tintColor3 ) { int r=(color)&255, g=(color>>8)&255, b=(color>>16)&255; int a1=(mask)&255, a2=(mask>>8)&255, a3=(mask>>16)&255; BlendColor(r,g,b,a1,a2,a3,tintColor1,tintColor2,tintColor3); color=(b<<16)|(g<<8)|r; } static HBITMAP DuplicateBitmap( HBITMAP src ) { BITMAP info; GetObject(src,sizeof(info),&info); BITMAPINFO bi={0}; bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth=info.bmWidth; bi.bmiHeader.biHeight=info.bmHeight; bi.bmiHeader.biPlanes=info.bmPlanes; bi.bmiHeader.biBitCount=info.bmBitsPixel; HDC hdc=CreateCompatibleDC(NULL); void *bits; HBITMAP dst=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,&bits,NULL,0); DeleteDC(hdc); memcpy(bits,info.bmBits,info.bmHeight*info.bmWidthBytes); return dst; } void MenuSkin::LoadSkinTintColors( CSkinParser &parser, const wchar_t *name, COLORREF &tintColor1, COLORREF &tintColor2, COLORREF &tintColor3, COLORREF backgroundColor ) const { wchar_t name2[256]; Sprintf(name2,_countof(name2),L"%s_tint1",name); const wchar_t *str=parser.FindSetting(name2); if (str && _wcsicmp(str,L"$Default")!=0 && LoadSkinNumbers(str,(int*)&tintColor1,1,NUMBERS_COLORS_ABGR)) { if (tintColor1==DEFAULT_GLASS_COLOR) tintColor1=DEFAULT_GLASS_CONFLICT; } else tintColor1=DEFAULT_GLASS_COLOR; Sprintf(name2,_countof(name2),L"%s_tint2",name); str=parser.FindSetting(name2); if (!str || _wcsicmp(str,L"$Default")==0 || !LoadSkinNumbers(str,(int*)&tintColor2,1,NUMBERS_COLORS_ABGR)) tintColor2=backgroundColor|0xFF000000; Sprintf(name2,_countof(name2),L"%s_tint3",name); str=parser.FindSetting(name2); if (!str || _wcsicmp(str,L"$Default")==0 || !LoadSkinNumbers(str,(int*)&tintColor3,1,NUMBERS_COLORS_ABGR)) tintColor3=0; } bool MenuSkin::LoadSkinColors( CSkinParser &parser, const wchar_t *name, COLORREF *colors, int count, COLORREF backgroundColor, TSkinNumberType type ) const { const wchar_t *str=parser.FindSetting(name); if (!str || !LoadSkinNumbers(str,(int*)colors,count,type)) return false; if (count>10) return false; wchar_t name2[256]; Sprintf(name2,_countof(name2),L"%s_mask",name); str=parser.FindSetting(name2); if (str) { COLORREF masks[10], tintColor1, tintColor2, tintColor3; if (LoadSkinNumbers(str,(int*)masks,count,NUMBERS_COLORS)) { LoadSkinTintColors(parser,name,tintColor1,tintColor2,tintColor3,backgroundColor); for (int i=0;i0 - bitmap ID // if maskIndex<0 - color RGB MenuBitmap res; res.Init(); wchar_t err[1024]; HBITMAP bmp=NULL; if (index>0) { if ((index&0xFF000000)==0x0F000000) { bmp=DuplicateBitmap(CustomBitmaps[index&0xFFFFFF].bitmap); } else if (hMod) { bmp=(HBITMAP)LoadImage(hMod,MAKEINTRESOURCE(index),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); if (!bmp) bmp=LoadImageResource(hMod,MAKEINTRESOURCE(index),false,false); if (!bmp) { GetErrorMessage(err,_countof(err),GetLastError()); Sprintf(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_BMPRES),index,err); return res; } } else { wchar_t path[_MAX_PATH]; GetSkinsPath(path); wchar_t fname[_MAX_PATH]; Sprintf(fname,_countof(fname),L"%s%d.bmp",path,index); bmp=(HBITMAP)LoadImage(NULL,fname,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_LOADFROMFILE); if (!bmp) { GetErrorMessage(err,_countof(err),GetLastError()); Sprintf(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_BMPFILE),fname,err); return res; } } } HBITMAP bmpMask=NULL; BITMAP infoMask={0}; if (maskIndex>0) { if ((maskIndex&0xFF000000)==0x0F000000) { bmpMask=DuplicateBitmap(CustomBitmaps[maskIndex&0xFFFFFF].bitmap); } else if (hMod) { bmpMask=(HBITMAP)LoadImage(hMod,MAKEINTRESOURCE(maskIndex),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); if (!bmpMask) bmpMask=LoadImageResource(hMod,MAKEINTRESOURCE(maskIndex),false,false); if (!bmpMask) { GetErrorMessage(err,_countof(err),GetLastError()); Sprintf(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_MASKRES),maskIndex,err); } } else { wchar_t path[_MAX_PATH]; GetSkinsPath(path); wchar_t fname[_MAX_PATH]; Sprintf(fname,_countof(fname),L"%s%d.bmp",path,maskIndex); bmpMask=(HBITMAP)LoadImage(NULL,fname,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_LOADFROMFILE); if (!bmpMask) { GetErrorMessage(err,_countof(err),GetLastError()); Sprintf(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_MASKFILE),fname,err); } } if (!bmpMask) { res=bmpMask; return res; } GetObject(bmpMask,sizeof(infoMask),&infoMask); if (index<0) { unsigned int colorRGB=index&0xFFFFFF; BITMAPINFO bi={0}; bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth=infoMask.bmWidth; bi.bmiHeader.biHeight=infoMask.bmHeight; bi.bmiHeader.biPlanes=1; bi.bmiHeader.biBitCount=32; HDC hdc=CreateCompatibleDC(NULL); unsigned int *bits; bmp=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&bits,NULL,0); if (infoMask.bmBitsPixel==32) { unsigned int *ptrMask=(unsigned int*)infoMask.bmBits; for (int i=infoMask.bmWidth*infoMask.bmHeight-1;i>=0;i--) bits[i]=colorRGB|(ptrMask[i]&0xFF000000); } else { for (int i=infoMask.bmWidth*infoMask.bmHeight-1;i>=0;i--) bits[i]=colorRGB|0xFF000000; } DeleteDC(hdc); } } else if (maskIndex<0) { infoMask.bmBits=&maskIndex; } BITMAP info; GetObject(bmp,sizeof(info),&info); if (bmpMask && index>0 && (info.bmWidth!=infoMask.bmWidth || info.bmHeight!=infoMask.bmHeight)) { Sprintf(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_MASKSIZE),index,maskIndex); } if (maskIndex<0 || (bmpMask && info.bmWidth==infoMask.bmWidth && info.bmHeight==infoMask.bmHeight)) { // apply color mask unsigned char *ptr=(unsigned char*)info.bmBits; int stride=info.bmBitsPixel/8; int pad=stride*info.bmWidth; pad=((pad+3)&~3)-pad; unsigned char *ptrMask=(unsigned char*)infoMask.bmBits; int strideMask=infoMask.bmBitsPixel/8; int padMask=strideMask*info.bmWidth; padMask=((padMask+3)&~3)-padMask; for (int y=0;y>24); int r=(pixel>>16)&255; int g=(pixel>>8)&255; int b=(pixel)&255; r=(r*a)/255; g=(g*a)/255; b=(b*a)/255; pixel=(a<<24)|(r<<16)|(g<<8)|b; } } return res; } static void MirrorBitmap( HBITMAP bmp ) { BITMAP info; GetObject(bmp,sizeof(info),&info); unsigned char *ptr=(unsigned char*)info.bmBits; if (!ptr) return; int stride=info.bmBitsPixel/8; int pitch=(stride*info.bmWidth+3)&~3; for (int y=0;y=(int)CustomBitmaps.size()) return true; id|=0x0F000000; } else { id=_wtol(str); if (id<=0) return true; } int id2; wchar_t name2[256]; Sprintf(name2,_countof(name2),L"%s_mask",name); str=parser.FindSetting(name2); if (!str) id2=0; else if (str[0]=='#' || str[0]=='$') { LoadSkinNumbers(str,&id2,1,NUMBERS_COLORS_RGB); id2|=0xFF000000; } else if (str[0]=='@') { id2=_wtol(str+1); if (id2<0 || id2>=(int)CustomBitmaps.size()) return true; id2|=0x0F000000; } else { id2=_wtol(str); if (id2<0) id2=0; } if (id2) { COLORREF tintColor1, tintColor2, tintColor3; LoadSkinTintColors(parser,name,tintColor1,tintColor2,tintColor3,backgroundColor); if (id>0 || id2>0) { bitmap=LoadSkinBitmap(hMod,id,id2,tintColor1,tintColor2,tintColor3,bPremultiply); } else { COLORREF color=((id>>16)&0xFF)|(id&0xFF00)|((id&0xFF)<<16); COLORREF mask=((id2>>16)&0xFF)|(id2&0xFF00)|((id2&0xFF)<<16); BlendColor(color,mask,tintColor1,tintColor2,tintColor3); bitmap=color; } } else if (id>0) { bitmap=LoadSkinBitmap(hMod,id,0,0,0,0,bPremultiply); } else { COLORREF color=((id>>16)&0xFF)|(id&0xFF00)|((id&0xFF)<<16); bitmap=color; } if (!bitmap.bIsBitmap) { if (!bAllowColor) Sprintf(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_RECBITMAP),name); return bAllowColor; } if (!bitmap.GetBitmap()) return false; if (bMirror) MirrorBitmap(bitmap.GetBitmap()); BITMAP info; if ((!countX && slicesX) || (!countY && slicesY)) GetObject(bitmap.GetBitmap(),sizeof(info),&info); if (countX) { Sprintf(name2,_countof(name2),L"%s_slices_X",name); str=parser.FindSetting(name2); if (str) LoadSkinNumbers(str,slicesX,countX,NUMBERS_SLICES); else memset(slicesX,0,countX*4); } else if (slicesX) *slicesX=info.bmWidth; if (countY) { Sprintf(name2,_countof(name2),L"%s_slices_Y",name); str=parser.FindSetting(name2); if (str) LoadSkinNumbers(str,slicesY,countY,NUMBERS_SLICES); else memset(slicesY,0,countY*4); } else if (slicesY) *slicesY=info.bmHeight; return true; } bool MenuSkin::LoadSkinBitmap( HMODULE hMod, CSkinParser &parser, const wchar_t *name, MenuBitmap &bitmap, SIZE *size, bool bMirror, bool bPremultiply ) const { int cx, cy; if (!LoadSkinBackground(hMod,parser,name,bitmap,0,&cx,0,&cy,0,bMirror,false,bPremultiply)) return false; if (size) { size->cx=cx; size->cy=cy; } return true; } MenuSkin::THAlign MenuSkin::ParseHAlign( const wchar_t *str ) { if (str) { if (_wcsicmp(str,L"center1")==0) return HALIGN_CENTER1; else if (_wcsicmp(str,L"center2")==0) return HALIGN_CENTER2; else if (_wcsicmp(str,L"left")==0) return HALIGN_LEFT; else if (_wcsicmp(str,L"left1")==0) return HALIGN_LEFT1; else if (_wcsicmp(str,L"left2")==0) return HALIGN_LEFT2; else if (_wcsicmp(str,L"right")==0) return HALIGN_RIGHT; else if (_wcsicmp(str,L"right1")==0) return HALIGN_RIGHT1; else if (_wcsicmp(str,L"right2")==0) return HALIGN_RIGHT2; else if (_wcsicmp(str,L"corner")==0) return HALIGN_CORNER; else if (_wcsicmp(str,L"none")==0) return HALIGN_NONE; } return HALIGN_CENTER; } MenuSkin::TIconSize MenuSkin::ParseIconSize( const wchar_t *str ) { if (str) { if (_wcsicmp(str,L"small")==0) return ICON_SIZE_SMALL; else if (_wcsicmp(str,L"large")==0) return ICON_SIZE_LARGE; else if (_wcsicmp(str,L"none")==0) return ICON_SIZE_NONE; } return ICON_SIZE_UNDEFINED; } MenuSkin::TVAlign MenuSkin::ParseVAlign( const wchar_t *str ) { if (str) { if (_wcsicmp(str,L"top")==0) return VALIGN_TOP; else if (_wcsicmp(str,L"bottom")==0) return VALIGN_BOTTOM; else if (_wcsicmp(str,L"corner")==0) return VALIGN_CORNER; else if (_wcsicmp(str,L"none")==0) return VALIGN_NONE; } return VALIGN_CENTER; } bool MenuSkin::LoadSkinItem( HMODULE hMod, CSkinParser &parser, const wchar_t *name, MenuSkin::ItemDrawSettings &settings, MenuSkin::ItemDrawSettings *pDefaults, COLORREF backgroundColor, bool bRTL ) const { wchar_t name2[256]; const wchar_t *str; Sprintf(name2,_countof(name2),L"%s_font",name); str=parser.FindSetting(name2); if (str) { settings.font=LoadSkinFont(str,NULL,0,0,true); settings.bOwnFont=true; } else if (pDefaults) { settings.font=pDefaults->font; settings.bOwnFont=false; } else { settings.font=LoadSkinFont(NULL,NULL,0,0,true); settings.bOwnFont=true; } Sprintf(name2,_countof(name2),L"%s_glow_size",name); str=parser.FindSetting(name2); if (str) settings.glowSize=_wtol(str); else if (pDefaults) settings.glowSize=pDefaults->glowSize; else settings.glowSize=0; Sprintf(name2,_countof(name2),L"%s_text_color",name); if (LoadSkinColors(parser,name2,settings.textColors,_countof(settings.textColors),backgroundColor)) {} else if (pDefaults) memcpy(settings.textColors,pDefaults->textColors,sizeof(settings.textColors)); else { settings.textColors[0]=GetSysColor(COLOR_MENUTEXT); settings.textColors[1]=GetSysColor(COLOR_HIGHLIGHTTEXT); settings.textColors[2]=GetSysColor(COLOR_GRAYTEXT); settings.textColors[3]=GetSysColor(COLOR_HIGHLIGHTTEXT); } Sprintf(name2,_countof(name2),L"%s_text_shadow_color",name); str=parser.FindSetting(name2); if (str && wcscmp(str,L"none")==0) memset(settings.textShadowColors,-1,sizeof(settings.textShadowColors)); else if (LoadSkinColors(parser,name2,settings.textShadowColors,_countof(settings.textShadowColors),backgroundColor)) {} else if (pDefaults) memcpy(settings.textShadowColors,pDefaults->textShadowColors,sizeof(settings.textShadowColors)); else memset(settings.textShadowColors,-1,sizeof(settings.textShadowColors)); Sprintf(name2,_countof(name2),L"%s_text_padding",name); str=parser.FindSetting(name2); if (str) LoadSkinNumbers(str,(int*)&settings.textPadding,4,NUMBERS_PADDING); else if (pDefaults) settings.textPadding=pDefaults->textPadding; else settings.textPadding=ScaleSkinElement(DEFAULT_TEXT_PADDING); Sprintf(name2,_countof(name2),L"%s_icon_padding",name); str=parser.FindSetting(name2); if (str) LoadSkinNumbers(str,(int*)&settings.iconPadding,4,NUMBERS_PADDING); else if (pDefaults) settings.iconPadding=pDefaults->iconPadding; else settings.iconPadding=ScaleSkinElement(DEFAULT_ICON_PADDING); Sprintf(name2,_countof(name2),L"%s_arrow_color",name); if (LoadSkinColors(parser,name2,settings.arrColors,_countof(settings.arrColors),backgroundColor)) {} else if (pDefaults) memcpy(settings.arrColors,pDefaults->arrColors,sizeof(settings.arrColors)); else { settings.arrColors[0]=settings.textColors[0]; settings.arrColors[1]=settings.textColors[1]; } Sprintf(name2,_countof(name2),L"%s_selection",name); str=parser.FindSetting(name2); if (str) { if (!LoadSkinBackground(hMod,parser,name2,settings.bmpSelection,backgroundColor,settings.selSlicesX,_countof(settings.selSlicesX),settings.selSlicesY,_countof(settings.selSlicesY),bRTL,true)) return false; } else if (pDefaults) { settings.bmpSelection=pDefaults->bmpSelection; settings.bmpSelection.bIsOwned=false; memcpy(settings.selSlicesX,pDefaults->selSlicesX,sizeof(settings.selSlicesX)); memcpy(settings.selSlicesY,pDefaults->selSlicesY,sizeof(settings.selSlicesY)); } else { settings.bmpSelection=GetSysColor(COLOR_HIGHLIGHT); } { const POINT *sizes=GetArrowsBitmapSizes(); settings.arrSize.cx=sizes[3].y-sizes[3].x; settings.arrSize.cy=sizes[6].y; } Sprintf(name2,_countof(name2),L"%s_arrow",name); str=parser.FindSetting(name2); if (str && wcscmp(str,L"0")==0) {} else if (str) { if (!LoadSkinBitmap(hMod,parser,name2,settings.bmpArrow,&settings.arrSize,bRTL)) return false; settings.arrSize.cy/=2; } else if (pDefaults) { settings.bmpArrow=pDefaults->bmpArrow; settings.bmpArrow.bIsOwned=false; settings.arrSize=pDefaults->arrSize; } Sprintf(name2,_countof(name2),L"%s_arrow_padding",name); str=parser.FindSetting(name2); if (str) LoadSkinNumbers(str,(int*)&settings.arrPadding,2,NUMBERS_PADDING); else if (pDefaults) settings.arrPadding=pDefaults->arrPadding; else settings.arrPadding=ScaleSkinElement(DEFAULT_ARROW_PADDING); Sprintf(name2,_countof(name2),L"%s_icon_frame",name); str=parser.FindSetting(name2); if (str) { if (!LoadSkinBackground(hMod,parser,name2,settings.bmpIconFrame,backgroundColor,settings.frameSlicesX,_countof(settings.frameSlicesX),settings.frameSlicesY,_countof(settings.frameSlicesY),bRTL)) return false; if (settings.bmpIconFrame.GetBitmap()) { Sprintf(name2,_countof(name2),L"%s_icon_frame_offset",name); str=parser.FindSetting(name2); if (str) LoadSkinNumbers(str,(int*)&settings.iconFrameOffset,2,NUMBERS_PADDING); else memset(&settings.iconFrameOffset,0,sizeof(settings.iconFrameOffset)); } } else if (pDefaults) { settings.bmpIconFrame=pDefaults->bmpIconFrame; settings.bmpIconFrame.bIsOwned=false; memcpy(settings.frameSlicesX,pDefaults->frameSlicesX,sizeof(settings.frameSlicesX)); memcpy(settings.frameSlicesY,pDefaults->frameSlicesY,sizeof(settings.frameSlicesY)); settings.iconFrameOffset=pDefaults->iconFrameOffset; } return true; } void MenuSkin::ParseOptionsString( const wchar_t *optionsStr, std::map &options ) const { options.clear(); wchar_t text[1024]; const wchar_t *str=optionsStr; while (*str) { str=GetToken(str,text,_countof(text),L"\n"); wchar_t token[256]; const wchar_t *val=GetToken(text,token,_countof(token),L"="); if (*token) options[token]=val; } if (wcschr(optionsStr,'|')) { // backwards compatibility for old-style settings const wchar_t *end=wcschr(optionsStr,'\n'); if (end && wcschr(end+1,'\n')) return; // multiple lines for (std::vector