/*
ToDo:
- картинки
- разобраться, что выдает GetTextExtentPoint32 вместо того, что положено
- намалевать тень под пунктами главного меню
- убрать рамку из меню, нарисовать под ним тень
- узнать алгоритм расчета цветов, используемый Офисом
- разобраться с варнингами
- малевать радиоточки и чекбоксы
- малевать задизабленные пункты меню
- подогнать под схемы с большими и экстра-большими шрифтами
*/
#include <tchar.h>
#include <windows.h>
#include "resource.h"
#include <string>
using namespace std;
#pragma comment (lib, "msimg32.lib"
int WhiteField, GrayField, SelRect, SelBorder, MenuText, MainMenuText, InactiveText, DisabledText;
class MenuInfo{
public:
MenuInfo(int maxmenus){
menus = new menuinfo[maxmenus];
c = 0;
}
int AddMenu(HMENU hMenu, int Pos, bool MainMenu){
menus[c].MainMenu = MainMenu;
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STRING;
mii.dwTypeData = NULL;
GetMenuItemInfo(hMenu, Pos, true, &mii);
menus[c].s = new char[mii.cch+1];
mii.cbSize = sizeof(MENUITEMINFO);
mii.cch++;
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU | MIIM_STATE;
mii.dwTypeData = menus[c].s;
GetMenuItemInfo(hMenu, Pos, true, &mii);
menus[c].FType = mii.fType;
menus[c].hSubMenu = mii.hSubMenu;
menus[c].FState = mii.fState;
c++;
return c-1;
}
char *GetMenuName(int id){
return menus[id].s;
}
bool IsMainMenu(int id){
return menus[id].MainMenu;
}
bool IsSeparator(int id){
return (menus[id].FType & MFT_SEPARATOR) == MFT_SEPARATOR;
}
HMENU GetSubMenu(int id){
return menus[id].hSubMenu;
}
bool IsEnabled(int id){
return (menus[id].FState & MFS_ENABLED) == MFS_ENABLED;
}
private:
struct menuinfo{
char *s;
int Checked;
int FType;
int FState;
HMENU hSubMenu;
bool DefaultMenu;
bool Enabled;
bool MainMenu;
} *menus;
int c;
};
MenuInfo *minfo;
HINSTANCE hInstance;
char MainWinClass[128] = "MyMain";
#define FONTNAME "Tahoma"
#define PTSIZE 8
class Color{
public:
template<class T> static void ColorToRGB(DWORD c, T *r, T *g, T *b){
*r = (T)(c & 0xFF);
*g = (T)((c & 0xFF00) >> 8);
*b = (T)((c & 0xFF0000) >> 16);
}
template<class T> static DWORD GetAlphaMix(T col1, T col2, double perc){
BYTE r1, r2, g1, g2, b1, b2;
ColorToRGB((DWORD)col1, &r1, &g1, &b1);
ColorToRGB((DWORD)col2, &r2, &g2, &b2);
r1 = (BYTE)((r1 - r2)*perc) + r2;
g1 = (BYTE)((g1 - g2)*perc) + g2;
b1 = (BYTE)((b1 - b2)*perc) + b2;
return RGB(r1, g1, b1);
}
static TRIVERTEX RGB2Vertex(int c, int x, int y){
TRIVERTEX res;
res.Alpha = 0x0000;
res.Red = (c % 256) << 8;
res.Green = ((c / 256) % 256) << 8;
res.Blue = (c / 65536) << 8;
res.x = x;
res.y = y;
return res;
}
static int GetAlphaMix(int col1, int col2, double perc){
int r1, r2, g1, g2, b1, b2;
r1 = col1%256; g1 = (col1/256)%256; b1 = col1/65536;
r2 = col2%256; g2 = (col2/256)%256; b2 = col2/65536;
r1 = (double)r1*perc + (double)r2*(1-perc);
g1 = (double)g1*perc + (double)g2*(1-perc);
b1 = (double)b1*perc + (double)b2*(1-perc);
return RGB(r1, g1, b1);
}
static void InitColors(){
WhiteField = GetSysColor(COLOR_WINDOW);
GrayField = GetSysColor(COLOR_BTNFACE);
SelRect = GetAlphaMix(GetSysColor(COLOR_HIGHLIGHT), GetSysColor(COLOR_WINDOW), 0.3);
SelBorder = GetSysColor(COLOR_HIGHLIGHT);
MenuText = GetSysColor(COLOR_WINDOWTEXT);
MainMenuText = GetSysColor(COLOR_MENUTEXT);
InactiveText = GetSysColor(COLOR_GRAYTEXT);
DisabledText = GetSysColor(COLOR_GRAYTEXT);
}
};
class MenuDraw {
public:
static void DrawMainMenu(DRAWITEMSTRUCT& dis){
// Рисуем пункт главного меню
if(dis.itemState & ODS_SELECTED){
DrawFilledRectWithBorder(dis.hDC,
dis.rcItem.left, dis.rcItem.top+1, dis.rcItem.right, dis.rcItem.bottom,
GetSysColor(COLOR_MENUBAR), GetSysColor(COLOR_APPWORKSPACE));
hPrint(dis.hDC,
dis.rcItem.left + 8, dis.rcItem.top + 3,
minfo->GetMenuName(dis.itemData), MainMenuText);
} else if(dis.itemState & ODS_INACTIVE){
hPrint(dis.hDC,
dis.rcItem.left + 8, dis.rcItem.top + 3,
minfo->GetMenuName(dis.itemData), InactiveText);
} else if(dis.itemState & ODS_HOTLIGHT){
DrawFilledRectWithBorder(dis.hDC,
dis.rcItem.left, dis.rcItem.top+1, dis.rcItem.right, dis.rcItem.bottom,
SelRect, SelBorder);
hPrint(dis.hDC,
dis.rcItem.left + 8, dis.rcItem.top + 3,
minfo->GetMenuName(dis.itemData), MainMenuText);
} else{
DrawFilledSysColorRect(dis.hDC,
dis.rcItem.left, dis.rcItem.top+1, dis.rcItem.right, dis.rcItem.bottom,
COLOR_MENUBAR);
hPrint(dis.hDC,
dis.rcItem.left + 8, dis.rcItem.top + 3,
minfo->GetMenuName(dis.itemData), MainMenuText);
}
}
static void DrawSubMenu(DRAWITEMSTRUCT& dis, bool IsSep){
HPEN hPEN, hOldPEN;
// Серое градиентное поле меню, левая часть
TRIVERTEX vert[2];
GRADIENT_RECT gRect = {0, 1};
vert[0] = Color::RGB2Vertex(WhiteField, 0, dis.rcItem.top);
vert[1] = Color::RGB2Vertex(GrayField, 23, dis.rcItem.bottom);
GradientFill(dis.hDC, vert, 2, &gRect, 1, GRADIENT_FILL_RECT_H);
// Белое поле меню, правая часть
DrawFilledRect(dis.hDC,
dis.rcItem.left+23, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom,
WhiteField);
if(!IsSep){
if(minfo->IsEnabled(dis.itemData)){
// Рамка, если пункт меню выделен
if(dis.itemState & ODS_SELECTED){
//DrawFilledRectWithBorder(dis.hDC,
// dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom,
// SelRect, SelBorder);
DrawNewGrad(dis.hDC,
dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom);
}
// Текст
hPrint(dis.hDC,
dis.rcItem.left + 30, dis.rcItem.top + 3,
minfo->GetMenuName(dis.itemData), MenuText);
DrawIco(dis.hDC, IDB_BITMAP3, IDB_BITMAP4,
dis.rcItem.left+3, dis.rcItem.top+2,
dis.itemState & ODS_SELECTED);
} else{
hPrint(dis.hDC,
dis.rcItem.left + 30, dis.rcItem.top + 3,
minfo->GetMenuName(dis.itemData), DisabledText);
}
} else{
// Линия разделителя
hPEN = CreatePen(0, 1, 0x808080);
hOldPEN = (HPEN)SelectObject(dis.hDC, hPEN);
MoveToEx(dis.hDC, dis.rcItem.left + 30, dis.rcItem.top + 2, NULL);
LineTo(dis.hDC, dis.rcItem.right, dis.rcItem.top + 2);
SelectObject(dis.hDC, hOldPEN);
DeleteObject(hPEN);
}
}
static void DrawNewGrad(HDC hDC, int x1, int y1, int x2, int y2){
int LastBkMode = SetBkMode(hDC, OPAQUE);
TRIVERTEX vert[2];
GRADIENT_RECT gRect = {0, 1};
vert[0] = Color::RGB2Vertex(RGB(255,195,134), x1, y1);
vert[1] = Color::RGB2Vertex(RGB(254,192,112), x2, (y1+y2)/2);
GradientFill(hDC, vert, 2, &gRect, 1, GRADIENT_FILL_RECT_V);
vert[0] = Color::RGB2Vertex(RGB(254,172,63), x1, (y1+y2)/2);
vert[1] = Color::RGB2Vertex(RGB(254,198,64), x2, y2);
GradientFill(hDC, vert, 2, &gRect, 1, GRADIENT_FILL_RECT_V);
HPEN hOldPen;
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(158,130,85));
hOldPen = (HPEN)SelectObject(hDC, hPen);
//Rectangle(hDC, x1, y1, x2, y2);
MoveToEx(hDC, x1, y1, NULL);
LineTo(hDC, x2-1, y1);
LineTo(hDC, x2-1, y2-1);
LineTo(hDC, x1, y2-1);
LineTo(hDC, x1, y1);
SelectObject(hDC, hOldPen);
DeleteObject(hPen);
SetBkMode(hDC, LastBkMode);
}
private:
static void DrawIco(HDC hDC, int PictRsrc, int MaskRsrc, int X, int Y, bool IsShadowed){
HDC memDC, tmpDC;
HBITMAP memBM, tmpBM, pictBM, maskBM;
memDC = CreateCompatibleDC(hDC);
tmpDC = CreateCompatibleDC(hDC);
memBM = CreateCompatibleBitmap(hDC, 18, 18);
tmpBM = CreateCompatibleBitmap(hDC, 16, 16);
SelectObject(memDC, memBM);
SelectObject(tmpDC, tmpBM);
pictBM = LoadBitmap(hInstance, MAKEINTRESOURCE(PictRsrc));
maskBM = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(MaskRsrc), IMAGE_BITMAP, 0, 0, LR_MONOCHROME);
BitBlt(memDC, 0, 0, 18, 18, hDC, X, Y, SRCCOPY);
if(IsShadowed){
HBRUSH hBrush, hOldBrush;
hBrush = CreateSolidBrush(RGB(136, 141, 157));
hOldBrush = (HBRUSH)SelectObject(tmpDC, hBrush);
RECT r;
SetRect(&r, 0, 0, 16, 16);
FillRect(tmpDC, &r, hBrush);
SelectObject(tmpDC, hOldBrush);
DeleteObject(hBrush);
MaskBlt(tmpDC, 0, 0, 16, 16, tmpDC, 0, 0, maskBM, 0, 0, MAKEROP4(BLACKNESS, SRCCOPY));
MaskBlt(memDC, 2, 2, 16, 16, tmpDC, 0, 0, maskBM, 0, 0, MAKEROP4(SRCPAINT, SRCCOPY));
SelectObject(tmpDC, pictBM);
MaskBlt(memDC, 0, 0, 16, 16, tmpDC, 0, 0, maskBM, 0, 0, MAKEROP4(SRCPAINT, SRCCOPY));
} else{
SelectObject(tmpDC, pictBM);
MaskBlt(memDC, 1, 1, 16, 16, tmpDC, 0, 0, maskBM, 0, 0, MAKEROP4(SRCPAINT, SRCCOPY));
}
BitBlt(hDC, X, Y, 18, 18, memDC, 0, 0, SRCCOPY);
DeleteObject(memDC);
DeleteObject(tmpDC);
DeleteObject(memBM);
DeleteObject(tmpBM);
DeleteObject(pictBM);
DeleteObject(maskBM);
}
static void hPrint(HDC hDC, int X, int Y, char *s, int color){
LOGFONT lf = {0, 0, 0, 0, FW_DONTCARE, false, false, false, RUSSIAN_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE};
int PointSize = PTSIZE, OldTextColor;
HFONT hFont, hOldFont;
char FontName[128] = FONTNAME;
if(hDC != 0){
SetBkMode(hDC, NEWTRANSPARENT);
CopyMemory(lf.lfFaceName, FontName, lstrlen(FontName));
lf.lfHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
hFont = CreateFontIndirect(&lf);
hOldFont = (HFONT)SelectObject(hDC, hFont);
OldTextColor = SetTextColor(hDC, color);
TextOut(hDC, X, Y, s, lstrlen(s));
SetTextColor(hDC, OldTextColor);
SelectObject(hDC, hOldFont);
DeleteObject(hFont);
}
}
static void DrawFilledRect(HDC hDC, int X1, int Y1, int X2, int Y2, int color){
HBRUSH hBrush;
HRGN hRgn;
RECT r;
SetRect(&r, X1, Y1, X2, Y2);
hBrush = CreateSolidBrush(color);
hRgn = CreateRectRgnIndirect(&r);
FillRgn(hDC, hRgn, hBrush);
DeleteObject(hBrush);
DeleteObject(hRgn);
}
static void DrawFilledRectWithBorder(HDC hDC,
int X1, int Y1, int X2, int Y2,
int FillColor, int BorderColor){
HBRUSH hBrush = CreateSolidBrush(FillColor);
HPEN hPen = CreatePen(0, 1, BorderColor);
SelectObject(hDC, hBrush);
SelectObject(hDC, hPen);
Rectangle(hDC, X1, Y1, X2, Y2+1);
DeleteObject(hBrush);
DeleteObject(hPen);
}
static void DrawFilledSysColorRect(HDC hDC, int X1, int Y1, int X2, int Y2, int nIndex){
HBRUSH hBrush;
HRGN hRgn;
RECT r;
SetRect(&r, X1, Y1, X2, Y2+1);
hBrush = GetSysColorBrush(nIndex);
hRgn = CreateRectRgnIndirect(&r);
FillRgn(hDC, hRgn, hBrush);
DeleteObject(hRgn);
}
static void DrawFilledRect1(HDC hDC, int X1, int Y1, int X2, int Y2){
HBRUSH hBrush = CreateSolidBrush(RGB(178, 186, 208));
HPEN hPen = CreatePen(0, 1, RGB(10, 36, 106));
SelectObject(hDC, hBrush);
SelectObject(hDC, hPen);
Rectangle(hDC, X1, Y1, X2, Y2);
DeleteObject(hBrush);
DeleteObject(hPen);
}
};
int SetOwnerDrawByPos(HMENU hMenu, int MenuPos, bool MainMenu){
int itemData;
itemData = minfo->AddMenu(hMenu, MenuPos, MainMenu);
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_FTYPE;
GetMenuItemInfo(hMenu, MenuPos, 1, &mii);
mii.fType |= MFT_OWNERDRAW;
//SetMenuItemInfo(hMenu, MenuPos, 1, &mii);
ModifyMenu(hMenu, MenuPos, MF_BYPOSITION | mii.fType, GetMenuItemID(hMenu, MenuPos), (LPSTR)itemData);
return itemData;
}
void SetOwnerDrawMenuTree(HMENU hMenu, bool MainMenu){
int n = GetMenuItemCount(hMenu), id;
for(int i=0; i<n; i++){
id = SetOwnerDrawByPos(hMenu, i, MainMenu);
if(minfo->GetSubMenu(id)) SetOwnerDrawMenuTree(minfo->GetSubMenu(id), false);
}
}
string GetClsName(HWND hWnd){
string res;
TCHAR buff[128];
GetClassName(hWnd, buff, 128);
return res;
}
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam){
CWPSTRUCT *cwp = (CWPSTRUCT *)lParam;
if(nCode == HC_ACTION){
switch(cwp->message){
case WM_CREATE:
if(GetClsName(cwp->hwnd) == string("#32768"
){
if((HIWORD(wParam) & MF_SYSMENU) != MF_SYSMENU){
//SetW
}
//lFlag = wParam \ &H10000
//
If ((lFlag
And MF_SYSMENU) <> MF_SYSMENU)
Then
// lRet = SetWindowLong(CWP.hWnd, GWL_WNDPROC,
AddressOf lProcShadow)
// SetProp CWP.hWnd, "OldWndProc", lRet
//
End If
}
break;
}
}
//lHooklProc = CallNextHookEx(WH_CALLWNDPROC, ncode, wParam, lParam)
return CallNextHookEx((HHOOK)WH_CALLWNDPROC, nCode, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
HMENU hMenu;
MEASUREITEMSTRUCT mis;
DRAWITEMSTRUCT dis;
bool IsSep;
int lTextColor;
LOGFONT lf = {0, 0, 0, 0, FW_DONTCARE, false, false, false, RUSSIAN_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE};
int PointSize = PTSIZE;
HFONT hFont, hOldFont;
char FontName[128] = FONTNAME;
static HHOOK idHook;
switch(uMsg){
//case WM_NCCREATE:
//break;
//return TRUE;
case WM_DESTROY:
UnhookWindowsHookEx(idHook);
PostQuitMessage(NULL);
break;
case WM_CREATE:
idHook = SetWindowsHookEx(WH_CALLWNDPROC, HookProc, 0, GetWindowThreadProcessId(hWnd, 0));
if(GetClsName(hWnd) == string("#32768"
){
MessageBox(0, _T("#32768 WM_CREATE"
, _T(""
, MB_ICONASTERISK);
}
hMenu = GetMenu(hWnd);
SetOwnerDrawMenuTree(hMenu, true);
return DefWindowProc(hWnd, uMsg, wParam, lParam);
case WM_DRAWITEM:
if(wParam == 0){
Color::InitColors();
CopyMemory(&dis, (void *)lParam, sizeof(DRAWITEMSTRUCT));
IsSep = minfo->IsSeparator(dis.itemData);
HFONT MyFont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0);
HFONT OldFont = (HFONT)SelectObject(dis.hDC, MyFont);
lTextColor = GetSysColor(COLOR_MENUTEXT);
if(minfo->IsMainMenu(dis.itemData)){
MenuDraw::DrawMainMenu(dis);
} else{
MenuDraw::DrawSubMenu(dis, IsSep);
}
}
return FALSE;
case WM_MEASUREITEM:
CopyMemory(&mis, (void *)lParam, sizeof(MEASUREITEMSTRUCT));
IsSep = minfo->IsSeparator(mis.itemData);
if(minfo->IsMainMenu(mis.itemData)){
SIZE sz;
char *mt;
HDC tmpDC;
mt = minfo->GetMenuName(mis.itemData);
tmpDC = GetDC(hWnd);
CopyMemory(lf.lfFaceName, FontName, lstrlen(FontName));
lf.lfHeight = -MulDiv(PointSize, GetDeviceCaps(tmpDC, LOGPIXELSY), 72);
hFont = CreateFontIndirect(&lf);
hOldFont = (HFONT)SelectObject(tmpDC, hFont);
GetTextExtentPoint32(tmpDC, mt, lstrlen(mt), &sz);
SelectObject(tmpDC, hOldFont);
DeleteObject(hFont);
mis.itemWidth = sz.cx + 3;
mis.itemHeight = 23;
} else{
mis.itemWidth = 150;
mis.itemHeight = IsSep?4:22;
}
CopyMemory((void *)lParam, &mis, sizeof(MEASUREITEMSTRUCT));
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void RegisterMainWin(){
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = NULL; //LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.hIconSm = NULL; //LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON2));
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = MainWinClass;
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wc.style = CS_HREDRAW | CS_VREDRAW | 0x20000;
int t = RegisterClassEx(&wc);
t = GetLastError();
}
HWND CreateMainWin(){
HWND hWnd = 0;
hWnd = CreateWindowEx(NULL, MainWinClass, "Window Title", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if(hWnd){
ShowWindow(hWnd, 1);
UpdateWindow(hWnd);
}
return hWnd;
}
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR CmdLine, int nCmdShow){
hInstance = hInst;
minfo = new MenuInfo(100);
RegisterMainWin();
CreateMainWin();
MSG msg;
while(true){
if(!GetMessage(&msg, NULL, 0, 0)) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}