00001 // $Id: selector.cpp 1740 2006-09-05 11:08:36Z luke $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 // The Selector Tool 00099 00100 /* 00101 */ 00102 00103 #include "camtypes.h" 00104 00105 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 //#include "stockcol.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00107 #include "csrstack.h" 00108 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00110 //#include "oilkeys.h" 00111 #include "oilfiles.h" 00112 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00113 #include "osrndrgn.h" 00114 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00115 #include "nodedoc.h" 00116 //#include "selmsg.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00117 //#include "docvmsg.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00118 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00119 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00120 #include "selector.h" 00121 #include "selinfo.h" 00122 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00123 #include "progress.h" 00124 #include "keypress.h" 00125 #include "vkextra.h" 00126 #include "insertnd.h" 00127 #include "nodetxts.h" 00128 00129 #include "tranlate.h" 00130 #include "opscale.h" 00131 #include "opscale2.h" 00132 #include "opsquash.h" 00133 #include "opshear.h" 00134 #include "opflip.h" 00135 #include "oprotate.h" 00136 #include "guides.h" 00137 #include "layer.h" 00138 //#include "basebar.h" 00139 00140 //#include "resource.h" 00141 //#include "viewrc.h" 00142 //#include "justin.h" 00143 //#include "markn.h" 00144 //#include "mario.h" 00145 //#include "rik.h" 00146 //#include "selstr.h" 00147 00148 //#include "will2.h" 00149 //#include "oleprefs.h" 00150 #include "menuops.h" 00151 00152 // knowledge of these classes is required by tab-selection. 00153 // remove these if/when the Iterate...() methods are virtual-ised. 00154 #include "nodebldr.h" 00155 00156 00157 //Matt 11/11/00 - I only wanted a function from the next line... 00158 #include "slicehelper.h" //For helper functions 00159 00160 //But I had to include all of these to get it to work!!... 00161 //#include "cxfrech.h" //For CamelotRecordHandler - in camtypes.h [AUTOMATICALLY REMOVED] 00162 #include "userattr.h" //For UserAttr 00163 #include "tmpltatr.h" //For TemplateAttribute 00164 00165 #include "cartprov.h" 00166 00167 00168 DECLARE_SOURCE( "$Revision: 1740 $" ); 00169 00170 // Karim 09/08/2000 - enable the new tab selection-iteration code. 00171 #define NEW_SELECTION_TAB_ITERATION 00172 00173 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 00174 #define MAX(a, b) ((a) < (b) ? (b) : (a)) 00175 #define CURSORID_UNSET 5000 00176 00177 // Macro defining whether text should come from resources or should be direct... 00178 #define T(res,string) res 00179 //#define T(res,string) string //<<<< 00180 00181 // These are still char* while we wait for resource technology to be developed for modules 00182 TCHAR* SelectorTool::FamilyName = _T("Selection Tools"); 00183 TCHAR* SelectorTool::ToolName = _T("Selector Tool"); 00184 TCHAR* SelectorTool::Purpose = _T("Selecting objects and editing the selection"); 00185 TCHAR* SelectorTool::Author = _T("Justin Flude & Phil Martin"); 00186 00187 SelectorInfoBarOp* SelectorTool::pInfoBarOp = NULL; 00188 BOOL SelectorTool::fSelectorIsCaching = FALSE; 00189 00190 INT32 SelectorTool::CursorStackID= CURSORID_UNSET; 00191 BOOL SelectorTool::bNormalClickCheckProfileDialog = FALSE; 00192 BOOL SelectorTool::bGlineSAllowed = TRUE; 00193 00194 00195 /********************************************************************************************* 00196 00197 Preference: BlobBorder 00198 Section: Selection Blob Sizes 00199 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00200 Range: 0 ... 10 (millimetres) 00201 Purpose: Determines how wide a border, in millimetres, to inflate the bounding 00202 rectangle of the selected object, when drawing blobs around it. The 00203 default is 4 mm. 00204 00205 **********************************************************************************************/ 00206 00207 INT32 SelectorTool::nBlobBorder = 4; 00208 00209 // This defines the number of millipoints in a millimetre. 00210 const MILLIPOINT nMPperMM = 2835L; 00211 00212 00213 00214 /********************************************************************************************* 00215 00216 Preference: InitialBlobs 00217 Section: Selector Tool 00218 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00219 Range: 0 ... UINT_MAX 00220 Purpose: Determines which blobs should be initially displayed. This should really be 00221 done by the blob manager! 00222 00223 **********************************************************************************************/ 00224 00225 unsigned SelectorTool::fBlobPref = 1; // by default, show only bounds/rotate blobs 00226 00227 00228 00229 /********************************************************************************************* 00230 00231 Preference: AllowCorelToggleClick 00232 Section: Selector Tool 00233 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00234 Range: FALSE ... TRUE 00235 Purpose: If TRUE (the default) allows a single click on a selected object to toggle 00236 between bounds blobs & rotate blobs, for compatibility with Corel Draw. This 00237 toggling option not allowed when Object blobs are shown, it would be a pain. 00238 00239 **********************************************************************************************/ 00240 00241 BOOL SelectorTool::fAllowCorelToggleClick = TRUE; 00242 00243 00244 00245 /********************************************************************************************* 00246 00247 Preference: SelectUnderLikeArtWorks 00248 Section: Selector Tool 00249 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00250 Range: FALSE ... TRUE 00251 Purpose: If TRUE then the select-under feature, Alt-Click, works exactly like ArtWorks, 00252 so that an Alt-Click on an unselected object selects that object rather than 00253 the object underneath. If FALSE then Alt-Click always selects an object 00254 underneath the clicked object, irrespective of whether the clicked object 00255 was selected or not. The default is FALSE, as this is consistent with the 00256 way that select-inside works. 00257 00258 **********************************************************************************************/ 00259 00260 BOOL SelectorTool::fSelectUnderLikeArtWorks = FALSE; 00261 00262 00263 00264 /********************************************************************************************* 00265 00266 Preference: SlaveLineWidthToButton 00267 00268 Section: Selector Tool 00269 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00270 Created: 26/2/96 00271 00272 Range: Boolean 00273 Purpose: Controls whether attributes (line widths mainly) are considered when 00274 calculating bounding rectangles for objects. This is used when displaying 00275 and editing width/height information in the infobar. 00276 00277 If TRUE, the "Scale Line Widths" button will also control the behaviour 00278 of the function SelectorInfoBarOp::DontConsiderAttrs() - if the button is 00279 depressed, Attrs will not be considered, and vice versa. 00280 00281 If FALSE, the behaviour will be fixed, and controlled by the preference 00282 "ConsiderLineWidths" in the "Selector Tool" section. 00283 00284 The default setting (TRUE) is to determine the state from the button setting. 00285 00286 Notes: This preference is read by our associated SelectorInfoBarOp 00287 This preference is not currently written by camelot 00288 00289 **********************************************************************************************/ 00290 00291 BOOL SelectorTool::fSlaveLineWidthToButton = TRUE; 00292 00293 00294 00295 /********************************************************************************************* 00296 00297 Preference: ConsiderLineWidths 00298 00299 Section: Selector Tool 00300 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00301 Created: 26/2/96 00302 00303 Range: Boolean 00304 Purpose: Controls whether attributes (line widths mainly) are considered when 00305 calculating bounding rectangles for objects. This is used when displaying 00306 and editing width/height information in the infobar. 00307 00308 This preference is IGNORED if the "SlaveLineWidthToButton" pref is TRUE. 00309 00310 The default setting (FALSE) is to ignore line widths in these displays. 00311 00312 Notes: This preference is read by our associated SelectorInfoBarOp 00313 This preference is not currently written by camelot 00314 00315 **********************************************************************************************/ 00316 00317 BOOL SelectorTool::fConsiderLineWidths = FALSE; 00318 00319 00320 00321 /********************************************************************************************* 00322 00323 Preference: fUseScalingFix 00324 00325 Section: Selector Tool 00326 Author: Harrison_Ainsworth (Xara Group Ltd) <camelotdev@xara.com> 00327 Created: 13/11/97 00328 00329 Range: Boolean 00330 Purpose: Controls whether my scaling fix is used 00331 00332 The default setting (FALSE) is to *not* use the new scaling fix 00333 00334 Notes: This preference is read by our associated SelectorInfoBarOp 00335 This preference is not currently written by camelot 00336 00337 **********************************************************************************************/ 00338 00339 BOOL SelectorTool::fUseScalingFix = FALSE; 00340 00341 00342 00343 /********************************************************************************************* 00344 00345 Preference: bPageDoubleClickOpenFile 00346 00347 Section: Selector Tool 00348 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00349 Created: 31/05/2005 00350 00351 Range: Boolean 00352 Purpose: Controls whether double-clicking on the page will show the Open dialog 00353 00354 **********************************************************************************************/ 00355 00356 BOOL SelectorTool::bPageDoubleClickOpenFile = FALSE; 00357 00358 00359 00360 // This is our own permanent pointer to the blob manager. 00361 BlobManager* SelectorTool::pBlobManager = NULL; 00362 00363 // Run-time type checking etc etc 00364 CC_IMPLEMENT_DYNCREATE(OpSelectorDragBox, Operation) 00365 CC_IMPLEMENT_DYNCREATE(OpDragRotateCentre, Operation) 00366 CC_IMPLEMENT_MEMDUMP(SelectorTool, DragTool) 00367 00368 // report memory line info 00369 #define new CAM_DEBUG_NEW 00370 00371 00372 00373 /******************************************************************************************** 00374 00375 > SelectorTool::SelectorTool() 00376 00377 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00378 Created: 21/6/93 00379 Purpose: Dummy Constructor - sets member pointers to NULL and initialises the 00380 record of which blobs are displayed (initially only "tiny" blobs). 00381 SeeAlso: SelectorTool::Init 00382 00383 ********************************************************************************************/ 00384 00385 SelectorTool::SelectorTool() 00386 : //pInfoBarOp(NULL), 00387 fRotateCentreIsValid(FALSE), 00388 eCurrentBlobs(BOUNDS_BLOBS), 00389 fShowToolBlobs(TRUE), 00390 fValidSelectedBlob(FALSE) 00391 { 00392 // initialise all our member pointers to NULL. 00393 00394 SelectionSpread = NULL; 00395 SelectRange = NULL; 00396 StartSpread = NULL; 00397 00398 pClickSimpleNode = NULL; 00399 pClickCompoundNode = NULL; 00400 pLastClickNode = NULL; 00401 pPreProcClickNode = NULL; 00402 00403 pNormalCursor = NULL; 00404 pAdjustCursor = NULL; 00405 pUnderCursor = NULL; 00406 pInsideCursor = NULL; 00407 pUnderAdjustCursor = NULL; 00408 pInsideAdjustCursor = NULL; 00409 pLeafCursor = NULL; 00410 pLeafAdjustCursor = NULL; 00411 00412 pALLCursor = NULL; 00413 pNWSECursor = NULL; 00414 pNESWCursor = NULL; 00415 pNSCursor = NULL; 00416 pWECursor = NULL; 00417 pGradFillCursor = NULL; 00418 pDragRotateCursor = NULL; 00419 pHorzGuideCursor = NULL; 00420 pVertGuideCursor = NULL; 00421 00422 bGlineSAllowed = TRUE; 00423 m_bComputeAreaDetails = FALSE; 00424 00425 // Pre-load the resource we're going to use 00426 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_6) ); 00427 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_3) ); 00428 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_1) ); 00429 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_8) ); 00430 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_45) ); 00431 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_45) ); 00432 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_27) ); 00433 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_27) ); 00434 (CamArtProvider::Get())->FindBitmap( _R(IDBMP_HANDLE_CENTRE) ); 00435 } 00436 00437 00438 00439 /******************************************************************************************** 00440 00441 > virtual SelectorTool::~SelectorTool() 00442 00443 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00444 Created: 21/6/93 00445 Purpose: Destructor (Virtual). Does nothing. 00446 00447 ********************************************************************************************/ 00448 00449 SelectorTool::~SelectorTool() 00450 { 00451 // empty 00452 } 00453 00454 00455 00456 /******************************************************************************************** 00457 00458 > virtual BOOL SelectorTool::Init() 00459 00460 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00461 Created: 21/6/93 00462 Returns: FALSE if it does not want to be created, TRUE otherwise 00463 Purpose: Used to check if the Tool was properly constructed 00464 SeeAlso: SelectorTool::SelectorTool 00465 00466 ********************************************************************************************/ 00467 00468 BOOL SelectorTool::Init() 00469 { 00470 /* // Claim the left Alt for our tool. 00471 ToolKeyClaimDetails Mods; 00472 Mods.Value = 0; 00473 Mods.Keys.Alternative1 = TRUE; 00474 ClaimToolKey((Tool*) this, Mods); 00475 */ 00476 // Read in the preferences. 00477 if (!ReadPrefs()) 00478 { 00479 TRACE( _T("Couldn't read Selector tool's preferences!\n")); 00480 return FALSE; 00481 } 00482 00483 // Declare the OpDescriptors of each transform. 00484 if (!OpTranslateTrans::Declare() || 00485 !OpRotateTrans::Declare() || 00486 !OpScaleTrans::Declare() || 00487 // !OpScale2Trans::Declare() || 00488 !OpShearTrans::Declare() || 00489 !OpSquashTrans::Declare() || 00490 !OpFlipTrans::Declare() || 00491 !OpSelectorDragBox::Declare() || 00492 !OpDragRotateCentre::Declare()) 00493 { 00494 ENSURE(FALSE, "Couldn't 'declare' all OpDescriptors in SelectorTool::Init"); 00495 return FALSE; 00496 } 00497 00498 // This should be set to NULL by default. It will be set properly below, if 00499 // everthing is working as it should 00500 pInfoBarOp = new SelectorInfoBarOp; 00501 00502 // In the debug version make sure we can create a bar. 00503 ENSURE(pInfoBarOp != NULL, "Failed to create selector tool info-bar"); 00504 if (pInfoBarOp == NULL) return FALSE; 00505 00506 // Get a permanent pointer to the blob manager. 00507 pBlobManager = GetApplication()->GetBlobManager(); 00508 ENSURE(pBlobManager != NULL, "Selector tool: couldn't get blob manager"); 00509 if (pBlobManager == NULL) return FALSE; 00510 00511 // Get a permanent pointer to the application's SelRange object. 00512 SelectRange = GetApplication()->FindSelection(); 00513 00514 // set the initial state of the blob buttons 00515 bsBlobStyle.Object = (fBlobPref & 2) != 0; 00516 bsBlobStyle.Fill = (fBlobPref & 4) != 0; 00517 bsBlobStyle.Artistic = (fBlobPref & 8) != 0; 00518 bsBlobStyle.ToolObject = TRUE; 00519 bsBlobStyle.Effect = FALSE; 00520 00521 // Object blobs & Tiny blobs are mutually exclusive. 00522 bsBlobStyle.Tiny = !bsBlobStyle.Object; 00523 00524 // If we are displaying tool blobs then decide whether they are bounds or rotate blobs. 00525 fShowToolBlobs = (fBlobPref & 1) != 0; 00526 eCurrentBlobs = (pInfoBarOp->InRotateMode()) ? ROTATE_BLOBS : BOUNDS_BLOBS; 00527 00528 // Success!! 00529 return TRUE; 00530 } 00531 00532 00533 00534 /******************************************************************************************** 00535 00536 > static BOOL SelectorTool::ReadPrefs() 00537 00538 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00539 Created: 15/8/94 00540 Inputs: - 00541 Outputs: - 00542 Returns: TRUE if successful. 00543 Purpose: Reads settings concerning the selector tool from the .INI file. 00544 Errors: - 00545 SeeAlso: SelectorTool::Init 00546 00547 ********************************************************************************************/ 00548 00549 BOOL SelectorTool::ReadPrefs() 00550 { 00551 return Camelot.DeclareSection(TEXT("Selector Tool"), 10) && 00552 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("BlobBorder"), 00553 &nBlobBorder, 0, 10) && 00554 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("LockAspect"), 00555 &SelectorInfoBarOp::fLockAspect, FALSE, TRUE) && 00556 // Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("LeaveCopy"), 00557 // &SelectorInfoBarOp::fLeaveCopy, FALSE, TRUE) && 00558 // Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("TransFills"), 00559 // &SelectorInfoBarOp::fTransFills, FALSE, TRUE) && 00560 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("ScaleLines"), 00561 &SelectorInfoBarOp::fScaleLines, FALSE, TRUE) && 00562 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("InitialBlobs"), 00563 &SelectorTool::fBlobPref, 0, UINT_MAX) && 00564 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("AllowCorelToggleClick"), 00565 &SelectorTool::fAllowCorelToggleClick, FALSE, TRUE) && 00566 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("SelectUnderLikeArtWorks"), 00567 &SelectorTool::fSelectUnderLikeArtWorks, FALSE, TRUE) && 00568 00569 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("SlaveLineWidthToButton"), &fSlaveLineWidthToButton) && 00570 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("ConsiderLineWidths"), &fConsiderLineWidths) && 00571 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("UseScalingFix"), &fUseScalingFix) && 00572 Camelot.DeclarePref(TEXT("Selector Tool"), TEXT("PageDoubleClickOpenFile"), &bPageDoubleClickOpenFile) 00573 ; 00574 } 00575 00576 00577 00578 /******************************************************************************************** 00579 00580 > virtual void SelectorTool::Describe(void *InfoPtr) 00581 00582 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00583 Created: 21/6/93 00584 Inputs: InfoPtr - A pointer to a tool info block. It is passed cast to void* as 00585 the version of the tool is unknown at this point. Later versions of the 00586 Tool class may have more items in this block, that this tool will not use 00587 Outputs: InfoPtr - The structure pointed to by InfoPtr will have had all the info 00588 that this version of the Tool knows about 00589 Purpose: Allows the tool manager to extract information about the tool 00590 00591 ********************************************************************************************/ 00592 00593 void SelectorTool::Describe(void *InfoPtr) 00594 { 00595 // Cast structure into the latest one we understand. 00596 ToolInfo_v1* Info = (ToolInfo_v1*) InfoPtr; 00597 Info->InfoVersion = 1; 00598 00599 // You should always have this line. 00600 Info->InterfaceVersion = GetToolInterfaceVersion(); 00601 00602 // These are all arbitrary at present. 00603 Info->Version = 1; 00604 Info->ID = GetID(); 00605 Info->TextID = _R(IDS_SELECTOR_TOOL); 00606 00607 Info->Family = FamilyName; 00608 Info->Name = ToolName; 00609 Info->Purpose = Purpose; 00610 Info->Author = Author; 00611 // Info->ToolBoxBitmap = _R(IDB_SELR_TOOLBOX); 00612 // Info->ToolBoxBitmapActive = _R(IDB_SELR_TOOLBOX_ACTIVE); 00613 00614 Info->InfoBarDialog = 0; 00615 Info->BubbleID = _R(IDBBL_SEL_TOOLICON); 00616 } 00617 00618 00619 00620 /******************************************************************************************** 00621 00622 > virtual void SelectorTool::SelectChange(BOOL isSelected) 00623 00624 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00625 Created: 24/11/93 00626 Inputs: isSelected - TRUE if this tool is becoming current, FALSE 00627 otherwise 00628 Purpose: Called whenever this tool is selected or deselected. 00629 00630 Performs jobs related to changing tools, eg changing the cursor to a 00631 selector cursor, changing the displayed tool blobs to selector tool blobs, 00632 changing the preferences for BlobManager-rendered blobs. 00633 00634 Errors: Debug warning if creating the cursor fails. 00635 00636 ********************************************************************************************/ 00637 00638 void SelectorTool::SelectChange(BOOL isSelected) 00639 { 00640 if (isSelected) 00641 { 00642 // Load the grad-fill cursor etc. 00643 pGradFillCursor = new Cursor(this, _R(IDCSR_SEL_GRADPOINT)); 00644 pNormalCursor = new Cursor(this, _R(IDCSR_SEL_NORMAL)); 00645 pAdjustCursor = new Cursor(this, _R(IDCSR_SEL_ADJUST)); 00646 pUnderCursor = new Cursor(this, _R(IDCSR_SEL_UNDER)); 00647 pInsideCursor = new Cursor(this, _R(IDCSR_SEL_INSIDE)); 00648 pUnderAdjustCursor = new Cursor(this, _R(IDCSR_SEL_UNDERADJUST)); 00649 pInsideAdjustCursor = new Cursor(this, _R(IDCSR_SEL_INSIDEADJUST)); 00650 pALLCursor = new Cursor(this, _R(IDCSR_SEL_GRADPOINT)); 00651 pLeafCursor = new Cursor(this, _R(IDCSR_SEL_LEAF)); 00652 pLeafAdjustCursor = new Cursor(this, _R(IDCSR_SEL_LEAFADJUST)); 00653 pNWSECursor = new Cursor(this, _R(IDCSR_SIZENWSE)); 00654 pNESWCursor = new Cursor(this, _R(IDCSR_SIZENESW)); 00655 pNSCursor = new Cursor(this, _R(IDCSR_SIZENS)); 00656 pWECursor = new Cursor(this, _R(IDCSR_SIZEWE)); 00657 pDragRotateCursor = new Cursor(this, _R(IDCSR_SEL_GRADPOINT)); 00658 pHorzGuideCursor = new Cursor(this, _R(IDCSR_SEL_HGUIDE)); 00659 pVertGuideCursor = new Cursor(this, _R(IDCSR_SEL_VGUIDE)); 00660 00661 // This tool has just been selected. Push an appropriate cursor. 00662 CursorStackID = CursorStack::GPush(pNormalCursor); 00663 00664 // Initially we know of no selection, above or below. 00665 SelectionSpread = NULL; 00666 pClickSimpleNode = NULL; 00667 pClickCompoundNode = NULL; 00668 pLastClickNode = NULL; 00669 pPreProcClickNode = NULL; 00670 00671 // Reset these flags used within the click logic etc. 00672 fIsBlobDrag = fPossibleToggleClick = fIgnoreSelChange = FALSE; 00673 fAllowIdleProcessing = fMouseHasMoved = TRUE; 00674 00675 // Create and display my info bar please 00676 if (pInfoBarOp != NULL) 00677 { 00678 pInfoBarOp->pSelectorTool = this; 00679 if(pInfoBarOp->WindowID == NULL) 00680 { 00681 // set its pointer to me and call its create() function to get it to set its 00682 // controls 00683 pInfoBarOp->Create(); 00684 } 00685 else 00686 { 00687 pInfoBarOp->Open(); 00688 pInfoBarOp->MakeCurrent(); 00689 } 00690 } 00691 00693 // Version 1.0 of Camelot doesn't have ArtLines, so make sure their blobs 00694 // are always OFF. 00695 bsBlobStyle.Artistic = FALSE; 00696 bsBlobStyle.Effect = FALSE; 00698 00699 // Which blobs do I want displayed 00700 // >>>> Commented on advice from Rik 00701 // if (Document::GetCurrent() != NULL) 00702 // { 00703 // Tell the blob manager about our blobs. 00704 if (pBlobManager != NULL) 00705 pBlobManager->ToolInterest(bsBlobStyle); 00706 // } 00707 00708 // Note that whenever we switch to the selector we always start in bounds 00709 // rather than rotate mode. 00710 eCurrentBlobs = BOUNDS_BLOBS; 00711 SelectorInfoBarOp::fRotateMode = FALSE; 00712 if (pInfoBarOp != NULL) 00713 { 00714 // Make sure the info-bar reflects the state of this. 00715 pInfoBarOp->SetRotateMode(SelectorInfoBarOp::fRotateMode); 00716 } 00717 00718 // Get the initial selection, if any, and render the blobs. 00719 if (UpdateSelectionInfo()) pBlobManager->RenderToolBlobsOn(this, SelectionSpread, NULL); 00720 } 00721 else 00722 { 00723 // Deselection - pop the tool's cursor(s). 00724 CursorStack::GPop(CursorStackID); 00725 CursorStackID=CURSORID_UNSET; 00726 00727 // Delete the grad-fill cursor etc. 00728 delete pGradFillCursor; 00729 delete pNormalCursor; 00730 delete pInsideCursor; 00731 delete pAdjustCursor; 00732 delete pUnderCursor; 00733 delete pUnderAdjustCursor; 00734 delete pInsideAdjustCursor; 00735 delete pLeafCursor; 00736 delete pLeafAdjustCursor; 00737 delete pALLCursor; 00738 delete pNWSECursor; 00739 delete pNESWCursor; 00740 delete pNSCursor; 00741 delete pWECursor; 00742 delete pDragRotateCursor; 00743 delete pHorzGuideCursor; 00744 delete pVertGuideCursor; 00745 00746 // Use this flag to avoid an unsightly redraw when simply changing tools. 00747 fSelectorIsCaching = TRUE; 00748 00749 // Hide and destroy the info bar. 00750 if (pInfoBarOp != NULL) 00751 { 00752 // Make sure that the info bars pointer here is set to null and close itself down. 00753 // But NOT if the application is on the way out 00754 if (pInfoBarOp->HasWindow() && !GetApplication()->CamelotIsDying() ) 00755 pInfoBarOp->Close(); 00756 pInfoBarOp->pSelectorTool = NULL; 00757 pInfoBarOp->Delete(); 00758 } 00759 00760 fSelectorIsCaching = FALSE; 00761 00762 // ensure any tool object blobs are removed. 00763 if (pBlobManager != NULL) 00764 { 00765 BlobStyle bsRemoves; 00766 bsRemoves.ToolObject = TRUE; 00767 pBlobManager->RemoveInterest(bsRemoves); 00768 } 00769 00770 // If there is a spread with the selection on it, there is still a view, 00771 // and the tool blobs are being displayed, then render them off 00772 pBlobManager->RenderToolBlobsOff(this, SelectionSpread, NULL); 00773 } 00774 } 00775 00776 /******************************************************************************************** 00777 00778 > static void SelectorTool::UnCacheInfoBar(BOOL Force) 00779 00780 Author: Chris_Parks (Xara Group Ltd) <camelotdev@xara.com> 00781 Created: 13/10/94 00782 Inputs: Force Flag - will be deleted even if current 00783 Outputs: - 00784 Returns: - 00785 Purpose: deletes the cached infobar and sets it's ID to NULL -this is normally called 00786 when the selector is not the current tool - 00787 it can be forced however(on close down etc..) 00788 Errors: - 00789 SeeAlso: - 00790 00791 ********************************************************************************************/ 00792 00793 void SelectorTool::UnCacheInfoBar(BOOL Force) 00794 { 00795 if (pInfoBarOp) 00796 { 00797 // normally we would only uncache the infobar if we were not the current tool 00798 // but there are occasions when we might need to force it 00799 if (pInfoBarOp->pSelectorTool == NULL || Force) 00800 { 00801 pInfoBarOp->Delete(); 00802 pInfoBarOp->WindowID = NULL; 00803 } 00804 } 00805 } 00806 00807 00808 00809 00810 /******************************************************************************************** 00811 00812 > virtual void SelectorTool::OnMouseMove(DocCoord dcPos, Spread* pSpread, ClickModifiers mods) 00813 00814 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00815 Created: 31/8/94 00816 Inputs: dcPos the current position of the mouse (spread coord) 00817 pSpread the spread containing the mouse 00818 mods the mouse modifiers, eg. Adjust, Constrain etc 00819 Outputs: - 00820 Returns: - 00821 Purpose: Called by the tool system for the current tool when the mouse is moved 00822 outside of a drag. Checks if the mouse is over one of the tool's blobs, 00823 changing the cursor appropriately if it is. 00824 Errors: - 00825 SeeAlso: SelectorTool::ChangeCursor; SelectorTool::IsNearBlob 00826 00827 ********************************************************************************************/ 00828 00829 void SelectorTool::OnMouseMove(DocCoord dcPos, Spread* pSpread, ClickModifiers mods) 00830 { 00831 // Note that the mouse has moved so we know to update the status-bar text on the next 00832 // idle event. 00833 fMouseHasMoved = TRUE; 00834 00835 // Make sure the cursor reflects which keys are down. It is possible for the modifier 00836 // keys to change state without key-events being generated, so we must check this on 00837 // every mouse move. 00838 // ResetCursorNow(); 00839 // Make sure the cursor reflects which keys are down. It is possible for the modifier 00840 // keys to change state without key-events being generated, so we must check this on 00841 // every mouse move. 00842 // (That means prepare the global vars for us by GetClickModifiers in FigureUserFeedback) 00843 // StartSpread = pSpread; 00844 // ClickMods = mods; 00845 // ClickStart = dcPos; 00846 00847 // If there isn't any selection, or it's in a different spread, then do nothing. 00848 // if (SelectionSpread == NULL || SelectionSpread != pSpread) 00849 // return; 00850 00851 String_256 str; 00852 Cursor* pPointerShape; 00853 00854 FigureUserFeedback(pSpread, dcPos, mods, FALSE, &str, &pPointerShape); 00855 00856 if (!(str.IsEmpty())) 00857 SetStatusText(&str); 00858 00859 if (CursorStackID!=CURSORID_UNSET) 00860 CursorStack::GSetTop(pPointerShape,CursorStackID); 00861 } 00862 00863 00864 00865 /******************************************************************************************** 00866 00867 > virtual BOOL SelectorTool::OnKeyPress(KeyPress* pKey) 00868 00869 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 00870 Created: 22/11/94 00871 Inputs: pKey pointer to a key-press object representing a keystroke. 00872 Outputs: - 00873 Returns: TRUE if the key-event is handled, FALSE if not. 00874 Purpose: Called when a key is pressed of released. If the key is a "click modifier" 00875 such as the ALT or CTRL key then the cursor is changed to indicate whatever 00876 the tool can do with that modifier. 00877 Errors: - 00878 SeeAlso: - 00879 00880 ********************************************************************************************/ 00881 00882 BOOL SelectorTool::OnKeyPress(KeyPress* pKey) 00883 { 00884 // Find the current state of the "click" keyboard modifiers... 00885 ClickMods = ClickModifiers::GetClickModifiers(); 00886 00887 switch (pKey->GetVirtKey()) 00888 { 00889 00890 // >>>> BODGE! These cases are only here to prevent the messages 00891 // being passed on to other key handlers (very slow!) 00892 case CAMKEY(CC_MOD_ADJUST): // bit 0 of fKeyStates (SHIFT) 00893 break; 00894 00895 case CAMKEY(CC_MOD_ALTERNATIVE): // bit 1 of fKeyStates (ALT) 00896 break; 00897 00898 case CAMKEY(CC_MOD_CONSTRAIN): // bit 2 of fKeyStates (CONTROL) 00899 break; 00900 // >>>> End 00901 00902 case CAMKEY(TAB): // moves selection to next rendered node 00903 if( pKey->IsConstrain() ) 00904 return FALSE; // We have other uses for Ctrl+Tab (switch document) 00905 00906 if (pKey->IsPress()) HandleTabKey(ClickMods.Adjust); 00907 break; 00908 00909 case CAMKEY(1): // toggle tool blobs 00910 if (pKey->IsModified()) 00911 { 00912 // if Ctrl/Alt/Shift is down, pass the message on to another handler 00913 // instead of 'absorbing' the keypress 00914 return FALSE; 00915 } 00916 if (pKey->IsPress() && !pKey->IsModified()) 00917 { 00918 BoundsButtonChange(); 00919 if (pInfoBarOp != NULL) 00920 { 00921 pInfoBarOp->SetLongGadgetValue(_R(IDC_SEL_SHOWBOUNDSBLOBS), fShowToolBlobs, FALSE); 00922 } 00923 } 00924 break; 00925 00926 case CAMKEY(2): // toggle edit blobs 00927 if (pKey->IsModified()) 00928 { 00929 return FALSE; 00930 } 00931 if (pKey->IsPress() && !pKey->IsModified()) 00932 { 00933 BlobStyle bs(TRUE); 00934 SelectionBlobChange(bs); 00935 if (pInfoBarOp != NULL) 00936 { 00937 pInfoBarOp->SetLongGadgetValue(_R(IDC_SEL_SHOWOBJECTBLOBS), bsBlobStyle.Object, FALSE); 00938 } 00939 } 00940 break; 00941 00942 case CAMKEY(3): // toggle fill blobs 00943 if (pKey->IsModified()) 00944 { 00945 return FALSE; 00946 } 00947 if (pKey->IsPress() && !pKey->IsModified()) 00948 { 00949 BlobStyle bs(FALSE, FALSE, TRUE); 00950 SelectionBlobChange(bs); 00951 if (pInfoBarOp != NULL) 00952 { 00953 pInfoBarOp->SetLongGadgetValue(_R(IDC_SEL_SHOWFILLBLOBS), bsBlobStyle.Fill, FALSE); 00954 } 00955 } 00956 break; 00957 00958 case CAMKEY(HOME): // select first object in render order 00959 if (pKey->IsPress()) 00960 { 00961 if (SelectionSpread != NULL) NodeRenderableInk::DeselectAll(); 00962 HandleTabKey(FALSE); 00963 } 00964 break; 00965 00966 case CAMKEY(END): // select last object in render order 00967 if (pKey->IsPress()) 00968 { 00969 if (SelectionSpread != NULL) NodeRenderableInk::DeselectAll(); 00970 HandleTabKey(TRUE); 00971 } 00972 break; 00973 00974 case CAMKEY(4): // toggle bounds/rotate blobs 00975 if (pKey->IsModified()) 00976 { 00977 return FALSE; 00978 } 00979 if (pKey->IsPress() && !pKey->IsModified()) 00980 { 00981 RotateButtonChange(SelectorInfoBarOp::fRotateMode = !SelectorInfoBarOp::fRotateMode); 00982 if (pInfoBarOp != NULL) 00983 { 00984 pInfoBarOp->SetLongGadgetValue(_R(IDC_SEL_ROTATEBUTTON), SelectorInfoBarOp::fRotateMode, 00985 FALSE); 00986 } 00987 } 00988 break; 00989 00990 default: // not interested in processing this 00991 return FALSE; 00992 } 00993 00994 // If we processed a click modifier then update the cursor and return that we processed 00995 // the keystroke. 00996 SetKeyDownCursor(ClickMods); 00997 00998 // Yes, we processed this key-event. 00999 return TRUE; 01000 } 01001 01002 01003 01004 /******************************************************************************************** 01005 01006 > void SelectorTool::SetKeyDownCursor(ClickModifiers cmods) 01007 01008 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01009 Created: 1/12/94 01010 Inputs: - 01011 Outputs: - 01012 Returns: - 01013 Purpose: Decodes the bit-field fKeyStates, indicating which combination of modifier 01014 keys are down, and sets the cursor appropriately. 01015 Errors: - 01016 SeeAlso: SelectorTool::OnKeyPress; SelectorTool::OnMouseMove 01017 01018 ********************************************************************************************/ 01019 01020 void SelectorTool::SetKeyDownCursor(ClickModifiers cmods) 01021 { 01022 // Get current position information for call to change pointer shape... 01023 Spread* pSpread; 01024 DocCoord dcMousePos; 01025 if( DocView::GetCurrentMousePos( &pSpread, &dcMousePos ) && 01026 Tool::GetCurrentID()== TOOLID_SELECTOR ) 01027 PORTNOTE("other", "Removed bar drag usage") 01028 #ifndef EXCLUDE_FROM_XARALX 01029 && !BaseBar::IsDragging()) 01030 #endif 01031 { 01032 // StartSpread = pSpread; 01033 // ClickStart = dcMousePos; 01034 // Call nice central routine to figure out what pointer shape to show... 01035 // (Set the status bar text while we're at it.) 01036 String_256 Str; 01037 Cursor* pPtr; 01038 FigureUserFeedback(pSpread, dcMousePos, cmods, TRUE, &Str, &pPtr); 01039 if (CursorStackID!=CURSORID_UNSET) 01040 CursorStack::GSetTop(pPtr,CursorStackID); 01041 if (!(Str.IsEmpty())) 01042 SetStatusText( &Str ); 01043 } 01044 } 01045 01046 01047 01048 /******************************************************************************************** 01049 01050 > void SelectorTool::ResetCursorNow() 01051 01052 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01053 Created: 13/1/95 01054 Inputs: - 01055 Outputs: - 01056 Returns: - 01057 Purpose: Called when a mouse button-up event or end-of-drag event occurs. It resets 01058 the cursor according to which modifier keys are up or down, without a key 01059 event having to take place. 01060 Errors: - 01061 SeeAlso: SelectorTool::SetKeyDownCursor; SelectorTool::HandleDragClick; 01062 SelectorTool::HandleButtonUp; SelectorTool::OnMouseMove 01063 01064 ********************************************************************************************/ 01065 01066 void SelectorTool::ResetCursorNow() 01067 { 01068 // Make sure the cursor reflects which keys are down. It is possible for the modifier 01069 // keys to change state without key-events being generated, so we must check this on 01070 // every mouse move. 01071 SetKeyDownCursor(ClickModifiers::GetClickModifiers()); 01072 } 01073 01074 01075 01076 /******************************************************************************************** 01077 01078 > static void SelectorTool::AllowIdleWork(BOOL fIsAllowed) 01079 01080 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01081 Created: 14/10/94 01082 Inputs: fIsAllowed if TRUE then the tool will act on idle events, if FALSE 01083 the tool will ignore idle events. 01084 Outputs: - 01085 Returns: - 01086 Purpose: Turns idle processing on or off. During idle events the selector tool 01087 updates the status bar, so if a drag operation, for instance, wants to 01088 place its own text in the status bar while it is running it should call 01089 this function at its start and at its end. 01090 Errors: - 01091 SeeAlso: SelectorTool::OnIdle 01092 01093 ********************************************************************************************/ 01094 01095 void SelectorTool::AllowIdleWork(BOOL fIsAllowed) 01096 { 01097 // Make sure the selector is the current tool. 01098 ERROR3IF(Tool::GetCurrent() == NULL || Tool::GetCurrent()->GetID() != TOOLID_SELECTOR, 01099 "Selector isn't current tool in SelectorTool::AllowIdleWork"); 01100 01101 if (Tool::GetCurrent() != NULL && Tool::GetCurrent()->GetID() == TOOLID_SELECTOR) 01102 { 01103 // Set the flag. 01104 #ifndef SELECTION_AREA_FEATURE 01105 ((SelectorTool*) Tool::GetCurrent())->fAllowIdleProcessing = fIsAllowed; 01106 #else 01107 ((SelectorTool*) Tool::GetCurrent())->fAllowIdleProcessing = fIsAllowed; 01108 // ((SelectorTool*) Tool::GetCurrent())->fAllowIdleProcessing = (fIsAllowed || m_bComputeAreaDetails); 01109 #endif 01110 } 01111 // >>>> 01112 //((SelectorTool*) Tool::GetCurrent())->fAllowIdleProcessing = FALSE; 01113 } 01114 01115 01116 01117 /******************************************************************************************** 01118 01119 > virtual BOOL SelectorTool::OnIdle() 01120 01121 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01122 Created: 12/10/94 01123 Inputs: - 01124 Outputs: - 01125 Returns: TRUE if idle events are still required (currently always returns TRUE). 01126 Purpose: Called on idle events. Performs hit-detection on the current mouse position 01127 and updates the status-bar text accordingly. 01128 Errors: - 01129 SeeAlso: - 01130 01131 ********************************************************************************************/ 01132 01133 BOOL SelectorTool::OnIdle() 01134 { 01135 #ifdef SELECTION_AREA_FEATURE 01136 if (m_bComputeAreaDetails) 01137 { 01138 BOOL bNeedsMoreTime = SelectRange->GetAreaDetailsWhileIdle(&xlSelectionArea, &xlSelectionPerim); 01139 if (!bNeedsMoreTime) 01140 { 01141 pInfoBarOp->SetEdit_OnSelectionChange(); 01142 pInfoBarOp->UpdateAllRecords(); 01143 m_bComputeAreaDetails = FALSE; 01144 } 01145 } 01146 #endif 01147 01148 return FALSE; 01149 /* // If the cursor is already over a blob then do nothing, as OnMouseMove will have 01150 // handled it already. We also have a "global" flag to turn idle processing on or off, 01151 // particularly useful during the running of drag Operations. 01152 // if (!fMouseHasMoved || !fAllowIdleProcessing || fCursorOverBlob || fCursorOverGradFillBlob) 01153 return TRUE; 01154 01155 // Clear this flag, so that we remember to check again next time. 01156 fMouseHasMoved = FALSE; 01157 01158 // Make sure the selected Doc is current as well. If there isn't one then what are we 01159 // doing here? 01160 Document* pdoc = Document::GetSelected(); 01161 if (pdoc != NULL) pdoc->SetCurrent(); else return TRUE; 01162 01163 // Check if the mouse is within the "selected" DocView. 01164 Spread* pSpread; 01165 DocCoord dcMousePos; 01166 if (DocView::GetCurrentMousePos(&pSpread, &dcMousePos)) 01167 { 01168 // Call nice central routine to figure out what status text to show... 01169 // Set the status bar text. 01170 String_256 Str; 01171 Cursor* pPtr; 01172 FigureUserFeedback(pSpread, dcMousePos, TRUE, &Str, &pPtr); 01173 SetStatusText( &Str ); 01174 } 01175 01176 // We always want lots more idle time, please. 01177 return TRUE; 01178 */ 01179 } 01180 01181 01182 01183 01184 /******************************************************************************************** 01185 01186 > void SelectorTool::FigureUserFeedback(Spread* pSpread, 01187 DocCoord dcPos, 01188 ClickModifiers cmods, 01189 BOOL DoSlowTests, 01190 String_256* pStr, 01191 Cursor** ppPointerShape 01192 ) 01193 01194 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01195 Created: 16/01/95 01196 Inputs: ? 01197 Outputs: ? 01198 Returns: - 01199 Purpose: This routine computes the status line help and the pointer shape for any 01200 given mouse position in the Selector tool. It does NOT set either of these 01201 values but returns them both to the caller. It's up to the caller to decide 01202 whether to use these values or not. 01203 The tests done to figure out which pointer shapes, status help (and maybe 01204 other stuff one day) is quite complex so it's most efficient to compute them 01205 all at once and then let the caller decide which ones to actually use. 01206 The precedence of pointer shapes, status messages and other user feedback is 01207 determined by the order of the checks within this function (not by flags any 01208 more!). 01209 Errors: - 01210 SeeAlso: - 01211 01212 ********************************************************************************************/ 01213 01214 void SelectorTool::FigureUserFeedback(Spread* pSpread, 01215 DocCoord dcPos, 01216 ClickModifiers cmods, 01217 BOOL DoSlowTests, 01218 String_256* pStr, 01219 Cursor** ppPointerShape 01220 ) 01221 { 01222 // Initialise the status text string and pointer shape to be "null". 01223 pStr->Empty(); 01224 *ppPointerShape = pNormalCursor; 01225 01226 if (Tool::GetCurrentID()!=TOOLID_SELECTOR) 01227 return; 01228 01229 //------------------------------------------------------------------------- 01230 // Blob cursors are top priority 01231 // 01232 // Check with the fill tool if the cursor is over one of its blobs (it's smart enough 01233 // to check if fill blobs are being displayed or not). 01234 UINT32 nStatusTextID; 01235 if (AttrFillGeometry::CheckForFillControlHit(dcPos, &nStatusTextID)) 01236 { 01237 *ppPointerShape = pGradFillCursor; 01238 *pStr = String_256(nStatusTextID); 01239 return; 01240 } 01241 01242 // If we aren't displaying our own tool blobs then don't do anything. 01243 if (fShowToolBlobs && !(SelectionRect.IsEmpty())) 01244 { 01245 // First check if we've hit the rotation centre. 01246 if (IsRotateCentreClicked(dcPos)) 01247 { 01248 ChangeCursorAndText(9, pStr, ppPointerShape); 01249 return; 01250 } 01251 01252 // Check if we hit any other bounds/rotate blob. 01253 INT32 nBlob = BoundsBlobHitTest(dcPos); 01254 if (nBlob != 0) 01255 { 01256 ChangeCursorAndText(nBlob, pStr, ppPointerShape); 01257 return; 01258 } 01259 } 01260 01261 BOOL DescribeDrag = TRUE; 01262 01263 //------------------------------------------------------------------------- 01264 // Next in priority order is the setting of the pointer shape due to 01265 // key modifiers 01266 if (!cmods.Adjust && !cmods.Constrain && !cmods.Alternative1) 01267 { 01268 *ppPointerShape = pNormalCursor; 01269 Append(pStr,T(_R(IDS_SEL_MODE0),"Normal select mode:")); 01270 goto FigureModeSet; 01271 } 01272 01273 if (cmods.Adjust && !cmods.Constrain && !cmods.Alternative1) 01274 { 01275 *ppPointerShape = pAdjustCursor; 01276 Append(pStr,T(_R(IDS_SEL_MODE1),"Adjust select:")); 01277 goto FigureModeSet; 01278 } 01279 01280 if (!cmods.Adjust && IsSelectUnderClick(cmods)) 01281 { 01282 *ppPointerShape = pUnderCursor; 01283 Append(pStr,T(_R(IDS_SEL_MODE2),"Select under:")); 01284 goto FigureModeSet; 01285 } 01286 01287 if (cmods.Adjust && IsSelectUnderClick(cmods)) 01288 { 01289 *ppPointerShape = pUnderAdjustCursor; 01290 Append(pStr,T(_R(IDS_SEL_MODE3),"Adjust select under:")); 01291 goto FigureModeSet; 01292 } 01293 01294 if (!cmods.Adjust && IsSelectMemberClick(cmods)) 01295 { 01296 *ppPointerShape = pInsideCursor; 01297 Append(pStr,T(_R(IDS_SEL_MODE4),"Force drag/Select member:")); 01298 goto FigureModeSet; 01299 } 01300 01301 if (cmods.Adjust && IsSelectMemberClick(cmods)) 01302 { 01303 *ppPointerShape = pInsideAdjustCursor; 01304 Append(pStr,T(_R(IDS_SEL_MODE5),"Force drag/Adjust select member:")); 01305 goto FigureModeSet; 01306 } 01307 01308 if (!cmods.Adjust && IsSelectLeafClick(cmods)) 01309 { 01310 *ppPointerShape = pLeafCursor; 01311 Append(pStr,T(_R(IDS_SEL_MODE6),"Select inside:")); 01312 goto FigureModeSet; 01313 } 01314 01315 if (cmods.Adjust && IsSelectLeafClick(cmods)) 01316 { 01317 *ppPointerShape = pLeafAdjustCursor; 01318 Append(pStr,T(_R(IDS_SEL_MODE7),"Adjust select inside:")); 01319 goto FigureModeSet; 01320 } 01321 01322 ERROR3("Out-of-range modifier combination in SelectorTool::FigureUserFeedback?!?"); 01323 return; 01324 01325 FigureModeSet: 01326 // Check for direct drag mode and say something about it... 01327 if (IsTranslateShortcut(cmods)) 01328 { 01329 if (SelectRange->FindFirst()) 01330 { 01331 *ppPointerShape = pALLCursor; 01332 Append(pStr,T(_R(IDS_SELINSIDE4),"Drag to move the selected objects")); 01333 } 01334 DescribeDrag = FALSE; 01335 } 01336 01337 01338 // See if there's a non-selectable object (guideline) under the mouse 01339 NodeRenderableInk* pPreProNode = FindPreProcessClickNode(pSpread,dcPos,TRUE); 01340 if (pPreProNode) 01341 { 01342 if (IS_A(pPreProNode,NodeGuideline)) 01343 { 01344 NodeGuideline* pGuideline = (NodeGuideline*)pPreProNode; 01345 if (pGuideline->GetType()==GUIDELINE_HORZ) 01346 { 01347 *ppPointerShape = pHorzGuideCursor; 01348 Append(pStr,T(_R(IDS_SELHORZGUIDE),"Drag up or down to move guideline; Drag onto top ruler to delete it")); 01349 return; 01350 } 01351 01352 if (pGuideline->GetType()==GUIDELINE_VERT) 01353 { 01354 *ppPointerShape = pVertGuideCursor; 01355 Append(pStr,T(_R(IDS_SELVERTGUIDE),"Drag left or right to move guideline; Drag onto left ruler to delete it")); 01356 return; 01357 } 01358 } 01359 } 01360 01361 //------------------------------------------------------------------------- 01362 // If we've got time to do the slow tests then go ahead and do them... 01363 if (DoSlowTests) 01364 { 01365 // Perform a hit-test at the mouse position and set the status bar text 01366 // accordingly. 01367 // Allow the hit-test to be interrupted if the mouse moves! 01368 Node* pInterruptedNode = NULL; 01369 NodeRenderableInk* pSimple = NodeRenderableInk::FindSimpleAtPoint(pSpread, 01370 dcPos, 01371 NULL, 01372 &pInterruptedNode); 01373 // If hit-test was interrupted then don't say anything about what's under the pointer! 01374 if (pInterruptedNode!=NULL) 01375 return; 01376 01377 NodeRenderableInk* pCompound = NodeRenderableInk::FindCompoundFromSimple(pSimple); 01378 01379 // If the SelRange thinks it knows what the last selected node was 01380 // we'll go along with that, otherwise we'll use our own record... 01381 Node* pLastSelNode = SelectRange->GetLastSelectedNode(); 01382 if ( pLastSelNode==NULL || !pLastSelNode->IsAnObject() ) 01383 { 01384 pLastSelNode = pLastClickNode; 01385 } 01386 01387 // Find out what we should do with the click... 01388 ClickActionCode action = CLICKACTION_NONE; 01389 NodeRenderableInk* pActionNode = NULL; 01390 action = DetermineClickAction(&pActionNode,(NodeRenderableInk*)pLastSelNode, 01391 pSimple,pCompound,pSpread,dcPos,cmods); 01392 01393 // Act upon the information... 01394 switch (action) 01395 { 01396 //-------------------------------------------------// 01397 // No action required... 01398 case CLICKACTION_NONE: 01399 break; 01400 01401 //-------------------------------------------------// 01402 // A Bounds handle was clicked on... 01403 case CLICKACTION_BOUNDTOPLEFT: 01404 case CLICKACTION_BOUNDTOP: 01405 case CLICKACTION_BOUNDTOPRIGHT: 01406 case CLICKACTION_BOUNDLEFT: 01407 case CLICKACTION_BOUNDRIGHT: 01408 case CLICKACTION_BOUNDBOTTOMLEFT: 01409 case CLICKACTION_BOUNDBOTTOM: 01410 case CLICKACTION_BOUNDBOTTOMRIGHT: 01411 case CLICKACTION_BOUNDTRANSFORMORIGIN: 01412 break; 01413 01414 //-------------------------------------------------// 01415 // Anything that's selected must be deselected... 01416 case CLICKACTION_SELNONE: 01417 if (!cmods.Adjust) 01418 { 01419 // If there are selected objects append message about clearing. 01420 if (SelectRange && SelectRange->FindFirst()) 01421 Append(pStr,T(_R(IDS_SELNONE1),"Click to clear the selection")); 01422 // Message about marquee drag 01423 if (DescribeDrag) 01424 Append(pStr,T(_R(IDS_SELNONE4),"Drag to marquee select objects")); 01425 Append(pStr,T(_R(IDS_SELNONE2),"Move pointer over object to select")); 01426 } 01427 else 01428 { 01429 // Adjust is held down so describe marquee add. 01430 if (DescribeDrag) 01431 Append(pStr,T(_R(IDS_SELNONE5),"Drag to marquee select objects to add to selection")); 01432 Append(pStr,T(_R(IDS_SELNONE3),"Move pointer over object to add/remove from selection")); 01433 } 01434 break; 01435 01436 //-------------------------------------------------// 01437 // The action node must be selected or toggled... 01438 case CLICKACTION_SELNODE: 01439 if (fShowToolBlobs && !cmods.Adjust && pActionNode->IsSelected()) 01440 if (SelectorInfoBarOp::fRotateMode) 01441 Append(pStr,T(_R(IDS_SELNODE1),"Click to change to scale & stretch bounds mode; Drag to move the selection")); 01442 else 01443 Append(pStr,T(_R(IDS_SELNODE2),"Click to change to rotate & skew bounds mode; Drag to move the selection")); 01444 else 01445 Append(pStr,cmods, 01446 T(_R(IDS_SELNODE3),"Click to select this #1%S alone; Drag to move it"), 01447 T(_R(IDS_SELNODE4),"Click to select this #1%S"), 01448 T(_R(IDS_SELNODE5),"Click to deselect this #1%S"), 01449 pActionNode); 01450 break; 01451 case CLICKACTION_SELUNDER: 01452 Append(pStr,cmods, 01453 T(_R(IDS_SELUNDER1),"Click to select the #1%S under the last selected object alone"), 01454 T(_R(IDS_SELUNDER2),"Click to select the #1%S under the last selected object"), 01455 T(_R(IDS_SELUNDER3),"Click to deselect the #1%S under the last selected object"), 01456 pActionNode); 01457 break; 01458 case CLICKACTION_SELUNDERCYCLE: 01459 Append(pStr,cmods, 01460 T(_R(IDS_SELUNDERCYCLE1),"Click to select the top #1%S alone; (Reached the bottom)"), 01461 T(_R(IDS_SELUNDERCYCLE2),"Click to select the top #1%S; (Reached the bottom)"), 01462 T(_R(IDS_SELUNDERCYCLE3),"Click to deselect the top #1%S; (Reached the bottom)"), 01463 pActionNode); 01464 break; 01465 case CLICKACTION_SELUNDERFAIL: 01466 Append(pStr,cmods, 01467 T(_R(IDS_SELUNDERFAIL1),"Click to select the #1%S alone; (Nothing under the last selected object)"), 01468 T(_R(IDS_SELUNDERFAIL2),"Click to select the #1%S; (Nothing under the last selected object)"), 01469 T(_R(IDS_SELUNDERFAIL3),"Click to deselect the #1%S; (Nothing under the last selected object)"), 01470 pActionNode); 01471 break; 01472 case CLICKACTION_SELUNDERFAIL2: 01473 Append(pStr,cmods, 01474 T(_R(IDS_SELUNDERFAIL21),"Click to select the #1%S alone; (The last selected object is not under the pointer)"), 01475 T(_R(IDS_SELUNDERFAIL22),"Click to select the #1%S; (The last selected object is not under the pointer)"), 01476 T(_R(IDS_SELUNDERFAIL23),"Click to deselect the #1%S; (The last selected object is not under the pointer)"), 01477 pActionNode); 01478 break; 01479 case CLICKACTION_SELINSIDE: 01480 // Append(pStr,T(_R(IDS_SELINSIDE4),"Drag to move selected objects")); <<<< 01481 Append(pStr,cmods, 01482 T(_R(IDS_SELINSIDE1),"Click to select the #1%S member of the last selected object alone"), 01483 T(_R(IDS_SELINSIDE2),"Click to select the #1%S member of the last selected object"), 01484 T(_R(IDS_SELINSIDE3),"Click to deselect the #1%S member of the last selected object"), 01485 pActionNode); 01486 break; 01487 case CLICKACTION_SELINSIDECYCLE: 01488 // Append(pStr,T(_R(IDS_SELINSIDECYCLE4),"Drag to move selected objects")); <<<< 01489 Append(pStr,cmods, 01490 T(_R(IDS_SELINSIDECYCLE1),"Click to select the top #1%S alone; (Reached the simplest object)"), 01491 T(_R(IDS_SELINSIDECYCLE2),"Click to select the top #1%S; (Reached the simplest object)"), 01492 T(_R(IDS_SELINSIDECYCLE3),"Click to deselect the top #1%S; (Reached the simplest object)"), 01493 pActionNode); 01494 break; 01495 case CLICKACTION_SELINSIDEFAIL: 01496 // Append(pStr,T(_R(IDS_SELINSIDEFAIL2),"Drag to move selected objects")); <<<< 01497 Append(pStr,T(_R(IDS_SELINSIDEFAIL1),"Nothing inside this object")); 01498 break; 01499 case CLICKACTION_SELINSIDEFAIL2: 01500 // Append(pStr,T(_R(IDS_SELINSIDEFAIL24),"Drag to move selected objects")); <<<< 01501 Append(pStr,cmods, 01502 T(_R(IDS_SELINSIDEFAIL21),"Click to select the #1%S alone; (The pointer is not over a member of the last selected object)"), 01503 T(_R(IDS_SELINSIDEFAIL22),"Click to select the #1%S; (The pointer is not over a member of the last selected object)"), 01504 T(_R(IDS_SELINSIDEFAIL23),"Click to deselect the #1%S; (The pointer is not over a member of the last selected object)"), 01505 pActionNode); 01506 break; 01507 case CLICKACTION_SELLEAF: 01508 Append(pStr,cmods, 01509 T(_R(IDS_SELLEAF1),"Click to select this #1%S alone"), 01510 T(_R(IDS_SELLEAF2),"Click to select this #1%S"), 01511 T(_R(IDS_SELLEAF3),"Click to deselect this #1%S"), 01512 pActionNode); 01513 break; 01514 //-------------------------------------------------// 01515 default: 01516 ERROR3("Unknown Click action code!"); 01517 break; 01518 }; 01519 01520 // If we're in normal click mode (no modifiers down) then remind the user 01521 // that they can use the modifer keys... 01522 if (!cmods.Adjust && !cmods.Constrain && !cmods.Alternative1) 01523 Append(pStr,_R(IDS_SELOPTIONS)); 01524 } 01525 } 01526 01527 01528 01529 01530 /******************************************************************************************** 01531 01532 > BOOL SelectorTool::Append(String_256* pStr, ClickModifiers cmods, UINT32 resID, NodeRenderableInk* pActionNode = NULL) 01533 01534 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01535 Created: 31/5/95 01536 Inputs: - 01537 Outputs: - 01538 Returns: - 01539 Purpose: - 01540 Errors: - 01541 01542 ********************************************************************************************/ 01543 01544 BOOL SelectorTool::Append(String_256* pStr, ClickModifiers cmods, 01545 UINT32 SelectID, 01546 UINT32 AddID, 01547 UINT32 RemoveID, 01548 NodeRenderableInk* pActionNode) 01549 { 01550 ERROR2IF(SelectID==0,FALSE,"Asked to append a string resource with a null ID"); 01551 01552 // Append a message to the string, preceding it with a separator if there was something 01553 // already in the string... 01554 if (!pStr->IsEmpty()) 01555 *pStr += String_256(_R(IDS_SEL_SEPARATOR)); 01556 01557 String_256 Message; 01558 UINT32 TemplateID = 0; 01559 Message.Empty(); 01560 01561 if (!cmods.Adjust || pActionNode==NULL) 01562 TemplateID = SelectID; 01563 else 01564 { 01565 if (!pActionNode->IsSelected()) 01566 TemplateID = AddID; 01567 else 01568 TemplateID = RemoveID; 01569 } 01570 01571 if (TemplateID==0) 01572 TemplateID = SelectID; 01573 01574 if (pActionNode==NULL) 01575 *pStr += String_256(TemplateID); 01576 else 01577 { 01578 String_256 sTemplateID(TemplateID); 01579 String_256 NodeDesc = pActionNode->Describe(FALSE); 01580 Message._MakeMsg( (TCHAR*) sTemplateID, &NodeDesc); 01581 *pStr += Message; 01582 } 01583 01584 return TRUE; 01585 } 01586 01587 01588 01589 01590 /******************************************************************************************** 01591 01592 > BOOL SelectorTool::Append(String_256* pStr, UINT32 resID) 01593 01594 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01595 Created: 31/5/95 01596 Inputs: - 01597 Outputs: - 01598 Returns: - 01599 Purpose: - 01600 Errors: - 01601 01602 ********************************************************************************************/ 01603 01604 BOOL SelectorTool::Append(String_256* pStr, UINT32 StringID) 01605 { 01606 // Append a message to the string, preceding it with a separator if there was something 01607 // already in the string... 01608 if (!pStr->IsEmpty()) 01609 *pStr += String_256(_R(IDS_SEL_SEPARATOR)); 01610 *pStr += String_256(StringID); 01611 01612 return TRUE; 01613 } 01614 01615 01616 01617 01618 /******************************************************************************************** 01619 01620 > BOOL SelectorTool::Append(String_256* pStr, String_256 String) 01621 01622 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01623 Created: 31/5/95 01624 Inputs: - 01625 Outputs: - 01626 Returns: - 01627 Purpose: - 01628 Errors: - 01629 01630 ********************************************************************************************/ 01631 01632 BOOL SelectorTool::Append(String_256* pStr, String_256 String) 01633 { 01634 // Append a message to the string, preceding it with a separator if there was something 01635 // already in the string... 01636 if (!pStr->IsEmpty()) 01637 *pStr += String_256(_R(IDS_SEL_SEPARATOR)); 01638 *pStr += String; 01639 01640 return TRUE; 01641 } 01642 01643 01644 01645 01646 /******************************************************************************************** 01647 01648 > BOOL SelectorTool::Append(String_256* pStr, 01649 String_256 SelectTemplate, 01650 String_256 AddTemplate, 01651 String_256 RemoveTemplate, 01652 NodeRenderableInk* pActionNode) 01653 01654 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01655 Created: 01/06/95 01656 Inputs: - 01657 Outputs: - 01658 Returns: - 01659 Purpose: - 01660 Errors: - 01661 01662 ********************************************************************************************/ 01663 01664 BOOL SelectorTool::Append(String_256* pStr, 01665 ClickModifiers cmods, 01666 String_256 SelectTemplate, 01667 String_256 AddTemplate, 01668 String_256 RemoveTemplate, 01669 NodeRenderableInk* pActionNode) 01670 { 01671 ERROR2IF(SelectTemplate.IsEmpty(),FALSE,"Asked to Append an empty message"); 01672 01673 // Append a message to the string, preceding it with a separator if there was something 01674 // already in the string... 01675 if (!pStr->IsEmpty()) 01676 *pStr += String_256(_R(IDS_SEL_SEPARATOR)); 01677 01678 String_256 Message; 01679 String_256* pTemplate; 01680 Message.Empty(); 01681 01682 if (!cmods.Adjust || pActionNode==NULL) 01683 pTemplate = &SelectTemplate; 01684 else 01685 { 01686 if (!pActionNode->IsSelected()) 01687 pTemplate = &AddTemplate; 01688 else 01689 pTemplate = &RemoveTemplate; 01690 } 01691 01692 if (pTemplate->IsEmpty()) 01693 pTemplate = &SelectTemplate; 01694 01695 if (pActionNode==NULL) 01696 *pStr += *pTemplate; 01697 else 01698 { 01699 String_256 NodeDesc = pActionNode->Describe(FALSE); 01700 Message._MakeMsg( (TCHAR*) *pTemplate, &NodeDesc ); 01701 *pStr += Message; 01702 } 01703 01704 return TRUE; 01705 } 01706 01707 01708 01709 01710 /******************************************************************************************** 01711 01712 > void SelectorTool::ChangeCursorAndText(INT32 nBlobID, String_256* pStr, Cursor** ppPointerShape) 01713 01714 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01715 Created: 31/8/94 01716 Inputs: nBlobID the numeric ID of the blob the mouse is over. 01717 Outputs: - 01718 Returns: - 01719 Purpose: Changes the cursor shape according to thew nBlobID parameter. Each blob 01720 has its own cursor shape. 01721 Errors: - 01722 SeeAlso: SelectorTool::OnMouseMove 01723 01724 ********************************************************************************************/ 01725 01726 void SelectorTool::ChangeCursorAndText(INT32 nBlobID, String_256* pStr, Cursor** ppPointerShape) 01727 { 01728 // So we can use just one switch table, just for this function, we give bounds and rotate 01729 // blobs distinct numeric ID's. 01730 if (eCurrentBlobs == ROTATE_BLOBS) nBlobID += 10; 01731 01732 // Choose an appropriate cursor. If the aspect ratio is locked then the cursor over corner 01733 // blobs is different. 01734 Cursor* pNewCursor; 01735 UINT32 nStringID; 01736 BOOL fLocked = pInfoBarOp->IsAspectLocked(); 01737 switch (nBlobID) 01738 { 01739 // Bounds: top-left & bottom-right. 01740 case 1: case 8: 01741 pNewCursor = fLocked ? pNWSECursor : pALLCursor; 01742 nStringID = _R(IDS_SEL_BOUNDSTEXT_1836); 01743 break; 01744 01745 // Bounds: top-middle & bottom-middle. 01746 case 2: case 7: 01747 pNewCursor = pNSCursor; 01748 nStringID = _R(IDS_SEL_BOUNDSTEXT_2745); 01749 break; 01750 01751 // Bounds: top-right & bottom-left. 01752 case 3: case 6: 01753 pNewCursor = fLocked ? pNESWCursor : pALLCursor; 01754 nStringID = _R(IDS_SEL_BOUNDSTEXT_1836); 01755 break; 01756 01757 // Bounds: left-middle & right-middle. 01758 case 4: case 5: 01759 pNewCursor = pWECursor; 01760 nStringID = _R(IDS_SEL_BOUNDSTEXT_2745); 01761 break; 01762 01763 // Rotate: top-left & bottom-right. 01764 case 11: case 18: 01765 pNewCursor = pNESWCursor; 01766 nStringID = _R(IDS_SEL_ROTATETEXT_1836); 01767 break; 01768 01769 // Rotate: top-middle & bottom-middle. 01770 case 12: case 17: 01771 pNewCursor = pWECursor; 01772 nStringID = _R(IDS_SEL_ROTATETEXT_2745); 01773 break; 01774 01775 // Rotate: top-right & bottom-left. 01776 case 13: case 16: 01777 pNewCursor = pNWSECursor; 01778 nStringID = _R(IDS_SEL_ROTATETEXT_1836); 01779 break; 01780 01781 // Rotate: left-middle & right-middle. 01782 case 14: case 15: 01783 pNewCursor = pNSCursor; 01784 nStringID = _R(IDS_SEL_ROTATETEXT_2745); 01785 break; 01786 01787 // Rotate: centre of rotation. 01788 case 9: case 19: 01789 pNewCursor = pDragRotateCursor; 01790 nStringID = _R(IDS_SEL_ROTATETEXT_CENTRE); 01791 break; 01792 01793 default: 01794 ENSURE(FALSE, "Bad blob ID in SelectorTool::ChangeCursor"); 01795 return; 01796 } 01797 01798 // Set the new cursor and the status bar text. 01799 *pStr = String_256(nStringID); 01800 *ppPointerShape = pNewCursor; 01801 01802 } 01803 01804 01805 01806 /******************************************************************************************** 01807 01808 > static void SelectorTool::SetStatusText(String_256* pStr) 01809 01810 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01811 Created: 13/10/94 01812 Inputs: pStr pointer to the string to display 01813 Outputs: - 01814 Returns: - 01815 Purpose: Sets the status bar text to the given string. 01816 Errors: - 01817 SeeAlso: - 01818 01819 ********************************************************************************************/ 01820 01821 void SelectorTool::SetStatusText(String_256* pStr) 01822 { 01823 GetApplication()->UpdateStatusBarText(pStr); 01824 } 01825 01826 01827 01828 /******************************************************************************************** 01829 01830 > static void SelectorTool::SetStatusText(UINT32 nStringID) 01831 01832 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01833 Created: 12/10/94 01834 Inputs: nStringID the numeric identifier of the string resource 01835 Outputs: - 01836 Returns: - 01837 Purpose: Sets the status bar text to the given string. Alternative to loading the 01838 string yourself and calling the other SetStatusText function. 01839 Errors: - 01840 SeeAlso: - 01841 01842 ********************************************************************************************/ 01843 01844 void SelectorTool::SetStatusText(UINT32 nStringID) 01845 { 01846 String_256 str(nStringID); 01847 SetStatusText(&str); 01848 } 01849 01850 01851 01852 01853 /******************************************************************************************** 01854 01855 > BOOL SelectorTool::GetStatusLineText(String_256* ptext, Spread* pSpread, 01856 DocCoord DocPos, ClickModifiers ClickMods) 01857 01858 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01859 Created: 16/01/95 01860 Inputs: ptext Pointer to text string to fill in 01861 pSpread Pointer to spread containing mouse position 01862 DocPos Mouse position within spread 01863 ClickMods Click modifiers 01864 Outputs: - 01865 Returns: - 01866 Purpose: Figure out what the status text for the Selector tool is at the given position 01867 on the given spread with the given click modifiers. 01868 Errors: - 01869 SeeAlso: - 01870 01871 ********************************************************************************************/ 01872 01873 BOOL SelectorTool::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers cmods) 01874 { 01875 ERROR2IF(ptext==NULL,FALSE,"ptext is NULL!"); 01876 01877 // StartSpread = pSpread; 01878 // ClickStart = DocPos; 01879 // ClickMods = cmods; 01880 01881 Cursor* pPtr; // Dummy to hold unused pointer shape computed by FigureUserFeedback 01882 FigureUserFeedback(pSpread, DocPos, cmods, TRUE, ptext, &pPtr); 01883 01884 if (CursorStackID!=CURSORID_UNSET) 01885 CursorStack::GSetTop(pPtr,CursorStackID); 01886 01887 return TRUE; 01888 } 01889 01890 01891 01892 01893 /******************************************************************************************** 01894 01895 > virtual void SelectorTool::OnClick(DocCoord dcPos, ClickType ctType, 01896 ClickModifiers cmMods, Spread* pSpread) 01897 01898 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01899 Created: 5/8/94 01900 Inputs: dcPos the position of the mouse when the click event happened 01901 ctType the type of mouse click, eg. CLICKTYPE_DRAG 01902 cmMods which buttons & keys were down 01903 pSpread points to the spread that was clicked 01904 Outputs: - 01905 Returns: - 01906 Purpose: Dispatches mouse events to the proper handler. The tool is given a chance 01907 to handle an event first, which allows it to override the usual behaviour 01908 if necessary. If it doesn't handle the event the selected object(s) are 01909 given a chance. Finally, any events still not handled are given to the 01910 selector tool to deal with. 01911 Errors: - 01912 SeeAlso: SelectorTool::PreProcessClick; SelectorTool::ProcessObjectClick; 01913 SelectorTool::PostProcessClick 01914 01915 ********************************************************************************************/ 01916 01917 void SelectorTool::OnClick(DocCoord dcPos, ClickType ctType, ClickModifiers cmMods, Spread* pSpread) 01918 { 01919 // This divides the trace output into "click events". 01920 /* if (IsUserName("Phil")) 01921 { 01922 TRACE( _T("===========================================================================\n")); 01923 TRACE( _T("OnClick - Modifiers: ")); 01924 if (cmMods.Adjust) TRACE( _T("Adjust ")); 01925 if (cmMods.Constrain) TRACE( _T("Constrain ")); 01926 if (cmMods.Menu) TRACE( _T("Menu ")); 01927 if (cmMods.Alternative1) TRACE( _T("Alt1 ")); 01928 if (cmMods.Alternative2) TRACE( _T("Alt2 ")); 01929 TRACE( _T("\n")); 01930 } 01931 */ 01932 // We mustn't allow nested drags! 01933 if (Operation::GetCurrentDragOp() != NULL) 01934 { 01935 // TRACEUSER( "JustinF", _T("Drag already running in SelectorTool::OnClick\n")); 01936 return; 01937 } 01938 01939 // If this event marks the beginning of a possible drag then remember the position of the 01940 // mouse etc. 01941 if (ctType == CLICKTYPE_SINGLE) 01942 { 01943 ClickStart = dcPos; 01944 StartSpread = pSpread; 01945 } 01946 01947 // Save the click modifiers etc to save passing them as parameters to lower-level routines. 01948 ClickMods = cmMods; 01949 TypeOfClick = ctType; 01950 SelectRange = GetApplication()->FindSelection(); 01951 01952 // Clicks are processed in three stages. If either of these routines return TRUE then we 01953 // have no further processing to do. 01954 if (PreProcessClick()) return; 01955 if (ProcessObjectClick()) return; 01956 01957 // Here we do the processing normally specific to the selector tool. 01958 PostProcessClick(); 01959 } 01960 01961 01962 01963 /******************************************************************************************** 01964 01965 > BOOL SelectorTool::PreProcessClick() 01966 01967 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 01968 Created: 5/8/94 01969 Inputs: - 01970 Outputs: - 01971 Returns: TRUE if it handled a mouse event, FALSE if it didn't and the event needs 01972 further handling. 01973 Purpose: This allows a click to be handled before going through the standard selector 01974 tool click handling code 01975 01976 At the momemt it sees if there is a node that would like the chance to handle 01977 the click, and if there is one, it's OnClick() handler is called. 01978 Errors: - 01979 SeeAlso: SelectorTool::OnClick 01980 01981 ********************************************************************************************/ 01982 01983 BOOL SelectorTool::PreProcessClick() 01984 { 01985 // This isn't very nice but at least it's quick! 01986 // We have to check blob hits BEFORE we allow the Click PreProcessor 01987 // to look for it's hits because they take precedence. 01988 UINT32 nStatusTextID; 01989 if (AttrFillGeometry::CheckForFillControlHit(ClickStart, &nStatusTextID)) 01990 return FALSE; 01991 01992 // If we aren't displaying our own tool blobs then don't do anything. 01993 if (fShowToolBlobs && !(SelectionRect.IsEmpty())) 01994 { 01995 // First check if we've hit the rotation centre. 01996 if (IsRotateCentreClicked(ClickStart)) 01997 return FALSE; 01998 01999 // Check if we hit any other bounds/rotate blob. 02000 INT32 nBlob = BoundsBlobHitTest(ClickStart); 02001 if (nBlob != 0) 02002 return FALSE; 02003 } 02004 02005 // Find a node that might be interested on the click before the selector tool handles it 02006 // in the standard way 02007 pPreProcClickNode = FindPreProcessClickNode(StartSpread,ClickStart); 02008 02009 if (pPreProcClickNode != NULL) 02010 { 02011 if (bGlineSAllowed) 02012 { 02013 // If we have a node that's interested, give 'em a call 02014 if (pPreProcClickNode->OnClick(ClickStart, TypeOfClick, ClickMods, StartSpread)) 02015 return TRUE; 02016 } 02017 else 02018 { 02019 if (!IS_A (pPreProcClickNode, NodeGuideline)) 02020 { 02021 // If we have a node that's interested, give 'em a call 02022 if (pPreProcClickNode->OnClick(ClickStart, TypeOfClick, ClickMods, StartSpread)) 02023 return TRUE; 02024 } 02025 } 02026 } 02027 02028 // No preprocess node, or the preprocess node didn't use the click. 02029 return FALSE; 02030 } 02031 02032 02033 /******************************************************************************************** 02034 02035 > NodeRenderableInk* SelectorTool::FindPreProcessClickNode(Spread* pSpread, 02036 DocCoord ClickPos, 02037 BOOL Interruptible = FALSE) 02038 02039 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 02040 Created: 14/9/95 02041 Inputs: pSpread = ptr to spread 02042 ClickPos = point clicked within given spread 02043 Interruptible = Flag specifying whether function can be aborted by mouse movement 02044 Outputs: - 02045 Returns: Ptr to a node that requires a chance to process the click itself 02046 OR NULL if no node is interested 02047 Purpose: This scans the nodes in the spread to see if any of them are interested in the click 02048 before it is processed in the normal way. 02049 02050 It uses member vars set up in SelectorTool::OnClick() 02051 02052 Currently, only NodeGuidelines can possibly be interested in this type of click 02053 Errors: - 02054 SeeAlso: SelectorTool::PreProcessClick(), SelectorTool::OnClick() 02055 02056 ********************************************************************************************/ 02057 /* Implementation notes 02058 02059 This func makes some assumptions at the moment. 02060 It assumes that NodeGuideline nodes are the only nodes that will be interested in this sort of click. 02061 It also assumes that these nodes will only live as top-level children in guide layers. 02062 02063 A more general version would scan the entire tree, and call a virtual node func called PreProcClick(). 02064 02065 It also assumes that guidelines can only be obscured by objects on other layers. 02066 All in all, this is rather specific code written quickly in order to get this ready for v1.1 02067 */ 02068 02069 NodeRenderableInk* SelectorTool::FindPreProcessClickNode(Spread* pSpread,DocCoord ClickPos,BOOL Interruptible) 02070 { 02071 ERROR2IF(pSpread == NULL,NULL,"pSpread is NULL"); 02072 02073 // Init the ptr to the node found that wants to deal with a pre process click 02074 NodeRenderableInk* pFoundNode = NULL; 02075 BOOL Found = FALSE; // Not found a node yet 02076 02077 // Start looking for layers in the spread 02078 Layer* pLayer = pSpread->FindFirstLayer(); 02079 02080 while (pLayer != NULL && !Found) 02081 { 02082 if (pLayer->IsGuide() && !pLayer->IsLocked() && pLayer->IsVisible()) 02083 { 02084 // We have a layer that's also a guide layer 02085 // Now look for the guidelines in this layer 02086 02087 Node* pNodeInLayer = pLayer->FindFirstChild(CC_RUNTIME_CLASS(NodeGuideline)); 02088 while (pNodeInLayer != NULL && !Found) 02089 { 02090 pFoundNode = (NodeGuideline*)pNodeInLayer; 02091 02092 // We have found a guideline 02093 // Get the hit test bounds 02094 DocRect Rect = pFoundNode->GetBoundingRect(FALSE,TRUE); 02095 02096 // If the click lies within its bounds, then we have found an interested node 02097 Found = (Rect.ContainsCoord(ClickPos)); 02098 02099 pNodeInLayer = pNodeInLayer->FindNext(CC_RUNTIME_CLASS(NodeGuideline)); 02100 } 02101 } 02102 02103 pLayer = pLayer->FindNextLayer(); 02104 } 02105 02106 if (Found) 02107 { 02108 // OK, we have found a node interested in a preprocess click 02109 // We now have to ensure that it's not being obscured visually by another node 02110 NodeRenderableInk* pNode = NULL; 02111 if (Interruptible) 02112 { 02113 // Allow the hit-test to be interrupted if the mouse moves! 02114 Node* pInterruptedNode = NULL; 02115 pNode = NodeRenderableInk::FindSimpleAtPoint( pSpread, 02116 ClickPos, 02117 NULL, 02118 &pInterruptedNode); 02119 // If hit-test was interrupted then don't say anything about what's under the pointer! 02120 if (pInterruptedNode!=NULL) 02121 return NULL; 02122 } 02123 else 02124 { 02125 // Can't be interrupted by mouse movement so just go for it... 02126 pNode = NodeRenderableInk::FindSimpleAtPoint(pSpread,ClickPos); 02127 } 02128 02129 if (pNode) 02130 { 02131 // Find out whether the hit node is in front of the guideline or not. 02132 // If it is, then clear the Found flag. 02133 Layer* pLowerLayer = (Layer*) pFoundNode->FindParent(CC_RUNTIME_CLASS(Layer)); // The guideline layer 02134 Layer* pNodeLayer = (Layer*) pNode->FindParent(CC_RUNTIME_CLASS(Layer)); // The layer containing the node 02135 // Make sure GuideLayer comes after NodeLayer 02136 do 02137 { 02138 pLowerLayer = pLowerLayer->FindNextLayer(); // Find the layer above the last one tested 02139 } 02140 while (pLowerLayer && pLowerLayer!=pNodeLayer); // Keep going while there is a layer 02141 // and that layer isn't the one we're looking for 02142 // Get here when either we've run out of layers or we've found the layer we want 02143 if (pLowerLayer && pLowerLayer==pNodeLayer) // If found layer above guide layer 02144 Found=FALSE; // Then flag that the guideline is obscured 02145 } 02146 } 02147 02148 if (!Found) 02149 pFoundNode = NULL; 02150 02151 return pFoundNode; 02152 } 02153 02154 02155 /******************************************************************************************** 02156 02157 > BOOL SelectorTool::ProcessObjectClick() 02158 02159 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 02160 Created: 5/8/94 02161 Inputs: - 02162 Outputs: - 02163 Returns: TRUE if an object handled a mouse event, FALSE if no object did and the 02164 event needs further handling. 02165 Purpose: Iterates over the selector objects, calling the OnClick method of each 02166 NodeRenderableInk and AttrFillGeometry. 02167 Errors: - 02168 SeeAlso: SelectorTool::OnClick; NodeRenderableInk::OnClick; 02169 AttrFillGeometry::OnClick 02170 02171 ********************************************************************************************/ 02172 02173 BOOL SelectorTool::ProcessObjectClick() 02174 { 02175 // Test for a click on a fill attribute first, as they aren't NodeRenderableInks, and so 02176 // won't be included in the tests below. 02177 if (AttrFillGeometry::CheckAttrClick(ClickStart, TypeOfClick, ClickMods, StartSpread)) 02178 { 02179 // TRACEUSER( "JustinF", _T("Click handled by AttrFillGeometry\n")); 02180 return TRUE; 02181 } 02182 02183 // Look for a selected object to pass the click to. 02184 Node* pNode; 02185 if (SelectRange == NULL || (pNode = SelectRange->FindFirst()) == NULL) 02186 { 02187 // TRACEUSER( "JustinF", _T("No selected object to handle click\n")); 02188 return FALSE; 02189 } 02190 02191 // If the click is in a different spread to the selection we don't handle it here. 02192 Spread* pSpread = pNode->FindParentSpread(); 02193 if (pSpread != StartSpread) 02194 { 02195 // TRACEUSER( "JustinF", _T("Click on unselected spread\n")); 02196 return FALSE; 02197 } 02198 02199 // Check if the click is on the rotation centre, so that has priority over all other 02200 // blob actions. 02201 if (IsRotateCentreClicked(ClickStart)) 02202 { 02203 // Someone clicked on the rotate centre, that's not going to do anything... 02204 return FALSE; 02205 } 02206 02207 // Next, check if the click is on one of the other tool blobs. Strictly speaking this 02208 // isn't needed because by definition the bound handles will always be outside the edit 02209 // handles 02210 if (BoundsBlobHitTest(ClickStart) != 0) 02211 { 02212 // Someone clicked on a bounds handle, that's not going to do anything... 02213 return FALSE; 02214 } 02215 02216 if (IsCurrent ()) // only do this processing if we are the current tool! 02217 { 02218 // Providing we are displaying object blobs or tool object blobs, try passing the click 02219 // on to each object in the selection. 02220 BlobManager* pBlobs = GetApplication()->GetBlobManager(); 02221 if ((pBlobs && pBlobs->GetCurrentInterest().Object) || 02222 (pBlobs && pBlobs->GetCurrentInterest().ToolObject) ) 02223 { 02224 // For all selected objects . . . 02225 while (pNode != NULL) 02226 { 02227 // Is this node ink-renderable? 02228 if (pNode->IsAnObject()) 02229 { 02230 // Yes. Does it want the click? 02231 if (((NodeRenderableInk*) pNode)->OnClick(ClickStart, TypeOfClick, 02232 ClickMods, StartSpread)) 02233 { 02234 // An object processed the click, so indicate that there's no more to do. 02235 // TRACEUSER( "JustinF", _T("Object processed click\n")); 02236 return TRUE; 02237 } 02238 } 02239 02240 // Try the next object in the selection. 02241 pNode = SelectRange->FindNext(pNode); 02242 } 02243 } 02244 02245 // If after all that we still haven't processed the click then someone else had better 02246 // have a go. 02247 } 02248 02249 return FALSE; 02250 } 02251 02252 02253 02254 /******************************************************************************************** 02255 02256 > void SelectorTool::PostProcessClick() 02257 02258 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 02259 Created: 5/8/94 02260 Inputs: - 02261 Outputs: - 02262 Returns: - 02263 Purpose: Last-chance handler for a mouse event. Calls HandleSingleClick for a 02264 CLICKTYPE_SINGLE and HandleDragClick for a CLICKTYPE_DRAG etc. 02265 Errors: - 02266 SeeAlso: SelectorTool::OnClick; SelectorTool::HandleSingleClick; 02267 SelectorTool::HandleDoubleClick(); SelectorTool::HandleDragClick 02268 02269 ********************************************************************************************/ 02270 02271 void SelectorTool::PostProcessClick() 02272 { 02273 switch (TypeOfClick) 02274 { 02275 case CLICKTYPE_SINGLE: 02276 HandleSingleClick(); 02277 break; 02278 02279 case CLICKTYPE_DOUBLE: 02280 HandleDoubleClick(); 02281 break; 02282 02283 case CLICKTYPE_DRAG: 02284 HandleDragClick(); 02285 break; 02286 02287 case CLICKTYPE_UP: 02288 HandleButtonUp(); 02289 break; 02290 02291 default: 02292 break; 02293 } 02294 } 02295 02296 02297 02298 /******************************************************************************************** 02299 02300 > virtual void SelectorTool::HandleSingleClick() 02301 02302 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 02303 Created: 5/8/94 02304 Inputs: - 02305 Outputs: - 02306 Returns: - 02307 Purpose: Called when the selector tool must process a button-down mouse event. 02308 Errors: - 02309 SeeAlso: SelectorTool::PostProcessClick; SelectorTool::HandleSingleClick; 02310 SelectorTool::HandleButtonUp; 02311 class OpTranslateTrans; class OpSelectorDragBox 02312 02313 ********************************************************************************************/ 02314 02315 void SelectorTool::HandleSingleClick() 02316 { 02317 // Make sure the mouse cursor reflects which modifiers are active. The keyboard handler 02318 // receives all key events such as Ctrl and SHIFT and sets the cursor appropriately. Here 02319 // we have to check if the user is using the right button, which also means Adjust, but 02320 // which will not have been detected by the key handler. 02321 // if (ClickMods.Adjust) 02322 // { 02323 // "Fake" a call of the key handler to do the work. 02324 SetKeyDownCursor(ClickMods); 02325 // } 02326 02327 // Find out which object, if any, was clicked on. We hit-detect both the simple node 02328 // that was clicked and any top-level compound object it may be part of. 02329 pClickSimpleNode = NodeRenderableInk::FindSimpleAtPoint(StartSpread, ClickStart); 02330 pClickCompoundNode = NodeRenderableInk::FindCompoundFromSimple(pClickSimpleNode); 02331 02332 // NOTE! All click handling takes place in HandleButtonUp using the information cached 02333 // here. This allows drags to be distinguished from clicks in a very clear way. 02334 } 02335 02336 02337 02338 /******************************************************************************************** 02339 02340 > virtual void SelectorTool::HandleDoubleClick() 02341 02342 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 02343 Created: 29/9/94 02344 Inputs: - 02345 Outputs: - 02346 Returns: - 02347 Purpose: Called when the selector tool must process a mouse double-click. Currently 02348 does nothing. 02349 Errors: - 02350 SeeAlso: - 02351 02352 ********************************************************************************************/ 02353 02354 void SelectorTool::HandleDoubleClick() 02355 { 02356 if (SelectorTool::bPageDoubleClickOpenFile && pClickSimpleNode==NULL && pClickCompoundNode==NULL) 02357 { 02358 // The user has double-clicked on the page 02359 // Interpret this as a request to open a file 02360 OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor( OPTOKEN_FILEOPEN ) ; 02361 if (pOpDesc) 02362 pOpDesc->Invoke(); 02363 return; 02364 } 02365 02366 // Treat double clicks like two single clicks, for now. 02367 HandleSingleClick(); 02368 } 02369 02370 02371 02372 /******************************************************************************************** 02373 02374 > virtual void SelectorTool::HandleDragClick() 02375 02376 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 02377 Created: 29/9/94 02378 Inputs: - 02379 Outputs: - 02380 Returns: - 02381 Purpose: Called when the selector tool must process a mouse drag. 02382 Errors: - 02383 SeeAlso: - 02384 02385 ********************************************************************************************/ 02386 02387 void SelectorTool::HandleDragClick() 02388 { 02389 // First check to see if the user is accidentally dragging with the menu button 02390 // If so then exit immediately! 02391 // Removed by Phil to allow dragging with the menu button 02392 // if (ClickMods.Menu) 02393 // return; 02394 02395 // Karim 12/07/2000 02396 // If the Adjust modifier is set (means the Shift key is pressed), then we'll forget 02397 // our click info, which is the trigger for our marquee-select behaviour. 02398 // This lets you start a marquee select by dragging from on top of an object, which 02399 // previously resulted in the object being dragged instead. 02400 if (ClickMods.Adjust) 02401 { 02402 pClickSimpleNode = NULL; 02403 pClickCompoundNode = NULL; 02404 } 02405 02406 // Next check for the translate keyboard shortcut. If we detect it run the translate 02407 // operation, provided there is something selected. 02408 if (IsTranslateShortcut(ClickMods)) 02409 { 02410 if (SelectionSpread != NULL) 02411 { 02412 // There is a selection, so start a translate drag... 02413 nClickedBoundsBlob = 0; 02414 GetUserParams(); 02415 DoTranslate(); 02416 } 02417 return; 02418 } 02419 02420 // Check if the click is on the rotation centre, so that has priority over all other 02421 // blob actions. 02422 if (IsRotateCentreClicked(ClickStart)) 02423 { 02424 // We are trying to drag the rotation centre blob. 02425 DoDragRotateCentre(); 02426 return; 02427 } 02428 02429 // Next, check if the click is on one of the other tool blobs. Set the transforms' 02430 // parameters according to the way the user has set the info-bar. Code within the 02431 // DoXXXX functions may override fields set here. NB. the call to GetUserParams() 02432 // must come AFTER nClickedBoundsBlob is set. 02433 nClickedBoundsBlob = BoundsBlobHitTest(ClickStart); 02434 GetUserParams(); 02435 if (nClickedBoundsBlob != 0) 02436 { 02437 SelectRange->MakePartialSelectionWhole(); 02438 02439 // Run the appropriate transform for the kind of blob clicked on. 02440 switch (nClickedBoundsBlob) 02441 { 02442 // Corner blob. 02443 case 1: case 3: case 6: case 8: 02444 if (pInfoBarOp->InRotateMode()) DoRotate(); else DoScale(); 02445 break; 02446 02447 // Edge blob. 02448 case 2: case 4: case 5: case 7: 02449 if (pInfoBarOp->InRotateMode()) DoShear(); else DoSquash(); 02450 break; 02451 02452 // That's enough blobs for now. 02453 default: 02454 ERROR3("Bad blob number in SelectorTool::HandleSingleClick"); 02455 break; 02456 } 02457 02458 // Clicks on blobs should have nothing more to do. 02459 return; 02460 } 02461 02462 // Right, that's the blobs dealt with. If we get here we clicked on either an object 02463 // (possibly already selected) or blank paper. First, check if there is an object at 02464 // the click position. 02465 if (pClickCompoundNode == NULL) 02466 { 02467 // There is only blank paper at the click point, so the user must be trying to start 02468 // a marquee selection drag. Run the drag-box operation. 02469 DoDragBox(); 02470 return; 02471 } 02472 02473 // Is the clicked object already selected? Note that the user may be trying to click on 02474 // a simple object selected inside a group that itself is not selected, so we must be 02475 // careful to check the state of the right nodes here. 02476 // 02477 // The logic here is: 02478 // If the clicked simple node or any of it's parents are selected 02479 // Then DON'T alter the selection to reflect the clicked object! 02480 // 02481 BOOL SimpleInSelected = FALSE; // So far haven't encountered any selected nodes 02482 Node* pNode = pClickSimpleNode; // Make a working pointer and initialise it 02483 do 02484 { 02485 if (pNode->IsSelected()) // If the working node is selected 02486 { 02487 SimpleInSelected = TRUE; // Then the simple node or one of its parents are 02488 break; // selected so we don't need to change the selection! 02489 } 02490 pNode = pNode->FindParent(); // Else check the working node's parent 02491 } 02492 while (pNode != pClickCompoundNode->FindParent());// until we've reached the compound node's parent 02493 // (Allows the compound node itself to be checked) 02494 02495 02496 if (!SimpleInSelected) 02497 { 02498 // No. If the click was with the left button we must deselect all other objects. 02499 // If the clicked object isn't selected, we can't run a transform on it. Hence we must 02500 // select it, but we prevent it being redrawn as selected (with its little blobs). 02501 if (!ClickMods.Adjust) 02502 { 02503 // Normal click, so deslect everything before we select the clicked node... 02504 NodeRenderableInk::DeselectAll(); 02505 ResetDefaults(); 02506 } 02507 else 02508 { 02509 // We clicked with the right button, but if the click was in a different spread to 02510 // the selection we have to deselect everything anyway. 02511 Node* pNode = SelectRange->FindFirst(); 02512 if (pNode != NULL && pNode->FindParentSpread() != SelectionSpread) 02513 { 02514 // Clicked node in a different spread from previous selection, so clear selection 02515 NodeRenderableInk::DeselectAll(); 02516 } 02517 } 02518 02519 // Now that the selection state of all other objects is dealt with, make sure the 02520 // clicked object is selected. 02521 pClickCompoundNode->Select(TRUE); 02522 02523 // Force a broadcast etc of the changing selection, so that our SelectionHasChanged 02524 // function gets called. If it doesn't then SelectionSpread will remain NULL and 02525 // the info-bar won't be able to dynamically update as the object is dragged. 02526 SelectRange->Update(TRUE); 02527 } 02528 02529 // Finally, run a transform on the selected object(s). Of course, the user may not be 02530 // trying to drag the selection - if so the button will come up before a significant 02531 // drag has occurred and we can take it from there. 02532 SelectRange->MakePartialSelectionWhole(); 02533 // NOTE! pNode may no longer be selected after MakePartialSelectionWhole! 02534 02535 // MRH 16/5/00 - This is a worthwhile check as doing the DoTranslate function with an 02536 // empy bounding rect results in a major error and screws up camelots selection tool 02537 // from here on! 02538 DocRect SelRect = GetSelectionBounds(pInfoBarOp->DontConsiderAttrs()); 02539 ERROR3IF(SelRect.IsEmpty(), "We've got an Empty Bounding Rect in HandleDragClick!"); 02540 02541 if (!SelRect.IsEmpty()) 02542 { 02543 DoTranslate(); 02544 } 02545 } 02546 02547 02548 02549 /******************************************************************************************** 02550 02551 > virtual void SelectorTool::HandleButtonUp() 02552 02553 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 02554 Created: 3/10/94 02555 Inputs: - 02556 Outputs: - 02557 Returns: - 02558 Purpose: Called when the selector tool must process a mouse button-up event. Note 02559 that such events are NOT sent if an Operation etc has run a drag in response 02560 to a CLICKTYPE_DRAG. 02561 Errors: - 02562 SeeAlso: - 02563 02564 ********************************************************************************************/ 02565 02566 void SelectorTool::HandleButtonUp() 02567 { 02568 // If the SelRange thinks it knows what the last selected node was 02569 // we'll go along with that, otherwise we'll use our own record... 02570 Node* pLastSelNode = SelectRange->GetLastSelectedNode(); 02571 if ( pLastSelNode==NULL || !pLastSelNode->IsAnObject() ) 02572 { 02573 pLastSelNode = pLastClickNode; 02574 } 02575 02576 // Find out what we should do with the click... 02577 ClickActionCode action = CLICKACTION_NONE; 02578 NodeRenderableInk* pActionNode = NULL; 02579 action = DetermineClickAction(&pActionNode,(NodeRenderableInk*)pLastSelNode, 02580 pClickSimpleNode,pClickCompoundNode,StartSpread,ClickStart,ClickMods); 02581 02582 // Act upon the information... 02583 switch (action) 02584 { 02585 //-------------------------------------------------// 02586 // No action required... 02587 case CLICKACTION_NONE: 02588 break; 02589 02590 //-------------------------------------------------// 02591 // A Bounds handle was clicked on... 02592 case CLICKACTION_BOUNDTOPLEFT: 02593 case CLICKACTION_BOUNDTOP: 02594 case CLICKACTION_BOUNDTOPRIGHT: 02595 case CLICKACTION_BOUNDLEFT: 02596 case CLICKACTION_BOUNDRIGHT: 02597 case CLICKACTION_BOUNDBOTTOMLEFT: 02598 case CLICKACTION_BOUNDBOTTOM: 02599 case CLICKACTION_BOUNDBOTTOMRIGHT: 02600 case CLICKACTION_BOUNDTRANSFORMORIGIN: 02601 break; 02602 02603 //-------------------------------------------------// 02604 // Anything that's selected must be deselected... 02605 case CLICKACTION_SELNONE: 02606 { 02607 // Don't clear selection if was an adjust click 02608 // (Change requested by Charles and Alan Burns) 02609 if (!ClickMods.Adjust) 02610 { 02611 // Markn 29/9/95: We ignore this if the click happend on a guideline. 02612 // If another type of node needs to behave like this, then a virt func in node 02613 // will be required instead of the hideous IS_A() clause in the 'if' statement 02614 if (pPreProcClickNode == NULL || !IS_A(pPreProcClickNode,NodeGuideline)) 02615 { 02616 // If the selector is not the current tool, get the tool to render its blobs BEFORE the selection changes 02617 RenderOtherToolBlobs(); 02618 02619 02620 NodeRenderableInk::DeselectAll(); 02621 ResetDefaults(); 02622 pLastClickNode = NULL; 02623 InvalidateRotationCentre(); 02624 02625 // If the selector is not the current tool, get the tool to render its blobs AFTER the selection changes 02626 RenderOtherToolBlobs(); 02627 02628 // Make sure the selection range stuff is updated and sends notifications. 02629 SelectRange->Update(TRUE); 02630 } 02631 } 02632 break; 02633 } 02634 02635 //-------------------------------------------------// 02636 // The action node must be selected or toggled... 02637 case CLICKACTION_SELNODE: 02638 case CLICKACTION_SELUNDER: 02639 case CLICKACTION_SELUNDERCYCLE: 02640 case CLICKACTION_SELUNDERFAIL: 02641 case CLICKACTION_SELUNDERFAIL2: 02642 case CLICKACTION_SELINSIDE: 02643 case CLICKACTION_SELINSIDECYCLE: 02644 case CLICKACTION_SELINSIDEFAIL: 02645 case CLICKACTION_SELINSIDEFAIL2: 02646 case CLICKACTION_SELLEAF: 02647 { 02648 ERROR3IF(pActionNode == NULL,"Action and ActionNode don't agree!"); 02649 02650 // If the selector is not the current tool, get the tool to render its blobs BEFORE the selection changes 02651 RenderOtherToolBlobs(); 02652 BOOL ChangedSelection = FALSE; 02653 02654 // Alter final selection behaviour based on state of Adjust modifier... 02655 if (ClickMods.Adjust) 02656 { 02657 // If Adjust is applied, toggle the state of the action node. 02658 if (pActionNode->IsSelected()) 02659 pActionNode->DeSelect(TRUE); 02660 else 02661 pActionNode->Select(TRUE); 02662 ChangedSelection = TRUE; 02663 } 02664 else 02665 { 02666 // Special case: 02667 // If this is a menu click on a selected node then don't upset the selection... 02668 if (pActionNode->IsSelected() 02669 && ClickMods.Menu) 02670 { 02671 // Menu-click on a selected node 02672 // So don't change anything! 02673 } 02674 02675 // Special case: 02676 // Check for clicks on a selected node in the Selector tool with bounds blobs showing... 02677 else if (pActionNode->IsSelected() 02678 && fShowToolBlobs 02679 && Tool::GetCurrentID()==TOOLID_SELECTOR 02680 && !ClickMods.Menu) 02681 { 02682 // Click on a selected node 02683 // So toggle the scale/rotation mode... 02684 BOOL fNewState = !SelectorInfoBarOp::fRotateMode; 02685 if (pInfoBarOp != NULL) 02686 pInfoBarOp->SetRotateMode(fNewState); 02687 else 02688 SelectorInfoBarOp::fRotateMode = fNewState; 02689 // Redraw blobs and update flags etc. 02690 RotateButtonChange(fNewState); 02691 // Didn't change the selection! 02692 } 02693 02694 // Fell through all the special cases so it's a normal click on a normal object 02695 // Deselect any other selected objects and select the clicked object alone 02696 else 02697 { 02698 bNormalClickCheckProfileDialog = TRUE; // need to check the profile dialog 02699 02700 NodeRenderableInk::DeselectAll(); 02701 //InvalidateRotationCentre(); 02702 ResetDefaults(); 02703 pActionNode->Select(TRUE); 02704 ChangedSelection = TRUE; 02705 } 02706 } 02707 02708 // If the selector is not the current tool, get the tool to render its blobs AFTER the selection changes 02709 RenderOtherToolBlobs(); 02710 02711 // If the logic resulted in the selection actually being changed then update things... 02712 if (ChangedSelection) 02713 { 02714 // Record this node as being the last one clicked on. 02715 pLastClickNode = pActionNode; 02716 // Make sure the selection range stuff is updated and sends notifications. 02717 SelectRange->Update(TRUE); 02718 02719 bNormalClickCheckProfileDialog = FALSE; // ensure this is updated 02720 } 02721 02722 break; 02723 } 02724 //-------------------------------------------------// 02725 default: 02726 ERROR3("Unknown Click action code!"); 02727 break; 02728 }; // switch (action) 02729 02730 // Make sure the cursor reflects which keys are down, now that the mouse button has 02731 // been released. 02732 ResetCursorNow(); 02733 } 02734 02735 02736 02737 02738 /******************************************************************************************** 02739 02740 > ClickActionCode SelectorTool::DetermineClickAction(NodeRenderableInk** ppActionNode, 02741 NodeRenderableInk* pLastClickNode, 02742 NodeRenderableInk* pClickSimpleNode, 02743 NodeRenderableInk* pClickCompoundNode, 02744 Spread* pStartSpread, 02745 DocCoord ClickStart, 02746 ClickModifiers ClickMods) 02747 02748 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02749 Created: 29/5/95 02750 Inputs: - 02751 Outputs: pNodeToSelect Pointer to pointer to node to select or NULL 02752 Returns: ActionCode describing what the click should do 02753 Purpose: Determine what action needs to be taken inresponse to a click. 02754 Errors: - 02755 SeeAlso: SelectorTool::HandleButtonUp 02756 02757 ********************************************************************************************/ 02758 02759 SelectorTool::ClickActionCode SelectorTool::DetermineClickAction(NodeRenderableInk** ppActionNode, 02760 NodeRenderableInk* pLastClickNode, 02761 NodeRenderableInk* pClickSimpleNode, 02762 NodeRenderableInk* pClickCompoundNode, 02763 Spread* pStartSpread, 02764 DocCoord ClickStart, 02765 ClickModifiers ClickMods) 02766 { 02767 *ppActionNode = NULL; 02768 02769 //-------------------------------------- 02770 // Did we click on the transform origin? 02771 if (IsRotateCentreClicked(ClickStart)) 02772 { 02773 return CLICKACTION_BOUNDTRANSFORMORIGIN; 02774 } 02775 02776 // Did we click on a bounds handle? 02777 INT32 BlobID = BoundsBlobHitTest(ClickStart); 02778 if (BlobID!=0) 02779 { 02780 return (SelectorTool::ClickActionCode)((INT32)CLICKACTION_BOUNDTOPLEFT+BlobID); 02781 } 02782 02783 //-------------------------------------- 02784 // Test "leaf" modifier... 02785 if (IsSelectLeafClick(ClickMods)) 02786 { 02787 // Go directly to leaf nodes! 02788 if (pClickSimpleNode!=pClickCompoundNode) 02789 { 02790 *ppActionNode = pClickSimpleNode; 02791 02792 // <<<<< Inclusion by Mike 11/01/96 02793 // this stuff is to check whether any parent is responding to 02794 // AllowSelectInside() and returning FALSE. Selections will not go down 02795 // into these objects if so. 02796 02797 Node* pParentNode = pClickSimpleNode->FindParent(); 02798 while (pParentNode) 02799 { 02800 if (pParentNode->IsLayer()) 02801 break; 02802 if (pParentNode->IsAnObject()) 02803 { 02804 if ( (!pParentNode->AllowSelectInside()) && 02805 ((NodeRenderableInk*)pParentNode)->CanSelectAsCompoundParent() 02806 ) 02807 { 02808 *ppActionNode = (NodeRenderableInk*)(pParentNode); 02809 } 02810 } 02811 if (pParentNode==pClickCompoundNode) 02812 break; 02813 pParentNode = pParentNode->FindParent(); 02814 } 02815 02816 // <<<<< End of inclusion 02817 } 02818 02819 // If we still haven't found what we're looking for 02820 // Cycle round to the top again... 02821 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELLEAF,CLICKACTION_SELNODE); 02822 } 02823 02824 //-------------------------------------- 02825 // Test "under" modifier... 02826 if (IsSelectUnderClick(ClickMods)) 02827 { 02828 // Try to perform a select under 02829 // First check that the context node is still under the pointer 02830 // If not then all we can do is a normal click operation... 02831 if (!ValidateLastClickUnder(pLastClickNode,pStartSpread,ClickStart)) 02832 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELUNDERFAIL2,CLICKACTION_SELUNDERFAIL2); 02833 02834 // Find the leaf node at the click position, but only search nodes 02835 // before the last clicked node. 02836 *ppActionNode = NodeRenderableInk::FindSimpleAtPoint(pStartSpread,ClickStart,pLastClickNode); 02837 // Then find a compound node containing the leaf, preferably a sibling 02838 // of the last clicked node. 02839 *ppActionNode = NodeRenderableInk::FindCompoundFromSimple(*ppActionNode,pLastClickNode); 02840 02841 // If the "under" node turns out to be the node we started from 02842 // return a failure code but go ahead and re-select it... 02843 // (If we failed to find anything under the last node, and the last node is the top node) 02844 if (*ppActionNode==NULL && pLastClickNode == pClickCompoundNode) 02845 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELUNDERFAIL,CLICKACTION_SELUNDERFAIL); 02846 02847 // If we still haven't found what we're looking for 02848 // Cycle round to the top again... 02849 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELUNDER,CLICKACTION_SELUNDERCYCLE); 02850 } 02851 02852 //-------------------------------------- 02853 // Test "member" modifier... 02854 if (IsSelectMemberClick(ClickMods)) 02855 { 02856 // See if the clicked simple node is a descendent of the last clicked node 02857 if (!ValidateLastClickInside(pLastClickNode,pClickSimpleNode)) 02858 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELINSIDEFAIL2,CLICKACTION_SELINSIDEFAIL2); 02859 02860 // If the node we're going to look inside is not compound and it's the top node 02861 // return a failure code but go ahead and re-select it... 02862 if (pLastClickNode && !pLastClickNode->IsCompound() && pLastClickNode == pClickCompoundNode) 02863 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELINSIDEFAIL,CLICKACTION_SELINSIDEFAIL); 02864 02865 // Try to perform a select inside 02866 *ppActionNode = NodeRenderableInk::FindInnerCompound(pClickSimpleNode,pLastClickNode); 02867 02868 // If we still haven't found what we're looking for 02869 // Cycle round to the top again... 02870 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELINSIDE,CLICKACTION_SELINSIDECYCLE); 02871 } 02872 02873 //-------------------------------------- 02874 // OK, so no modifiers are currently down 02875 // Just try to do a normal click action... 02876 return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_NONE,CLICKACTION_SELNODE); 02877 } 02878 02879 02880 02881 02882 /******************************************************************************************** 02883 02884 > BOOL SelectorTool::ValidateLastClickUnder(NodeRenderableInk* pLastClickNode, DocCoord ClickStart) 02885 02886 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02887 Created: 03/06/95 02888 Inputs: - 02889 Outputs: pLastClickNode Pointer to last node selected or NULL 02890 Returns: TRUE if last click node is still under the pointer (somewhere) 02891 FALSE otherwise 02892 Purpose: Validate that the last click node is still under the pointer 02893 Note! This routine can be slow depending on how deep it has to look in the 02894 tree for the last selected object. 02895 Errors: - 02896 SeeAlso: SelectorTool::HandleButtonUp 02897 02898 ********************************************************************************************/ 02899 02900 BOOL SelectorTool::ValidateLastClickUnder(NodeRenderableInk* pLastClickNode, Spread* pStartSpread, DocCoord ClickStart) 02901 { 02902 NodeRenderableInk* pSearchNode = NULL; 02903 do 02904 { 02905 pSearchNode = NodeRenderableInk::FindSimpleAtPoint(pStartSpread,ClickStart,pSearchNode); 02906 } 02907 while (pSearchNode && pLastClickNode!=NodeRenderableInk::FindCompoundFromSimple(pSearchNode,pLastClickNode)); 02908 02909 return (pSearchNode!=NULL); 02910 } 02911 02912 02913 02914 02915 /******************************************************************************************** 02916 02917 > BOOL SelectorTool::ValidateLastClickInside(NodeRenderableInk* pLastClickNode,NodeRenderableInk* pClickSimpleNode) 02918 02919 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02920 Created: 03/06/95 02921 Inputs: - 02922 Outputs: pLastClickNode Pointer to last node selected or NULL 02923 Returns: TRUE if the simple clicked node is inside the last clicked node somewhere 02924 FALSE otherwise 02925 Purpose: Validate that the simple node is inside the last clicked node 02926 Errors: - 02927 SeeAlso: SelectorTool::HandleButtonUp 02928 02929 ********************************************************************************************/ 02930 02931 BOOL SelectorTool::ValidateLastClickInside(NodeRenderableInk* pLastClickNode,NodeRenderableInk* pClickSimpleNode) 02932 { 02933 Node* pSearchNode = pClickSimpleNode; 02934 while (pSearchNode && pSearchNode!=pLastClickNode) 02935 pSearchNode = pSearchNode->FindParent(); 02936 return (pSearchNode!=NULL); 02937 } 02938 02939 02940 02941 02942 /******************************************************************************************** 02943 02944 > ClickActionCode SelectorTool::CycleClickAction( NodeRenderableInk** ppActionNode, 02945 NodeRenderableInk* pClickCompoundNode, 02946 ClickActionCode foundAction, 02947 ClickActionCode cycleAction 02948 ) 02949 02950 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02951 Created: 29/5/95 02952 Inputs: - 02953 Outputs: pNodeToSelect Pointer to pointer to node to select or NULL 02954 Returns: ActionCode describing what the click should do 02955 Purpose: Determine what action needs to be taken inresponse to a click. 02956 Errors: - 02957 SeeAlso: SelectorTool::HandleButtonUp 02958 02959 ********************************************************************************************/ 02960 02961 SelectorTool::ClickActionCode SelectorTool::CycleClickAction(NodeRenderableInk** ppActionNode, 02962 NodeRenderableInk* pClickCompoundNode, 02963 ClickActionCode foundAction, 02964 ClickActionCode cycleAction 02965 ) 02966 { 02967 // If we have found a node then return the specified action code... 02968 if (*ppActionNode) 02969 { 02970 return foundAction; 02971 } 02972 // Else no suitable node so see whether the click occurred over a compound node 02973 else 02974 { 02975 // If click occurred over a compound node then we can return that 02976 // along with the alternative action code... 02977 if (pClickCompoundNode) 02978 { 02979 *ppActionNode = pClickCompoundNode; 02980 return cycleAction; 02981 } 02982 else 02983 // Else if there wasn't even a compound node we must return the information that 02984 // the click occurred over white space... 02985 { 02986 *ppActionNode = NULL; 02987 return CLICKACTION_SELNONE; 02988 } 02989 } 02990 } 02991 02992 02993 02994 02995 /******************************************************************************************** 02996 02997 > void SelectorTool::RenderOtherToolBlobs() 02998 02999 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 03000 Created: 15/5/95 03001 Inputs: - 03002 Outputs: - 03003 Returns: - 03004 Purpose: Called when the selector tool changes the selection, and yet may not be the *current* tool. 03005 This can happen nowadays with the right mouse button popping up the menu and changing the selection 03006 whilst in any tool. 03007 Errors: - 03008 SeeAlso: SelectorTool::OnKeyPress 03009 03010 ********************************************************************************************/ 03011 03012 void SelectorTool::RenderOtherToolBlobs() 03013 { 03014 // Get the current tool 03015 Tool* pTool = Tool::GetCurrent(); 03016 03017 // Get the tool to remove all its blobs before we deselect the nodes. 03018 // Only do this if the current tool is NOT the selector tool, 03019 // because the selector handles the selection itself through sel changed messages. 03020 if (pTool!=NULL && StartSpread!=NULL && !pTool->AreToolBlobsRenderedOnSelection()) 03021 pBlobManager->RenderToolBlobsOff(pTool, StartSpread, NULL); 03022 } 03023 03024 03025 03026 /******************************************************************************************** 03027 03028 > virtual void SelectorTool::HandleTabKey(BOOL fIsShifted) 03029 03030 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03031 Created: 10/1/95 03032 Inputs: fIsShifted if TRUE then the shift key is down, which generally 03033 means cycle in the opposite direction. 03034 Outputs: - 03035 Returns: - 03036 Purpose: Called when the selector tool receives a TAB keystroke. Moves the 03037 selection to the next object in render-order. 03038 Errors: - 03039 SeeAlso: SelectorTool::OnKeyPress 03040 03041 ********************************************************************************************/ 03042 void SelectorTool::HandleTabKey(BOOL fIsShifted) 03043 { 03044 #ifdef NEW_SELECTION_TAB_ITERATION 03045 //[ 03046 // Rules: 03047 // 1. if Shift is pressed, then we move forward through the tree, 03048 // otherwise we move backward. 03049 // 2. if nothing is selected, then we select the backmost or foremost 03050 // object on the spread, depending on whether Shift is pressed or not, 03051 // and taking into account locked/invisible layers. 03052 // 3. we iterate according to the rules defined by our Iterate...() methods. 03053 03054 // ignore invisible and locked layers please, and reset our starting layer to NULL. 03055 BOOL fIgnoreInvisibleLayers = TRUE; 03056 BOOL fIgnoreLockedLayers = TRUE; 03057 m_pIterStartLayer = NULL; 03058 03059 BOOL fMoveForward = fIsShifted; 03060 IterateFlags iterFlags(fIgnoreInvisibleLayers, fIgnoreLockedLayers); 03061 03062 // tell the selrange to match our settings. 03063 RangeControl rc = SelectRange->GetRangeControlFlags(); 03064 RangeControl oldrc = rc; 03065 rc.IgnoreInvisibleLayers = !iterFlags.IgnoreInvisibleLayers; 03066 rc.IgnoreLockedLayers = iterFlags.IgnoreLockedLayers; 03067 SelectRange->Range::SetRangeControl(rc); 03068 03069 Node* pThisNode = NULL; 03070 NodeRenderableInk* pInkNode = NULL; 03071 03072 // if there is a selection, then we start on its first or last member. 03073 if (SelectionSpread != NULL) 03074 { 03075 // assumes that SelectRange has PromoteToParent FALSE, and will return a valid ink-node. 03076 if (fMoveForward) 03077 { 03078 pThisNode = SelectRange->FindLast(); 03079 pThisNode = IteratePreProcess(pThisNode); 03080 pInkNode = IterateNextInk(pThisNode, iterFlags); 03081 } 03082 else 03083 { 03084 pThisNode = SelectRange->FindFirst(); 03085 pThisNode = IteratePreProcess(pThisNode); 03086 pInkNode = IteratePrevInk(pThisNode, iterFlags); 03087 03088 } 03089 03090 // we must remove the current selection before we select our chosen node. 03091 if (pInkNode != NULL) 03092 NodeRenderableInk::DeselectAll(); 03093 } 03094 03095 // no selection, so we'll give the iteration code a bad guess (NULL) in the right spot 03096 // (the first or last layer in the spread), and let it use that guess to choose the node. 03097 else 03098 { 03099 Document* pDoc = Document::GetSelected(); 03100 PORTNOTE("spread", "Multi-spread warning!") 03101 Spread* pSpread = pDoc->FindFirstSpread(); 03102 if (pSpread != NULL) 03103 { 03104 if (fMoveForward) 03105 { 03106 pThisNode = pSpread->FindLastLayer(); 03107 pInkNode = IteratePostProcessNext(NULL, pThisNode, iterFlags); 03108 } 03109 else 03110 { 03111 pThisNode = pSpread->FindFirstLayer(); 03112 pInkNode = IteratePostProcessPrev(NULL, pThisNode, iterFlags); 03113 } 03114 } 03115 } 03116 03117 // ensure the RangeControl of the selrange is reset correctly. 03118 SelectRange->Range::SetRangeControl(oldrc); 03119 03120 // Select the given node, if it exists. 03121 if (pInkNode != NULL) 03122 { 03123 pInkNode->Select(TRUE); 03124 SelectRange->Update(TRUE); 03125 } 03126 //] 03127 #else 03128 //[ 03129 // To begin, make a range that covers the entire document tree but ignores locked layers. 03130 Range rng(NULL, NULL, RangeControl(TRUE, TRUE, TRUE, TRUE)); 03131 03132 // We have two different courses of action, depending on whether there is anything 03133 // currently selected. If neither work then pNode will remain NULL and we do nothing. 03134 NodeRenderableInk* pInkNode = NULL; 03135 if (SelectionSpread != NULL) 03136 { 03137 // Something is selected. Are we tabbing forwards or backwards? 03138 if (!fIsShifted) 03139 { 03140 // Tab - move backwards. 03141 Node* pFirstNode = SelectRange->FindFirst(); 03142 if (pFirstNode != NULL) 03143 { 03144 // Ensure we're in the normal selection surface (not "inside") 03145 pFirstNode = FindFrom((NodeRenderableInk*) pFirstNode); 03146 03147 // Find the next node that could be selected. 03148 pFirstNode = rng.FindPrev(pFirstNode); 03149 pFirstNode = EnsureInkPrev(&rng, pFirstNode); 03150 03151 // If we've run out of "prev" nodes then start from the end again 03152 if (pFirstNode == NULL) 03153 { 03154 pFirstNode = rng.FindLast(); 03155 pFirstNode = EnsureInkPrev(&rng, pFirstNode); 03156 } 03157 03158 if (pFirstNode != NULL) pInkNode = (NodeRenderableInk*) pFirstNode; 03159 else TRACEUSER( "JustinF", _T("nothing in tree ?!?\n")); 03160 } 03161 } 03162 else 03163 { 03164 // Shift tab - move forwards. 03165 Node* pLastNode = SelectRange->FindLast(); 03166 if (pLastNode != NULL) 03167 { 03168 // Ensure we're in the normal selection surface (not "inside") 03169 pLastNode = FindFrom((NodeRenderableInk*) pLastNode); 03170 03171 // Find the next node that could be selected. 03172 pLastNode = rng.FindNext(pLastNode); 03173 pLastNode = EnsureInkNext(&rng, pLastNode); 03174 03175 // If we've run out of "next" nodes then start from the beginning again 03176 if (pLastNode == NULL) 03177 { 03178 pLastNode = rng.FindFirst(); 03179 pLastNode = EnsureInkNext(&rng, pLastNode); 03180 } 03181 03182 if (pLastNode != NULL) pInkNode = (NodeRenderableInk*) pLastNode; 03183 else TRACEUSER( "JustinF", _T("nothing in tree ?!?\n")); 03184 } 03185 } 03186 03187 // Before we select the node we have found, deselect everything else. 03188 if (pInkNode != NULL) NodeRenderableInk::DeselectAll(); 03189 } 03190 else 03191 { 03192 // Nothing is selected. Forwards or backwards? 03193 if (!fIsShifted) 03194 { 03195 // Tab: Backwards - find the frontmost object. 03196 pInkNode = (NodeRenderableInk*) rng.FindLast(); 03197 pInkNode = EnsureInkPrev(&rng, pInkNode); 03198 } 03199 else 03200 { 03201 // Shift-tab: Forwards - find the rearmost object. 03202 pInkNode = (NodeRenderableInk*) rng.FindFirst(); 03203 pInkNode = EnsureInkNext(&rng, pInkNode); 03204 } 03205 } 03206 03207 03208 // Select the given node, if it exists. 03209 if (pInkNode != NULL) 03210 { 03211 pInkNode->Select(TRUE); 03212 SelectRange->Update(TRUE); 03213 } 03214 03215 //] 03216 #endif 03217 } 03218 03219 03220 03222 // 03223 // Karim 07/08/2000 03224 // New iteration code, added for tab-selection because certain compound nodes - bevels, 03225 // shadows, contours - seriously interfered with the old range-based iteration process. 03226 // 03227 // A new feature with this code is that the user can tab through the contents of a group, 03228 // which was previously impossible under CorelXara. 03229 // 03230 // For clarity, iteration no longer uses Range code. Instead, an intelligent node-to-node 03231 // iteration is performed. This is in three(ish) parts. 03232 // 1. PreProcess - Ensures that the iteration starts off on the correct node. 03233 // For instance, if the iteration begins on a text character, then 03234 // it must actually behave as if it is beginning on that character's 03235 // enclosing text-story. 03236 // 03237 // 2. Next/Prev - Moves on to the next/previous ink-node, regardless of whether 03238 // that node is actually the correct node to move to. 03239 // 03240 // 3. PostProcess Post-processes the choice made in step 2, and if necessary, 03241 // recursively returns to step 2, to ensure the correct node is 03242 // found. 03243 // 03244 // Below is an example tree, with the expected iteration order. 03245 // (in reality, Shadow wouldn't have more than one sibling ink). 03246 // 03247 // Group 03248 // | 03249 // Shape1--ShadowController--Shape2 03250 // | 03251 // Shadow--BevelController--Shape3 03252 // | 03253 // Bevel--Shape4 03254 // 03255 // forward iteration order: Shape1, Shadow, Bevel, Shape4, Shape3, Shape2, Shape1, etc. 03256 // 03257 03258 /******************************************************************************************** 03259 03260 > Node* SelectorTool::IteratePreProcess(Node* pCurrent) 03261 03262 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 03263 Created: 04/08/2000 03264 03265 Inputs: pCurrent the node which you _think_ we should start from. 03266 Returns: the node which we should _actually_ start from :o) 03267 03268 Purpose: Iteration pre-processing. If we're asked to iterate over the selection, 03269 and we happen to be on a node, eg a text-character, which is unsuitable 03270 for iterating from, then we need to move to a more suitable node. 03271 03272 Notes: Could be made into a virtual function based in Node or NodeRenderableInk. 03273 Could also possibly be called from IterateNextInk()/IteratePrevInk(). 03274 03275 Errors: No errors, but if you give me a NULL node, I'll give itcha right back ;o) 03276 03277 ********************************************************************************************/ 03278 Node* SelectorTool::IteratePreProcess(Node* pCurrent) 03279 { 03280 if (pCurrent == NULL) 03281 return NULL; 03282 03283 if (pCurrent->IsABaseTextClass()) 03284 pCurrent = ((BaseTextClass*)pCurrent)->FindParentStory(); 03285 03286 return pCurrent; 03287 } 03288 03289 03290 03291 /******************************************************************************************** 03292 03293 > NodeRenderableInk* SelectorTool::IterateNextInk(Node* pCurrent, 03294 IterateFlags iterFlags) 03295 03296 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 03297 Created: 04/08/2000 03298 Inputs: pCurrent the node which we're gonna start iterating from. 03299 iterFlags some flags which tell us how to iterate. 03300 Returns: the next node in the iteration, or 03301 NULL if the given node or its parent is NULL. 03302 03303 Purpose: Iterate over the document. See notes up above for more info. 03304 Notes: * Could be made into a non-virtual function in Node or NodeRenderableInk. 03305 * Recursively calls IteratePostProcessNext(). 03306 03307 ********************************************************************************************/ 03308 NodeRenderableInk* SelectorTool::IterateNextInk(Node* pCurrent, IterateFlags iterFlags) 03309 { 03310 Node* pParent = (pCurrent == NULL) ? NULL : pCurrent->FindParent(); 03311 Node* pNext = (pCurrent == NULL) ? NULL : pCurrent->FindNextInk(); 03312 return IteratePostProcessNext(pNext, pParent, iterFlags); 03313 } 03314 03315 03316 03317 /******************************************************************************************** 03318 03319 > NodeRenderableInk* SelectorTool::IteratePostProcessNext(Node* pCurrent, Node* pParent, 03320 IterateFlags iterFlags) 03321 03322 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 03323 Created: 04/08/2000 03324 Inputs: pCurrent the node to post-process. 03325 pParent the parent of the node to post-process. 03326 iterFlags some flags which tell us how to iterate. 03327 Returns: the correct node in the iteration, based on this guess. 03328 03329 Purpose: IterateNextInk() is brainless - it picks the next sibling of a node. 03330 This function makes an informed decision, based on that chosen node, 03331 to get the selection-tabbing order correct. See notes above for more info. 03332 03333 Notes: * Could be made into a virtual function based in Node or NodeRenderableInk. 03334 * Recursively calls itself and IterateNextInk(). 03335 03336 ********************************************************************************************/ 03337 NodeRenderableInk* SelectorTool::IteratePostProcessNext(Node* pCurrent, Node* pParent, 03338 IterateFlags iterFlags) 03339 { 03340 // return NULL if we are given no parent. 03341 if (pParent == NULL) 03342 return NULL; 03343 03344 // we're NULL, so post-processing is based on our parent. 03345 if (pCurrent == NULL) 03346 { 03347 if (pParent->ShouldITransformWithChildren()) 03348 return IterateNextInk(pParent, iterFlags); 03349 03350 else if (pParent->IsLayer()) 03351 { 03352 Layer* pNextLayer = ((Layer*)pParent); 03353 03354 pNextLayer = pNextLayer->FindNextLayer( iterFlags.IgnoreInvisibleLayers, 03355 iterFlags.IgnoreLockedLayers ); 03356 if (pNextLayer == NULL) 03357 { 03358 Spread* pSpread = pParent->FindParentSpread(); 03359 if (pSpread != NULL) 03360 { 03361 pNextLayer = pSpread->FindFirstLayer(); 03362 if ( (iterFlags.IgnoreInvisibleLayers && !pNextLayer->IsVisible()) || 03363 (iterFlags.IgnoreLockedLayers && pNextLayer->IsLocked()) ) 03364 pNextLayer = pNextLayer->FindNextLayer( iterFlags.IgnoreInvisibleLayers, 03365 iterFlags.IgnoreLockedLayers ); 03366 } 03367 } 03368 03369 if (pNextLayer == NULL || pNextLayer == m_pIterStartLayer) 03370 return NULL; 03371 03372 if (m_pIterStartLayer == NULL) 03373 m_pIterStartLayer = pNextLayer; 03374 03375 pCurrent = pNextLayer->FindFirstChildInk(); 03376 return IteratePostProcessNext(pCurrent, pNextLayer, iterFlags); 03377 } 03378 03379 else 03380 { 03381 pCurrent = pParent->FindFirstChildInk(); 03382 if (pCurrent != NULL) 03383 pCurrent = IteratePostProcessPrev(pCurrent, pParent, iterFlags); 03384 03385 return (NodeRenderableInk*)pCurrent; 03386 } 03387 } 03388 03389 // we're non-NULL, so post-processing is based on us. 03390 else 03391 { 03392 if (pCurrent->ShouldITransformWithChildren()) 03393 { 03394 pParent = pCurrent; 03395 pCurrent = pCurrent->FindFirstChildInk(); 03396 return IteratePostProcessNext(pCurrent, pParent, iterFlags); 03397 } 03398 else if (pCurrent->IsANodeClipView()) 03399 { 03400 return IterateNextInk(pCurrent, iterFlags); 03401 } 03402 else if (pCurrent->IS_KIND_OF(NodeBlender)) 03403 { 03404 return IterateNextInk(pCurrent, iterFlags); 03405 } 03406 else 03407 return (NodeRenderableInk*)pCurrent; 03408 } 03409 } 03410 03411 03412 03413 /******************************************************************************************** 03414 03415 > NodeRenderableInk* SelectorTool::IteratePrevInk(Node* pCurrent, 03416 IterateFlags iterFlags) 03417 03418 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 03419 Created: 07/08/2000 03420 Inputs: pCurrent the node which we're gonna start iterating from. 03421 iterFlags some flags which tell us how to iterate. 03422 Returns: the previous node in the iteration, or 03423 NULL if the given node or its parent is NULL. 03424 03425 Purpose: Iterate over the document. See notes up above for more info. 03426 Notes: * Could be made into a non-virtual function in Node or NodeRenderableInk. 03427 * Recursively calls IteratePostProcessPrev(). 03428 03429 ********************************************************************************************/ 03430 NodeRenderableInk* SelectorTool::IteratePrevInk(Node* pCurrent, IterateFlags iterFlags) 03431 { 03432 Node* pParent = (pCurrent == NULL) ? NULL : pCurrent->FindParent(); 03433 Node* pPrev = (pCurrent == NULL) ? NULL : pCurrent->FindPreviousInk(); 03434 return IteratePostProcessPrev(pPrev, pParent, iterFlags); 03435 } 03436 03437 03438 03439 /******************************************************************************************** 03440 03441 > NodeRenderableInk* SelectorTool::IteratePostProcessPrev(Node* pCurrent, Node* pParent, 03442 IterateFlags iterFlags) 03443 03444 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 03445 Created: 07/08/2000 03446 Inputs: pCurrent the node to post-process. 03447 pParent the parent of the node to post-process. 03448 iterFlags some flags which tell us how to iterate. 03449 Returns: the correct node in the iteration, based on this guess. 03450 03451 Purpose: IteratePrevInk() is brainless - it picks the previous sibling of a node. 03452 This function makes an informed decision, based on that chosen node, 03453 to get the selection-tabbing order correct. See notes above for more info. 03454 03455 Notes: * Could be made into a virtual function based in Node or NodeRenderableInk. 03456 * Recursively calls itself and IteratePrevInk(). 03457 03458 ********************************************************************************************/ 03459 NodeRenderableInk* SelectorTool::IteratePostProcessPrev(Node* pCurrent, Node* pParent, 03460 IterateFlags iterFlags) 03461 { 03462 // return NULL if we are given no parent. 03463 if (pParent == NULL) 03464 return NULL; 03465 03466 // we're NULL, so post-processing is based on our parent. 03467 if (pCurrent == NULL) 03468 { 03469 if (pParent->ShouldITransformWithChildren()) 03470 return IteratePrevInk(pParent, iterFlags); 03471 03472 else if (pParent->IsLayer()) 03473 { 03474 Layer* pPrevLayer = ((Layer*)pParent); 03475 03476 pPrevLayer = pPrevLayer->FindPrevLayer( iterFlags.IgnoreInvisibleLayers, 03477 iterFlags.IgnoreLockedLayers ); 03478 if (pPrevLayer == NULL) 03479 { 03480 Spread* pSpread = pParent->FindParentSpread(); 03481 if (pSpread != NULL) 03482 { 03483 pPrevLayer = pSpread->FindLastLayer(); 03484 if ( (iterFlags.IgnoreInvisibleLayers && !pPrevLayer->IsVisible()) || 03485 (iterFlags.IgnoreLockedLayers && pPrevLayer->IsLocked()) ) 03486 pPrevLayer = pPrevLayer->FindPrevLayer( iterFlags.IgnoreInvisibleLayers, 03487 iterFlags.IgnoreLockedLayers ); 03488 } 03489 } 03490 03491 if (pPrevLayer == NULL || pPrevLayer == m_pIterStartLayer) 03492 return NULL; 03493 03494 if (m_pIterStartLayer == NULL) 03495 m_pIterStartLayer = pPrevLayer; 03496 03497 pCurrent = pPrevLayer->FindLastChildInk(); 03498 return IteratePostProcessPrev(pCurrent, pPrevLayer, iterFlags); 03499 } 03500 03501 else 03502 { 03503 pCurrent = pParent->FindLastChildInk(); 03504 if (pCurrent != NULL) 03505 pCurrent = IteratePostProcessPrev(pCurrent, pParent, iterFlags); 03506 03507 return (NodeRenderableInk*)pCurrent; 03508 } 03509 } 03510 03511 // we're non-NULL, so post-processing is based on us. 03512 else 03513 { 03514 if (pCurrent->ShouldITransformWithChildren()) 03515 { 03516 pParent = pCurrent; 03517 pCurrent = pCurrent->FindLastChildInk(); 03518 return IteratePostProcessPrev(pCurrent, pParent, iterFlags); 03519 } 03520 else if (pCurrent->IsANodeClipView()) 03521 { 03522 return IteratePrevInk(pCurrent, iterFlags); 03523 } 03524 else if (pCurrent->IS_KIND_OF(NodeBlender)) 03525 { 03526 return IteratePrevInk(pCurrent, iterFlags); 03527 } 03528 else 03529 return (NodeRenderableInk*)pCurrent; 03530 } 03531 } 03532 03533 03534 03535 /******************************************************************************************** 03536 03537 > BOOL SelectorTool::IsRotateCentreClicked(DocCoord ClickStart) const 03538 03539 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03540 Created: 3/10/94 03541 Inputs: - 03542 Outputs: - 03543 Returns: TRUE if the current mouse click is on the rotation centre blob. 03544 Purpose: Decides whether the user has clicked the rotation centre blob, correctly 03545 handling the case where rotate blobs aren't visible. 03546 Errors: - 03547 SeeAlso: - 03548 03549 ********************************************************************************************/ 03550 03551 BOOL SelectorTool::IsRotateCentreClicked(DocCoord ClickStart) const 03552 { 03553 return fShowToolBlobs && eCurrentBlobs==ROTATE_BLOBS 03554 && !(SelectionRect.IsEmpty()) && IsNearBlob(dcRotateCentre, ClickStart); 03555 } 03556 03557 03558 03559 03560 /******************************************************************************************** 03561 03562 > BOOL SelectorTool::IsTranslateShortcut(ClickModifiers cmods) const 03563 03564 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03565 Created: 5/10/94 03566 Inputs: - 03567 Outputs: - 03568 Returns: TRUE if the translate shortcut keys are detected. 03569 Purpose: Detects whether the current mouse click modifiers denote the translate 03570 drag operation shortcut. 03571 Errors: - 03572 SeeAlso: - 03573 03574 ********************************************************************************************/ 03575 03576 BOOL SelectorTool::IsTranslateShortcut(ClickModifiers cmods) const 03577 { 03578 return cmods.Constrain && cmods.Alternative1; 03579 } 03580 03581 03582 03583 03584 /******************************************************************************************** 03585 03586 > BOOL SelectorTool::IsClickModified(ClickModifiers cmods) const 03587 03588 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03589 Created: 29/9/94 03590 Inputs: - 03591 Outputs: - 03592 Returns: TRUE if the current click is "modified". 03593 Purpose: Tests whether any of the modifiers, eg. Constrain, Adjust etc, apply to 03594 the current mouse click (as received by the OnClick function). 03595 Errors: - 03596 SeeAlso: - 03597 03598 ********************************************************************************************/ 03599 03600 BOOL SelectorTool::IsClickModified(ClickModifiers cmods) const 03601 { 03602 return cmods.Adjust || cmods.Constrain || cmods.Alternative1 || cmods.Menu; 03603 } 03604 03605 03606 03607 03608 /******************************************************************************************** 03609 03610 > BOOL SelectorTool::IsSelectUnderClick(ClickModifiers cmods) const 03611 03612 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03613 Created: 14/10/94 03614 Inputs: - 03615 Outputs: - 03616 Returns: TRUE if the current click signifies the object below the clicked object 03617 should be selected, FALSE otherwise. 03618 Purpose: Decides whether the current click is modified to be an "under" click or not 03619 Errors: - 03620 SeeAlso: SelectorTool::HandleSingleClick 03621 03622 ********************************************************************************************/ 03623 03624 BOOL SelectorTool::IsSelectUnderClick(ClickModifiers cmods) const 03625 { 03626 return (!cmods.Constrain && cmods.Alternative1); 03627 } 03628 03629 03630 03631 03632 /******************************************************************************************** 03633 03634 > BOOL SelectorTool::IsSelectMemberClick(ClickModifiers cmods) const 03635 03636 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03637 Created: 24/11/94 03638 Inputs: - 03639 Outputs: - 03640 Returns: TRUE if the last click indicated "select-inside". 03641 Purpose: Reports whether the current mouse click meant the user wanted to "select- 03642 inside" or not. 03643 Errors: - 03644 SeeAlso: SelectorTool::HandleButtonUp 03645 03646 ********************************************************************************************/ 03647 03648 BOOL SelectorTool::IsSelectMemberClick(ClickModifiers cmods) const 03649 { 03650 return (cmods.Constrain && cmods.Alternative1); 03651 } 03652 03653 03654 03655 /******************************************************************************************** 03656 03657 > BOOL SelectorTool::IsSelectLeafClick(ClickModifiers cmods) const 03658 03659 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03660 Created: 24/11/94 03661 Inputs: - 03662 Outputs: - 03663 Returns: TRUE if the last click indicated "select-leaf". 03664 Purpose: Reports whether the current mouse click meant the user wanted to "select- 03665 leaf" or not. 03666 Errors: - 03667 SeeAlso: SelectorTool::HandleButtonUp 03668 03669 ********************************************************************************************/ 03670 03671 BOOL SelectorTool::IsSelectLeafClick(ClickModifiers cmods) const 03672 { 03673 return (cmods.Constrain && !cmods.Alternative1); 03674 } 03675 03676 03677 03678 /******************************************************************************************** 03679 03680 > NodeRenderableInk* SelectorTool::FindFrom(NodeRenderableInk* pSimpleNode) const 03681 03682 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03683 Created: 11/1/95 03684 Inputs: pSimpleNode the simple node to begin searching from 03685 Outputs: - 03686 Returns: The compound object the simple node is part of, if any, or pSimpleNode 03687 if it isn't. 03688 Purpose: Front-end short-hand for NodeRenderableInk::FindCompoundFromSimple 03689 Errors: - 03690 SeeAlso: NodeRenderableInk::FindCompoundFromSimple 03691 03692 ********************************************************************************************/ 03693 03694 NodeRenderableInk* SelectorTool::FindFrom(NodeRenderableInk* pSimpleNode) const 03695 { 03696 return NodeRenderableInk::FindCompoundFromSimple(pSimpleNode); 03697 } 03698 03699 03700 03701 03702 /******************************************************************************************** 03703 03704 > NodeRenderableInk* SelectorTool::EnsureInkNext(Range* range, Node* pNode) const 03705 03706 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 03707 Created: 19/1/95 03708 Inputs: pNode Pointer to node which may or may not be ink (may even be NULL). 03709 Outputs: - 03710 Returns: A pointer to an ink node or NULL. 03711 Purpose: Ensure that the node passed is an ink node and if not find the next object 03712 in the range that IS. 03713 Errors: - 03714 03715 ********************************************************************************************/ 03716 03717 NodeRenderableInk* SelectorTool::EnsureInkNext(Range* range, Node* pNode) const 03718 { 03719 while ( pNode && !pNode->IsAnObject() ) 03720 { 03721 pNode = range->FindNext(pNode); 03722 } 03723 return (NodeRenderableInk*) pNode; 03724 } 03725 03726 03727 03728 03729 /******************************************************************************************** 03730 03731 > NodeRenderableInk* SelectorTool::EnsureInkPrev(Range* range, Node* pNode) const 03732 03733 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 03734 Created: 19/1/95 03735 Inputs: pNode Pointer to node which may or may not be ink (may even be NULL). 03736 Outputs: - 03737 Returns: A pointer to an ink node or NULL. 03738 Purpose: Ensure that the node passed is an ink node and if not find the prev object 03739 in the range that IS. 03740 Errors: - 03741 03742 ********************************************************************************************/ 03743 03744 NodeRenderableInk* SelectorTool::EnsureInkPrev(Range* range, Node* pNode) const 03745 { 03746 while ( pNode && !pNode->IsAnObject() ) 03747 { 03748 pNode = range->FindPrev(pNode); 03749 } 03750 return (NodeRenderableInk*) pNode; 03751 } 03752 03753 03754 03755 03756 /******************************************************************************************** 03757 03758 > void SelectorTool::DragMove(TransformBoundingData* pBoundingData) 03759 03760 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03761 Created: 26/8/94 03762 Inputs: pBoundingData - Pointer to the current size, position etc of the selection 03763 Purpose: This function is called by the transform operations every time the mouse 03764 moves, so that the selector tool can keep the fields in its info bar up 03765 to date. 03766 03767 ********************************************************************************************/ 03768 03769 void SelectorTool::DragMove(TransformBoundingData* pBoundingData) 03770 { 03771 // Debug check - WHY DO WE GET CALLS TO THIS FUNCTION WHEN THERE IS NOTHING SELECTED?!? 03772 if (SelectionSpread == NULL) 03773 { 03774 // TRACEUSER( "JustinF", _T("Null SelectionSpread in SelectorTool::DragMove ?\n")); 03775 return; 03776 } 03777 03778 // Delegate handling of this to the info-bar. 03779 if (pInfoBarOp != NULL) pInfoBarOp->SetEdit_OnDrag(pBoundingData, SelectionSpread); 03780 } 03781 03782 03783 03784 /******************************************************************************************** 03785 03786 > virtual BOOL SelectorTool::DragFinished(DragEndType det) 03787 03788 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03789 Created: 13/1/95 03790 Inputs: det an code indicating how the drag was ended, eg. did the 03791 user hit ESCAPE and cancel it? 03792 Outputs: - 03793 Returns: A boolean which if FALSE means "cancel that there drag, sonny". 03794 Purpose: Called by the tool base-class when a drag is finished. In the selector 03795 tool this resets the cursor and always returns success. It also makes 03796 sure that the info-bar text reflects the selection, even if the drag was 03797 cancelled with the ESCAPE key. 03798 Errors: - 03799 SeeAlso: SelectorTool::ResetCursorNow 03800 03801 ********************************************************************************************/ 03802 03803 BOOL SelectorTool::DragFinished(DragEndType det) 03804 { 03805 // Restore our normal non-drag cursor. 03806 ResetCursorNow(); 03807 03808 // If the drag was cancelled then we must manually force an update of the info-bar, 03809 // which will show incorrect bounds for the selection. 03810 if (det == DT_CANCELLED) UpdateSelectionInfo(); 03811 03812 // We always leave cancelling the drag to a capricious user. 03813 return TRUE; 03814 } 03815 03816 03817 03818 /******************************************************************************************** 03819 03820 > void SelectorTool::GetUserParams() 03821 03822 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03823 Created: 31/8/94 03824 Inputs: - 03825 Outputs: - 03826 Returns: - 03827 Purpose: Sets up the transformation data structure with default values read from 03828 the tool's info-bar, as set by the user. Individual transformations can 03829 override these settings later. 03830 Errors: - 03831 SeeAlso: - 03832 03833 ********************************************************************************************/ 03834 03835 void SelectorTool::GetUserParams() 03836 { 03837 // Get the button settings. NB. ScaleLines should be FALSE for all except the scale 03838 // transform, or it affects lines of thickness > 0. 03839 tdParams.ScaleLines = FALSE; 03840 tdParams.LockAspect = pInfoBarOp->IsAspectLocked(); 03841 tdParams.StartBlob = nClickedBoundsBlob; 03842 03843 // tdParams.LeaveCopy = pInfoBarOp->ShouldLeaveCopy(); 03844 // tdParams.TransFills = pInfoBarOp->ShouldTransFills(); 03845 tdParams.LeaveCopy = FALSE; 03846 tdParams.TransFills = TRUE; 03847 03848 // Set the default transform centre to the centre of the select object(s), if any. 03849 /* if (SelectionSpread != NULL && !SelectionRect.IsEmpty()) 03850 { 03851 DocRect dr = InflatedSelRect(); 03852 tdParams.CentreOfTrans.x = (dr.lo.x + dr.hi.x) / 2; 03853 tdParams.CentreOfTrans.y = (dr.lo.y + dr.hi.y) / 2; 03854 } */ 03855 03856 tdParams.CentreOfTrans = dcRotateCentre; 03857 tdParams.pRange = 0; 03858 } 03859 03860 03861 03862 /******************************************************************************************** 03863 03864 > void SelectorTool::DoScale() 03865 03866 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03867 Created: 31/8/94 03868 Inputs: - 03869 Outputs: - 03870 Returns: - 03871 Purpose: Runs a scale transformation drag of the selection. 03872 Errors: - 03873 SeeAlso: - 03874 03875 ********************************************************************************************/ 03876 03877 void SelectorTool::DoScale() 03878 { 03879 // Get the setting of the "Scale Lines" button, which should only be effective in a 03880 // scale transform. 03881 tdParams.ScaleLines = pInfoBarOp->ShouldScaleLines(); 03882 03883 // Set the centre of the transform to the opposite blob. 03884 tdParams.CentreOfTrans = GetSelPosNearBlob(9 - nClickedBoundsBlob); 03885 03886 // Run the drag. 03887 StartXformDrag(new OpScaleTrans, DRAGTYPE_AUTOSCROLL); 03888 } 03889 03890 03891 03892 /******************************************************************************************** 03893 03894 > void DoSquash() 03895 03896 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03897 Created: 31/8/94 03898 Inputs: - 03899 Outputs: - 03900 Returns: - 03901 Purpose: Runs a squash transformation drag on the selection. 03902 Errors: - 03903 SeeAlso: - 03904 03905 ********************************************************************************************/ 03906 03907 void SelectorTool::DoSquash() 03908 { 03909 tdParams.CentreOfTrans = GetSelPosNearBlob(9 - nClickedBoundsBlob); 03910 StartXformDrag(new OpSquashTrans, DRAGTYPE_AUTOSCROLL); 03911 } 03912 03913 03914 03915 /******************************************************************************************** 03916 03917 > void SelectorTool::DoShear() 03918 03919 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03920 Created: 31/8/94 03921 Inputs: - 03922 Outputs: - 03923 Returns: - 03924 Purpose: Runs a shear transformation drag on the selection. 03925 Errors: - 03926 SeeAlso: - 03927 03928 ********************************************************************************************/ 03929 03930 void SelectorTool::DoShear() 03931 { 03932 tdParams.CentreOfTrans = GetSelPosNearBlob(9 - nClickedBoundsBlob); 03933 StartXformDrag(new OpShearTrans, DRAGTYPE_AUTOSCROLL); 03934 } 03935 03936 03937 03938 /******************************************************************************************** 03939 03940 > void SelectorTool::DoTranslate() 03941 03942 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03943 Created: 31/8/94 03944 Inputs: - 03945 Outputs: - 03946 Returns: - 03947 Purpose: Runs a translation transformation drag on the selection. 03948 Errors: - 03949 SeeAlso: - 03950 03951 ********************************************************************************************/ 03952 03953 void SelectorTool::DoTranslate() 03954 { 03955 // Override the default transform parameters. 03956 tdParams.CentreOfTrans = ClickStart; 03957 03958 // Set drag pointer shape. 03959 if (CursorStackID!=CURSORID_UNSET) 03960 CursorStack::GSetTop(pALLCursor,CursorStackID); 03961 03962 // Start the translation. 03963 // WEBSTER - markn 14/2/97 03964 // Disable OLE drags in Webster 03965 #ifndef WEBSTER 03966 #if (_OLE_VER >= 0x200) 03967 if (OLEPrefs::DoOLEDrags()) 03968 StartXformDrag(new OpTranslateTrans, DRAGTYPE_OLESCROLL); 03969 else 03970 #endif 03971 #endif // WEBSTER 03972 StartXformDrag(new OpTranslateTrans, DRAGTYPE_AUTOSCROLL); 03973 } 03974 03975 03976 03977 /******************************************************************************************** 03978 03979 > void SelectorTool::DoRotate() 03980 03981 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 03982 Created: 31/8/94 03983 Inputs: - 03984 Outputs: - 03985 Returns: - 03986 Purpose: Runs a rotation transformation drag on the selection. 03987 Errors: - 03988 SeeAlso: - 03989 03990 ********************************************************************************************/ 03991 03992 void SelectorTool::DoRotate() 03993 { 03994 // Set the rotation centre. 03995 // tdParams.CentreOfTrans = dcRotateCentre; 03996 03997 // Do the rotation. 03998 StartXformDrag(new OpRotateTrans, DRAGTYPE_NOSCROLL); 03999 } 04000 04001 04002 04003 /******************************************************************************************** 04004 04005 > void SelectorTool::DoDragBox() 04006 04007 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 04008 Created: 31/8/94 04009 Inputs: - 04010 Outputs: - 04011 Returns: - 04012 Purpose: Runs a selector tool drag-box operation. 04013 Errors: - 04014 SeeAlso: - 04015 04016 ********************************************************************************************/ 04017 04018 void SelectorTool::DoDragBox() 04019 { 04020 OpSelectorDragBox* pOpDragBox = new OpSelectorDragBox; 04021 if (pOpDragBox == NULL) 04022 { 04023 InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK)); 04024 } 04025 else 04026 { 04027 pOpDragBox->StartDragBox(StartSpread, ClickStart, ClickMods); 04028 } 04029 } 04030 04031 04032 04033 /******************************************************************************************** 04034 04035 > void SelectorTool::DoDragRotateCentre() 04036 04037 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04038 Created: 31/8/94 04039 Inputs: - 04040 Outputs: - 04041 Returns: - 04042 Purpose: Runs a drag operation for moving the centre of rotation blob around. 04043 Errors: - 04044 SeeAlso: - 04045 04046 ********************************************************************************************/ 04047 04048 void SelectorTool::DoDragRotateCentre() 04049 { 04050 OpDragRotateCentre* pOp = new OpDragRotateCentre; 04051 if (pOp == NULL) 04052 { 04053 InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK)); 04054 } 04055 else 04056 { 04057 // Note that we have to pass the rotate centre position as the point where the 04058 // drag was initiated, to ensure that it is removed cleanly. 04059 pOp->StartDragCentreBlob(StartSpread, dcRotateCentre, ClickMods); 04060 } 04061 } 04062 04063 04064 04065 /******************************************************************************************** 04066 04067 > BOOL SelectorTool::StartXformDrag(TransOperation* pXformDragOp, DragType dragtype) 04068 04069 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04070 Created: 31/8/94 04071 Inputs: pXformDragOp pointer to a transformation mouse-drag operation 04072 dragtype what kind of drag to do, eg. DRAGTYPE_AUTOSCROLL 04073 Outputs: - 04074 Returns: TRUE if the drag is successful. 04075 Purpose: Fills in any remaining fields of the transformation data structure(s) and 04076 calls the Tranform virtual function that initiates a mouse drag. 04077 Errors: - 04078 SeeAlso: - 04079 04080 ********************************************************************************************/ 04081 04082 BOOL SelectorTool::StartXformDrag(TransOperation* pXformDragOp, DragType dragtype) 04083 { 04084 // Couldn't allocate the op? 04085 if (pXformDragOp == NULL) 04086 { 04087 InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK)); 04088 return FALSE; 04089 } 04090 04091 // Get the current DocView (there must be one or we wouldn't be here). 04092 DocView* pDocView = DocView::GetCurrent(); 04093 ERROR2IF(pDocView == NULL, FALSE, "Null current DocView in SelectorTool::StartXformDrag"); 04094 04095 ERROR3IF(tdParams.StartBlob < 0 || tdParams.StartBlob > 8, "Bad blob number in SelectorTool::StartXformDrag"); 04096 04097 04098 // Calculate the "offset" from a bounds blob to the corresponding point on a selected object. 04099 // When we're using rectangles including effects of attributes, this is easy, as the offset 04100 // is a constant from the "inclusive" rectangle. However, when using the "exclusive" rect, 04101 // we have to factor in the distance from the appropriate edge of the exclusive rect to 04102 // the same edge of the inclusive rect. (If there is an object with large line thickness on 04103 // one side of the selection, and one with a small line width on the other side, the 04104 // gap between the two rectangles is different on each side!) 04105 04106 // Get the distance of the gap between the selected object (inclusive rect) and its blobs. 04107 INT32 nOffset = GetBlobBorderSize(); 04108 04109 // Get the inside and outside rectangles. The DontConsiderAttrs flag may be FALSE, in which case, 04110 // the 2 rectangles will be identical. Thus the code in the switch statements below is generic, 04111 // and will do nothing extra if we're using the inclusive bounds. 04112 DocRect Small = GetSelectionBounds(pInfoBarOp->DontConsiderAttrs()); 04113 DocRect Large = GetSelectionBounds(FALSE); 04114 04115 DocCoord dcOffset(0, 0); // Default to offsets of 0 04116 04117 // Adjust the sign of x and/or y according to the blob number in the transform 04118 // parameters. 04119 switch (tdParams.StartBlob) // Pass 1: Adjust the X offset if necessary 04120 { 04121 // Cases 0 (no blob), 2, 7 all leave x as zero 04122 04123 case 1: // Left side 04124 case 4: 04125 case 6: 04126 dcOffset.x = nOffset + (Small.lo.x - Large.lo.x); 04127 break; 04128 04129 case 3: // Right side 04130 case 5: 04131 case 8: 04132 dcOffset.x = -nOffset + (Small.hi.x - Large.hi.x); 04133 break; 04134 } 04135 04136 switch (tdParams.StartBlob) // Pass 2: Adjust the Y offset if necessary 04137 { 04138 // Cases 0 (no blob), 4, and 5 all leave y as zero 04139 04140 case 1: // Top edge 04141 case 2: 04142 case 3: 04143 dcOffset.y = -nOffset + (Small.hi.y - Large.hi.y); 04144 break; 04145 04146 case 6: // Bottom edge 04147 case 7: 04148 case 8: 04149 dcOffset.y = nOffset + (Small.lo.y - Large.lo.y); 04150 break; 04151 } 04152 04153 // Fill the Transform Bounding Data structure up here 04154 DocRect SelRect = GetSelectionBounds(pInfoBarOp->DontConsiderAttrs()); 04155 04156 BoundingData.x = SelRect.lo.x; 04157 BoundingData.y = SelRect.lo.y; 04158 BoundingData.Width = SelRect.Width(); 04159 BoundingData.Height = SelRect.Height(); 04160 BoundingData.XScale = (FIXED16) 1; 04161 BoundingData.YScale = (FIXED16) 1; 04162 BoundingData.Rotation = (ANGLE) 0; 04163 BoundingData.Shear = (ANGLE) 0; 04164 04165 // Run the transformation drag operation and return success code. 04166 pXformDragOp->DragStarted(&tdParams, this, &BoundingData, ClickStart, 04167 StartSpread, ClickMods, dcOffset, NULL, dragtype); 04168 return TRUE; 04169 } 04170 04171 04172 04173 /******************************************************************************************** 04174 04175 > BOOL SelectorTool::StartXformImmediate(const char* chOpToken, void* pvParam2) 04176 04177 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04178 Created: 31/8/94 04179 Inputs: chOpToken the token of the OpDescriptor associated with the 04180 transformation Operation to be run. 04181 pvParam2 a parameter to pass on to the Operation, if needed. 04182 Outputs: - 04183 Returns: TRUE if the transformation is successful. 04184 Purpose: Runs a tranformation Operation in response to the user changing settings 04185 on the tool's info-bar. This works through the traditional 'Invoke' 04186 method. 04187 Errors: - 04188 SeeAlso: - 04189 04190 ********************************************************************************************/ 04191 04192 BOOL SelectorTool::StartXformImmediate(const TCHAR* chOpToken, void* pvParam2) 04193 { 04194 // Try to find the OpDescriptor associated with the given token. 04195 OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor( const_cast<TCHAR *>(chOpToken) ); 04196 04197 // Did we find it? 04198 if (pOpDesc == NULL) 04199 { 04200 // TRACEUSER( "JustinF", _T("Couldn't find token %s\n"), (LPCTSTR) chOpToken); 04201 ERROR3("Couldn't find OpDescriptor in Selector::StartXformImmediate"); 04202 return FALSE; 04203 } 04204 04205 // Run the transform. 04206 OpParam opparam(&tdParams, pvParam2); 04207 pOpDesc->Invoke( &opparam ); 04208 04209 return TRUE; 04210 } 04211 04212 04213 04214 /******************************************************************************************** 04215 04216 > void SelectorTool::RotationCentreDragged(const DocCoord& dcNewPos) 04217 04218 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04219 Created: 31/8/94 04220 Inputs: dcNewPos the new centre of rotation (in spread coords) 04221 Outputs: - 04222 Returns: - 04223 Purpose: Updates the centre of rotation, marking it as now 'valid'. This is 04224 called by the drag operation on successful completion. 04225 Errors: - 04226 SeeAlso: - 04227 04228 ********************************************************************************************/ 04229 04230 void SelectorTool::RotationCentreDragged(const DocCoord& dcNewPos) 04231 { 04232 dcRotateCentre = dcNewPos; 04233 fRotateCentreIsValid = TRUE; 04234 fValidSelectedBlob = FALSE; 04235 } 04236 04237 04238 04239 /******************************************************************************************** 04240 04241 > void SelectorTool::InvalidateRotationCentre() 04242 04243 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04244 Created: 28/9/94 04245 Inputs: - 04246 Outputs: - 04247 Returns: - 04248 Purpose: Tells the selector tool that it should position the centre of rotation 04249 in the middle of the selection's bounds, the next time there is a 04250 selection. 04251 Errors: - 04252 SeeAlso: OpSelectorDragBox::DragFinished 04253 04254 ********************************************************************************************/ 04255 04256 void SelectorTool::InvalidateRotationCentre() 04257 { 04258 fRotateCentreIsValid = FALSE; 04259 } 04260 04261 04262 04263 /******************************************************************************************** 04264 04265 > void SelectorTool::BoundsButtonChange() 04266 04267 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04268 Created: 31/8/94 04269 Inputs: - 04270 Outputs: - 04271 Returns: - 04272 Purpose: Called by the info-bar when the user has clicked on the 'Show Bounds/Rotate 04273 Blobs' button. Handles keeping a record of the setting, changing the 04274 blobs on-screen etc. 04275 Errors: - 04276 SeeAlso: - 04277 04278 ********************************************************************************************/ 04279 04280 void SelectorTool::BoundsButtonChange() 04281 { 04282 if (fShowToolBlobs) 04283 { 04284 // Need to hide the tool's bounds/rotate blobs 04285 pBlobManager->RenderToolBlobsOff(this, SelectionSpread, NULL); 04286 04287 // Record that we aren't showing our tool blobs. 04288 fBlobPref &= ~1; 04289 fShowToolBlobs = FALSE; 04290 } 04291 else 04292 { 04293 // Record which blobs are being displayed. 04294 fBlobPref |= 1; 04295 eCurrentBlobs = (pInfoBarOp->InRotateMode()) ? ROTATE_BLOBS : BOUNDS_BLOBS; 04296 fShowToolBlobs = TRUE; 04297 04298 // Need to draw the tool blobs on if they are needed 04299 pBlobManager->RenderToolBlobsOn(this, SelectionSpread, NULL); 04300 } 04301 } 04302 04303 04304 04305 /******************************************************************************************** 04306 04307 > void SelectorTool::RotateButtonChange(BOOL fNewState) 04308 04309 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04310 Created: 31/8/94 04311 Inputs: fNewState TRUE if the button is down, FALSE if it is up. 04312 Outputs: - 04313 Returns: - 04314 Purpose: Called by the info-bar when the user clicks on the "Rotate Blobs" button, 04315 that switches between rotate blobs and bounds blobs. Handles updating 04316 on-screen blobs etc. 04317 Errors: - 04318 SeeAlso: - 04319 04320 ********************************************************************************************/ 04321 04322 void SelectorTool::RotateButtonChange(BOOL fNewState) 04323 { 04324 // Hide the current blobs. 04325 pBlobManager->RenderToolBlobsOff(this, SelectionSpread, NULL); 04326 04327 // If we aren't currently displaying tool blobs then switch them on. 04328 if (!fShowToolBlobs) 04329 { 04330 // We "fake" a click on the bounds button. Note that BoundsButtonChange() will render 04331 // the new blobs and update eCurrentBlobs correctly. 04332 if (pInfoBarOp != NULL) 04333 { 04334 pInfoBarOp->SetLongGadgetValue(_R(IDC_SEL_SHOWBOUNDSBLOBS), TRUE, FALSE); 04335 } 04336 BoundsButtonChange(); 04337 return; 04338 } 04339 04340 // Update our records of which blobs should be visible. 04341 eCurrentBlobs = (fNewState) ? ROTATE_BLOBS : BOUNDS_BLOBS; 04342 04343 // Draw the new blobs. 04344 pBlobManager->RenderToolBlobsOn(this, SelectionSpread, NULL); 04345 } 04346 04347 04348 04349 /******************************************************************************************** 04350 04351 > void SelectorTool::FlipButtonChange(BOOL fIsVertical) 04352 04353 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04354 Created: 31/8/94 04355 Inputs: fIsVertical TRUE if it is a vertical flip, FALSE if it is horizontal 04356 Outputs: - 04357 Returns: - 04358 Purpose: Called by the info-bar when the user clicks one of the "flip" buttons. 04359 Gets default transformation parameters and starts an "immediate" 04360 transformation operation. 04361 Errors: - 04362 SeeAlso: - 04363 04364 ********************************************************************************************/ 04365 04366 void SelectorTool::FlipButtonChange(BOOL fIsVertical) 04367 { 04368 if (SelectionSpread != NULL) 04369 { 04370 Range* Selection = GetApplication()->FindSelection(); 04371 RangeControl TransFlags = Selection->GetRangeControlFlags(); 04372 TransFlags.IgnoreNoneRenderable=TRUE; 04373 TransFlags.IgnoreInvisibleLayers = TRUE; 04374 Selection->SetRangeControl(TransFlags); 04375 SliceHelper::ModifySelectionToContainWholeButtonElements(); 04376 04377 04378 // Get the info-bar settings. 04379 GetUserParams(); 04380 04381 // Horizontal flips have StartBlob = 2 or 7, vertical flips have StartBlob = 4 or 5. 04382 tdParams.StartBlob = (fIsVertical) ? 4 : 2; 04383 StartXformImmediate( OPTOKEN_FLIP, NULL ); 04384 04385 04386 SliceHelper::RestoreSelection(); 04387 } 04388 } 04389 04390 04391 04392 /******************************************************************************************** 04393 04394 > void SelectorTool::SetRotateCentre(INT32 nBlob) 04395 04396 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04397 Created: 16/11/94 04398 Inputs: nBlob the bounds blob identifier which indicates the new position of 04399 the rotation centre around the selected object(s). If zero then 04400 the rotation centre is set to the centre of the selection. 04401 Outputs: - 04402 Returns: - 04403 Purpose: Responds to a click on the "telephone keypad" by moving the centre of 04404 rotation to the given position, as described by a blob number. The blob 04405 itself will be redrawn if necessary. 04406 Errors: - 04407 SeeAlso: - 04408 04409 ********************************************************************************************/ 04410 04411 void SelectorTool::SetRotateCentre(INT32 nBlob) 04412 { 04413 // If there is nothing selected then ignore this call (the "telephone keypad" should 04414 // be disabled. 04415 if (SelectionSpread == NULL) return; 04416 04417 // Remember which blob was chosen as the rotation centre. This can be useful for other 04418 // transformations. 04419 nLastSelectedBlob = nBlob; 04420 fValidSelectedBlob = TRUE; 04421 04422 // Test if the rotate centre blob is currently being displayed. If it is then hide it. 04423 if (eCurrentBlobs==ROTATE_BLOBS && fShowToolBlobs) 04424 { 04425 // Get an XOR render region (may be the first of many). 04426 DocRect drClip = OpDragRotateCentre::CalcBlobClipRect(dcRotateCentre); 04427 RenderRegion* pRegion = DocView::RenderOnTop(&drClip, SelectionSpread, ClippedEOR); 04428 while (pRegion != NULL) 04429 { 04430 // Draw the blob in each render-region. 04431 RenderRotateCentre(pRegion, dcRotateCentre); 04432 pRegion = DocView::GetNextOnTop(&drClip); 04433 } 04434 } 04435 04436 // Move to the given edge/side of the selected object(s) (or the center blob if nBlob==0) 04437 dcRotateCentre = GetSelPosNearBlob(nBlob); 04438 04439 // Mark the rotation centre as "valid", ie. positively set by the user. 04440 fRotateCentreIsValid = TRUE; 04441 04442 // Now redraw it if it was previously being displayed. 04443 if (eCurrentBlobs==ROTATE_BLOBS && fShowToolBlobs) 04444 { 04445 // Get an XOR render region (may be the first of many). 04446 DocRect drClip = OpDragRotateCentre::CalcBlobClipRect(dcRotateCentre); 04447 RenderRegion* pRegion = DocView::RenderOnTop(&drClip, SelectionSpread, ClippedEOR); 04448 while (pRegion != NULL) 04449 { 04450 // Draw the blob in each render-region. 04451 RenderRotateCentre(pRegion, dcRotateCentre); 04452 pRegion = DocView::GetNextOnTop(&drClip); 04453 } 04454 } 04455 } 04456 04457 04458 04459 /******************************************************************************************** 04460 04461 > void SelectorTool::DoTranslateImmediate(MILLIPOINT nXpos, MILLIPOINT nYpos) 04462 04463 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04464 Created: 16/11/94 04465 Inputs: nXpos new position of selection, in millipoints 04466 nYpos 04467 Outputs: - 04468 Returns: - 04469 Purpose: Runs a translation of the selected object(s) initiated from the info-bar. 04470 Errors: - 04471 SeeAlso: - 04472 04473 ********************************************************************************************/ 04474 04475 void SelectorTool::DoTranslateImmediate(MILLIPOINT nXpos, MILLIPOINT nYpos) 04476 { 04477 TRACEUSER( "Matt", _T("Translate\n")); 04478 04479 // SliceHelper::ModifySelectionToContainWholeButtonElements(); 04480 04481 // Check here if the selection would be moved off the pasteboard? 04482 04483 // Package parameters and transform. 04484 nClickedBoundsBlob = 0; 04485 GetUserParams(); 04486 04487 DocRect SelRect = GetSelectionBounds(pInfoBarOp->DontConsiderAttrs()); 04488 04489 DocCoord coord (nXpos - SelRect.lo.x, nYpos - SelRect.lo.y); 04490 StartXformImmediate( OPTOKEN_TRANSLATE, &coord); 04491 04492 04493 // SliceHelper::RestoreSelection(); 04494 } 04495 04496 04497 04498 /******************************************************************************************** 04499 04500 > void SelectorTool::DoScaleImmediate(MILLIPOINT nWidth, MILLIPOINT nHeight) 04501 04502 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04503 Created: 16/11/94 04504 Inputs: nWidth new dimensions of the selection, in millipoints 04505 nHeight 04506 Outputs: - 04507 Returns: - 04508 Purpose: Scales the selected object(s) as initiated from the info-bar. 04509 Errors: - 04510 SeeAlso: - 04511 04512 ********************************************************************************************/ 04513 04514 void SelectorTool::DoScaleImmediate(MILLIPOINT nWidth, MILLIPOINT nHeight ) 04515 { 04516 // TRACEUSER( "JustinF", _T("Scale\n")); 04517 04518 // Work out the relative change in scale. 04519 DocRect SelRect = GetSelectionBounds(pInfoBarOp->DontConsiderAttrs()); 04520 double fpXscale = double(nWidth) / SelRect.Width(); 04521 double fpYscale = double(nHeight) / SelRect.Height(); 04522 04523 // Check if the scale will under- or over-flow the fixed-point numbers used in the 04524 // transform system. 04525 if (BeyondFixedRange(fpXscale) || BeyondFixedRange(fpYscale)) 04526 { 04527 TRACEUSER( "JustinF", _T("Under/overflow in SelectorTool::DoScaleImmediate\n")); 04528 pInfoBarOp->ReportEditError(_R(IDS_SEL_ERROR_TOO_SMALL_OR_LARGE)); 04529 return; 04530 } 04531 04532 // Work out the centre of the transform. 04533 nClickedBoundsBlob = (fValidSelectedBlob) ? nLastSelectedBlob : 0; 04534 GetUserParams(); 04535 04536 // Read the scale lines setting from the info bar, overriding the default setting of 04537 // FALSE. 04538 tdParams.ScaleLines = pInfoBarOp->ShouldScaleLines(); 04539 04540 // Build up the transform's parameter block and do it. 04541 FIXED16 fxParams[2]; 04542 fxParams[0] = fpXscale; 04543 fxParams[1] = fpYscale; 04544 04545 04546 StartXformImmediate( OPTOKEN_SCALE, &fxParams); 04547 } 04548 04549 04550 04551 /******************************************************************************************** 04552 04553 > void SelectorTool::DoScaleImmediate(MILLIPOINT nWidth, MILLIPOINT nHeight, BOOL Other) 04554 04555 Author: Harrison_Ainsworth (Xara Group Ltd) <camelotdev@xara.com> 04556 Created: 7/11/97 04557 Inputs: nWidth new dimensions of the selection, in millipoints 04558 nHeight 04559 BOOL extra param to distinguish this function from the above 04560 Outputs: - 04561 Returns: - 04562 Purpose: As the previous DoScaleImmediate() ( Scales the selected object(s) as 04563 initiated by the info-bar ), except omitting calculation of the 04564 relative change in scale, and filling the transform's parameter block 04565 with the width & height instead, and sending a different optoken. 04566 Errors: - 04567 SeeAlso: DoScaleImmediate(MILLIPOINT nWidth, MILLIPOINT nHeight) 04568 04569 ********************************************************************************************/ 04570 04571 /*void SelectorTool::DoScaleImmediate(MILLIPOINT nWidth, MILLIPOINT nHeight, BOOL) 04572 { 04573 04574 // Work out the centre of the transform. 04575 nClickedBoundsBlob = (fValidSelectedBlob) ? nLastSelectedBlob : 0; 04576 GetUserParams(); 04577 04578 04579 // Read the scale lines setting from the info bar, overriding the default setting of 04580 // FALSE. 04581 tdParams.ScaleLines = pInfoBarOp->ShouldScaleLines(); 04582 04583 04584 // Build up the transform's parameter block 04585 MILLIPOINT fxParams[2]; 04586 fxParams[0] = nWidth; 04587 fxParams[1] = nHeight; 04588 04589 04590 StartXformImmediate(OPTOKEN_SCALE2, &fxParams); 04591 04592 } 04593 */ 04594 04595 04596 04597 /******************************************************************************************** 04598 04599 > void SelectorTool::DoScalePercentImmediate(double nWpercent, double nHpercent) 04600 04601 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04602 Created: 16/11/94 04603 Inputs: nWpercent the new dimensions of the selection as a percentage of 04604 nHpercent the old dimensions. Note that these values may be 04605 negative, in which case the object will be flipped 04606 in the relevant axis as well as scaled. Values near 04607 to 0 (less than 0.01) will be clipped to +/- 0.01 04608 Outputs: - 04609 Returns: - 04610 Purpose: Scales the selection as initiated from the info-bar. 04611 Errors: - 04612 SeeAlso: - 04613 04614 ********************************************************************************************/ 04615 04616 void SelectorTool::DoScalePercentImmediate(double nWPercent, double nHPercent) 04617 { 04618 // TRACEUSER( "JustinF", _T("ScalePercent\n")); 04619 04620 // Work out the relative change in scale and check if its in (reasonable) range. 04621 double fpXscale = nWPercent / 100.0; 04622 double fpYscale = nHPercent / 100.0; 04623 if (BeyondFixedRange(fpXscale) || BeyondFixedRange(fpYscale) || 04624 fpXscale > 100 || fpYscale > 100) 04625 { 04626 TRACEUSER( "JustinF", _T("Under/overflow in SelectorTool::DoScalePercentImmediate\n")); 04627 pInfoBarOp->ReportEditError(_R(IDS_SEL_ERROR_TOO_SMALL_OR_LARGE)); 04628 return; 04629 } 04630 04631 // Work out the centre of the transform. 04632 nClickedBoundsBlob = (fValidSelectedBlob) ? nLastSelectedBlob : 0; 04633 GetUserParams(); 04634 04635 // Read the scale lines setting from the info bar, overriding the default setting of 04636 // FALSE. 04637 tdParams.ScaleLines = pInfoBarOp->ShouldScaleLines(); 04638 04639 // Package up the transformation parameters and do it. 04640 FIXED16 fxParams[2]; 04641 fxParams[0] = fpXscale; 04642 fxParams[1] = fpYscale; 04643 04644 StartXformImmediate( OPTOKEN_SCALE, fxParams); 04645 } 04646 04647 04648 04649 /******************************************************************************************** 04650 04651 > void SelectorTool::DoRotateImmediate(ANGLE nAngle) 04652 04653 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04654 Created: 16/11/94 04655 Inputs: nAngle the angle (in degrees) to rotate the selection by 04656 Outputs: - 04657 Returns: - 04658 Purpose: Rotates the selection as initiated from the info-bar. 04659 Errors: - 04660 SeeAlso: - 04661 04662 ********************************************************************************************/ 04663 04664 void SelectorTool::DoRotateImmediate(ANGLE nAngle) 04665 { 04666 // TRACEUSER( "JustinF", _T("Rotate\n")); 04667 04668 nClickedBoundsBlob = 0; 04669 GetUserParams(); 04670 04671 StartXformImmediate( OPTOKEN_ROTATE, &nAngle); 04672 } 04673 04674 04675 04676 /******************************************************************************************** 04677 04678 > void SelectorTool::DoShearImmediate(ANGLE nAngle) 04679 04680 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04681 Created: 16/11/94 04682 Inputs: nAngle the amount (in degrees) to shear the selection by 04683 Outputs: - 04684 Returns: - 04685 Purpose: Shears the selection, as initiated from the info-bar. 04686 Errors: - 04687 SeeAlso: - 04688 04689 ********************************************************************************************/ 04690 04691 void SelectorTool::DoShearImmediate(ANGLE nAngle) 04692 { 04693 // TRACEUSER( "JustinF", _T("Shear\n")); 04694 04695 if (fValidSelectedBlob && 04696 (nLastSelectedBlob == 2 || 04697 nLastSelectedBlob == 4 || 04698 nLastSelectedBlob == 5 || 04699 nLastSelectedBlob == 7)) 04700 { 04701 nClickedBoundsBlob = nLastSelectedBlob; 04702 } 04703 else 04704 { 04705 nClickedBoundsBlob = 7; // TEMP: until we work out an axis for shearing? 04706 } 04707 04708 GetUserParams(); 04709 StartXformImmediate( OPTOKEN_SHEAR, &nAngle); 04710 } 04711 04712 04713 04714 /******************************************************************************************** 04715 04716 > void SelectorTool::PublicDoTranslate() 04717 04718 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 04719 Created: 22 May 2000 04720 Inputs: 04721 Outputs: 04722 Returns: 04723 Purpose: 04724 Errors: 04725 See also: 04726 04727 ********************************************************************************************/ 04728 void SelectorTool::PublicDoTranslate() 04729 { 04730 // only do a drag if there is a selection. 04731 if (SelectionSpread != NULL) 04732 { 04733 nClickedBoundsBlob = 0; 04734 GetUserParams(); 04735 DoTranslate(); 04736 } 04737 } 04738 04739 04740 04741 /******************************************************************************************** 04742 04743 > void SelectorTool::ResetDefaults() 04744 04745 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04746 Created: 8/9/94 04747 Inputs: - 04748 Outputs: - 04749 Returns: - 04750 Purpose: Called after a click or drag event if the tool's options should be set back 04751 to certain defaults. For example, if the selection is changed to a new 04752 object then rotate blobs should be switched back to bounds blobs and 04753 the "Leave Copy" button should be OFF. 04754 Errors: - 04755 SeeAlso: - 04756 04757 ********************************************************************************************/ 04758 04759 void SelectorTool::ResetDefaults() 04760 { 04761 // Karim 16/10/2000 04762 // We don't want to worry about resetting blobs and stuff 04763 // unless we're actually the current tool in use. 04764 if (Tool::GetCurrentID() != TOOLID_SELECTOR) 04765 return; 04766 04767 // If necessary switch back to bounds blobs. 04768 if (eCurrentBlobs != BOUNDS_BLOBS) 04769 { 04770 // "Fake" a click on the rotate toggle. 04771 pInfoBarOp->SetRotateMode(FALSE); 04772 RotateButtonChange(FALSE); 04773 } 04774 04775 /* 04776 // If necessary switch the "Leave Copy" button to OFF. 04777 if (pInfoBarOp->ShouldLeaveCopy()) 04778 { 04779 // "Fake" a click on the LeaveCopy button. 04780 pInfoBarOp->SetLeaveCopy(FALSE); 04781 } 04782 */ 04783 } 04784 04785 04786 04787 /******************************************************************************************** 04788 04789 > Spread* SelectorTool::GetSelectionSpread() const 04790 04791 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04792 Created: 31/8/94 04793 Inputs: - 04794 Outputs: - 04795 Returns: A spread pointer, ho ho, or NULL. 04796 Purpose: Returns a pointer to the spread containing the currently selected object(s). 04797 Used internally by the info-bar. 04798 Errors: - 04799 SeeAlso: - 04800 04801 ********************************************************************************************/ 04802 04803 Spread* SelectorTool::GetSelectionSpread() const 04804 { 04805 return SelectionSpread; 04806 } 04807 04808 04809 04810 /******************************************************************************************** 04811 04812 > const DocRect& SelectorTool::GetSelectionBounds(BOOL WithNoAttrs = FALSE) const 04813 04814 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04815 Created: 31/8/94 04816 Inputs: WithNoAttrs - TRUE if you want the bounds of the selection, exclusive 04817 of attribute effects such as line width 04818 FALSE (the default) if you want the full bounding box including attributes. 04819 Outputs: - 04820 Returns: A rectangle. 04821 Purpose: Returns a reference to the bounds of the selected object(s). This will be 04822 empty if there is no current selection. Used internally by the info-bar. 04823 Errors: - 04824 SeeAlso: - 04825 04826 ********************************************************************************************/ 04827 04828 const DocRect& SelectorTool::GetSelectionBounds(BOOL WithNoAttrs) const 04829 { 04830 if (WithNoAttrs) 04831 return(SelectionRectNoAttr); 04832 04833 return SelectionRect; 04834 } 04835 04836 04837 04838 /******************************************************************************************** 04839 04840 > const BOOL SelectorTool::GetAreaDetails(BOOL WithNoAttrs, XLONG* pxlArea, XLONG* pxlPerim) const 04841 04842 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 04843 Created: 03/03/2005 04844 Inputs: WithNoAttrs - TRUE if you want the area exclusive 04845 of attribute effects such as line width 04846 FALSE if you want the area including attributes 04847 Outputs: xlArea - area in millipoints 04848 xlPerim - length of perimeter in millipoints 04849 Returns: TRUE if eveyrthing went OK 04850 Purpose: Returns a reference to the bounds of the selected object(s). This will be 04851 empty if there is no current selection. Used internally by the info-bar. 04852 Errors: - 04853 SeeAlso: - 04854 04855 ********************************************************************************************/ 04856 04857 const BOOL SelectorTool::GetAreaDetails(BOOL WithNoAttrs, XLONG* pxlArea, XLONG* pxlPerim) const 04858 { 04859 *pxlArea = xlSelectionArea; 04860 *pxlPerim = xlSelectionPerim; 04861 return TRUE; 04862 } 04863 04864 04865 04866 /******************************************************************************************** 04867 04868 > void SelectorTool::SelectionHasChanged() 04869 04870 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 04871 Created: 21/7/94 04872 04873 Purpose: Called when the selection changes, allowing the selector tool to do things 04874 like update its tools blobs etc 04875 04876 ********************************************************************************************/ 04877 04878 void SelectorTool::SelectionHasChanged(BOOL DontUpdate) 04879 { 04880 // If necessary remove the blobs. 04881 pBlobManager->RenderToolBlobsOff(this, SelectionSpread, NULL); 04882 04883 // After interrogating the selection manager, is there now a selection? 04884 if (UpdateSelectionInfo()) 04885 { 04886 // Yes. Draw the tool blobs back in again, if there's a selection. 04887 pBlobManager->RenderToolBlobsOn(this, SelectionSpread, NULL); 04888 } 04889 04890 // Was the cursor over a blob? If so we must reset it. 04891 // Check if the mouse is within the "selected" DocView. 04892 Spread* pSpread; 04893 DocCoord dcMousePos; 04894 if (DocView::GetCurrentMousePos(&pSpread, &dcMousePos)) 04895 { 04896 // Call nice central routine to figure out what status text to show... 04897 // Set the status bar text. 04898 // StartSpread = pSpread; 04899 // ClickStart = dcMousePos; 04900 String_256 Str; 04901 Cursor* pPtr; 04902 FigureUserFeedback(pSpread, dcMousePos, ClickModifiers::GetClickModifiers(), TRUE, &Str, &pPtr); 04903 if (CursorStackID!=CURSORID_UNSET) 04904 CursorStack::GSetTop(pPtr,CursorStackID); 04905 } 04906 04907 // Give the info-bar a chance to update its records of the contents of the 04908 // edit-fields. 04909 pInfoBarOp->UpdateAllRecords(); 04910 04911 } 04912 04913 04914 04915 /******************************************************************************************** 04916 > void SelectorTool::AttributeHasChanged() 04917 04918 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 04919 Created: 29/6/95 04920 Purpose: Responds to an attribute-changing message by forcing the selection range to 04921 update its bounds, then forces a recalc and redraw of the tool's bounds 04922 blobs. 04923 SeeAlso: SelectorTool::SelectionHasChanged; SelectorInfoBarOp::Message 04924 ********************************************************************************************/ 04925 04926 void SelectorTool::AttributeHasChanged() 04927 { 04928 // If there's no SelRange then we're a bit stuck. 04929 if (SelectRange == NULL) return; 04930 04931 // Make sure the SelRange has updated bounds. 04932 SelectRange->UpdateBounds(); 04933 04934 // Force a recalc and redraw of the bounds blobs. 04935 SelectionHasChanged(); 04936 } 04937 04938 04939 04940 /******************************************************************************************** 04941 > void SelectorTool::SelectionBlobChange(BlobStyle ChangingBlobs) 04942 04943 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 04944 Created: 21/7/94 04945 Inputs: ChangingBlobs - The blob types that are changing 04946 Purpose: Called when the types of blob that are being displayed is to be changed 04947 (ie, someone pressed one of the buttons on the info bar). This function 04948 tells the blob manager what it wants to know and will display or 04949 remove the Tool blobs as appropriate. You should only have one of the 04950 blobs types set to TRUE at a time. ie, only change one type of blob at 04951 once. 04952 ********************************************************************************************/ 04953 04954 void SelectorTool::SelectionBlobChange(BlobStyle ChangingBlobs) 04955 { 04956 // Get a pointer to the BlobManager 04957 BlobManager* BlobMgr = GetApplication()->GetBlobManager(); 04958 if (BlobMgr == NULL) return; 04959 04960 // The Object Blobs 04961 if (ChangingBlobs.Object==TRUE) 04962 { 04963 if (bsBlobStyle.Object==TRUE) 04964 { 04965 // Remove the Object blobs from the world 04966 BlobMgr->RemoveInterest(ChangingBlobs); 04967 ChangingBlobs.Object = bsBlobStyle.Object = FALSE; 04968 fBlobPref &= ~2; 04969 04970 // Add Tiny blobs. 04971 ChangingBlobs.Tiny = bsBlobStyle.Tiny = TRUE; 04972 BlobMgr->AddInterest(ChangingBlobs); 04973 } 04974 else 04975 { 04976 // draw the Object blobs in 04977 BlobMgr->AddInterest(ChangingBlobs); 04978 bsBlobStyle.Object = TRUE; 04979 fBlobPref |= 2; 04980 04981 // Remove the tiny blobs 04982 ChangingBlobs.Object = FALSE; 04983 ChangingBlobs.Tiny = TRUE; 04984 BlobMgr->RemoveInterest(ChangingBlobs); 04985 bsBlobStyle.Tiny = FALSE; 04986 } 04987 } 04988 04989 // The fill blobs 04990 if (ChangingBlobs.Fill==TRUE) 04991 { 04992 if (bsBlobStyle.Fill==TRUE) 04993 { 04994 // Remove the Fill blobs from the world 04995 BlobMgr->RemoveInterest(ChangingBlobs); 04996 bsBlobStyle.Fill = FALSE; 04997 fBlobPref &= ~4; 04998 } 04999 else 05000 { 05001 // draw the Fill blobs in 05002 BlobMgr->AddInterest(ChangingBlobs); 05003 bsBlobStyle.Fill = TRUE; 05004 fBlobPref |= 4; 05005 } 05006 } 05007 05008 // The Artistic Blobs 05009 if (ChangingBlobs.Artistic==TRUE) 05010 { 05011 if (bsBlobStyle.Artistic==TRUE) 05012 { 05013 // Remove the Artistic blobs from the world 05014 BlobMgr->RemoveInterest(ChangingBlobs); 05015 bsBlobStyle.Artistic = FALSE; 05016 fBlobPref &= ~8; 05017 } 05018 else 05019 { 05020 // draw the Artistic blobs in 05021 BlobMgr->AddInterest(ChangingBlobs); 05022 bsBlobStyle.Artistic = TRUE; 05023 fBlobPref |= 8; 05024 } 05025 } 05026 } 05027 05028 05029 05030 /******************************************************************************************** 05031 > void SelectorTool::ViewChanged(const DocViewMsg& msg) 05032 05033 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05034 Created: 30/7/94 05035 Inputs: A constant reference to a DocView message that notified us of this event. 05036 Purpose: When views and documents are changing the selector tool needs to get its 05037 selection data correct for the old and new views so that its tool blobs 05038 can be removed. This function allows the info bar to get the tool to 05039 re-calculate this information and draw the blobs accordingly. The blobs 05040 are only drawn once here as this functions real purpose it to get rid of the 05041 blobs from the old view and the draw them in on the new view. As a result 05042 the info bar calls this function twice after setting up the appropriate 05043 docview info. 05044 ********************************************************************************************/ 05045 05046 void SelectorTool::ViewChanged(const DocViewMsg& msg) 05047 { 05048 switch (msg.State) 05049 { 05050 case DocViewMsg::SELABOUTTOCHANGE: 05051 { 05052 // Draw the blobs onto the new view 05053 if (msg.pNewDocView != NULL) 05054 { 05055 // The view is about to change, so get rid of the blobs in this view now 05056 pBlobManager->RenderToolBlobsOff(this, SelectionSpread, NULL); 05058 // Ignore Selection Changed messages until the view has actually changed 05059 // JCF: this has been moved to here so that this flag does *not* get set 05060 // if there is no new view to switch to, otherwise it remains set and the 05061 // info-bar's gadgets don't get disabled. This appears to be due to a 05062 // bug in the messages sent when the Document is closed - the 05063 // SELABOUTTOCHANGE message gets sent *twice*, once before the SELCHANGED 05064 // message and once after!?!? 05065 fIgnoreSelChange = TRUE; 05067 } 05068 05069 // Ignore Selection Changed messages until the view has actually changed 05070 // fIgnoreSelChange = TRUE; 05071 break; 05072 } 05073 05074 case DocViewMsg::SELCHANGED: 05075 { 05076 // This record the document's associated with the old and new views. 05077 Document* pOldViewsDoc = NULL; 05078 Document* pNewViewsDoc = NULL; 05079 05080 // Find out about the document associated with the old view 05081 if (msg.pOldDocView != NULL) 05082 pOldViewsDoc = msg.pOldDocView->GetDoc(); 05083 05084 // Find out about the document associated with the new view 05085 if (msg.pNewDocView != NULL) 05086 pNewViewsDoc = msg.pNewDocView->GetDoc(); 05087 05088 // If the document associated with the new view is different to the document associated 05089 // with the old view, then mark the rotation centre's position as needing updating. 05090 if (pOldViewsDoc != pNewViewsDoc) 05091 fRotateCentreIsValid = FALSE; 05092 05093 // Draw the blobs onto the new view 05094 if (msg.pNewDocView != NULL) 05095 { 05096 // [Removed by Jason] 05097 // SelectRange->Update(); 05098 // There is no need to update the SelRange in this manner - we should now only call this 05099 // method WHEN we HAVE CHANGED the selection. I believe this line was added when the 05100 // SelRange didn't correctly cope with Doc{View} changes; it's unnecessary (bad) these days. 05101 05102 // There is a new view so draw the bounds blobs on it. 05103 if (UpdateSelectionInfo()) 05104 pBlobManager->RenderToolBlobsOn(this, SelectionSpread, NULL); 05105 } 05106 05107 // Stop ignoring the selection changed messages again 05108 fIgnoreSelChange = FALSE; 05109 break; 05110 } 05111 05112 default: 05113 break; 05114 } 05115 } 05116 05117 05118 05119 /******************************************************************************************** 05120 > BlobStyle SelectorTool::GetBlobStyle() const 05121 05122 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05123 Created: 21/7/94 05124 Returns: BlobStyle - The selector tools ideas about which blobs are being displayed 05125 and which are not 05126 Purpose: To find out what blobs the selector tool is displaying 05127 ********************************************************************************************/ 05128 05129 BlobStyle SelectorTool::GetBlobStyle() const 05130 { 05131 return bsBlobStyle; 05132 } 05133 05134 05135 05136 /******************************************************************************************** 05137 > SelectorTool::BlobType SelectorTool::GetCurrentToolBlobType() const 05138 05139 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05140 Created: 31/8/94 05141 Inputs: - 05142 Outputs: - 05143 Returns: A blob-type enumeration. 05144 Purpose: Returns the type of tool blobs currently being displayed, either None, 05145 Bounds, or Rotate blobs. Used internally by the info-bar. 05146 Errors: - 05147 SeeAlso: - 05148 ********************************************************************************************/ 05149 05150 SelectorTool::BlobType SelectorTool::GetCurrentToolBlobType() const 05151 { 05152 return eCurrentBlobs; 05153 } 05154 05155 05156 05157 /******************************************************************************************** 05158 > void SelectorTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect) 05159 05160 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05161 Created: 26/7/94 05162 Inputs: pSpread - The spread that needs redrawing 05163 pClipRect - The clipping rectangle that needs redrawing 05164 Purpose: Draws the blobs specific to the selector tool. This can include the 05165 Bounds blobs that surround the current selection, or the Rotate/Shear 05166 blobs that also surround the selection. 05167 ********************************************************************************************/ 05168 05169 void SelectorTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect) 05170 { 05171 // Draw the bounds blobs if there is anything to render in a view on the given spread. 05172 if (fShowToolBlobs && DocView::GetSelected() != NULL && 05173 SelectionSpread != NULL && SelectionSpread == pSpread) 05174 { 05175 // Calculate the inflated selection rectangle. 05176 DocRect drBlobRect = InflatedSelRect(); 05177 05178 // Render the appropriate blobs. 05179 switch (eCurrentBlobs) 05180 { 05181 case BOUNDS_BLOBS: 05182 RenderBoundBlobs(pSpread, pClipRect, drBlobRect); 05183 break; 05184 05185 case ROTATE_BLOBS: 05186 RenderRotateBlobs(pSpread, pClipRect, drBlobRect); 05187 break; 05188 05189 default: 05190 ENSURE(FALSE, "Unknown blob type in SelectorTool::RenderToolBlobs"); 05191 return; 05192 } 05193 } 05194 } 05195 05196 05197 05198 /******************************************************************************************** 05199 > BOOL SelectorTool::UpdateSelectionInfo() 05200 05201 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05202 Created: 26/7/94 05203 Inputs: - 05204 Outputs: - 05205 Returns: TRUE if there is a current selection, FALSE if no object is selected. 05206 Purpose: Updates the selector tool's record of the spread and bounding rectangle 05207 of the current selection. 05208 Errors: - 05209 SeeAlso: SelectorTool::SelectChange; SelectorTool::SelectionHasChanged 05210 ********************************************************************************************/ 05211 05212 BOOL SelectorTool::UpdateSelectionInfo() 05213 { 05214 // Reset all our infomation about the selection to "no selection". 05215 SelectionSpread = NULL; 05216 SelectionRect.MakeEmpty(); 05217 SelectionRectNoAttr.MakeEmpty(); 05218 xlSelectionArea = 0; 05219 xlSelectionPerim = 0; 05220 05221 // Go find the first node in the selection, so that we can find out about its spread 05222 Node* pFirstNode = SelectRange->FindFirst(); 05223 if (pFirstNode != NULL) 05224 { 05225 // Find the spread that the selection lives on, if any, and its bounds. 05226 SelectionSpread = pFirstNode->FindParentSpread(); 05227 if (SelectionSpread != NULL) 05228 { 05229 // Update the bounding rectangle of the selection. 05230 SelectionRect = SelectRange->GetBoundingRect(); 05231 SelectionRectNoAttr = SelectRange->GetBoundsWithoutAttrs(); 05232 05233 #ifdef SELECTION_AREA_FEATURE 05234 // This needs to be done in the background 05235 m_bComputeAreaDetails = TRUE; 05236 // SelectRange->GetAreaDetails(&xlSelectionArea, &xlSelectionPerim); 05237 #endif 05238 05239 // ENSURE(!SelectionRect.IsEmpty(),"Empty selection bounds in SelectorTool::UpdateSelectionInfo"); 05240 if (SelectionRect.IsEmpty()) 05241 SelectionRect.Inflate(1); 05242 05243 if (SelectionRectNoAttr.IsEmpty()) 05244 SelectionRectNoAttr.Inflate(1); 05245 05246 // If any previous routine has indicated the rotation centre should be moved back 05247 // to the centre of the selection then recalc its position. 05248 if (!fRotateCentreIsValid) 05249 { 05250 // Default rotation centre is the centre of the object. Call GetSelPosNearBlob 05251 // to calculate the position for us. 0 means blob 0, the center blob. 05252 dcRotateCentre = GetSelPosNearBlob(0); 05253 05254 nLastSelectedBlob = 0; 05255 fValidSelectedBlob = TRUE; 05256 // fRotateCentreIsValid = TRUE; 05257 // TRACEUSER( "JustinF", _T("Reseting rotation centre to middle of selection\n")); 05258 } 05259 } 05260 /* #ifdef _DEBUG 05261 else 05262 { 05263 TRACEUSER( "JustinF", _T("Nothing selected cos first node has no parent spread\n")); 05264 } 05265 #endif 05266 */ } /* 05267 #ifdef _DEBUG 05268 else 05269 { 05270 TRACEUSER( "JustinF", _T("Nothing selected cos no first node in SelRange\n")); 05271 } 05272 #endif 05273 */ 05274 // Update the info-bar with the selection's extent. 05275 pInfoBarOp->SetEdit_OnSelectionChange(); 05276 05277 // Return TRUE if there is a selection. 05278 return SelectionSpread != NULL; 05279 } 05280 05281 05282 05283 /******************************************************************************************** 05284 > INT32 SelectorTool::BoundsBlobHitTest(const DocCoord& dcMousePos) const 05285 05286 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05287 Created: 27/7/94 05288 Inputs: dcMousePos - the position of the mouse (in spread coords). 05289 Outputs: - 05290 Returns: 0 if no blob is hit, a blob number (1 - 8) if a blob is hit. Blob numbers 05291 follow this pattern:- 05292 1-----2-----3 05293 | | 05294 4 5 05295 | | 05296 6-----7-----8 05297 Purpose: Checks if the given coordinate is over or very near a blob. 05298 Errors: - 05299 SeeAlso: SelectorTool::IsNearBlob 05300 ********************************************************************************************/ 05301 05302 INT32 SelectorTool::BoundsBlobHitTest(const DocCoord& dcMousePos) const 05303 { 05304 // If there isn't a selected object or no blobs at all we can't have clicked on a blob. 05305 if (SelectionRect.IsEmpty() || !fShowToolBlobs) return 0; 05306 05307 // Calculate the rectangle describing where the blobs are on the screen, and the 05308 // mid-points of its horizontal and vertical sides. 05309 DocRect drBlobRect = InflatedSelRect(); 05310 INT32 nMidX = (drBlobRect.lo.x + drBlobRect.hi.x) / 2; 05311 INT32 nMidY = (drBlobRect.lo.y + drBlobRect.hi.y) / 2; 05312 05313 // Check each blob in turn to see if it is near enough to the given point. 05314 if (IsNearBlob(dcMousePos, DocCoord(drBlobRect.lo.x, drBlobRect.hi.y))) 05315 return 1; // top-left 05316 else if (IsNearBlob(dcMousePos, DocCoord(nMidX, drBlobRect.hi.y))) 05317 return 2; // top-mid 05318 else if (IsNearBlob(dcMousePos, drBlobRect.hi)) 05319 return 3; // top-right 05320 else if (IsNearBlob(dcMousePos, DocCoord(drBlobRect.lo.x, nMidY))) 05321 return 4; // mid-left 05322 else if (IsNearBlob(dcMousePos, DocCoord(drBlobRect.hi.x, nMidY))) 05323 return 5; // mid-right 05324 else if (IsNearBlob(dcMousePos, drBlobRect.lo)) 05325 return 6; // bottom-left 05326 else if (IsNearBlob(dcMousePos, DocCoord(nMidX, drBlobRect.lo.y))) 05327 return 7; // bottom-mid 05328 else if (IsNearBlob(dcMousePos, DocCoord(drBlobRect.hi.x, drBlobRect.lo.y))) 05329 return 8; // bottom-right 05330 05331 // It's not near any of them! 05332 return 0; 05333 } 05334 05335 05336 05337 /******************************************************************************************** 05338 > static BOOL SelectorTool::IsNearBlob(const DocCoord& dcBlobOrigin, 05339 const DocCoord& dcTestPoint) 05340 05341 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05342 Created: 27/7/94 05343 Inputs: dcBlobOrigin --- the centre of the blob to be checked 05344 dcTestPoint --- the point that is being tested 05345 Outputs: - 05346 Returns: TRUE if the point is over or "near" the given blob. 05347 Purpose: Private helper function for SelectorTool::BlobHitTest 05348 Errors: - 05349 SeeAlso: SelectorTool::BoundsBlobHitTest 05350 ********************************************************************************************/ 05351 05352 BOOL SelectorTool::IsNearBlob(const DocCoord& dcBlobOrigin, const DocCoord& dcTestPoint) 05353 { 05354 // Get the click radius from the preferences. 05355 DocView* pDocView = DocView::GetCurrent(); 05356 ENSURE(pDocView != NULL, "Null current DocView in SelectorTool::IsNearBlob"); 05357 MILLIPOINT nNearRadius = OSRenderRegion::GetHitTestRadius(pDocView); 05358 05359 // Calculate the blob's rectangle, given it's centre, and inflate it by the 05360 // "is near enough" radius. 05361 DocRect drBlobRect; 05362 pBlobManager->GetBlobRect(DocCoord(dcBlobOrigin), &drBlobRect); 05363 drBlobRect.Inflate(nNearRadius); 05364 05365 // Return TRUE if the test point is within the blob's rectangle (a hit). 05366 return drBlobRect.ContainsCoord(dcTestPoint); 05367 } 05368 05369 05370 05371 /******************************************************************************************** 05372 > DocCoord SelectorTool::GetBoundsBlobPos(INT32 nBlob) const 05373 05374 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05375 Created: 5/8/94 05376 Inputs: nBlob the numerical ID of the blob (1-8) 05377 Outputs: - 05378 Returns: The position of the blob, in Spread coords. 05379 Purpose: Calculates the position of a given blob within the document. 05380 Errors: - 05381 SeeAlso: - 05382 ********************************************************************************************/ 05383 05384 DocCoord SelectorTool::GetBoundsBlobPos(INT32 nBlob) const 05385 { 05386 // Get the rectangle describing the position of the bounds blobs, and the 05387 // mid-points along its sides. 05388 DocRect drBlobRect = InflatedSelRect(); 05389 INT32 nMidX = (drBlobRect.lo.x + drBlobRect.hi.x) / 2; 05390 INT32 nMidY = (drBlobRect.lo.y + drBlobRect.hi.y) / 2; 05391 05392 DocCoord dc(0,0); 05393 switch (nBlob) 05394 { 05395 case 0: 05396 dc = DocCoord(nMidX, nMidY); break; 05397 case 1: 05398 dc = DocCoord(drBlobRect.lo.x, drBlobRect.hi.y); break; 05399 case 2: 05400 dc = DocCoord(nMidX, drBlobRect.hi.y); break; 05401 case 3: 05402 dc = drBlobRect.hi; break; 05403 case 4: 05404 dc = DocCoord(drBlobRect.lo.x, nMidY); break; 05405 case 5: 05406 dc = DocCoord(drBlobRect.hi.x, nMidY); break; 05407 case 6: 05408 dc = drBlobRect.lo; break; 05409 case 7: 05410 dc = DocCoord(nMidX, drBlobRect.lo.y); break; 05411 case 8: 05412 dc = DocCoord(drBlobRect.hi.x, drBlobRect.lo.y); break; 05413 default: 05414 ENSURE(FALSE, "Bad blob number in SelectorTool::GetBlobPos"); break; 05415 } 05416 05417 return dc; 05418 } 05419 05420 05421 05422 /******************************************************************************************** 05423 > DocCoord SelectorTool::GetSelPosNearBlob(INT32 nBlob) const 05424 05425 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05426 Created: 8/8/94 05427 Inputs: nBlob --- the blob's number 05428 Outputs: - 05429 Returns: A position on the bounds of the selected object(s). 05430 Purpose: This returns the nearest point to the given blob on the selected object(s) 05431 bounding rectangle. 05432 05433 Notes: The "point on the object" is actually a point on the object's bounding 05434 box. The bounding box used will include or exclude effects of attributes 05435 such as line width depending on the pInfoBarOp->DontConsiderAttrs() flag. 05436 05437 ********************************************************************************************/ 05438 05439 DocCoord SelectorTool::GetSelPosNearBlob(INT32 nBlob) const 05440 { 05441 // Use the selection bounds. If we should include line widths, then we use a large rectangle. 05442 // This means we'll scale the object either so that the edge of the line doesn't move, 05443 // or so that the edge of the object (ignoring line width) doesn't move, depending on 05444 // the mode we're in. This will also affect exactly which bit of the object will snap 05445 // to the grid. 05446 DocRect SelRect = GetSelectionBounds(pInfoBarOp->DontConsiderAttrs()); 05447 05448 // This function should never need to be called if the SelRect is empty. 05449 ERROR3IF(SelRect.IsEmpty(), "Empty SelRect in SelectorTool::InflatedSelRect"); 05450 05451 // Calculate the midpoints of the sides of the rectangle. 05452 INT32 nMidX = (SelRect.lo.x + SelRect.hi.x) / 2; 05453 INT32 nMidY = (SelRect.lo.y + SelRect.hi.y) / 2; 05454 05455 // Here we go . . . 05456 DocCoord dc(0,0); 05457 switch (nBlob) 05458 { 05459 case 0: 05460 dc = DocCoord(nMidX, nMidY); break; 05461 case 1: 05462 dc = DocCoord(SelRect.lo.x, SelRect.hi.y); break; 05463 case 2: 05464 dc = DocCoord(nMidX, SelRect.hi.y); break; 05465 case 3: 05466 dc = SelRect.hi; break; 05467 case 4: 05468 dc = DocCoord(SelRect.lo.x, nMidY); break; 05469 case 5: 05470 dc = DocCoord(SelRect.hi.x, nMidY); break; 05471 case 6: 05472 dc = SelRect.lo; break; 05473 case 7: 05474 dc = DocCoord(nMidX, SelRect.lo.y); break; 05475 case 8: 05476 dc = DocCoord(SelRect.hi.x, SelRect.lo.y); break; 05477 default: 05478 ENSURE(FALSE, "Bad blob number in SelectorTool::GetSelPosNearBlob"); break; 05479 } 05480 return dc; 05481 } 05482 05483 05484 05485 /******************************************************************************************** 05486 > DocRect SelectorTool::InflatedSelRect() const 05487 05488 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05489 Created: 26/7/94 05490 Inputs: - 05491 Outputs: - 05492 Returns: The bounding rectangle of the selection (which must not be empty) 05493 inflated by 4 (visible) millimetres, or whatever the preference is set to. 05494 Purpose: Calculates the position of the blobs around a selected object. 05495 05496 Notes: Note that the Inflated SelRect as used for blob plotting is always based 05497 on the selection bounding rect INCLUDING Attributes. This means the blobs 05498 always sit outside the object. This differs from all the rectangles 05499 which are used to read/set object values, which may/may not include 05500 the effects of attributes such as line width in them. 05501 05502 SeeAlso: SelectorTool::InflateByBlobBorder; SelectorTool::RenderToolBlobs 05503 ********************************************************************************************/ 05504 05505 DocRect SelectorTool::InflatedSelRect() const 05506 { 05507 DocRect dr = SelectionRect; 05508 InflateByBlobBorder(&dr); 05509 return dr; 05510 } 05511 05512 05513 05514 /******************************************************************************************** 05515 05516 > static INT32 SelectorTool::GetBlobBorderSize(void) 05517 05518 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> (Moved code of Justin's here to centralise the calculation) 05519 Created: 27/2/96 05520 05521 Returns: The size of the border (in millipoints) 05522 05523 Purpose: Determines the width of the "border" between a selected object and the bounds 05524 blobs around it. NB. there must be a selected DocView for this function to 05525 work (it relies on the selected view's scale factor). 05526 05527 SeeAlso: SelectorTool::InflateByBlobBorder 05528 05529 ********************************************************************************************/ 05530 05531 INT32 SelectorTool::GetBlobBorderSize(void) 05532 { 05533 // We can only do this if there is a current DocView 05534 DocView* pDocView = DocView::GetSelected(); 05535 05536 // Make sure we are not passed in a load of junk 05537 if (pDocView == NULL) 05538 { 05539 ERROR3("No Selected DocView in SelectorTool::InflateByBlobBorder"); 05540 return(0); 05541 } 05542 05543 // We have to inflate the rectangle by (nBlobBorder) mm, so we must calculate how much 05544 // that is in DocCoord units, accounting for the view's scale factor. 05545 INT32 nBorder = (nMPperMM * nBlobBorder) << F16SHIFT; 05546 nBorder /= pDocView->GetViewScale().GetRawLong(); 05547 05548 return(nBorder); 05549 } 05550 05551 05552 05553 /******************************************************************************************** 05554 05555 > static void SelectorTool::InflateByBlobBorder(DocRect* pdrRect) 05556 05557 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05558 Created: 3/10/94 05559 Inputs: pdrRect --- pointer to the rectangle (in doc or spread coords) to 05560 inflate. May not be NULL. 05561 Outputs: The inflated rectangle. 05562 05563 Purpose: Useful public function that adjusts a bounding rectangle by the "border" 05564 between a selected object and the bounds blobs around it. NB. there must 05565 be a selected DocView for this function to work (it relies on the selected 05566 view's scale factor). 05567 05568 SeeAlso: SelectorTool::GetBlobBorderSize; 05569 SelectorTool::InflatedSelRect; 05570 OpZoomFitSelectedDescriptor::AdjustRect 05571 05572 ********************************************************************************************/ 05573 05574 void SelectorTool::InflateByBlobBorder(DocRect* pdr) 05575 { 05576 ERROR3IF(pdr->IsEmpty(), "Empty rectangle passed to SelectorTool::InflateByBlobBorder"); 05577 05578 if (pdr != NULL) 05579 pdr->Inflate(GetBlobBorderSize()); 05580 } 05581 05582 05583 05584 /******************************************************************************************** 05585 > void SelectorTool::RenderBoundBlobs(Spread* pSpread, DocRect* pClipRect, 05586 const DocRect& drcBlobRect) 05587 05588 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05589 Created: 21/7/94 05590 Inputs: pSpread - The spread that needs redrawing 05591 pClipRect - The clipping rectangle that needs redrawing 05592 drcBlobRect - where to draw the blobs 05593 Purpose: This function is called from the RenderToolBlobs function. If the Bounds 05594 Blobs should be displayed (ie the user has switch them on) then this function 05595 is used to draw them. The blobs appear as a small rect at each corner of the 05596 selection rectangle as well as one at the midpoint of each of the sides of 05597 the rectangle. This function should not be called directly, but only through 05598 RenderToolBlobs() 05599 ********************************************************************************************/ 05600 05601 void SelectorTool::RenderBoundBlobs(Spread* pSpread, DocRect* pClipRect, 05602 const DocRect& drcBlobRect) 05603 { 05604 // Get an XOR render region (may be the first of many). 05605 RenderRegion* pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR); 05606 while (pRegion != NULL) 05607 { 05608 // Set the line and fill styles 05609 pRegion->SetLineColour(COLOUR_NONE); 05610 pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB); 05611 05612 // Render a blob at each corner of the Selections bounds 05613 pRegion->DrawBlob(DocCoord(drcBlobRect.lo.x, drcBlobRect.lo.y), BT_SELECTED); 05614 pRegion->DrawBlob(DocCoord(drcBlobRect.hi.x, drcBlobRect.hi.y), BT_SELECTED); 05615 pRegion->DrawBlob(DocCoord(drcBlobRect.lo.x, drcBlobRect.hi.y), BT_SELECTED); 05616 pRegion->DrawBlob(DocCoord(drcBlobRect.hi.x, drcBlobRect.lo.y), BT_SELECTED); 05617 05618 // Find the x- and y-ordinates of the mid-point of each side. 05619 INT32 xMid = (drcBlobRect.lo.x + drcBlobRect.hi.x) / 2; 05620 INT32 yMid = (drcBlobRect.lo.y + drcBlobRect.hi.y) / 2; 05621 05622 // Render the blobs on each of the sides 05623 pRegion->DrawBlob(DocCoord(xMid, drcBlobRect.lo.y), BT_SELECTED); 05624 pRegion->DrawBlob(DocCoord(xMid, drcBlobRect.hi.y), BT_SELECTED); 05625 pRegion->DrawBlob(DocCoord(drcBlobRect.lo.x, yMid), BT_SELECTED); 05626 pRegion->DrawBlob(DocCoord(drcBlobRect.hi.x, yMid), BT_SELECTED); 05627 05628 // Render the rotation centre. 05629 // RenderRotateCentre(pRegion, dcRotateCentre); 05630 05631 // Go on to the next render region, if any. 05632 pRegion = DocView::GetNextOnTop(pClipRect); 05633 } 05634 } 05635 05636 05637 05638 /******************************************************************************************** 05639 > void SelectorTool::RenderRotateBlobs(Spread* pSpread, DocRect* pdrClip, const DocRect& drBlob) 05640 05641 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05642 Created: 21/7/94 05643 Inputs: pSpread - The spread that needs redrawing 05644 pdrClip - The clipping rectangle that needs redrawing 05645 drBlob - Where to draw the blobs. 05646 Purpose: Draws the blobs that appear when the Rotate/Shear mode of the selector tool 05647 is entered. These blobs appear in the same places as the Bounds Blobs, 05648 around the edge of the given rectangle. The only difference is their 05649 appearance. The blobs at the corner of the selection appear as small 05650 curved arrows to indicate rotation and the ones on the side of the 05651 selection appear as straight arrows to indicate shearing. 05652 SeeAlso: SelectorTool::RenderToolBlobs 05653 ********************************************************************************************/ 05654 05655 void SelectorTool::RenderRotateBlobs(Spread* pSpread, DocRect* pdrClip, const DocRect& drBlob) 05656 { 05657 // Get the size of a blob at this scale. 05658 // INT32 nSize = pBlobManager->GetBlobSize(); 05659 05660 // Get an XOR render region (may be the first of many). 05661 RenderRegion* pRegion = DocView::RenderOnTop(pdrClip, pSpread, ClippedEOR); 05662 while (pRegion != NULL) 05663 { 05664 // Render a blob at each corner of the Selections bounds 05665 // This is not the correct shape etc for these blobs, but it will do for now... 05666 pRegion->DrawBitmapBlob(DocCoord(drBlob.lo.x, drBlob.lo.y), _R(IDBMP_HANDLE_6)); 05667 pRegion->DrawBitmapBlob(DocCoord(drBlob.hi.x, drBlob.hi.y), _R(IDBMP_HANDLE_3)); 05668 pRegion->DrawBitmapBlob(DocCoord(drBlob.lo.x, drBlob.hi.y), _R(IDBMP_HANDLE_1)); 05669 pRegion->DrawBitmapBlob(DocCoord(drBlob.hi.x, drBlob.lo.y), _R(IDBMP_HANDLE_8)); 05670 05671 // Find the x- and y-ordinates of the mid-point of each side. 05672 INT32 xMid = (drBlob.lo.x + drBlob.hi.x) / 2; 05673 INT32 yMid = (drBlob.lo.y + drBlob.hi.y) / 2; 05674 05675 // Render the blobs on each of the sides 05676 pRegion->DrawBitmapBlob(DocCoord(xMid, drBlob.lo.y), _R(IDBMP_HANDLE_45)); 05677 pRegion->DrawBitmapBlob(DocCoord(xMid, drBlob.hi.y), _R(IDBMP_HANDLE_45)); 05678 pRegion->DrawBitmapBlob(DocCoord(drBlob.lo.x, yMid), _R(IDBMP_HANDLE_27)); 05679 pRegion->DrawBitmapBlob(DocCoord(drBlob.hi.x, yMid), _R(IDBMP_HANDLE_27)); 05680 05681 // Render the rotation centre. 05682 RenderRotateCentre(pRegion, dcRotateCentre); 05683 05684 // Go on to the next render region, if any. 05685 pRegion = DocView::GetNextOnTop(pdrClip); 05686 } 05687 } 05688 05689 05690 05691 /******************************************************************************************** 05692 > void SelectorTool::RenderRotateCentre(RenderRegion* pRegion, const DocCoord& dcPos) 05693 05694 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05695 Created: 31/8/94 05696 Inputs: pRegion the RenderRegion to draw into 05697 dcPos the position of the rotation centre (spread coords) 05698 Outputs: - 05699 Returns: - 05700 Purpose: Draws the rotation centre blob at the given position. This is a separate 05701 function so that the drag code can call it as well. 05702 Errors: - 05703 SeeAlso: - 05704 ********************************************************************************************/ 05705 05706 void SelectorTool::RenderRotateCentre(RenderRegion* pRegion, const DocCoord& dcPos) 05707 { 05708 pRegion->DrawBitmapBlob(dcPos, _R(IDBMP_HANDLE_CENTRE)); 05709 } 05710 05711 05712 05713 /******************************************************************************************** 05714 > static BOOL SelectorTool::BeyondFixedRange(double fpFixedVal) 05715 05716 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05717 Created: 19/3/96 05718 Inputs: fpFixedVal the double that is to be converted to fixed-point 05719 Returns: TRUE if the number is beyond the range that fixed-piont numbers can 05720 represent, FALSE if its OK to convert. 05721 Purpose: Checks for under/overflow in fixed-point calculations. 05722 ********************************************************************************************/ 05723 05724 BOOL SelectorTool::BeyondFixedRange(double fpFixedVal) 05725 { 05726 const double fpMinFixPt = 1.0 / (INT32(SHRT_MAX) + 1); // 1/32768 05727 const double fpMaxFixPt = SHRT_MAX; // 32767 05728 double fp = fabs(fpFixedVal); 05729 return fp < fpMinFixPt || fp > fpMaxFixPt; 05730 } 05731 05732 05733 05734 /******************************************************************************************** 05735 > OpSelectorDragBox::OpSelectorDragBox() 05736 05737 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05738 Created: 20/7/93 05739 Purpose: Constructor. This simply sets a few of the operation flags. 05740 ********************************************************************************************/ 05741 05742 OpSelectorDragBox::OpSelectorDragBox() 05743 { 05744 // Empty. 05745 } 05746 05747 05748 05749 /******************************************************************************************** 05750 > void OpSelectorDragBox::StartDragBox(DocCoord Anchor) 05751 05752 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05753 Created: 5/7/93 05754 Inputs: Anchor - The position of the mouse at the start of the Drag 05755 Purpose: This is called when a Drag operation has been detected 05756 ********************************************************************************************/ 05757 05758 void OpSelectorDragBox::StartDragBox(Spread* pSpread, DocCoord Anchor, ClickModifiers ClickMods) 05759 { 05760 // We had better take a note of the starting point of the drag 05761 StartSpread = pSpread; 05762 StartPoint = Anchor; 05763 LastMousePosition = Anchor; 05764 05765 // Put some helpful text in the status bar. 05766 SelectorTool::AllowIdleWork(FALSE); 05767 SelectorTool::SetStatusText(_R(IDS_SEL_DRAGBOXTEXT)); 05768 05769 // And tell the Dragging system that we need drags to happen 05770 StartDrag(DRAGTYPE_AUTOSCROLL); 05771 } 05772 05773 05774 05775 05776 /******************************************************************************************** 05777 > void OpSelectorDragBox::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, BOOL bSolidDrag) 05778 05779 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05780 Created: 5/7/93 05781 Inputs: PointerPos - The current position of the mouse in Doc Coords 05782 ClickMods - Which key modifiers are being pressed 05783 Purpose: This is called every time the mouse moves, during a drag. All it really does 05784 is update the EORed bounding box 05785 SeeAlso: ClickModifiers 05786 ********************************************************************************************/ 05787 05788 void OpSelectorDragBox::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, 05789 Spread* pSpread, BOOL bSolidDrag) 05790 { 05791 // First Rub out the old box 05792 DocRect Rect = DocRect(MIN(StartPoint.x, LastMousePosition.x), 05793 MIN(StartPoint.y, LastMousePosition.y), 05794 MAX(StartPoint.x, LastMousePosition.x), 05795 MAX(StartPoint.y, LastMousePosition.y)); 05796 RenderDragBlobs(Rect, StartSpread, bSolidDrag); 05797 05798 // Update the box and draw in the new one 05799 if (pSpread != StartSpread) 05800 { 05801 PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos); 05802 } 05803 05804 LastMousePosition = PointerPos; 05805 Rect = DocRect(MIN(StartPoint.x, LastMousePosition.x), 05806 MIN(StartPoint.y, LastMousePosition.y), 05807 MAX(StartPoint.x, LastMousePosition.x), 05808 MAX(StartPoint.y, LastMousePosition.y)); 05809 RenderDragBlobs(Rect, StartSpread, bSolidDrag); 05810 } 05811 05812 05813 05814 /******************************************************************************************** 05815 > void OpSelectorDragBox::DragFinished(DocCoord PointerPos, 05816 ClickModifiers ClickMods, BOOL Success, BOOL bSolidDrag) 05817 05818 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05819 Created: 5/7/93 05820 Inputs: PointerPos - The position of the mouse at the end of the drag 05821 ClickMods - the key modifiers being pressed 05822 Success - TRUE if the drag was terminated properly, FALSE if it 05823 was ended with the escape key being pressed 05824 Purpose: This is called when a drag operation finishes. This removes the EORed drag 05825 rect from the screen and then selects all the objects that were in it 05826 SeeAlso: ClickModifiers 05827 ********************************************************************************************/ 05828 05829 void OpSelectorDragBox::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, 05830 Spread* Spread, BOOL Success, BOOL bSolidDrag) 05831 { 05832 // Build the rectangle of the drag box at the end of the drag 05833 DocRect BoundingRect(MIN(StartPoint.x, LastMousePosition.x), 05834 MIN(StartPoint.y, LastMousePosition.y), 05835 MAX(StartPoint.x, LastMousePosition.x), 05836 MAX(StartPoint.y, LastMousePosition.y)); 05837 05838 // First Rub out the old box 05839 RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag); 05840 05841 // Put the hourglass up 05842 BeginSlowJob(); 05843 05844 // Go and try and select a few things 05845 if (Success) 05846 { 05847 // If we didn't drag with the right button then deselect everything prior to selecting 05848 // the "lasso-ed" objects, and remember to restore the rotation centre to the middle of 05849 // the selection. 05850 if (!ClickMods.Adjust) 05851 { 05852 NodeRenderableInk::DeselectAll(); 05853 ((SelectorTool*) Tool::GetCurrent())->InvalidateRotationCentre(); 05854 } 05855 05856 // Select the objects in the BoundingRect 05857 NodeRenderableInk::SelectAllInRect(BoundingRect, Spread, 05858 NodeRenderableInk::SET); 05859 /* 05860 // This old code makes right-button marquee consistent with the right-button selection toggle. 05861 // Unfortunately Charles & Burns don't like it (this week). 05862 NodeRenderableInk::SelectAllInRect(BoundingRect, Spread, 05863 ClickMods.Adjust ? NodeRenderableInk::SelStateAction::TOGGLE 05864 : NodeRenderableInk::SelStateAction::SET); 05865 */ 05866 // Reenable the tool's idle processing. 05867 SelectorTool::AllowIdleWork(TRUE); 05868 05869 // End the Drag 05870 if (!EndDrag()) FailAndExecute(); 05871 } 05872 else 05873 { 05874 // Reenable the tool's idle processing. 05875 SelectorTool::AllowIdleWork(TRUE); 05876 05877 // Set up the flags that say it all went wrong. 05878 EndDrag(); 05879 FailAndExecute(); 05880 } 05881 05882 // Final end of the operation. 05883 End(); 05884 05885 } 05886 05887 05888 /******************************************************************************************** 05889 > void OpSelectorDragBox::RenderDragBlobs( DocRect Rect, Spread *pSpread, BOOL bSolidDrag ) 05890 05891 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05892 Created: 6/9/93 05893 Purpose: EORs a rectangle onto the screen to mark out the size of the current 05894 selection. 05895 ********************************************************************************************/ 05896 05897 void OpSelectorDragBox::RenderDragBlobs(DocRect Rect, Spread *pSpread, BOOL bSolidDrag) 05898 { 05899 RenderRegion* pRegion = DocView::RenderOnTop(&Rect, pSpread, ClippedEOR); 05900 while (pRegion) 05901 { 05902 // Set the line colour 05903 pRegion->SetLineColour(COLOUR_XORSELECT); 05904 05905 // And Draw the rect 05906 DocRect RubberBox = DocRect(MIN(StartPoint.x, LastMousePosition.x), 05907 MIN(StartPoint.y, LastMousePosition.y), 05908 MAX(StartPoint.x, LastMousePosition.x), 05909 MAX(StartPoint.y, LastMousePosition.y)); 05910 05911 // Draw the rectangle 05912 pRegion->DrawDragRect(&RubberBox); 05913 05914 // Get the Next render region 05915 pRegion = DocView::GetNextOnTop(&Rect); 05916 } 05917 } 05918 05919 05920 05921 /******************************************************************************************** 05922 > BOOL OpSelectorDragBox::Declare() 05923 05924 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05925 Created: 5/7/93 05926 Returns: TRUE if all went OK, False otherwise 05927 Purpose: Adds the operation to the list of all known operations 05928 ********************************************************************************************/ 05929 05930 BOOL OpSelectorDragBox::Declare() 05931 { 05932 return RegisterOpDescriptor(0, _R(IDS_SELECTOR_BOX), CC_RUNTIME_CLASS(OpSelectorDragBox), 05933 OPTOKEN_SELECTOR_DRAGBOX, OpSelectorDragBox::GetState); 05934 } 05935 05936 05937 05938 /******************************************************************************************** 05939 > OpState OpSelectorDragBox::GetState(String_256* Description, OpDescriptor*) 05940 05941 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 05942 Created: 5/7/93 05943 Outputs: Description - GetState fills this string with an approriate description 05944 of the current state of the selector tool 05945 Returns: The state of the operation, so that menu items (ticks and greying can be 05946 done properly 05947 Purpose: Find out the state of the operation at the specific time 05948 ********************************************************************************************/ 05949 05950 OpState OpSelectorDragBox::GetState(String_256* Description, OpDescriptor*) 05951 { 05952 OpState Blobby; 05953 return Blobby; 05954 } 05955 05956 05957 05958 /******************************************************************************************** 05959 > OpDragRotateCentre::OpDragRotateCentre() 05960 05961 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05962 Created: 31/8/94 05963 Inputs: - 05964 Outputs: - 05965 Returns: - 05966 Purpose: Constructs an Operation that allows the user to drag around the centre of 05967 rotation blobby. 05968 Errors: - 05969 SeeAlso: - 05970 ********************************************************************************************/ 05971 05972 OpDragRotateCentre::OpDragRotateCentre() 05973 { 05974 // Empty. 05975 } 05976 05977 05978 05979 /******************************************************************************************** 05980 > void OpDragRotateCentre::StartDragCentreBlob(Spread* pSpread, const DocCoord& dcPos, 05981 ClickModifiers) 05982 05983 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 05984 Created: 31/8/94 05985 Inputs: pSpread the spread containing the rotation centre blob 05986 dcPos the initial position of the blob 05987 05988 The ClickModifiers argument is currently unused. 05989 Outputs: - 05990 Returns: - 05991 Purpose: Starts a drag of the rotation centre blobby. 05992 Errors: - 05993 SeeAlso: - 05994 ********************************************************************************************/ 05995 05996 void OpDragRotateCentre::StartDragCentreBlob(Spread* pSpread, const DocCoord& dcPos, 05997 ClickModifiers) 05998 { 05999 // Put some helpful text in the status bar and prevent the selector tool overwriting 06000 // it until we are done. 06001 SelectorTool::AllowIdleWork(FALSE); 06002 SelectorTool::SetStatusText(_R(IDS_SEL_DRAGROTCENTRE)); 06003 06004 // Remember the starting positions etc and initiate a drag. 06005 m_pStartSpread = pSpread; 06006 m_dcLastMousePos = m_dcLastPos = m_dcFirstPos = dcPos; 06007 StartDrag(DRAGTYPE_AUTOSCROLL); 06008 } 06009 06010 06011 06012 /******************************************************************************************** 06013 > void OpDragRotateCentre::DragPointerMove(DocCoord dcPos, ClickModifiers, Spread* pSpread, BOOL bSolidDrag) 06014 06015 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06016 Created: 31/8/94 06017 Inputs: dcPos the current mouse position 06018 pSpread the spread containing the mouse 06019 Outputs: - 06020 Returns: - 06021 Purpose: Called while the rotation centre blobby is being dragged around. Erases it 06022 from its old position and redraws it at its new position. 06023 Errors: - 06024 SeeAlso: - 06025 ********************************************************************************************/ 06026 06027 void OpDragRotateCentre::DragPointerMove(DocCoord dcPos, ClickModifiers mods, Spread* pSpread, BOOL bSolidDrag) 06028 { 06029 // If the mouse has moved outside the spread the drag was started on then we must 06030 // account for this. 06031 if (pSpread != m_pStartSpread) 06032 { 06033 // Convert from one spread's coordinates to another's. 06034 dcPos = MakeRelativeToSpread(m_pStartSpread, pSpread, dcPos); 06035 } 06036 06037 // Now remember this mouse position. 06038 m_dcLastMousePos = dcPos; 06039 06040 // If the constrain key is down then "snap" to the nearest blob position (as if the 06041 // "telephone keypad" had been clicked). 06042 if (mods.Constrain) 06043 { 06044 // Find the current nearest blob. 06045 DocCoord dcNearest; 06046 GetNearestBlob(dcPos, &dcNearest, NULL); 06047 06048 // If it is the same as the last snapped position do nothing, otherwise update 06049 // our records. 06050 if (dcNearest == m_dcLastPos) return; 06051 dcPos = dcNearest; 06052 } 06053 else 06054 { 06055 // If the constrain key ISN'T down then we will snap to the grid, if appropriate. 06056 DocView::SnapCurrent(pSpread, &dcPos); 06057 } 06058 06059 // Erase the old centre blob and draw the new one. 06060 RenderDragBlobs(CalcBlobClipRect(m_dcLastPos), m_pStartSpread, bSolidDrag); 06061 m_dcLastPos = dcPos; 06062 RenderDragBlobs(CalcBlobClipRect(m_dcLastPos), m_pStartSpread, bSolidDrag); 06063 } 06064 06065 06066 06067 /******************************************************************************************** 06068 > virtual BOOL OpDragRotateCentre::DragKeyPress(KeyPress* pKey, BOOL bSolidDrag) 06069 06070 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06071 Created: 18/11/94 06072 Inputs: pKey pointer to the KeyPress object representing a key event 06073 Outputs: - 06074 Returns: TRUE if the key-press was handled, FALSE otherwise. 06075 Purpose: Checks for the constrain key going up or down, snapping the dragged rotation 06076 centre to the nearest bounds blob when it goes down, freeing it to follow 06077 the mouse when the key goes up. 06078 Errors: - 06079 SeeAlso: - 06080 ********************************************************************************************/ 06081 06082 BOOL OpDragRotateCentre::DragKeyPress(KeyPress* pKey, BOOL bSolidDrag) 06083 { 06084 // Is this the constrain key going down or up? Note that we have to explicitly compare 06085 // against the virtual key-code as the keypress stuff has a bug in it - it won't have the 06086 // constrain bit set when the release bit is set as well (ie. it doesn't send CTRL key-up 06087 // events). 06088 if (pKey->GetVirtKey() == CAMKEY(CC_MOD_CONSTRAIN)) 06089 { 06090 // Treat the keypress as it was a mouse move to get the right snapping behaviour... 06091 DragPointerMove(m_dcLastMousePos,ClickModifiers::GetClickModifiers(),m_pStartSpread, FALSE); 06092 06093 // Yes, we did process this one. 06094 return TRUE; 06095 } 06096 06097 // Nope, didn't process it. 06098 return FALSE; 06099 } 06100 06101 06102 06103 /******************************************************************************************** 06104 > void OpDragRotateCentre::DragFinished(DocCoord dcPos, ClickModifiers mods, 06105 Spread* pSpread, BOOL fDragOK, BOOL bSolidDrag) 06106 06107 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06108 Created: 31/8/94 06109 Inputs: dcPos final position of the mouse 06110 pSpread spread containing the mouse 06111 fDragOK whether the drag was cancelled or not 06112 Outputs: - 06113 Returns: - 06114 Purpose: Moves the rotation centre to its final resting place (he he) and ends the 06115 drag. 06116 Errors: - 06117 SeeAlso: - 06118 ********************************************************************************************/ 06119 06120 void OpDragRotateCentre::DragFinished(DocCoord dcPos, ClickModifiers mods, 06121 Spread* pSpread, BOOL fDragOK, BOOL bSolidDrag) 06122 { 06123 // Put the hourglass up 06124 BeginSlowJob(); 06125 06126 // Act on the termination status of the drag operation. 06127 if (fDragOK) 06128 { 06129 // Treat the drag finish as a mouse move to gett he right snapping behaviour... 06130 DragPointerMove(dcPos,mods,pSpread, bSolidDrag); 06131 06132 // Tell the selector tool that the rotation centre has moved. 06133 ((SelectorTool*) Tool::GetCurrent())->RotationCentreDragged(m_dcLastPos); 06134 06135 // Reenable the tool's idle processing. 06136 SelectorTool::AllowIdleWork(TRUE); 06137 06138 // End the Drag 06139 if (!EndDrag()) FailAndExecute(); 06140 } 06141 else 06142 { 06143 // Remove the last drawn rotation centre blob. 06144 RenderDragBlobs(CalcBlobClipRect(m_dcLastPos), m_pStartSpread, bSolidDrag); 06145 06146 // Redraw it at its original position. 06147 RenderDragBlobs(CalcBlobClipRect(m_dcLastPos = m_dcFirstPos), m_pStartSpread, bSolidDrag); 06148 06149 // Reenable the tool's idle processing. 06150 SelectorTool::AllowIdleWork(TRUE); 06151 06152 // Set up the flags that say it all went wrong. 06153 EndDrag(); 06154 FailAndExecute(); 06155 } 06156 06157 // Finished. 06158 End(); 06159 } 06160 06161 06162 06163 /******************************************************************************************** 06164 > void OpDragRotateCentre::RenderDragBlobs(DocRect drClip, Spread* pSpread, BOOL bSolidDrag) 06165 06166 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06167 Created: 31/8/94 06168 Inputs: drClip clipping rectangle for output 06169 pSpread the spread to render into 06170 Outputs: - 06171 Returns: - 06172 Purpose: Draws the rotation centre blobby at the last-known position of the mouse 06173 during a drag. 06174 Errors: - 06175 SeeAlso: - 06176 ********************************************************************************************/ 06177 06178 void OpDragRotateCentre::RenderDragBlobs(DocRect drClip, Spread* pSpread, BOOL bSolidDrag) 06179 { 06180 RenderRegion* pRegion = DocView::RenderOnTop(&drClip, pSpread, ClippedEOR); 06181 while (pRegion != NULL) 06182 { 06183 // Set the line colour 06184 pRegion->SetLineColour(COLOUR_XORSELECT); 06185 06186 // Draw the rotation centre at the given position. 06187 ((SelectorTool*) Tool::GetCurrent())->RenderRotateCentre(pRegion, m_dcLastPos); 06188 06189 // Get the next render region. 06190 pRegion = DocView::GetNextOnTop(&drClip); 06191 } 06192 } 06193 06194 06195 06196 /******************************************************************************************** 06197 > BOOL OpDragRotateCentre::Declare() 06198 06199 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06200 Created: 31/8/94 06201 Inputs: - 06202 Outputs: - 06203 Returns: TRUE if the op is correctly registered. 06204 Purpose: Registers the rotation centre drag Operation. 06205 Errors: - 06206 SeeAlso: - 06207 ********************************************************************************************/ 06208 06209 BOOL OpDragRotateCentre::Declare() 06210 { 06211 return RegisterOpDescriptor(0, /* _R(IDS_DRAGROTATECENTREOP), */ 0, 06212 CC_RUNTIME_CLASS(OpDragRotateCentre), 06213 OPTOKEN_SELECTOR_DRAGCENTRE, GetState); 06214 } 06215 06216 06217 06218 /******************************************************************************************** 06219 > OpState OpDragRotateCentre::GetState(String_256*, OpDescriptor*) 06220 06221 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06222 Created: 31/8/94 06223 Inputs: - 06224 Outputs: - 06225 Returns: Returns a default OpState. 06226 Purpose: Controls whether the rotation-centre mouse drag operation is available or 06227 not. Currently, is always available (the internal logic of the selector 06228 tool & info-bar handle this really). 06229 Errors: - 06230 SeeAlso: - 06231 ********************************************************************************************/ 06232 06233 OpState OpDragRotateCentre::GetState(String_256*, OpDescriptor*) 06234 { 06235 OpState os; 06236 return os; 06237 } 06238 06239 06240 06241 /******************************************************************************************** 06242 > static DocRect OpDragRotateCentre::CalcBlobClipRect(const DocCoord& dcPos) 06243 06244 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06245 Created: 31/8/94 06246 Inputs: dcPos the centre of the blob that the clipping rectangle will surround 06247 Outputs: - 06248 Returns: A clipping rectangle for use by RenderDragBlobs. 06249 Purpose: Constructs a rectangle that surrounds the given point during a mouse 06250 drag. Some rendering functions require a clipping rectangle that isn't 06251 always available (poor design or what) so this makes one up as necessary. 06252 Errors: - 06253 SeeAlso: OpDragRotateCentre::RenderDragBlobs 06254 ********************************************************************************************/ 06255 06256 DocRect OpDragRotateCentre::CalcBlobClipRect(const DocCoord& dcPos) 06257 { 06258 INT32 nSize = SelectorTool::pBlobManager->GetBlobSize() + 2; 06259 return DocRect(DocCoord(dcPos.x - nSize, dcPos.y - nSize), 06260 DocCoord(dcPos.x + nSize, dcPos.y + nSize)); 06261 } 06262 06263 06264 06265 /******************************************************************************************** 06266 > void OpDragRotateCentre::GetNearestBlob(const DocCoord& dcPos, 06267 DocCoord* pPos, 06268 INT32* pBlob) const 06269 06270 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com> 06271 Created: 18/11/94 06272 Inputs: dcPos the point for which the nearest blob is to be found 06273 Outputs: pPos if not NULL then is set to the position of the nearest blob 06274 pBlob if not NULL then is set to the blob ID of the nearest blob 06275 Returns: - 06276 Purpose: Finds the nearest bounds blob to the given point. 06277 Errors: - 06278 SeeAlso: OpDragRotateCentre::DragKeyPress; OpDragRotateCentre::DragPointerMove 06279 ********************************************************************************************/ 06280 06281 void OpDragRotateCentre::GetNearestBlob(const DocCoord& dcPos, DocCoord* pPos, INT32* pBlob) const 06282 { 06283 // Find out the nearest blob position to dcPos. 06284 SelectorTool* pTool = (SelectorTool*) Tool::GetCurrent(); 06285 DocCoord dcNearestBlob(0,0); 06286 06287 // This needs to be done a little carefully, as INT32_MAX itself is not big enough. 06288 XLONG nNearestDistance = XLONG(INT32_MAX); 06289 nNearestDistance *= nNearestDistance; // square it to be safe 06290 06291 // Compare against every blob. 06292 INT32 i; 06293 for (i = 0; i < 9; i++) 06294 { 06295 // Find out the square of the distance, in millipoints, to the i'th blob. 06296 DocCoord dcBlob = pTool->GetSelPosNearBlob(i); 06297 XLONG dx = XLONG(dcPos.x) - XLONG(dcBlob.x); 06298 XLONG dy = XLONG(dcPos.y) - XLONG(dcBlob.y); 06299 XLONG dz2 = (dx * dx) + (dy * dy); 06300 06301 // If it's nearer remember it. 06302 if (dz2 < nNearestDistance) 06303 { 06304 nNearestDistance = dz2; 06305 dcNearestBlob = dcBlob; 06306 } 06307 } 06308 06309 // Set the output paramaters accordingly. 06310 if (pPos != NULL) *pPos = dcNearestBlob; 06311 if (pBlob != NULL) *pBlob = i; 06312 }