OpNodePathEditBlob Class Reference

This operation is started by the NodePath when it receives a drag message from one of the tools. It uses it to drag one (or many) of the paths control points around the screen to edit the path. As the control points are dragged about the new version of the path, corosponding to the new blob positions, will be EOR rendered to the window. More...

#include <pathedit.h>

Inheritance diagram for OpNodePathEditBlob:

SelOperation UndoableOperation Operation MessageHandler ListItem CCObject SimpleCCObject OpNewPath OpNodePathAddEndpoint OpNodePathEditControlBlob OpReshapeOrAddPoint OpCloseNodePaths List of all members.

Public Member Functions

 OpNodePathEditBlob ()
void DoStartDragEdit (NodePath *, DocCoord Anchor, Spread *)
 This is called to start a drag operation on an endpoint on a path.
virtual void DragPointerMove (DocCoord Pos, ClickModifiers Mods, Spread *pSpread, BOOL bSolidDrag)
 This is called every time the mouse moves, during a drag.
virtual void DragFinished (DocCoord Pos, ClickModifiers Mods, Spread *pSpread, BOOL Success, BOOL bSolidDrag)
 This is called when a drag operation finishes.
virtual void RenderDragBlobs (DocRect, Spread *, BOOL bSolidDrag)
 Renders the new version of the path to the window. It makes use of flags in the path to determine which segments of the path need to be rendered.

Static Public Member Functions

static BOOL Init ()
 Adds the operation to the list of all known operations.
static OpState GetState (String_256 *Description, OpDescriptor *)
 Find out the state of the operation at the specific time.

Protected Member Functions

BOOL BuildEditPath ()
 Builds a copy of the path that we can edit, without destroying the original data. Also sets the NeedToRender flags for EOR display.
BOOL BuildEditPaths ()
 Builds a copy of each path in the selection that we can edit, without destroying the original data. Also sets the NeedToRender flags for EOR display.
BOOL CopyEditedPathBack ()
 Copies the contents of the edited path back into the original path.
BOOL CopyEditedPathBack (NodePath *pOrigPath, Path *pEditPath)
 Copies the contents of the edited path back into the original path.
BOOL CopyNeedToRenderFlags ()
 This function is used to copy the NeedToRender flags from EditPath to OriginalPath. This are then used in optimised region invalidation.
BOOL CopyNeedToRenderFlags (NodePath *pOrigPath, Path *pEditPath)
 This function is used to copy the NeedToRender flags from EditPath to OriginalPath. This are then used in optimised region invalidation.
void RecalculatePath (DocCoord Offset, BOOL SnapEnds=FALSE, INT32 SnapIndex=0)
 This goes through the path, moves all the selected coords by the offset and then performs some magic to smooth the rest of the path round the changes if it needs it.
void RecalculatePaths (Path *pEditPath, DocCoord Offset, BOOL SnapEnds=FALSE, INT32 SnapIndex=0)
 This goes through the path, moves all the selected coords by the offset and then performs some magic to smooth the rest of the path round the changes if it needs it.
void SnapEndsTogether ()
 Sets the closefigure flag in the last element in the subpath. Also turns off the rotate bit in the points we're snapping together if their smoothing bits are also turned off. This prevents us creating a cusp that has rotate flags set.
void SnapEndsTogether (Path *pEditPath)
 Sets the closefigure flag in the last element in the subpath. Also turns off the rotate bit in the points we're snapping together if their smoothing bits are also turned off. This prevents us creating a cusp that has rotate flags set.
BOOL JoinWithOtherPath ()
 Looks at the member variables dealing with snapping to another path and joins the paths together. It will always join the other path to the original path, keeping the original path's attributes intact. NOTE: This routine will alter the OriginalPath member variable if it joins paths, because it has to make a copy of the path for undo purposes.
BOOL JoinWithOtherPath (NodePath **pOrigPath)
 Looks at the member variables dealing with snapping to another path and joins the paths together. It will always join the other path to the original path, keeping the original path's attributes intact. NOTE: This routine will alter the OriginalPath member variable if it joins paths, because it has to make a copy of the path for undo purposes.
BOOL FillPathIfEndsSnapped ()
 Will look at the EndSnapped flag and set the IsFilled bit in the OriginalPath Builds undo information. Returns FALSE if it couldn't build the Undo info.
BOOL FillPathIfEndsSnapped (NodePath *pOrigPath)
 Will look at the EndSnapped flag and set the IsFilled bit in the OriginalPath Builds undo information. Returns FALSE if it couldn't build the Undo info.
virtual void RenderDraggingBlobs (DocRect, Spread *)
 Call this function to render all the blobs on screen from this operation.
virtual void RenderDraggingBlobs (Path *pEditPath, Spread *)
 Call this function to render all the blobs on screen from this operation.
void RenderPathEditBlobs (DocRect Rect, Spread *pSpread)
void RenderPathEditBlobs (Path *pEditPath, Spread *pSpread)
 Call this function to render all the blobs on screen from this operation.
virtual void SetStatusLineHelp ()
 Updates the status line message to reflect the current situation.
NodeGroupGetGroupParentOfCurve ()
 To determine if the current selection is a blend on a curve.
NodeGroupGetGroupParentOfCurve (NodePath *pOrigPath)
 To determine if the current selection is a blend on a curve.
BOOL InsertChangeBlendStepsAction (NodeBlend *pNodeBlend)
 To adjust the number of steps in a blend on a path as a result of the path being edited (which is why its here rather than in the blend code). First the number of steps is calculated from the new path distance and if different a new action is inserted.
AttrBrushTypeGetAppliedBrush ()
 To get the brush attribute that is applied to the nodepath we are editing.
virtual BOOL EditBrushAttribute (INT32 FirstIndex, INT32 LastIndex, AttrBrushType *pAttrBrush)
 If we use the shape editor to edit a nodepath with an applied brush attribute and this brush attribute makes use of sampled pressure or time information then the brush needs to resample its data. So here we will insert a few actions to make that happen.
MILLIPOINT GetLengthOfPathSection (Path *pPath, INT32 FirstIndex, INT32 LastIndex)
 As above, if you want to know the distance between two points on the edit path then this function is for you.
MILLIPOINT GetDistanceToPathIndex (Path *pPath, INT32 Index)
 As above,.
void ChangeCursor (Cursor *cursor)
 Changes the mouse pointer to a new shape.
BOOL CreateCursors ()
 Creates the cursor objects for the path operations.
void DestroyCursors ()
 Deletes the path operation cursors.

Protected Attributes

SpreadStartSpread
DocCoord StartMousePos
DocCoord LastMousePos
DocCoord ConstrainPoint
DocCoord ConstrainPrevPoint
DocCoord ConstrainNextPoint
NodePathOriginalPath
Path EditPath
List OriginalPaths
List EditPaths
List PathsDragStarted
BOOL MultiplePaths
BOOL EndSnapped
BOOL SnapToAnother
NodePathSnapToPath
INT32 SnapToIndex
BOOL SnapToLineOrCurve
DocCoord SnapToCoords [4]
CursorpMoveCursor
CursorpCloseCursor
CursorpCrossHairCursor
CursorMyCurrentCursor
INT32 CurrentCursorID
ObjChangePathEdit EditObjChange
List ObjChanges
INT32 DragPoint
INT32 UpdatePoint
BOOL DragStarted

Detailed Description

This operation is started by the NodePath when it receives a drag message from one of the tools. It uses it to drag one (or many) of the paths control points around the screen to edit the path. As the control points are dragged about the new version of the path, corosponding to the new blob positions, will be EOR rendered to the window.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
1/7/93

Definition at line 143 of file pathedit.h.


Constructor & Destructor Documentation

OpNodePathEditBlob::OpNodePathEditBlob  ) 
 


Member Function Documentation

BOOL OpNodePathEditBlob::BuildEditPath  )  [protected]
 

Builds a copy of the path that we can edit, without destroying the original data. Also sets the NeedToRender flags for EOR display.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> - later attacked by Peter
Date:
17/02/94
Returns:
TRUE if it managed to build the path, FALSE if it failed

Errors: If it runs out of memory then it will return FALSE

Reimplemented in OpNodePathEditControlBlob, and OpReshapeOrAddPoint.

Definition at line 2197 of file pathedit.cpp.

02198 {
02199     // Make a copy of the original path
02200     UINT32 NumCoords = OriginalPath->InkPath.GetNumCoords();
02201     if (!EditPath.Initialise(NumCoords, 24))
02202         return FALSE;
02203     if (!EditPath.CopyPathDataFrom(&(OriginalPath->InkPath)))
02204         return FALSE;
02205 
02206     // Go though all the coords, with scary amounts of looking back and forwards
02207     UINT32 LastEndPoint = 0;                // The EndPoint before this one
02208     BOOL SetNextEndPoint = FALSE;       // TRUE if we want to set the next EndPoint to render
02209     BOOL SetNextNextEndPoint = FALSE;   // TRUE if we want the one after the next one to render
02210     PathFlags* Flags = EditPath.GetFlagArray();
02211     for (UINT32 i=0; i<NumCoords; i++)
02212     {
02213         // Make all the flags FALSE by default
02214         Flags[i].NeedToRender = FALSE;
02215 
02216         if (Flags[i].IsEndPoint)
02217         {
02218             // if the endpoint 2 elements back was selected and the last element was smooth
02219             // then we need to mark this point for rendering
02220             if (SetNextNextEndPoint)
02221             {
02222                 Flags[i].NeedToRender = TRUE;
02223                 SetNextNextEndPoint = FALSE;
02224             }
02225 
02226             // We have found an Endpoint, do we want to mark this one as renderable
02227             if (SetNextEndPoint)
02228             {
02229                 // As the last element was selected, this element needs to render
02230                 Flags[i].NeedToRender = TRUE;
02231                 SetNextEndPoint = FALSE;
02232 
02233                 // If the smooth flag is set then the next item needs to render as well
02234                 if (Flags[i].IsRotate || Flags[i].IsSmooth)
02235                     SetNextNextEndPoint = TRUE;
02236             }
02237 
02238             // If its selected, then its renderable
02239             if (Flags[i].IsSelected)
02240             {
02241                 Flags[i].NeedToRender = TRUE;
02242                 if (Flags[LastEndPoint].IsRotate || Flags[LastEndPoint].IsSmooth)
02243                     Flags[LastEndPoint].NeedToRender = TRUE;
02244 
02245                 // Set the flag for the next endpoint
02246                 SetNextEndPoint = TRUE;
02247             }
02248 
02249             LastEndPoint = i;
02250         }
02251     }
02252 
02253     return TRUE;
02254 }

BOOL OpNodePathEditBlob::BuildEditPaths  )  [protected]
 

Builds a copy of each path in the selection that we can edit, without destroying the original data. Also sets the NeedToRender flags for EOR display.

Author:
Chris_Snook (Xara Group Ltd) <camelotdev@xara.com> (based upon Rik/Peter)
Date:
17/02/94
Returns:
TRUE if it managed to build the paths, FALSE if it failed

Errors: If it runs out of memory then it will return FALSE

Definition at line 2107 of file pathedit.cpp.

02108 {
02109     NodeListItem* pCurrent = (NodeListItem*) OriginalPaths.GetHead ();
02110 
02111     while (pCurrent)
02112     {
02113         NodePath* pCurrentPath = (NodePath*) (pCurrent->pNode);
02114         Path* NewEditPath = new Path ();
02115 
02116         UINT32 NumCoords = pCurrentPath->InkPath.GetNumCoords();
02117 
02118         if (!NewEditPath->Initialise(NumCoords, 24))
02119             return FALSE;
02120         if (!NewEditPath->CopyPathDataFrom(&(pCurrentPath->InkPath)))
02121             return FALSE;
02122 
02123         // Go though all the coords, with scary amounts of looking back and forwards
02124         UINT32 LastEndPoint = 0;                // The EndPoint before this one
02125         BOOL SetNextEndPoint = FALSE;       // TRUE if we want to set the next EndPoint to render
02126         BOOL SetNextNextEndPoint = FALSE;   // TRUE if we want the one after the next one to render
02127         PathFlags* Flags = NewEditPath->GetFlagArray();
02128         
02129         for (UINT32 i=0; i<NumCoords; i++)
02130         {
02131             // Make all the flags FALSE by default
02132             Flags[i].NeedToRender = FALSE;
02133 
02134             if (Flags[i].IsEndPoint)
02135             {
02136                 // if the endpoint 2 elements back was selected and the last element was smooth
02137                 // then we need to mark this point for rendering
02138                 if (SetNextNextEndPoint)
02139                 {
02140                     Flags[i].NeedToRender = TRUE;
02141                     SetNextNextEndPoint = FALSE;
02142                 }
02143 
02144                 // We have found an Endpoint, do we want to mark this one as renderable
02145                 if (SetNextEndPoint)
02146                 {
02147                     // As the last element was selected, this element needs to render
02148                     Flags[i].NeedToRender = TRUE;
02149                     SetNextEndPoint = FALSE;
02150 
02151                     // If the smooth flag is set then the next item needs to render as well
02152                     if (Flags[i].IsRotate || Flags[i].IsSmooth)
02153                         SetNextNextEndPoint = TRUE;
02154                 }
02155 
02156                 // If its selected, then its renderable
02157                 if (Flags[i].IsSelected)
02158                 {
02159                     Flags[i].NeedToRender = TRUE;
02160                     if (Flags[LastEndPoint].IsRotate || Flags[LastEndPoint].IsSmooth)
02161                         Flags[LastEndPoint].NeedToRender = TRUE;
02162 
02163                     // Set the flag for the next endpoint
02164                     SetNextEndPoint = TRUE;
02165                 }
02166 
02167                 LastEndPoint = i;
02168             }
02169         }
02170 
02171         NodeListItem* pInsert = new NodeListItem;
02172         pInsert->pNode = (Node*) NewEditPath;
02173         
02174         EditPaths.AddTail (pInsert);//NewEditPath);
02175         
02176         pCurrent = (NodeListItem*) OriginalPaths.GetNext (pCurrent);
02177 
02178         //delete (pInsert);
02179 
02180 //      delete (NewEditPath);               // cleanup that is necessary
02181     }
02182     
02183     return (TRUE);
02184 }

void OpNodePathEditBlob::ChangeCursor Cursor cursor  )  [protected]
 

Changes the mouse pointer to a new shape.

Author:
Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/9/94
Parameters:
Pointer to the cursor to use now. [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
OpNodePathEditBlob::CreateCursors(), OpNodePathEditBlob::DestroyCursors()

Definition at line 4787 of file pathedit.cpp.

04788 {
04789     if (cursor != MyCurrentCursor)
04790     {   // only change if this cursor is different from the current cursor
04791         if (MyCurrentCursor != NULL)
04792         {   // If one of our cursors is on the stack then get it off
04793             CursorStack::GPop(CurrentCursorID); 
04794         }
04795         MyCurrentCursor = cursor;
04796         CurrentCursorID = CursorStack::GPush(cursor);
04797     }
04798 }   

BOOL OpNodePathEditBlob::CopyEditedPathBack NodePath pOrigPath,
Path pEditPath
[protected]
 

Copies the contents of the edited path back into the original path.

Author:
Chris_Snook (Xara Group Ltd) <camelotdev@xara.com> (based upon Will/Peter)
Date:
20/4/2000
Returns:
TRUE if it worked, FALSE if it failed

Definition at line 2441 of file pathedit.cpp.

02442 {
02443     // Now to do some undo information. To do this, I have to look at each element in the path
02444     // and each element in the copy, and see which ones differ. I then have to work out how many
02445     // elements that was, and create a path to contain those elements, along with an array of indices 
02446     // telling me where those elements came from
02447     INT32 NumElements = pOrigPath->InkPath.GetNumCoords();
02448     PathVerb* SourceVerbs = pOrigPath->InkPath.GetVerbArray();
02449     DocCoord* SourceCoords = pOrigPath->InkPath.GetCoordArray();
02450     PathFlags* SourceFlags = pOrigPath->InkPath.GetFlagArray();
02451     PathVerb* DestVerbs = pEditPath->GetVerbArray();
02452     DocCoord* DestCoords = pEditPath->GetCoordArray();
02453     PathFlags* DestFlags = pEditPath->GetFlagArray();
02454 
02455     INT32 ChangedElements = 0;
02456     INT32 i;    
02457 
02458     for (i=0;i<NumElements;i++)
02459     {
02460         if (SourceVerbs[i] != DestVerbs[i] || 
02461             SourceCoords[i] != DestCoords[i] || 
02462             SourceFlags[i] != DestFlags[i]
02463             )
02464             ChangedElements++;
02465     }
02466 
02467     // ChangedElements is the number of elements in this path that will change.
02468     // We have to create three arrays to contain the changed elements, plus one array
02469     // to tell me where the elements should go (the indices)
02470 
02471     // I also have to create an action object to contain these arrays. I have to create the action
02472     // object first because that does all the work of deciding if there's enough memory in the 
02473     // undo buffer to store the action, and prompting the user accordingly of there isn't
02474 
02475     if (ChangedElements > 0)
02476     {
02477         ModifyPathAction* ModAction;
02478     
02479         ActionCode Act;
02480         Act = ModifyPathAction::Init(this, &UndoActions, ChangedElements, (Action**)(&ModAction));
02481         if (Act == AC_FAIL)
02482         {
02483             FailAndExecute();
02484             End();
02485             return FALSE;
02486         }
02487 
02488         PathVerb* ChangedVerbs=NULL;
02489         DocCoord* ChangedCoords=NULL;
02490         PathFlags* ChangedFlags=NULL;
02491         INT32* ChangedIndices=NULL;
02492 
02493         // If the function returned AC_NO_RECORD we shouldn't record any undo information in the action
02494         // NOTE - during unwind all actions return AC_OK with a NULL ModAction pointer!!
02495         if ((Act!=AC_NORECORD) && (ModAction!=NULL))
02496         {
02497             // This next bit is a bit hellish. Any one of these four allocations can fail, in which case 
02498             // we have to tidy up afterwards. Cue a lot of nested ifs and elses.
02499 
02500             ALLOC_WITH_FAIL(ChangedVerbs,(PathVerb*) CCMalloc(ChangedElements * sizeof(PathVerb)),this);
02501             if (ChangedVerbs)
02502             {
02503                 ALLOC_WITH_FAIL(ChangedCoords,(DocCoord*) CCMalloc(ChangedElements * sizeof(DocCoord)),this);
02504                 if (ChangedCoords)
02505                 {
02506                     ALLOC_WITH_FAIL(ChangedFlags,(PathFlags*) CCMalloc(ChangedElements * sizeof(PathFlags)),this);
02507                     if (ChangedFlags)
02508                     {
02509                         ALLOC_WITH_FAIL(ChangedIndices,(INT32*) CCMalloc(ChangedElements * sizeof(INT32)),this);
02510                         if (!ChangedIndices)
02511                         {
02512                             CCFree( ChangedFlags );
02513                             CCFree( ChangedCoords );
02514                             CCFree( ChangedVerbs);
02515                             FailAndExecute();
02516                             End();
02517                             return FALSE;
02518                         }
02519                     }
02520                     else
02521                     {
02522                         CCFree( ChangedCoords );
02523                         CCFree( ChangedVerbs );
02524                         FailAndExecute();
02525                         End();
02526                         return FALSE;
02527 
02528                     }
02529                 }
02530                 else
02531                 {
02532                     CCFree( ChangedVerbs);
02533                     FailAndExecute();
02534                     End();
02535                     return FALSE;
02536 
02537                 }
02538             }
02539 
02540             // Now to put the undo data into the undo action
02541             INT32 index = 0;
02542             for (i=0;i<NumElements;i++)
02543             {
02544                 if (SourceVerbs[i] != DestVerbs[i] || 
02545                     SourceCoords[i] != DestCoords[i] || 
02546                     SourceFlags[i] != DestFlags[i]
02547                     )
02548                 {
02549                     ChangedVerbs[index] = SourceVerbs[i];
02550                     ChangedFlags[index] = SourceFlags[i];
02551                     ChangedCoords[index] = SourceCoords[i];
02552                     ChangedIndices[index] = i;
02553                     index++;
02554                 }
02555             }
02556 
02557             // Now we've allocated the arrays, let's tell the action about 'em
02558             ModAction->StoreArrays(ChangedVerbs, ChangedFlags, ChangedCoords, ChangedIndices, pOrigPath);
02559         }
02560     }
02561         
02562     if (!pOrigPath->InkPath.CopyPathDataFrom(pEditPath))
02563         return FALSE;
02564     else
02565         return TRUE;
02566 }

BOOL OpNodePathEditBlob::CopyEditedPathBack  )  [protected]
 

Copies the contents of the edited path back into the original path.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
17/02/94
Returns:
TRUE if it worked, FALSE if it failed

Definition at line 2269 of file pathedit.cpp.

02270 {
02271     // Now to do some undo information. To do this, I have to look at each element in the path
02272     // and each element in the copy, and see which ones differ. I then have to work out how many
02273     // elements that was, and create a path to contain those elements, along with an array of indices 
02274     // telling me where those elements came from
02275     INT32 NumElements = OriginalPath->InkPath.GetNumCoords();
02276     PathVerb* SourceVerbs = OriginalPath->InkPath.GetVerbArray();
02277     DocCoord* SourceCoords = OriginalPath->InkPath.GetCoordArray();
02278     PathFlags* SourceFlags = OriginalPath->InkPath.GetFlagArray();
02279     PathVerb* DestVerbs = EditPath.GetVerbArray();
02280     DocCoord* DestCoords = EditPath.GetCoordArray();
02281     PathFlags* DestFlags = EditPath.GetFlagArray();
02282 
02283     // DY now we want to keep track of the indexes of the changed section
02284     INT32 ChangedElements = 0;
02285     INT32 FirstChanged = -1;
02286     INT32 LastChanged  = -1;
02287 
02288     INT32 i;
02289     for (i=0;i<NumElements;i++)
02290     {
02291         if (SourceVerbs[i] != DestVerbs[i] || SourceCoords[i] != DestCoords[i] || 
02292             SourceFlags[i] != DestFlags[i] )
02293         {
02294             ChangedElements++;
02295 
02296         //  if (SourceCoords[i] != DestCoords[i])
02297             {
02298                 // if we have not yet set the first changed index then set it
02299                 if (FirstChanged == -1)
02300                     FirstChanged = i;
02301 
02302                 // always set the last changed index
02303                 LastChanged = i;
02304             }
02305         }
02306 
02307     }
02308     
02309 
02310     // ChangedElements is the number of elements in this path that will change.
02311     // We have to create three arrays to contain the changed elements, plus one array
02312     // to tell me where the elements should go (the indices)
02313 
02314     // I also have to create an action object to contain these arrays. I have to create the action
02315     // object first because that does all the work of deciding if there's enough memory in the 
02316     // undo buffer to store the action, and prompting the user accordingly of there isn't
02317 
02318     if (ChangedElements > 0)
02319     {
02320         // do the brush editing here
02321         // the following block deals with editing path blobs.  For some reason I could not get a
02322         // sensible value for the distance of the edited path from the changed indexes, however
02323         // it works quite nicely by finding the contraining points.
02324         if (DragPoint != -1)
02325         {
02326             // this section only applies for editing blobs (as reshaping has no drag point)
02327             FirstChanged = DragPoint;
02328             LastChanged = DragPoint;
02329             OriginalPath->InkPath.FindPrevEndPoint(&FirstChanged);
02330             OriginalPath->InkPath.FindNextEndPoint(&LastChanged);
02331         }
02332         
02333         AttrBrushType* pAttrBrush = GetAppliedBrush();
02334         if (pAttrBrush != NULL)
02335             EditBrushAttribute(FirstChanged, LastChanged, pAttrBrush);
02336 
02337         ModifyPathAction* ModAction;
02338     
02339         ActionCode Act;
02340         Act = ModifyPathAction::Init(this, &UndoActions, ChangedElements, (Action**)(&ModAction));
02341         if (Act == AC_FAIL)
02342         {
02343             FailAndExecute();
02344             End();
02345             return FALSE;
02346         }
02347 
02348         PathVerb* ChangedVerbs=NULL;
02349         DocCoord* ChangedCoords=NULL;
02350         PathFlags* ChangedFlags=NULL;
02351         INT32* ChangedIndices=NULL;
02352 
02353         // If the function returned AC_NO_RECORD we shouldn't record any undo information in the action
02354         // NOTE - during unwind all actions return AC_OK with a NULL ModAction pointer!!
02355         if ((Act!=AC_NORECORD) && (ModAction!=NULL))
02356         {
02357             // This next bit is a bit hellish. Any one of these four allocations can fail, in which case 
02358             // we have to tidy up afterwards. Cue a lot of nested ifs and elses.
02359 
02360             ALLOC_WITH_FAIL(ChangedVerbs,(PathVerb*) CCMalloc(ChangedElements * sizeof(PathVerb)),this);
02361             if (ChangedVerbs)
02362             {
02363                 ALLOC_WITH_FAIL(ChangedCoords,(DocCoord*) CCMalloc(ChangedElements * sizeof(DocCoord)),this);
02364                 if (ChangedCoords)
02365                 {
02366                     ALLOC_WITH_FAIL(ChangedFlags,(PathFlags*) CCMalloc(ChangedElements * sizeof(PathFlags)),this);
02367                     if (ChangedFlags)
02368                     {
02369                         ALLOC_WITH_FAIL(ChangedIndices,(INT32*) CCMalloc(ChangedElements * sizeof(INT32)),this);
02370                         if (!ChangedIndices)
02371                         {
02372                             CCFree( ChangedFlags );
02373                             CCFree( ChangedCoords );
02374                             CCFree( ChangedVerbs);
02375                             FailAndExecute();
02376                             End();
02377                             return FALSE;
02378                         }
02379                     }
02380                     else
02381                     {
02382                         CCFree( ChangedCoords );
02383                         CCFree( ChangedVerbs );
02384                         FailAndExecute();
02385                         End();
02386                         return FALSE;
02387 
02388                     }
02389                 }
02390                 else
02391                 {
02392                     CCFree( ChangedVerbs);
02393                     FailAndExecute();
02394                     End();
02395                     return FALSE;
02396 
02397                 }
02398             }
02399 
02400             // Now to put the undo data into the undo action
02401             INT32 index = 0;
02402             for (i=0;i<NumElements;i++)
02403             {
02404                 if (SourceVerbs[i] != DestVerbs[i] || 
02405                     SourceCoords[i] != DestCoords[i] || 
02406                     SourceFlags[i] != DestFlags[i]
02407                     )
02408                 {
02409                     ChangedVerbs[index] = SourceVerbs[i];
02410                     ChangedFlags[index] = SourceFlags[i];
02411                     ChangedCoords[index] = SourceCoords[i];
02412                     ChangedIndices[index] = i;
02413                     index++;
02414                 }
02415             }
02416 
02417             // Now we've allocated the arrays, let's tell the action about 'em
02418             ModAction->StoreArrays(ChangedVerbs, ChangedFlags, ChangedCoords, ChangedIndices, OriginalPath);
02419         }
02420     }
02421         
02422     if (!OriginalPath->InkPath.CopyPathDataFrom(&EditPath))
02423         return FALSE;
02424     else
02425         return TRUE;
02426 }

BOOL OpNodePathEditBlob::CopyNeedToRenderFlags NodePath pOrigPath,
Path pEditPath
[protected]
 

This function is used to copy the NeedToRender flags from EditPath to OriginalPath. This are then used in optimised region invalidation.

Author:
Chris_Snook (Xara Group Ltd) <camelotdev@xara.com> (based upon Peter)
Date:
20/04/95
Parameters:
- [INPUTS]
(see below) [OUTPUTS]
Returns:
TRUE if the flags have been copied; FALSE if the paths were different lengths

Definition at line 2618 of file pathedit.cpp.

02619 {
02620     const INT32 OrigLength = pOrigPath->InkPath.GetNumCoords();
02621     const INT32 EditLength = pEditPath->GetNumCoords();
02622 
02623     if (EditLength != OrigLength)
02624         return FALSE;
02625 
02626     PathFlags* EditFlags = pEditPath->GetFlagArray();
02627     PathFlags* OrigFlags = pOrigPath->InkPath.GetFlagArray();
02628         
02629     for (INT32 loop = 0; loop < EditLength; loop ++)
02630     {
02631         OrigFlags[loop].NeedToRender = EditFlags[loop].NeedToRender;
02632     }
02633 
02634     return TRUE;
02635 }

BOOL OpNodePathEditBlob::CopyNeedToRenderFlags  )  [protected]
 

This function is used to copy the NeedToRender flags from EditPath to OriginalPath. This are then used in optimised region invalidation.

Author:
Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
Date:
10/03/95
Parameters:
- [INPUTS]
(see below) [OUTPUTS]
Returns:
TRUE if the flags have been copied; FALSE if the paths were different lengths

Definition at line 2583 of file pathedit.cpp.

02584 {
02585     const INT32 OrigLength = OriginalPath->InkPath.GetNumCoords();
02586     const INT32 EditLength = EditPath.GetNumCoords();
02587 
02588     if (EditLength != OrigLength)
02589         return FALSE;
02590 
02591     PathFlags* EditFlags = EditPath.GetFlagArray();
02592     PathFlags* OrigFlags = OriginalPath->InkPath.GetFlagArray();
02593         
02594     for (INT32 loop = 0; loop < EditLength; loop ++)
02595     {
02596         OrigFlags[loop].NeedToRender = EditFlags[loop].NeedToRender;
02597     }
02598 
02599     return TRUE;
02600 }

BOOL OpNodePathEditBlob::CreateCursors  )  [protected]
 

Creates the cursor objects for the path operations.

Author:
Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/9/94
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
FALSE if the cursors wern't created, TRUE if they were.

Errors: -

See also:
OpNodePathEditBlob::DestroyCursors()

Definition at line 4816 of file pathedit.cpp.

04817 {
04818     if (pMoveCursor == NULL)
04819     {   // If already created then don't create a new set.
04820         MyCurrentCursor = NULL;
04821         pMoveCursor = new Cursor(TOOLID_BEZTOOL, _R(IDC_MOVEBEZIERCURSOR));
04822         pCloseCursor = new Cursor(TOOLID_BEZTOOL, _R(IDC_CLOSEPATHCURSOR));
04823         pCrossHairCursor = new Cursor(TOOLID_BEZTOOL, _R(IDC_CROSSHAIRCURSOR));
04824         // See if any of them failed
04825         if ((!pMoveCursor || !pMoveCursor->IsValid())
04826                 || (!pCloseCursor || !pCloseCursor->IsValid())
04827                 || (!pCrossHairCursor || !pCrossHairCursor->IsValid()))
04828         {   
04829             // They did fail, so clean up
04830             TRACE( _T("Cursors not created in OpNodePathEditBlob::CreateCursors\n"));
04831             delete pMoveCursor;
04832             delete pCloseCursor;
04833             delete pCrossHairCursor;
04834             pMoveCursor = NULL;
04835             return FALSE;           
04836         }
04837     }
04838     return TRUE;
04839 }

void OpNodePathEditBlob::DestroyCursors  )  [protected]
 

Deletes the path operation cursors.

Author:
Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/9/94
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
OpNodePathEditBlob::CreateCursors()

Definition at line 4858 of file pathedit.cpp.

04859 {
04860     if (pMoveCursor != NULL)
04861     {   // If one is NULL then the rest don't exist
04862         if (MyCurrentCursor != NULL)
04863         {
04864             CursorStack::GPop(CurrentCursorID);         
04865         }
04866         delete pMoveCursor;
04867         delete pCloseCursor;
04868         delete pCrossHairCursor;
04869         pMoveCursor = NULL;
04870         MyCurrentCursor = NULL;
04871         CurrentCursorID = 0;
04872     }
04873 }

void OpNodePathEditBlob::DoStartDragEdit NodePath OrigPath,
DocCoord  Anchor,
Spread pSpread
 

This is called to start a drag operation on an endpoint on a path.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> - latterly Peter
Date:
5/7/93
Parameters:
OrigPath - Pointer to the path we are about to edit [INPUTS] Anchor - The position of the mouse at the start of the Drag pSpread - The spread that the path is on

Definition at line 227 of file pathedit.cpp.

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