1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2006,2008 - Stefan Kueng
\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
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
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
20 #include "MenuButton.h"
\r
22 #include "MyMemDC.h"
\r
27 static char THIS_FILE[]=__FILE__;
\r
28 #define new DEBUG_NEW
\r
32 const int g_ciArrowSizeX = 4 ;
\r
33 const int g_ciArrowSizeY = 2 ;
\r
35 IMPLEMENT_DYNCREATE(CMenuButton, COddButton)
\r
37 CMenuButton::CMenuButton(void):
\r
39 m_bMouseOver(FALSE),
\r
45 CMenuButton::~CMenuButton(void)
\r
49 void CMenuButton::UseThemes(bool bUseThemes)
\r
51 m_bUseThemes = bUseThemes;
\r
55 bool CMenuButton::SetCurrentEntry(INT_PTR entry)
\r
57 if (entry >= m_sEntries.GetCount())
\r
59 m_currentEntry = entry;
\r
60 SetWindowText(m_sEntries[m_currentEntry]);
\r
64 void CMenuButton::RemoveAll()
\r
66 m_sEntries.RemoveAll();
\r
67 m_currentEntry = -1;
\r
70 INT_PTR CMenuButton::AddEntry(const CString& sEntry)
\r
72 INT_PTR ret = m_sEntries.Add(sEntry);
\r
74 SetWindowText(m_sEntries[0]);
\r
79 INT_PTR CMenuButton::AddEntries(const CStringArray& sEntries)
\r
81 INT_PTR ret = m_sEntries.Append(sEntries);
\r
83 SetWindowText(m_sEntries[0]);
\r
88 BEGIN_MESSAGE_MAP(CMenuButton, _Inherited)
\r
89 ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
\r
92 ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
\r
97 int CMenuButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
\r
99 if (_Inherited::OnCreate(lpCreateStruct) == -1)
\r
105 bool CMenuButton::ShowMenu()
\r
108 GetWindowRect(rDraw);
\r
110 if (popup.CreatePopupMenu())
\r
112 for (int index=0; index<m_sEntries.GetCount(); ++index)
\r
114 popup.AppendMenu(MF_ENABLED | MF_STRING, index+1, m_sEntries.GetAt(index));
\r
116 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rDraw.left, rDraw.bottom-1, this);
\r
119 if (cmd > m_sEntries.GetCount())
\r
121 m_currentEntry = cmd-1;
\r
122 SetWindowText(m_sEntries.GetAt(cmd-1));
\r
128 BOOL CMenuButton::OnClicked()
\r
131 GetWindowRect(rDraw);
\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
139 // check if the user clicked on the arrow or the button part
\r
142 CPoint temp(m_SeparatorX, 0);
\r
143 ClientToScreen(&temp);
\r
144 rArrow.left = temp.x;
\r
145 if (rArrow.PtInRect(point))
\r
152 // click on button or user pressed enter/space.
\r
153 // let the parent handle the message the usual way
\r
157 void CMenuButton::OnNMThemeChanged(NMHDR * /*pNMHDR*/, LRESULT *pResult)
\r
160 m_xpButton.Open(GetSafeHwnd(), L"BUTTON");
\r
165 void CMenuButton::OnMouseMove(UINT nFlags, CPoint point)
\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
178 _Inherited::OnMouseMove(nFlags, point);
\r
181 LRESULT CMenuButton::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/)
\r
185 m_bMouseOver = FALSE;
\r
191 void CMenuButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
\r
193 ASSERT(lpDrawItemStruct);
\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
201 SendMessage(WM_ERASEBKGND, (WPARAM)dcMem.GetSafeHdc());
\r
203 if (state & ODS_FOCUS)
\r
204 state |= ODS_DEFAULT;
\r
207 m_xpButton.Open(GetSafeHwnd(), L"BUTTON");
\r
210 if ((m_xpButton.IsAppThemed())&&(m_bUseThemes))
\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
222 m_xpButton.DrawBackground(dcMem.GetSafeHdc(), BP_PUSHBUTTON, nFrameState, &rDraw, NULL);
\r
223 m_xpButton.GetBackgroundContentRect(dcMem.GetSafeHdc(), BP_PUSHBUTTON, nFrameState, &rDraw, &rDraw);
\r
227 UINT uFrameState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
\r
229 if (state & ODS_SELECTED)
\r
230 uFrameState |= DFCS_PUSHED;
\r
232 if (state & ODS_DISABLED)
\r
233 uFrameState |= DFCS_INACTIVE;
\r
235 dcMem.DrawFrameControl(&rDraw, DFC_BUTTON, uFrameState);
\r
237 if (state & ODS_SELECTED)
\r
238 rDraw.OffsetRect(1,1);
\r
245 CRect rFocus(rDraw.left, rDraw.top, rDraw.right - 1, rDraw.bottom);
\r
246 dcMem.DrawFocusRect(&rFocus);
\r
249 rDraw.DeflateRect(::GetSystemMetrics(SM_CXEDGE), ::GetSystemMetrics(SM_CYEDGE));
\r
250 rDraw.DeflateRect(0, 0, 1, 0);
\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
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
262 dcMem.DrawEdge(&rDraw, EDGE_ETCHED, BF_RIGHT);
\r
263 m_SeparatorX = rDraw.right;
\r
264 rDraw.right -= (::GetSystemMetrics(SM_CXEDGE) * 2) + 1 ;
\r
268 GetWindowText(szTemp);
\r
270 CFont *pOldFont = dcMem.SelectObject(GetFont());
\r
271 dcMem.SetTextColor((state & ODS_DISABLED) ? ::GetSysColor(COLOR_GRAYTEXT) : ::GetSysColor(COLOR_BTNTEXT));
\r
273 dcMem.SetBkMode(TRANSPARENT);
\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
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
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
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
298 format |= GetStyle()&
\r
299 dcMem.DrawText(szTemp, rectText, format);
\r
301 dcMem.SelectObject(pOldFont);
\r
305 void CMenuButton::DrawArrow(CDC* pDC,
\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
320 CBrush* pOldBrush = pDC->SelectObject(&brsArrow);
\r
321 CPen* pOldPen = pDC->SelectObject(&penArrow);
\r
323 pDC->SetPolyFillMode(WINDING);
\r
324 pDC->Polygon(ptsArrow, 3);
\r
326 pDC->SelectObject(pOldBrush);
\r
327 pDC->SelectObject(pOldPen);
\r
330 BOOL CMenuButton::PreTranslateMessage(MSG* pMsg)
\r
332 if(pMsg->message == WM_KEYDOWN)
\r
334 switch(pMsg->wParam)
\r
338 ShowMenu(); // Activate pop-up when F4-key or down-arrow is hit
\r
341 return TRUE; // Disable up-arrow
\r
345 return _Inherited::PreTranslateMessage(pMsg);
\r
348 void CMenuButton::OnDestroy()
\r
350 m_sEntries.RemoveAll();
\r
352 _Inherited::OnDestroy();
\r