1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
24 #include <sal/types.h>
25 #include <sal/log.hxx>
29 #include <syswinwrapper.hxx>
31 // windowserrorstring.hxx includes postwin.h, which #undef OPAQUE, so "#redef" it
32 #include <comphelper/windowserrorstring.hxx>
34 #error OPAQUE should not be defined!?
38 static HCURSOR afxCursors
[10] = { nullptr, };
39 static HBRUSH afxHalftoneBrush
= nullptr;
43 // the struct below is used to determine the qualities of a particular handle
46 size_t nOffsetX
; // offset within RECT for X coordinate
47 size_t nOffsetY
; // offset within RECT for Y coordinate
48 int nCenterX
; // adjust X by Width()/2 * this number
49 int nCenterY
; // adjust Y by Height()/2 * this number
50 int nHandleX
; // adjust X by handle size * this number
51 int nHandleY
; // adjust Y by handle size * this number
52 int nInvertX
; // handle converts to this when X inverted
53 int nInvertY
; // handle converts to this when Y inverted
58 // this array describes all 8 handles (clock-wise)
59 const AFX_HANDLEINFO afxHandleInfo
[] =
61 // corner handles (top-left, top-right, bottom-right, bottom-left
62 { offsetof(RECT
, left
), offsetof(RECT
, top
), 0, 0, 0, 0, 1, 3 },
63 { offsetof(RECT
, right
), offsetof(RECT
, top
), 0, 0, -1, 0, 0, 2 },
64 { offsetof(RECT
, right
), offsetof(RECT
, bottom
), 0, 0, -1, -1, 3, 1 },
65 { offsetof(RECT
, left
), offsetof(RECT
, bottom
), 0, 0, 0, -1, 2, 0 },
67 // side handles (top, right, bottom, left)
68 { offsetof(RECT
, left
), offsetof(RECT
, top
), 1, 0, 0, 0, 4, 6 },
69 { offsetof(RECT
, right
), offsetof(RECT
, top
), 0, 1, -1, 0, 7, 5 },
70 { offsetof(RECT
, left
), offsetof(RECT
, bottom
), 1, 0, 0, -1, 6, 4 },
71 { offsetof(RECT
, left
), offsetof(RECT
, top
), 0, 1, 0, 0, 5, 7 }
76 // the struct below gives us information on the layout of a RECT struct and
77 // the relationship between its members
80 size_t nOffsetAcross
; // offset of opposite point (ie. left->right)
81 int nSignAcross
; // sign relative to that point (ie. add/subtract)
86 // this array is indexed by the offset of the RECT member / sizeof(int)
87 const AFX_RECTINFO afxRectInfo
[] =
89 { offsetof(RECT
, right
), +1 },
90 { offsetof(RECT
, bottom
), +1 },
91 { offsetof(RECT
, left
), -1 },
92 { offsetof(RECT
, top
), -1 },
96 static HBRUSH
HalftoneBrush()
98 if (afxHalftoneBrush
== nullptr)
101 for (int i
= 0; i
< 8; i
++)
102 grayPattern
[i
] = static_cast<WORD
>(0x5555 << (i
& 1));
103 HBITMAP grayBitmap
= CreateBitmap(8, 8, 1, 1, &grayPattern
);
104 if (grayBitmap
!= nullptr)
106 afxHalftoneBrush
= CreatePatternBrush(grayBitmap
);
107 DeleteObject(grayBitmap
);
110 return afxHalftoneBrush
;
114 static void DrawDragRect(
115 HDC hDC
,LPRECT lpRect
,SIZE size
,
116 LPRECT lpRectLast
,SIZE sizeLast
,
117 HBRUSH hBrush
= nullptr,HBRUSH hBrushLast
= nullptr)
119 // first, determine the update region and select it
121 HRGN rgnOutside
,rgnInside
;
122 rgnOutside
= CreateRectRgnIndirect(lpRect
);
124 InflateRect(&rect
,-size
.cx
, -size
.cy
);
125 IntersectRect(&rect
,&rect
,lpRect
);
126 rgnInside
= CreateRectRgnIndirect(&rect
);
127 rgnNew
= CreateRectRgn(0, 0, 0, 0);
128 CombineRgn(rgnNew
,rgnOutside
,rgnInside
,RGN_XOR
);
130 HBRUSH hBrushOld
= nullptr;
131 if (hBrush
== nullptr)
132 hBrush
= HalftoneBrush();
133 if (hBrushLast
== nullptr)
136 HRGN
rgnLast(nullptr);
137 HRGN
rgnUpdate(nullptr);
138 if (lpRectLast
!= nullptr)
140 // find difference between new region and old region
141 rgnLast
= CreateRectRgn(0, 0, 0, 0);
149 InflateRect(&rect
,-sizeLast
.cx
, -sizeLast
.cy
);
150 IntersectRect(&rect
,&rect
, lpRectLast
);
151 SetRectRgn(rgnInside
,rect
.left
,rect
.top
,rect
.right
,rect
.bottom
);
152 CombineRgn(rgnLast
,rgnOutside
,rgnInside
, RGN_XOR
);
154 // // only diff them if brushes are the same
155 if (hBrush
== hBrushLast
)
157 rgnUpdate
= CreateRectRgn(0, 0, 0, 0);
158 CombineRgn(rgnUpdate
,rgnLast
,rgnNew
, RGN_XOR
);
161 if (hBrush
!= hBrushLast
&& lpRectLast
!= nullptr)
163 // brushes are different -- erase old region first
164 SelectClipRgn(hDC
,rgnLast
);
165 GetClipBox(hDC
,&rect
);
166 hBrushOld
= static_cast<HBRUSH
>(SelectObject(hDC
,static_cast<HGDIOBJ
>(hBrushLast
)));
167 PatBlt(hDC
,rect
.left
,rect
.top
,(rect
.right
-rect
.left
),(rect
.bottom
-rect
.top
),PATINVERT
);
169 SelectObject(hDC
,static_cast<HGDIOBJ
>(hBrushOld
));
173 // draw into the update/new region
174 SelectClipRgn(hDC
,rgnUpdate
);
176 GetClipBox(hDC
,&rect
);
177 hBrushOld
= static_cast<HBRUSH
>(SelectObject(hDC
, static_cast<HGDIOBJ
>(hBrush
)));
178 PatBlt(hDC
,rect
.left
, rect
.top
,(rect
.right
-rect
.left
),(rect
.bottom
-rect
.top
), PATINVERT
);
181 if (hBrushOld
!= nullptr)
182 SelectObject(hDC
, static_cast<HGDIOBJ
>(hBrushOld
));
183 SelectClipRgn(hDC
,nullptr);
187 void winwrap::TransformRect(LPRECT rect
,HWND pWnd
,HWND pWndClipTo
)
190 pt
.x
= rect
->left
;pt
.y
= rect
->top
;
191 ClientToScreen(pWnd
,&pt
);
192 ScreenToClient(pWndClipTo
,&pt
);
193 rect
->left
= pt
.x
; rect
->top
= pt
.y
;
195 pt
.x
= rect
->right
;pt
.y
= rect
->bottom
;
196 ClientToScreen(pWnd
,&pt
);
197 ScreenToClient(pWndClipTo
,&pt
);
198 rect
->right
= pt
.x
; rect
->bottom
= pt
.y
;
202 static void NormalizeRect(LPRECT rp
)
204 if(rp
->left
> rp
->right
) {
206 rp
->left
= rp
->right
;
210 if(rp
->top
> rp
->bottom
) {
212 rp
->top
= rp
->bottom
;
218 using namespace winwrap
;
226 Tracker::Tracker(LPCRECT lpSrcRect
, UINT nStyle
)
229 CopyRect(&m_rect
,lpSrcRect
);
233 static HBRUSH afxHatchBrush
= nullptr;
234 static HPEN afxBlackDottedPen
= nullptr;
235 static int afxHandleSize
= 0;
238 void Tracker::Construct()
240 static bool bInitialized
= false;
243 if (afxHatchBrush
== nullptr)
245 // create the hatch pattern + bitmap
246 WORD hatchPattern
[8];
247 WORD wPattern
= 0x1111;
248 for (int i
= 0; i
< 4; i
++)
250 hatchPattern
[i
] = wPattern
;
251 hatchPattern
[i
+4] = wPattern
;
254 HBITMAP hatchBitmap
= CreateBitmap(8, 8, 1, 1,&hatchPattern
);
256 // create black hatched brush
257 afxHatchBrush
= CreatePatternBrush(hatchBitmap
);
258 DeleteObject(hatchBitmap
);
261 if (afxBlackDottedPen
== nullptr)
263 // create black dotted pen
264 afxBlackDottedPen
= CreatePen(PS_DOT
, 0, RGB(0, 0, 0));
267 // get default handle size from Windows profile setting
268 static const WCHAR szWindows
[] = L
"windows";
269 static const WCHAR szInplaceBorderWidth
[] = L
"oleinplaceborderwidth";
270 afxHandleSize
= GetProfileIntW(szWindows
, szInplaceBorderWidth
, 4);
273 afxCursors
[0] = afxCursors
[2] = LoadCursor(nullptr,IDC_SIZENWSE
);
274 afxCursors
[4] = afxCursors
[6] = LoadCursor(nullptr,IDC_SIZENS
);
275 afxCursors
[1] = afxCursors
[3] = LoadCursor(nullptr,IDC_SIZENESW
);
276 afxCursors
[5] = afxCursors
[7] = LoadCursor(nullptr,IDC_SIZEWE
);
277 afxCursors
[8] = LoadCursor(nullptr,IDC_SIZEALL
);
281 m_nHandleSize
= afxHandleSize
;
282 m_sizeMin
.cy
= m_sizeMin
.cx
= m_nHandleSize
*2;
284 SetRectEmpty(&m_rectLast
);
285 m_sizeLast
.cx
= m_sizeLast
.cy
= 0;
287 m_bFinalErase
= FALSE
;
295 int Tracker::HitTest(POINT point
) const
297 TrackerHit hitResult
= hitNothing
;
300 GetTrueRect(&rectTrue
);
301 NormalizeRect(&rectTrue
);
302 if (PtInRect(&rectTrue
,point
))
304 if ((m_nStyle
& (resizeInside
|resizeOutside
)) != 0)
305 hitResult
= static_cast<TrackerHit
>(HitTestHandles(point
));
307 hitResult
= hitMiddle
;
313 BOOL
Tracker::SetCursor(HWND pWnd
, UINT nHitTest
) const
315 // trackers should only be in client area
316 if (nHitTest
!= HTCLIENT
)
319 // convert cursor position to client co-ordinates
321 GetCursorPos(&point
);
322 ScreenToClient(pWnd
,&point
);
324 // do hittest and normalize hit
325 int nHandle
= HitTestHandles(point
);
329 // need to normalize the hittest such that we get proper cursors
330 nHandle
= NormalizeHit(nHandle
);
332 // handle special case of hitting area between handles
333 // (logically the same -- handled as a move -- but different cursor)
334 if (nHandle
== hitMiddle
&& !PtInRect(&m_rect
,point
))
336 // only for trackers with hatchedBorder (ie. in-place resizing)
337 if (m_nStyle
& hatchedBorder
)
338 nHandle
= TrackerHit(9);
341 ::SetCursor(afxCursors
[nHandle
]);
346 BOOL
Tracker::Track(HWND hWnd
,POINT point
,BOOL bAllowInvert
,
349 // perform hit testing on the handles
350 int nHandle
= HitTestHandles(point
);
353 // didn't hit a handle, so just return FALSE
357 // otherwise, call helper function to do the tracking
358 m_bAllowInvert
= bAllowInvert
;
359 SetCursor(hWnd
,nHandle
);
360 return TrackHandle(nHandle
, hWnd
, point
, hWndClipTo
);
364 BOOL
Tracker::TrackHandle(int nHandle
,HWND hWnd
,POINT point
,HWND hWndClipTo
)
366 // don't handle if capture already set
367 if (GetCapture() != nullptr)
370 // save original width & height in pixels
371 int nWidth
= m_rect
.right
- m_rect
.left
;
372 int nHeight
= m_rect
.bottom
- m_rect
.top
;
374 // set capture to the window which received this message
377 if (hWndClipTo
!= nullptr)
378 UpdateWindow(hWndClipTo
);
379 RECT rectSave
= m_rect
;
381 // find out what x/y coords we are supposed to modify
384 GetModifyPointers(nHandle
, &px
, &py
, &xDiff
, &yDiff
);
385 xDiff
= point
.x
- xDiff
;
386 yDiff
= point
.y
- yDiff
;
388 // get DC for drawing
390 if (hWndClipTo
!= nullptr)
392 // clip to arbitrary window by using adjusted Window DC
393 hDrawDC
= GetDCEx(hWndClipTo
,nullptr, DCX_CACHE
);
397 // otherwise, just use normal DC
398 hDrawDC
= GetDC(hWnd
);
404 // get messages until capture lost or cancelled/accepted
408 int const bRet
= GetMessageW(&msg
, nullptr, 0, 0);
409 SAL_WARN_IF(-1 == bRet
, "embedserv", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
410 if (-1 == bRet
|| 0 == bRet
)
413 if (GetCapture() != hWnd
)
418 // handle movement/accept messages
422 // handle resize cases (and part of move)
424 *px
= static_cast<int>(static_cast<short>(LOWORD(msg
.lParam
))) - xDiff
;
426 *py
= static_cast<int>(static_cast<short>(HIWORD(msg
.lParam
))) - yDiff
;
429 if (nHandle
== hitMiddle
)
431 m_rect
.right
= m_rect
.left
+ nWidth
;
432 m_rect
.bottom
= m_rect
.top
+ nHeight
;
434 // allow caller to adjust the rectangle if necessary
435 AdjustRect(nHandle
,&m_rect
);
437 // only redraw and callback if the rect actually changed!
438 m_bFinalErase
= (msg
.message
== WM_LBUTTONUP
);
439 if (!EqualRect(&rectOld
,&m_rect
) || m_bFinalErase
)
444 DrawTrackerRect(&rectOld
,hWndClipTo
,hDrawDC
,hWnd
);
446 OnChangedRect(rectOld
);
447 if (msg
.message
!= WM_LBUTTONUP
)
453 if (!EqualRect(&rectOld
,&m_rect
))
456 DrawTrackerRect(&m_rect
,hWndClipTo
,hDrawDC
,hWnd
);
460 // handle cancel messages
462 if (msg
.wParam
!= VK_ESCAPE
)
468 m_bErase
= m_bFinalErase
= TRUE
;
469 DrawTrackerRect(&m_rect
, hWndClipTo
, hDrawDC
, hWnd
);
474 // just dispatch rest of the messages
476 DispatchMessageW(&msg
);
482 if (hWndClipTo
!= nullptr)
483 ReleaseDC(hWndClipTo
,hDrawDC
);
485 ReleaseDC(hWnd
,hDrawDC
);
488 // restore rect in case bMoved is still FALSE
491 m_bFinalErase
= FALSE
;
494 // return TRUE only if rect has changed
495 return !EqualRect(&rectSave
,&m_rect
);
499 void Tracker::OnChangedRect(const RECT
& /*rectOld*/)
504 void Tracker::AdjustRect(int nHandle
, LPRECT
)
506 if(nHandle
== hitMiddle
)
509 // convert the handle into locations within m_rect
511 GetModifyPointers(nHandle
, &px
, &py
, nullptr, nullptr);
513 // enforce minimum width
514 int nNewWidth
= m_rect
.right
- m_rect
.left
;
515 int nAbsWidth
= m_bAllowInvert
? abs(nNewWidth
) : nNewWidth
;
516 if (px
!= nullptr && nAbsWidth
< m_sizeMin
.cx
)
518 nNewWidth
= nAbsWidth
!= 0 ? nNewWidth
/ nAbsWidth
: 1;
519 const AFX_RECTINFO
* pRectInfo
=
520 &afxRectInfo
[px
- reinterpret_cast<int*>(&m_rect
)];
521 *px
= *reinterpret_cast<int*>(reinterpret_cast<BYTE
*>(&m_rect
) + pRectInfo
->nOffsetAcross
) +
522 nNewWidth
* m_sizeMin
.cx
* -pRectInfo
->nSignAcross
;
525 // enforce minimum height
526 int nNewHeight
= m_rect
.bottom
- m_rect
.top
;
527 int nAbsHeight
= m_bAllowInvert
? abs(nNewHeight
) : nNewHeight
;
528 if (py
!= nullptr && nAbsHeight
< m_sizeMin
.cy
)
530 nNewHeight
= nAbsHeight
!= 0 ? nNewHeight
/ nAbsHeight
: 1;
531 const AFX_RECTINFO
* pRectInfo
=
532 &afxRectInfo
[py
- reinterpret_cast<int*>(&m_rect
)];
533 *py
= *reinterpret_cast<int*>(reinterpret_cast<BYTE
*>(&m_rect
) + pRectInfo
->nOffsetAcross
) +
534 nNewHeight
* m_sizeMin
.cy
* -pRectInfo
->nSignAcross
;
539 void Tracker::DrawTrackerRect(
540 LPRECT lpRect
,HWND pWndClipTo
,HDC pDC
,HWND pWnd
)
542 // first, normalize the rectangle for drawing
544 NormalizeRect(&rect
);
546 // convert to client coordinates
547 if (pWndClipTo
!= nullptr)
548 TransformRect(&rect
,pWnd
,pWndClipTo
);
551 size
.cx
= 0; size
.cy
= 0;
554 // otherwise, size depends on the style
555 if (m_nStyle
& hatchedBorder
)
557 size
.cx
= size
.cy
= std::max(1,GetHandleSize(&rect
)-1);
558 InflateRect(&rect
,size
.cx
,size
.cy
);
562 size
.cx
= 1; // CX_BORDER;
563 size
.cy
= 1; // CY_BORDER;
568 if (m_bFinalErase
|| !m_bErase
)
569 DrawDragRect(pDC
,&rect
,size
,&m_rectLast
,m_sizeLast
);
571 // remember last rectangles
577 void Tracker::Draw(HDC hDC
) const
579 // set initial DC state
580 SetMapMode(hDC
,MM_TEXT
);
581 SetViewportOrgEx(hDC
,0, 0,nullptr);
582 SetWindowOrgEx(hDC
,0, 0,nullptr);
584 // get normalized rectangle
586 NormalizeRect(&rect
);
588 HPEN pOldPen
= nullptr;
589 HBRUSH pOldBrush
= nullptr;
594 if ((m_nStyle
& (dottedLine
|solidLine
)) != 0)
596 if (m_nStyle
& dottedLine
)
597 pOldPen
= static_cast<HPEN
>(SelectObject(hDC
,afxBlackDottedPen
));
599 pOldPen
= static_cast<HPEN
>(SelectObject(hDC
,reinterpret_cast<HGDIOBJ
>(BLACK_PEN
)));
600 pOldBrush
= static_cast<HBRUSH
>(SelectObject(hDC
,reinterpret_cast<HGDIOBJ
>(NULL_BRUSH
)));
601 nOldROP
= SetROP2(hDC
,R2_COPYPEN
);
602 InflateRect(&rect
,+1, +1); // borders are one pixel outside
603 Rectangle(hDC
,rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
604 SetROP2(hDC
,nOldROP
);
607 // if hatchBrush is going to be used, need to unrealize it
608 if ((m_nStyle
& (hatchInside
|hatchedBorder
)) != 0)
609 UnrealizeObject(static_cast<HGDIOBJ
>(afxHatchBrush
));
612 if ((m_nStyle
& hatchInside
) != 0)
614 pTemp
= SelectObject(hDC
,reinterpret_cast<HGDIOBJ
>(NULL_PEN
));
615 if (pOldPen
== nullptr)
616 pOldPen
= static_cast<HPEN
>(pTemp
);
617 pTemp
= SelectObject(hDC
,static_cast<HGDIOBJ
>(afxHatchBrush
));
618 if (pOldBrush
== nullptr)
619 pOldBrush
= static_cast<HBRUSH
>(pTemp
);
620 SetBkMode(hDC
,TRANSPARENT
);
621 nOldROP
= SetROP2(hDC
,R2_MASKNOTPEN
);
622 Rectangle(hDC
,rect
.left
+1, rect
.top
+1, rect
.right
, rect
.bottom
);
623 SetROP2(hDC
,nOldROP
);
626 // draw hatched border
627 if ((m_nStyle
& hatchedBorder
) != 0)
629 pTemp
= SelectObject(hDC
,static_cast<HGDIOBJ
>(afxHatchBrush
));
630 if (pOldBrush
== nullptr)
631 pOldBrush
= static_cast<HBRUSH
>(pTemp
);
632 SetBkMode(hDC
,OPAQUE
);
634 GetTrueRect(&rectTrue
);
635 PatBlt(hDC
,rectTrue
.left
, rectTrue
.top
, rectTrue
.right
-rectTrue
.left
,
636 rect
.top
-rectTrue
.top
, 0x000F0001 /* Pn */);
637 PatBlt(hDC
,rectTrue
.left
, rect
.bottom
,
638 rectTrue
.right
-rectTrue
.left
,
639 rectTrue
.bottom
-rect
.bottom
, 0x000F0001 /* Pn */);
640 PatBlt(hDC
,rectTrue
.left
, rect
.top
, rect
.left
-rectTrue
.left
,
641 rect
.bottom
-rect
.top
, 0x000F0001 /* Pn */);
642 PatBlt(hDC
,rect
.right
, rect
.top
, rectTrue
.right
-rect
.right
,
643 rect
.bottom
-rect
.top
, 0x000F0001 /* Pn */);
646 // draw resize handles
647 if ((m_nStyle
& (resizeInside
|resizeOutside
)) != 0)
649 UINT mask
= GetHandleMask();
650 HBRUSH hbrush
= CreateSolidBrush(RGB(0,0,0));
651 for (int i
= 0; i
< 8; ++i
)
655 GetHandleRect(static_cast<TrackerHit
>(i
), &rect
);
656 // FillSolidRect(hDC,rect, RGB(0, 0, 0));
657 FillRect(hDC
,&rect
,hbrush
);
660 DeleteObject(hbrush
);
664 if (pOldPen
!= nullptr)
665 SelectObject(hDC
,pOldPen
);
666 if (pOldBrush
!= nullptr)
667 SelectObject(hDC
,pOldBrush
);
672 void Tracker::GetHandleRect(int nHandle
,RECT
* pHandleRect
) const
674 // get normalized rectangle of the tracker
676 NormalizeRect(&rectT
);
677 if ((m_nStyle
& (solidLine
|dottedLine
)) != 0)
678 InflateRect(&rectT
,+1, +1);
680 // since the rectangle itself was normalized, we also have to invert the
682 nHandle
= NormalizeHit(nHandle
);
684 // handle case of resize handles outside the tracker
685 int size
= GetHandleSize();
686 if (m_nStyle
& resizeOutside
)
687 InflateRect(&rectT
,size
-1, size
-1);
689 // calculate position of the resize handle
690 int nWidth
= rectT
.right
- rectT
.left
;
691 int nHeight
= rectT
.bottom
- rectT
.top
;
693 const AFX_HANDLEINFO
* pHandleInfo
= &afxHandleInfo
[nHandle
];
694 rect
.left
= *reinterpret_cast<int*>(reinterpret_cast<BYTE
*>(&rectT
) + pHandleInfo
->nOffsetX
);
695 rect
.top
= *reinterpret_cast<int*>(reinterpret_cast<BYTE
*>(&rectT
) + pHandleInfo
->nOffsetY
);
696 rect
.left
+= size
* pHandleInfo
->nHandleX
;
697 rect
.top
+= size
* pHandleInfo
->nHandleY
;
698 rect
.left
+= pHandleInfo
->nCenterX
* (nWidth
- size
) / 2;
699 rect
.top
+= pHandleInfo
->nCenterY
* (nHeight
- size
) / 2;
700 rect
.right
= rect
.left
+ size
;
701 rect
.bottom
= rect
.top
+ size
;
707 int Tracker::GetHandleSize(LPRECT lpRect
) const
709 LPCRECT rect
= lpRect
== nullptr ? &m_rect
: lpRect
;
711 int size
= m_nHandleSize
;
712 if (!(m_nStyle
& resizeOutside
))
714 // make sure size is small enough for the size of the rect
715 int sizeMax
= std::min(abs(rect
->right
- rect
->left
),
716 abs(rect
->bottom
- rect
->top
));
717 if (size
* 2 > sizeMax
)
724 UINT
Tracker::GetHandleMask() const
726 UINT mask
= 0x0F; // always have 4 corner handles
727 int size
= m_nHandleSize
*3;
728 if (abs(m_rect
.right
- m_rect
.left
) - size
> 4)
730 if (abs(m_rect
.bottom
- m_rect
.top
) - size
> 4)
736 void Tracker::GetTrueRect(LPRECT lpTrueRect
) const
739 NormalizeRect(&rect
);
741 if ((m_nStyle
& (resizeOutside
|hatchedBorder
)) != 0)
742 nInflateBy
+= GetHandleSize() - 1;
743 if ((m_nStyle
& (solidLine
|dottedLine
)) != 0)
745 InflateRect(&rect
,nInflateBy
, nInflateBy
);
750 int Tracker::NormalizeHit(int nHandle
) const
752 if (nHandle
== hitMiddle
|| nHandle
== hitNothing
)
754 const AFX_HANDLEINFO
* pHandleInfo
= &afxHandleInfo
[nHandle
];
755 if (m_rect
.right
- m_rect
.left
< 0)
757 nHandle
= static_cast<TrackerHit
>(pHandleInfo
->nInvertX
);
758 pHandleInfo
= &afxHandleInfo
[nHandle
];
760 if (m_rect
.bottom
- m_rect
.top
< 0)
761 nHandle
= static_cast<TrackerHit
>(pHandleInfo
->nInvertY
);
766 int Tracker::HitTestHandles(POINT point
) const
769 UINT mask
= GetHandleMask();
771 // see if hit anywhere inside the tracker
773 if (!PtInRect(&rect
,point
))
774 return hitNothing
; // totally missed
776 // see if we hit a handle
777 for (int i
= 0; i
< 8; ++i
)
781 GetHandleRect(static_cast<TrackerHit
>(i
), &rect
);
782 if (PtInRect(&rect
,point
))
783 return static_cast<TrackerHit
>(i
);
787 // last of all, check for non-hit outside of object, between resize handles
788 if ((m_nStyle
& hatchedBorder
) == 0)
791 NormalizeRect(&rect
);
792 if ((m_nStyle
& (dottedLine
|solidLine
)) != 0)
793 InflateRect(&rect
,+1, +1);
794 if (!PtInRect(&rect
,point
))
795 return hitNothing
; // must have been between resize handles
797 return hitMiddle
; // no handle hit, but hit object (or object border)
801 void Tracker::GetModifyPointers(
802 int nHandle
, int** ppx
, int** ppy
, int* px
, int* py
)
804 if (nHandle
== hitMiddle
)
805 nHandle
= hitTopLeft
; // same as hitting top-left
810 // fill in the part of the rect that this handle modifies
811 // (Note: handles that map to themselves along a given axis when that
812 // axis is inverted don't modify the value on that axis)
814 const AFX_HANDLEINFO
* pHandleInfo
= &afxHandleInfo
[nHandle
];
815 if (pHandleInfo
->nInvertX
!= nHandle
)
817 *ppx
= reinterpret_cast<int*>(reinterpret_cast<BYTE
*>(&m_rect
) + pHandleInfo
->nOffsetX
);
823 // middle handle on X axis
825 *px
= m_rect
.left
+ (m_rect
.left
-m_rect
.right
) / 2;
827 if (pHandleInfo
->nInvertY
!= nHandle
)
829 *ppy
= reinterpret_cast<int*>(reinterpret_cast<BYTE
*>(&m_rect
) + pHandleInfo
->nOffsetY
);
835 // middle handle on Y axis
837 *py
= m_rect
.top
+ (m_rect
.top
-m_rect
.bottom
) / 2;
841 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */