slicetool.cpp

Go to the documentation of this file.
00001 // $Id: slicetool.cpp 1282 2006-06-09 09:46:49Z alex $
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 // Implementation of the Slice tool
00099 
00100 // standard includes for a tool
00101 #include "camtypes.h"
00102 #include "oilfiles.h"
00103 #include "csrstack.h"
00104 //#include "viewrc.h"
00105 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "markn.h"
00108 
00109 #include "layer.h"      // knowing about layers
00110 //#include "document.h" // being able to get the spread - in camtypes.h [AUTOMATICALLY REMOVED]
00111 #include "slice.h"      // for the export slices op
00112 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "ink.h"      // knowing about ink nodes - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "sprdmsg.h"    // SpreadMsg
00115 #include "layermsg.h"   // the layer messaging
00116 
00117 #include "barcreationdlg.h" // for the create dlg
00118 
00119 #include "slicetool.h"      // <******* Remember to change to include the tool's header file
00120 //#include "sliceres.h"     // most of the resources that this tool uses
00121 //#include "simon.h" // some string resources
00122 
00123 // for the use of wix temple attribs
00124 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 //#include "attrval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 #include "userattr.h"
00127 #include "tmpltatr.h"
00128 
00129 // named set stuff
00130 #include "ngcore.h"
00131 #include "ngitem.h"
00132 #include "ngsentry.h"
00133 
00134 #include "selall.h" // for OPTOKEN_EDITSELECTNONE
00135 #include "bitfilt.h" // for the get spread bounds
00136 
00137 #include "slicehelper.h"
00138 #include "opdupbar.h" // bar duplication
00139 #include "blobs.h" // for the blob manager?
00140 
00141 #include "selector.h"   // for OpSelectorDragBox
00142 #include "guides.h"     // for NodeGuideline
00143 #include "keypress.h"   // for KeyPress
00144 #include "basebar.h"    // for BaseBar
00145 #include "nodetxts.h"   // for TextStory
00146 #include "opdragbx.h"   // for OpDragBox
00147 #include "progress.h"   // for BeginSlowJob()
00148 
00149 #include "vkextra.h"    // for CAMKEY(CC_)... key defs
00150 //#include "justin.h"       // for _R(IDS_SLICE_MODE0) etc.
00151 //#include "will2.h"        // for _R(IDS_SELOPTIONS)
00152 //#include "resource.h" // for _R(IDS_OUT_OF_MEMORY) and _R(IDS_OK)
00153 
00154 //#include "clikdrag.h" // for _R(IDS_TRANSLTRANS_)...
00155 
00156 #include "opdupbar.h" // for the showlayer stuff
00157 
00158 // enable slice-tool blobs.
00159 #define SLICETOOL_BLOBS
00160 
00161 void UpdateCurrentStateGadget(); // forward def
00162 
00163 DECLARE_SOURCE( "$Revision: 1282 $" );
00164 
00165 #define MIN(a, b)   ((a) < (b) ? (a) : (b))
00166 #define MAX(a, b)   ((a) < (b) ? (b) : (a))
00167 #define CURSORID_UNSET 5000
00168 
00169 CC_IMPLEMENT_MEMDUMP(SliceTool, DragTool)
00170 CC_IMPLEMENT_DYNCREATE(SliceInfoBarOp, InformationBarOp)
00171 CC_IMPLEMENT_DYNCREATE(OpSliceDragBox, OpSelectorDragBox)
00172 CC_IMPLEMENT_DYNCREATE(OpSliceTranslate, TransOperation)
00173 
00174 // Must come after the last CC_IMPLEMENT.. macro
00175 #define new CAM_DEBUG_NEW     
00176 
00177 // These are still char* while we wait for resource technology to be developed for modules
00178 char* SliceTool::FamilyName = "Slice Tools";
00179 char* SliceTool::ToolName   = "Slice Tool";
00180 char* SliceTool::Purpose    = "Slice manipulation";
00181 char* SliceTool::Author     = "Simon K";
00182 
00183 // Init those other useful static vars
00184 BOOL            SliceTool::CurrentTool          = FALSE;
00185 SliceInfoBarOp* SliceTool::pSliceInfoBarOp      = NULL;
00186 
00187 // global variable to remember what bar we were working on between switching tools
00188 String_256  g_BarName;
00189 
00190 /********************************************************************************************
00191 
00192 >   SliceTool::SliceTool()
00193 
00194     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00195     Created:    3/10/94
00196     Purpose:    Default Constructor.
00197                 Other initialisation is done in SliceTool::Init which is called by the Tool Manager
00198     SeeAlso:    SliceTool::Init
00199 
00200 ********************************************************************************************/
00201 
00202 SliceTool::SliceTool()
00203 {
00204     // initialise all our member pointers to NULL.
00205 
00206     pcNormalSliceCursor     = NULL;
00207     pcAdjustCursor          = NULL;
00208     pcUnderCursor           = NULL;
00209     pcInsideCursor          = NULL;
00210     pcUnderAdjustCursor     = NULL;
00211     pcInsideAdjustCursor    = NULL;
00212     pcLeafCursor            = NULL;
00213     pcLeafAdjustCursor      = NULL;
00214 
00215     pcCurrentCursor     = NULL;
00216     pcALLCursor         = NULL;
00217     pcHorzGuideCursor   = NULL;
00218     pcVertGuideCursor   = NULL;
00219 
00220     StartSpread     = NULL;
00221     SelectionSpread = NULL;
00222     SelectRange     = NULL;
00223 
00224     pClickSimpleNode    = NULL;
00225     pClickCompoundNode  = NULL;
00226     pLastClickNode      = NULL;
00227     pPreProcClickNode   = NULL;
00228 }
00229 
00230 /********************************************************************************************
00231 
00232 >   SliceTool::~SliceTool()
00233 
00234     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00235     Created:    3/10/94
00236     Purpose:    Destructor (Virtual). Does nothing.
00237 
00238 ********************************************************************************************/
00239 
00240 SliceTool::~SliceTool()
00241 {
00242 }
00243 
00244 
00245 /********************************************************************************************
00246 
00247 >   BOOL SliceTool::Init( INT32 Pass )
00248 
00249     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00250     Created:    3/10/94
00251     Returns:    FALSE if it does not want to be created, TRUE otherwise
00252     Purpose:    Used to check if the Tool was properly constructed
00253     SeeAlso:    SliceTool::SliceTool
00254 
00255 ********************************************************************************************/
00256 
00257 BOOL SliceTool::Init()
00258 {
00259     // declare all ops used by SliceTool.
00260     BOOL    ok = OpSliceDragBox::Declare();
00261     if (ok) ok = OpSliceTranslate::Declare();
00262     ERROR2IF(!ok, FALSE, "Couldn't Declare all Ops in SliceTool::Init.");
00263 
00264     // This section reads in the infobar definition and creates an instance of
00265     // SliceInfoBarOp.  Also pSliceInfoBarOp, the ptr to the tool's infobar, is set up
00266     // after the infobar is successfully read and created.
00267     if (ok)
00268     {
00269         CCResTextFile       file;               // Resource File
00270         SliceInfoBarOpCreate BarCreate;         // Object that creates SliceInfoBarOp objects
00271 
00272                 ok = file.open(_R(IDM_SLICE_BAR), _R(IDT_INFO_BAR_RES));        // Open resource
00273         if (ok) ok = DialogBarOp::ReadBarsFromFile(file,BarCreate); // Read and create info bar
00274         if (ok) file.close();                                       // Close resource
00275 
00276         ENSURE(ok,"Unable to load Slicebar.ini from resource\n"); 
00277 
00278         if (ok)
00279         {
00280             // Info bar now exists.  Now get a pointer to it
00281             String_32 str = String_32(_R(IDS_SLICETOOL_INFOBARNAME));
00282             DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str);
00283 
00284                     ok = (pDialogBarOp != NULL);
00285             if (ok) ok = pDialogBarOp->IsKindOf(CC_RUNTIME_CLASS(SliceInfoBarOp));
00286             if (ok) pSliceInfoBarOp = (SliceInfoBarOp*)pDialogBarOp;
00287 
00288             ENSURE(ok,"Error finding the Slice tool info bar");
00289         }
00290     }
00291 
00292     // Get a permanent pointer to the application's SelRange object.
00293     SelectRange = GetApplication()->FindSelection();
00294 
00295     // init the global var with the first bar name to create - ie Bar1
00296     g_BarName.MakeMsg(_R(IDS_BARNAME), 1);
00297 
00298     return (ok);
00299 }
00300 
00301 
00302 /********************************************************************************************
00303 
00304 >   void SliceTool::Describe(void *InfoPtr)
00305 
00306     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00307     Created:    3/10/94
00308     Inputs:     InfoPtr -   A pointer to a tool info block. It is passed cast to void* as
00309                             the version of the tool is unknown at this point. Later versions 
00310                             of the Tool class may have more items in this block, that this 
00311                             tool will not use
00312     Outputs:    InfoPtr -   The structure pointed to by InfoPtr will have had all the info
00313                             that this version of the Tool knows about
00314     Purpose:    Allows the tool manager to extract information about the tool
00315 
00316 ********************************************************************************************/
00317 
00318 void SliceTool::Describe(void *InfoPtr)
00319 {
00320     // Cast structure into the latest one we understand.
00321     ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr;
00322 
00323     Info->InfoVersion = 1;
00324     
00325     Info->InterfaceVersion = GetToolInterfaceVersion();  // You should always have this line.
00326         
00327     // These are all arbitrary at present.
00328     Info->Version = 1;
00329     Info->ID      = GetID();
00330     Info->TextID  = _R(IDS_SLICE_TOOL);
00331 
00332     Info->Family  = FamilyName;
00333     Info->Name    = ToolName;
00334     Info->Purpose = Purpose;
00335     Info->Author  = Author;
00336 
00337     Info->BubbleID = _R(IDBBL_SLICE_TOOLBOX);
00338 }
00339 
00340 /********************************************************************************************
00341 
00342 >   virtual void SliceTool::SelectChange(BOOL isSelected)
00343 
00344     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00345     Created:    3/10/94
00346     Inputs:     isSelected  - TRUE  = tool has been selected
00347                             - FALSE = tool has been deselected
00348     Outputs:    -
00349     Returns:    -
00350     Purpose:    Starts up and closes down the Slice tool
00351     Errors:     Debug warning if creating the cursor fails.
00352     SeeAlso:    -
00353 
00354 ********************************************************************************************/
00355 
00356 void SliceTool::SelectChange(BOOL isSelected)
00357 {
00358     if (isSelected)
00359     {
00360         // create the tool's cursors and push the default one onto the stack.
00361         if (!CreateCursors()) return;
00362         CurrentCursorID = CursorStack::GPush(pcNormalSliceCursor, FALSE);
00363         pcCurrentCursor = pcNormalSliceCursor;
00364 
00365         // This tool is now the current one.
00366         CurrentTool = TRUE;
00367 
00368         // Create and display the tool's info bar.
00369         if (pSliceInfoBarOp->Create())
00370             pSliceInfoBarOp->SetSliceTool(this);
00371 
00372         // register the Slice tool's blob preference with the app.
00373         BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
00374         if (pBlobMgr != NULL)
00375         {
00376             // we only use Tiny blobs.
00377             BlobStyle bsBlobs;
00378             bsBlobs.Tiny = TRUE;
00379             pBlobMgr->ToolInterest(bsBlobs);
00380         }
00381 
00382         // make a note that we mustn't ignore any selection-changed messages.
00383         m_fIgnoreSelChange = FALSE;
00384 
00385         // Update our info on the selection, and if necessary, render our tool blobs on.
00386         if (UpdateSelectionInfo())
00387             pBlobMgr->RenderToolBlobsOn(this, SelectionSpread, NULL);
00388     }
00389     else
00390     {
00391         // destroy the tool's cursors, if they exist.
00392         if (pcCurrentCursor != NULL)
00393         {
00394             CursorStack::GPop(CurrentCursorID);
00395             pcCurrentCursor = NULL;
00396             CurrentCursorID = 0;
00397         }
00398         DestroyCursors();
00399 
00400         // remove the info-bar's slice-tool pointer.
00401         pSliceInfoBarOp->SetSliceTool(NULL);
00402 
00403         // Remove the info bar from view by deleting the actual underlying window
00404         pSliceInfoBarOp->m_InfoBarCreated = FALSE;
00405         pSliceInfoBarOp->Delete();
00406 
00407         // ensure any tool object blobs are removed.
00408         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00409         if (BlobMgr != NULL)
00410         {
00411             BlobStyle bsRemoves;
00412             bsRemoves.ToolObject = TRUE;
00413             BlobMgr->RemoveInterest(bsRemoves);
00414         }
00415 
00416         // No longer the current tool
00417         CurrentTool = FALSE;
00418 
00419         // Ensure our tool blobs have been rendered off the selected spread.
00420         BlobMgr->RenderToolBlobsOff(this, SelectionSpread, NULL);
00421     }
00422 }
00423 
00424 
00425 
00426 /********************************************************************************************
00427 
00428 >   void SliceTool::SelectionHasChanged()
00429 
00430     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00431     Created:    19/10/1999
00432     Purpose:    A public function, for other people to call whenever they need
00433                 to let the Slice tool know that the selection has changed.
00434 
00435                 Allows the Slice tool to update its private information about the selection.
00436     Errors:     
00437     See also:   UpdateSelectionInfo();
00438 
00439 ********************************************************************************************/
00440 void SliceTool::SelectionHasChanged()
00441 {
00442     // we sometimes ignore selection-changed messages, so that we don't get our tool-blobs
00443     // in a tizzy.
00444     if (m_fIgnoreSelChange)
00445         return;
00446 
00447     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
00448     if (pBlobMgr==NULL) return;
00449 
00450     // ok, remove any of our previous tool blobs.
00451     pBlobMgr->RenderToolBlobsOff(this, SelectionSpread, NULL);
00452 
00453     // reset our set-sel flag, to indicate that we did not change the selection.
00454     // this flag needs to be cleared whenever the selection changes, in order to
00455     // keep a record of whether we changed the selection. whenever we update the
00456     // selection from within SliceTool, we must set this flag manually *after* we
00457     // tell everyone that the selection changed.
00458     m_bSliceToolSetSel = FALSE;
00459 
00460     // update the Slice tool's selection information,
00461     // and if necessary then render our tool blobs on.
00462     if (UpdateSelectionInfo())
00463         pBlobMgr->RenderToolBlobsOn(this, SelectionSpread, NULL);
00464 }
00465 
00466 
00467 
00468 /********************************************************************************************
00469 
00470 >   void SliceTool::ViewChanged(const DocViewMsg& msg)
00471 
00472     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00473     Created:    06/07/2000
00474     Inputs:     
00475     Outputs:    
00476     Returns:    
00477     Purpose:    The SliceTool's view-changed message handler.
00478                 This method is called directly from the message handler of SliceInfoBarOp,
00479                 which lives further down in this file. Its sole purpose is to let us redraw
00480                 our blobs correctly.
00481     Errors:     
00482     See also:   
00483 
00484 ********************************************************************************************/
00485 void SliceTool::ViewChanged(const DocViewMsg& msg)
00486 {
00487 #ifdef SLICETOOL_BLOBS
00488     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
00489     if (pBlobMgr==NULL) return;
00490 
00491     switch (msg.State)
00492     {
00493         case DocViewMsg::DocViewState::SELABOUTTOCHANGE:
00494         {
00495             // Only bother rendering our tool blobs off if the new view is valid.
00496             if (msg.pNewDocView != NULL)
00497             {
00498                 pBlobMgr->RenderToolBlobsOff(this, SelectionSpread, NULL);
00499 
00500                 // so that we don't draw tool blobs onto any new view, we must ignore any
00501                 // select-change message until the new view is installed.
00502                 m_fIgnoreSelChange = TRUE;
00503             }
00504 
00505             break;
00506         }
00507 
00508         case DocViewMsg::DocViewState::SELCHANGED:
00509         {
00510             // if the new DocView is valid, then draw our blobs onto it.
00511             if (msg.pNewDocView != NULL)
00512                 if (UpdateSelectionInfo())
00513                     pBlobMgr->RenderToolBlobsOn(this, SelectionSpread, NULL);
00514 
00515             // ok, the new view is now here - we can stop ignoring sel-changed messages.
00516             m_fIgnoreSelChange = FALSE;
00517             break;
00518         }
00519 
00520         default:
00521             break;
00522     }
00523 #endif
00524 }
00525 
00526 
00527 
00528 /********************************************************************************************
00529 
00530 >   BOOL SliceTool::CreateCursors()
00531 
00532     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00533     Created:    3/10/94
00534     Inputs:     -
00535     Outputs:    -
00536     Returns:    TRUE if all the Slice tool cursors have been successfully created
00537     Purpose:    Creates all the Slice tool cursors
00538     SeeAlso:    -
00539 
00540 ********************************************************************************************/
00541 
00542 BOOL SliceTool::CreateCursors()
00543 {
00544     // This tool has just been selected.  Create its cursors.
00545     pcNormalSliceCursor = new Cursor(this, _R(IDC_POINTER_SLICE));
00546     pcAdjustCursor      = new Cursor(this, _R(IDC_POINTER_SLICE_ADJUST));
00547     pcUnderCursor       = new Cursor(this, _R(IDC_POINTER_SLICE_UNDER));
00548     pcInsideCursor      = new Cursor(this, _R(IDC_POINTER_SLICE_INSIDE));
00549     pcUnderAdjustCursor = new Cursor(this, _R(IDC_POINTER_SLICE_UNDERADJUST));
00550     pcInsideAdjustCursor= new Cursor(this, _R(IDC_POINTER_SLICE_INSIDEADJUST));
00551     pcALLCursor         = new Cursor(this, _R(IDCSR_SEL_GRADPOINT));
00552     pcLeafCursor        = new Cursor(this, _R(IDC_POINTER_SLICE_LEAF));
00553     pcLeafAdjustCursor  = new Cursor(this, _R(IDC_POINTER_SLICE_LEAFADJUST));
00554     pcHorzGuideCursor   = new Cursor(this, _R(IDCSR_SEL_HGUIDE));
00555     pcVertGuideCursor   = new Cursor(this, _R(IDCSR_SEL_VGUIDE));
00556 
00557     // now check them...
00558     BOOL    ok = (pcNormalSliceCursor != NULL && pcNormalSliceCursor->IsValid());
00559     if (ok) ok = (pcAdjustCursor != NULL && pcAdjustCursor->IsValid());
00560     if (ok) ok = (pcUnderCursor != NULL && pcUnderCursor->IsValid());
00561     if (ok) ok = (pcInsideCursor != NULL && pcInsideCursor->IsValid());
00562     if (ok) ok = (pcUnderAdjustCursor != NULL && pcUnderAdjustCursor->IsValid());
00563     if (ok) ok = (pcInsideAdjustCursor != NULL && pcInsideAdjustCursor->IsValid());
00564     if (ok) ok = (pcALLCursor != NULL && pcALLCursor->IsValid());
00565     if (ok) ok = (pcLeafCursor != NULL && pcLeafCursor->IsValid());
00566     if (ok) ok = (pcLeafAdjustCursor != NULL && pcLeafAdjustCursor->IsValid());
00567     if (ok) ok = (pcHorzGuideCursor != NULL && pcHorzGuideCursor->IsValid());
00568     if (ok) ok = (pcVertGuideCursor != NULL && pcVertGuideCursor->IsValid());
00569     if (!ok)
00570         DestroyCursors();
00571 
00572     return ok;
00573 }
00574 
00575 /********************************************************************************************
00576 
00577 >   void SliceTool::DestroyCursors()
00578 
00579     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00580     Created:    3/10/94
00581     Inputs:     -
00582     Outputs:    -
00583     Returns:    -
00584     Purpose:    Destroys all the Slice tool cursors
00585     SeeAlso:    -
00586 
00587 ********************************************************************************************/
00588 
00589 void SliceTool::DestroyCursors()
00590 {
00591     if (pcNormalSliceCursor != NULL) delete pcNormalSliceCursor;
00592     if (pcAdjustCursor != NULL) delete pcAdjustCursor;
00593     if (pcUnderCursor != NULL) delete pcUnderCursor;
00594     if (pcInsideCursor != NULL) delete pcInsideCursor;
00595     if (pcUnderAdjustCursor != NULL) delete pcUnderAdjustCursor;
00596     if (pcInsideAdjustCursor != NULL) delete pcInsideAdjustCursor;
00597     if (pcALLCursor != NULL) delete pcALLCursor;
00598     if (pcLeafCursor != NULL) delete pcLeafCursor;
00599     if (pcLeafAdjustCursor != NULL) delete pcLeafAdjustCursor;
00600     if (pcHorzGuideCursor != NULL) delete pcHorzGuideCursor;
00601     if (pcVertGuideCursor != NULL) delete pcVertGuideCursor;
00602 }
00603 
00604 /********************************************************************************************
00605 
00606 >   void SliceTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00607                         Spread* pSpread )
00608 
00609     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00610     Created:    3/10/94
00611     Inputs:     PointerPos  -   The DocCoord of the point where the mouse button was clicked
00612                 Click       -   Describes the type of click that was detected. 
00613                 ClickMods   -   Indicates which buttons caused the click and which modifers were
00614                                 pressed at the same time
00615                 pSpread     -   The spread in which the click happened
00616     Returns:    -
00617     Purpose:    To handle a Mouse Click event for the Slice Tool.
00618     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00619 
00620 ********************************************************************************************/
00621 
00622 void SliceTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00623                         Spread* pSpread )
00624 {
00625     // we ignore any clicks with the Menu mouse button.
00626     if (ClickMods.Menu)
00627         return;
00628 
00629     // remember the mouse position, in case we're starting a drag.
00630     if (Click == CLICKTYPE_SINGLE)
00631     {
00632         ClickStart = PointerPos;
00633         StartSpread = pSpread;
00634     }
00635 
00636     // remember the click modifiers for later use in the click-processing code.
00637     this->ClickMods = ClickMods;
00638     TypeOfClick = Click;
00639 
00640     // update our spread and bounds info about the selection.
00641     UpdateSelectionInfo();
00642 
00643     // refresh our ptr to the app's selection, for use in the click-processing code.
00644     SelectRange = GetApplication()->FindSelection();
00645     RangeControl rc = SelectRange->GetRangeControlFlags();
00646     rc.IgnoreInvisibleLayers = TRUE;
00647     SelectRange->Range::SetRangeControl(rc);
00648 
00649     // we process clicks in three stages, stopping as soon as
00650     // a stage successfully processes the click.
00651 
00652     // this function checks for and lets us drag guidelines.
00653     if (!PreProcessClick())
00654     {
00655         // this function is a carry-over from the Selector tool code.
00656         // it currently does nothing.
00657         if (!ProcessObjectClick())
00658         {
00659             // this function deals with normal mouse-click handling.
00660             PostProcessClick();
00661         }
00662     }
00663 }
00664 
00665 
00666 
00667 /*******************************************************************
00668 
00669 >   virtual BOOL SliceTool::PreProcessClick()
00670 
00671     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00672     Created:    08/10/1999
00673     Inputs:     
00674     Outputs:    
00675     Returns:    TRUE if the mouse event was handled here,
00676                 FALSE otherwise.
00677     Purpose:    Allows a click to be handled before going through the
00678                 main click-handling code.
00679                 This function checks for a clicked Node, and allows it
00680                 to respond to the click.
00681     Errors:     
00682     See also:   SliceTool::OnClick
00683 
00684 *******************************************************************/
00685 BOOL SliceTool::PreProcessClick()
00686 {
00687     // look for a Node which may be interested in this click, and allow it to respond.
00688     pPreProcClickNode = FindPreProcessClickNode(StartSpread, ClickStart);
00689     if (pPreProcClickNode != NULL)
00690         if (pPreProcClickNode->OnClick(ClickStart, TypeOfClick, ClickMods, StartSpread))
00691             return TRUE;
00692 
00693     // no preprocess node, or it didn't want the click.
00694     return FALSE;
00695 }
00696 
00697 
00698 
00699 /*******************************************************************
00700 
00701 >   virtual NodeRenderableInk* SliceTool::FindPreProcessClickNode(Spread* pSpread,
00702                                                                 DocCoord ClickPos,
00703                                                                 BOOL bInterruptible = FALSE)
00704     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00705     Created:    08/10/1999
00706     Inputs:     pSpread         pointer to a Spread.
00707                 dcPos           mouse click position in pSpread.
00708                 bInterruptible  whether or not this function can be
00709                                 aborted by mouse movement.
00710     Outputs:    
00711     Returns:    Pointer to a node interested in processing the OnClick message.
00712     Purpose:    Scans the nodes in the spread, to see if any of them want to
00713                 deal with this message before its processing continues.
00714 
00715                 Currently, only looks for NodeGuidelines.
00716     Errors:     
00717     See also:   SliceTool::PreProcessClick()
00718 
00719 *******************************************************************/
00720 NodeRenderableInk* SliceTool::FindPreProcessClickNode(  Spread* pSpread,
00721                                                         DocCoord ClickPos,
00722                                                         BOOL bInterruptible )
00723 {
00724     ERROR2IF(pSpread == NULL, NULL, "SliceTool::FindPreProcessClickNode- pSpread is NULL");
00725 
00726     // initialise vars.
00727     NodeRenderableInk* pFoundNode = NULL;
00728     BOOL bFound = FALSE;
00729 
00730     // look for a visible, editable guides layer.
00731     Layer* pLayer = pSpread->FindFirstLayer();
00732     while (pLayer != NULL && !bFound)
00733     {
00734         if (pLayer->IsGuide() && !pLayer->IsLocked() && pLayer->IsVisible())
00735         {
00736             // We have a layer that's also a guide layer
00737             // Now look for the guidelines in this layer
00738             Node* pNodeInLayer = pLayer->FindFirstChild(CC_RUNTIME_CLASS(NodeGuideline));
00739             while (pNodeInLayer != NULL && !bFound)
00740             {
00741                 pFoundNode = (NodeGuideline*)pNodeInLayer;
00742 
00743                 // found a node - now hit-test its bounds.
00744                 DocRect Rect = pFoundNode->GetBoundingRect(FALSE,TRUE);
00745                 bFound = (Rect.ContainsCoord(ClickPos));
00746 
00747                 pNodeInLayer = pNodeInLayer->FindNext(CC_RUNTIME_CLASS(NodeGuideline));
00748             }
00749         }
00750 
00751         pLayer = pLayer->FindNextLayer();
00752     }
00753 
00754     if (bFound)
00755     {
00756         // OK, we have found a node interested in a preprocess click
00757         // We now have to ensure that it's not being obscured visually by another node
00758         NodeRenderableInk* pNode = NULL;
00759         if (bInterruptible)
00760         {
00761             // Allow the hit-test to be interrupted if the mouse moves!
00762             Node* pInterruptedNode = NULL;
00763             pNode = NodeRenderableInk::FindSimpleAtPoint(   pSpread,
00764                                                             ClickPos,
00765                                                             NULL,
00766                                                             &pInterruptedNode);
00767             // If hit-test was interrupted then don't say anything about what's under the pointer!
00768             if (pInterruptedNode!=NULL)
00769                 return NULL;
00770         }
00771         else
00772         {
00773             // Can't be interrupted by mouse movement so just go for it...
00774             pNode = NodeRenderableInk::FindSimpleAtPoint(pSpread,ClickPos);
00775         }
00776 
00777         if (pNode)
00778         {
00779             // Find out whether the hit node is in front of the guideline or not.
00780             // If it is, then clear the Found flag.
00781             Layer* pLowerLayer = (Layer*) pFoundNode->FindParent(CC_RUNTIME_CLASS(Layer));  // The guideline layer
00782             Layer* pNodeLayer = (Layer*) pNode->FindParent(CC_RUNTIME_CLASS(Layer));        // The layer containing the node
00783             // Make sure GuideLayer comes after NodeLayer
00784             do
00785             {
00786                 pLowerLayer = pLowerLayer->FindNextLayer();     // Find the layer above the last one tested
00787             }
00788             while (pLowerLayer && pLowerLayer!=pNodeLayer);     // Keep going while there is a layer
00789                                                                 // and that layer isn't the one we're looking for
00790             // Get here when either we've run out of layers or we've found the layer we want
00791             if (pLowerLayer && pLowerLayer==pNodeLayer)         // If found layer above guide layer
00792                 bFound=FALSE;                                   // Then flag that the guideline is obscured
00793         }
00794     }
00795 
00796     if (!bFound)
00797         pFoundNode = NULL;
00798 
00799     return pFoundNode;
00800 }
00801 
00802 
00803 
00804 /*******************************************************************
00805 
00806 >   BOOL SliceTool::ProcessObjectClick()
00807 
00808     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00809     Created:    08/10/1999
00810     Inputs:     
00811     Outputs:    
00812     Returns:    FALSE always.
00813     Purpose:    In the selector tool, this function OnClick()'s each object
00814                 in the tool's range if the Selector tool is using Object blobs.
00815                 We don't, so we won't.
00816     Errors:     
00817     See also:   SelectorTool::ProcessObjectClick
00818 
00819 *******************************************************************/
00820 BOOL SliceTool::ProcessObjectClick()
00821 {
00822     return FALSE;
00823 }
00824 
00825 
00826 
00827 /*******************************************************************
00828 
00829 >   void SliceTool::PostProcessClick()
00830 
00831     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00832     Created:    08/10/1999
00833     Inputs:     
00834     Outputs:    
00835     Returns:    
00836     Purpose:    Provided a click wasn't eaten by a pre- or mid- processor,
00837                 we deal with it here. This passes the event on to specific
00838                 handler routines, depending on its type (TypeOfClick):
00839 
00840                 CLICKTYPE_SINGLE    HandleSingleClick
00841                 CLICKTYPE_DOUBLE    HandleDoubleClick
00842                 CLICKTYPE_DRAG      HandleDragClick
00843                 CLICKTYPE_UP        HandleButtonUp
00844     Errors:     
00845     See also:   
00846 
00847 *******************************************************************/
00848 void SliceTool::PostProcessClick()
00849 {
00850     switch (TypeOfClick)
00851     {
00852     case CLICKTYPE_SINGLE:
00853         HandleSingleClick();
00854         break;
00855 
00856     case CLICKTYPE_DOUBLE:
00857         HandleDoubleClick();
00858         break;
00859 
00860     case CLICKTYPE_DRAG:
00861         HandleDragClick();
00862         break;
00863 
00864     case CLICKTYPE_UP:
00865         HandleButtonUp();
00866         break;
00867 
00868     default:
00869         // unrecognised click-type - do nothing.
00870         break;
00871     }
00872 }
00873 
00874 
00875 
00876 /*******************************************************************
00877 
00878 >   virtual void SliceTool::HandleSingleClick()
00879 
00880     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00881     Created:    08/10/1999
00882     Purpose:    Single mouse-click event handler.
00883     Errors:     
00884     See also:   
00885 
00886 *******************************************************************/
00887 void SliceTool::HandleSingleClick()
00888 {
00889     // work out what modifiers apply to this click.
00890     SetKeyDownCursor(ClickMods);
00891 
00892     // Find out which object, if any, was clicked on.  We hit-detect both the simple node
00893     // that was clicked and any top-level compound object it may be part of.
00894     pClickSimpleNode = NodeRenderableInk::FindSimpleAtPoint(StartSpread, ClickStart);
00895     pClickCompoundNode = NodeRenderableInk::FindCompoundFromSimple(pClickSimpleNode);
00896 }
00897 
00898 
00899 
00900 /*******************************************************************
00901 
00902 >   void SliceTool::HandleDoubleClick()
00903 
00904     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00905     Created:    08/10/1999
00906     Purpose:    Double mouse-click event handler.
00907                 Currently, passes the event to HandleSingleClick().
00908     Errors:     
00909     See also:   
00910 
00911 *******************************************************************/
00912 void SliceTool::HandleDoubleClick()
00913 {
00914     HandleSingleClick();
00915 }
00916 
00917 
00918 
00919 /*******************************************************************
00920 
00921 >   void SliceTool::HandleDragClick()
00922 
00923     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00924     Created:    08/10/1999
00925     Purpose:    Mouse-drag event handler.
00926     Errors:     
00927     See also:   
00928 
00929 *******************************************************************/
00930 void SliceTool::HandleDragClick()
00931 {
00932     // check for a forced translate, and perform a translate
00933     // operation if appropriate.
00934     if (IsTranslateShortcut(ClickMods))
00935     {       
00936         if (SelectionSpread != NULL)
00937         {
00938             // There is a selection, so start a translate drag...
00939             DoTranslate();
00940         }
00941         return;
00942     }
00943     
00944     // Right, we clicked on either an object (possibly already selected) or
00945     // blank paper.  First, check if there is an object at the click position.
00946     if (pClickCompoundNode == NULL)
00947     {
00948         // There is only blank paper at the click point, so assume the user is
00949         // trying to start a marquee selection drag. Run the drag-box operation.
00950         DoDragBox();
00951         return;
00952     }
00953 
00954     // Is the clicked object already selected?  Note that the user may be trying to click on
00955     // a simple object selected inside a group that itself is not selected, so we must be
00956     // careful to check the state of the right nodes here.
00957     //
00958     // The logic here is:
00959     // If the clicked simple node or any of it's parents are selected
00960     // Then DON'T alter the selection to reflect the clicked object!
00961     //
00962     BOOL SimpleInSelected = FALSE;              // So far haven't encountered any selected nodes
00963     Node* pNode = pClickSimpleNode;             // Make a working pointer and initialise it
00964     do
00965     {
00966         if (pNode->IsSelected())                // If the working node is selected
00967         {
00968             SimpleInSelected = TRUE;            // Then the simple node or one of its parents are
00969             break;                              // selected so we don't need to change the selection!
00970         }
00971         pNode = pNode->FindParent();            // Else check the working node's parent
00972     }
00973     while (pNode != pClickCompoundNode->FindParent());// until we've reached the compound node's parent
00974                                                 // (Allows the compound node itself to be checked)
00975 
00976     if (!SimpleInSelected)
00977     {
00978         // No.  If the click was with the left button we must deselect all other objects.
00979         // If the clicked object isn't selected, we can't run a transform on it.  Hence we must
00980         // select it, but we prevent it being redrawn as selected (with its little blobs).
00981         if (!ClickMods.Adjust)
00982         {
00983             // Normal click, so deselect everything before we select the clicked node...
00984             NodeRenderableInk::DeselectAll(TRUE, FALSE);
00985         }
00986         else
00987         {
00988             // We clicked with the right button, but if the click was in a different spread to
00989             // the selection we have to deselect everything anyway.
00990             Node* pNode = SelectRange->FindFirst();
00991             if (pNode != NULL && pNode->FindParentSpread() != SelectionSpread)
00992             {
00993                 // Clicked node in a different spread from previous selection, so clear selection
00994                 NodeRenderableInk::DeselectAll(TRUE, FALSE);
00995             }
00996         }
00997 
00998         // Now that the selection state of all other objects is dealt with, make sure the
00999         // clicked object, as well as objects sharing its name, is selected.
01000         //
01001         // Currently, SelectAllSetsOfThisNode indirectly does a SelectRange->Update(TRUE).
01002         String_256 SetName;
01003         SetName.Empty();
01004         SliceHelper::SelectAllSetsOfThisNode(pClickCompoundNode, SetName, FALSE);
01005         if (SetName.IsEmpty())
01006         {
01007             pClickCompoundNode->Select(TRUE);
01008 
01009             // force a broadcast of the changed selection, so that our info-bar is informed.
01010             SelectRange->Update(TRUE);
01011         }
01012     }
01013 
01014     // Finally, run a transform on the selected object(s).  Of course, the user may not be
01015     // trying to drag the selection - if so the button will come up before a significant
01016     // drag has occurred and we can take it from there.
01017     MakeSelectionValidForDrag();
01018 
01019     // do the drag.
01020     DoTranslate();
01021 
01022     // remember that _we_ changed the selection.
01023     m_bSliceToolSetSel = TRUE;
01024 }
01025 
01026 
01027 
01028 /********************************************************************************************
01029 
01030 >   void SliceTool::HandleButtonUp()
01031 
01032     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from JustinF's SelectorTool::HandleButtonUp code
01033     Created:    08/10/1999
01034     Purpose:    Mouse button-up event handler.
01035     Errors:     
01036     See also:   
01037 
01038 ********************************************************************************************/
01039 void SliceTool::HandleButtonUp()
01040 {
01041     // if we changed the selection last, or the SelRange cannot provide a usable
01042     // last-selection then use our own record. otherwise, ask the SelRange.
01043     Node* pLastSelNode = NULL;
01044     if (m_bSliceToolSetSel && pLastClickNode != NULL)
01045     {
01046         pLastSelNode = pLastClickNode;
01047     }
01048     else
01049     {
01050         pLastSelNode = SelectRange->GetLastSelectedNode();
01051         if ( pLastSelNode == NULL || !pLastSelNode->IsAnObject() )
01052             pLastSelNode = pLastClickNode;
01053     }
01054 
01055     // Find out what we should do with the click...
01056     ClickActionCode action = CLICKACTION_NONE;
01057     NodeRenderableInk* pActionNode = NULL;
01058     action = DetermineClickAction(  &pActionNode, (NodeRenderableInk*)pLastSelNode,
01059                                     pClickSimpleNode, pClickCompoundNode, StartSpread,
01060                                     ClickStart, ClickMods );
01061 
01062     // Act upon the information...
01063     switch (action)
01064     {
01065         //-------------------------------------------------//
01066         // No action required...
01067         case CLICKACTION_NONE:
01068             break;
01069 
01070         //-------------------------------------------------//
01071         // Anything that's selected must be deselected...
01072         case CLICKACTION_SELNONE:
01073         {
01074             // Don't clear selection if was an adjust click
01075             // (Change requested by Charles and Alan Burns)
01076             if (!ClickMods.Adjust)
01077             {
01078                 // Markn 29/9/95: We ignore this if the click happened on a guideline.
01079                 // If another type of node needs to behave like this, then a virt func in node
01080                 // will be required instead of the hideous IS_A() clause in the 'if' statement
01081                 if (pPreProcClickNode == NULL || !IS_A(pPreProcClickNode,NodeGuideline))
01082                 {
01083                     NodeRenderableInk::DeselectAll(TRUE, TRUE);
01084                     pLastClickNode = NULL;
01085                 }
01086             }
01087             break;
01088         }
01089 
01090         //-------------------------------------------------//
01091         // The action node must be selected or toggled...
01092         case CLICKACTION_SELNODE:
01093         case CLICKACTION_SELUNDER:
01094         case CLICKACTION_SELUNDERCYCLE:
01095         case CLICKACTION_SELUNDERFAIL:
01096         case CLICKACTION_SELUNDERFAIL2:
01097         case CLICKACTION_SELINSIDE:
01098         case CLICKACTION_SELINSIDECYCLE:
01099         case CLICKACTION_SELINSIDEFAIL:
01100         case CLICKACTION_SELINSIDEFAIL2:
01101         case CLICKACTION_SELLEAF:
01102         {
01103             ERROR3IF(pActionNode == NULL, "Action and ActionNode don't agree!");
01104 
01105             // if we aren't Adjust'ing, ensure no previous selection.
01106             if (!ClickMods.Adjust)
01107                 NodeRenderableInk::DeselectAll(TRUE, FALSE);
01108 
01109             // try to select all nodes sharing the same name as the clicked node.
01110             String_256 SetName;
01111             SetName.Empty();
01112             SliceHelper::SelectAllSetsOfThisNode(pActionNode, SetName, ClickMods.Adjust);
01113 
01114             // if the clicked node has no name then the above code will do nothing,
01115             // so we need to do a normal select, accounting for Adjust.
01116             if (SetName.IsEmpty())
01117             {
01118                 if (ClickMods.Adjust)
01119                 {
01120                     // If Adjust is applied, toggle the state of the action node.
01121                     if (pActionNode->IsSelected())
01122                         pActionNode->DeSelect(TRUE);
01123                     else
01124                         pActionNode->Select(TRUE);
01125                 }
01126                 else
01127                 {
01128                     pActionNode->Select(TRUE);
01129                 }
01130 
01131                 // force a broadcast of the changed selection state, so our info-bar updates.
01132                 SelectRange->Update(TRUE);
01133             }
01134 
01135             // update our record of the last clicked node.
01136             pLastClickNode = pActionNode;
01137 
01138             // make a note that we changed the selection.
01139             m_bSliceToolSetSel = TRUE;
01140 
01141             break;
01142         }
01143         //-------------------------------------------------//
01144         default:
01145             ERROR3("Unknown Click action code!");
01146             break;
01147     }; // switch (action)
01148 
01149     // Make sure the cursor reflects which keys are down, now that the mouse button has
01150     // been released.
01151     SetKeyDownCursor(ClickModifiers::GetClickModifiers());
01152 }
01153 
01154 
01155 
01156 /*******************************************************************
01157 
01158 >   virtual BOOL SliceTool::OnKeyPress(KeyPress *pKeyPress)
01159 
01160     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01161     Created:    08/10/1999
01162     Inputs:     pKeyPress   pointer to a key-press object.
01163     Outputs:    
01164     Returns:    
01165     Purpose:    key-press event handler. Catch CTRL/ALT/SHIFT modifiers
01166                 and change the status-line/cursor appropriately.
01167     Errors:     
01168     See also:   
01169 
01170 *******************************************************************/
01171 BOOL SliceTool::OnKeyPress(KeyPress *pKeyPress)
01172 {
01173     // Find the current state of the "click" keyboard modifiers...
01174     ClickMods = ClickModifiers::GetClickModifiers();
01175 
01176     switch (pKeyPress->GetVirtKey())
01177     {
01178     case CAMKEY(CC_MOD_ADJUST):                     // bit 0 of fKeyStates (SHIFT)
01179     case CAMKEY(CC_MOD_ALTERNATIVE):                    // bit 1 of fKeyStates (ALT)
01180     case CAMKEY(CC_MOD_CONSTRAIN):                  // bit 2 of fKeyStates (CONTROL)
01181         // apparently, this is a bodge for speed.
01182         // see SelectorTool::OnKeyPress for details :-)
01183         break;
01184 
01185     case CAMKEY(TAB):                               // moves selection to next rendered node
01186         if (pKeyPress->IsPress()) HandleTabKey(ClickMods.Adjust);
01187         break;
01188 
01189     case CAMKEY(HOME):                              // select first object in render order
01190         if (pKeyPress->IsPress())
01191         {
01192             if (SelectionSpread != NULL)
01193                 NodeRenderableInk::DeselectAll();
01194             HandleTabKey(FALSE);
01195         }
01196         break;
01197 
01198     case CAMKEY(END):                               // select last object in render order
01199         if (pKeyPress->IsPress())
01200         {
01201             if (SelectionSpread != NULL)
01202                 NodeRenderableInk::DeselectAll();
01203             HandleTabKey(TRUE);
01204         }
01205         break;
01206 
01207     default:                                    // not interested in processing this
01208         return FALSE;
01209     }   
01210 
01211     // If we processed a click modifier then update the cursor and return that we processed
01212     // the keystroke.
01213     SetKeyDownCursor(ClickMods);
01214 
01215     // Yes, we processed this key-event.
01216     return TRUE;
01217 }
01218 
01219 
01220 
01221 /*******************************************************************
01222 
01223 >   void SliceTool::HandleTabKey(BOOL bIsShifted)
01224 
01225     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01226     Created:    08/10/1999
01227     Purpose:    Tab keypress event handler.
01228     Errors:     
01229     See also:   
01230 
01231 *******************************************************************/
01232 void SliceTool::HandleTabKey(BOOL bIsShifted)
01233 {
01234 }
01235 
01236 
01237 
01238 /*******************************************************************
01239 
01240 >   void SliceTool::SetKeyDownCursor(ClickModifiers cmMods)
01241 
01242     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01243     Created:    08/10/1999
01244     Inputs:     
01245     Outputs:    
01246     Returns:    
01247     Purpose:    Decodes the bit-field fKeyStates, indicating which combination
01248                 of modifier keys are down, and sets the cursor appropriately.
01249     Errors:     
01250     See also:   
01251 
01252 *******************************************************************/
01253 void SliceTool::SetKeyDownCursor(ClickModifiers cmMods)
01254 {
01255     // Get current position information for call to change pointer shape...
01256     Spread*  pSpread;
01257     DocCoord dcMousePos;
01258     if (DocView::GetCurrentMousePos(&pSpread, &dcMousePos) &&
01259         Tool::GetCurrentID() == TOOLID_SLICETOOL &&
01260         !BaseBar::IsDragging())
01261     {
01262         // Call nice central routine to figure out what pointer shape to show...
01263         // (Set the status bar text while we're at it.)
01264         String_256 Str;
01265         Cursor* pPtr;
01266         FigureUserFeedback(pSpread, dcMousePos, cmMods, TRUE, &Str, &pPtr);
01267         if (CurrentCursorID != CURSORID_UNSET)
01268             CursorStack::GSetTop(pPtr, CurrentCursorID);
01269         if (!(Str.IsEmpty()))
01270             SetStatusText( &Str );
01271     }
01272 }
01273 
01274 
01275 
01276 /********************************************************************************************
01277 
01278 >   void SliceTool::OnMouseMove( DocCoord PointerPos,Spread* pSpread, ClickModifiers ClickMod )
01279 
01280     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01281     Created:    3/10/94
01282     Inputs:     PointerPos  -   The DocCoord of the point where the mouse has moved to
01283                 pSpread     -   The spread in which the move occurred
01284                 ClickMods   -   The state of the various modifiers at the time of the mouse move
01285     Returns:    TRUE if it handled the Click, FALSE otherwise
01286     Purpose:    To handle a Mouse Move event for the Slice Tool.
01287     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
01288 
01289 ********************************************************************************************/
01290 
01291 void SliceTool::OnMouseMove(DocCoord PointerPos,Spread* pSpread,ClickModifiers ClickMods)
01292 {
01293     // Display status bar text for the current position
01294 //  DisplayStatusBarHelp(PointerPos, pSpread, ClickMods);
01295 
01296     // If there isn't any selection, or it's in a different spread, then do nothing.
01297     if (SelectionSpread == NULL || SelectionSpread != pSpread) return;
01298     
01299     String_256 str;
01300     Cursor* pcPointerShape;
01301 
01302     FigureUserFeedback(pSpread, PointerPos, ClickMods, FALSE, &str, &pcPointerShape);
01303 
01304     if (!(str.IsEmpty()))
01305         SetStatusText(&str);
01306 
01307     if (CurrentCursorID != CURSORID_UNSET)
01308         CursorStack::GSetTop(pcPointerShape, CurrentCursorID);
01309 }
01310 
01311 
01312 
01313 /********************************************************************************************
01314 >   void SliceTool::MakeSelectionValidForDrag()
01315 
01316     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01317     Created:    3/5/95
01318     Inputs:     -
01319     Outputs:    Can change the current selection, causing an immediate SelChanged message
01320     Returns:    -
01321     Purpose:    Runs through the selected nodes, making sure they are all happy with being 
01322                 dragged.  Checks are:-
01323                 1. Sub-Selected text characters are deselected, and their parent story (and
01324                 all other nodes sharing its name) are selected instead.
01325     Errors:     -
01326     SeeAlso:    -
01327 ********************************************************************************************/
01328 
01329 void SliceTool::MakeSelectionValidForDrag()
01330 {
01331     SelRange *pSelection = GetApplication()->FindSelection();
01332     RangeControl rg = pSelection->GetRangeControlFlags();
01333     rg.IgnoreInvisibleLayers = TRUE; // oddly setting this to TRUE actually means DO INCLUDE INIVISIBLE LAYERS in the range!!!
01334     RangeControl rcOld = rg;
01335     rg.PromoteToParent = TRUE;
01336     pSelection->Range::SetRangeControl(rg);
01337 
01338     Node* pNode = pSelection->FindFirst();
01339     BOOL ChangedSelection = FALSE;
01340 
01341     while (pNode != NULL)
01342     {
01343         // Push any text sub-selection up to the selected story, and make sure
01344         // that any other Nodes sharing the text-story's name are also selected.
01345         if (pNode->IsAnAbstractTextChar())
01346         {
01347             ((NodeRenderableInk*)pNode)->DeSelect(TRUE);
01348             TextStory* pStory = (TextStory*)pNode->FindParent(CC_RUNTIME_CLASS(TextStory));
01349             if (pStory != NULL)
01350             {
01351                 String_256 SetName;
01352                 SetName.Empty();
01353                 SliceHelper::SelectAllSetsOfThisNode(pStory, SetName, FALSE);
01354                 if (SetName.IsEmpty())
01355                     pStory->Select(TRUE);
01356                 ChangedSelection = TRUE;
01357             }
01358         }
01359 
01360         pNode = pSelection->FindNext(pNode);
01361     }
01362 
01363     // if we needed to change the selection, tell everyone.
01364     if (ChangedSelection)
01365     {
01366         // this call commented out, as I don't think it is required. pushing the selection
01367         // up to the letter's text-story in this way does not actually change the overall
01368         // state of the selection wrt named sets, as the story shares the same set name as
01369         // the character (always?). - Karim 25/10/1999
01370 //      GetApplication()->FindSelection()->Update(TRUE);    // is this call actually 
01371         m_bSliceToolSetSel = TRUE;
01372     }
01373 
01374     pSelection->Range::SetRangeControl(rcOld);
01375 }
01376 
01377 
01378 
01379 /*******************************************************************
01380 
01381 >   void SliceTool::DoTranslate()
01382 
01383     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01384     Created:    11/10/1999
01385     Inputs:     
01386     Outputs:    
01387     Returns:    
01388     Purpose:    Runs a translation tranformation drag on the selection.
01389     Errors:     
01390     See also:   
01391 
01392 *******************************************************************/
01393 void SliceTool::DoTranslate()
01394 {
01395     // set appropriate transform parameters...
01396     TransformData tdParams;
01397     tdParams.CentreOfTrans  = ClickStart;
01398     tdParams.LeaveCopy      = FALSE;
01399     tdParams.LockAspect     = TRUE;
01400     tdParams.pRange         = NULL;
01401     tdParams.ScaleLines     = FALSE;
01402     tdParams.StartBlob      = 0;
01403     tdParams.TransFills     = TRUE;
01404 
01405     // set drag pointer shape.
01406     if (CurrentCursorID != CURSORID_UNSET)
01407         CursorStack::GSetTop(pcALLCursor, CurrentCursorID);
01408 
01409     // attempt to create a translate op.
01410     OpSliceTranslate* pSliceTransOp = new OpSliceTranslate();
01411     if (pSliceTransOp == NULL)
01412     {
01413         InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
01414         return;
01415     }
01416 
01417     // Get the current DocView (there must be one or we wouldn't be here).
01418     DocView* pDocView = DocView::GetCurrent();
01419     ERROR3IF(pDocView == NULL, "SliceTool::DoTranslate- Null current DocView");
01420 
01421     DocCoord dcOffset(0, 0);        // Default to offsets of 0
01422 
01423     // Fill a Transform Bounding Data structure up here
01424     BoundingData.x        = SelectionRect.lo.x;
01425     BoundingData.y        = SelectionRect.lo.y;
01426     BoundingData.Width    = SelectionRect.Width();
01427     BoundingData.Height   = SelectionRect.Height();
01428     BoundingData.XScale   = (FIXED16) 1;
01429     BoundingData.YScale   = (FIXED16) 1;
01430     BoundingData.Rotation = (ANGLE) 0;
01431     BoundingData.Shear    = (ANGLE) 0;
01432 
01433     // Run the transformation drag operation and return success code.
01434     pSliceTransOp->DragStarted(&tdParams, this, &BoundingData, ClickStart,
01435                               StartSpread, ClickMods, dcOffset, NULL, DRAGTYPE_AUTOSCROLL);
01436 }
01437 
01438 
01439 
01440 /*******************************************************************
01441 
01442 >   void SliceTool::DoDragBox()
01443 
01444     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01445     Created:    11/10/1999
01446     Inputs:     
01447     Outputs:    
01448     Returns:    
01449     Purpose:    Runs a selector-tool drag-box operation.
01450     Errors:     
01451     See also:   
01452 
01453 *******************************************************************/
01454 void SliceTool::DoDragBox()
01455 {
01456     OpSliceDragBox* pOpDragBox = new OpSliceDragBox();
01457     if (pOpDragBox == NULL)
01458     {
01459         InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
01460     }
01461     else
01462     {
01463         pOpDragBox->StartDragBox(StartSpread, ClickStart, ClickMods);
01464     }
01465 }
01466 
01467 
01468 
01469 /********************************************************************************************
01470 
01471 >   void SliceTool::DisplayStatusBarHelp(DocCoord DocPos, Spread* pSpread, ClickModifiers ClickMods)
01472 
01473     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01474     Created:    12/12/94
01475     Inputs:     DocPos - the document coordinate of the point to display help on
01476                 pSpread - pointer to the spread containing DocPos
01477                 ClickMods - the current click modifiers
01478     Outputs:    -
01479     Returns:    -
01480     Purpose:    Displays status help string for the given position in the status bar.
01481     SeeAlso:    SliceTool::GetCurrentStatusText
01482 
01483 ********************************************************************************************/
01484 
01485 void SliceTool::DisplayStatusBarHelp(DocCoord DocPos, Spread* pSpread, ClickModifiers ClickMods)
01486 {
01487     String_256 StatusMsg("");
01488 
01489     // Get a string from the underlying help function and display it.
01490     GetCurrentStatusText(&StatusMsg, pSpread, DocPos, ClickMods);
01491     GetApplication()->UpdateStatusBarText(&StatusMsg);                           
01492 }
01493 
01494 
01495 
01496 /*******************************************************************
01497 
01498 >   BOOL SliceTool::GetStatusLineText(String_256* ptext, Spread* pSpread,
01499                                       DocCoord DocPos, ClickModifiers ClickMods)
01500 
01501     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Phil's code for SelectorTool.
01502     Created:    11/10/1999
01503     Inputs:     ptext       Pointer to text string to fill in
01504                 pSpread     Pointer to spread containing mouse position
01505                 DocPos      Mouse position within spread
01506                 ClickMods   Click modifiers
01507     Outputs:    -
01508     Returns:    TRUE always, indicating that the string was updated.
01509     Purpose:    Figure out what the status text for the SliceTool is at the given position
01510                 on the given spread with the given click modifiers.
01511     Errors:     ERROR2 if pText is NULL.
01512     See also:   
01513 
01514 *******************************************************************/
01515 BOOL SliceTool::GetStatusLineText(String_256* ptext, Spread* pSpread,
01516                                   DocCoord DocPos, ClickModifiers ClickMods)
01517 {
01518     ERROR2IF(ptext==NULL,FALSE,"ptext is NULL!");
01519 
01520     Cursor* pcDummy;        // Dummy to hold unused pointer shape computed by FigureUserFeedback
01521     FigureUserFeedback(pSpread, DocPos, ClickMods, TRUE, ptext, &pcDummy);
01522 
01523     if (CurrentCursorID != CURSORID_UNSET)
01524         CursorStack::GSetTop(pcDummy,CurrentCursorID);
01525 
01526     return TRUE;
01527 }
01528 
01529 
01530 
01531 /********************************************************************************************
01532 
01533 >   void SliceTool::GetCurrentStatusText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
01534 
01535     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01536     Created:    12/12/94
01537     Inputs:     pSpread points to a spread
01538                 DocPos points to a point in a document
01539                 ClickMods are the current click modifiers
01540     Outputs:    Updates the string in ptext
01541     Returns:    -
01542     Purpose:    Selects a suitable string for the status line based on the current location
01543                 (as input via the parameters).
01544     SeeAlso:    SliceTool::GetStatusLineText, SliceTool::DisplayStatusBarHelp
01545 
01546 ********************************************************************************************/
01547 
01548 void SliceTool::GetCurrentStatusText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
01549 {
01550     // You must use the Spread, DocCoord and ClickModifiers to select a suitable string to be 
01551     // displaied in the status bar.  This is usually done via some sort of switch statement.
01552     // Having selected a suitable string you should do a statement like 
01553     //  ptext->Load(<#Insert your string ID here #>);
01554 
01555     // Delete this line when you display useful strings!
01556     ptext->Empty(); 
01557 }
01558 
01559 
01560 
01561 /*******************************************************************
01562 
01563 >   virtual void SliceTool::FigureUserFeedback( Spread* pSpread,
01564                                                 DocCoord dcPos,
01565                                                 ClickModifiers cmods,
01566                                                 BOOL DoSlowTests,
01567                                                 String_256* pStr,
01568                                                 Cursor** ppPointerShape )
01569     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01570     Created:    08/10/1999
01571     Inputs:     
01572     Outputs:    
01573     Returns:    
01574     Purpose:    This routine computes the status line help and the pointer shape for a
01575                 given mouse position in the Slice tool. It does NOT set either of these
01576                 values but returns them both to the caller. It's up to the caller to decide
01577                 whether to use these values or not.
01578                 The tests done to figure out which pointer shapes, status help (and maybe 
01579                 other stuff one day) is quite complex so it's most efficient to compute them
01580                 all at once and then let the caller decide which ones to actually use.
01581                 The precedence of pointer shapes, status messages and other user feedback is
01582                 determined by the order of the checks within this function.
01583     Errors:     
01584     See also:   
01585 
01586 *******************************************************************/
01587 void SliceTool::FigureUserFeedback( Spread* pSpread,
01588                                     DocCoord dcPos,
01589                                     ClickModifiers cmods,
01590                                     BOOL DoSlowTests,
01591                                     String_256* pStr,
01592                                     Cursor** ppPointerShape )
01593 {
01594     // Initialise the status text string and pointer shape to be "null".
01595     pStr->Empty();
01596     *ppPointerShape = pcNormalSliceCursor;
01597 
01598     if (Tool::GetCurrentID() != TOOLID_SLICETOOL)
01599         return;
01600 
01601     BOOL DescribeDrag = TRUE;
01602 
01603     //-------------------------------------------------------------------------
01604     // Set the pointer shape, according to the key modifiers and/or the current state.
01605     if (!cmods.Adjust && !cmods.Constrain && !cmods.Alternative1)
01606     {
01607         *ppPointerShape = pcNormalSliceCursor;
01608         Append(pStr,_R(IDS_SLICE_MODE0));   // "Normal select mode:"
01609     }
01610 
01611     else if (cmods.Adjust && !cmods.Constrain && !cmods.Alternative1)
01612     {
01613         *ppPointerShape = pcAdjustCursor;
01614         Append(pStr,_R(IDS_SLICE_MODE1));   //  "Adjust select:"
01615     }
01616 
01617     else if (!cmods.Adjust && IsSelectUnderClick(cmods))
01618     {
01619         *ppPointerShape = pcUnderCursor;
01620         Append(pStr,_R(IDS_SLICE_MODE2));   //  "Select under:"
01621     }
01622 
01623     else if (cmods.Adjust && IsSelectUnderClick(cmods))
01624     {
01625         *ppPointerShape = pcUnderAdjustCursor;
01626         Append(pStr,_R(IDS_SLICE_MODE3));   //  "Adjust select under:"
01627     }
01628 
01629     else if (!cmods.Adjust && IsSelectMemberClick(cmods))
01630     {
01631         *ppPointerShape = pcInsideCursor;
01632         Append(pStr,_R(IDS_SLICE_MODE4));   //  "Force drag/Select member:"
01633     }
01634 
01635     else if (cmods.Adjust && IsSelectMemberClick(cmods))
01636     {
01637         *ppPointerShape = pcInsideAdjustCursor;
01638         Append(pStr,_R(IDS_SLICE_MODE5));   //  "Force drag/Adjust select member:"
01639     }
01640 
01641     else if (!cmods.Adjust && IsSelectLeafClick(cmods))
01642     {
01643         *ppPointerShape = pcLeafCursor;
01644         Append(pStr,_R(IDS_SLICE_MODE6));   //  "Select inside:"
01645     }
01646 
01647     else if (cmods.Adjust && IsSelectLeafClick(cmods))
01648     {
01649         *ppPointerShape = pcLeafAdjustCursor;
01650         Append(pStr,_R(IDS_SLICE_MODE7));   //  "Adjust select inside:"
01651     }
01652 
01653     else
01654     {
01655         ERROR3("SliceTool::FigureUserFeedback- out of range ClickModifiers");
01656         return;
01657     }
01658 
01659     // Check for direct drag mode and say something about it...
01660     if (IsTranslateShortcut(cmods))
01661     {
01662         if (SelectRange->FindFirst())
01663         {
01664             *ppPointerShape = pcALLCursor;
01665             Append(pStr,_R(IDS_SLICEINSIDE4));  //  "Drag to move the selected objects"
01666         }
01667         DescribeDrag = FALSE;
01668     }
01669 
01670     // See if there's a non-selectable object (guideline) under the mouse
01671     NodeRenderableInk* pPreProNode = FindPreProcessClickNode(pSpread,dcPos,TRUE);
01672     if (pPreProNode)
01673     {
01674         if (IS_A(pPreProNode,NodeGuideline))
01675         {
01676             NodeGuideline* pGuideline = (NodeGuideline*)pPreProNode;
01677             if (pGuideline->GetType()==GUIDELINE_HORZ)
01678             {
01679                 *ppPointerShape = pcHorzGuideCursor;
01680                 //  "Drag up or down to move guideline; Drag onto top ruler to delete it"
01681                 Append(pStr,_R(IDS_SLICEHORZGUIDE));
01682                 return;
01683             }
01684 
01685             if (pGuideline->GetType()==GUIDELINE_VERT)
01686             {
01687                 *ppPointerShape = pcVertGuideCursor;
01688                 //  "Drag left or right to move guideline; Drag onto left ruler to delete it"
01689                 Append(pStr,_R(IDS_SLICEVERTGUIDE));
01690                 return;
01691             }
01692         }
01693     }
01694 
01695     //-------------------------------------------------------------------------
01696     // If we've got time to do the slow tests then go ahead and do them...
01697     if (DoSlowTests)
01698     {
01699         // Perform a hit-test at the mouse position and set the status bar text
01700         // accordingly.
01701         // Allow the hit-test to be interrupted if the mouse moves!
01702         Node* pInterruptedNode = NULL;
01703         NodeRenderableInk* pSimple = NodeRenderableInk::FindSimpleAtPoint(pSpread, 
01704                                                                           dcPos, 
01705                                                                           NULL, 
01706                                                                           &pInterruptedNode);
01707         // If hit-test was interrupted then don't say anything about what's under the pointer!
01708         if (pInterruptedNode!=NULL)
01709             return;
01710 
01711         NodeRenderableInk* pCompound = NodeRenderableInk::FindCompoundFromSimple(pSimple);
01712 
01713         // if we changed the selection last, or the SelRange cannot provide a usable
01714         // last-selection then use our own record. otherwise, ask the SelRange.
01715         Node* pLastSelNode = NULL;
01716         if (m_bSliceToolSetSel && pLastClickNode != NULL)
01717         {
01718             pLastSelNode = pLastClickNode;
01719         }
01720         else
01721         {
01722             pLastSelNode = SelectRange->GetLastSelectedNode();
01723             if ( pLastSelNode==NULL || !(pLastSelNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk))) )
01724                 pLastSelNode = pLastClickNode;
01725         }
01726 
01727         // Find out what we should do with the click...
01728         ClickActionCode action = CLICKACTION_NONE;
01729         NodeRenderableInk* pActionNode = NULL;
01730         action = DetermineClickAction(&pActionNode,(NodeRenderableInk*)pLastSelNode,
01731                                         pSimple,pCompound,pSpread,dcPos,cmods);
01732 
01733         // Act upon the information...
01734         switch (action)
01735         {
01736             //-------------------------------------------------//
01737             // No action required...
01738             case CLICKACTION_NONE:
01739                 break;
01740 
01741             //-------------------------------------------------//
01742             // Anything that's selected must be deselected...
01743             case CLICKACTION_SELNONE:
01744                 if (!cmods.Adjust)
01745                 {
01746                     // If there are selected objects append message about clearing.
01747                     if (SelectRange && SelectRange->FindFirst())
01748                         Append(pStr,_R(IDS_SLICENONE1));    //  "Click to clear the selection"
01749                     // Message about marquee drag
01750                     if (DescribeDrag)
01751                         Append(pStr,_R(IDS_SLICENONE4));    //  "Drag to marquee select objects"
01752                     Append(pStr,_R(IDS_SLICENONE2));        //  "Move pointer over object to select"
01753                 }
01754                 else
01755                 {
01756                     // Adjust is held down so describe marquee add.
01757                     if (DescribeDrag)
01758                         //  "Drag to marquee select objects to add to selection"
01759                         Append(pStr,_R(IDS_SLICENONE5));
01760                     //  "Move pointer over object to add/remove from selection"
01761                     Append(pStr,_R(IDS_SLICENONE3));
01762                 }
01763                 break;
01764 
01765             //-------------------------------------------------//
01766             // The action node must be selected...
01767             case CLICKACTION_SELNODE:
01768                 Append(pStr,cmods,
01769                             _R(IDS_SLICENODE3), // "Click to select this #1%S alone; Drag to move it"
01770                             _R(IDS_SLICENODE4), // "Click to select this #1%S"
01771                             _R(IDS_SLICENODE5), // "Click to deselect this #1%S"
01772                             pActionNode);
01773                 break;
01774             case CLICKACTION_SELUNDER:
01775                 Append(pStr,cmods,
01776                             _R(IDS_SLICEUNDER1), // "Click to select the #1%S under the last selected object alone"
01777                             _R(IDS_SLICEUNDER2), // "Click to select the #1%S under the last selected object"
01778                             _R(IDS_SLICEUNDER3), // "Click to deselect the #1%S under the last selected object"
01779                             pActionNode);
01780                 break;
01781             case CLICKACTION_SELUNDERCYCLE:
01782                 Append(pStr,cmods,
01783                             _R(IDS_SLICEUNDERCYCLE1), // "Click to select the top #1%S alone; (Reached the bottom)"
01784                             _R(IDS_SLICEUNDERCYCLE2), // "Click to select the top #1%S; (Reached the bottom)"
01785                             _R(IDS_SLICEUNDERCYCLE3), // "Click to deselect the top #1%S; (Reached the bottom)"
01786                             pActionNode);
01787                 break;
01788             case CLICKACTION_SELUNDERFAIL:
01789                 Append(pStr,cmods,
01790                             _R(IDS_SLICEUNDERFAIL1), // "Click to select the #1%S alone; (Nothing under the last selected object)"
01791                             _R(IDS_SLICEUNDERFAIL2), // "Click to select the #1%S; (Nothing under the last selected object)"
01792                             _R(IDS_SLICEUNDERFAIL3), // "Click to deselect the #1%S; (Nothing under the last selected object)"
01793                             pActionNode);
01794                 break;
01795             case CLICKACTION_SELUNDERFAIL2:
01796                 Append(pStr,cmods,
01797                             _R(IDS_SLICEUNDERFAIL21), // "Click to select the #1%S alone; (The last selected object is not under the pointer)"
01798                             _R(IDS_SLICEUNDERFAIL22), // "Click to select the #1%S; (The last selected object is not under the pointer)"
01799                             _R(IDS_SLICEUNDERFAIL23), // "Click to deselect the #1%S; (The last selected object is not under the pointer)"
01800                             pActionNode);
01801                 break;
01802             case CLICKACTION_SELINSIDE:
01803                 Append(pStr,cmods,
01804                             _R(IDS_SLICEINSIDE1), // "Click to select the #1%S member of the last selected object alone"
01805                             _R(IDS_SLICEINSIDE2), // "Click to select the #1%S member of the last selected object"
01806                             _R(IDS_SLICEINSIDE3), // "Click to deselect the #1%S member of the last selected object"
01807                             pActionNode);
01808                 break;
01809             case CLICKACTION_SELINSIDECYCLE:
01810                 Append(pStr,cmods,
01811                             _R(IDS_SLICEINSIDECYCLE1), // "Click to select the top #1%S alone; (Reached the simplest object)"
01812                             _R(IDS_SLICEINSIDECYCLE2), // "Click to select the top #1%S; (Reached the simplest object)"
01813                             _R(IDS_SLICEINSIDECYCLE3), // "Click to deselect the top #1%S; (Reached the simplest object)"
01814                             pActionNode);
01815                 break;
01816             case CLICKACTION_SELINSIDEFAIL:
01817                 Append(pStr, _R(IDS_SLICEINSIDEFAIL1)); // "Nothing inside this object"
01818                 break;
01819             case CLICKACTION_SELINSIDEFAIL2:
01820                 Append(pStr,cmods,
01821                             _R(IDS_SLICEINSIDEFAIL21), // "Click to select the #1%S alone; (The pointer is not over a member of the last selected object)"
01822                             _R(IDS_SLICEINSIDEFAIL22), // "Click to select the #1%S; (The pointer is not over a member of the last selected object)"
01823                             _R(IDS_SLICEINSIDEFAIL23), // "Click to deselect the #1%S; (The pointer is not over a member of the last selected object)"
01824                             pActionNode);
01825                 break;
01826             case CLICKACTION_SELLEAF:
01827                 Append(pStr,cmods,
01828                             _R(IDS_SLICELEAF1), // "Click to select this #1%S alone"
01829                             _R(IDS_SLICELEAF2), // "Click to select this #1%S"
01830                             _R(IDS_SLICELEAF3), // "Click to deselect this #1%S"
01831                             pActionNode);
01832                 break;
01833             //-------------------------------------------------//
01834             default:
01835                 ERROR3("Unknown Click action code!");
01836                 break;
01837         };
01838 
01839         // If we're in normal click mode (no modifiers down) then remind the user
01840         // that they can use the modifer keys...
01841         if (!cmods.Adjust && !cmods.Constrain && !cmods.Alternative1)
01842             Append(pStr,_R(IDS_SELOPTIONS));
01843     }
01844 }
01845 
01846 
01847 
01848 
01849 /********************************************************************************************
01850 
01851 >   ClickActionCode SliceTool::DetermineClickAction(NodeRenderableInk** ppActionNode,
01852                                                     NodeRenderableInk* pLastClickNode,
01853                                                     NodeRenderableInk* pClickSimpleNode,
01854                                                     NodeRenderableInk* pClickCompoundNode,
01855                                                     Spread* pStartSpread,
01856                                                     DocCoord ClickStart,
01857                                                     ClickModifiers ClickMods )
01858 
01859     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Phil's SelectorTool code
01860     Created:    11/10/1999
01861     Inputs:     -
01862     Outputs:    pNodeToSelect   Pointer to pointer to node to select or NULL
01863     Returns:    ActionCode describing what the click should do
01864     Purpose:    Determine what action needs to be taken in response to a click.
01865     Errors:     -
01866     SeeAlso:    SliceTool::HandleButtonUp
01867 
01868 ********************************************************************************************/
01869 
01870 SliceTool::ClickActionCode SliceTool::DetermineClickAction( NodeRenderableInk** ppActionNode,
01871                                                             NodeRenderableInk* pLastClickNode,
01872                                                             NodeRenderableInk* pClickSimpleNode,
01873                                                             NodeRenderableInk* pClickCompoundNode,
01874                                                             Spread* pStartSpread,
01875                                                             DocCoord ClickStart,
01876                                                             ClickModifiers ClickMods)
01877 {
01878     *ppActionNode = NULL;
01879 
01880     //--------------------------------------
01881     // Test "leaf" modifier...
01882     if (IsSelectLeafClick(ClickMods))
01883     {
01884         // Go directly to leaf nodes!
01885         if (pClickSimpleNode != pClickCompoundNode)
01886         {
01887             *ppActionNode = pClickSimpleNode;
01888             
01889             // <<<<< Inclusion by Mike 11/01/96
01890             // this stuff is to check whether any parent is responding to
01891             // AllowSelectInside() and returning FALSE. Selections will not go down
01892             // into these objects if so.
01893 
01894             Node* pParentNode = pClickSimpleNode->FindParent();
01895             while (pParentNode)
01896             {
01897                 if (pParentNode->IsKindOf(CC_RUNTIME_CLASS(Layer)))
01898                     break;
01899                 if (pParentNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)))
01900                 {
01901                     if ( (!pParentNode->AllowSelectInside()) && 
01902                          ((NodeRenderableInk*)pParentNode)->CanSelectAsCompoundParent()
01903                        )
01904                     {
01905                         *ppActionNode = (NodeRenderableInk*)(pParentNode);
01906                     }
01907                 }
01908                 if (pParentNode==pClickCompoundNode)
01909                     break;
01910                 pParentNode = pParentNode->FindParent();
01911             }
01912 
01913             // <<<<< End of inclusion
01914         }
01915 
01916         // If we still haven't found what we're looking for
01917         // Cycle round to the top again...
01918         return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELLEAF,CLICKACTION_SELNODE);
01919     }
01920 
01921     //--------------------------------------
01922     // Test "under" modifier...
01923     if (IsSelectUnderClick(ClickMods))
01924     {
01925         // Try to perform a select under
01926         // First check that the context node is still under the pointer
01927         // If not then all we can do is a normal click operation...
01928         if (!ValidateLastClickUnder(pLastClickNode,pStartSpread,ClickStart))
01929             return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELUNDERFAIL2,CLICKACTION_SELUNDERFAIL2);
01930 
01931         // Find the leaf node at the click position, but only search nodes
01932         // before the last clicked node.
01933         *ppActionNode = NodeRenderableInk::FindSimpleAtPoint(pStartSpread,ClickStart,pLastClickNode);
01934         // Then find a compound node containing the leaf, preferably a sibling
01935         // of the last clicked node.
01936         *ppActionNode = NodeRenderableInk::FindCompoundFromSimple(*ppActionNode,pLastClickNode);
01937 
01938         // If the "under" node turns out to be the node we started from
01939         // return a failure code but go ahead and re-select it...
01940         // (If we failed to find anything under the last node, and the last node is the top node)
01941         if (*ppActionNode==NULL && pLastClickNode == pClickCompoundNode)
01942             return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELUNDERFAIL,CLICKACTION_SELUNDERFAIL);
01943 
01944         // If we still haven't found what we're looking for
01945         // Cycle round to the top again...
01946         return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELUNDER,CLICKACTION_SELUNDERCYCLE);
01947     }
01948 
01949     //--------------------------------------
01950     // Test "member" modifier...
01951     if (IsSelectMemberClick(ClickMods))
01952     {
01953         // See if the clicked simple node is a descendent of the last clicked node
01954         if (!ValidateLastClickInside(pLastClickNode,pClickSimpleNode))
01955             return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELINSIDEFAIL2,CLICKACTION_SELINSIDEFAIL2);
01956 
01957         // If the node we're going to look inside is not compound and it's the top node
01958         // return a failure code but go ahead and re-select it...
01959         if (pLastClickNode && !pLastClickNode->IsCompound() && pLastClickNode == pClickCompoundNode)
01960             return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELINSIDEFAIL,CLICKACTION_SELINSIDEFAIL);
01961 
01962         // Try to perform a select inside
01963         *ppActionNode = NodeRenderableInk::FindInnerCompound(pClickSimpleNode,pLastClickNode);
01964 
01965         // If we still haven't found what we're looking for
01966         // Cycle round to the top again...
01967         return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_SELINSIDE,CLICKACTION_SELINSIDECYCLE);
01968     }
01969 
01970     //--------------------------------------
01971     // OK, so no modifiers are currently down
01972     // Just try to do a normal click action...
01973     return CycleClickAction(ppActionNode,pClickCompoundNode,CLICKACTION_NONE,CLICKACTION_SELNODE);
01974 }
01975 
01976 
01977 
01978 /********************************************************************************************
01979 
01980 >   BOOL SliceTool::ValidateLastClickUnder(NodeRenderableInk* pLastClickNode, DocCoord ClickStart)
01981 
01982     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Phil's SelectorTool code
01983     Created:    11/10/1999
01984     Inputs:     -
01985     Outputs:    pLastClickNode  Pointer to last node selected or NULL
01986     Returns:    TRUE if last click node is still under the pointer (somewhere)
01987                 FALSE otherwise
01988     Purpose:    Validate that the last click node is still under the pointer
01989                 Note! This routine can be slow depending on how deep it has to look in the
01990                 tree for the last selected object.
01991     Errors:     -
01992     SeeAlso:    SliceTool::HandleButtonUp
01993 
01994 ********************************************************************************************/
01995 
01996 BOOL SliceTool::ValidateLastClickUnder(NodeRenderableInk* pLastClickNode, Spread* pStartSpread, DocCoord ClickStart)
01997 {
01998     NodeRenderableInk* pSearchNode = NULL;
01999     do
02000     {
02001         pSearchNode = NodeRenderableInk::FindSimpleAtPoint(pStartSpread,ClickStart,pSearchNode);
02002     }
02003     while (pSearchNode && pLastClickNode!=NodeRenderableInk::FindCompoundFromSimple(pSearchNode,pLastClickNode));
02004 
02005     return (pSearchNode!=NULL);
02006 }
02007 
02008 
02009 
02010 
02011 /********************************************************************************************
02012 
02013 >   BOOL SliceTool::ValidateLastClickInside(NodeRenderableInk* pLastClickNode,NodeRenderableInk* pClickSimpleNode)
02014 
02015     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Phil's SelectorTool code
02016     Created:    11/10/1999
02017     Inputs:     -
02018     Outputs:    pLastClickNode  Pointer to last node selected or NULL
02019     Returns:    TRUE if the simple clicked node is inside the last clicked node somewhere
02020                 FALSE otherwise
02021     Purpose:    Validate that the simple node is inside the last clicked node
02022     Errors:     -
02023     SeeAlso:    SliceTool::HandleButtonUp
02024 
02025 ********************************************************************************************/
02026 
02027 BOOL SliceTool::ValidateLastClickInside(NodeRenderableInk* pLastClickNode,NodeRenderableInk* pClickSimpleNode)
02028 {
02029     Node* pSearchNode = pClickSimpleNode;
02030     while (pSearchNode && pSearchNode!=pLastClickNode)
02031         pSearchNode = pSearchNode->FindParent();
02032     return (pSearchNode!=NULL);
02033 }
02034 
02035 
02036 
02037 /********************************************************************************************
02038 
02039 >   ClickActionCode SliceTool::CycleClickAction(NodeRenderableInk** ppActionNode,
02040                                                 NodeRenderableInk* pClickCompoundNode,
02041                                                 ClickActionCode foundAction,
02042                                                 ClickActionCode cycleAction)
02043 
02044     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Phil's SelectorTool code
02045     Created:    11/10/1999
02046     Inputs:     -
02047     Outputs:    pNodeToSelect   Pointer to pointer to node to select or NULL
02048     Returns:    ActionCode describing what the click should do
02049     Purpose:    Determine what action needs to be taken in response to a click.
02050     Errors:     -
02051     SeeAlso:    SliceTool::DetermineClickAction
02052 
02053 ********************************************************************************************/
02054 
02055 SliceTool::ClickActionCode SliceTool::CycleClickAction( NodeRenderableInk** ppActionNode,
02056                                                         NodeRenderableInk* pClickCompoundNode,
02057                                                         ClickActionCode foundAction,
02058                                                         ClickActionCode cycleAction )
02059 {
02060     // If we have found a node then return the specified action code...
02061     if (*ppActionNode)
02062     {
02063         return foundAction;
02064     }
02065     // Else no suitable node so see whether the click occurred over a compound node
02066     else
02067     {
02068         // If click occurred over a compound node then we can return that
02069         // along with the alternative action code...
02070         if (pClickCompoundNode)
02071         {
02072             *ppActionNode = pClickCompoundNode;
02073             return cycleAction;
02074         }
02075         else
02076         // Else if there wasn't even a compound node we must return the information that
02077         // the click occurred over white space...
02078         {
02079             *ppActionNode = NULL;
02080             return CLICKACTION_SELNONE;
02081         }
02082     }
02083 }
02084 
02085 
02086 
02087 /********************************************************************************************
02088 
02089 >   static void SliceTool::SetStatusText(String_256* pStr)
02090 
02091     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02092     Created:    13/10/94
02093     Inputs:     pStr            pointer to the string to display
02094     Outputs:    -
02095     Returns:    -
02096     Purpose:    Sets the status bar text to the given string.
02097     Errors:     -
02098     SeeAlso:    -
02099 
02100 ********************************************************************************************/
02101 void SliceTool::SetStatusText(String_256* pStr)
02102 {
02103     GetApplication()->UpdateStatusBarText(pStr);
02104 }
02105 
02106 
02107 
02108 /********************************************************************************************
02109 
02110 >   static void SliceTool::SetStatusText(UINT32 nStringID)
02111 
02112     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02113     Created:    12/10/94
02114     Inputs:     nStringID           the numeric identifier of the string resource
02115     Outputs:    -
02116     Returns:    -
02117     Purpose:    Sets the status bar text to the given string.  Alternative to loading the
02118                 string yourself and calling the other SetStatusText function.
02119     Errors:     -
02120     SeeAlso:    -
02121 
02122 ********************************************************************************************/
02123 void SliceTool::SetStatusText(UINT32 nStringID)
02124 {
02125     String_256 str(nStringID);
02126     SetStatusText(&str);
02127 }
02128 
02129 
02130 
02131 /********************************************************************************************
02132 
02133 >   BOOL SliceTool::IsTranslateShortcut(ClickModifiers cmods) const
02134 
02135     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02136     Created:    5/10/94
02137     Inputs:     -
02138     Outputs:    -
02139     Returns:    TRUE if the translate shortcut keys are detected.
02140     Purpose:    Detects whether the current mouse click modifiers denote the translate
02141                 drag operation shortcut.
02142     Errors:     -
02143     SeeAlso:    -
02144 
02145 ********************************************************************************************/
02146 
02147 BOOL SliceTool::IsTranslateShortcut(ClickModifiers cmods) const
02148 {
02149     return cmods.Constrain && cmods.Alternative1;
02150 }
02151 
02152 
02153 
02154 
02155 /********************************************************************************************
02156 
02157 >   BOOL SliceTool::IsClickModified(ClickModifiers cmods) const
02158 
02159     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02160     Created:    29/9/94
02161     Inputs:     -
02162     Outputs:    -
02163     Returns:    TRUE if the current click is "modified".
02164     Purpose:    Tests whether any of the modifiers, eg. Constrain, Adjust etc, apply to
02165                 the current mouse click (as received by the OnClick function).
02166     Errors:     -
02167     SeeAlso:    SliceTool::HandleSingleClick
02168 
02169 ********************************************************************************************/
02170 
02171 BOOL SliceTool::IsClickModified(ClickModifiers cmods) const
02172 {
02173     return cmods.Adjust || cmods.Constrain || cmods.Alternative1 || cmods.Menu;
02174 }
02175 
02176 
02177 
02178 
02179 /********************************************************************************************
02180 
02181 >   BOOL SliceTool::IsSelectUnderClick(ClickModifiers cmods) const
02182 
02183     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02184     Created:    14/10/94
02185     Inputs:     -
02186     Outputs:    -
02187     Returns:    TRUE if the current click signifies the object below the clicked object
02188                 should be selected, FALSE otherwise.
02189     Purpose:    Decides whether the current click is modified to be an "under" click or not
02190     Errors:     -
02191     SeeAlso:    SliceTool::HandleSingleClick
02192 
02193 ********************************************************************************************/
02194 
02195 BOOL SliceTool::IsSelectUnderClick(ClickModifiers cmods) const
02196 {
02197     return (!cmods.Constrain && cmods.Alternative1);
02198 }
02199 
02200 
02201 
02202 
02203 /********************************************************************************************
02204 
02205 >   BOOL SliceTool::IsSelectMemberClick(ClickModifiers cmods) const
02206 
02207     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02208     Created:    24/11/94
02209     Inputs:     -
02210     Outputs:    -
02211     Returns:    TRUE if the last click indicated "select-inside".
02212     Purpose:    Reports whether the current mouse click meant the user wanted to "select-
02213                 inside" or not.
02214     Errors:     -
02215     SeeAlso:    SliceTool::HandleButtonUp
02216 
02217 ********************************************************************************************/
02218 
02219 BOOL SliceTool::IsSelectMemberClick(ClickModifiers cmods) const
02220 {
02221     return (cmods.Constrain && cmods.Alternative1);
02222 }
02223 
02224 
02225 
02226 /********************************************************************************************
02227 
02228 >   BOOL SliceTool::IsSelectLeafClick(ClickModifiers cmods) const
02229 
02230     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02231     Created:    24/11/94
02232     Inputs:     -
02233     Outputs:    -
02234     Returns:    TRUE if the last click indicated "select-leaf".
02235     Purpose:    Reports whether the current mouse click meant the user wanted to "select-
02236                 leaf" or not.
02237     Errors:     -
02238     SeeAlso:    SliceTool::HandleButtonUp
02239 
02240 ********************************************************************************************/
02241 
02242 BOOL SliceTool::IsSelectLeafClick(ClickModifiers cmods) const
02243 {
02244     return (cmods.Constrain && !cmods.Alternative1);
02245 }
02246 
02247 
02248 
02249 /********************************************************************************************
02250 
02251 >   NodeRenderableInk* SliceTool::FindFrom(NodeRenderableInk* pSimpleNode) const
02252 
02253     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
02254     Created:    11/1/95
02255     Inputs:     pSimpleNode         the simple node to begin searching from
02256     Outputs:    -
02257     Returns:    The compound object the simple node is part of, if any, or pSimpleNode
02258                 if it isn't.
02259     Purpose:    Front-end short-hand for NodeRenderableInk::FindCompoundFromSimple
02260     Errors:     -
02261     SeeAlso:    NodeRenderableInk::FindCompoundFromSimple
02262 
02263 ********************************************************************************************/
02264 
02265 NodeRenderableInk* SliceTool::FindFrom(NodeRenderableInk* pSimpleNode) const
02266 {
02267     return NodeRenderableInk::FindCompoundFromSimple(pSimpleNode);
02268 }
02269 
02270 
02271 
02272 /********************************************************************************************
02273 
02274 >   BOOL SliceTool::Append(String_256* pStr, ClickModifiers cmods, UINT32 resID, NodeRenderableInk* pActionNode = NULL)
02275 
02276     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02277     Created:    31/5/95
02278     Inputs:     -
02279     Outputs:    -
02280     Returns:    -
02281     Purpose:    -
02282     Errors:     -
02283 
02284 ********************************************************************************************/
02285 
02286 BOOL SliceTool::Append(String_256* pStr, ClickModifiers cmods,
02287                                             UINT32 SelectID,
02288                                             UINT32 AddID,
02289                                             UINT32 RemoveID,
02290                                             NodeRenderableInk* pActionNode)
02291 {
02292     ERROR2IF(SelectID==0, FALSE, "Asked to append a string resource with a null ID");
02293 
02294     // Append a message to the string, preceding it with a separator if there was something
02295     // already in the string...
02296     if (!pStr->IsEmpty())
02297         *pStr += String_256(_R(IDS_SLICE_SEPARATOR));
02298 
02299     String_256 Message;
02300     UINT32 TemplateID = 0;
02301     Message.Empty();
02302 
02303     if (!cmods.Adjust || pActionNode==NULL)
02304         TemplateID = SelectID;
02305     else
02306     {
02307         if (!pActionNode->IsSelected())
02308             TemplateID = AddID;
02309         else
02310             TemplateID = RemoveID;
02311     }
02312 
02313     if (TemplateID==0)
02314         TemplateID = SelectID;
02315 
02316     if (pActionNode==NULL)
02317         *pStr += String_256(TemplateID);
02318     else
02319     {
02320         Message._MakeMsg( (TCHAR*) String_256(TemplateID), &pActionNode->Describe(FALSE) );
02321         *pStr += Message;
02322     }
02323 
02324     return TRUE;
02325 }
02326 
02327 
02328 
02329 
02330 /********************************************************************************************
02331 
02332 >   BOOL SliceTool::Append(String_256* pStr, UINT32 resID)
02333 
02334     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02335     Created:    31/5/95
02336     Inputs:     -
02337     Outputs:    -
02338     Returns:    -
02339     Purpose:    -
02340     Errors:     -
02341 
02342 ********************************************************************************************/
02343 
02344 BOOL SliceTool::Append(String_256* pStr, UINT32 StringID)
02345 {
02346     // Append a message to the string, preceding it with a separator if there was something
02347     // already in the string...
02348     if (!pStr->IsEmpty())
02349         *pStr += String_256(_R(IDS_SLICE_SEPARATOR));
02350     *pStr += String_256(StringID);
02351 
02352     return TRUE;
02353 }
02354 
02355 
02356 
02357 
02358 /********************************************************************************************
02359 
02360 >   BOOL SliceTool::Append(String_256* pStr, String_256 String)
02361 
02362     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02363     Created:    31/5/95
02364     Inputs:     -
02365     Outputs:    -
02366     Returns:    -
02367     Purpose:    -
02368     Errors:     -
02369 
02370 ********************************************************************************************/
02371 
02372 BOOL SliceTool::Append(String_256* pStr, String_256 String)
02373 {
02374     // Append a message to the string, preceding it with a separator if there was something
02375     // already in the string...
02376     if (!pStr->IsEmpty())
02377         *pStr += String_256(_R(IDS_SLICE_SEPARATOR));
02378     *pStr += String;
02379 
02380     return TRUE;
02381 }
02382 
02383 
02384 
02385 
02386 /********************************************************************************************
02387 
02388 >   BOOL SliceTool::Append(String_256* pStr,
02389                               String_256 SelectTemplate,
02390                               String_256 AddTemplate,
02391                               String_256 RemoveTemplate,
02392                               NodeRenderableInk* pActionNode)
02393 
02394     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02395     Created:    01/06/95
02396     Inputs:     -
02397     Outputs:    -
02398     Returns:    -
02399     Purpose:    -
02400     Errors:     -
02401 
02402 ********************************************************************************************/
02403 
02404 BOOL SliceTool::Append(String_256* pStr,
02405                           ClickModifiers cmods,
02406                           String_256 SelectTemplate,
02407                           String_256 AddTemplate,
02408                           String_256 RemoveTemplate,
02409                           NodeRenderableInk* pActionNode)
02410 {
02411     ERROR2IF(SelectTemplate.IsEmpty(), FALSE, "Asked to Append an empty message");
02412 
02413     // Append a message to the string, preceding it with a separator if there was something
02414     // already in the string...
02415     if (!pStr->IsEmpty())
02416         *pStr += String_256(_R(IDS_SLICE_SEPARATOR));
02417 
02418     String_256 Message;
02419     String_256* pTemplate;
02420     Message.Empty();
02421 
02422     if (!cmods.Adjust || pActionNode==NULL)
02423         pTemplate = &SelectTemplate;
02424     else
02425     {
02426         if (!pActionNode->IsSelected())
02427             pTemplate = &AddTemplate;
02428         else
02429             pTemplate = &RemoveTemplate;
02430     }
02431 
02432     if (pTemplate->IsEmpty())
02433         pTemplate = &SelectTemplate;
02434 
02435     if (pActionNode==NULL)
02436         *pStr += *pTemplate;
02437     else
02438     {
02439         Message._MakeMsg( (TCHAR*) *pTemplate, &pActionNode->Describe(FALSE) );
02440         *pStr += Message;
02441     }
02442 
02443     return TRUE;
02444 }
02445 
02446 
02447 
02448 /********************************************************************************************
02449 
02450 >   virtual void SliceTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
02451 
02452     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02453     Created:    06/07/2000
02454     Inputs:     pSpread     the spread to render blobs for - must == this->SelectionSpread.
02455                 pClipRect   the rect to clip to - used to prevent unnecessary drawing.
02456 
02457     Purpose:    Render the SliceTool's tool blob.
02458                 This consists of a bounding rect identical to that used when dragging the
02459                 selection (dotted, red line), around whatever is currently selected.
02460 
02461     See also:   SLICETOOL_BLOBS #define at the top of this file to turn this off.
02462 
02463 ********************************************************************************************/
02464 void SliceTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
02465 {
02466 #ifdef SLICETOOL_BLOBS
02467     // don't bother if there's no selection spread or if we're asked to render for
02468     // a spread which isn't the selection spread.
02469     if (    SelectionSpread == NULL ||
02470             pSpread != SelectionSpread )
02471     {
02472         return;
02473     }
02474 
02475     // get the selected DocView - if we can't get it, then leave now.
02476     DocView* pView = DocView::GetSelected();
02477     if (pView == NULL)
02478         return;
02479 
02480     // sorry, this isn't the neatest programming.
02481     // The size of the SliceTool blob bitmap is about 10 pixels square.
02482     // The desired distance between blob and selection is 2 pixels.
02483     INT32 BlobGap   = (INT32)(2  * pView->GetScaledPixelWidth().MakeDouble());
02484     INT32 BlobRadius    = (INT32)(10 * pView->GetScaledPixelWidth().MakeDouble());
02485          BlobRadius /= 2;
02486 
02487     // the absolute position of the blob - just left and below the top of the selection rect.
02488     DocCoord dcBlobPos( m_drBlobRect.hix + (BlobGap + BlobRadius),
02489                         m_drBlobRect.hiy - (BlobGap + BlobRadius) );
02490 
02491     // render the blobs for all appropriate render regions.
02492     RenderRegion* pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR);
02493     while (pRegion != NULL)
02494     {
02495         pRegion->SaveContext();
02496 
02497         // draw a bounding box around the selection.
02498         pRegion->SetLineColour(COLOUR_XORSELECT);
02499         pRegion->DrawDragBounds(&m_drBlobRect);
02500 
02501         // draw a little named-set bitmap to denote what sets are selected.
02502         switch (m_SetSelectionState)
02503         {
02504         case FullSetsSelected:
02505             pRegion->DrawBitmapBlob(dcBlobPos, _R(IDBMP_SLICE_SELALL));
02506             break;
02507 
02508         case HalfSetsSelected:
02509             pRegion->DrawBitmapBlob(dcBlobPos, _R(IDBMP_SLICE_SELSOME));
02510             break;
02511 
02512         case NoSetsSelected:
02513             pRegion->DrawBitmapBlob(dcBlobPos, _R(IDBMP_SLICE_SELNONE));
02514             break;
02515 
02516         default:
02517             ERROR3("SliceTool::RenderToolBlobs; unrecognised set-selection state!");
02518             break;
02519         }
02520 
02521         pRegion->RestoreContext();
02522 
02523         // Go on to the next render region, if any.
02524         pRegion = DocView::GetNextOnTop(pClipRect);
02525     }
02526 #endif
02527 }
02528 
02529 
02530 
02531 /********************************************************************************************
02532 >   BOOL SliceTool::UpdateSelectionInfo()
02533 
02534     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from JustinF's SelectorTool::UpdateSelectionInfo() code.
02535     Created:    14/10/1999
02536     Inputs:     
02537     Outputs:    
02538     Returns:    TRUE if there is a current selection,
02539                 FALSE if no object is selected.
02540     Purpose:    Updates the slice tool's record of the spread and bounding rectangle
02541                 of the current selection.
02542     Errors:     
02543     SeeAlso:    SliceTool::SelectChange
02544 ********************************************************************************************/
02545 BOOL SliceTool::UpdateSelectionInfo()
02546 {
02547     // Reset all our infomation about the selection to "no selection".
02548     SelectionSpread = NULL;
02549     SelectionRect.MakeEmpty();
02550     m_drBlobRect.MakeEmpty();
02551 
02552     // we want to take controller nodes into account in this selection.
02553     RangeControl rc = SelectRange->GetRangeControlFlags();
02554     RangeControl rcOld = rc;
02555     rc.PromoteToParent = TRUE;
02556     SelectRange->Range::SetRangeControl(rc);
02557 
02558     // Go find the first node in the selection, so that we can find out about its spread
02559     Node* pFirstNode = SelectRange->FindFirst();
02560     if (pFirstNode != NULL)
02561     {
02562         // Find the spread that the selection lives on, if any, and its bounds.
02563         SelectionSpread = pFirstNode->FindParentSpread();
02564         if (SelectionSpread != NULL)
02565         {
02566             // Update the bounding rectangle of the selection.
02567             SelectionRect   = SelectRange->GetBoundingRect();
02568 
02569             // update our tool-blob rendering info.
02570             m_drBlobRect    = SelectRange->GetBoundingRectForEorDragging();
02571 
02572             // call the name-gallery to update named set info.
02573             NameGallery* pNameGallery = NameGallery::Instance();
02574             if (pNameGallery)
02575                 pNameGallery->FastUpdateNamedSetSizes();
02576 
02577             // decide what set-selection info we'll be displaying.
02578             INT32 nSetSelection = SliceHelper::DoesSelectionOnlyContainCompleteSets();
02579             m_SetSelectionState =   (nSetSelection == 1) ?  FullSetsSelected :
02580                                     (nSetSelection == 0) ?  HalfSetsSelected :
02581                                                             NoSetsSelected;
02582 
02583             if (SelectionRect.IsEmpty())
02584                 SelectionRect.Inflate(1);
02585         }
02586     }
02587 
02588     SelectRange->Range::SetRangeControl(rcOld);
02589 
02590     // Return TRUE if there is a selection.
02591     return SelectionSpread != NULL;
02592 }
02593 
02594 
02595 
02596 /********************************************************************************************
02597 
02598 >   virtual BOOL SliceTool::DragFinished(DragEndType det)
02599 
02600     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from JustinF's SelectorTool::DragFinished
02601     Created:    14/10/1999
02602     Inputs:     det     a code indicating how the drag was ended,
02603                         eg. did the user hit ESCAPE and cancel it?
02604     Outputs:    
02605     Returns:    TRUE if the drag operation should be allowed to complete,
02606                 FALSE to signal that the drag should be cancelled.
02607     Purpose:    Called by TransOperation when a drag is finished.
02608                 Allows the SliceTool to reset its cursors and update
02609                 its current selection information.
02610     Errors:     
02611     SeeAlso:    
02612 
02613 ********************************************************************************************/
02614 BOOL SliceTool::DragFinished(DragEndType det)
02615 {
02616     // update the current cursor.
02617     SetKeyDownCursor(ClickModifiers::GetClickModifiers());
02618 
02619     // we always leave cancelling the drag to a capricious user.
02620     return TRUE;
02621 }
02622 
02623 
02624 
02625 //----------------------------------------------
02626 //----------------------------------------------
02627 //----------------------------------------------
02628 //----------------------------------------------
02629 
02630 
02631 
02632 /********************************************************************************************
02633 
02634 >   MsgResult SliceInfoBarOp::Message(Msg* Message) 
02635 
02636     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02637     Created:    3/10/94
02638     Inputs:     Message = The message to handle
02639     Outputs:    -
02640     Returns:    -
02641     Purpose:    Slice info bar dialog message handler
02642     Errors:     -
02643     SeeAlso:    -
02644 
02645 ********************************************************************************************/
02646 
02647 MsgResult SliceInfoBarOp::Message(Msg* Message) 
02648 {
02649     if (IS_OUR_DIALOG_MSG(Message))
02650     {
02651         DialogMsg* Msg = (DialogMsg*)Message;
02652 
02653         switch(Msg->DlgMsg)
02654         {
02655         case DIM_CANCEL:
02656             m_InfoBarCreated = FALSE;
02657             Close(); // close the dlg
02658             break;
02659 
02660         case DIM_CREATE:
02661             m_InfoBarCreated = TRUE;
02662             // Initialise the infobar controls here
02663             // This is sent when you create the infobar in your tool startup code
02664             SetStringGadgetValue(_R(IDC_ST_STATE), _R(IDS_ROLLOVER_DEFAULT), FALSE, 0);
02665             SetStringGadgetValue(_R(IDC_ST_STATE), _R(IDS_ROLLOVER_MOUSE), FALSE, 1);
02666             SetStringGadgetValue(_R(IDC_ST_STATE), _R(IDS_ROLLOVER_CLICKED), FALSE, 2);
02667             SetStringGadgetValue(_R(IDC_ST_STATE), _R(IDS_ROLLOVER_SELECTED), FALSE, 3);
02668             SetStringGadgetValue(_R(IDC_ST_STATE), _R(IDS_ROLLOVER_ALL), FALSE, 4);
02669             SetStringGadgetValue(_R(IDC_ST_STATE), _R(IDS_ROLLOVER_NONE), TRUE, 5);
02670             UpdateCurrentStateGadget();
02671 
02672             // set up the bar name bit and grey the buttons that are selection dependant
02673             // check again for the new bar name
02674             m_TopVisibleState = -1;
02675 
02676             // set the bar name
02677             //g_BarName.MakeMsg(_R(IDS_BARNAME), 1);
02678             OnSelectionChanged();
02679             break;
02680 
02681             //NB: This means someone has altered the combo box. It doesn't mean
02682             //the selection has changed!
02683         case DIM_SELECTION_CHANGED: 
02684             if(Msg->GadgetID == _R(IDC_ST_STATE))
02685             {
02686                 OpParam Param(GetSelectedValueIndex(_R(IDC_ST_STATE)), 0);
02687                 OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_SHOWSTATE); 
02688                 if (pOpDesc != NULL)
02689                     pOpDesc->Invoke((OpParam*)&Param);
02690                 else
02691                 {
02692                     ERROR3("Couldn't find OPTOKEN_SHOWSTATE op descriptor");
02693                 }
02694                 UpdateCurrentStateGadget();
02695             }
02696             break;
02697 
02698             case DIM_LFT_BN_CLICKED:
02699                 switch(Msg->GadgetID)
02700                 {
02701                 case _R(IDC_ST_EXPORT):
02702                     // run the Op that does the image slicing / rollover creation
02703                     // from the back end
02704                     {
02705                         // matt-24/08/2000
02706                         // Should check first that buttons exist in this document - same code as
02707                         // used in Edit section below...
02708 
02709                         BOOL ok = TRUE;
02710                         // nothing selected then test that the bar we think we are editing exists
02711                         // warn if it doesn't
02712                         if (!m_EditingBar)
02713                         {
02714                             String_256 defaultLayerName;
02715                             defaultLayerName.Load(_R(IDS_ROLLOVER_DEFAULT));
02716                             Layer * pDef = SliceHelper::FindLayerCalled(defaultLayerName);
02717 
02718                             if (pDef == NULL || !SliceHelper::BarNameExists(pDef, g_BarName))
02719                             {
02720                                 ok = FALSE;
02721                                 InformWarning(_R(IDS_BAR_DOESNT_EXIST));
02722                             }
02723                         }
02724 
02725                         if (ok)
02726                         {
02727                             OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_IMAGESLICE); 
02728                             if (pOpDesc != NULL)
02729                                 pOpDesc->Invoke();//(OpParam*)&Param);
02730                             else
02731                             {
02732                                 ERROR3("Couldn't find OPTOKEN_IMAGESLICE op descriptor");
02733                             }
02734                         }
02735                     }
02736                     break;
02737 
02738                 case _R(IDC_NEW_BAR):
02739                     // Brings up the edit/create bar dlg
02740                     {
02741                         // find a new bar name and put it in g_BarName
02742                         String_256 DefLayerName;
02743                         DefLayerName.Load(_R(IDS_ROLLOVER_DEFAULT));
02744                         Layer * pDef = SliceHelper::FindLayerCalled(DefLayerName);
02745                         INT32 UnusedBarID = 0;
02746                         String_256 NewBarName = "";
02747 
02748                         do
02749                         {
02750                             UnusedBarID++;
02751                             NewBarName.MakeMsg(_R(IDS_BARNAME), UnusedBarID);
02752                         }
02753                         while (pDef && SliceHelper::BarNameExists(pDef, NewBarName));
02754 
02755                         // run the op to create a new bar
02756                         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_BARCREATIONDLG); 
02757                         if (pOpDesc != NULL)
02758                         {
02759                             OpParamBarCreationDlg Param(FALSE /*Creating*/, NewBarName, SliceHelper::CountButtonsInBar(NewBarName));
02760                             pOpDesc->Invoke((OpParam*)&Param);
02761                         }
02762                         else
02763                         {
02764                             ERROR3("Couldn't find OPTOKEN_BARCREATIONDLG op descriptor");
02765                         }
02766                     }
02767                     break;
02768                 
02769                 case _R(IDC_EDIT_BAR):
02770                     // Brings up the edit/create bar dlg
02771                     {
02772                         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_BARCREATIONDLG); 
02773                         if (pOpDesc != NULL)
02774                         {
02775                             BOOL ok = TRUE;
02776                             // nothing selected then test that the bar we think we are editing exists
02777                             // warn if it doesn't
02778                             if (!m_EditingBar)
02779                             {
02780                                 String_256 DefLayerName;
02781                                 DefLayerName.Load(_R(IDS_ROLLOVER_DEFAULT));
02782                                 Layer * pDef = SliceHelper::FindLayerCalled(DefLayerName);
02783 
02784                                 if (pDef == NULL || !SliceHelper::BarNameExists(pDef, g_BarName))
02785                                 {
02786                                     ok = FALSE;
02787                                     InformWarning(_R(IDS_BAR_DOESNT_EXIST));
02788                                 }
02789                             }
02790 
02791                             if (ok)
02792                             {
02793                                 OpParamBarCreationDlg Param(TRUE /*editing*/, g_BarName, SliceHelper::CountButtonsInBar(g_BarName));
02794                                 pOpDesc->Invoke((OpParam*)&Param);
02795                             }
02796                         }
02797                         else
02798                         {
02799                             ERROR3("Couldn't find OPTOKEN_BARCREATIONDLG op descriptor");
02800                         }
02801                     }
02802                     break;
02803                 
02804                 case _R(IDC_STATES):
02805                     // Brings up the edit/create bar dlg
02806                     {
02807                         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_BARSTATESDLG); 
02808                         if (pOpDesc != NULL)
02809                         {
02810                             BOOL ok = TRUE;
02811                             // nothing selected then test that the bar we think we are editing exists
02812                             // warn if it doesn't
02813                             if (!m_EditingBar)
02814                             {
02815                                 String_256 DefLayerName;
02816                                 DefLayerName.Load(_R(IDS_ROLLOVER_DEFAULT));
02817                                 Layer * pDef = SliceHelper::FindLayerCalled(DefLayerName);
02818 
02819                                 if (pDef == NULL || !SliceHelper::BarNameExists(pDef, g_BarName))
02820                                 {
02821                                     ok = FALSE;
02822                                     InformWarning(_R(IDS_BAR_DOESNT_EXIST));
02823                                 }
02824                             }
02825 
02826                             if (ok)
02827                             {
02828                                 OpParamBarStatesDlg ParamBarStatesDlg(g_BarName);
02829                                 pOpDesc->Invoke(&ParamBarStatesDlg);
02830                             }
02831                         }
02832                     }
02833                     break;
02834                 
02835                 case _R(IDC_REDEFINE_STATE):
02836                     // Brings up the edit/create bar dlg
02837                     {
02838                         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_BARREDEFINESTATEDLG); 
02839                         if (pOpDesc != NULL)
02840                         {
02841                             FindCurrentState();
02842                             BOOL ok = TRUE;
02843                             // nothing selected then test that the bar we think we are editing exists
02844                             // warn if it doesn't
02845                             if (!m_EditingBar)
02846                             {
02847                                 String_256 DefLayerName;
02848                                 DefLayerName.Load(_R(IDS_ROLLOVER_DEFAULT));
02849                                 Layer * pDef = SliceHelper::FindLayerCalled(DefLayerName);
02850 
02851                                 if (pDef == NULL || !SliceHelper::BarNameExists(pDef, g_BarName))
02852                                 {
02853                                     ok = FALSE;
02854                                     InformWarning(_R(IDS_BAR_DOESNT_EXIST));
02855                                 }
02856                             }
02857 
02858                             if (ok)
02859                             {
02860                                 OpParamBarRedefineStateDlg ParamBarRedefineStateDlg(g_BarName, m_TopVisibleState);
02861                                 pOpDesc->Invoke(&ParamBarRedefineStateDlg);
02862                             }
02863                         }
02864                     }
02865                     break;
02866 
02867                 case _R(IDC_LIVE): // is the stretching live or not???
02868                     {
02869                         NodeBarProperty* pNodeBarProperty = (NodeBarProperty*) Document::GetCurrent()->GetSetSentinel()->FindBarProperty();
02870                         INT32 BarNumber = SliceHelper::GetBarNumberFromBarName(g_BarName);
02871 
02872                         if (pNodeBarProperty  && BarNumber < pNodeBarProperty->HowMany())
02873                         {
02874                             BarDataType NewBarData = pNodeBarProperty->Bar(BarNumber);
02875                             NewBarData.IsLive = !NewBarData.IsLive;
02876 
02877                             NameGallery * pNameGallery = NameGallery::Instance();
02878                             if (pNameGallery)
02879                                 pNameGallery->FastUpdateNamedSetSizes();
02880 
02881                             pNodeBarProperty->MakeChange(BarNumber, NewBarData);
02882                         }
02883                     }
02884 
02885                     break;
02886                 
02887                 }
02888             break;
02889         }
02890 
02891     }
02892     else if (m_InfoBarCreated && MESSAGE_IS_A(Message, SpreadMsg))
02893     {
02894         SpreadMsg*  pSpreadMsg = (SpreadMsg*) Message;
02895 
02896         switch (pSpreadMsg->Reason)
02897         {
02898             case SpreadMsg::LAYERCHANGES:
02899                 UpdateCurrentStateGadget();
02900                 break;
02901         }
02902     }
02903     else if (m_InfoBarCreated && MESSAGE_IS_A(Message, LayerMsg))
02904     {
02905         LayerMsg *TheMsg = (LayerMsg *) Message;
02906 
02907         if (TheMsg->Reason == LayerMsg::LAYER_VISIBILITY_CHANGED)
02908             {
02909                 UpdateCurrentStateGadget();
02910             }
02911     }
02912 
02913     // Karim MacDonald - 20/10/1999
02914     // for whenever someone announces that they have changed the selection.
02915     // FYI, this SelChangingMsg is also sent when you do (SelRange*)->Update(TRUE).
02916     else if (m_InfoBarCreated && MESSAGE_IS_A(Message, SelChangingMsg))
02917     {
02918         SelChangingMsg* pSelChange = (SelChangingMsg*)Message;
02919 
02920         if (pSelChange->State == SelChangingMsg::SelectionState::SELECTIONCHANGED ||
02921             pSelChange->State == SelChangingMsg::SelectionState::NONCOLOURATTCHANGED)
02922         {
02923             OnSelectionChanged();
02924         }
02925     }
02926 
02927     // Karim 06/07/2000 - the slice tool needs to know if the view changes.
02928     else if (MESSAGE_IS_A(Message, DocViewMsg))
02929     {
02930         if (pSliceTool != NULL)
02931             pSliceTool->ViewChanged( *((DocViewMsg*)Message) );
02932     }
02933 
02934     // If we've changed to a different document then get rid of the dlg.
02935     else if (MESSAGE_IS_A(Message, DocChangingMsg))
02936     {
02937         DocChangingMsg* TheMsg = (DocChangingMsg*) Message;
02938         if (TheMsg->State == DocChangingMsg::BORN || TheMsg->State == DocChangingMsg::SELCHANGED)
02939         {
02940             // this will be a new set of named sets!
02941             NameGallery * pNameGallery = NameGallery::Instance();
02942             if (pNameGallery)
02943                 pNameGallery->FastUpdateNamedSetSizes();
02944         }
02945     }
02946 
02947     // Pass the message on to the immediate Slice class
02948     return (InformationBarOp::Message(Message));
02949 }    
02950 
02951 
02952 /********************************************************************************************
02953 
02954 >   static INT32 SliceInfoBarOp::FindCurrentState()
02955 
02956     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02957     Created:    18/8/99
02958     Returns:    An ID as to what layers exist and are visible
02959                 from the specailly named rollover states.
02960 
02961                 0 = default
02962                 1 = mouse
02963                 2 = clicked
02964                 3 = selected
02965                 4 = all the above
02966                 5 = doesn't exactly match any of the above
02967 
02968                 NB. matches the order of the combo box options
02969 
02970     Purpose:    Scans the layers looking at which named states are visible
02971 
02972 ********************************************************************************************/
02973 
02974 INT32 SliceInfoBarOp::FindCurrentState()
02975 {
02976     // find a spread?
02977     Spread* pSelSpread = Document::GetSelectedSpread();
02978     if (pSelSpread == NULL)
02979         return 6;
02980 
02981     // init the layer pointers
02982     Layer * pLayer = pSelSpread->FindFirstLayer();
02983 
02984     BOOL ActiveState[4];
02985     String_32 StateName[4];
02986 
02987     StateName[DEFAULT].Load(_R(IDS_ROLLOVER_DEFAULT)); // = "Default";
02988     StateName[MOUSE].Load(_R(IDS_ROLLOVER_MOUSE)); // = "Mouse";
02989     StateName[CLICKED].Load(_R(IDS_ROLLOVER_CLICKED)); // = "Clicked";
02990     StateName[SELECTED].Load(_R(IDS_ROLLOVER_SELECTED)); // = "Selected";
02991 
02992     INT32 i = 0;
02993 
02994     // init the bools
02995     for (i = 0; i < 4 ; i++)
02996         ActiveState[i] = FALSE;
02997 
02998     m_TopVisibleState = -1;
02999 
03000     while (pLayer)
03001     {
03002         for (i = 0; i < 4 ; i++)
03003         {
03004             if (pLayer->GetLayerID().CompareTo(StateName[i]) == 0)
03005             {
03006                 ActiveState[i] = pLayer->IsVisible();
03007                 if (ActiveState[i])
03008                 {
03009                     if (m_TopVisibleState == -1 || m_TopVisibleState > i)
03010                         m_TopVisibleState = i;
03011                 }
03012             }
03013         }
03014 
03015         pLayer = pLayer->FindNextLayer();
03016     }
03017 
03018 
03019     if (ActiveState[0] && ActiveState[1] && (ActiveState[2] || !INCLUDE_CLICKED_STATE ) && ActiveState[3])
03020         return 4; // all states
03021 
03022     if (ActiveState[0] && !ActiveState[1] && !ActiveState[2] && !ActiveState[3])
03023         return 0; // default state
03024 
03025     if (!ActiveState[0] && ActiveState[1] && !ActiveState[2] && !ActiveState[3])
03026         return 1; // mouse state
03027 
03028     if (!ActiveState[0] && !ActiveState[1] && ActiveState[2] && !ActiveState[3])
03029         return 2; // clicked state
03030 
03031     if (!ActiveState[0] && !ActiveState[1] && !ActiveState[2] && ActiveState[3])
03032         return 3; // selected state
03033 
03034     if (!ActiveState[0] && !ActiveState[1] && !ActiveState[2] && !ActiveState[3])
03035         return 5; // no states
03036 
03037     return 6; // another state
03038 }
03039 
03040 /********************************************************************************************
03041 
03042 >   void SliceInfoBarOp::OnSelectionChanged()
03043 
03044     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03045     Created:    4/10/99
03046     Returns:    -
03047     Purpose:    Should be called everytime something changes the selection,
03048                 so that the correct buttons are highlit and the correct
03049                 text is placed in the barname edit field.
03050 ********************************************************************************************/
03051 void SliceInfoBarOp::OnSelectionChanged()
03052 {
03053     // find out what the bar id should be - the next one to be used or the selected one
03054     Spread* pSpread = Document::GetSelectedSpread();
03055 
03056     // is it part of a animation?
03057     // test the current frame if it is a frame or a layer
03058     if (pSpread != NULL)
03059     {
03060         Layer * ActiveLayer = pSpread->FindActiveLayer();
03061         if (ActiveLayer && ActiveLayer->IsFrame())
03062         {
03063             // can't do lots of these things if we are in an animation
03064             EnableGadget(_R(IDC_NEW_BAR), FALSE);
03065             EnableGadget(_R(IDC_EDIT_BAR), FALSE);
03066             EnableGadget(_R(IDC_STATES), FALSE);
03067             EnableGadget(_R(IDC_LIVE), FALSE);
03068             EnableGadget(_R(IDC_ST_EXPORT), FALSE);
03069             EnableGadget(_R(IDC_ST_STATE), FALSE);
03070         }
03071         else
03072         {
03073             // get the selection
03074             Range Sel(*(GetApplication()->FindSelection()));
03075 
03076             // set the range flags so it includes shadow and bevel manager nodes
03077             RangeControl rg = Sel.GetRangeControlFlags();
03078             rg.PromoteToParent = TRUE;
03079             rg.IgnoreInvisibleLayers = TRUE;
03080             Sel.Range::SetRangeControl(rg);
03081 
03082             m_EditingBar = FALSE;
03083             g_BarName = GetNextUnusedBarName(&Sel, &m_EditingBar);
03084             INT32 BarNo = SliceHelper::GetBarNumberFromBarName(g_BarName);
03085             NodeBarProperty* pNodeBarProperty = (NodeBarProperty*) Document::GetCurrent()->GetSetSentinel()->FindBarProperty();
03086             BOOL IsBar = pNodeBarProperty && BarNo < pNodeBarProperty->HowMany();
03087 
03088             SetStringGadgetValue(_R(IDC_ST_BARNAME), &g_BarName);
03089 
03090             EnableGadget(_R(IDC_NEW_BAR), !m_EditingBar && Sel.Count() >= 1);
03091             EnableGadget(_R(IDC_EDIT_BAR), TRUE /*m_EditingBar || BarNo > 0*/);
03092             EnableGadget(_R(IDC_STATES), TRUE /*m_EditingBar || BarNo > 0*/);
03093             EnableGadget(_R(IDC_LIVE), IsBar);
03094             EnableGadget(_R(IDC_ST_EXPORT), TRUE);
03095             EnableGadget(_R(IDC_ST_STATE), TRUE);
03096 
03097             // set the live tick
03098             SetLongGadgetValue(_R(IDC_LIVE), IsBar && pNodeBarProperty->Bar(BarNo).IsLive);
03099         }
03100     }
03101 
03102     // Karim - 19/10/1999
03103     // inform the SliceTool that the selection has changed.
03104     if (pSliceTool != NULL)
03105         pSliceTool->SelectionHasChanged();
03106 }
03107 
03108 
03109 /********************************************************************************************
03110 
03111 >   String_256 SliceInfoBarOp::GetNextUnusedBarName(Range * pSel, BOOL * pFromSel)
03112 
03113     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03114     Created:    4/10/99
03115     Returns:    The text that is the bar name to put in the bar name field
03116     Params:     pSel    -   A pointer to the selection range
03117                 pFromSel-   Fills in this bool that says if the name
03118                             was taken from the selection or not.
03119     Purpose:    It checks the selection to see if it is part of a bar.
03120                 If it was it returns that name.
03121                 If not it returns the next good name to use for a bar,
03122                 that isn't currently in use.
03123 ********************************************************************************************/
03124 String_256 SliceInfoBarOp::GetNextUnusedBarName(Range * pSel, BOOL * pFromSel)
03125 {
03126     String_256 NewBarName = "";
03127 
03128     *pFromSel = FALSE;
03129 
03130     Spread* pSpread = Document::GetSelectedSpread();
03131     if (pSpread == NULL)
03132         return g_BarName;
03133 
03134     if (pSel)
03135     {
03136         Node * pCurrent = pSel->FindFirst();
03137         // for each node in the selection
03138         while (pCurrent)
03139         {
03140             // does the selection contain a named bar ?
03141             // if it does we will edit this and return it here
03142             if (ScanFromNodeForBarMembers(pCurrent, NewBarName))
03143             {
03144                 *pFromSel = TRUE;
03145                 return NewBarName;
03146             }
03147 
03148             pCurrent = pSel->FindNext(pCurrent);
03149         }
03150 
03151         // no actual bar in selection so does the selection cross a bar?
03152         SliceHelper::BarNameInRect(pSel->GetBoundingRect(), &g_BarName);
03153     }
03154 
03155     return g_BarName; // keep what we had before
03156 }
03157 
03158 
03159 /********************************************************************************************
03160 
03161 >   BOOL SliceInfoBarOp::ScanFromNodeForBarMembers(Node * pNode, String_256 &BarName, String_256 * pButtonName)
03162 
03163     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03164     Created:    4/10/99
03165     Returns:    The text that is the bar name to put in the bar name field
03166     Params:     pNode       -   The node to check from
03167                 BarName     -   Fills in this string with the name of the bar found.
03168                 pButtonName -   If provided fills in the button name found in a bar.
03169     Purpose:    Checks from a given node down looking for the first template attrib (set name)
03170                 Once found it returns TRUE and fills in the bar name string from the question
03171                 of the template attrib and if a ptr to a string was passed for the button name
03172                 it fills this in too.
03173                 NB. This function is RECURSIVE
03174 ********************************************************************************************/
03175 BOOL SliceInfoBarOp::ScanFromNodeForBarMembers(Node * pNode, String_256 &BarName, String_256 * pButtonName)
03176 {
03177     if (!BarName.IsEmpty())
03178         return TRUE;
03179 
03180     if (pNode->IsAnAttribute())
03181     {
03182         if (pNode->IsKindOf(CC_RUNTIME_CLASS(TemplateAttribute)))
03183             {
03184                 BarName = SliceHelper::GetBarName((TemplateAttribute *)pNode);
03185 
03186                 // found a bar name? then set the button name too
03187                 if (!BarName.IsEmpty() && pButtonName)
03188                     *pButtonName = ((TemplateAttribute *)pNode)->GetParam();
03189             }
03190     }
03191     else // find anything else interesting?
03192     {
03193         Node * pChildNode = pNode->FindFirstChild();
03194 
03195         while (pChildNode)
03196         {
03197             // ***recursive call***
03198             if (ScanFromNodeForBarMembers(pChildNode, BarName, pButtonName))
03199                 return TRUE;
03200             pChildNode = pChildNode->FindNext();
03201         }
03202     }
03203 
03204     return FALSE;
03205 }
03206 
03207 
03208 
03209 /********************************************************************************************
03210 
03211 >   void SliceInfoBarOp::DealWithSingleClick(DocCoord PointerPos, 
03212                     ClickModifiers Mods, Spread * pSpread);
03213 
03214     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com> from DavidMc
03215     Created:    4/10/99 &   16/6/99
03216     Inputs:     
03217     Outputs:    -
03218     Returns:    -
03219     Purpose:    Deals with a single click situation (i.e. no drag occurred)
03220     
03221 ********************************************************************************************/
03222 /*
03223  *  Commented out by Karim MacDonald, 14/10/1999.
03224  *  This function has been replaced by HandleClick() and the other
03225  *  Handle...() functions.
03226 
03227 void SliceInfoBarOp::DealWithSingleClick(DocCoord PointerPos, ClickModifiers Mods, 
03228                                             Spread * pSpread)
03229 {
03230     // first, find out if the click is over an object
03231     NodeRenderableInk * pInk = NodeRenderableInk::FindSimpleAtPoint(pSpread, PointerPos);
03232     NodeRenderableInk * pCompound = NodeRenderableInk::FindCompoundFromSimple(pInk);
03233 
03234     if (pCompound)
03235     {
03236         pInk = pCompound;
03237     }
03238 
03239     // has the shift key been pressed ?
03240     BOOL bShift = Mods.Adjust;
03241     BOOL bRedraw = FALSE;
03242     SelRange Sel(*(GetApplication()->FindSelection()));
03243 
03244     RangeControl rg = Sel.GetRangeControlFlags();
03245     rg.PromoteToParent = TRUE;
03246     Sel.Range::SetRangeControl(rg);
03247 
03248     DocRect RedrawRect = Sel.GetBlobBoundingRect();
03249 
03250     Document * pDoc = Document::GetCurrent();
03251 
03252     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
03253 
03254     BlobStyle bs1(TRUE);
03255     BlobStyle bs2(FALSE);
03256     
03257     if (pDoc && BlobMgr)
03258     {
03259         bs1 = BlobMgr->GetCurrentInterest();
03260         BlobMgr->ToolInterest(bs2);
03261         pDoc->ForceRedraw(Document::GetSelectedSpread(), RedrawRect);       
03262     }
03263 
03264     if (pInk)
03265     {
03266         String_256 SetName;
03267         SetName.Empty();
03268 
03269         BOOL WasSelected = pInk->IsSelected();
03270 
03271         // change the selection
03272         if (!bShift)
03273         {
03274             NodeRenderableInk::DeselectAll(TRUE, FALSE);
03275         }
03276 
03277         // set name shows the last set found
03278         // find the top shape on this layer
03279         Node *pParent = pInk->FindParent();
03280         Node *pNode = pInk;
03281         while (!pParent->IsLayer())
03282         {
03283             pNode = pParent;
03284             pParent = pNode->FindParent();
03285         }
03286         SliceHelper::SelectAllSetsOfThisNode(pNode, SetName, bShift);
03287         
03288         // the clicked chappie isn't in a set just select him
03289         if (SetName.IsEmpty())
03290         {
03291             if (bShift)
03292                 pInk->SetSelected(!WasSelected);
03293             else
03294                 pInk->SetSelected(TRUE);
03295         }
03296 
03297         GetApplication()->UpdateSelection();
03298         
03299         OnSelectionChanged();
03300     }
03301     else
03302     {
03303         // deselect everything
03304         NodeRenderableInk::DeselectAll(TRUE, FALSE);
03305         GetApplication()->UpdateSelection();
03306 
03307         OnSelectionChanged();
03308     }
03309 
03310     // force a redraw on the area
03311     if (pDoc && BlobMgr)
03312     {
03313         BlobMgr->ToolInterest(bs1);
03314         pDoc->ForceRedraw(Document::GetSelectedSpread(), RedrawRect);       
03315     }
03316 }*/
03317 
03318 
03319 
03320 //-----------------------------------------------
03321 //-----------------------------------------------
03322 //-----------------------------------------------
03323 //-----------------------------------------------
03324 
03325 
03326 
03327 /********************************************************************************************
03328 
03329 >   void OpSliceDragBox::StartDragBox(  Spread* pSpread,
03330                                         DocCoord Anchor,
03331                                         ClickModifiers ClickMods)
03332     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpSelectorDragBox code.
03333     Created:    11/10/1999
03334     Inputs:     Anchor - The position of the mouse at the start of the Drag
03335     Purpose:    This is called when a Drag operation has been detected
03336 
03337 ********************************************************************************************/
03338 void OpSliceDragBox::StartDragBox(Spread* pSpread, DocCoord Anchor, ClickModifiers ClickMods)
03339 {
03340     // We had better take a note of the starting point of the drag
03341     StartSpread = pSpread;
03342     StartPoint = Anchor;
03343     LastMousePosition = Anchor;
03344 
03345     // Put some helpful text in the status bar.
03346     SliceTool::SetStatusText(_R(IDS_SLICE_DRAGBOXTEXT));
03347 
03348     // And tell the Dragging system that we need drags to happen
03349     StartDrag(DRAGTYPE_AUTOSCROLL);
03350 }
03351 
03352 
03353 
03354 /********************************************************************************************
03355 
03356 >   void OpSliceDragBox::DragFinished(  DocCoord PointerPos,
03357                                         ClickModifiers ClickMods,
03358                                         BOOL Success, BOOL bSolidDrag)
03359 
03360     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpSelectorDragBox code.
03361     Created:    11/10/1999
03362     Inputs:     PointerPos - The position of the mouse at the end of the drag
03363                 ClickMods - the key modifiers being pressed
03364                 Success - TRUE if the drag was terminated properly, FALSE if it
03365                 was ended with the escape key being pressed
03366     Purpose:    This is called when a drag operation finishes. This removes the EORed drag
03367                 rect from the screen and then selects all the objects that were in it.
03368                 It then goes through these objects, selecting all other objects sharing a name
03369                 with any of them.
03370     SeeAlso:    ClickModifiers
03371 ********************************************************************************************/
03372 void OpSliceDragBox::DragFinished(  DocCoord PointerPos,
03373                                     ClickModifiers ClickMods,
03374                                     Spread* pSpread, BOOL Success, BOOL bSolidDrag)
03375 {
03376     // Build the rectangle of the drag box at the end of the drag
03377     DocRect BoundingRect(MIN(StartPoint.x, LastMousePosition.x),
03378                          MIN(StartPoint.y, LastMousePosition.y),
03379                          MAX(StartPoint.x, LastMousePosition.x),
03380                          MAX(StartPoint.y, LastMousePosition.y));
03381 
03382     // First Rub out the old box
03383     RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag);
03384 
03385     // Put the hourglass up
03386     BeginSlowJob();
03387 
03388     // we need this info in scope here, although it is used just below.
03389     SelRange* pSel = GetApplication()->FindSelection();
03390     RangeControl rcOld = pSel->GetRangeControlFlags();
03391 
03392     // Go and try and select a few things
03393     if (Success)
03394     {
03395         // If we didn't drag with the right button then deselect everything prior to selecting
03396         // the "lasso-ed" objects.
03397         if (!ClickMods.Adjust)
03398             NodeRenderableInk::DeselectAll(TRUE, FALSE);
03399 
03400         // Select the objects in the BoundingRect
03401         RangeControl rg = rcOld;
03402         rg.IgnoreInvisibleLayers = TRUE;
03403         rcOld = rg;
03404         rg.PromoteToParent = TRUE;
03405         pSel->Range::SetRangeControl(rg);
03406         SliceHelper::SelectAllSetsInRect(BoundingRect, pSpread,
03407                                            SliceHelper::SelStateAction::SET);
03408 
03409         // End the Drag
03410         if (!EndDrag())
03411             FailAndExecute();
03412     }
03413     else
03414     {
03415         // Set up the flags that say it all went wrong.
03416         EndDrag();
03417         FailAndExecute();
03418     }
03419 
03420     // return the app's selection range flags to their original state.
03421     pSel->Range::SetRangeControl(rcOld);
03422 
03423     // Final end of the operation.
03424     End();
03425 }
03426 
03427 
03428 
03429 /********************************************************************************************
03430 >   BOOL OpSliceDragBox::Declare()
03431 
03432     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpSelectorDragBox code.
03433     Created:    11/10/1999
03434     Returns:    TRUE if all went OK, False otherwise.
03435     Purpose:    Adds the operation to the list of all known operations.
03436 ********************************************************************************************/
03437 BOOL OpSliceDragBox::Declare()
03438 {   
03439     return RegisterOpDescriptor(0, _R(IDS_SLICE_BOX), CC_RUNTIME_CLASS(OpSliceDragBox), 
03440                                 OPTOKEN_SLICE_DRAGBOX, OpSliceDragBox::GetState);
03441 }
03442 
03443 
03444 
03445 /*******************************************************************
03446 
03447 >   OpState OpSliceDragBox::GetState(String_256*, OpDescriptor*)
03448 
03449     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpSelectorDragBox code.
03450     Created:    11/10/1999
03451     Inputs:     
03452     Outputs:    Description     GetState fills this string with an approriate description
03453                                 of the current state of the SliceTool.
03454     Returns:    The state of the operation, so that menu items (ticks and greying) can be
03455                 done properly.
03456     Purpose:    Find out the state of the operation at the specific time.
03457     Errors:     
03458     See also:   
03459 
03460 *******************************************************************/
03461 OpState OpSliceDragBox::GetState(String_256*, OpDescriptor*)
03462 {
03463     OpState os;
03464 
03465     return os;
03466 }
03467 
03468 
03469 
03470 //-----------------------------------------------
03471 //-----------------------------------------------
03472 //-----------------------------------------------
03473 //-----------------------------------------------
03474 
03475 
03476 
03477 RectListItem::RectListItem(DocRect source) : m_Rect(source)
03478 {
03479     // empty.
03480 }
03481 
03482 
03483 
03484 DocRect RectListItem::GetRect()
03485 {
03486     return m_Rect;
03487 }
03488 
03489 
03490 
03491 //-----------------------------------------------
03492 //-----------------------------------------------
03493 //-----------------------------------------------
03494 //-----------------------------------------------
03495 
03496 
03497 
03498 /********************************************************************************************
03499 
03500 >   OpSliceTranslate::OpSliceTranslate()
03501 
03502     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03503     Created:    13/10/1999
03504     Purpose:    Constructor.
03505 
03506 ********************************************************************************************/
03507 
03508 OpSliceTranslate::OpSliceTranslate() : TransOperation()
03509 {
03510     //Set status line help
03511     StatusHelpID = _R(IDS_TRANSLTRANS_STATUS1);
03512     StatusHelpID2 = _R(IDS_TRANSLTRANS_STATUS2);
03513     CanScaleLines = FALSE;
03514 }
03515 
03516 
03517 
03518 
03519 /********************************************************************************************
03520 
03521 >   void OpSliceTranslate::InitTransformImmediate(OpParam* pOpParam)
03522 
03523     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03524     Created:    13/10/1999
03525     Inputs:     pOpParam - The parameters that were passed into the operation
03526     Purpose:    Sets up the transform ready for an immediate translation. This is called from
03527                 DoWithParam()
03528     SeeAlso:    TransOperation::DoWithParam()
03529 
03530 ********************************************************************************************/
03531 
03532 void OpSliceTranslate::InitTransformImmediate(OpParam* pOpParam)
03533 {
03534     // Set the initial position
03535     StartPos = DocCoord(0,0);
03536     RawStartPos = StartPos;
03537     MagStartPos = StartPos;
03538 
03539     // and copy the offset to translate by from Param2
03540     DocCoord* Offset = (DocCoord*)(pOpParam->Param2);
03541     LastPos.x = Offset->x;
03542     LastPos.y = Offset->y;
03543 
03544     OriginalGridOffset.x=0;
03545     OriginalGridOffset.y=0;
03546 }
03547 
03548 
03549 /********************************************************************************************
03550 
03551 >   virtual void OpSliceTranslate::InitTransformOnDrag(DocCoord PointerPos, ClickModifiers ClickMods)
03552 
03553     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03554     Created:    13/10/1999
03555     Inputs:     PointerPos - The position of the mouse at the start of the drag
03556                 ClickMods - the keyboard modifiers that were active at the start of the drag
03557     Purpose:    Sets up the parameters needed to build the transform matrix at the start
03558                 of the drag. The base class version of this function does nothing.
03559 
03560 ********************************************************************************************/
03561 
03562 void OpSliceTranslate::InitTransformOnDrag(DocCoord PointerPos, ClickModifiers ClickMods)
03563 {
03564     // make a note of the current mouse position
03565     LastPos = PointerPos;
03566 
03567     // Record the offset from the mouse pos to the grid
03568     OriginalGridOffset = GetStartPos();
03569     DocView::ForceSnapToGrid(StartSpread, &OriginalGridOffset);
03570     OriginalGridOffset = GetStartPos() - OriginalGridOffset;
03571 }
03572 
03573 
03574 
03575 
03576 /********************************************************************************************
03577 
03578 >   virtual void OpSliceTranslate::UpdateTransformOnDrag(DocCoord PointerPos, Spread* pSpread,
03579                                     ClickModifiers& ClickMods)
03580 
03581     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03582     Created:    13/10/1999
03583     Inputs:     PointerPos - The latest position of the mouse
03584     Purpose:    Updates the op's state variables (eg selection bounding rect) and
03585                 its blobs whenever the mouse moves.
03586 
03587 ********************************************************************************************/
03588 
03589 void OpSliceTranslate::UpdateTransformOnDrag(   DocCoord PointerPos, Spread* pSpread,
03590                                                 ClickModifiers& ClickMods   )
03591 {
03592     // get the bounding box of the dragged selection and update it's position.
03593     DocCoord Offset = PointerPos - LastPos;
03594     DocRect Bounds( BoundingData.x, BoundingData.y,
03595                     BoundingData.x+BoundingData.Width,
03596                     BoundingData.y+BoundingData.Height );
03597     Bounds.lo = Bounds.lo + Offset;
03598     Bounds.hi = Bounds.hi + Offset;
03599     DocRect SnappedBounds = Bounds;
03600 
03601     // if this drag is not being constrained, then see if we can snap to anything.
03602     if (!ClickMods.Constrain)
03603     {
03604         // Apply snapping to the pointer pos
03605         // First apply magnetic snapping alone
03606         if (MagneticGripPoint && DocView::SnapSelected(pSpread, &PointerPos, TRUE, FALSE))
03607         {
03608             // Magnetic snapping worked!
03609         }
03610         else
03611         {
03612             // Magnetic snapping failed!
03613 
03614             // If magnetic snapping failed then try grid snapping
03615             // on the adjusted coordinate
03616             if (ClickMods.Alternative1)
03617             {
03618                 PointerPos = PointerPos - OriginalGridOffset;
03619                 DocView::SnapSelected(pSpread, &PointerPos, FALSE, TRUE);
03620                 PointerPos = PointerPos + OriginalGridOffset;
03621             }
03622 
03623             // ok, try snapping the selection's bounds to anything in the document,
03624             // eg guidelines, other objects etc.
03625             else
03626             {
03627                 if (DocView::SnapSelected(pSpread,&SnappedBounds,LastRawPos,RawPos))
03628                     PointerPos = PointerPos + (SnappedBounds.lo - Bounds.lo);
03629 
03630                 // right, nothing else has worked - how about trying to snap our bounding
03631                 // rect to one of the bounding rects in our list?
03632                 else
03633                 {
03634                     if (SnapRectToBoxList(pSpread, &SnappedBounds, LastRawPos, RawPos))
03635                         PointerPos = PointerPos + (SnappedBounds.lo - Bounds.lo);
03636                 }
03637             }
03638         }
03639     }
03640 
03641     // Update BoundingData's offset information and pass it to Bounds.
03642     INT32 dx = PointerPos.x - LastPos.x;
03643     INT32 dy = PointerPos.y - LastPos.y;
03644     BoundingData.x += dx;
03645     BoundingData.y += dy;
03646     BoundingData.XYChanged = TRUE;
03647     Bounds.lox = BoundingData.x;
03648     Bounds.loy = BoundingData.y;
03649     Bounds.hix = BoundingData.x + BoundingData.Width;
03650     Bounds.hiy = BoundingData.y + BoundingData.Height;
03651 
03652     // do a collision-detection between the selection bounding rect and a list
03653     // of named-set bounding rect's we built at the start of the drag.
03654     // highlight our selection bounding rect if we collide.
03655     if (TestBoundingBoxCollision(&Bounds, pSpread))
03656     {
03657         // new collision? if so, draw fresh blobs.
03658         if (!m_bDrawingBlobs)
03659         {
03660             m_DragRect = Bounds;
03661             m_bDrawingBlobs = TRUE;
03662             RenderDragBlobs(m_DragRect, StartSpread, FALSE);
03663         }
03664 
03665         // otherwise, update the current blobs.
03666         else
03667         {
03668             // rub out old collision blobs.
03669             RenderDragBlobs(m_DragRect, StartSpread, FALSE);
03670 
03671             // update the record of the selection bounds.
03672             m_DragRect = Bounds;
03673 
03674             // draw new collision blobs.
03675             RenderDragBlobs(m_DragRect, StartSpread, FALSE);
03676         }
03677     }
03678 
03679     // we're not presently colliding, so if we have to then
03680     // rub out any old blobs and invalidate the drag-rect.
03681     else
03682     {
03683         if (m_bDrawingBlobs)
03684         {
03685             // don't render the blobs from here anymore, and render them off.
03686             m_bDrawingBlobs = FALSE;
03687             RenderDragBlobs(m_DragRect, StartSpread, FALSE);
03688 
03689             // invalidate the drag-rect, so that it won't be rendered from RenderDragBlobs().
03690             m_DragRect.hix = m_DragRect.lox - 1;
03691         }
03692     }
03693 
03694     // Make a mental note of the current position
03695     LastPos = PointerPos;
03696 
03697     // Update the current spread (must do this if CanChangeSpread returns TRUE)
03698     CurrentSpread = pSpread;
03699 }
03700 
03701 
03702 
03703 /********************************************************************************************
03704 
03705 >   virtual BOOL OpSliceTranslate::CanChangeSpread()
03706 
03707     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03708     Created:    05/June/2006
03709     Inputs:     -
03710     Outputs:    -
03711     Returns:    TRUE if this transform allows the drag to be transferred to another spread
03712     Purpose:    Tell the baseclass functions whether to draw drag feedback only on the start
03713                 spread or to allow drag rendering to be done on other spreads too.
03714 
03715 ********************************************************************************************/
03716 
03717 BOOL OpSliceTranslate::CanChangeSpread()
03718 {
03719     return TRUE;
03720 }
03721 
03722 
03723 /********************************************************************************************
03724 
03725 >   BOOL OpSliceTranslate::SnapRectToBoxList(   Spread* pSpread, DocRect* pDocRect,
03726                                                 const DocCoord& PrevCoord,
03727                                                 const DocCoord& CurCoord )
03728     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03729     Created:    26/10/1999
03730     Inputs:     pSpread     a pointer to the relevant spread node.
03731                 pDocRect    a pointer to the rectangle to snap.
03732                 PrevCoord   a reference to the ???
03733                 CurCoord    a reference to ???
03734     Outputs:    pDocRect may be moved so that it snaps to the side of one of this op's
03735                 list of collision-rectangles.
03736     Returns:    TRUE if pDocRect was snapped,
03737                 FALSE otherwise.
03738     Purpose:    Performs a snap check between the given rect and this op's own collection of
03739                 collision bounding rects. The given rect is tested successively against each
03740                 member of our list, and if it lies within snapping distance of any rectangle
03741                 it will be shifted to snap to that rectangle.
03742 
03743                 This function does not live with all the other snap functions because it's
03744                 use is restricted solely to the context of this operation.
03745     Errors:     
03746     See also:   All Snap member functions of all classes derived from NodeRenderableBounded.
03747 
03748 ********************************************************************************************/
03749 BOOL OpSliceTranslate::SnapRectToBoxList(   Spread* pSpread, DocRect* pDocRect,
03750                                             const DocCoord& PrevCoord,
03751                                             const DocCoord& CurCoord )
03752 {
03753     // iterate over our list of rectangles and check pDocRect against each one.
03754     BOOL bSnapped = FALSE;
03755     RectListItem* pCurListItem = (RectListItem*)m_lBoxes.GetHead();
03756     while (!bSnapped && pCurListItem != NULL)
03757     {
03758         bSnapped = CSnap::SnapRectToRect(pDocRect, pCurListItem->GetRect());
03759         pCurListItem = (RectListItem*)m_lBoxes.GetNext(pCurListItem);
03760     }
03761 
03762     return bSnapped;
03763 }
03764 
03765 
03766 
03767 /********************************************************************************************
03768 
03769 >   void OpSliceTranslate::BuildMatrix()
03770 
03771     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03772     Created:    13/10/1999
03773     Purpose:    Builds the transform matrix required to rotate the selection about the
03774                 point CentreOfRot by the required number of degrees
03775 
03776 ********************************************************************************************/
03777 
03778 void OpSliceTranslate::BuildMatrix()
03779 {
03780     // Build a translation matrix by takeing the offset from the last mouse position
03781     // to the start mouse position
03782         Transform = Matrix(LastPos.x-GetStartPos().x, LastPos.y-GetStartPos().y);
03783 }
03784 
03785 
03786 
03787 /********************************************************************************************
03788 
03789 >   virtual BOOL OpSliceTranslate::ShouldPointerBeOffset()
03790 
03791     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03792     Created:    13/10/1999
03793     Returns:    FALSE
03794     Purpose:    Tells the base class of the operation that we want our mouse coords to be
03795                 left alone
03796 
03797 ********************************************************************************************/
03798 
03799 BOOL OpSliceTranslate::ShouldPointerBeOffset()
03800 {
03801     return FALSE;
03802 }
03803 
03804 
03805 
03806 
03807 /********************************************************************************************
03808 
03809 >   virtual void OpSliceTranslate::ConstrainDrag(DocCoord* PointerPos)
03810 
03811     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03812     Created:    13/10/1999
03813     Inputs:     PointerPos - The current position of the mouse that needs to be constrained
03814     Outputs:    PointerPos - The position of the mouse after it has been constrained
03815     Purpose:    Will constrain the mouse position to lie along rays from the centre of
03816                 rotation at the constrain angle apart (45 degrees by default)
03817 
03818 ********************************************************************************************/
03819 
03820 void OpSliceTranslate::ConstrainDrag(DocCoord* PointerPos)
03821 {
03822     // Lock the mouse to move along the axis or diagonally
03823     DocCoord Blobby = GetStartPos();
03824     DocView::ConstrainToAngle(Blobby, PointerPos);
03825 }
03826 
03827 
03828 
03829 /********************************************************************************************
03830 
03831 >   BOOL OpSliceTranslate::Declare()
03832 
03833     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Rik's OpTranslateTrans code.
03834     Created:    13/10/1999
03835     Returns:    TRUE if all went OK, False otherwise
03836     Purpose:    Adds the operation to the list of all known operations
03837 
03838 ********************************************************************************************/
03839 
03840 BOOL OpSliceTranslate::Declare()
03841 {
03842     return (RegisterOpDescriptor(0, _R(IDS_SLICE_MOVE), CC_RUNTIME_CLASS(OpSliceTranslate),
03843                                 OPTOKEN_SLICE_TRANSLATE, TransOperation::GetState)); 
03844 }
03845 
03846 
03847 
03848 /********************************************************************************************
03849 
03850 >   void OpSliceTranslate::CompileCollisionBoxList()
03851 
03852     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03853     Created:    12/10/1999
03854     Inputs:     
03855     Outputs:    Fills OpSliceTranslate::m_lBoxes with the required
03856                 list of collision-test bounding boxes.
03857     Returns:    
03858     Purpose:    Make a list of the overall bounding rects of all named sets
03859                 which have no members selected. These are used to collision
03860                 detect against during the drag.
03861     Errors:     
03862     See also:   
03863 
03864 ********************************************************************************************/
03865 void OpSliceTranslate::CompileCollisionBoxList()
03866 {
03867     // iterate through the current list of named sets. if a named set
03868     // has no objects selected, then add its collective bounding rect
03869     // to the list of bounding rects, for collision detection.
03870     NameGallery * pNameGallery = NameGallery::Instance();
03871     //pNameGallery->ForceUpdate();
03872     SGUsedNames* pNames = pNameGallery ? pNameGallery->GetUsedNames() : NULL;
03873     SGNameItem* pItem = pNames ? (SGNameItem*) pNames->GetChild() : NULL;
03874 
03875     m_lBoxes.DeleteAll();
03876     while (pItem != NULL)
03877     {
03878         if (pItem->IsNoneSelected())
03879             m_lBoxes.AddTail(new RectListItem(pItem->GetSetBounds()));
03880 
03881         // get next name.
03882         pItem = (SGNameItem*)pItem->GetNext();
03883     }
03884 }
03885 
03886 
03887 
03888 /********************************************************************************************
03889 
03890 >   BOOL OpSliceTranslate::TestBoundingBoxCollision(Rect* pTestRect)
03891 
03892     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03893     Created:    12/10/1999
03894     Inputs:     pTestRect   the rectangle to test against each member of m_lBoxes.
03895                 pSpread     a pointer to the current spread, so that we can render
03896                             the collided rectangle(s?) if necessary.
03897     Outputs:    
03898     Returns:    TRUE if a collision is detected,
03899                 FALSE otherwise.
03900     Purpose:    Test the given bounding rect against the list of bounding rects,
03901                 m_lBoxes, and return TRUE if it overlaps any of them.
03902 
03903                 TODO: we may want to change this to a void function.
03904 
03905     Additional: Karim MacDonald 07/02/2000
03906                 This function now always returns FALSE, to prevent visual artifacts when
03907                 dragging with the slice-tool, a result of the re-introduction of a dashed 
03908                 line around the selection during a drag.
03909 
03910     Errors:     ERROR2 if any parameter is NULL.
03911     See also:   
03912 
03913 ********************************************************************************************/
03914 BOOL OpSliceTranslate::TestBoundingBoxCollision(DocRect* pTestRect, Spread* pSpread)
03915 {
03916     ERROR2IF(pTestRect == NULL || pSpread == NULL, FALSE,
03917         "OpSliceTranslate::TestBoundingBoxCollision- called with a NULL parameter");
03918 
03919     // iterate over the list, checking each rect against pTestRect.
03920     RectListItem* pListItem = (RectListItem*)m_lBoxes.GetHead();
03921     BOOL bCollision = FALSE;
03922     while (!bCollision && pListItem != NULL)
03923     {
03924         bCollision = pTestRect->IsIntersectedWith(pListItem->GetRect());
03925 
03926         // get the next object in the list.
03927         if (!bCollision)
03928             pListItem = (RectListItem*)m_lBoxes.GetNext(pListItem);
03929     }
03930 
03931 //  return bCollision;
03932     return FALSE;
03933 }
03934 
03935 
03936 
03937 /********************************************************************************************
03938 
03939 >   void OpSliceTranslate::DragStarted( TransformData* TransData,
03940                                         DragTool* pTool,
03941                                         TransformBoundingData* pBounds,
03942                                         DocCoord ClickPos,
03943                                         Spread* pSpread,
03944                                         ClickModifiers ClickMods,
03945                                         DocCoord ClickOffset, 
03946                                         Node* NodeToTransform,
03947                                         DragType dragtype )
03948 
03949     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03950     Created:    14/10/1999
03951     Inputs:     see TransOperation::DragStarted
03952     Purpose:    When we start a drag, we want to compile a list of bounding boxes which
03953                 belong to non-selected named sets, eg if there is a group of objects called
03954                 'Button One' and it is not selected, we want to add its overall bounding box
03955                 to a list, so we can collision detect against it later.
03956 
03957                 Once that's done we call the base class, to start the drag.
03958     Errors:     
03959     See also:   
03960 
03961 ********************************************************************************************/
03962 void OpSliceTranslate::DragStarted( TransformData* TransData,
03963                                     DragTool* pTool,
03964                                     TransformBoundingData* pBounds,
03965                                     DocCoord ClickPos,
03966                                     Spread* pSpread,
03967                                     ClickModifiers ClickMods,
03968                                     DocCoord ClickOffset, 
03969                                     Node* NodeToTransform,
03970                                     DragType dragtype )
03971 {
03972     // compile a list of any names which are *not* used by the current selection,
03973     // and use this to compile a list of bounding boxes which we can collision-test
03974     // against during the drag, to announce if we cross any other named sets.
03975     CompileCollisionBoxList();
03976     m_bDrawingBlobs = FALSE;
03977 
03978     // call the base-class drag-start routine.
03979     TransOperation::DragStarted(TransData, pTool, pBounds, ClickPos, pSpread, ClickMods,
03980                                 ClickOffset, NodeToTransform, dragtype);
03981 }
03982 
03983 
03984 
03985 /********************************************************************************************
03986 
03987 >   void OpSliceTranslate::DragFinished(DocCoord dcPos, ClickModifiers ClickMods,
03988                                         Spread* pSpread, BOOL bSuccess, BOOL bSolidDrag)
03989 
03990     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03991     Created:    15/10/1999
03992     Inputs:     dcPos       
03993                 ClickMods   
03994                 pSpread     
03995                 bSuccess    
03996     Outputs:    
03997     Returns:    
03998     Purpose:    Called when the selection-drag finishes, either normally or having been
03999                 cancelled by the user. Performs tidy-up when the drag is over.
04000 
04001                 This function calls the base-class for its main functionality.
04002     Errors:     
04003     See also:   
04004 
04005 ********************************************************************************************/
04006 void OpSliceTranslate::DragFinished(DocCoord dcPos, ClickModifiers ClickMods,
04007                                     Spread* pSpread, BOOL bSuccess, BOOL bSolidDrag)
04008 {
04009     // have we finished drawing blobs, or do we need to erase them?
04010     if (m_bDrawingBlobs)
04011     {
04012         RenderDragBlobs(m_DragRect, StartSpread, bSolidDrag);       // erase them.
04013         TransOperation::RenderDragBlobs(m_DragRect, StartSpread, bSolidDrag);
04014     }
04015 
04016     m_lBoxes.DeleteAll();
04017     TransOperation::DragFinished(dcPos, ClickMods, pSpread, bSuccess, bSolidDrag);
04018 }
04019 
04020 
04021 
04022 /********************************************************************************************
04023 
04024 >   void SliceInfoBarOp::UpdateCurrentStateGadget()
04025 
04026     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
04027     Created:    18/2/00
04028     Returns:    
04029     Purpose:    Quickly scans the tree to find the layers shown then sets the drop down control
04030                 which displays the current button state.
04031                 Includes the #define code for including the "clicked" state or not.
04032 
04033 ********************************************************************************************/
04034 void SliceInfoBarOp::UpdateCurrentStateGadget()
04035 {
04036     INT32 state = FindCurrentState();
04037 
04038 /*  // set the text in the redefine state button
04039     String_256 RedefineText = "";
04040     String_256 Temp(_R(IDS_ROLLOVER_DEFAULT));
04041     RedefineText += " \"";
04042     if (m_TopVisibleState == 1)
04043         Temp.Load(_R(IDS_ROLLOVER_MOUSE));
04044     else if (m_TopVisibleState == 2)
04045         Temp.Load(_R(IDS_ROLLOVER_CLICKED));
04046     else if (m_TopVisibleState == 3)
04047         Temp.Load(_R(IDS_ROLLOVER_SELECTED));
04048     else if (m_TopVisibleState == 4)
04049         Temp.Load(_R(IDS_BACK_BAR));
04050 
04051     RedefineText.MakeMsg(_R(IDS_DESIGN_TEXT), (LPCSTR) Temp);
04052     SetStringGadgetValue(_R(IDC_REDEFINE_STATE), &RedefineText);
04053 */
04054     EnableGadget(_R(IDC_REDEFINE_STATE), m_TopVisibleState != -1);
04055 
04056     // set the text in the dropdown
04057     if (state >=0 && state <= 4 + INCLUDE_CLICKED_STATE)
04058         SetSelectedValueIndex(_R(IDC_ST_STATE), state);
04059     else
04060         SetStringGadgetValue(_R(IDC_ST_STATE), _R(IDS_MANY), FALSE, -1);
04061 }

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