1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2011 - TortoiseGit
4 // Copyright (C) 2008 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 CIconMenu::CIconMenu(void) : CMenu()
25 , pfnBeginBufferedPaint(NULL
)
26 , pfnEndBufferedPaint(NULL
)
27 , pfnGetBufferedPaintBits(NULL
)
29 if (SysInfo::Instance().IsVistaOrLater())
31 HMODULE hUxTheme
= ::GetModuleHandle (_T("UXTHEME.DLL"));
33 pfnGetBufferedPaintBits
= (FN_GetBufferedPaintBits
)::GetProcAddress(hUxTheme
, "GetBufferedPaintBits");
34 pfnBeginBufferedPaint
= (FN_BeginBufferedPaint
)::GetProcAddress(hUxTheme
, "BeginBufferedPaint");
35 pfnEndBufferedPaint
= (FN_EndBufferedPaint
)::GetProcAddress(hUxTheme
, "EndBufferedPaint");
39 CIconMenu::~CIconMenu(void)
41 std::map
<UINT
, HBITMAP
>::iterator it
;
42 for (it
= bitmaps
.begin(); it
!= bitmaps
.end(); ++it
)
44 ::DeleteObject(it
->second
);
49 BOOL
CIconMenu::AppendMenuIcon(UINT_PTR nIDNewItem
, LPCTSTR lpszNewItem
, UINT uIcon
/* = 0 */, HMENU hsubmenu
)
51 TCHAR menutextbuffer
[255] = {0};
52 _tcscpy_s(menutextbuffer
, 255, lpszNewItem
);
55 return CMenu::AppendMenu(MF_STRING
| MF_ENABLED
, nIDNewItem
, menutextbuffer
);
57 MENUITEMINFO info
= {0};
58 info
.cbSize
= sizeof(info
);
59 info
.fMask
= MIIM_STRING
| MIIM_FTYPE
| MIIM_ID
;
60 info
.fType
= MFT_STRING
;
64 info
.fMask
|= MIIM_SUBMENU
;
65 info
.hSubMenu
= hsubmenu
;
69 info
.wID
= (UINT
)nIDNewItem
;
70 info
.dwTypeData
= menutextbuffer
;
71 if (SysInfo::Instance().IsVistaOrLater())
73 info
.fMask
|= MIIM_BITMAP
;
74 info
.hbmpItem
= IconToBitmapPARGB32(uIcon
);
78 info
.fMask
|= MIIM_BITMAP
;
79 info
.hbmpItem
= HBMMENU_CALLBACK
;
81 icons
[nIDNewItem
] = uIcon
;
82 return InsertMenuItem((UINT
)nIDNewItem
, &info
);
85 BOOL
CIconMenu::AppendMenuIcon(UINT_PTR nIDNewItem
, UINT_PTR nNewItem
, UINT uIcon
/* = 0 */, HMENU hsubmenu
)
88 temp
.LoadString((UINT
)nNewItem
);
90 return AppendMenuIcon(nIDNewItem
, temp
, uIcon
, hsubmenu
);
93 void CIconMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct
)
95 if ((lpDrawItemStruct
==NULL
)||(lpDrawItemStruct
->CtlType
!= ODT_MENU
))
96 return; //not for a menu
97 HICON hIcon
= (HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(icons
[lpDrawItemStruct
->itemID
]), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
100 DrawIconEx(lpDrawItemStruct
->hDC
,
101 lpDrawItemStruct
->rcItem
.left
- 16,
102 lpDrawItemStruct
->rcItem
.top
+ (lpDrawItemStruct
->rcItem
.bottom
- lpDrawItemStruct
->rcItem
.top
- 16) / 2,
108 void CIconMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct
)
110 if (lpMeasureItemStruct
==NULL
)
112 lpMeasureItemStruct
->itemWidth
+= 2;
113 if (lpMeasureItemStruct
->itemHeight
< 16)
114 lpMeasureItemStruct
->itemHeight
= 16;
117 HBITMAP
CIconMenu::IconToBitmapPARGB32(UINT uIcon
)
119 std::map
<UINT
, HBITMAP
>::iterator bitmap_it
= bitmaps
.lower_bound(uIcon
);
120 if (bitmap_it
!= bitmaps
.end() && bitmap_it
->first
== uIcon
)
121 return bitmap_it
->second
;
123 HICON hIcon
= (HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(uIcon
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
127 if (pfnBeginBufferedPaint
== NULL
|| pfnEndBufferedPaint
== NULL
|| pfnGetBufferedPaintBits
== NULL
)
131 sizIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
132 sizIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
135 SetRect(&rcIcon
, 0, 0, sizIcon
.cx
, sizIcon
.cy
);
138 HDC hdcDest
= CreateCompatibleDC(NULL
);
141 if (SUCCEEDED(Create32BitHBITMAP(hdcDest
, &sizIcon
, NULL
, &hBmp
)))
143 HBITMAP hbmpOld
= (HBITMAP
)SelectObject(hdcDest
, hBmp
);
146 BLENDFUNCTION bfAlpha
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
147 BP_PAINTPARAMS paintParams
= {0};
148 paintParams
.cbSize
= sizeof(paintParams
);
149 paintParams
.dwFlags
= BPPF_ERASE
;
150 paintParams
.pBlendFunction
= &bfAlpha
;
153 HPAINTBUFFER hPaintBuffer
= pfnBeginBufferedPaint(hdcDest
, &rcIcon
, BPBF_DIB
, &paintParams
, &hdcBuffer
);
156 if (DrawIconEx(hdcBuffer
, 0, 0, hIcon
, sizIcon
.cx
, sizIcon
.cy
, 0, NULL
, DI_NORMAL
))
158 // If icon did not have an alpha channel we need to convert buffer to PARGB
159 ConvertBufferToPARGB32(hPaintBuffer
, hdcDest
, hIcon
, sizIcon
);
162 // This will write the buffer contents to the destination bitmap
163 pfnEndBufferedPaint(hPaintBuffer
, TRUE
);
166 SelectObject(hdcDest
, hbmpOld
);
176 bitmaps
.insert(bitmap_it
, std::make_pair(uIcon
, hBmp
));
180 HRESULT
CIconMenu::Create32BitHBITMAP(HDC hdc
, const SIZE
*psize
, __deref_opt_out
void **ppvBits
, __out HBITMAP
* phBmp
)
185 SecureZeroMemory(&bmi
, sizeof(bmi
));
186 bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
187 bmi
.bmiHeader
.biPlanes
= 1;
188 bmi
.bmiHeader
.biCompression
= BI_RGB
;
190 bmi
.bmiHeader
.biWidth
= psize
->cx
;
191 bmi
.bmiHeader
.biHeight
= psize
->cy
;
192 bmi
.bmiHeader
.biBitCount
= 32;
194 HDC hdcUsed
= hdc
? hdc
: GetDC(NULL
);
197 *phBmp
= CreateDIBSection(hdcUsed
, &bmi
, DIB_RGB_COLORS
, ppvBits
, NULL
, 0);
200 ReleaseDC(NULL
, hdcUsed
);
203 return (NULL
== *phBmp
) ? E_OUTOFMEMORY
: S_OK
;
206 HRESULT
CIconMenu::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer
, HDC hdc
, HICON hicon
, SIZE
& sizIcon
)
210 HRESULT hr
= pfnGetBufferedPaintBits(hPaintBuffer
, &prgbQuad
, &cxRow
);
213 Gdiplus::ARGB
*pargb
= reinterpret_cast<Gdiplus::ARGB
*>(prgbQuad
);
214 if (!HasAlpha(pargb
, sizIcon
, cxRow
))
217 if (GetIconInfo(hicon
, &info
))
221 hr
= ConvertToPARGB32(hdc
, pargb
, info
.hbmMask
, sizIcon
, cxRow
);
224 DeleteObject(info
.hbmColor
);
225 DeleteObject(info
.hbmMask
);
233 bool CIconMenu::HasAlpha(__in
Gdiplus::ARGB
*pargb
, SIZE
& sizImage
, int cxRow
)
235 ULONG cxDelta
= cxRow
- sizImage
.cx
;
236 for (ULONG y
= sizImage
.cy
; y
; --y
)
238 for (ULONG x
= sizImage
.cx
; x
; --x
)
240 if (*pargb
++ & 0xFF000000)
252 HRESULT
CIconMenu::ConvertToPARGB32(HDC hdc
, __inout
Gdiplus::ARGB
*pargb
, HBITMAP hbmp
, SIZE
& sizImage
, int cxRow
)
255 SecureZeroMemory(&bmi
, sizeof(bmi
));
256 bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
257 bmi
.bmiHeader
.biPlanes
= 1;
258 bmi
.bmiHeader
.biCompression
= BI_RGB
;
260 bmi
.bmiHeader
.biWidth
= sizImage
.cx
;
261 bmi
.bmiHeader
.biHeight
= sizImage
.cy
;
262 bmi
.bmiHeader
.biBitCount
= 32;
264 HRESULT hr
= E_OUTOFMEMORY
;
265 HANDLE hHeap
= GetProcessHeap();
266 void *pvBits
= HeapAlloc(hHeap
, 0, bmi
.bmiHeader
.biWidth
* 4 * bmi
.bmiHeader
.biHeight
);
270 if (GetDIBits(hdc
, hbmp
, 0, bmi
.bmiHeader
.biHeight
, pvBits
, &bmi
, DIB_RGB_COLORS
) == bmi
.bmiHeader
.biHeight
)
272 ULONG cxDelta
= cxRow
- bmi
.bmiHeader
.biWidth
;
273 Gdiplus::ARGB
*pargbMask
= static_cast<Gdiplus::ARGB
*>(pvBits
);
275 for (ULONG y
= bmi
.bmiHeader
.biHeight
; y
; --y
)
277 for (ULONG x
= bmi
.bmiHeader
.biWidth
; x
; --x
)
287 *pargb
++ |= 0xFF000000;
297 HeapFree(hHeap
, 0, pvBits
);
303 BOOL
CIconMenu::SetMenuItemData(UINT_PTR nIDNewItem
, LONG_PTR data
)
306 MENUITEMINFO menuinfo
={0};
307 menuinfo
.cbSize
= sizeof(menuinfo
);
308 GetMenuItemInfo((UINT
)nIDNewItem
, &menuinfo
);
309 menuinfo
.dwItemData
=data
;
310 menuinfo
.fMask
|= MIIM_DATA
;
311 return SetMenuItemInfo((UINT
)nIDNewItem
,&menuinfo
);
315 LONG_PTR
CIconMenu::GetMenuItemData(UINT_PTR nIDNewItem
)
317 MENUITEMINFO menuinfo
={0};
318 menuinfo
.fMask
|= MIIM_DATA
;
319 menuinfo
.cbSize
= sizeof(menuinfo
);
320 GetMenuItemInfo((UINT
)nIDNewItem
, &menuinfo
);
322 return menuinfo
.dwItemData
;