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 INT_PTR CMenuButton::AddEntry(const CString& sEntry)
\r
66 INT_PTR ret = m_sEntries.Add(sEntry);
\r
68 SetWindowText(m_sEntries[0]);
\r
73 INT_PTR CMenuButton::AddEntries(const CStringArray& sEntries)
\r
75 INT_PTR ret = m_sEntries.Append(sEntries);
\r
77 SetWindowText(m_sEntries[0]);
\r
82 BEGIN_MESSAGE_MAP(CMenuButton, _Inherited)
\r
83 ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
\r
86 ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
\r
91 int CMenuButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
\r
93 if (_Inherited::OnCreate(lpCreateStruct) == -1)
\r
99 bool CMenuButton::ShowMenu()
\r
102 GetWindowRect(rDraw);
\r
104 if (popup.CreatePopupMenu())
\r
106 for (int index=0; index<m_sEntries.GetCount(); ++index)
\r
108 popup.AppendMenu(MF_ENABLED | MF_STRING, index+1, m_sEntries.GetAt(index));
\r
110 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rDraw.left, rDraw.bottom-1, this);
\r
113 if (cmd > m_sEntries.GetCount())
\r
115 m_currentEntry = cmd-1;
\r
116 SetWindowText(m_sEntries.GetAt(cmd-1));
\r
122 BOOL CMenuButton::OnClicked()
\r
125 GetWindowRect(rDraw);
\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
133 // check if the user clicked on the arrow or the button part
\r
136 CPoint temp(m_SeparatorX, 0);
\r
137 ClientToScreen(&temp);
\r
138 rArrow.left = temp.x;
\r
139 if (rArrow.PtInRect(point))
\r
146 // click on button or user pressed enter/space.
\r
147 // let the parent handle the message the usual way
\r
151 void CMenuButton::OnNMThemeChanged(NMHDR * /*pNMHDR*/, LRESULT *pResult)
\r
154 m_xpButton.Open(GetSafeHwnd(), L"BUTTON");
\r
159 void CMenuButton::OnMouseMove(UINT nFlags, CPoint point)
\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
172 _Inherited::OnMouseMove(nFlags, point);
\r
175 LRESULT CMenuButton::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/)
\r
179 m_bMouseOver = FALSE;
\r
185 void CMenuButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
\r
187 ASSERT(lpDrawItemStruct);
\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
195 SendMessage(WM_ERASEBKGND, (WPARAM)dcMem.GetSafeHdc());
\r
197 if (state & ODS_FOCUS)
\r
198 state |= ODS_DEFAULT;
\r
201 m_xpButton.Open(GetSafeHwnd(), L"BUTTON");
\r
204 if ((m_xpButton.IsAppThemed())&&(m_bUseThemes))
\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
216 m_xpButton.DrawBackground(dcMem.GetSafeHdc(), BP_PUSHBUTTON, nFrameState, &rDraw, NULL);
\r
217 m_xpButton.GetBackgroundContentRect(dcMem.GetSafeHdc(), BP_PUSHBUTTON, nFrameState, &rDraw, &rDraw);
\r
221 UINT uFrameState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
\r
223 if (state & ODS_SELECTED)
\r
224 uFrameState |= DFCS_PUSHED;
\r
226 if (state & ODS_DISABLED)
\r
227 uFrameState |= DFCS_INACTIVE;
\r
229 dcMem.DrawFrameControl(&rDraw, DFC_BUTTON, uFrameState);
\r
231 if (state & ODS_SELECTED)
\r
232 rDraw.OffsetRect(1,1);
\r
239 CRect rFocus(rDraw.left, rDraw.top, rDraw.right - 1, rDraw.bottom);
\r
240 dcMem.DrawFocusRect(&rFocus);
\r
243 rDraw.DeflateRect(::GetSystemMetrics(SM_CXEDGE), ::GetSystemMetrics(SM_CYEDGE));
\r
244 rDraw.DeflateRect(0, 0, 1, 0);
\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
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
256 dcMem.DrawEdge(&rDraw, EDGE_ETCHED, BF_RIGHT);
\r
257 m_SeparatorX = rDraw.right;
\r
258 rDraw.right -= (::GetSystemMetrics(SM_CXEDGE) * 2) + 1 ;
\r
262 GetWindowText(szTemp);
\r
264 CFont *pOldFont = dcMem.SelectObject(GetFont());
\r
265 dcMem.SetTextColor((state & ODS_DISABLED) ? ::GetSysColor(COLOR_GRAYTEXT) : ::GetSysColor(COLOR_BTNTEXT));
\r
267 dcMem.SetBkMode(TRANSPARENT);
\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
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
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
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
292 format |= GetStyle()&
\r
293 dcMem.DrawText(szTemp, rectText, format);
\r
295 dcMem.SelectObject(pOldFont);
\r
299 void CMenuButton::DrawArrow(CDC* pDC,
\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
314 CBrush* pOldBrush = pDC->SelectObject(&brsArrow);
\r
315 CPen* pOldPen = pDC->SelectObject(&penArrow);
\r
317 pDC->SetPolyFillMode(WINDING);
\r
318 pDC->Polygon(ptsArrow, 3);
\r
320 pDC->SelectObject(pOldBrush);
\r
321 pDC->SelectObject(pOldPen);
\r
324 BOOL CMenuButton::PreTranslateMessage(MSG* pMsg)
\r
326 if(pMsg->message == WM_KEYDOWN)
\r
328 switch(pMsg->wParam)
\r
332 ShowMenu(); // Activate pop-up when F4-key or down-arrow is hit
\r
335 return TRUE; // Disable up-arrow
\r
339 return _Inherited::PreTranslateMessage(pMsg);
\r
342 void CMenuButton::OnDestroy()
\r
344 m_sEntries.RemoveAll();
\r
346 _Inherited::OnDestroy();
\r