OSDN Git Service

Show Ignore Sub Menu
[tortoisegit/TortoiseGitJp.git] / src / Utils / MiscUI / IconMenu.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2008 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "StdAfx.h"\r
20 #include "IconMenu.h"\r
21 \r
22 CIconMenu::CIconMenu(void) : CMenu()\r
23         , pfnBeginBufferedPaint(NULL)\r
24         , pfnEndBufferedPaint(NULL)\r
25         , pfnGetBufferedPaintBits(NULL)\r
26 {\r
27         OSVERSIONINFOEX inf;\r
28         SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
29         inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
30         GetVersionEx((OSVERSIONINFO *)&inf);\r
31         winVersion = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
32 \r
33         if (winVersion >= 0x0600)\r
34         {\r
35                 HMODULE hUxTheme = ::GetModuleHandle (_T("UXTHEME.DLL"));\r
36 \r
37                 pfnGetBufferedPaintBits = (FN_GetBufferedPaintBits)::GetProcAddress(hUxTheme, "GetBufferedPaintBits");\r
38                 pfnBeginBufferedPaint = (FN_BeginBufferedPaint)::GetProcAddress(hUxTheme, "BeginBufferedPaint");\r
39                 pfnEndBufferedPaint = (FN_EndBufferedPaint)::GetProcAddress(hUxTheme, "EndBufferedPaint");\r
40         }\r
41 }\r
42 \r
43 CIconMenu::~CIconMenu(void)\r
44 {\r
45         std::map<UINT, HBITMAP>::iterator it;\r
46         for (it = bitmaps.begin(); it != bitmaps.end(); ++it)\r
47         {\r
48                 ::DeleteObject(it->second);\r
49         }\r
50         bitmaps.clear();\r
51 }\r
52 \r
53 BOOL CIconMenu::AppendMenuIcon(UINT_PTR nIDNewItem, LPCTSTR lpszNewItem, UINT uIcon /* = 0 */)\r
54 {\r
55         TCHAR menutextbuffer[255] = {0};\r
56         _tcscpy_s(menutextbuffer, 255, lpszNewItem);\r
57 \r
58         if (uIcon == 0)\r
59                 return CMenu::AppendMenu(MF_STRING | MF_ENABLED, nIDNewItem, menutextbuffer);\r
60 \r
61         MENUITEMINFO info = {0};\r
62         info.cbSize = sizeof(info);\r
63         info.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;\r
64         info.fType = MFT_STRING;\r
65         info.wID = nIDNewItem;\r
66         info.dwTypeData = menutextbuffer;\r
67         if (winVersion >= 0x600)\r
68         {\r
69                 info.fMask |= MIIM_BITMAP;\r
70                 info.hbmpItem = IconToBitmapPARGB32(uIcon);\r
71         }\r
72         else\r
73         {\r
74                 info.fMask |= MIIM_BITMAP;\r
75                 info.hbmpItem = HBMMENU_CALLBACK;\r
76         }\r
77         icons[nIDNewItem] = uIcon;\r
78         return InsertMenuItem(nIDNewItem, &info);\r
79 }\r
80 \r
81 BOOL CIconMenu::AppendMenuIcon(UINT_PTR nIDNewItem, UINT_PTR nNewItem, UINT uIcon /* = 0 */)\r
82 {\r
83         CString temp;\r
84         temp.LoadString(nNewItem);\r
85 \r
86         return AppendMenuIcon(nIDNewItem, temp, uIcon);\r
87 }\r
88 \r
89 void CIconMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)\r
90 {\r
91         if ((lpDrawItemStruct==NULL)||(lpDrawItemStruct->CtlType != ODT_MENU))\r
92                 return;         //not for a menu\r
93         HICON hIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(icons[lpDrawItemStruct->itemID]), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
94         if (hIcon == NULL)\r
95                 return;\r
96         DrawIconEx(lpDrawItemStruct->hDC,\r
97                 lpDrawItemStruct->rcItem.left - 16,\r
98                 lpDrawItemStruct->rcItem.top + (lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top - 16) / 2,\r
99                 hIcon, 16, 16,\r
100                 0, NULL, DI_NORMAL);\r
101         DestroyIcon(hIcon);\r
102 }\r
103 \r
104 void CIconMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)\r
105 {\r
106         if (lpMeasureItemStruct==NULL)\r
107                 return;\r
108         lpMeasureItemStruct->itemWidth += 2;\r
109         if (lpMeasureItemStruct->itemHeight < 16)\r
110                 lpMeasureItemStruct->itemHeight = 16;\r
111 }\r
112 \r
113 HBITMAP CIconMenu::IconToBitmapPARGB32(UINT uIcon)\r
114 {\r
115         std::map<UINT, HBITMAP>::iterator bitmap_it = bitmaps.lower_bound(uIcon);\r
116         if (bitmap_it != bitmaps.end() && bitmap_it->first == uIcon)\r
117                 return bitmap_it->second;\r
118 \r
119         HICON hIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(uIcon), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
120         if (!hIcon)\r
121                 return NULL;\r
122 \r
123         if (pfnBeginBufferedPaint == NULL || pfnEndBufferedPaint == NULL || pfnGetBufferedPaintBits == NULL)\r
124                 return NULL;\r
125 \r
126         SIZE sizIcon;\r
127         sizIcon.cx = GetSystemMetrics(SM_CXSMICON);\r
128         sizIcon.cy = GetSystemMetrics(SM_CYSMICON);\r
129 \r
130         RECT rcIcon;\r
131         SetRect(&rcIcon, 0, 0, sizIcon.cx, sizIcon.cy);\r
132         HBITMAP hBmp = NULL;\r
133 \r
134         HDC hdcDest = CreateCompatibleDC(NULL);\r
135         if (hdcDest)\r
136         {\r
137                 if (SUCCEEDED(Create32BitHBITMAP(hdcDest, &sizIcon, NULL, &hBmp)))\r
138                 {\r
139                         HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, hBmp);\r
140                         if (hbmpOld)\r
141                         {\r
142                                 BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };\r
143                                 BP_PAINTPARAMS paintParams = {0};\r
144                                 paintParams.cbSize = sizeof(paintParams);\r
145                                 paintParams.dwFlags = BPPF_ERASE;\r
146                                 paintParams.pBlendFunction = &bfAlpha;\r
147 \r
148                                 HDC hdcBuffer;\r
149                                 HPAINTBUFFER hPaintBuffer = pfnBeginBufferedPaint(hdcDest, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer);\r
150                                 if (hPaintBuffer)\r
151                                 {\r
152                                         if (DrawIconEx(hdcBuffer, 0, 0, hIcon, sizIcon.cx, sizIcon.cy, 0, NULL, DI_NORMAL))\r
153                                         {\r
154                                                 // If icon did not have an alpha channel we need to convert buffer to PARGB\r
155                                                 ConvertBufferToPARGB32(hPaintBuffer, hdcDest, hIcon, sizIcon);\r
156                                         }\r
157 \r
158                                         // This will write the buffer contents to the destination bitmap\r
159                                         pfnEndBufferedPaint(hPaintBuffer, TRUE);\r
160                                 }\r
161 \r
162                                 SelectObject(hdcDest, hbmpOld);\r
163                         }\r
164                 }\r
165 \r
166                 DeleteDC(hdcDest);\r
167         }\r
168 \r
169         DestroyIcon(hIcon);\r
170 \r
171         if(hBmp)\r
172                 bitmaps.insert(bitmap_it, std::make_pair(uIcon, hBmp));\r
173         return hBmp;\r
174 }\r
175 \r
176 HRESULT CIconMenu::Create32BitHBITMAP(HDC hdc, const SIZE *psize, __deref_opt_out void **ppvBits, __out HBITMAP* phBmp)\r
177 {\r
178         *phBmp = NULL;\r
179 \r
180         BITMAPINFO bmi;\r
181         SecureZeroMemory(&bmi, sizeof(bmi));\r
182         bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
183         bmi.bmiHeader.biPlanes = 1;\r
184         bmi.bmiHeader.biCompression = BI_RGB;\r
185 \r
186         bmi.bmiHeader.biWidth = psize->cx;\r
187         bmi.bmiHeader.biHeight = psize->cy;\r
188         bmi.bmiHeader.biBitCount = 32;\r
189 \r
190         HDC hdcUsed = hdc ? hdc : GetDC(NULL);\r
191         if (hdcUsed)\r
192         {\r
193                 *phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);\r
194                 if (hdc != hdcUsed)\r
195                 {\r
196                         ReleaseDC(NULL, hdcUsed);\r
197                 }\r
198         }\r
199         return (NULL == *phBmp) ? E_OUTOFMEMORY : S_OK;\r
200 }\r
201 \r
202 HRESULT CIconMenu::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hdc, HICON hicon, SIZE& sizIcon)\r
203 {\r
204         RGBQUAD *prgbQuad;\r
205         int cxRow;\r
206         HRESULT hr = pfnGetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow);\r
207         if (SUCCEEDED(hr))\r
208         {\r
209                 Gdiplus::ARGB *pargb = reinterpret_cast<Gdiplus::ARGB *>(prgbQuad);\r
210                 if (!HasAlpha(pargb, sizIcon, cxRow))\r
211                 {\r
212                         ICONINFO info;\r
213                         if (GetIconInfo(hicon, &info))\r
214                         {\r
215                                 if (info.hbmMask)\r
216                                 {\r
217                                         hr = ConvertToPARGB32(hdc, pargb, info.hbmMask, sizIcon, cxRow);\r
218                                 }\r
219 \r
220                                 DeleteObject(info.hbmColor);\r
221                                 DeleteObject(info.hbmMask);\r
222                         }\r
223                 }\r
224         }\r
225 \r
226         return hr;\r
227 }\r
228 \r
229 bool CIconMenu::HasAlpha(__in Gdiplus::ARGB *pargb, SIZE& sizImage, int cxRow)\r
230 {\r
231         ULONG cxDelta = cxRow - sizImage.cx;\r
232         for (ULONG y = sizImage.cy; y; --y)\r
233         {\r
234                 for (ULONG x = sizImage.cx; x; --x)\r
235                 {\r
236                         if (*pargb++ & 0xFF000000)\r
237                         {\r
238                                 return true;\r
239                         }\r
240                 }\r
241 \r
242                 pargb += cxDelta;\r
243         }\r
244 \r
245         return false;\r
246 }\r
247 \r
248 HRESULT CIconMenu::ConvertToPARGB32(HDC hdc, __inout Gdiplus::ARGB *pargb, HBITMAP hbmp, SIZE& sizImage, int cxRow)\r
249 {\r
250         BITMAPINFO bmi;\r
251         SecureZeroMemory(&bmi, sizeof(bmi));\r
252         bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
253         bmi.bmiHeader.biPlanes = 1;\r
254         bmi.bmiHeader.biCompression = BI_RGB;\r
255 \r
256         bmi.bmiHeader.biWidth = sizImage.cx;\r
257         bmi.bmiHeader.biHeight = sizImage.cy;\r
258         bmi.bmiHeader.biBitCount = 32;\r
259 \r
260         HRESULT hr = E_OUTOFMEMORY;\r
261         HANDLE hHeap = GetProcessHeap();\r
262         void *pvBits = HeapAlloc(hHeap, 0, bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight);\r
263         if (pvBits)\r
264         {\r
265                 hr = E_UNEXPECTED;\r
266                 if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight)\r
267                 {\r
268                         ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;\r
269             Gdiplus::ARGB *pargbMask = static_cast<Gdiplus::ARGB *>(pvBits);\r
270 \r
271                         for (ULONG y = bmi.bmiHeader.biHeight; y; --y)\r
272                         {\r
273                                 for (ULONG x = bmi.bmiHeader.biWidth; x; --x)\r
274                                 {\r
275                                         if (*pargbMask++)\r
276                                         {\r
277                                                 // transparent pixel\r
278                                                 *pargb++ = 0;\r
279                                         }\r
280                                         else\r
281                                         {\r
282                                                 // opaque pixel\r
283                                                 *pargb++ |= 0xFF000000;\r
284                                         }\r
285                                 }\r
286 \r
287                                 pargb += cxDelta;\r
288                         }\r
289 \r
290                         hr = S_OK;\r
291                 }\r
292 \r
293                 HeapFree(hHeap, 0, pvBits);\r
294         }\r
295 \r
296         return hr;\r
297 }\r