OpFreeHand Class Reference

This is the FreeHand Operation that builds a smoothed curve. More...

#include <opfree.h>

Inheritance diagram for OpFreeHand:

SelOperation UndoableOperation Operation MessageHandler ListItem CCObject SimpleCCObject OpDrawBrush List of all members.

Public Member Functions

 OpFreeHand ()
 Constructor. This simply sets a few of the operation flags.
void DoDrag (DocCoord Anchor, Spread *, INT32, FreeHandJoinInfo *pJoinInfo, Path *ToolPath)
 Starts up the drag operation by storing all start positions and setting up a Path to store the mouse movement in.
virtual BOOL SnappingDrag ()
virtual BOOL DragKeyPress (KeyPress *pKeyPress, BOOL bSolidDrag)
 Find out if any of the key modifiers are being pressed when the mouse is not moving.
virtual void DragPointerMove (DocCoord PointerPos, ClickModifiers ClickMods, Spread *, BOOL bSolidDrag)
 Handles the event of the mouse moving during a drag.
virtual void DragPointerIdle (DocCoord PointerPos, ClickModifiers ClickMods, Spread *, BOOL bSolidDrag)
 This function checks the Adjust Click modifier while the mouse is not moving to see if it is released during a straight line mode edit. This means that the shift key can be toggled in mid drag to create a straight line shape.
virtual void DragFinished (DocCoord PointerPos, ClickModifiers ClickMods, Spread *, BOOL Success, BOOL bSolidDrag)
 Handles the drag finishing by rubbing out an EOR on the screen and adding the path to the tree and building all the undo that will be needed.
void RenderDragBlobs (DocRect, Spread *, BOOL bSolidDrag)
 EORs the whole path onto the screen a segment at a time. It needs to do it a segment at a time as that is how it is drawn as the path is created. This has to redraw it identically or there will be bits of EORed stuff left all over the place.
virtual BOOL IsBrushOp ()

Static Public Member Functions

static BOOL Declare ()
 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 LoadCursors ()
 Tries to Load all the cursors used by the Free Hand Operation.
void RemoveCursors ()
 Gets rid of all this operations cursors. It takes the cursor I was using off the top of the stack and deletes all the cursors I had allocated.
void SetCursorOnMove (ClickModifiers ClickMods, Spread *pSpread, DocCoord *PointerPos)
 Decides which cursor to display depending on which modifiers are pressed and where the mouse is. PointerPos can be changed in this function.
virtual void SetCursorAndStatus (CursorType CurType)
 Chanages the cursor to the type requested and sets the status bar message to the appropriate message for the mode we are in.
BOOL PrepareTrackDataPath ()
 Tries to initialise the path that we will store the track data in. It also sets up the pressure info stuff it there is pressure info coming in. If the path initialise ok, then it inserts the initial position (including its pressure) into the path. This function will only fail if there is no memory to init the path with.
void RubOutPath (DocCoord Pos, Spread *pSpread)
 Tries to rub out sections of the path that have already been drawn.
void AddPointsToPath (DocCoord Pos, Spread *pSpread)
 Adds a new point to the path of mouse moves. When the drag finishes the mouse moves will be smoothed.
BOOL CheckMouseOverSelectedPath (DocCoord *Pos, Spread *pSpread, BOOL *IsNearEndpoint)
 Checks to see if the mouse is over the endpoint of any of the selected paths in the document and if it is, it makes a note of the path and which end it was attached to.
BOOL AreSlotsInSameSubPath (INT32 Slot1, INT32 Slot2, Path *pPath)
 This function is used when editing lines with the freehand tool. If the drag was started in the middle of a path and the cursor is over that path again, we need to know if we can complete the edit. This is only possible if the start and end points are both on the same sub-path in the path. This function is used to determine if the 2 points are indeed in the same sub-path to allow the tool to display correct cursor etc.
BOOL CompleteOperation ()
 Does all the steps required to build a new path, smooth it, try to join it with others and finally insert it into the tree.
BOOL CreateNewPath (NodePath **pNewPath)
 Builds a new Node Path ready to have a Curve fitted into it. If anything goes wrong (not enough memory, the path was not worth fitting etc) then this function fails.
BOOL SmoothNewCurve (NodePath *pNewNodePath)
 Sets up the curve fitter and finally smooths the data in the Track data buffer into the new node path.
BOOL InsertSmoothCurveWithUndo (NodePath *pNewNodePath)
 Starts the Selection operation (recording the selection state etc) and trys to join our new path with any others touching it, before inserting it into the tree.
virtual BOOL TryToReplaceSection (NodePath *pNewNodePath)
 Replaces a section from the path that the drag was started and finished on with the newly drawn curve section.
BOOL SplitAtPoint (const DocCoord &SplitPoint, INT32 *, INT32 *)
 Splits the path, building undo info, at the coord supplied.
BOOL ReplaceMiddleOfPath (NodePath *pNewPath, INT32 FirstChangedIndex, INT32 NumElements)
 This will replace a section of the path with a new path, building undo as it goes. This function will also set up the Retro Curve fitting data so that it is able to retro fit the new section of the path.
virtual BOOL EditBrushLists (NodePath *pNewPath, INT32 FirstChangedIndex, INT32 NumElements)
 When a path is edited that has a timestamping brush applied to it we those points are stored in a list that is generated when the path is drawn. If we subsequently want to edit that path we have to insert or remove points from that list. This function works out what points should be inserted or removed, and performs that action on the applied brush.
virtual BOOL SimpleJoinBrush (NodePath *pNodePath, Path *pPath)
 Currently this only has to do work if we are editing a brush containing pressure data. In this case we must generate a new list of pressure info and insert it into the brush.
virtual BOOL ReverseBrushPressure ()
 The base class version does nothing, derived class should reverse its pressure cache.
virtual BOOL RetroSmoothBrush (NodePath *pNodePath)
 As above, the base class version does nothing.
BOOL TryToJoinNewPathWithOthers (NodePath *FreePath)
 Tries to Join the new path with any of the existing paths.
BOOL SimpleJoin (NodePath *FreePath, NodePath *OldPath)
 Joins a Simple path with the new freehand path and builds all the undo that is needed in the process.
BOOL ComplexJoin (NodePath *FreePath, NodePath *OldPath)
 Joins a Complex path with the new freehand path and builds all the undo that is needed in the process.
BOOL ComplexToComplexJoin (NodePath *FreePath, NodePath *OldPath)
 Joins a Complex path with the new freehand path and builds all the undo that is needed in the process. The new freehand path joins two of the complex paths sub-paths together.
BOOL VeryComplexToComplexJoin (NodePath *FreePath, NodePath *TopPath, NodePath *BotPath)
 Joins the new freehand path with two other paths (Which may or may not be complex) and builds all the undo info that is needed.
virtual BOOL ApplyAttributes (NodePath *NewPath, Document *pDocument)
 Applies the current attributes to the Node passed in. It has to find the current document to do this and will ENSURE if it fails to find one.
BOOL AddPressureAttribute (NodePath *NewPath)
 If pressure recording is enabled, this method builds attributes to apply to the path in order to attach the pressure as a variable-width stroking description.
NodePathMakeCopy (Node *pOriginalNode)
 Copies the node pOriginalNode and all its children an returns a pointer to them. It makes sure that the Node is a NodePath (Which it should be).
BOOL InsertNewNode (NodePath *pNewNode, DocRect &Invalid, Node *pOldNode, Node *pOtherOld=NULL)
 Inserts a new path into the tree and hides the original node that it was Joined to. It will also invalidate the region of the new path that is different.
void SetRetroPath (NodePath *pNodePath, INT32 Start, INT32 Len)
 Calls the freehand tool with details about the new path, so that future changes in the smoothness will re-build the new path.
void AddStraightLine ()
 Adds a straight line to the Path, that has been made by the Straight Line Mode. This means it has to set some flags in the path to stop the curve fitter from smoothing this straight line segment out.
void RenderLine (RenderRegion *pRender, Path *pPath, INT32 CoordIndex, BOOL StartCoord=FALSE)
 Renders an EORed line between 2 coordinates in the path. May render pressure information feedback using the pressure stored with the path corrdinates.
void RenderLine (DocRect *Rect, Spread *pSpread, Path *pPath, INT32 Index, BOOL StartCoord=FALSE)
 Renders an EORed line between 2 coordinates in the path. May render pressure information feedback using the pressure stored with the path corrdinates.
void RenderEorStraightLine (DocRect *, Spread *pSpread, DocCoord *Start=NULL, DocCoord *End=NULL)
 Renders the EORed rubber band straight line that is used in Straight Line Mode. It simply draws a single Straight Line segment.
void RenderEorStraightLine (RenderRegion *pRender, DocCoord *pStart, INT32 StartWidth, DocCoord *pEnd, INT32 EndWidth)
 Simply renders a straight-line segment to the given render region.
SimpleJoinType GetSimpleJoinType (Path *pNewPath, Path *ExistingPath)
 This function works out how the new path joins onto the existing path.
INT32 GetCurrentLineWidth (void)
 Determines the current line width (the width of any future lines the user will draw) by asking the attribute manager.
NodeGroupGetParentOfNodePath ()
 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.
BOOL InvalidateBrushRegion (NodePath *pNodePath)
 We may be editing a brushed path with the FHT so this function makes sure the correct region is invalidated.

Protected Attributes

PathTrackData
DocCoord StartPoint
SpreadStartSpread
SpreadPreviousSpread
INT32 Smoothness
DocCoord PreviousPoint
INT32 LineSegmentCount
BOOL CanLineJoin
BOOL IsStraightLineMode
DocCoord StraightLinePos
BOOL AddPressureToPath
UINT32 FreeHandPressure
FreeHandJoinInfopJoinInfo
NodePathStartPath
NodePathEndPath
INT32 CloseTo
double Mu
BOOL IsEndNearEndpoint
CursorpFreeHandCursor
CursorpJoinCursor
CursorpRubOutCursor
CursorpStraightCursor
CursorpModifyCursor
CursorMyCurrentCursor
INT32 CurrentCursorID
NodePathm_pNewNodePath

Private Member Functions

 CC_DECLARE_DYNCREATE (OpFreeHand)

Detailed Description

This is the FreeHand Operation that builds a smoothed curve.

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

Definition at line 167 of file opfree.h.


Constructor & Destructor Documentation

OpFreeHand::OpFreeHand  ) 
 

Constructor. This simply sets a few of the operation flags.

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

Definition at line 168 of file opfree.cpp.

00169 {
00170     // Set all our cursors to NULL to start with
00171     pFreeHandCursor = NULL;
00172     pJoinCursor = NULL;
00173     pStraightCursor = NULL;
00174     pRubOutCursor = NULL;
00175     pModifyCursor = NULL;
00176     
00177     // Set other default values
00178     TrackData = NULL;
00179     StartSpread = NULL;
00180     PreviousSpread = NULL;
00181     Smoothness = 512;
00182     LineSegmentCount = 0;
00183     CanLineJoin = FALSE;
00184     IsStraightLineMode = FALSE;
00185     AddPressureToPath = FALSE;
00186     FreeHandPressure = 0;
00187 
00188     // The paths that we are joined to, or NULL if we are joined to none
00189     pJoinInfo = NULL;
00190     StartPath = NULL;
00191     EndPath = NULL;
00192     CloseTo = 0;
00193     Mu = 0.0;
00194     IsEndNearEndpoint = FALSE;
00195     CurrentCursorID = 0;
00196 
00197     m_pNewNodePath = NULL;
00198 
00199 }


Member Function Documentation

void OpFreeHand::AddPointsToPath DocCoord  PointerPos,
Spread pSpread
[protected]
 

Adds a new point to the path of mouse moves. When the drag finishes the mouse moves will be smoothed.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
13/4/94
Parameters:
Pos - The position of the mouse [INPUTS] pSpread - Pointer to the spread that we are drawing on

Reimplemented in OpDrawBrush.

Definition at line 1079 of file opfree.cpp.

01080 {
01081     // If this coord is the same as the last one, then do not bother adding to the track data
01082     if (PreviousPoint == PointerPos)
01083         return;
01084  
01085     // Insert the move
01086     if (TrackData->InsertLineTo(PointerPos))
01087     {
01088         // and add pressure info if needed
01089         if (AddPressureToPath)
01090             TrackData->AddExtraInfo(CI_PRESSURE, FreeHandPressure);
01091     }
01092     else
01093     {
01094         // Oh no, we ran out of mem in the middle of making a new path
01095         // Tidy up and report back to HQ
01096         EndDrag();
01097 
01098         // Tell the World that it all went horribly wrong
01099         InformError();
01100 
01101         // Remove it from the tree and delete it
01102         TrackData->ClearPath();
01103         
01104         // End this operation and return
01105         FailAndExecute();
01106         End();
01107         return;
01108     }
01109     
01110     // Now we have to render a new bit of EORed data to the screen
01111     RenderLine(NULL, pSpread, TrackData, TrackData->GetNumCoords()-1);
01112 
01113     // Make a note of the coord, inc the line count and mark joining to the start as active.
01114     PreviousPoint = PointerPos;
01115     LineSegmentCount++;
01116     CanLineJoin = TRUE;
01117 }

BOOL OpFreeHand::AddPressureAttribute NodePath pNewPath  )  [protected]
 

If pressure recording is enabled, this method builds attributes to apply to the path in order to attach the pressure as a variable-width stroking description.

Author:
Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
Date:
21/1/97
Parameters:
NewPath - The NodePath to apply the attributes to [INPUTS]
Returns:
TRUE if it worked, FALSE if it did not
This attaches an AttrStrokeType to control stroking, and an AttrVariableWidth to record the pressure values

Definition at line 1858 of file opfree.cpp.

01859 {
01860     if (IsBrushOp())
01861         return TRUE;
01862 // Taken out by vector stroking code Neville 2/10/97
01863 #ifdef VECTOR_STROKING
01864 
01865     ERROR3IF(GetWorkingDoc() == NULL, "No working doc");
01866     ERROR3IF(pNewPath == NULL, "Illegal NULL param");
01867     ERROR3IF(TrackData == NULL, "Trackdata is NULL");
01868 
01869     // If pressure recording is disabled, then we don't need to do anything
01870     if (TrackData == NULL || !AddPressureToPath)
01871         return(TRUE);
01872 
01873     // --- Generate a smoothed pressure function from the recorded pressure samples
01874     // If this fails, then we still return TRUE, and the path just loses pressure information
01875     PressureSmoother Bob;
01876     ValueFunction *pValFunc = Bob.Smooth(TrackData, GetCurrentLineWidth());
01877     if (pValFunc == NULL)
01878         return(TRUE);
01879 
01880     // We only want to override the stroke type if it is a simple constant-width line
01881     // - if there is any new-style stroke type applied, we won't try to override it
01882     BOOL ApplyType = FALSE;
01883     
01884     // BLOCK
01885     {
01886         Node *pNode = pNewPath->FindFirstChild();
01887         if (pNode != NULL)
01888             pNode = pNode->FindNext(CC_RUNTIME_CLASS(AttrStrokeType));
01889 
01890         ApplyType = (pNode == NULL);    // If there are no StrokeType attrs applied already, we must apply one
01891         if (!ApplyType)
01892         {
01893             // If there is another StrokeType, but it will not stroke using the variable width info,
01894             // (i.e. has a NULL PathProcessor attached to it) we will need to replace it.
01895             StrokeTypeAttrValue *pSAV = (StrokeTypeAttrValue *) ((AttrStrokeType *)pNode)->GetAttributeValue();
01896             if (pSAV->GetPathProcessor() == NULL)
01897                 ApplyType = TRUE;
01898         }
01899     }
01900 
01901     // --- Create a simple variable-width path stroke attribute
01902     AttrStrokeType *pStrokeType = NULL;
01903     if (ApplyType)
01904     {
01905         pStrokeType = new AttrStrokeType;
01906         if (pStrokeType == NULL)
01907             return(TRUE);               // Return TRUE - we'll only lose the pressure info
01908 
01909         PathProcessorStroke *pProcessor = new PathProcessorStroke;
01910         if (pProcessor == NULL)
01911         {
01912             delete pStrokeType;
01913             return(TRUE);               // Return TRUE - we'll only lose the pressure info
01914         }
01915 
01916         // And attach the stroker to the attribute
01917         ((StrokeTypeAttrValue *)pStrokeType->GetAttributeValue())->SetPathProcessor(pProcessor);
01918     }
01919 
01920     // --- Create a pressure table variable-width attribute
01921     AttrVariableWidth *pVarWidth = new AttrVariableWidth;
01922     if (pVarWidth != NULL)
01923         ((VariableWidthAttrValue *)pVarWidth->GetAttributeValue())->SetWidthFunction(pValFunc);
01924 
01925     if (pVarWidth == NULL)
01926     {
01927         if (pStrokeType != NULL)
01928             delete pStrokeType;
01929         return(TRUE);               // Return TRUE - we'll only lose the pressure info
01930     }
01931 
01932     // --- Now apply the attributes to the path, removing any of a similar type which
01933     // are already applied to it.
01934     if (ApplyType)
01935     {
01936         DoRemoveAttrTypeFromSubtree(pNewPath, pStrokeType->GetRuntimeClass());
01937         pStrokeType->AttachNode(pNewPath, FIRSTCHILD, FALSE, FALSE);
01938     }
01939 
01940     // And apply the variable width attribute
01941     DoRemoveAttrTypeFromSubtree(pNewPath, pVarWidth->GetRuntimeClass());
01942     pVarWidth->AttachNode(pNewPath, FIRSTCHILD, FALSE, FALSE);
01943 
01944 #endif // VECTOR_STROKING
01945 
01946     return(TRUE);
01947 }

void OpFreeHand::AddStraightLine  )  [protected]
 

Adds a straight line to the Path, that has been made by the Straight Line Mode. This means it has to set some flags in the path to stop the curve fitter from smoothing this straight line segment out.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
22/4/94

Definition at line 1132 of file opfree.cpp.

01133 {
01134     PathFlags Flags;
01135     Flags.Spare1 = TRUE;
01136     
01137     // If this coord is the same as the last one, then do not bother adding to the track data
01138     if (PreviousPoint == StraightLinePos)
01139         return;
01140 
01141     // add the line segment into the path
01142     if (TrackData->InsertLineTo(StraightLinePos, &Flags))
01143     {
01144         // and add pressure info if needed
01145         if (AddPressureToPath)
01146             TrackData->AddExtraInfo(CI_PRESSURE, FreeHandPressure);
01147     }
01148     else
01149     {
01150         // Oh no, we ran out of mem in the middle of making a new path
01151         // Tidy up and report back to HQ
01152         EndDrag();
01153 
01154         // Tell the World that it all went horribly wrong
01155         InformError();
01156 
01157         // Remove it from the tree and delete it
01158         TrackData->ClearPath();
01159         
01160         // End this operation and return
01161         FailAndExecute();
01162         End();
01163         return;
01164     }
01165 
01166     // Count this bit of line
01167     LineSegmentCount++;
01168     CanLineJoin = TRUE;
01169 }

BOOL OpFreeHand::ApplyAttributes NodePath NewPath,
Document pDocument
[protected, virtual]
 

Applies the current attributes to the Node passed in. It has to find the current document to do this and will ENSURE if it fails to find one.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
4/5/94
Parameters:
NewPath - The NodePath to apply the current attributes to [INPUTS] pDocument - the document this path will be placed into.
Returns:
TRUE if it worked, FALSE if it did not

Reimplemented in OpDrawBrush.

Definition at line 1812 of file opfree.cpp.

01813 {
01814     // Find the current document to get the current attributes from
01815     ENSURE(pDocument!=NULL, "Null Document while setting attributes for new NodePath");
01816     if (pDocument!=NULL)
01817     {
01818         // Apply the current attributes to the path
01819         if (pDocument->GetAttributeMgr().ApplyCurrentAttribsToNode((NodeRenderableInk*)NewPath))
01820         {
01821             // ok special case exception here, if we have a brush attribute then delete it
01822             Node* pNode = NewPath->FindFirstChild(CC_RUNTIME_CLASS(AttrBrushType));
01823             if (pNode)
01824             {
01825                 pNode->CascadeDelete();
01826                 delete pNode;
01827             }
01828 
01829             return TRUE;
01830         }
01831     }
01832 
01833     // We failed to apply the attributes, so fail
01834     return FALSE;
01835 }

BOOL OpFreeHand::AreSlotsInSameSubPath INT32  Slot1,
INT32  Slot2,
Path pPath
[protected]
 

This function is used when editing lines with the freehand tool. If the drag was started in the middle of a path and the cursor is over that path again, we need to know if we can complete the edit. This is only possible if the start and end points are both on the same sub-path in the path. This function is used to determine if the 2 points are indeed in the same sub-path to allow the tool to display correct cursor etc.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
2/6/95
Parameters:
Slot1,Slot2 - The slot numbers of the 2 points in question. [INPUTS] pPath - the path that the slots live in
Returns:
TRUE if both these slots live in the same sub-path of pPath, FALSE if they are in different sub-paths, or there was an error.

Definition at line 1304 of file opfree.cpp.

01305 {
01306     // saftey check
01307     ERROR2IF(pPath==NULL, FALSE, "NULL pointer passed to OpFreeHand::AreSlotsInSameSubPath");
01308 
01309     // We need to look at the verb array in the path.
01310     PathVerb* pVerbs = pPath->GetVerbArray();
01311     INT32 NumSlots = pPath->GetNumCoords();
01312 
01313     // find the largest and smallest slot numbers
01314     INT32 First = Slot1<Slot2 ? Slot1 : Slot2;
01315     INT32 Last = Slot1<Slot2 ? Slot2 : Slot1;
01316 
01317     // Make sure that the slot numbers make sense
01318     ERROR2IF(Last>NumSlots, FALSE, "Bad Slot numbers in OpFreeHand::AreSlotsInSameSubPath");
01319 
01320     // loop from the first to the last slot looking for MoveTos
01321     while (Last>First)
01322     {
01323         // See if the current last slot is a MoveTo
01324         if (pVerbs[Last]==PT_MOVETO)
01325             return FALSE;
01326 
01327         // move the last point back
01328         Last--;
01329     }
01330 
01331     // We did not find any MoveTos between the points
01332     return TRUE;    
01333 }

OpFreeHand::CC_DECLARE_DYNCREATE OpFreeHand   )  [private]
 

BOOL OpFreeHand::CheckMouseOverSelectedPath DocCoord PointerPos,
Spread pSpread,
BOOL *  IsNearEndpoint
[protected]
 

Checks to see if the mouse is over the endpoint of any of the selected paths in the document and if it is, it makes a note of the path and which end it was attached to.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/4/94
Parameters:
PointerPos - The position of the mouse [INPUTS] pSpread - the spread that the cursor is over
IsNearEndPoint - if this function returns TRUE this param is valid, otherwise [OUTPUTS] it is not. It will be TRUE if the cursor was found to be close to an open end of the path, or FALSE if it was near the middle of the path
Returns:
TRUE if the pointer was over the end of a selected line, FALSE if it was not

Definition at line 1191 of file opfree.cpp.

01192 {
01193     // Assume we do not find a path
01194     EndPath = NULL;
01195 
01196     // Find out about the blob manager so we can find the size of a blob
01197     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
01198     if (pBlobMgr==NULL)
01199         return FALSE;
01200 
01201     // Find out about the size of a blob
01202     DocRect BlobRect;
01203     pBlobMgr->GetBlobRect(*PointerPos, &BlobRect);
01204 
01205     // Find the selected range of objects
01206     SelRange* Selected = GetApplication()->FindSelection();
01207     Node* pNode = Selected->FindFirst();
01208 
01209     // see if the selection is on the same spread as the drag
01210     if (pNode!=NULL)
01211     {
01212         // Get the spread and return if it is different from the one with the cursor
01213         Spread* pNodeSpread = pNode->FindParentSpread();
01214         if (pNodeSpread!=pSpread)
01215             return FALSE;       
01216     }
01217 
01218     // Walk through the selection
01219     while (pNode!=NULL)
01220     {
01221         if (pNode->IsNodePath())
01222         {
01223             // Now we know it's a NodePath, get a pointer to the Path object within it, so
01224             // we can find any endpoints
01225             Path* ThisPath = &(((NodePath*)pNode)->InkPath);
01226             
01227             // See if it touches any point in this path
01228             INT32 CloseSlot = 0;
01229             if (ThisPath->IsNearOpenEnd(BlobRect, &CloseSlot))
01230             {
01231                 // Yep, better work out where and set the path where is all happens
01232                 ThisPath->SetPathPosition(CloseSlot);
01233                 *PointerPos = ThisPath->GetCoord();
01234                 *IsNearEndpoint = TRUE;
01235                 EndPath = (NodePath*)pNode;
01236                 return TRUE;
01237             }
01238 
01239             // See if it is near the middle of the path
01240             // This is only relavent if this node is the same as the one we started on
01241             // and we started in the middle of it
01242             if ((StartPath!=NULL) && (StartPath==pNode) && (pJoinInfo->IsNearEndPoint==FALSE))
01243             {
01244                 INT32 Range = BlobRect.Width() / 2;
01245                 if (ThisPath->IsPointCloseTo(*PointerPos, Range*Range, &CloseTo, &Mu))
01246                 {
01247                     // OK, we are near the path we started on
01248                     // see if we are in the same sub-path as we started on
01249                     if (AreSlotsInSameSubPath(CloseTo, pJoinInfo->CloseSlot, ThisPath))
01250                     {
01251                         // Yep, better work out where and set the path where is all happens
01252                         *IsNearEndpoint = FALSE;
01253                         EndPath = (NodePath*)pNode;
01254                         return TRUE;
01255                     }
01256                 }
01257             }
01258         }   
01259 
01260         // Now find the next selected node
01261         pNode = Selected->FindNext(pNode);
01262     }
01263 
01264     // we will see if this point is anywhere near the start point
01265     if ((BlobRect.ContainsCoord(StartPoint)) && (StartPath==NULL))
01266     {
01267         // Note the path that we are starting at and modify the coord
01268         // This will make the start and end points the same, causing the path to be closed
01269         *PointerPos = StartPoint;
01270 
01271         // Wait till the cursor has moved out of the original snap range before we start joining
01272         if (CanLineJoin==TRUE)
01273         {
01274             *IsNearEndpoint = TRUE;
01275             return TRUE;
01276         }
01277     }
01278 
01279     // Did not find a join
01280     return FALSE;
01281 }

BOOL OpFreeHand::CompleteOperation  )  [protected]
 

Does all the steps required to build a new path, smooth it, try to join it with others and finally insert it into the tree.

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

Definition at line 783 of file opfree.cpp.

00784 {
00785     // Create a new path to be inserted into the tree
00786     NodePath* pNewNodePath = NULL;
00787     if (!CreateNewPath(&pNewNodePath))
00788         return FALSE;
00789 
00790     if (!SmoothNewCurve(pNewNodePath))
00791     {
00792         // Get rid of the path we created and fail
00793         delete pNewNodePath;
00794         return FALSE;
00795     }
00796 
00797     if (!InsertSmoothCurveWithUndo(pNewNodePath))
00798     {
00799         // Get rid of the path we created, and fail
00800         pNewNodePath->CascadeDelete();
00801         delete pNewNodePath;
00802         return FALSE;
00803     }
00804 
00805     // Mark the selection as no longer valid (the Bounding Box will have changed)
00806     SelRange* Selection = GetApplication()->Selection;
00807     if (Selection)
00808         Selection->Update();
00809 
00810     
00811     // All worked
00812     return TRUE;
00813 }

BOOL OpFreeHand::ComplexJoin NodePath FreePath,
NodePath OldPath
[protected]
 

Joins a Complex path with the new freehand path and builds all the undo that is needed in the process.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/5/94
Parameters:
FreePath - The New freehand Path that is to be joined [INPUTS] OldPath - The path that we are trying to Join to
Returns:
TRUE if it worked, FALSE if it failed

Definition at line 1653 of file opfree.cpp.

01654 {
01655     // First we should make a note of the region that we will need redrawing
01656     DocRect InvalidRect = FreePath->GetBlobBoundingRect();
01657 
01658     // Need to know where in the joined path that the new section will be
01659     INT32 NumSlots = FreePath->InkPath.GetNumCoords();
01660     INT32 StartSlot;
01661 
01662     // Make a copy of the node we are joining with
01663     NodePath* JoinPath = MakeCopy(OldPath);
01664     if (JoinPath == NULL)
01665         return FALSE;
01666 
01667     // before Joining them
01668     BOOL NewPathReversed = FALSE;
01669     if (!JoinPath->InkPath.ComplexJoin(&FreePath->InkPath, &StartSlot, &NewPathReversed))
01670         return FALSE;
01671 
01672     // Insert the new object
01673     if (!InsertNewNode(JoinPath, InvalidRect, OldPath))
01674         return FALSE;
01675 
01676     // Reverse the original mouse move data if the fitted path was reversed
01677     if (NewPathReversed)
01678         TrackData->Reverse();
01679 
01680     // Tell the freehand tool all about it
01681     SetRetroPath(JoinPath, StartSlot, NumSlots);
01682     
01683     m_pNewNodePath = JoinPath;
01684     // It Worked!
01685     return TRUE;
01686 }

BOOL OpFreeHand::ComplexToComplexJoin NodePath FreePath,
NodePath OldPath
[protected]
 

Joins a Complex path with the new freehand path and builds all the undo that is needed in the process. The new freehand path joins two of the complex paths sub-paths together.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/5/94
Parameters:
FreePath - The New freehand Path that is to be joined [INPUTS] OldPath - The path that we are trying to Join to
Returns:
TRUE if it worked, FALSE if it failed

Definition at line 1705 of file opfree.cpp.

01706 {
01707     // First we should make a note of the region that we will need redrawing
01708     DocRect InvalidRect = FreePath->GetBlobBoundingRect();
01709 
01710     // Need to know where in the joined path that the new section will be
01711     INT32 NumSlots = FreePath->InkPath.GetNumCoords();
01712     INT32 StartSlot;
01713 
01714     // Make a copy of the of the path we are Joining to
01715     NodePath* JoinPath = MakeCopy(OldPath);
01716     if (JoinPath==NULL)
01717         return FALSE;
01718 
01719     // Join the new path with the one already there
01720     BOOL NewPathReversed = FALSE;
01721     if (!JoinPath->InkPath.ComplexToSameComplexJoin(&FreePath->InkPath, &StartSlot, &NewPathReversed))
01722         return FALSE;
01723 
01724     // Insert the new object
01725     if (!InsertNewNode(JoinPath, InvalidRect, OldPath))
01726         return FALSE;
01727 
01728     // Reverse the original mouse move data if the fitted path was reversed
01729     if (NewPathReversed)
01730         TrackData->Reverse();
01731 
01732     // Tell the freehand tool all about it
01733     SetRetroPath(JoinPath, StartSlot, NumSlots);
01734 
01735     // It Worked!
01736     return TRUE;
01737 }

BOOL OpFreeHand::CreateNewPath NodePath **  pNewPath  )  [protected]
 

Builds a new Node Path ready to have a Curve fitted into it. If anything goes wrong (not enough memory, the path was not worth fitting etc) then this function fails.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
25/10/94
Parameters:
pNewPath - This will be a pointer to an initialised NodePath or NULL [OUTPUTS]
Returns:
TRUE if it worked, FALSE if something went wrong

Definition at line 831 of file opfree.cpp.

00832 {
00833     // Get a pointer to the path and set it to NULL
00834     NodePath*& pFreeHandPath = *pNewPath;
00835     pFreeHandPath = NULL;
00836 
00837     // Only bother if they had actually drawn something
00838     if (LineSegmentCount==0)
00839         return FALSE;
00840 
00841     // See if the path ever got away from the initial snap coord
00842     DocCoord* Coords = TrackData->GetCoordArray();
00843     INT32 NumCoords = TrackData->GetNumCoords();
00844     BOOL IsAllSame = TRUE;
00845 
00846     // loop through all the coords comparing them with the first one
00847     for (INT32 i=0; (i<NumCoords) && (IsAllSame); i++)
00848     {
00849         // if this coord is different from the first one, then we are ok
00850         if (Coords[0] != Coords[i])
00851             IsAllSame = FALSE;
00852     }
00853 
00854     // If it did not (ie all the coordinates were the same, then chuck it away)
00855     if (IsAllSame)
00856         return FALSE;
00857 
00858     // Init the path with the default memory block sizes
00859     pFreeHandPath = new NodePath;
00860     if (pFreeHandPath==NULL)
00861         return FALSE;
00862 
00863     // Ok, we got a new NodePath, try and init the Path inside it
00864     if (!pFreeHandPath->SetUpPath(24, 12))
00865     {
00866         // The path failed to init, so get rid of it and fail
00867         delete pFreeHandPath;
00868         pFreeHandPath = NULL;
00869         return FALSE;
00870     }
00871 
00872     // Must have worked
00873     return TRUE;
00874 }

BOOL OpFreeHand::Declare  )  [static]
 

Adds the operation to the list of all known operations.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/7/93
Returns:
TRUE if all went OK, False otherwise

Reimplemented in OpDrawBrush.

Definition at line 3278 of file opfree.cpp.

03279 {
03280     return (RegisterOpDescriptor(
03281                                 0, 
03282                                 _R(IDS_FREE_HAND_TOOL),
03283                                 CC_RUNTIME_CLASS(OpFreeHand), 
03284                                 OPTOKEN_FREEHAND,
03285                                 OpFreeHand::GetState,
03286                                 0,  /* help ID */
03287                                 _R(IDBBL_FREEHANDTOOLOP),
03288                                 0   /* bitmap ID */));
03289 }

void OpFreeHand::DoDrag DocCoord  Anchor,
Spread pSpread,
INT32  Smooth,
FreeHandJoinInfo pFreeHandInfo,
Path ToolPath
 

Starts up the drag operation by storing all start positions and setting up a Path to store the mouse movement in.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/7/93
Parameters:
Anchor - The position of the mouse at the start of the Drag [INPUTS] pSpread - The spread that the drag started on Smooth - Closeness of fit value pFreeHandInfo - Pointer to the info about the Joining set up by the freehand tool ToolPath - The path to store the mouse tracking info in

Definition at line 221 of file opfree.cpp.

00223 {
00224     TRACEUSER( "Diccon", _T("Do Drag Free, StartPos = %d, %d\n"), Anchor.x, Anchor.y);
00225 // WEBSTER - markn 25/4/97
00226 // No pen stuff required in Webster
00227 // Taken out by vector stroking code Neville 2/10/97
00228 #ifdef VECTOR_STROKING
00229     // Tell the pressure pen that we're starting a new stroke
00230     CCPen *pPen = GetApplication()->GetPressurePen();
00231     if (pPen != NULL)
00232         pPen->StartStroke();
00233 #endif // VECTOR_STROKING
00234 
00235     // Snap the starting coord if we are not joining to something else
00236     if ((pFreeHandInfo==NULL) || (pFreeHandInfo->pJoinPath==NULL))
00237         DocView::SnapCurrent(pSpread, &Anchor, FALSE, TRUE);
00238 
00239     // Make a note of various starting conditions
00240     TrackData   = ToolPath;
00241     Smoothness  = Smooth;
00242     pJoinInfo   = pFreeHandInfo;
00243     StartPath   = pJoinInfo->pJoinPath;
00244     EndPath     = NULL;
00245 
00246     // Make a mental note of the start point
00247     StartPoint    = Anchor;
00248     StartSpread   = pSpread;
00249     PreviousSpread= pSpread;
00250     PreviousPoint = Anchor;
00251     LineSegmentCount = 0;
00252     IsStraightLineMode = FALSE;
00253     CanLineJoin = FALSE;
00254 
00255     // Prepare the Track data path and add in the initial click point to the path
00256     if (!PrepareTrackDataPath())
00257     {
00258         // We failed to get the memory to initialise the track data
00259         InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
00260         FailAndExecute();
00261         End();
00262         return;
00263     }
00264         
00265     // Create some cursors that I might need
00266     if (!LoadCursors())
00267     {
00268         // The cursors did not load, so fail?
00269         FailAndExecute();
00270         End();
00271         return;
00272     }
00273 
00274     // Push my new cursor onto the stack
00275     CurrentCursorID = CursorStack::GPush(pFreeHandCursor, TRUE);
00276     MyCurrentCursor = pFreeHandCursor;
00277 
00278     // And tell the Dragging system that we need drags to happen
00279     StartDrag( DRAGTYPE_NOSCROLL );
00280 }

void OpFreeHand::DragFinished DocCoord  PointerPos,
ClickModifiers  ClickMods,
Spread pSpread,
BOOL  Success,
BOOL  bSolidDrag
[virtual]
 

Handles the drag finishing by rubbing out an EOR on the screen and adding the path to the tree and building all the undo that will be needed.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/7/93
Parameters:
PointerPos - The position of the mouse at the end of the drag [INPUTS] ClickMods - the key modifiers being pressed pSpread - The spread that the drag finished on Success - TRUE if the drag was terminated properly, FALSE if it was ended with the escape key being pressed
See also:
ClickModifiers

Reimplemented from Operation.

Reimplemented in OpDrawBrush.

Definition at line 647 of file opfree.cpp.

00649 {
00650     // Erase the whole EORed line
00651     DocRect ClipRect(0,0,0,0);
00652     RenderDragBlobs(ClipRect, StartSpread, bSolidDrag);
00653 
00654     // Get rid of all the cursors
00655     RemoveCursors();
00656 
00657     // Put the hour glass up as we have to and end the drag
00658     BeginSlowJob();
00659     EndDrag();
00660 
00661     // Add the new path to the tree if it was valid
00662     BOOL Worked = FALSE;
00663     if (Success)
00664     {
00665         // were we in the middle of drawing a straight line
00666         if (IsStraightLineMode)
00667         {
00668             // we were so add it in to the track data
00669             AddStraightLine();
00670             PreviousPoint = StraightLinePos;
00671             IsStraightLineMode = FALSE;
00672         }
00673 
00674         // try and smooth the path and insert it into the tree
00675         Worked = CompleteOperation();
00676 
00677         // DY 9/99 Check to see if we are editing a blend on a curve, if so we may wish to 
00678         // change the number of steps in the blend (making use of the path distance).
00679         NodeGroup* pParent = GetParentOfNodePath();           
00680         if (pParent!= NULL)
00681         {
00682             if (pParent->IS_KIND_OF(NodeBlend))
00683             {
00684                 InsertChangeBlendStepsAction((NodeBlend*)pParent);               
00685 
00686                 ObjChangeFlags cFlags(