pathedit.cpp

Go to the documentation of this file.
00001 // $Id: pathedit.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 // Contains general purpose operations to be used in path editing
00099 
00100 /*
00101 */
00102 
00103 #include "camtypes.h"
00104 #include "pathedit.h"
00105 
00106 // Code headers
00107 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 #include "blobs.h"
00110 #include "csrstack.h"
00111 //#include "cursor.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 //#include "fixmem.h"               // for CCMalloc and CCFree - in camtypes.h [AUTOMATICALLY REMOVED]
00115 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 #include "lineattr.h"           // for getting the line width of a NodePath
00117 #include "nodepath.h"
00118 #include "ndtxtpth.h"
00119 //#include "nodeattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00120 #include "objchge.h"
00121 #include "pathops.h"
00122 #include "progress.h"
00123 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 //#include "tranform.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 #include "attrmap.h"
00126 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00127 //#include "bubbleid.h"
00128 #include "ndbldpth.h"
00129 #include "nodeblnd.h"
00130 //#include "blndtool.h"
00131 #include "nodebldr.h"
00132 #include "opdrbrsh.h"
00133 // Resource headers
00134 #include "helpids.h"
00135 //#include "jim.h"
00136 //#include "peter.h"
00137 //#include "resource.h"    
00138 //#include "barsdlgs.h"
00139 //#include "rik.h"
00140 //#include "simon.h"
00141 //#include "viewrc.h"
00142 //#include "phil.h"
00143 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00144 #include "brshattr.h"
00145 #include "opbevel.h"
00146 #include "nodemold.h"
00147 #include "ndmldpth.h"
00148 #include "samplist.h"
00149 #include "brshattr.h"
00150 #include "ophist.h"
00151 #include "blndtool.h"
00152 
00153 #define PATRACE if(IsUserName("Peter")) TRACE
00154 
00155 // Useful macro, given a pointer to a CCObject (or an object derived
00156 // from it) and a string format, it will output the class name of the object. 
00157 // Example usage PACLASSNAME(CurrentNode,"Node class = %s\n");
00158 // N.B. No checking is node or parameters.  Note semi-colon on end.
00159 #define PACLASSNAME(a,b) CCRuntimeClass* Blob; Blob = a->GetRuntimeClass(); PATRACE(b,Blob->m_lpszClassName)
00160 
00161 // Operations
00162 CC_IMPLEMENT_DYNCREATE( OpJoinShapes, SelOperation)
00163 CC_IMPLEMENT_DYNCREATE( OpBreakShapes, SelOperation) 
00164 CC_IMPLEMENT_DYNCREATE( OpDeletePoints, SelOperation)       // Deleting of path endpoints
00165 CC_IMPLEMENT_DYNCREATE( OpNodePathEditBlob, SelOperation)   // Dragging of a path endpoint
00166 CC_IMPLEMENT_DYNCREATE( OpToggleSmooth, SelOperation)       // Toggling smooth/cuspness of endpoints
00167 CC_IMPLEMENT_DYNCREATE( OpNodePathEditControlBlob, OpNodePathEditBlob)  // Dragging of a Bezier control point
00168 CC_IMPLEMENT_DYNCREATE( OpReshapeOrAddPoint, OpNodePathEditBlob)        // Reshaping a path OR adding an endpoint
00169 CC_IMPLEMENT_DYNCREATE( OpNodePathAddEndpoint, OpNodePathEditBlob)      // Adding an endpoint to the end of the path
00170 CC_IMPLEMENT_DYNCREATE( OpNewPath, OpNodePathEditBlob)
00171 CC_IMPLEMENT_DYNCREATE( OpCloseNodePaths, OpNodePathAddEndpoint)        // Closing all selected open paths
00172 
00173 // Actions
00174 CC_IMPLEMENT_DYNCREATE( ModifyPathAction, Action)
00175 CC_IMPLEMENT_DYNCREATE( RemovePathElementAction, Action)
00176 CC_IMPLEMENT_DYNCREATE( InsertPathElementAction, Action)
00177 CC_IMPLEMENT_DYNCREATE( ModifyElementAction, Action)
00178 CC_IMPLEMENT_DYNCREATE( ModifyFlagsAction, Action)
00179 CC_IMPLEMENT_DYNCREATE( RecalcBoundsAction, Action)
00180 CC_IMPLEMENT_DYNCREATE( RecordBoundsAction, Action)
00181 CC_IMPLEMENT_DYNCREATE( ModifyFilledAction, Action)
00182 CC_IMPLEMENT_DYNCREATE( SavePathArraysAction, Action)
00183 //CC_IMPLEMENT_DYNCREATE( StorePathSubSelStateAction, Action)
00184 
00185 // Declare smart memory handling in Debug builds
00186 #define new CAM_DEBUG_NEW
00187 
00188 /********************************************************************************************
00189 
00190 >   OpNodePathEditBlob::OpNodePathEditBlob()
00191 
00192     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00193     Created:    20/7/93
00194     Purpose:    Constructor. This simply sets a few of the operation flags.
00195 
00196 ********************************************************************************************/
00197 
00198 OpNodePathEditBlob::OpNodePathEditBlob()
00199 {
00200     EndSnapped = FALSE; 
00201     SnapToAnother = FALSE;  
00202     pMoveCursor = NULL;
00203     SnapToPath = NULL;
00204     DragStarted = FALSE;
00205     SnapToLineOrCurve = FALSE;
00206     DragPoint = -1;
00207     UpdatePoint = -1;
00208     ConstrainPoint = DocCoord(-1,-1);
00209     ConstrainPrevPoint = DocCoord(-1,-1);
00210     ConstrainNextPoint = DocCoord(-1,-1);
00211     MultiplePaths = FALSE;
00212 }
00213 
00214 /********************************************************************************************
00215 
00216 >   void OpNodePathEditBlob::DoStartDragEdit(NodePath* OrigPath, DocCoord Anchor, Spread *pSpread)
00217 
00218     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> - latterly Peter
00219     Created:    5/7/93
00220     Inputs:     OrigPath - Pointer to the path we are about to edit
00221                 Anchor - The position of the mouse at the start of the Drag
00222                 pSpread - The spread that the path is on
00223     Purpose:    This is called to start a drag operation on an endpoint on a path
00224 
00225 ********************************************************************************************/
00226 
00227 void OpNodePathEditBlob::DoStartDragEdit(NodePath* OrigPath, DocCoord Anchor, Spread *pSpread)
00228 {
00229     BOOL Success = TRUE;
00230     
00231     // We had better take a note of the starting point of the drag
00232     LastMousePos = Anchor;
00233     StartMousePos = Anchor;
00234     StartSpread  = pSpread;
00235 
00236     SelRange* theSelection = GetApplication ()->FindSelection ();
00237 
00238     BOOL selectionContainsMoulds = FALSE;
00239 
00240     if (theSelection)
00241     {
00242         // we need to do some special processing to handle moulds ....
00243 
00244         Node* pCurrentNode = (Node*) theSelection->FindFirst ();
00245 
00246         while (pCurrentNode)
00247         {
00248             if (IS_A (pCurrentNode, NodeMould))
00249             {
00250                 selectionContainsMoulds = TRUE;
00251                 pCurrentNode = NULL;
00252             }
00253             else
00254             {
00255                 pCurrentNode = (Node*) theSelection->FindNext (pCurrentNode);
00256             }
00257         }
00258     }
00259 
00260     if (!selectionContainsMoulds)
00261     {
00262         BevelTools::BuildListOfSelectedNodes(&OriginalPaths, CC_RUNTIME_CLASS(NodePath));
00263     }
00264     else
00265     {
00266         // now go and get those moulds baby !
00267         BevelTools::BuildListOfSelectedNodes(&OriginalPaths, CC_RUNTIME_CLASS(NodeMouldPath));
00268 
00269         // now, we also have to rescan the selection (again!) BUT this time without
00270         // the moulds
00271 
00272         Node* pCurrentNode = (Node*) theSelection->FindFirst ();
00273 
00274         while (pCurrentNode)
00275         {
00276             if (IS_A (pCurrentNode, NodeMould))
00277             {
00278                 pCurrentNode = (Node*) theSelection->FindNext (pCurrentNode);
00279             }
00280             else
00281             {
00282                 if (IS_A (pCurrentNode, NodePath))
00283                 {
00284                     NodeListItem* pInsert = new NodeListItem ();
00285                     
00286                     if (pInsert)    // and insert into the paths list ....
00287                     {
00288                         pInsert->pNode = pCurrentNode;
00289                         OriginalPaths.AddHead (pInsert);
00290                     }
00291                 }
00292                 
00293                 pCurrentNode = (Node*) theSelection->FindNext (pCurrentNode);
00294             }
00295         }
00296     }
00297 
00298     if (OriginalPaths.GetCount () == 1)
00299     //if (TRUE)
00300     {
00301         MultiplePaths = FALSE;
00302         
00303         OriginalPath = OrigPath;
00304 
00305         // Now calculate DragPoint and UpdatePoint
00306         if (Success)
00307         {
00308             PathFlags* Flags = OriginalPath->InkPath.GetFlagArray();
00309             PathVerb* Verbs = OriginalPath->InkPath.GetVerbArray();
00310             INT32 NumCoords = OriginalPath->InkPath.GetNumCoords();
00311 
00312             for (INT32 i=0;i<NumCoords;i++)
00313             {
00314                 if (Flags[i].IsEndPoint && Flags[i].IsSelected 
00315                                 && !(OriginalPath->InkPath.IsSubPathClosed(i) && (Verbs[i] == PT_MOVETO)) )
00316                 {
00317                     // If you are dragging a closepoint then you are actually dragging two points
00318                     // but we need to update the line tool as the user thinks they are dragging one.
00319                     // If we are on the opening moveto then just skip the tests.
00320                     if (DragPoint != -1)
00321                     {
00322                         UpdatePoint = -1;
00323                         DragPoint = -1;
00324                         break;
00325                     }
00326                     else
00327                     {
00328                         UpdatePoint = i;
00329                         DragPoint = i;
00330                     }
00331                 }
00332             }
00333             // On exit from that loop, DragPoint = -1 if there are multiple selected endpoints,
00334             // otherwise DragPoint is the index to the selected endpoint.  UpdatePoint is the
00335             // index of the point displaied in the Line tool
00336         }
00337 
00338         // Set the constrain point
00339         if (DragPoint != -1)
00340         {
00341             ConstrainPoint = OriginalPath->InkPath.GetCoordArray()[DragPoint];
00342 
00343             // Get the previous endpoint
00344             INT32 OtherEndpoint = DragPoint;
00345             if (OriginalPath->InkPath.FindPrevEndPoint(&OtherEndpoint))
00346                 ConstrainPrevPoint = OriginalPath->InkPath.GetCoordArray()[OtherEndpoint];
00347             else
00348                 ConstrainPrevPoint = ConstrainPoint;
00349 
00350             // Get the next endpoint
00351             OtherEndpoint = DragPoint;
00352             if (OriginalPath->InkPath.FindNextEndPoint(&OtherEndpoint))
00353                 ConstrainNextPoint = OriginalPath->InkPath.GetCoordArray()[OtherEndpoint];
00354             else
00355                 ConstrainNextPoint = ConstrainPoint;
00356         }
00357         else
00358         {
00359             ConstrainPoint = Anchor;
00360             ConstrainPrevPoint = Anchor;
00361             ConstrainNextPoint = Anchor;
00362         }
00363 
00364         // We also need to make a version of the path that we can change
00365         Success = BuildEditPath();
00366 
00367         // Create and send a change message about this path edit
00368         // This one is handled by moulds in their OnChildChange() function
00369         if (Success)
00370             Success = (EditObjChange.ObjChangeStarting(OrigPath,this,&EditPath,StartSpread,TRUE) == CC_OK);
00371 
00372         // Create and display the cursors for this operation
00373         if (Success)
00374             Success = CreateCursors();
00375         if (Success)
00376             ChangeCursor(pCrossHairCursor);
00377         
00378     //  // Render the bits of the path that are different
00379         DocRect EditPathBBox = EditPath.GetBoundingRect();
00380     //  if (Success)
00381     //      RenderPathEditBlobs(EditPathBBox, pSpread);
00382 
00383         // Tell the Dragging system that we need drags to happen
00384         if (Success)
00385             Success = StartDrag(DRAGTYPE_AUTOSCROLL, &EditPathBBox, &LastMousePos);
00386 
00387         if (!Success)
00388         {
00389             InformError();
00390             FailAndExecute();
00391             End();
00392         }
00393     }
00394     else
00395     {
00396         // lets try and keep things the same for the blobs parent as in the one selection case
00397         // BUT lets also try and do our extra stuff ....  I expect time MUCK UPS to occur !!!!
00398 
00399         MultiplePaths = TRUE;
00400         
00401         OriginalPath = OrigPath;
00402 
00403         // we need to make OrigPath the first one in our linked list ....
00404 
00405         NodeListItem* pCurrentOrig = (NodeListItem*) OriginalPaths.GetHead ();
00406 
00407         while (pCurrentOrig)
00408         {
00409             NodePath* pOrigPath = (NodePath*) (pCurrentOrig->pNode);
00410 
00411             if (pOrigPath == OrigPath)
00412             {
00413                 NodeListItem* newHead = (NodeListItem*) OriginalPaths.RemoveItem (pCurrentOrig);
00414                 
00415                 OriginalPaths.AddHead (newHead);
00416 
00417                 pCurrentOrig = NULL;
00418             }
00419             else
00420             {
00421                 pCurrentOrig = (NodeListItem*) OriginalPaths.GetNext (pCurrentOrig);
00422             }
00423         }
00424 
00425         // Now calculate DragPoint and UpdatePoint
00426         if (Success)
00427         {
00428             NodeListItem* pCurrentOrig = (NodeListItem*) OriginalPaths.GetHead ();
00429 
00430             // CGS:  while loop taken out since we can only be 'drag sensitive' to the point
00431             // that is actually being dragged
00432 
00433             //while (pCurrentOrig)
00434             {
00435                 NodePath* pOrigPath = (NodePath*) (pCurrentOrig->pNode);
00436                 
00437                 PathFlags* Flags = pOrigPath->InkPath.GetFlagArray();
00438                 PathVerb* Verbs = pOrigPath->InkPath.GetVerbArray();
00439                 INT32 NumCoords = pOrigPath->InkPath.GetNumCoords();
00440 
00441                 for (INT32 i=0;i<NumCoords;i++)
00442                 {
00443                     if (Flags[i].IsEndPoint && Flags[i].IsSelected 
00444                                     && !(pOrigPath->InkPath.IsSubPathClosed(i) && (Verbs[i] == PT_MOVETO)) )
00445                     {
00446                         // If you are dragging a closepoint then you are actually dragging two points
00447                         // but we need to update the line tool as the user thinks they are dragging one.
00448                         // If we are on the opening moveto then just skip the tests.
00449                         if (DragPoint != -1)
00450                         {
00451                             UpdatePoint = -1;
00452                             DragPoint = -1;
00453                             break;
00454                         }
00455                         else
00456                         {
00457                             UpdatePoint = i;
00458                             DragPoint = i;
00459                         }
00460                     }
00461                 }
00462 
00463             //  pCurrentOrig = (NodeListItem*) OriginalPaths.GetNext (pCurrentOrig);
00464             }
00465             // On exit from that loop, DragPoint = -1 if there are multiple selected endpoints,
00466             // otherwise DragPoint is the index to the selected endpoint.  UpdatePoint is the
00467             // index of the point displaied in the Line tool
00468         }
00469 
00470         // Set the constrain point - do this only for the path that was clicked on
00471         // Any guesses as to why?
00472 
00473         // Answer:  cause its the one that is being dragged!
00474         if (DragPoint != -1)
00475         {
00476             NodeListItem* pCurrentOrig = (NodeListItem*) OriginalPaths.GetHead ();
00477             NodePath* pOrigPath = (NodePath*) (pCurrentOrig->pNode);
00478             
00479             ConstrainPoint = pOrigPath->InkPath.GetCoordArray()[DragPoint];
00480 
00481             // Get the previous endpoint
00482             INT32 OtherEndpoint = DragPoint;
00483             if (pOrigPath->InkPath.FindPrevEndPoint(&OtherEndpoint))
00484                 ConstrainPrevPoint = pOrigPath->InkPath.GetCoordArray()[OtherEndpoint];
00485             else
00486                 ConstrainPrevPoint = ConstrainPoint;
00487 
00488             // Get the next endpoint
00489             OtherEndpoint = DragPoint;
00490             if (pOrigPath->InkPath.FindNextEndPoint(&OtherEndpoint))
00491                 ConstrainNextPoint = pOrigPath->InkPath.GetCoordArray()[OtherEndpoint];
00492             else
00493                 ConstrainNextPoint = ConstrainPoint;
00494         }
00495         else
00496         {
00497             ConstrainPoint = Anchor;
00498             ConstrainPrevPoint = Anchor;
00499             ConstrainNextPoint = Anchor;
00500         }
00501 
00502         // We also need to make versions of the paths that we can change
00503         Success = BuildEditPaths();
00504 
00505         // Create and send a change message about this path edit
00506         // This one is handled by moulds in their OnChildChange() function
00507         if (Success)
00508         {
00509             NodeListItem* pCurrentOrig = (NodeListItem*) OriginalPaths.GetHead ();
00510             NodeListItem* pCurrentEdit = (NodeListItem*) EditPaths.GetHead ();
00511 
00512             while ((pCurrentOrig) && (pCurrentEdit))
00513             {
00514                 NodePath* pOrigPath = (NodePath*) (pCurrentOrig->pNode);
00515                 Path* pEditPath = (Path*) (pCurrentEdit->pNode);
00516                 
00518                 
00519                 ObjChangePathEdit* NewEditObjChange = new ObjChangePathEdit;
00520                 
00521                 Success = (NewEditObjChange->ObjChangeStarting(pOrigPath,this,pEditPath,StartSpread,TRUE) == CC_OK);
00522 
00523                 NodeListItem* pInsert = new NodeListItem;
00524                 pInsert->pNode = (Node*) NewEditObjChange;
00525         
00526                 ObjChanges.AddTail (pInsert);
00527 
00529 
00530                 BOOL* NewDragStartVal = new BOOL;
00531                 *NewDragStartVal = FALSE;
00532                 
00533                 NodeListItem* pInsert2 = new NodeListItem;
00534                 pInsert2->pNode = (Node*) NewDragStartVal;
00535 
00536                 PathsDragStarted.AddTail (pInsert2);
00537 
00539 
00540                 if (Success)
00541                 {
00542                     pCurrentOrig = (NodeListItem*) OriginalPaths.GetNext (pCurrentOrig);
00543                     pCurrentEdit = (NodeListItem*) EditPaths.GetNext (pCurrentEdit);
00544                 }
00545                 else
00546                 {
00547                     pCurrentOrig = NULL;
00548                     pCurrentEdit = NULL;
00549                 }
00550 
00552                 
00553                 //delete (NewEditObjChange);
00554         //      delete (NewDragStartVal);
00555             }
00556         }
00557 
00558         // Create and display the cursors for this operation (only once - obviously)
00559         if (Success)
00560             Success = CreateCursors();
00561         if (Success)
00562             ChangeCursor(pCrossHairCursor);
00563         
00564     //  // Render the bits of the path that are different
00565         DocRect EditPathBBox;// = EditPath.GetBoundingRect();
00566 
00567         SelRange* Selected = GetApplication()->FindSelection();
00568 
00569         EditPathBBox = Selected->GetBlobBoundingRect ();
00570 
00571     //  if (Success)
00572     //      RenderPathEditBlobs(EditPathBBox, pSpread);
00573 
00574         // Tell the Dragging system that we need drags to happen
00575         if (Success)
00576             Success = StartDrag(DRAGTYPE_AUTOSCROLL, &EditPathBBox, &LastMousePos);
00577 
00578         if (!Success)
00579         {
00580             InformError();
00581             FailAndExecute();
00582             End();
00583         }
00584     }
00585 }
00586 
00587 
00588 
00589 /********************************************************************************************
00590 
00591 >   void OpNodePathEditBlob::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread*, BOOL bSolidDrag)
00592 
00593     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00594     Created:    5/7/93
00595     Inputs:     PointerPos - The current position of the mouse in Doc Coords
00596                 ClickMods - Which key modifiers are being pressed
00597     Purpose:    This is called every time the mouse moves, during a drag.
00598     SeeAlso:    ClickModifiers
00599 
00600 ********************************************************************************************/
00601 
00602 void OpNodePathEditBlob::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
00603 {
00604     if (!MultiplePaths)
00605     {
00606         // If drag has moved onto a different spread, convert the coord to be relative to the
00607         // original spread.
00608         if (pSpread != StartSpread)
00609             PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
00610 
00611         Path TempPath;
00612 
00613         // inform the parent a change is happening
00614         if (EditObjChange.ChangeMask.EorBlobs)
00615         {
00616             ChangeCode Chge = EditObjChange.RenderCurrentBlobs(OriginalPath,this,&EditPath,StartSpread,TRUE);
00617             if (Chge!=CC_OK)
00618                 return;
00619             // Create a local copy of the edit path
00620             if (!TempPath.Initialise(EditPath.GetNumCoords(), 12))
00621                 return;
00622             TempPath.CopyPathDataFrom(&EditPath);
00623         }
00624 
00625         // Rub out the old EORed version of the path
00626         if (DragStarted)
00627             RenderPathEditBlobs( EditPath.GetBoundingRect(), StartSpread );
00628         else
00629             DragStarted = TRUE;
00630 
00631         // Here is where we do all the snapping to grids and endpoints
00632         // and constrain. First we check for snapping to line ends, then
00633         // we check constrain, then we check grid snapping
00634 
00635         EndSnapped = FALSE;         // TRUE if this point snaps to the other end
00636         SnapToAnother = FALSE;      // TRUE if this point snaps to another line end
00637         SnapToLineOrCurve = FALSE;  // No snapped other line
00638 
00639         // We might be snapping to an endpoint if the point we're dragging around is either
00640         // a moveto, or is the last point in the subpath. We will also only snap if there is
00641         // only one point selected on the path
00642 
00643         DocRect PointerBlobRect;
00644         GetApplication()->GetBlobManager()->GetBlobRect(PointerPos,&PointerBlobRect);
00645 
00646         DocCoord* Coords = EditPath.GetCoordArray();
00647 //      PathFlags* Flags = EditPath.GetFlagArray();
00648         PathVerb* Verbs = EditPath.GetVerbArray();
00649         INT32 NumCoords = EditPath.GetNumCoords();
00650 
00651         INT32 i;
00652         if (DragPoint != -1)
00653         {
00654             // Look to see if the selected point is a real endpoint
00655             if (Verbs[DragPoint] == PT_MOVETO)
00656             {
00657                 // This is the start element of the path
00658                 i = DragPoint;
00659                 if (!EditPath.IsSubPathClosed(i))
00660                 {
00661                     INT32 NextEndpoint = i;
00662                     if (EditPath.FindNextEndPoint(&NextEndpoint))
00663                     {
00664                         EditPath.FindEndElOfSubPath(&i);        // i indexes to the end element
00665 
00666                         // If this sub-path is one element long then closing it is not allowed
00667                         if ((i != NextEndpoint) && PointerBlobRect.ContainsCoord(Coords[i]))
00668                         {
00669                             PointerPos.x = Coords[i].x + LastMousePos.x - Coords[DragPoint].x;
00670                             PointerPos.y = Coords[i].y + LastMousePos.y - Coords[DragPoint].y;
00671                             EndSnapped = TRUE;
00672                         }
00673                     }
00674                 }
00675             }
00676             else if (DragPoint+1 == NumCoords || Verbs[DragPoint+1] == PT_MOVETO)
00677             {
00678                 // This is the last element in the sub-path
00679                 i = DragPoint;
00680                 if (!EditPath.IsSubPathClosed(i))
00681                 {
00682                     INT32 PrevEndpoint = i;
00683                     if (EditPath.FindPrevEndPoint(&PrevEndpoint))
00684                     {
00685                         EditPath.FindStartOfSubPath(&i);            // i is the index of the start of the sub-path
00686             
00687                         // If this sub-path is one element long then closing it is not allowed
00688                         if ((i != PrevEndpoint) && PointerBlobRect.ContainsCoord(Coords[i]))
00689                         {
00690                             PointerPos.x = Coords[i].x + LastMousePos.x - Coords[DragPoint].x;
00691                             PointerPos.y = Coords[i].y + LastMousePos.y - Coords[DragPoint].y;
00692                             EndSnapped = TRUE;
00693                         }
00694                     }
00695                 }
00696             }
00697         }
00698         
00699         // That detects snapping to the opposite endpoint, but what about snapping to another
00700         // endpoint altogether? Time to check for that eventuality
00701         if (!EndSnapped && DragPoint != -1 && (Verbs[DragPoint] == PT_MOVETO || DragPoint+1 == NumCoords || Verbs[DragPoint+1] == PT_MOVETO) )
00702         {
00703             // Get a snapshot of the selection
00704             SelRange* Selected = GetApplication()->FindSelection();
00705             Node* pNode = Selected->FindFirst();
00706             INT32 SubIndex,SubEnd;
00707             while (pNode)
00708             {
00709                 if ((pNode->FindParentSpread() == StartSpread) && 
00710                     (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodePath)) /*&&
00711                     (pNode != OriginalPath)*/ )
00712                 {
00713                     Path* ThisPath = &(((NodePath*)pNode)->InkPath);
00714                     INT32 ThisNum = ThisPath->GetNumCoords();
00715                     SubIndex = 0;
00716                     SubEnd = 0;
00717                     while (SubIndex<ThisNum)        
00718                     {
00719                         DocCoord tempStart,tempEnd;
00720                         ThisPath->SetPathPosition(SubIndex);    // Set the path's internal pos index
00721                         if (ThisPath->GetSubPathEnds(&tempStart, &tempEnd))
00722                         {
00723                             SubEnd = ThisPath->GetPathPosition();
00724                             // Now SubIndex is the index of the first, and SubEnd is the index
00725                             // of the last element (N.B. might be a curveto, in which case add 2)
00726                             if (ThisPath->GetVerb() == PT_BEZIERTO)
00727                                 SubEnd+=2;
00728 
00729                             BOOL ClosedPath = (ThisPath->GetVerbArray()[SubEnd] & PT_CLOSEFIGURE) ;
00730 
00731                             // Now compare start and end with pointer position, but not if this
00732                             // subpath is the currently dragging subpath
00733                             if ( !ClosedPath &&
00734                                  ( (pNode != OriginalPath) ||
00735                                    ((pNode == OriginalPath) && (DragPoint != SubIndex) && (DragPoint != SubEnd)) ) )
00736                             {
00737                                 if (PointerBlobRect.ContainsCoord(tempStart))
00738                                 {
00739                                     break;
00740                                 }
00741                                 if (PointerBlobRect.ContainsCoord(tempEnd))
00742                                 {
00743                                     SubIndex = SubEnd;
00744                                     break;
00745                                 }
00746                             }
00747                         }
00748                         else
00749                         {
00750                             SubEnd = ThisPath->GetPathPosition();
00751                         }
00752                         SubIndex = SubEnd+1;        // Points at start of next subpath
00753                                                     // or top of whole path
00754                     }
00755                     // if SubIndex < ThisNum we must have found a matching coord
00756                     // so that's what we'll snap to
00757                     if (SubIndex < ThisNum)
00758                         break;              // Exit the outer while condition
00759                 }
00760                 pNode = Selected->FindNext(pNode);
00761             }
00762             // At this point, if pNode == NULL we haven't snapped to anything
00763             // otherwise pNode is the path we've snapped to, and SubIndex is the index
00764             // of the point we've snapped to
00765             if (pNode)
00766             {
00767                 // if the snap to path is the path being edited check that it has the same number of points!
00768                 if (((NodePath*)pNode == OriginalPath) && (OriginalPath->InkPath.GetNumCoords() != EditPath.GetNumCoords()))
00769                 {
00770 //                  PATRACE( _T("Attempted to join an edited path!\n"));
00771                     // The problem is that if the OriginalPath has been edited by adding an endpoint then
00772                     // SnapToIndex will be wrong as it counted along the original path, not the edited one....
00773                 }
00774                 else
00775                 {
00776                     SnapToAnother = TRUE;
00777                     SnapToPath = (NodePath*)pNode;
00778                     SnapToIndex = SubIndex;
00779                     SnapToPath->InkPath.SetPathPosition(SnapToIndex);
00780                     PointerPos.x = SnapToPath->InkPath.GetCoord().x + LastMousePos.x - Coords[DragPoint].x;
00781                     PointerPos.y = SnapToPath->InkPath.GetCoord().y + LastMousePos.y - Coords[DragPoint].y;
00782                 }
00783             }
00784         }
00785 
00786         // We don't allow closed paths consiting of two straight segments
00787         if (EndSnapped && NumCoords < 4)
00788             EndSnapped = FALSE;
00789             
00790         if ((EndSnapped || SnapToAnother) && (pMoveCursor != NULL))
00791             ChangeCursor(pCloseCursor);
00792         else
00793             ChangeCursor(pCrossHairCursor);
00794         
00795         // Now constrain the mouse point
00796         ERROR3IF(ConstrainPoint == DocCoord(-1,-1),"ConstrainPoint wasn't set");
00797         ERROR3IF(ConstrainPrevPoint == DocCoord(-1,-1),"ConstrainPrevPoint wasn't set");
00798         ERROR3IF(ConstrainNextPoint == DocCoord(-1,-1),"ConstrainNextPoint wasn't set");
00799         if (!EndSnapped && ClickMods.Constrain)
00800         {
00801             if (ClickMods.Adjust)
00802             {
00803                 if (ClickMods.Alternative1 || ClickMods.Alternative2)
00804                     DocView::ConstrainToAngle(ConstrainNextPoint, &PointerPos);
00805                 else
00806                     DocView::ConstrainToAngle(ConstrainPrevPoint, &PointerPos);
00807             }
00808             else
00809                 DocView::ConstrainToAngle(ConstrainPoint, &PointerPos);
00810         }
00811 
00812         // Only snap to grid if we didn't snap to the end of a path
00813         if (!EndSnapped)
00814             DocView::SnapCurrent(StartSpread,&PointerPos);
00815 
00816         // This is the bit where we go off and re-calculate the paths position,
00817         // based on how much the mouse has moved
00818         DocCoord Offset;
00819         Offset.x = PointerPos.x - LastMousePos.x;
00820         Offset.y = PointerPos.y - LastMousePos.y;
00821 
00822         RecalculatePath(Offset, EndSnapped, DragPoint);
00823 
00824         if (EditObjChange.ChangeMask.EorBlobs)
00825         {
00826             ChangeCode Chge = EditObjChange.RenderChangedBlobs(OriginalPath,this,&EditPath,StartSpread,TRUE);
00827             if (Chge!=CC_OK)
00828             {
00829                 // replace the old edit path
00830                 EditPath.CopyPathDataFrom(&TempPath);
00831                 RenderPathEditBlobs( EditPath.GetBoundingRect(), StartSpread );
00832                 return;
00833             }
00834         }
00835 
00836         // Update the Last Mouse Position
00837         LastMousePos = PointerPos;
00838 
00839         // Draw in the new version of the path
00840         RenderPathEditBlobs( EditPath.GetBoundingRect(), StartSpread );
00841 
00842         // If there is just one selected point being dragged then we should show
00843         // its new position in the line tool infobar.
00844         if (UpdatePoint != -1)
00845             BROADCAST_TO_ALL(PathEditedMsg(&EditPath, pSpread, UpdatePoint));
00846 
00847         SetStatusLineHelp();
00848     }
00849     else
00850     {   
00851         NodeListItem* pCurrentOrig = (NodeListItem*) OriginalPaths.GetHead ();
00852         NodeListItem* pCurrentEdit = (NodeListItem*) EditPaths.GetHead ();
00853         NodeListItem* pCurrentChange = (NodeListItem*) ObjChanges.GetHead ();
00854         NodeListItem* pCurrentDragS = (NodeListItem*) PathsDragStarted.GetHead ();
00855 
00856         DocCoord Offset(0,0);
00857 
00858         while ((pCurrentOrig) && (pCurrentEdit) && (pCurrentChange) && (pCurrentDragS))
00859         {
00860             NodePath* pOrigPath = (NodePath*) (pCurrentOrig->pNode);
00861             Path* pEditPath = (Path*) (pCurrentEdit->pNode);
00862             ObjChangePathEdit* pObjChange = (ObjChangePathEdit*) (pCurrentChange->pNode);
00863             BOOL* pDragStarted = (BOOL*) (pCurrentDragS->pNode);
00864 
00866 
00867             // If drag has moved onto a different spread, convert the coord to be relative to the
00868             // original spread.
00869             if (pSpread != StartSpread)
00870                 PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
00871 
00872             Path TempPath;
00873 
00874             // inform the parent a change is happening
00875             if (pObjChange->ChangeMask.EorBlobs)
00876             {
00877                 ChangeCode Chge = pObjChange->RenderCurrentBlobs(pOrigPath,this,pEditPath,StartSpread,TRUE);
00878                 if (Chge!=CC_OK)
00879                     return;
00880                 // Create a local copy of the edit path
00881                 if (!TempPath.Initialise(pEditPath->GetNumCoords(), 12))
00882                     return;
00883                 TempPath.CopyPathDataFrom(pEditPath);
00884             }
00885 
00886             // Rub out the old EORed version of the path
00887             if (*pDragStarted == TRUE)
00888             {
00889                 RenderPathEditBlobs(pEditPath, /*pEditPath->GetBoundingRect(),*/ StartSpread );
00890             }
00891             else
00892             {
00893                 *pDragStarted = TRUE;
00894 //              pCurrentDragS->pNode = (Node*) pDragStarted;
00895             }
00896 
00897             DocRect PointerBlobRect;
00898             GetApplication()->GetBlobManager()->GetBlobRect(PointerPos,&PointerBlobRect);
00899 
00900             DocCoord* Coords = pEditPath->GetCoordArray();
00901 //          PathFlags* Flags = pEditPath->GetFlagArray();
00902             PathVerb* Verbs = pEditPath->GetVerbArray();
00903             INT32 NumCoords = pEditPath->GetNumCoords();
00904 
00905             if (pCurrentOrig == (NodeListItem*) OriginalPaths.GetHead ())
00906             {
00907                 // CGS:  all of the operations to do with snapping to grid, snapping to opposite
00908                 // end point and snapping to another lines endpoint are relevant only for
00909                 // that control point that the drag was initiated from ....
00910 
00911                 // Here is where we do all the snapping to grids and endpoints
00912                 // and constrain. First we check for snapping to line ends, then
00913                 // we check constrain, then we check grid snapping
00914 
00915                 EndSnapped = FALSE;         // TRUE if this point snaps to the other end
00916                 SnapToAnother = FALSE;      // TRUE if this point snaps to another line end
00917                 SnapToLineOrCurve = FALSE;  // No snapped other line
00918 
00919                 // We might be snapping to an endpoint if the point we're dragging around is either
00920                 // a moveto, or is the last point in the subpath. We will also only snap if there is
00921                 // only one point selected on the path
00922 
00923                 INT32 i;
00924                 if (DragPoint != -1)
00925                 {
00926                     // Look to see if the selected point is a real endpoint
00927                     if (Verbs[DragPoint] == PT_MOVETO)
00928                     {
00929                         // This is the start element of the path
00930                         i = DragPoint;
00931                         if (!pEditPath->IsSubPathClosed(i))
00932                         {
00933                             INT32 NextEndpoint = i;
00934                             if (pEditPath->FindNextEndPoint(&NextEndpoint))
00935                             {
00936                                 pEditPath->FindEndElOfSubPath(&i);      // i indexes to the end element
00937 
00938                                 // If this sub-path is one element long then closing it is not allowed
00939                                 if ((i != NextEndpoint) && PointerBlobRect.ContainsCoord(Coords[i]))
00940                                 {
00941                                     PointerPos.x = Coords[i].x + LastMousePos.x - Coords[DragPoint].x;
00942                                     PointerPos.y = Coords[i].y + LastMousePos.y - Coords[DragPoint].y;
00943                                     EndSnapped = TRUE;
00944                                 }
00945                             }
00946                         }
00947                     }
00948                     else if (DragPoint+1 == NumCoords || Verbs[DragPoint+1] == PT_MOVETO)
00949                     {
00950                         // This is the last element in the sub-path
00951                         i = DragPoint;
00952                         if (!pEditPath->IsSubPathClosed(i))
00953                         {
00954                             INT32 PrevEndpoint = i;
00955                             if (pEditPath->FindPrevEndPoint(&PrevEndpoint))
00956                             {
00957                                 pEditPath->FindStartOfSubPath(&i);          // i is the index of the start of the sub-path
00958                     
00959                                 // If this sub-path is one element long then closing it is not allowed
00960                                 if ((i != PrevEndpoint) && PointerBlobRect.ContainsCoord(Coords[i]))
00961                                 {
00962                                     PointerPos.x = Coords[i].x + LastMousePos.x - Coords[DragPoint].x;
00963                                     PointerPos.y = Coords[i].y + LastMousePos.y - Coords[DragPoint].y;
00964                                     EndSnapped = TRUE;
00965                                 }
00966                             }
00967                         }
00968                     }
00969                 }
00970             
00971                 // That detects snapping to the opposite endpoint, but what about snapping to another
00972                 // endpoint altogether? Time to check for that eventuality
00973                 if (!EndSnapped && DragPoint != -1 && (Verbs[DragPoint] == PT_MOVETO || DragPoint+1 == NumCoords || Verbs[DragPoint+1] == PT_MOVETO) )
00974                 {
00975                     // Get a snapshot of the selection
00976                     SelRange* Selected = GetApplication()->FindSelection();
00977 
00978                     // do something extra in here ....
00979 
00980                     Node* pNode = Selected->FindFirst();
00981                     INT32 SubIndex,SubEnd;
00982                     while (pNode)
00983                     {
00985                         if ((pNode->FindParentSpread() == StartSpread) && 
00986                             (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodePath)) /*&&
00987                     (pNode != OriginalPath)*/ )
00988                         {
00990                             Path* ThisPath = &(((NodePath*)pNode)->InkPath);
00991                             INT32 ThisNum = ThisPath->GetNumCoords();
00992                             SubIndex = 0;
00993                             SubEnd = 0;
00994                             while (SubIndex<ThisNum)        
00995                             {
00996                                 DocCoord tempStart,tempEnd;
00997                                 ThisPath->SetPathPosition(SubIndex);    // Set the path's internal pos index
00998                                 if (ThisPath->GetSubPathEnds(&tempStart, &tempEnd))
00999                                 {
01000                                     SubEnd = ThisPath->GetPathPosition();
01001                                     // Now SubIndex is the index of the first, and SubEnd is the index
01002                                     // of the last element (N.B. might be a curveto, in which case add 2)
01003                                     if (ThisPath->GetVerb() == PT_BEZIERTO)
01004                                         SubEnd+=2;
01005 
01006                                     BOOL ClosedPath = (ThisPath->GetVerbArray()[SubEnd] & PT_CLOSEFIGURE) ;
01007 
01008                                     // Now compare start and end with pointer position, but not if this
01009                                     // subpath is the currently dragging subpath
01010                                     if ( !ClosedPath &&
01011                                          ( (pNode != OriginalPath) ||
01012                                            ((pNode == OriginalPath) && (DragPoint != SubIndex) && (DragPoint != SubEnd)) ) )
01013                                     {
01014                                         if (PointerBlobRect.ContainsCoord(tempStart))
01015                                         {
01016                                             break;
01017                                         }
01018                                         if (PointerBlobRect.ContainsCoord(tempEnd))
01019                                         {
01020                                             SubIndex = SubEnd;
01021                                             break;
01022                                         }
01023                                     }
01024                                 }
01025                                 else
01026                                 {
01027                                     SubEnd = ThisPath->GetPathPosition();
01028                                 }
01029                                 SubIndex = SubEnd+1;        // Points at start of next subpath
01030                                                             // or top of whole path
01031                             }
01032                             // if SubIndex < ThisNum we must have found a matching coord
01033                             // so that's what we'll snap to
01034                             if (SubIndex < ThisNum)
01035                                 break;              // Exit the outer while condition
01036                         }
01037                         pNode = Selected->FindNext(pNode);
01038                     }
01039                     // At this point, if pNode == NULL we haven't snapped to anything
01040                     // otherwise pNode is the path we've snapped to, and SubIndex is the index
01041                     // of the point we've snapped to
01042                     if (pNode)
01043                     {
01044                         // if the snap to path is the path being edited check that it has the same number of points!
01045                         if (((NodePath*)pNode == pOrigPath) && (pOrigPath->InkPath.GetNumCoords() != pEditPath->GetNumCoords()))
01046                         {
01047 //                          PATRACE( _T("Attempted to join an edited path!\n"));
01048                             // The problem is that if the OriginalPath has been edited by adding an endpoint then
01049                             // SnapToIndex will be wrong as it counted along the original path, not the edited one....
01050                         }
01051                         else
01052                         {
01053                             SnapToAnother = TRUE;
01054                             SnapToPath = (NodePath*)pNode;
01055                             SnapToIndex = SubIndex;
01056                             SnapToPath->InkPath.SetPathPosition(SnapToIndex);
01057                             PointerPos.x = SnapToPath->InkPath.GetCoord().x + LastMousePos.x - Coords[DragPoint].x;
01058                             PointerPos.y = SnapToPath->InkPath.GetCoord().