xref: /aoo41x/main/embedserv/source/embed/tracker.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 #if defined(_MSC_VER) && (_MSC_VER > 1310)
28 #pragma warning(disable : 4917 4555)
29 #endif
30 
31 #include "stdafx.h"
32 #include <stddef.h>
33 #include "syswinwrapper.hxx"
34 
35 
36 HCURSOR _afxCursors[10] = { 0, };
37 HBRUSH _afxHalftoneBrush = 0;
38 
39 
40 // the struct below is used to determine the qualities of a particular handle
41 struct AFX_HANDLEINFO
42 {
43     size_t nOffsetX;    // offset within RECT for X coordinate
44     size_t nOffsetY;    // offset within RECT for Y coordinate
45     int nCenterX;       // adjust X by Width()/2 * this number
46     int nCenterY;       // adjust Y by Height()/2 * this number
47     int nHandleX;       // adjust X by handle size * this number
48     int nHandleY;       // adjust Y by handle size * this number
49     int nInvertX;       // handle converts to this when X inverted
50     int nInvertY;       // handle converts to this when Y inverted
51 };
52 
53 // this array describes all 8 handles (clock-wise)
54 const AFX_HANDLEINFO _afxHandleInfo[] =
55 {
56     // corner handles (top-left, top-right, bottom-right, bottom-left
57     { offsetof(RECT, left), offsetof(RECT, top),        0, 0,  0,  0, 1, 3 },
58     { offsetof(RECT, right), offsetof(RECT, top),       0, 0, -1,  0, 0, 2 },
59     { offsetof(RECT, right), offsetof(RECT, bottom),    0, 0, -1, -1, 3, 1 },
60     { offsetof(RECT, left), offsetof(RECT, bottom),     0, 0,  0, -1, 2, 0 },
61 
62     // side handles (top, right, bottom, left)
63     { offsetof(RECT, left), offsetof(RECT, top),        1, 0,  0,  0, 4, 6 },
64     { offsetof(RECT, right), offsetof(RECT, top),       0, 1, -1,  0, 7, 5 },
65     { offsetof(RECT, left), offsetof(RECT, bottom),     1, 0,  0, -1, 6, 4 },
66     { offsetof(RECT, left), offsetof(RECT, top),        0, 1,  0,  0, 5, 7 }
67 };
68 
69 // the struct below gives us information on the layout of a RECT struct and
70 //  the relationship between its members
71 struct AFX_RECTINFO
72 {
73     size_t nOffsetAcross;   // offset of opposite point (ie. left->right)
74     int nSignAcross;        // sign relative to that point (ie. add/subtract)
75 };
76 
77 // this array is indexed by the offset of the RECT member / sizeof(int)
78 const AFX_RECTINFO _afxRectInfo[] =
79 {
80     { offsetof(RECT, right), +1 },
81     { offsetof(RECT, bottom), +1 },
82     { offsetof(RECT, left), -1 },
83     { offsetof(RECT, top), -1 },
84 };
85 
86 
87 HBRUSH HalftoneBrush()
88 {
89     if (_afxHalftoneBrush == NULL)
90     {
91         WORD grayPattern[8];
92         for (int i = 0; i < 8; i++)
93             grayPattern[i] = (WORD)(0x5555 << (i & 1));
94         HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern);
95         if (grayBitmap != NULL)
96         {
97             _afxHalftoneBrush = CreatePatternBrush(grayBitmap);
98             DeleteObject(grayBitmap);
99         }
100     }
101     return _afxHalftoneBrush;
102 }
103 
104 
105 
106 void DrawDragRect(
107     HDC hDC,LPRECT lpRect,SIZE size,
108     LPRECT lpRectLast,SIZE sizeLast,
109     HBRUSH hBrush = NULL,HBRUSH hBrushLast = NULL)
110 {
111     // first, determine the update region and select it
112     HRGN rgnNew;
113     HRGN rgnOutside,rgnInside;
114     rgnOutside = CreateRectRgnIndirect(lpRect);
115     RECT rect = *lpRect;
116     InflateRect(&rect,-size.cx, -size.cy);
117     IntersectRect(&rect,&rect,lpRect);
118     rgnInside = CreateRectRgnIndirect(&rect);
119     rgnNew = CreateRectRgn(0, 0, 0, 0);
120     CombineRgn(rgnNew,rgnOutside,rgnInside,RGN_XOR);
121 
122     HBRUSH hBrushOld = NULL;
123     if (hBrush == NULL)
124         hBrush = HalftoneBrush();
125     if (hBrushLast == NULL)
126         hBrushLast = hBrush;
127 
128     HRGN rgnLast(NULL);
129     HRGN rgnUpdate(NULL);
130     if (lpRectLast != NULL)
131     {
132         // find difference between new region and old region
133         rgnLast = CreateRectRgn(0, 0, 0, 0);
134         SetRectRgn(
135             rgnOutside,
136             lpRectLast->left,
137             lpRectLast->top,
138             lpRectLast->right,
139             lpRectLast->bottom);
140         rect = *lpRectLast;
141         InflateRect(&rect,-sizeLast.cx, -sizeLast.cy);
142         IntersectRect(&rect,&rect, lpRectLast);
143         SetRectRgn(rgnInside,rect.left,rect.top,rect.right,rect.bottom);
144         CombineRgn(rgnLast,rgnOutside,rgnInside, RGN_XOR);
145 
146 // 		// only diff them if brushes are the same
147         if (hBrush == hBrushLast)
148         {
149             rgnUpdate = CreateRectRgn(0, 0, 0, 0);
150             CombineRgn(rgnUpdate,rgnLast,rgnNew, RGN_XOR);
151         }
152     }
153     if (hBrush != hBrushLast && lpRectLast != NULL)
154     {
155         // brushes are different -- erase old region first
156         SelectClipRgn(hDC,rgnLast);
157         GetClipBox(hDC,&rect);
158         hBrushOld = (HBRUSH)SelectObject(hDC,(HGDIOBJ)hBrushLast);
159         PatBlt(hDC,rect.left,rect.top,(rect.right-rect.left),(rect.bottom-rect.top),PATINVERT);
160 
161         SelectObject(hDC,(HGDIOBJ)hBrushOld);
162         hBrushOld = NULL;
163     }
164 
165     // draw into the update/new region
166     SelectClipRgn(hDC,rgnUpdate);
167 
168     GetClipBox(hDC,&rect);
169     hBrushOld = (HBRUSH) SelectObject(hDC,(HGDIOBJ) hBrush);
170     PatBlt(hDC,rect.left, rect.top,(rect.right-rect.left),(rect.bottom-rect.top), PATINVERT);
171 
172     // cleanup DC
173     if (hBrushOld != NULL)
174         SelectObject(hDC,(HGDIOBJ)hBrushOld);
175     SelectClipRgn(hDC,NULL);
176 }
177 
178 
179 void winwrap::TransformRect(LPRECT rect,HWND pWnd,HWND pWndClipTo)
180 {
181     POINT pt;
182     pt.x = rect->left;pt.y = rect->top;
183     ClientToScreen(pWnd,&pt);
184     ScreenToClient(pWndClipTo,&pt);
185     rect->left = pt.x; rect->top = pt.y;
186 
187     pt.x = rect->right;pt.y = rect->bottom;
188     ClientToScreen(pWnd,&pt);
189     ScreenToClient(pWndClipTo,&pt);
190     rect->right = pt.x; rect->bottom = pt.y;
191 }
192 
193 
194 void NormalizeRect(LPRECT rp)
195 {
196     if(rp->left > rp->right) {
197         UINT tmp = rp->left;
198         rp->left = rp->right;
199         rp->right = tmp;
200     }
201 
202     if(rp->top > rp->bottom) {
203         UINT tmp = rp->top;
204         rp->top = rp->bottom;
205         rp->bottom = tmp;
206     }
207 }
208 
209 
210 using namespace winwrap;
211 
212 
213 Tracker::Tracker()
214 {
215 }
216 
217 
218 Tracker::Tracker(LPCRECT lpSrcRect, UINT nStyle)
219 {
220     Construct();
221     CopyRect(&m_rect,lpSrcRect);
222     m_nStyle = nStyle;
223 }
224 
225 HBRUSH _afxHatchBrush = 0;
226 HPEN _afxBlackDottedPen = 0;
227 int _afxHandleSize = 0;
228 
229 
230 void Tracker::Construct()
231 {
232     static BOOL bInitialized = false;
233     if (!bInitialized)
234     {
235         if (_afxHatchBrush == NULL)
236         {
237             // create the hatch pattern + bitmap
238             WORD hatchPattern[8];
239             WORD wPattern = 0x1111;
240             for (int i = 0; i < 4; i++)
241             {
242                 hatchPattern[i] = wPattern;
243                 hatchPattern[i+4] = wPattern;
244                 wPattern <<= 1;
245             }
246             HBITMAP hatchBitmap = CreateBitmap(8, 8, 1, 1,&hatchPattern);
247 
248             // create black hatched brush
249             _afxHatchBrush = CreatePatternBrush(hatchBitmap);
250             DeleteObject(hatchBitmap);
251         }
252 
253         if (_afxBlackDottedPen == NULL)
254         {
255             // create black dotted pen
256             _afxBlackDottedPen = CreatePen(PS_DOT, 0, RGB(0, 0, 0));
257         }
258 
259         // get default handle size from Windows profile setting
260         static const TCHAR szWindows[] = TEXT("windows");
261         static const TCHAR szInplaceBorderWidth[] =
262             TEXT("oleinplaceborderwidth");
263         _afxHandleSize = GetProfileInt(szWindows, szInplaceBorderWidth, 4);
264         bInitialized = TRUE;
265 
266         _afxCursors[0] = _afxCursors[2] = LoadCursor(0,IDC_SIZENWSE);
267         _afxCursors[4] = _afxCursors[6] = LoadCursor(0,IDC_SIZENS);
268         _afxCursors[1] = _afxCursors[3] = LoadCursor(0,IDC_SIZENESW);
269         _afxCursors[5] = _afxCursors[7] = LoadCursor(0,IDC_SIZEWE);
270         _afxCursors[8] = LoadCursor(0,IDC_SIZEALL);
271     }
272 
273     m_nStyle = 0;
274     m_nHandleSize = _afxHandleSize;
275     m_sizeMin.cy = m_sizeMin.cx = m_nHandleSize*2;
276 
277     SetRectEmpty(&m_rectLast);
278     m_sizeLast.cx = m_sizeLast.cy = 0;
279     m_bErase = FALSE;
280     m_bFinalErase =  FALSE;
281 }
282 
283 Tracker::~Tracker()
284 {
285 }
286 
287 
288 int Tracker::HitTest(POINT point) const
289 {
290     TrackerHit hitResult = hitNothing;
291 
292     RECT rectTrue;
293     GetTrueRect(&rectTrue);
294     NormalizeRect(&rectTrue);
295     if (PtInRect(&rectTrue,point))
296     {
297         if ((m_nStyle & (resizeInside|resizeOutside)) != 0)
298             hitResult = (TrackerHit)HitTestHandles(point);
299         else
300             hitResult = hitMiddle;
301     }
302     return hitResult;
303 }
304 
305 
306 BOOL Tracker::SetCursor(HWND pWnd, UINT nHitTest) const
307 {
308     // trackers should only be in client area
309     if (nHitTest != HTCLIENT)
310         return FALSE;
311 
312     // convert cursor position to client co-ordinates
313     POINT point;
314     GetCursorPos(&point);
315     ScreenToClient(pWnd,&point);
316 
317     // do hittest and normalize hit
318     int nHandle = HitTestHandles(point);
319     if (nHandle < 0)
320         return FALSE;
321 
322     // need to normalize the hittest such that we get proper cursors
323     nHandle = NormalizeHit(nHandle);
324 
325     // handle special case of hitting area between handles
326     //  (logically the same -- handled as a move -- but different cursor)
327     if (nHandle == hitMiddle && !PtInRect(&m_rect,point))
328     {
329         // only for trackers with hatchedBorder (ie. in-place resizing)
330         if (m_nStyle & hatchedBorder)
331             nHandle = (TrackerHit)9;
332     }
333 
334     ::SetCursor(_afxCursors[nHandle]);
335     return TRUE;
336 }
337 
338 
339 
340 BOOL Tracker::Track(HWND hWnd,POINT point,BOOL bAllowInvert,
341                     HWND hWndClipTo)
342 {
343     // perform hit testing on the handles
344     int nHandle = HitTestHandles(point);
345     if (nHandle < 0)
346     {
347         // didn't hit a handle, so just return FALSE
348         return FALSE;
349     }
350 
351     // otherwise, call helper function to do the tracking
352     m_bAllowInvert = bAllowInvert;
353     SetCursor(hWnd,nHandle);
354     return TrackHandle(nHandle, hWnd, point, hWndClipTo);
355 }
356 
357 
358 BOOL Tracker::TrackHandle(int nHandle,HWND hWnd,POINT point,HWND hWndClipTo)
359 {
360     // don't handle if capture already set
361     if (GetCapture() != NULL)
362         return FALSE;
363 
364     // save original width & height in pixels
365     int nWidth = m_rect.right - m_rect.left;
366     int nHeight = m_rect.bottom - m_rect.top;
367 
368     // set capture to the window which received this message
369     SetCapture(hWnd);
370     UpdateWindow(hWnd);
371     if (hWndClipTo != NULL)
372         UpdateWindow(hWndClipTo);
373     RECT rectSave = m_rect;
374 
375 	// find out what x/y coords we are supposed to modify
376     int *px, *py;
377     int xDiff, yDiff;
378     GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff);
379     xDiff = point.x - xDiff;
380     yDiff = point.y - yDiff;
381 
382     // get DC for drawing
383     HDC hDrawDC;
384     if (hWndClipTo != NULL)
385     {
386         // clip to arbitrary window by using adjusted Window DC
387         hDrawDC = GetDCEx(hWndClipTo,NULL, DCX_CACHE);
388     }
389     else
390     {
391         // otherwise, just use normal DC
392         hDrawDC = GetDC(hWnd);
393     }
394 
395     RECT rectOld;
396     BOOL bMoved = FALSE;
397 
398     // get messages until capture lost or cancelled/accepted
399     for (;;)
400     {
401         MSG msg;
402         GetMessage(&msg, NULL, 0, 0);
403 
404         if (GetCapture() != hWnd)
405             break;
406 
407         switch (msg.message)
408         {
409             // handle movement/accept messages
410         case WM_LBUTTONUP:
411         case WM_MOUSEMOVE:
412             rectOld = m_rect;
413             // handle resize cases (and part of move)
414             if (px != NULL)
415                 *px = (int)(short)LOWORD(msg.lParam) - xDiff;
416             if (py != NULL)
417                 *py = (int)(short)HIWORD(msg.lParam) - yDiff;
418 
419             // handle move case
420             if (nHandle == hitMiddle)
421             {
422                 m_rect.right = m_rect.left + nWidth;
423                 m_rect.bottom = m_rect.top + nHeight;
424             }
425             // allow caller to adjust the rectangle if necessary
426             AdjustRect(nHandle,&m_rect);
427 
428             // only redraw and callback if the rect actually changed!
429             m_bFinalErase = (msg.message == WM_LBUTTONUP);
430             if (!EqualRect(&rectOld,&m_rect) || m_bFinalErase)
431             {
432                 if (bMoved)
433                 {
434                     m_bErase = TRUE;
435                     DrawTrackerRect(&rectOld,hWndClipTo,hDrawDC,hWnd);
436                 }
437                 OnChangedRect(rectOld);
438                 if (msg.message != WM_LBUTTONUP)
439                     bMoved = TRUE;
440             }
441             if (m_bFinalErase)
442                 goto ExitLoop;
443 
444             if (!EqualRect(&rectOld,&m_rect))
445             {
446                 m_bErase = FALSE;
447                 DrawTrackerRect(&m_rect,hWndClipTo,hDrawDC,hWnd);
448             }
449             break;
450 
451             // handle cancel messages
452         case WM_KEYDOWN:
453             if (msg.wParam != VK_ESCAPE)
454                 break;
455         case WM_RBUTTONDOWN:
456             if (bMoved)
457             {
458                 m_bErase = m_bFinalErase = TRUE;
459                 DrawTrackerRect(&m_rect, hWndClipTo, hDrawDC, hWnd);
460             }
461             m_rect = rectSave;
462             goto ExitLoop;
463 
464             // just dispatch rest of the messages
465         default:
466             DispatchMessage(&msg);
467             break;
468         }
469     }
470 
471   ExitLoop:
472     if (hWndClipTo != NULL)
473         ReleaseDC(hWndClipTo,hDrawDC);
474     else
475         ReleaseDC(hWnd,hDrawDC);
476     ReleaseCapture();
477 
478     // restore rect in case bMoved is still FALSE
479     if (!bMoved)
480         m_rect = rectSave;
481     m_bFinalErase = FALSE;
482     m_bErase = FALSE;
483 
484     // return TRUE only if rect has changed
485     return !EqualRect(&rectSave,&m_rect);
486 }
487 
488 
489 void Tracker::OnChangedRect(const RECT& /*rectOld*/)
490 {
491 }
492 
493 
494 void Tracker::AdjustRect(int nHandle, LPRECT)
495 {
496     if(nHandle == hitMiddle)
497         return;
498 
499     // convert the handle into locations within m_rect
500     int *px, *py;
501     GetModifyPointers(nHandle, &px, &py, NULL, NULL);
502 
503     // enforce minimum width
504     int nNewWidth = m_rect.right - m_rect.left;
505     int nAbsWidth = m_bAllowInvert ? abs(nNewWidth) : nNewWidth;
506     if (px != NULL && nAbsWidth < m_sizeMin.cx)
507     {
508         nNewWidth = nAbsWidth != 0 ? nNewWidth / nAbsWidth : 1;
509         const AFX_RECTINFO* pRectInfo =
510             &_afxRectInfo[(int*)px - (int*)&m_rect];
511         *px = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
512             nNewWidth * m_sizeMin.cx * -pRectInfo->nSignAcross;
513     }
514 
515     // enforce minimum height
516     int nNewHeight = m_rect.bottom - m_rect.top;
517     int nAbsHeight = m_bAllowInvert ? abs(nNewHeight) : nNewHeight;
518     if (py != NULL && nAbsHeight < m_sizeMin.cy)
519     {
520         nNewHeight = nAbsHeight != 0 ? nNewHeight / nAbsHeight : 1;
521         const AFX_RECTINFO* pRectInfo =
522             &_afxRectInfo[(int*)py - (int*)&m_rect];
523         *py = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
524             nNewHeight * m_sizeMin.cy * -pRectInfo->nSignAcross;
525     }
526 }
527 
528 
529 void Tracker::DrawTrackerRect(
530     LPRECT lpRect,HWND pWndClipTo,HDC pDC,HWND pWnd)
531 {
532     // first, normalize the rectangle for drawing
533     RECT rect = *lpRect;
534     NormalizeRect(&rect);
535 
536     // convert to client coordinates
537     if (pWndClipTo != NULL)
538         TransformRect(&rect,pWnd,pWndClipTo);
539 
540     SIZE size;
541     size.cx = 0; size.cy = 0;
542     if (!m_bFinalErase)
543     {
544         // otherwise, size depends on the style
545         if (m_nStyle & hatchedBorder)
546         {
547             size.cx = size.cy = max(1,GetHandleSize(&rect)-1);
548             InflateRect(&rect,size.cx,size.cy);
549         }
550         else
551         {
552             size.cx = 1; // CX_BORDER;
553             size.cy = 1; // CY_BORDER;
554         }
555     }
556 
557     // and draw it
558     if (m_bFinalErase || !m_bErase)
559         DrawDragRect(pDC,&rect,size,&m_rectLast,m_sizeLast);
560 
561     // remember last rectangles
562     m_rectLast = rect;
563     m_sizeLast = size;
564 }
565 
566 
567 void Tracker::Draw(HDC hDC) const
568 {
569     // set initial DC state
570     SetMapMode(hDC,MM_TEXT);
571     SetViewportOrgEx(hDC,0, 0,NULL);
572     SetWindowOrgEx(hDC,0, 0,NULL);
573 
574     // get normalized rectangle
575     RECT rect = m_rect;
576     NormalizeRect(&rect);
577 
578     HPEN pOldPen = NULL;
579     HBRUSH pOldBrush = NULL;
580     HGDIOBJ pTemp;
581     int nOldROP;
582 
583     // draw lines
584     if ((m_nStyle & (dottedLine|solidLine)) != 0)
585     {
586         if (m_nStyle & dottedLine)
587             pOldPen = (HPEN)SelectObject(hDC,_afxBlackDottedPen);
588         else
589             pOldPen = (HPEN)SelectObject(hDC,(HGDIOBJ)BLACK_PEN);
590         pOldBrush = (HBRUSH)SelectObject(hDC,(HGDIOBJ)NULL_BRUSH);
591         nOldROP = SetROP2(hDC,R2_COPYPEN);
592         InflateRect(&rect,+1, +1);   // borders are one pixel outside
593         Rectangle(hDC,rect.left, rect.top, rect.right, rect.bottom);
594         SetROP2(hDC,nOldROP);
595     }
596 
597     // if hatchBrush is going to be used, need to unrealize it
598     if ((m_nStyle & (hatchInside|hatchedBorder)) != 0)
599         UnrealizeObject((HGDIOBJ)_afxHatchBrush);
600 
601     // hatch inside
602     if ((m_nStyle & hatchInside) != 0)
603     {
604         pTemp = SelectObject(hDC,(HGDIOBJ)NULL_PEN);
605         if (pOldPen == NULL)
606             pOldPen = (HPEN)pTemp;
607         pTemp = SelectObject(hDC,(HGDIOBJ)_afxHatchBrush);
608         if (pOldBrush == NULL)
609             pOldBrush = (HBRUSH)pTemp;
610         SetBkMode(hDC,TRANSPARENT);
611         nOldROP = SetROP2(hDC,R2_MASKNOTPEN);
612         Rectangle(hDC,rect.left+1, rect.top+1, rect.right, rect.bottom);
613         SetROP2(hDC,nOldROP);
614     }
615 
616     // draw hatched border
617     if ((m_nStyle & hatchedBorder) != 0)
618     {
619         pTemp = SelectObject(hDC,(HGDIOBJ)_afxHatchBrush);
620         if (pOldBrush == NULL)
621             pOldBrush = (HBRUSH)pTemp;
622         SetBkMode(hDC,OPAQUE);
623         RECT rectTrue;
624         GetTrueRect(&rectTrue);
625         PatBlt(hDC,rectTrue.left, rectTrue.top, rectTrue.right-rectTrue.left,
626                rect.top-rectTrue.top, 0x000F0001 /* Pn */);
627         PatBlt(hDC,rectTrue.left, rect.bottom,
628                rectTrue.right-rectTrue.left,
629                rectTrue.bottom-rect.bottom, 0x000F0001 /* Pn */);
630         PatBlt(hDC,rectTrue.left, rect.top, rect.left-rectTrue.left,
631                rect.bottom-rect.top, 0x000F0001 /* Pn */);
632         PatBlt(hDC,rect.right, rect.top, rectTrue.right-rect.right,
633                rect.bottom-rect.top, 0x000F0001 /* Pn */);
634     }
635 
636     // draw resize handles
637     if ((m_nStyle & (resizeInside|resizeOutside)) != 0)
638     {
639         UINT mask = GetHandleMask();
640         HBRUSH hbrush = CreateSolidBrush(RGB(0,0,0));
641         for (int i = 0; i < 8; ++i)
642         {
643             if (mask & (1<<i))
644             {
645                 GetHandleRect((TrackerHit)i, &rect);
646                 // FillSolidRect(hDC,rect, RGB(0, 0, 0));
647                 FillRect(hDC,&rect,hbrush);
648             }
649         }
650         DeleteObject(hbrush);
651     }
652 
653     // cleanup pDC state
654     if (pOldPen != NULL)
655         SelectObject(hDC,pOldPen);
656     if (pOldBrush != NULL)
657         SelectObject(hDC,pOldBrush);
658     RestoreDC(hDC,-1);
659 }
660 
661 
662 void Tracker::GetHandleRect(int nHandle,RECT* pHandleRect) const
663 {
664     // get normalized rectangle of the tracker
665     RECT rectT = m_rect;
666     NormalizeRect(&rectT);
667     if ((m_nStyle & (solidLine|dottedLine)) != 0)
668         InflateRect(&rectT,+1, +1);
669 
670     // since the rectangle itself was normalized, we also have to invert the
671     //  resize handles.
672     nHandle = NormalizeHit(nHandle);
673 
674     // handle case of resize handles outside the tracker
675     int size = GetHandleSize();
676     if (m_nStyle & resizeOutside)
677         InflateRect(&rectT,size-1, size-1);
678 
679     // calculate position of the resize handle
680     int nWidth = rectT.right - rectT.left;
681     int nHeight = rectT.bottom - rectT.top;
682     RECT rect;
683     const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
684     rect.left = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetX);
685     rect.top = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetY);
686     rect.left += size * pHandleInfo->nHandleX;
687     rect.top += size * pHandleInfo->nHandleY;
688     rect.left += pHandleInfo->nCenterX * (nWidth - size) / 2;
689     rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2;
690     rect.right = rect.left + size;
691     rect.bottom = rect.top + size;
692 
693     *pHandleRect = rect;
694 }
695 
696 
697 int Tracker::GetHandleSize(LPRECT lpRect) const
698 {
699     if (lpRect == NULL)
700         lpRect = (LPRECT)&m_rect;
701 
702     int size = m_nHandleSize;
703     if (!(m_nStyle & resizeOutside))
704     {
705         // make sure size is small enough for the size of the rect
706         int sizeMax = min(abs(lpRect->right - lpRect->left),
707                           abs(lpRect->bottom - lpRect->top));
708         if (size * 2 > sizeMax)
709             size = sizeMax / 2;
710     }
711     return size;
712 }
713 
714 
715 UINT Tracker::GetHandleMask() const
716 {
717     UINT mask = 0x0F;   // always have 4 corner handles
718     int size = m_nHandleSize*3;
719     if (abs(m_rect.right - m_rect.left) - size > 4)
720         mask |= 0x50;
721     if (abs(m_rect.bottom - m_rect.top) - size > 4)
722         mask |= 0xA0;
723     return mask;
724 }
725 
726 
727 void Tracker::GetTrueRect(LPRECT lpTrueRect) const
728 {
729     RECT rect = m_rect;
730     NormalizeRect(&rect);
731     int nInflateBy = 0;
732     if ((m_nStyle & (resizeOutside|hatchedBorder)) != 0)
733         nInflateBy += GetHandleSize() - 1;
734     if ((m_nStyle & (solidLine|dottedLine)) != 0)
735         ++nInflateBy;
736     InflateRect(&rect,nInflateBy, nInflateBy);
737     *lpTrueRect = rect;
738 }
739 
740 
741 int Tracker::NormalizeHit(int nHandle) const
742 {
743     if (nHandle == hitMiddle || nHandle == hitNothing)
744         return nHandle;
745     const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
746     if (m_rect.right - m_rect.left < 0)
747     {
748         nHandle = (TrackerHit)pHandleInfo->nInvertX;
749         pHandleInfo = &_afxHandleInfo[nHandle];
750     }
751     if (m_rect.bottom - m_rect.top < 0)
752         nHandle = (TrackerHit)pHandleInfo->nInvertY;
753     return nHandle;
754 }
755 
756 
757 int Tracker::HitTestHandles(POINT point) const
758 {
759     RECT rect;
760     UINT mask = GetHandleMask();
761 
762     // see if hit anywhere inside the tracker
763     GetTrueRect(&rect);
764     if (!PtInRect(&rect,point))
765         return hitNothing;  // totally missed
766 
767     // see if we hit a handle
768     for (int i = 0; i < 8; ++i)
769     {
770         if (mask & (1<<i))
771         {
772             GetHandleRect((TrackerHit)i, &rect);
773             if (PtInRect(&rect,point))
774                 return (TrackerHit)i;
775         }
776     }
777 
778     // last of all, check for non-hit outside of object, between resize handles
779     if ((m_nStyle & hatchedBorder) == 0)
780     {
781         RECT rect = m_rect;
782         NormalizeRect(&rect);
783         if ((m_nStyle & dottedLine|solidLine) != 0)
784             InflateRect(&rect,+1, +1);
785         if (!PtInRect(&rect,point))
786             return hitNothing;  // must have been between resize handles
787     }
788     return hitMiddle;   // no handle hit, but hit object (or object border)
789 }
790 
791 
792 
793 void Tracker::GetModifyPointers(
794     int nHandle, int** ppx, int** ppy, int* px, int* py)
795 {
796     if (nHandle == hitMiddle)
797         nHandle = hitTopLeft;   // same as hitting top-left
798 
799     *ppx = NULL;
800     *ppy = NULL;
801 
802     // fill in the part of the rect that this handle modifies
803     //  (Note: handles that map to themselves along a given axis when that
804     //   axis is inverted don't modify the value on that axis)
805 
806     const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
807     if (pHandleInfo->nInvertX != nHandle)
808     {
809         *ppx = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetX);
810         if (px != NULL)
811             *px = **ppx;
812     }
813     else
814     {
815         // middle handle on X axis
816         if (px != NULL)
817             *px = m_rect.left + (m_rect.left-m_rect.right) / 2;
818     }
819     if (pHandleInfo->nInvertY != nHandle)
820     {
821         *ppy = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetY);
822         if (py != NULL)
823             *py = **ppy;
824     }
825     else
826     {
827         // middle handle on Y axis
828         if (py != NULL)
829             *py = m_rect.top + (m_rect.top-m_rect.bottom) / 2;
830     }
831 }
832 
833 // Fix strange warnings about some
834 // ATL::CAxHostWindow::QueryInterface|AddRef|Releae functions.
835 // warning C4505: 'xxx' : unreferenced local function has been removed
836 #if defined(_MSC_VER)
837 #pragma warning(disable: 4505)
838 #endif
839