OSDN Git Service

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