selector.cpp

Go to the documentation of this file.
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 }

Generated on Sat Nov 10 03:47:54 2007 for Camelot by  doxygen 1.4.4