mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-12 18:07:24 +10:00
346 lines
11 KiB
C++
346 lines
11 KiB
C++
// Classic Shell (c) 2009-2016, Ivo Beltchev
|
|
// Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author
|
|
|
|
#include "stdafx.h"
|
|
#include "ClassicIEDLL.h"
|
|
#include "Settings.h"
|
|
#include "ResourceHelper.h"
|
|
#include "SettingsUIHelper.h"
|
|
#include <vssym32.h>
|
|
#include <dwmapi.h>
|
|
|
|
static _declspec(thread) SIZE g_SysButtonSize; // the size of the system buttons (close, minimize) for this thread's window
|
|
static WNDPROC g_OldClassCaptionProc;
|
|
static HBITMAP g_GlowBmp;
|
|
static HBITMAP g_GlowBmpMax;
|
|
static LONG g_bInjected; // the process is injected
|
|
static int g_DPI;
|
|
static UINT g_Message; // private message to detect if the caption is subclassed
|
|
static ATOM g_SubclassAtom;
|
|
|
|
struct CustomCaption
|
|
{
|
|
int leftPadding;
|
|
int topPadding;
|
|
int iconPadding;
|
|
};
|
|
|
|
static CustomCaption g_CustomCaption[3]={
|
|
{2,3,10}, // Aero
|
|
{4,2,10}, // Aero maximized
|
|
{4,2,10}, // Basic
|
|
};
|
|
|
|
void GetSysButtonSize( HWND hWnd )
|
|
{
|
|
TITLEBARINFOEX titleInfo={sizeof(titleInfo)};
|
|
SendMessage(hWnd,WM_GETTITLEBARINFOEX,0,(LPARAM)&titleInfo);
|
|
int buttonLeft=titleInfo.rgrect[2].left;
|
|
if (buttonLeft>titleInfo.rgrect[5].left) buttonLeft=titleInfo.rgrect[5].left;
|
|
int buttonRight=titleInfo.rgrect[2].right;
|
|
if (buttonRight<titleInfo.rgrect[5].right) buttonRight=titleInfo.rgrect[5].right;
|
|
|
|
int w=buttonRight-buttonLeft;
|
|
int h=titleInfo.rgrect[5].bottom-titleInfo.rgrect[5].top;
|
|
g_SysButtonSize.cx=w;
|
|
g_SysButtonSize.cy=h;
|
|
}
|
|
|
|
// Subclasses the main IE frame to redraw the caption when the text changes
|
|
static LRESULT CALLBACK SubclassFrameProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if (uMsg==g_Message)
|
|
{
|
|
GetSysButtonSize(hWnd);
|
|
HWND caption=FindWindowEx(hWnd,NULL,L"Client Caption",NULL);
|
|
if (caption)
|
|
InvalidateRect(caption,NULL,FALSE);
|
|
return 0;
|
|
}
|
|
if (uMsg==WM_SETTEXT || uMsg==WM_ACTIVATE)
|
|
{
|
|
HWND caption=FindWindowEx(hWnd,NULL,L"Client Caption",NULL);
|
|
if (caption)
|
|
InvalidateRect(caption,NULL,FALSE);
|
|
}
|
|
if (uMsg==WM_SIZE || uMsg==WM_SETTINGCHANGE)
|
|
{
|
|
GetSysButtonSize(hWnd);
|
|
if (uMsg==WM_SETTINGCHANGE)
|
|
{
|
|
CSettingsLockWrite lock;
|
|
UpdateSettings();
|
|
}
|
|
}
|
|
while (1)
|
|
{
|
|
WNDPROC proc=(WNDPROC)GetProp(hWnd,MAKEINTATOM(g_SubclassAtom));
|
|
if (proc)
|
|
return CallWindowProc(proc,hWnd,uMsg,wParam,lParam);
|
|
}
|
|
}
|
|
|
|
static LRESULT DefCaptionProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
while (1)
|
|
{
|
|
WNDPROC proc=(WNDPROC)GetProp(hWnd,MAKEINTATOM(g_SubclassAtom));
|
|
if (proc)
|
|
return CallWindowProc(proc,hWnd,uMsg,wParam,lParam);
|
|
}
|
|
}
|
|
|
|
// Subclasses the caption window to draw the icon and the text
|
|
static LRESULT CALLBACK SubclassCaptionProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if (uMsg==g_Message)
|
|
return 1;
|
|
if (uMsg==WM_ERASEBKGND)
|
|
return 0;
|
|
if (uMsg==WM_PAINT)
|
|
{
|
|
HTHEME theme=OpenThemeData(hWnd,L"Window");
|
|
if (!theme) return DefCaptionProc(hWnd,uMsg,wParam,lParam);
|
|
|
|
// get the icon and the text from the parent
|
|
HWND parent=GetParent(hWnd);
|
|
wchar_t caption[256];
|
|
GetWindowText(parent,caption,_countof(caption));
|
|
HICON hIcon=(HICON)SendMessage(parent,WM_GETICON,ICON_SMALL,0);
|
|
int iconSize=GetSystemMetrics(SM_CXSMICON);
|
|
|
|
bool bMaximized=IsZoomed(parent)!=0;
|
|
bool bActive=(parent==GetActiveWindow());
|
|
RECT rc;
|
|
GetClientRect(hWnd,&rc);
|
|
|
|
if (!g_DPI)
|
|
{
|
|
HDC hdc=GetDC(NULL);
|
|
g_DPI=GetDeviceCaps(hdc,LOGPIXELSY);
|
|
ReleaseDC(NULL,hdc);
|
|
}
|
|
|
|
// create a font from the user settings
|
|
HFONT font=CreateFontSetting(GetSettingString(L"CaptionFont"),g_DPI);
|
|
if (!font)
|
|
{
|
|
LOGFONT lFont;
|
|
GetThemeSysFont(theme,TMT_CAPTIONFONT,&lFont);
|
|
font=CreateFontIndirect(&lFont);
|
|
}
|
|
|
|
bool bIcon=GetSettingBool(L"ShowIcon");
|
|
bool bCenter=GetSettingBool(L"CenterCaption");
|
|
bool bGlow=GetSettingBool(bMaximized?L"MaxGlow":L"Glow");
|
|
|
|
DTTOPTS opts={sizeof(opts),DTT_COMPOSITED|DTT_TEXTCOLOR};
|
|
opts.crText=GetSettingInt(bMaximized?(bActive?L"MaxColor":L"InactiveMaxColor"):(bActive?L"TextColor":L"InactiveColor"))&0xFFFFFF;
|
|
|
|
BOOL bComposition;
|
|
if (SUCCEEDED(DwmIsCompositionEnabled(&bComposition)) && bComposition)
|
|
{
|
|
// Aero Theme
|
|
PAINTSTRUCT ps;
|
|
HDC hdc=BeginPaint(hWnd,&ps);
|
|
|
|
BP_PAINTPARAMS paintParams={sizeof(paintParams),BPPF_ERASE};
|
|
HDC hdcPaint=NULL;
|
|
HPAINTBUFFER hBufferedPaint=BeginBufferedPaint(hdc,&ps.rcPaint,BPBF_TOPDOWNDIB,&paintParams,&hdcPaint);
|
|
if (hdcPaint)
|
|
{
|
|
// exclude the caption buttons
|
|
rc.right-=g_SysButtonSize.cx+5;
|
|
if (GetWinVersion()==WIN_VER_VISTA) rc.bottom++;
|
|
if (!bMaximized)
|
|
{
|
|
rc.left+=g_CustomCaption[0].leftPadding;
|
|
int y=g_CustomCaption[0].topPadding;
|
|
if (y>rc.bottom-iconSize) y=rc.bottom-iconSize;
|
|
if (bIcon)
|
|
{
|
|
DrawIconEx(hdcPaint,rc.left,y,hIcon,iconSize,iconSize,0,NULL,DI_NORMAL|DI_NOMIRROR);
|
|
rc.left+=iconSize;
|
|
}
|
|
rc.left+=g_CustomCaption[0].iconPadding;
|
|
rc.bottom++;
|
|
}
|
|
else
|
|
{
|
|
// when the window is maximized, the caption bar is partially off-screen, so align the icon to the bottom
|
|
rc.left+=g_CustomCaption[1].leftPadding;
|
|
if (bIcon)
|
|
{
|
|
DrawIconEx(hdcPaint,rc.left,rc.bottom-iconSize-g_CustomCaption[1].topPadding,hIcon,iconSize,iconSize,0,NULL,DI_NORMAL|DI_NOMIRROR);
|
|
rc.left+=iconSize;
|
|
}
|
|
rc.left+=g_CustomCaption[1].iconPadding;
|
|
if (GetWinVersion()>=WIN_VER_WIN10)
|
|
rc.bottom++;
|
|
}
|
|
if (GetWinVersion()<WIN_VER_WIN10)
|
|
rc.top=rc.bottom-g_SysButtonSize.cy;
|
|
HFONT font0=(HFONT)SelectObject(hdcPaint,font);
|
|
RECT rcText={0,0,0,0};
|
|
opts.dwFlags|=DTT_CALCRECT;
|
|
DrawThemeTextEx(theme,hdcPaint,0,0,caption,-1,DT_VCENTER|DT_NOPREFIX|DT_SINGLELINE|DT_CALCRECT,&rcText,&opts);
|
|
int textWidth=rcText.right-rcText.left;
|
|
if (bCenter && textWidth<rc.right-rc.left)
|
|
{
|
|
rc.left+=(rc.right-rc.left-textWidth)/2;
|
|
}
|
|
if (textWidth>rc.right-rc.left)
|
|
textWidth=rc.right-rc.left;
|
|
opts.dwFlags&=~DTT_CALCRECT;
|
|
|
|
if (bGlow)
|
|
{
|
|
HDC hSrc=CreateCompatibleDC(hdcPaint);
|
|
HGDIOBJ bmp0=SelectObject(hSrc,bMaximized?g_GlowBmpMax:g_GlowBmp);
|
|
BLENDFUNCTION func={AC_SRC_OVER,0,255,AC_SRC_ALPHA};
|
|
AlphaBlend(hdcPaint,rc.left-11,rc.top,11,rc.bottom-rc.top,hSrc,0,0,11,24,func);
|
|
AlphaBlend(hdcPaint,rc.left,rc.top,textWidth,rc.bottom-rc.top,hSrc,11,0,2,24,func);
|
|
AlphaBlend(hdcPaint,rc.left+textWidth,rc.top,11,rc.bottom-rc.top,hSrc,13,0,11,24,func);
|
|
SelectObject(hSrc,bmp0);
|
|
DeleteDC(hSrc);
|
|
}
|
|
DrawThemeTextEx(theme,hdcPaint,0,0,caption,-1,DT_VCENTER|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS,&rc,&opts);
|
|
SelectObject(hdcPaint,font0);
|
|
|
|
EndBufferedPaint(hBufferedPaint,TRUE);
|
|
}
|
|
EndPaint(hWnd,&ps);
|
|
}
|
|
else
|
|
{
|
|
// Basic Theme
|
|
|
|
// first draw the caption bar
|
|
DefCaptionProc(hWnd,uMsg,wParam,lParam);
|
|
|
|
// then draw the caption directly in the window DC
|
|
HDC hdc=GetWindowDC(hWnd);
|
|
|
|
// exclude the caption buttons
|
|
rc.right-=g_SysButtonSize.cx+5;
|
|
rc.top=rc.bottom-g_SysButtonSize.cy;
|
|
|
|
rc.left+=g_CustomCaption[2].leftPadding;
|
|
if (bIcon)
|
|
{
|
|
DrawIconEx(hdc,rc.left,rc.bottom-iconSize-g_CustomCaption[2].topPadding,hIcon,iconSize,iconSize,0,NULL,DI_NORMAL|DI_NOMIRROR);
|
|
rc.left+=iconSize;
|
|
}
|
|
rc.left+=g_CustomCaption[2].iconPadding;
|
|
|
|
HFONT font0=(HFONT)SelectObject(hdc,font);
|
|
RECT rcText={0,0,0,0};
|
|
opts.dwFlags|=DTT_CALCRECT;
|
|
DrawThemeTextEx(theme,hdc,0,0,caption,-1,DT_VCENTER|DT_NOPREFIX|DT_SINGLELINE|DT_CALCRECT,&rcText,&opts);
|
|
int textWidth=rcText.right-rcText.left;
|
|
if (bCenter && textWidth<rc.right-rc.left)
|
|
{
|
|
rc.left+=(rc.right-rc.left-textWidth)/2;
|
|
}
|
|
if (textWidth>rc.right-rc.left)
|
|
textWidth=rc.right-rc.left;
|
|
opts.dwFlags&=~DTT_CALCRECT;
|
|
|
|
if (bGlow)
|
|
{
|
|
HDC hSrc=CreateCompatibleDC(hdc);
|
|
HGDIOBJ bmp0=SelectObject(hSrc,bMaximized?g_GlowBmpMax:g_GlowBmp);
|
|
BLENDFUNCTION func={AC_SRC_OVER,0,255,AC_SRC_ALPHA};
|
|
AlphaBlend(hdc,rc.left-11,rc.top,11,rc.bottom-rc.top,hSrc,0,0,11,24,func);
|
|
AlphaBlend(hdc,rc.left,rc.top,textWidth,rc.bottom-rc.top,hSrc,11,0,2,24,func);
|
|
AlphaBlend(hdc,rc.left+textWidth,rc.top,11,rc.bottom-rc.top,hSrc,13,0,11,24,func);
|
|
SelectObject(hSrc,bmp0);
|
|
DeleteDC(hSrc);
|
|
}
|
|
DrawThemeTextEx(theme,hdc,0,0,caption,-1,DT_VCENTER|DT_NOPREFIX|DT_SINGLELINE|DT_END_ELLIPSIS,&rc,&opts);
|
|
SelectObject(hdc,font0);
|
|
|
|
ReleaseDC(hWnd,hdc);
|
|
}
|
|
|
|
DeleteObject(font);
|
|
CloseThemeData(theme);
|
|
return 0;
|
|
}
|
|
return DefCaptionProc(hWnd,uMsg,wParam,lParam);
|
|
}
|
|
|
|
// Replacement proc for the "Client Caption" class that hooks the main frame and the caption windows
|
|
static LRESULT CALLBACK ClassCaptionProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if (uMsg==WM_CREATE)
|
|
{
|
|
WNDPROC proc=(WNDPROC)SetWindowLongPtr(hWnd,GWLP_WNDPROC,(LONG_PTR)SubclassCaptionProc);
|
|
SetProp(hWnd,MAKEINTATOM(g_SubclassAtom),(HANDLE)proc);
|
|
HWND frame=GetParent(hWnd);
|
|
proc=(WNDPROC)SetWindowLongPtr(frame,GWLP_WNDPROC,(LONG_PTR)SubclassFrameProc);
|
|
SetProp(frame,MAKEINTATOM(g_SubclassAtom),(HANDLE)proc);
|
|
PostMessage(frame,g_Message,0,0);
|
|
}
|
|
return CallWindowProc(g_OldClassCaptionProc,hWnd,uMsg,wParam,lParam);
|
|
}
|
|
|
|
static BOOL CALLBACK EnumTopWindows( HWND hwnd, LPARAM lParam )
|
|
{
|
|
DWORD processId;
|
|
DWORD threadId=GetWindowThreadProcessId(hwnd,&processId);
|
|
if (processId==GetCurrentProcessId())
|
|
{
|
|
HWND caption=FindWindowEx(hwnd,NULL,L"Client Caption",NULL);
|
|
if (caption)
|
|
{
|
|
LogToFile(CIE_LOG,L"InitClassicIE: caption=%p",caption);
|
|
if (!g_OldClassCaptionProc)
|
|
g_OldClassCaptionProc=(WNDPROC)SetClassLongPtr(caption,GCLP_WNDPROC,(LONG_PTR)ClassCaptionProc);
|
|
WNDPROC proc=(WNDPROC)SetWindowLongPtr(caption,GWLP_WNDPROC,(LONG_PTR)SubclassCaptionProc);
|
|
SetProp(caption,MAKEINTATOM(g_SubclassAtom),(HANDLE)proc);
|
|
proc=(WNDPROC)SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LONG_PTR)SubclassFrameProc);
|
|
SetProp(hwnd,MAKEINTATOM(g_SubclassAtom),(HANDLE)proc);
|
|
PostMessage(hwnd,g_Message,0,0);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void InitClassicIE( HMODULE hModule )
|
|
{
|
|
CRegKey regKey;
|
|
if (regKey.Open(HKEY_CURRENT_USER,GetSettingsRegPath())==ERROR_SUCCESS)
|
|
{
|
|
DWORD val;
|
|
if (regKey.QueryDWORDValue(L"CustomAero",val)==ERROR_SUCCESS)
|
|
{
|
|
g_CustomCaption[0].leftPadding=(val&255);
|
|
g_CustomCaption[0].topPadding=((val>>8)&255);
|
|
g_CustomCaption[0].iconPadding=((val>>16)&255);
|
|
}
|
|
if (regKey.QueryDWORDValue(L"CustomAeroMax",val)==ERROR_SUCCESS)
|
|
{
|
|
g_CustomCaption[1].leftPadding=(val&255);
|
|
g_CustomCaption[1].topPadding=((val>>8)&255);
|
|
g_CustomCaption[1].iconPadding=((val>>16)&255);
|
|
}
|
|
if (regKey.QueryDWORDValue(L"CustomBasic",val)==ERROR_SUCCESS)
|
|
{
|
|
g_CustomCaption[2].leftPadding=(val&255);
|
|
g_CustomCaption[2].topPadding=((val>>8)&255);
|
|
g_CustomCaption[2].iconPadding=((val>>16)&255);
|
|
}
|
|
}
|
|
|
|
g_Message=RegisterWindowMessage(L"ClassicIE.Injected");
|
|
g_SubclassAtom=GlobalAddAtom(L"ClassicIE.Subclass");
|
|
ChangeWindowMessageFilter(g_Message,MSGFLT_ADD);
|
|
g_GlowBmp=(HBITMAP)LoadImage(g_Instance,MAKEINTRESOURCE(IDB_GLOW),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
|
|
PremultiplyBitmap(g_GlowBmp,GetSettingInt(L"GlowColor"));
|
|
g_GlowBmpMax=(HBITMAP)LoadImage(g_Instance,MAKEINTRESOURCE(IDB_GLOW),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
|
|
PremultiplyBitmap(g_GlowBmpMax,GetSettingInt(L"MaxGlowColor"));
|
|
|
|
EnumWindows(EnumTopWindows,0);
|
|
}
|