Correctly deallocate buffer
[TortoiseGit.git] / src / Utils / MiscUI / SplitterControl.cpp
blob0c61c16823cc48106f1f7a4103a4d5321646c4ae
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2006,2010 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include ".\splittercontrol.h"
22 #ifdef _DEBUG
23 #define new DEBUG_NEW
24 #undef THIS_FILE
25 static char THIS_FILE[] = __FILE__;
26 #endif
28 /////////////////////////////////////////////////////////////////////////////
29 // CSplitterControl
31 // hCursor1 is for vertical one
32 // and hCursor2 is for horizontal one
33 static HCURSOR SplitterControl_hCursor1 = NULL;
34 static HCURSOR SplitterControl_hCursor2 = NULL;
36 CSplitterControl::CSplitterControl()
37 : m_bIsPressed(FALSE)
38 , m_nMin(-1)
39 , m_nMax(-1)
40 , m_bMouseOverControl(false)
41 , m_nType(0)
42 , m_nX(0)
43 , m_nY(0)
44 , m_nSavePos(0)
48 CSplitterControl::~CSplitterControl()
53 BEGIN_MESSAGE_MAP(CSplitterControl, CStatic)
54 ON_WM_PAINT()
55 ON_WM_MOUSEMOVE()
56 ON_WM_SETCURSOR()
57 ON_WM_LBUTTONDOWN()
58 ON_WM_LBUTTONUP()
59 ON_WM_ERASEBKGND()
60 ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
61 END_MESSAGE_MAP()
63 /////////////////////////////////////////////////////////////////////////////
64 // CSplitterControl message handlers
67 // Set style for splitter control
68 // nStyle = SPS_VERTICAL or SPS_HORIZONTAL
69 int CSplitterControl::SetSplitterStyle(int nStyle)
71 int m_nOldStyle = m_nType;
72 m_nType = nStyle;
73 return m_nOldStyle;
75 int CSplitterControl::GetSplitterStyle()
77 return m_nType;
80 void CSplitterControl::OnPaint()
82 CPaintDC dc(this); // device context for painting
83 CRect rcClient;
84 GetClientRect(rcClient);
85 if (m_bMouseOverControl)
87 CPen pen, *pOP;
89 rcClient.DeflateRect(1,1,1,1);
91 pen.CreatePen(0, 1, GetSysColor(COLOR_3DSHADOW));
92 pOP = dc.SelectObject(&pen);
94 dc.MoveTo(rcClient.left, rcClient.top);
95 dc.LineTo(rcClient.right, rcClient.top);
96 dc.MoveTo(rcClient.left, rcClient.bottom);
97 dc.LineTo(rcClient.right, rcClient.bottom);
99 // Restore pen
100 dc.SelectObject(pOP);
102 else
104 dc.SetBkColor(GetSysColor(COLOR_3DFACE));
105 dc.ExtTextOut(0, 0, ETO_OPAQUE, &rcClient, NULL, 0, NULL);
109 void CSplitterControl::OnMouseMove(UINT nFlags, CPoint point)
111 if (m_bIsPressed)
113 CWindowDC dc(NULL);
114 DrawLine(&dc);
116 CPoint pt = point;
117 ClientToScreen(&pt);
118 GetParent()->ScreenToClient(&pt);
120 if (pt.x < m_nMin)
121 pt.x = m_nMin;
122 if (pt.y < m_nMin)
123 pt.y = m_nMin;
125 if (pt.x > m_nMax)
126 pt.x = m_nMax;
127 if (pt.y > m_nMax)
128 pt.y = m_nMax;
130 GetParent()->ClientToScreen(&pt);
131 m_nX = pt.x;
132 m_nY = pt.y;
133 DrawLine(&dc);
135 if (!m_bMouseOverControl)
137 TRACKMOUSEEVENT Tme;
138 Tme.cbSize = sizeof(TRACKMOUSEEVENT);
139 Tme.dwFlags = TME_LEAVE;
140 Tme.hwndTrack = m_hWnd;
141 TrackMouseEvent(&Tme);
143 m_bMouseOverControl = true;
144 Invalidate();
146 CStatic::OnMouseMove(nFlags, point);
149 LRESULT CSplitterControl::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/)
151 m_bMouseOverControl = false;
152 Invalidate();
153 return 0;
156 BOOL CSplitterControl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
158 if (nHitTest == HTCLIENT)
160 (m_nType == SPS_VERTICAL)?(::SetCursor(SplitterControl_hCursor1))
161 :(::SetCursor(SplitterControl_hCursor2));
162 return 0;
164 else
165 return CStatic::OnSetCursor(pWnd, nHitTest, message);
168 void CSplitterControl::OnLButtonDown(UINT nFlags, CPoint point)
170 CStatic::OnLButtonDown(nFlags, point);
172 m_bIsPressed = TRUE;
173 SetCapture();
174 CRect rcWnd;
175 GetWindowRect(rcWnd);
177 if (m_nType == SPS_VERTICAL)
178 m_nX = rcWnd.left + rcWnd.Width() / 2;
180 else
181 m_nY = rcWnd.top + rcWnd.Height() / 2;
183 if (m_nType == SPS_VERTICAL)
184 m_nSavePos = m_nX;
185 else
186 m_nSavePos = m_nY;
188 CWindowDC dc(NULL);
189 DrawLine(&dc);
192 void CSplitterControl::OnLButtonUp(UINT nFlags, CPoint point)
194 if (m_bIsPressed)
196 ClientToScreen(&point);
197 CWindowDC dc(NULL);
199 DrawLine(&dc);
200 CPoint pt(m_nX, m_nY);
201 m_bIsPressed = FALSE;
202 CWnd *pOwner = GetOwner();
203 if (pOwner && IsWindow(pOwner->m_hWnd))
205 CRect rc;
206 int delta;
207 pOwner->GetClientRect(rc);
208 pOwner->ScreenToClient(&pt);
209 MoveWindowTo(pt);
211 if (m_nType == SPS_VERTICAL)
212 delta = m_nX - m_nSavePos;
213 else
214 delta = m_nY - m_nSavePos;
217 SPC_NMHDR nmsp;
219 nmsp.hdr.hwndFrom = m_hWnd;
220 nmsp.hdr.idFrom = GetDlgCtrlID();
221 nmsp.hdr.code = SPN_SIZED;
222 nmsp.delta = delta;
224 pOwner->SendMessage(WM_NOTIFY, nmsp.hdr.idFrom, (LPARAM)&nmsp);
228 CStatic::OnLButtonUp(nFlags, point);
229 ReleaseCapture();
232 void CSplitterControl::DrawLine(CDC* pDC)
234 int nRop = pDC->SetROP2(R2_NOTXORPEN);
236 CRect rcWnd;
237 int d = 1;
238 GetWindowRect(rcWnd);
239 CPen pen;
240 pen.CreatePen(0, 1, ::GetSysColor(COLOR_GRAYTEXT));
241 CPen *pOP = pDC->SelectObject(&pen);
243 if (m_nType == SPS_VERTICAL)
245 pDC->MoveTo(m_nX - d, rcWnd.top);
246 pDC->LineTo(m_nX - d, rcWnd.bottom);
248 pDC->MoveTo(m_nX + d, rcWnd.top);
249 pDC->LineTo(m_nX + d, rcWnd.bottom);
251 else // m_nType == SPS_HORIZONTAL
253 pDC->MoveTo(rcWnd.left, m_nY - d);
254 pDC->LineTo(rcWnd.right, m_nY - d);
256 pDC->MoveTo(rcWnd.left, m_nY + d);
257 pDC->LineTo(rcWnd.right, m_nY + d);
259 pDC->SetROP2(nRop);
260 pDC->SelectObject(pOP);
263 void CSplitterControl::MoveWindowTo(CPoint pt)
265 CRect rc;
266 GetWindowRect(rc);
267 CWnd* pParent;
268 pParent = GetParent();
269 if (!pParent || !::IsWindow(pParent->m_hWnd))
270 return;
272 pParent->ScreenToClient(rc);
273 if (m_nType == SPS_VERTICAL)
275 int nMidX = (rc.left + rc.right) / 2;
276 int dx = pt.x - nMidX;
277 rc.OffsetRect(dx, 0);
279 else
281 int nMidY = (rc.top + rc.bottom) / 2;
282 int dy = pt.y - nMidY;
283 rc.OffsetRect(0, dy);
285 MoveWindow(rc);
288 void CSplitterControl::ChangeWidth(CWnd *pWnd, int dx, DWORD dwFlag)
290 CWnd* pParent = pWnd->GetParent();
291 if (pParent && ::IsWindow(pParent->m_hWnd))
293 CRect rcWnd;
294 pWnd->GetWindowRect(rcWnd);
295 pParent->ScreenToClient(rcWnd);
296 if (dwFlag == CW_LEFTALIGN)
297 rcWnd.right += dx;
298 else if (dwFlag == CW_RIGHTALIGN)
299 rcWnd.left -= dx;
300 pWnd->MoveWindow(rcWnd);
304 void CSplitterControl::ChangeHeight(CWnd *pWnd, int dy, DWORD dwFlag)
306 CWnd* pParent = pWnd->GetParent();
307 if (pParent && ::IsWindow(pParent->m_hWnd))
309 CRect rcWnd;
310 pWnd->GetWindowRect(rcWnd);
311 pParent->ScreenToClient(rcWnd);
312 if (dwFlag == CW_TOPALIGN)
313 rcWnd.bottom += dy;
314 else if (dwFlag == CW_BOTTOMALIGN)
315 rcWnd.top -= dy;
316 pWnd->MoveWindow(rcWnd);
320 void CSplitterControl::ChangePos(CWnd* pWnd, int dx, int dy)
322 CWnd* pParent = pWnd->GetParent();
323 if (pParent && ::IsWindow(pParent->m_hWnd))
325 CRect rcWnd;
326 pWnd->GetWindowRect(rcWnd);
327 pParent->ScreenToClient(rcWnd);
328 rcWnd.OffsetRect(-dx, dy);
330 pWnd->MoveWindow(rcWnd);
334 void CSplitterControl::SetRange(int nMin, int nMax)
336 m_nMin = nMin;
337 m_nMax = nMax;
340 // Set splitter range from (nRoot - nSubtraction) to (nRoot + nAddition)
341 // If (nRoot < 0)
342 // nRoot = <current position of the splitter>
343 void CSplitterControl::SetRange(int nSubtraction, int nAddition, int nRoot)
345 if (nRoot < 0)
347 CRect rcWnd;
348 GetWindowRect(rcWnd);
349 if (m_nType == SPS_VERTICAL)
350 nRoot = rcWnd.left + rcWnd.Width() / 2;
351 else // if m_nType == SPS_HORIZONTAL
352 nRoot = rcWnd.top + rcWnd.Height() / 2;
354 m_nMin = nRoot - nSubtraction;
355 m_nMax = nRoot + nAddition;
357 void CSplitterControl::PreSubclassWindow()
359 // Enable notifications - CStatic has this disabled by default
360 DWORD dwStyle = GetStyle();
361 ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY);
363 CRect rc;
364 GetClientRect(rc);
366 // Determine default type base on it's size.
367 m_nType = (rc.Width() < rc.Height())?SPS_VERTICAL:SPS_HORIZONTAL;
369 if (!SplitterControl_hCursor1)
371 SplitterControl_hCursor1 = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE);
372 SplitterControl_hCursor2 = AfxGetApp()->LoadStandardCursor(IDC_SIZENS);
375 // force the splitter not to be splitted.
376 SetRange(0, 0, -1);
379 CStatic::PreSubclassWindow();
382 BOOL CSplitterControl::OnEraseBkgnd(CDC* /*pDC*/)
384 return TRUE;