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