OSDN Git Service

merge original branch.
[tortoisegit/TortoiseGitJp.git] / src / Utils / MiscUI / MenuButton.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2006,2008 - Stefan Kueng\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 "MenuButton.h"\r
21 \r
22 #include "MyMemDC.h"\r
23 \r
24 \r
25 #ifdef _DEBUG\r
26 #undef THIS_FILE\r
27 static char THIS_FILE[]=__FILE__;\r
28 #define new DEBUG_NEW\r
29 #endif\r
30 \r
31 \r
32 const int g_ciArrowSizeX = 4 ;\r
33 const int g_ciArrowSizeY = 2 ;\r
34 \r
35 IMPLEMENT_DYNCREATE(CMenuButton, COddButton)\r
36 \r
37 CMenuButton::CMenuButton(void):\r
38         _Inherited(),\r
39         m_bMouseOver(FALSE),\r
40         m_currentEntry(-1),\r
41         m_bUseThemes(true)\r
42 {\r
43 }\r
44 \r
45 CMenuButton::~CMenuButton(void)\r
46 {\r
47 }\r
48 \r
49 void CMenuButton::UseThemes(bool bUseThemes)\r
50 {\r
51         m_bUseThemes = bUseThemes;\r
52         Invalidate();\r
53 }\r
54 \r
55 bool CMenuButton::SetCurrentEntry(INT_PTR entry)\r
56 {\r
57         if (entry >= m_sEntries.GetCount())\r
58                 return false;\r
59         m_currentEntry = entry;\r
60         SetWindowText(m_sEntries[m_currentEntry]);\r
61         return true;\r
62 }\r
63 \r
64 void CMenuButton::RemoveAll()\r
65 {\r
66         m_sEntries.RemoveAll();\r
67         m_currentEntry = -1;\r
68 }\r
69 \r
70 INT_PTR CMenuButton::AddEntry(const CString& sEntry)\r
71 {\r
72         INT_PTR ret = m_sEntries.Add(sEntry);\r
73         m_currentEntry = 0;\r
74         SetWindowText(m_sEntries[0]);\r
75         Invalidate();\r
76         return ret;\r
77 }\r
78 \r
79 INT_PTR CMenuButton::AddEntries(const CStringArray& sEntries)\r
80 {\r
81         INT_PTR ret = m_sEntries.Append(sEntries);\r
82         m_currentEntry = 0;\r
83         SetWindowText(m_sEntries[0]);\r
84         Invalidate();\r
85         return ret;\r
86 }\r
87 \r
88 BEGIN_MESSAGE_MAP(CMenuButton, _Inherited)\r
89         ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)\r
90         ON_WM_CREATE()\r
91         ON_WM_MOUSEMOVE()\r
92         ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)\r
93         ON_WM_DESTROY()\r
94 END_MESSAGE_MAP()\r
95 \r
96 \r
97 int CMenuButton::OnCreate(LPCREATESTRUCT lpCreateStruct) \r
98 {\r
99         if (_Inherited::OnCreate(lpCreateStruct) == -1)\r
100                 return -1;\r
101 \r
102         return 0;\r
103 }\r
104 \r
105 bool CMenuButton::ShowMenu()\r
106 {\r
107         CRect rDraw;\r
108         GetWindowRect(rDraw);\r
109         CMenu popup;\r
110         if (popup.CreatePopupMenu())\r
111         {\r
112                 for (int index=0; index<m_sEntries.GetCount(); ++index)\r
113                 {\r
114                         popup.AppendMenu(MF_ENABLED | MF_STRING, index+1, m_sEntries.GetAt(index));\r
115                 }\r
116                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rDraw.left, rDraw.bottom-1, this);\r
117                 if (cmd <= 0)\r
118                         return false;\r
119                 if (cmd > m_sEntries.GetCount())\r
120                         return false;\r
121                 m_currentEntry = cmd-1;\r
122                 SetWindowText(m_sEntries.GetAt(cmd-1));\r
123                 return true;\r
124         }\r
125         return false;\r
126 }\r
127 \r
128 BOOL CMenuButton::OnClicked()\r
129 {\r
130         CRect rDraw;\r
131         GetWindowRect(rDraw);\r
132 \r
133         CPoint point;\r
134         DWORD ptW = GetMessagePos();\r
135         point.x = GET_X_LPARAM(ptW);\r
136         point.y = GET_Y_LPARAM(ptW);\r
137         if (rDraw.PtInRect(point))\r
138         {\r
139                 // check if the user clicked on the arrow or the button part\r
140                 CRect rArrow;\r
141                 rArrow = rDraw;\r
142                 CPoint temp(m_SeparatorX, 0);\r
143                 ClientToScreen(&temp);\r
144                 rArrow.left = temp.x;\r
145                 if (rArrow.PtInRect(point))\r
146                 {\r
147                         if (ShowMenu())\r
148                                 return FALSE;\r
149                         return TRUE;\r
150                 }\r
151         }\r
152         // click on button or user pressed enter/space.\r
153         // let the parent handle the message the usual way\r
154         return FALSE;\r
155 }\r
156 \r
157 void CMenuButton::OnNMThemeChanged(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
158 {\r
159         if (m_bUseThemes)\r
160                 m_xpButton.Open(GetSafeHwnd(), L"BUTTON");\r
161         Invalidate(FALSE);\r
162         *pResult = 0;\r
163 }\r
164 \r
165 void CMenuButton::OnMouseMove(UINT nFlags, CPoint point)\r
166 {\r
167         if (!m_bMouseOver)\r
168         {\r
169                 m_bMouseOver = TRUE;\r
170                 TRACKMOUSEEVENT tme;\r
171                 tme.cbSize = sizeof(tme);\r
172                 tme.dwFlags = TME_LEAVE;\r
173                 tme.hwndTrack = m_hWnd;\r
174                 _TrackMouseEvent(&tme);\r
175                 Invalidate(FALSE);\r
176         }\r
177 \r
178         _Inherited::OnMouseMove(nFlags, point);\r
179 }\r
180 \r
181 LRESULT CMenuButton::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/)\r
182 {\r
183         if (m_bMouseOver)\r
184         {\r
185                 m_bMouseOver = FALSE;\r
186                 Invalidate(FALSE);\r
187         }\r
188         return 0;\r
189 }\r
190 \r
191 void CMenuButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) \r
192 {\r
193         ASSERT(lpDrawItemStruct);\r
194 \r
195         CDC*    pDC      = CDC::FromHandle(lpDrawItemStruct->hDC);\r
196         CMyMemDC  dcMem(pDC);\r
197         UINT    state    = lpDrawItemStruct->itemState;\r
198         CRect   rDraw    = lpDrawItemStruct->rcItem;\r
199         CRect   rArrow;\r
200 \r
201         SendMessage(WM_ERASEBKGND, (WPARAM)dcMem.GetSafeHdc());\r
202 \r
203         if (state & ODS_FOCUS)\r
204                 state |= ODS_DEFAULT;\r
205 \r
206         if(!m_xpButton)\r
207                 m_xpButton.Open(GetSafeHwnd(), L"BUTTON");\r
208 \r
209         // Draw Outer Edge\r
210         if ((m_xpButton.IsAppThemed())&&(m_bUseThemes))\r
211         {\r
212                 int nFrameState = 0;\r
213                 if ((state & ODS_SELECTED) != 0)\r
214                         nFrameState |= PBS_PRESSED;\r
215                 if ((state & ODS_DISABLED) != 0)\r
216                         nFrameState |= PBS_DISABLED;\r
217                 if ((state & ODS_HOTLIGHT) != 0 || m_bMouseOver)\r
218                         nFrameState |= PBS_HOT;\r
219                 else if ((state & ODS_DEFAULT) != 0)\r
220                         nFrameState |= PBS_DEFAULTED;\r
221 \r
222                 m_xpButton.DrawBackground(dcMem.GetSafeHdc(), BP_PUSHBUTTON, nFrameState, &rDraw, NULL);\r
223                 m_xpButton.GetBackgroundContentRect(dcMem.GetSafeHdc(), BP_PUSHBUTTON, nFrameState, &rDraw, &rDraw);\r
224         }\r
225         else\r
226         {\r
227                 UINT uFrameState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;\r
228 \r
229                 if (state & ODS_SELECTED)\r
230                         uFrameState |= DFCS_PUSHED;\r
231 \r
232                 if (state & ODS_DISABLED)\r
233                         uFrameState |= DFCS_INACTIVE;\r
234 \r
235                 dcMem.DrawFrameControl(&rDraw, DFC_BUTTON, uFrameState);\r
236 \r
237                 if (state & ODS_SELECTED)\r
238                         rDraw.OffsetRect(1,1);\r
239         }\r
240 \r
241 \r
242         // Draw Focus\r
243         if (IsDefault()) \r
244         {\r
245                 CRect rFocus(rDraw.left, rDraw.top, rDraw.right - 1, rDraw.bottom);\r
246                 dcMem.DrawFocusRect(&rFocus);\r
247         }\r
248 \r
249         rDraw.DeflateRect(::GetSystemMetrics(SM_CXEDGE), ::GetSystemMetrics(SM_CYEDGE));\r
250         rDraw.DeflateRect(0, 0, 1, 0);\r
251 \r
252         // Draw Arrow\r
253         rArrow.left             = rDraw.right - g_ciArrowSizeX - ::GetSystemMetrics(SM_CXEDGE) /2;\r
254         rArrow.right    = rArrow.left + g_ciArrowSizeX;\r
255         rArrow.top              = (rDraw.bottom + rDraw.top)/2 - g_ciArrowSizeY / 2;\r
256         rArrow.bottom   = (rDraw.bottom + rDraw.top)/2 + g_ciArrowSizeY / 2;\r
257 \r
258         DrawArrow(&dcMem, &rArrow, (state & ODS_DISABLED) ? ::GetSysColor(COLOR_GRAYTEXT) : ::GetSysColor(COLOR_BTNTEXT));\r
259         rDraw.right = rArrow.left - ::GetSystemMetrics(SM_CXEDGE) / 2 - 2;\r
260 \r
261         // Draw Separator\r
262         dcMem.DrawEdge(&rDraw, EDGE_ETCHED,     BF_RIGHT);\r
263         m_SeparatorX = rDraw.right;\r
264         rDraw.right -= (::GetSystemMetrics(SM_CXEDGE) * 2) + 1 ;\r
265 \r
266         // Draw Text\r
267         CString szTemp;\r
268         GetWindowText(szTemp);\r
269 \r
270         CFont *pOldFont = dcMem.SelectObject(GetFont());\r
271         dcMem.SetTextColor((state & ODS_DISABLED) ? ::GetSysColor(COLOR_GRAYTEXT) : ::GetSysColor(COLOR_BTNTEXT));\r
272 \r
273         dcMem.SetBkMode(TRANSPARENT);\r
274 \r
275         // DrawText() ignores the DT_VCENTER if DT_WORDBREAK\r
276         // is specified. Therefore we center the text ourselves.\r
277         CRect rectText(rDraw);\r
278         rectText.bottom=0;\r
279         dcMem.DrawText(szTemp,rectText,DT_WORDBREAK | DT_CALCRECT);\r
280         if (rDraw.Height()>rectText.Height())\r
281                 rectText.OffsetRect(0,(rDraw.Height()-rectText.Height())/2);\r
282         rectText.left = rDraw.left;\r
283         rectText.right = rDraw.right;\r
284 \r
285         // check the button styles and draw the text accordingly\r
286         DWORD style = GetStyle();\r
287         UINT format = DT_CENTER | DT_VCENTER;\r
288         if (style & BS_BOTTOM)\r
289                 format |= DT_BOTTOM;\r
290         if (style & BS_LEFT)\r
291                 format |= DT_LEFT;\r
292         if (style & BS_MULTILINE)\r
293                 format |= DT_WORDBREAK;\r
294         if (style & BS_RIGHT)\r
295                 format |= DT_RIGHT;\r
296         if (style & BS_TOP)\r
297                 format |= DT_TOP;\r
298         format |= GetStyle()&\r
299         dcMem.DrawText(szTemp, rectText, format);\r
300 \r
301         dcMem.SelectObject(pOldFont);\r
302 }\r
303 \r
304 \r
305 void CMenuButton::DrawArrow(CDC* pDC, \r
306                                                         RECT* pRect, \r
307                                                         COLORREF clrArrow)\r
308 {\r
309         POINT ptsArrow[3];\r
310 \r
311         ptsArrow[0].x = pRect->left;\r
312         ptsArrow[0].y = pRect->top;\r
313         ptsArrow[1].x = pRect->right;\r
314         ptsArrow[1].y = pRect->top;\r
315         ptsArrow[2].x = (pRect->left + pRect->right)/2;\r
316         ptsArrow[2].y = pRect->bottom;\r
317         CBrush brsArrow(clrArrow);\r
318         CPen penArrow(PS_SOLID, 1 , clrArrow);\r
319 \r
320         CBrush* pOldBrush = pDC->SelectObject(&brsArrow);\r
321         CPen*   pOldPen   = pDC->SelectObject(&penArrow);\r
322 \r
323         pDC->SetPolyFillMode(WINDING);\r
324         pDC->Polygon(ptsArrow, 3);\r
325 \r
326         pDC->SelectObject(pOldBrush);\r
327         pDC->SelectObject(pOldPen);\r
328 }\r
329 \r
330 BOOL CMenuButton::PreTranslateMessage(MSG* pMsg)\r
331 {\r
332         if(pMsg->message == WM_KEYDOWN)\r
333         {\r
334                 switch(pMsg->wParam)\r
335                 {\r
336                 case VK_DOWN:\r
337                 case VK_F4:\r
338                         ShowMenu();                     // Activate pop-up when F4-key or down-arrow is hit\r
339                         return TRUE;\r
340                 case VK_UP:\r
341                         return TRUE;            // Disable up-arrow\r
342                 }\r
343         }\r
344 \r
345         return _Inherited::PreTranslateMessage(pMsg);\r
346 }\r
347 \r
348 void CMenuButton::OnDestroy()\r
349 {\r
350         m_sEntries.RemoveAll();\r
351 \r
352         _Inherited::OnDestroy();\r
353 }\r