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(FALSE,TRUE);
00687                 ObjChangeParam ObjChange(OBJCHANGE_FINISHED,cFlags,NULL,this);
00688                 Worked = UpdateChangedNodes(&ObjChange);
00689             }
00690         
00691         }
00692     }
00693 // WEBSTER - markn 25/4/97
00694 // No pen stuff required in Webster
00695 // Taken out by vector stroking code Neville 2/10/97
00696 #ifdef VECTOR_STROKING
00697     // Inform the pressure pen that we've finished the stroke
00698     CCPen *pPen = GetApplication()->GetPressurePen();
00699     if (pPen != NULL)
00700         pPen->EndStroke();
00701 #endif // VECTOR_STROKING
00702 
00703 
00704 
00705     // If the operation failed, then tidy up
00706     if (Worked==FALSE)
00707         FailAndExecute();
00708     else    
00709     {
00710         // Update all the parents of the effected paths.
00711         ObjChangeFlags cFlags;
00712         ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
00713         ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this);
00714         UpdateChangedNodes(&ObjChange);
00715     }
00716     // End the operation properly
00717     End();
00718 }

BOOL OpFreeHand::DragKeyPress KeyPress pKeyPress,
BOOL  bSolidDrag
[virtual]
 

Find out if any of the key modifiers are being pressed when the mouse is not moving.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/11/94
Parameters:
pKeyPress - pointer to a keypress object [INPUTS]
Returns:
TRUE if it handled the keypress, FALSE otherwise

Reimplemented from Operation.

Definition at line 735 of file opfree.cpp.

00736 {
00737     // Need something to see if there was a usful keypress
00738 //  BOOL IsKeyPress = FALSE;
00739 
00740     // and something to keep them in
00741     ClickModifiers ClickMods;
00742     ClickMods.Adjust = FALSE;
00743     ClickMods.Menu = FALSE;
00744     ClickMods.Constrain = FALSE;
00745     ClickMods.Alternative1 = FALSE;
00746     ClickMods.Alternative2 = FALSE;
00747     ClickMods.Pressure = 0;
00748 
00749     // See if was one of the key we are interested in
00750     if (pKeyPress->IsConstrain() || pKeyPress->IsAdjust() || pKeyPress->IsAlternative() || pKeyPress->IsRelease())
00751     {
00752         // Set up the click modifier
00753         ClickMods.Constrain = pKeyPress->IsConstrainPressed();
00754         ClickMods.Adjust = pKeyPress->IsAdjustPressed();
00755         ClickMods.Alternative1 = pKeyPress->IsAlternativePressed();
00756 
00757         // fake a mouse move message
00758         DragPointerMove(PreviousPoint, ClickMods, PreviousSpread, FALSE);
00759 
00760         // Tell the keypress people that we used the keypress
00761         return TRUE;
00762     }
00763 
00764     // Did not use the click
00765     return FALSE;
00766 }

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

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.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/5/94
Parameters:
PointerPos - The position of the mouse [INPUTS] ClickMods - The modifiers that are held down at the time of the idle event pSpread - The spread that the mouse was over at the event

Reimplemented from Operation.

Reimplemented in OpDrawBrush.

Definition at line 610 of file opfree.cpp.

00611 {
00612     // If we are in straight line mode, but the alt button has just come up, then
00613     // end the straight line we are drawing
00614     if (IsStraightLineMode && !ClickMods.Alternative1)
00615     {
00616         // Add the straight line into the path and set the flags as needed
00617         AddStraightLine();
00618         PreviousPoint = StraightLinePos;
00619 
00620         // No longer in Straight Line mode, so set the flag
00621         IsStraightLineMode = FALSE;
00622     }
00623 }

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

Handles the event of the mouse moving during a drag.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/7/93
Parameters:
PointerPos - The current position of the mouse in Doc Coords [INPUTS] ClickMods - Which key modifiers are being pressed pSpread - The spread that the mouse is moving over
See also:
ClickModifiers

Reimplemented from Operation.

Reimplemented in OpDrawBrush.

Definition at line 514 of file opfree.cpp.

00516 {
00517     // Find out the view
00518     DocView* pView = DocView::GetSelected();
00519 
00520     // Lets whip out the current pressure value
00521     if (AddPressureToPath)
00522         FreeHandPressure = ClickMods.Pressure;
00523 
00524     // If drag has moved onto a different spread, convert the coord to be relative to the
00525     // original spread.
00526     if (pSpread != StartSpread)
00527         PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
00528 
00529     // Change the Cursor to display the appropriate thing.
00530     SetCursorOnMove(ClickMods, StartSpread, &PointerPos);
00531 
00532     // Are we in Straight Line Mode? We won't allow straight line mode when recording pressure
00533     if (ClickMods.Alternative1)
00534     {
00535         // Snap and Constrain if needed
00536         DocView::SnapCurrent(pSpread, &PointerPos);
00537         if (ClickMods.Constrain)
00538             DocView::ConstrainToAngle(PreviousPoint, &PointerPos);
00539 
00540         // If we are in the Straight Line mode rub out the line
00541         if (IsStraightLineMode)
00542             RenderEorStraightLine(NULL, pSpread);
00543         else
00544         {
00545             // We are just entering straight line mode, so switch the dragging mode
00546             // so that the document auto scrolls at the edges of the screen
00547             if (pView!=NULL)
00548                 pView->ChangeDragType(DRAGTYPE_AUTOSCROLL);
00549         }
00550 
00551         // Update the mouse position and render the EORed straight line
00552         StraightLinePos = PointerPos;
00553         RenderEorStraightLine(NULL, pSpread);
00554         
00555         // Finally note that we are in this mode
00556         IsStraightLineMode = TRUE;
00557     }
00558     else
00559     {
00560         // See if we were in Straight Line mode before
00561         if (IsStraightLineMode)
00562         {
00563             // We do not want to rub the straight line off, so do no drawing
00564             // Add the straight line into the path and set the flags as needed
00565             AddStraightLine();
00566             PreviousPoint = StraightLinePos;
00567 
00568             // We are just leaving straight line mode, so go back to not scrolling
00569             if (pView!=NULL)
00570                 pView->ChangeDragType(DRAGTYPE_NOSCROLL);
00571 
00572             // No longer in Straight Line mode, so set the flag
00573             IsStraightLineMode = FALSE;
00574         }
00575         else
00576         {
00577             // Either rub out sections of the path, or add new points to it
00578             if (ClickMods.Adjust)
00579                 RubOutPath(PointerPos, StartSpread);
00580             else
00581                 AddPointsToPath(PointerPos, StartSpread);
00582         }
00583     }
00584 
00585     // Set the last spread to something appropriate
00586     PreviousSpread = pSpread;
00587 }

BOOL OpFreeHand::EditBrushLists NodePath pNewPath,
INT32  FirstChangedIndex,
INT32  NumElements
[protected, virtual]
 

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.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/3/2000
Parameters:
pNewPath - The New curve that has just been draw [INPUTS] FirstChangedIndex - The first coord in the path that is to be replaced NumElements - The number of elements in the path that need replacing
Returns:
TRUE if it worked, FALSE if not

Reimplemented in OpDrawBrush.

Definition at line 2869 of file opfree.cpp.

02870 {
02871     // what we are going to do here is find out whether or not the path we are editing is brushed
02872     // and if so whether or not it uses pressure data.  If so then we will generate a list of pressure
02873     // data and insert it.
02874     ERROR2IF(pNewPath == NULL, FALSE, "pNewPath is NULL in OpFreeHand::EditPressureList");
02875 
02876     /* First up, find the nearest applied brush attribute. If there isn't one or
02877     it has no pressure list then we don't have to bother with all this */
02878     AttrBrushType* pAttrBrush = NULL;
02879 
02880     pAttrBrush = EndPath->GetAppliedBrushAttribute();
02881     if (pAttrBrush == NULL)
02882         return TRUE;
02883 
02884     // if we are attempting to edit an existing brush that does not have an existing pressure
02885     // cache then leave now
02886     if (!pAttrBrush->ContainsPressureCache())
02887         return TRUE;
02888     
02889     // most of the action takes place in the attribute value so get that also
02890     BrushAttrValue* pVal = (BrushAttrValue*)pAttrBrush->GetAttributeValue();
02891     if (pVal == NULL)
02892         return TRUE;
02893 
02894     /* next, record the following distances:
02895     - original path length;
02896     - distance to first changed point;
02897     - distance to last changed point;
02898     */
02899 
02900 //  double OriginalLength = EndPath->InkPath.GetPathLength();
02901     MILLIPOINT DistToFirstPoint = -1;
02902     MILLIPOINT DistToLastPoint = -1;
02903     DocCoord* pCoords = EndPath->InkPath.GetCoordArray();
02904 
02905     DocCoord FirstChangedPoint;
02906     DocCoord LastChangedPoint;
02907 
02908     if (pCoords == NULL)
02909         return FALSE;
02910     if (FirstChangedIndex + NumElements > EndPath->InkPath.GetNumCoords())
02911     {
02912         ERROR3("Illegal number of coordinates");
02913         return FALSE;
02914     }
02915 
02916     // get the coordinates from the array
02917     FirstChangedPoint = pCoords[FirstChangedIndex];
02918     LastChangedPoint  =  pCoords[FirstChangedIndex + NumElements];
02919 
02920     EndPath->InkPath.GetDistanceToPoint(FirstChangedPoint, &DistToFirstPoint);
02921     EndPath->InkPath.GetDistanceToPoint(LastChangedPoint, &DistToLastPoint);
02922 
02923     // Now find the indices into the pressure list that correspond to those distances
02924     INT32 StartPressureIndex = pVal->GetPressureListIndexAtDistance(DistToFirstPoint);
02925     if (StartPressureIndex == -1)
02926     {
02927         ERROR3("StartPressureIndex is -1 in OpDrawBrush::EditPressureList");
02928         return FALSE;
02929     }
02930     
02931     INT32 EndPressureIndex = pVal->GetPressureListIndexAtDistance(DistToLastPoint);
02932     if (EndPressureIndex == -1 || EndPressureIndex <= StartPressureIndex)
02933     {
02934         ERROR3("EndPressureIndex is invalid in OpDrawBrush::EditPressureList");
02935         return FALSE;
02936     }
02937     UINT32 NumObjects = EndPressureIndex - StartPressureIndex;
02938 
02939     CDistanceSampler* pSampler = OpDrawBrush::GeneratePressureData(pAttrBrush, DistToFirstPoint, DistToLastPoint,
02940                                                 (MILLIPOINT)pNewPath->InkPath.GetPathLength()); 
02941 
02942     if (pSampler == NULL)  // did it fail, of so we must quit
02943         return FALSE;
02944     /* now get the pressure points list and ask the attribute to clear all the points
02945     between the edited distances*/
02946 
02947     // Invalidate it as it was
02948     pAttrBrush->ClearCachedRect();
02949     DocRect Rect = pAttrBrush->GetAttrBoundingRect(EndPath);
02950     EndPath->ReleaseCached();
02951     Spread* pSpread = Document::GetSelectedSpread();
02952     if (!DoInvalidateRegion(pSpread, Rect))
02953         return FALSE;
02954     // now make a removepoints action
02955     RemovePressurePointsAction* pAction; 
02956     
02957     if (RemovePressurePointsAction::Init(this, &UndoActions, pAttrBrush, (UINT32)StartPressureIndex, NumObjects, NULL, &pAction) == AC_FAIL)
02958         return FALSE;
02959 
02960     // now insert our new points into the attribute
02961     AddPressurePointsAction* pAddAction;
02962 
02963     // find out how many points we're inserting
02964     UINT32 NumAddPoints = pSampler->GetNumItems();
02965     
02966     if (AddPressurePointsAction::Init(this, &UndoActions, pAttrBrush, pSampler, (UINT32)StartPressureIndex, NumAddPoints,  &pAddAction) == AC_FAIL)
02967         return FALSE;
02968     
02969     // invalidate the whole thing3
02970     pAttrBrush->ClearCachedRect();
02971     Rect = pAttrBrush->GetAttrBoundingRect(EndPath);    
02972     EndPath->ReleaseCached();
02973     if (!DoInvalidateRegion(pSpread, Rect))
02974         return FALSE;
02975     return TRUE;
02976     
02977 }

INT32 OpFreeHand::GetCurrentLineWidth void   )  [protected]
 

Determines the current line width (the width of any future lines the user will draw) by asking the attribute manager.

Author:
Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
Date:
22/1/97
Returns:
A line width in millipoints. NOTE that this is half the normal linewidth value (i.e. it's the line radius)

Definition at line 2302 of file opfree.cpp.

02303 {
02304     ERROR3IF(GetWorkingDoc() == NULL, "No working doc!");
02305     AttrLineWidth *pWidthAttr = (AttrLineWidth *) GetWorkingDoc()->GetAttributeMgr().
02306                     GetCurrentAttribute(CC_RUNTIME_CLASS(NodeRenderableInk), CC_RUNTIME_CLASS(AttrLineWidth));
02307     INT32 LineWidth = 1000;
02308     if (pWidthAttr != NULL)
02309         LineWidth = pWidthAttr->Value.LineWidth / 2;
02310 
02311     return(LineWidth);
02312 }

NodeGroup * OpFreeHand::GetParentOfNodePath  )  [protected]
 

To determine if the current selection is a blend on a curve.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
26/9/99 inputs : - outputs: returns: pointer to the first nodeblend in the selection if successful, NULL otherwise

Definition at line 3328 of file opfree.cpp.

03329 {
03330     NodeGroup* pParent = NULL;
03331     if (m_pNewNodePath == NULL)  // if no blendpath node then there won't be a group parent
03332         return NULL;
03333     if (!m_pNewNodePath->IsANodeBlendPath())
03334         return NULL;
03335 
03336     pParent = (NodeGroup*)m_pNewNodePath->FindParent();
03337     
03338     ERROR2IF(!(pParent->IS_KIND_OF(NodeGroup)), NULL, "Node is not NodeGroup");
03339     return pParent;
03340 
03341 }

SimpleJoinType OpFreeHand::GetSimpleJoinType Path pNewPath,
Path pExistingPath
[protected]
 

This function works out how the new path joins onto the existing path.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
21/6/2000
Parameters:
pNewNodePath - The New Path that we want to join to an existing path [INPUTS] ReversedPath
Returns:
the type of join, (an enumerated type, see the header file)

Definition at line 3183 of file opfree.cpp.

03184 {
03185     if (pNewPath == NULL || pExistingPath == NULL)
03186         return JOINTYPE_NONE;
03187 
03188     // we must join to one of these paths
03189     if (StartPath == NULL && EndPath == NULL)
03190         return JOINTYPE_NONE;
03191     // get the new coordinate array
03192     DocCoord* pNewCoords = pNewPath->GetCoordArray();
03193     if (pNewCoords == NULL)
03194         return JOINTYPE_NONE;
03195 
03196     INT32 NumCoords = pNewPath->GetNumCoords();
03197     // get the start and end coordinates of the new path
03198     DocCoord NewStart = pNewCoords[0];
03199     DocCoord NewEnd   = pNewCoords[NumCoords-1];
03200     
03201     SimpleJoinType ReturnVal = JOINTYPE_NONE;
03202     /*
03203     DocCoord* pOldCoords = pExistingPath->GetCoordArray();
03204     if (pOldCoords != NULL)
03205     {
03206         INT32 NumOldCoords = pExistingPath->GetNumCoords();
03207         DocCoord OldStart = pOldCoords[0];
03208         DocCoord OldEnd = pOldCoords[NumOldCoords -1];
03209 
03210         // ok we don't have to be dead on here
03211         MILLIPOINT NewStartToOldStart = NewStart.Distance(OldStart);
03212         MILLIPOINT NewStartToOldEnd   = NewStart.Distance(OldEnd);
03213     
03214         // for some reason these must be reversed
03215         if (NewStartToOldStart < NewStartToOldEnd)
03216             ReturnVal = JOINTYPE_NEWSTART_TO_OLDSTART;
03217         else
03218             ReturnVal = JOINTYPE_NEWSTART_TO_OLDEND;
03219     }
03220     return Retural;
03221     */
03222     if (StartPath != NULL)
03223     {
03224         DocCoord* pOldCoords = StartPath->InkPath.GetCoordArray();
03225         if (pOldCoords != NULL)
03226         {
03227             INT32 NumOldCoords = StartPath->InkPath.GetNumCoords();
03228             DocCoord OldStart = pOldCoords[0];
03229             DocCoord OldEnd = pOldCoords[NumOldCoords -1];
03230 
03231             // ok we don't have to be dead on here
03232             MILLIPOINT NewStartToOldStart = (MILLIPOINT)NewStart.Distance(OldStart);
03233             MILLIPOINT NewStartToOldEnd   = (MILLIPOINT)NewStart.Distance(OldEnd);
03234         
03235             
03236             if (NewStartToOldStart < NewStartToOldEnd)
03237                 ReturnVal = JOINTYPE_NEWSTART_TO_OLDSTART;
03238             else
03239                 ReturnVal = JOINTYPE_NEWSTART_TO_OLDEND;
03240         }
03241     }
03242 
03243     // if endpath is not null then we are joining the end of the new path to one of the
03244     // ends of the old path
03245     if (EndPath != NULL)
03246     {
03247         DocCoord* pOldCoords = EndPath->InkPath.GetCoordArray();
03248         if (pOldCoords != NULL)
03249         {
03250             INT32 NumOldCoords = EndPath->InkPath.GetNumCoords();
03251             DocCoord OldStart = pOldCoords[0];
03252             DocCoord OldEnd = pOldCoords[NumOldCoords-1];
03253 
03254             // ok we don't have to be dead on here
03255             const MILLIPOINT NewEndToOldStart = (MILLIPOINT)NewEnd.Distance(OldStart);
03256             const MILLIPOINT NewEndToOldEnd   = (MILLIPOINT)NewEnd.Distance(OldEnd);
03257         
03258             if (NewEndToOldStart < NewEndToOldEnd)
03259                 ReturnVal = JOINTYPE_NEWEND_TO_OLDSTART;
03260             else
03261                 ReturnVal = JOINTYPE_NEWEND_TO_OLDEND;
03262         }
03263     }
03264 
03265     return ReturnVal;       
03266 }

OpState OpFreeHand::GetState String_256 Description,
OpDescriptor
[static]
 

Find out the state of the operation at the specific time.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/7/93
Parameters:
Description - GetState fills this string with an approriate description [OUTPUTS] of the current state of the push tool
Returns:
The state of the operation, so that menu items (ticks and greying can be done properly

Reimplemented in OpDrawBrush.

Definition at line 3306 of file opfree.cpp.

03307 {
03308     OpState Blobby;
03309     
03310     return Blobby;
03311 }

BOOL OpFreeHand::InsertChangeBlendStepsAction NodeBlend pNodeBlend  )  [protected]
 

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.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
1/9/99
Parameters:
the node blend to change [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: Error2 if pNodeBlend is null

See also:
called from OpNodePathEditBlob::DragFinished or OpNodePathAddEndpoint::CompleteThisPath

Definition at line 3362 of file opfree.cpp.

03363 {
03364     
03365     ERROR2IF(pNodeBlend==NULL, FALSE, "NodeBlend pointer is NULL");
03366     // do we wish to keep the distance between steps constant?  If so then determine
03367     // how many steps we need with the new length and make an action.
03368     if (pNodeBlend->GetEditState() == EDIT_DISTANCE)
03369     {
03370         UINT32 NewNumSteps = 0;
03371         UINT32 OldNumSteps = pNodeBlend->GetNumBlendSteps();
03372         double DistanceEntered = pNodeBlend->GetDistanceEntered();
03373         NodeBlendPath* pNodeBlendPath = pNodeBlend->GetNodeBlendPath(0);
03374         double NewPathLength = pNodeBlendPath->GetPathLength();
03375         
03376         NewNumSteps = (UINT32)(NewPathLength/DistanceEntered);
03377         
03378 
03379         BOOL ok = TRUE;  
03380     
03381         if (OldNumSteps != NewNumSteps)
03382         {
03383             ChangeBlendStepsAction* pAction;
03384                     ok = (InvalidateBoundsAction::Init(this,&UndoActions,pNodeBlend,TRUE) != AC_FAIL);
03385             if (ok) ok = ChangeBlendStepsAction::Init(this,&UndoActions,pNodeBlend,OldNumSteps,DistanceEntered, &pAction) != AC_FAIL;
03386             if (ok) ok = DoInvalidateNodeRegion(pNodeBlend,TRUE,FALSE);
03387             if (ok) ok = (InvalidateBoundsAction::Init(this,&UndoActions,pNodeBlend,TRUE) != AC_FAIL);
03388             pNodeBlend->SetNumBlendSteps(NewNumSteps);
03389 
03390             // added 9/9/99 now we are shifting the last object along the curve to ensure precise
03391             // step distances.  To do this we must set the proportion of the curve used in the 
03392             // NodeBlender objects.
03393             if (ok)
03394             {
03395                 double PathDistanceUsed = NewNumSteps * DistanceEntered;
03396                 double PathProportion = PathDistanceUsed / NewPathLength;
03397 //              double PathDistanceUnused = NewPathLength - PathDistanceUsed;
03398 
03399                 if (PathProportion != 1.0)
03400                 {
03401                     //ChangeBlenderAction* pBlenderAction;
03402                     ChangeBlenderOpParam BlenderParam;
03403                     BlenderParam.m_ChangeType = CHANGEBLENDER_PATHEND;
03404                             
03405                 
03406                     NodeBlender* pNodeBlender = pNodeBlend->FindFirstBlender();
03407                     INT32 NumBlenders = pNodeBlend->GetNumBlenders();
03408                         
03409                     while (pNodeBlender != NULL)
03410                     {
03411                         NumBlenders--;
03412                         if (NumBlenders ==0)
03413                         {                                   
03414                             BlenderParam.m_NewPathEnd = PathProportion;
03415                             ok = ChangeBlenderAction::Init(this, &UndoActions, pNodeBlender, BlenderParam);
03416                             if (ok)
03417                             {
03418                                 DocCoord NewPoint;
03419                                 double ExtraParam = 0.0;  //passed to the function but not used afterwards
03420                                 ok = pNodeBlender->GetPointOnNodeBlendPath(1.0,&NewPoint,&ExtraParam);
03421 
03422                                 if (ok)
03423                                 {
03424                                     NodeRenderableInk* pEnd = pNodeBlender->GetNodeEnd();
03425                                     NodeBlend* pNodeBlend = pNodeBlender->GetNodeBlend();
03426 
03427                                     ok = ((pEnd != NULL) && (pNodeBlend != NULL));
03428                     
03429                                     if (ok) 
03430                                         ok = pNodeBlend->TransformNodeToPoint(pEnd,&NewPoint,this,ExtraParam);
03431                                 }
03432                             }
03433                         }   
03434                         pNodeBlender = pNodeBlend->FindNextBlender(pNodeBlender);
03435                     }
03436                 } // end if (pathproportion..
03437             } // end if(ok)
03438 
03439         }
03440     }
03441 
03442     return TRUE;
03443 }

BOOL OpFreeHand::InsertNewNode NodePath pNewNode,
DocRect Invalid,
Node pOldNode,
Node pOtherOld = NULL
[protected]
 

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.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
3/5/94
Parameters:
pNewNode - The Node to add to the tree [INPUTS] Invalid - The region that needs to be invalidated pOldNode - The node that we joined to and will be hidden pOtherOld - We may need to hide 2 other paths. This should be NULL(default) or a pointer to a Node
Returns:
TRUE if it worked, FALSE if not

Definition at line 1999 of file opfree.cpp.

02000 {
02001     // Insert the new object
02002     if (!DoInsertNewNode(pNewNode, pOldNode, NEXT, FALSE))
02003         return FALSE;
02004 
02005     // Invalidate the appropriate region.
02006     // if the filled state of the path has changed, then we need to invalidate the whole thing
02007     ENSURE(pOldNode->IsKindOf(CC_RUNTIME_CLASS(NodePath)), "Joining to something that was not a path");
02008 
02009 //  if (pNewNode->InkPath.IsFilled == ((NodePath*)pOldNode)->InkPath.IsFilled)
02010 //  {
02011         // Invalidate only the new bit
02012 //      Spread* pSpread = pOldNode->FindParentSpread();
02013 //      if (!DoInvalidateRegion(pSpread, Invalid))
02014 //          return FALSE;
02015 //  }
02016 //  else
02017 
02018     // invalidate the whole thing
02019     if (!DoInvalidateNodeRegion(pNewNode, TRUE))
02020         return FALSE;       
02021 
02022     // Hide the original
02023     if (!DoHideNode(pOldNode, TRUE))
02024         return FALSE;
02025 
02026     // and hide the other original node if it existed
02027     if (pOtherOld != NULL)
02028         if (!DoHideNode(pOtherOld, TRUE))
02029             return FALSE;
02030 
02031     // Make sure that the Selection Cache is refreshed
02032     SelRange* Selection = GetApplication()->Selection;
02033     if (Selection)
02034         Selection->Update();
02035 
02036     // It worked
02037     return TRUE;
02038 }

BOOL OpFreeHand::InsertSmoothCurveWithUndo NodePath pNewNodePath  )  [protected]
 

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.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
25/10/94
Parameters:
pNewNodePath - The NodePath that we are trying to insert into the tree [INPUTS]
Returns:
TRUE if it worked, FALSE if it failed

Definition at line 954 of file opfree.cpp.

00955 {
00956     // Start the selection operation.
00957     // We don't want to save the end selection status because it will be
00958     // restored automatically when the operation is redone
00959     if (!DoStartSelOp(FALSE))
00960         return FALSE;
00961 
00962     // Now we have to go round testing if we are joined to any other paths
00963     // and building the appropriate undo info
00964     if ((StartPath!=NULL) && (StartPath==EndPath) &&
00965         (pJoinInfo->IsNearEndPoint==FALSE) && (IsEndNearEndpoint==FALSE))
00966     {
00967         // This is a case for replacing a section of an existing path
00968         // with the new smoothed path
00969         if (!TryToReplaceSection(pNewNodePath))
00970             return FALSE;
00971     }
00972     else
00973     {
00974         // If the new curve did not start and end on the same path we made need to make a few adjustments
00975         if (pJoinInfo->IsNearEndPoint==FALSE)
00976             StartPath = NULL;
00977 
00978         // this is a case for inserting the path in the normal way
00979         // Joining it to other paths that it touches etc
00980         if (!TryToJoinNewPathWithOthers(pNewNodePath))
00981             return FALSE;
00982     }
00983 
00984     // Clear the Node path that we made, as it will have been copied into the tree by now
00985     pNewNodePath->CascadeDelete();
00986     delete pNewNodePath;
00987 
00988     // It all worked ok
00989     return TRUE;
00990 }

BOOL OpFreeHand::InvalidateBrushRegion NodePath pNodePath  )  [protected]
 

We may be editing a brushed path with the FHT so this function makes sure the correct region is invalidated.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
26/9/99 inputs : pNodePath - the nodepath that we have edited outputs: returns: TRUE if successful, or if we are not really editing a brush, FALSE if something went wrong

Definition at line 3463 of file opfree.cpp.

03464 {
03465     ERROR2IF(pNodePath == NULL, FALSE, "Nodepath is NULL in OpFreeHand::InvalidateBrushRegion");
03466     
03467     AttrBrushType* pAttrBrush;
03468     NodeAttribute* pAttr = NULL;
03469     BrushHandle Handle = BrushHandle_NoBrush;
03470     pNodePath->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
03471     if (pAttr != NULL)
03472     {
03473         pAttrBrush = (AttrBrushType*)pAttr;
03474         Handle = pAttrBrush->GetBrushHandle();
03475     }
03476     if (Handle == BrushHandle_NoBrush)
03477         return TRUE;
03478 
03479     pAttrBrush->ClearCachedRect();
03480     pAttrBrush->FlushCache();
03481     DocRect BRect = pNodePath->GetBoundingRect(); //pAttrBrush->GetAttrBoundingRect(pNodePath);
03482     pNodePath->ReleaseCached();
03483     pAttrBrush->ClearCachedRect();
03484     Spread* pSpread = Document::GetSelectedSpread();
03485     if (pSpread != NULL)
03486         return DoInvalidateRegion(pSpread, BRect);  
03487     else 
03488         return FALSE;
03489 }

virtual BOOL OpFreeHand::IsBrushOp  )  [inline, virtual]
 

Reimplemented in OpDrawBrush.

Definition at line 200 of file opfree.h.

00200 { return FALSE;}

BOOL OpFreeHand::LoadCursors  )  [protected]
 

Tries to Load all the cursors used by the Free Hand Operation.

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

Definition at line 352 of file opfree.cpp.

00353 {
00354     // Try to load all the cursors
00355     if (FreeHandTool::FreehandPtrCrosshair)
00356     {
00357         pFreeHandCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDTOOLCURSOR_X));
00358         pJoinCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDJOINCURSOR_X));
00359         pRubOutCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDRUBOUTCUR_X));
00360         pStraightCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDLINECURSOR_X));
00361         pModifyCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDMODIFY_X));
00362     }
00363     else
00364     {
00365         pFreeHandCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDTOOLCURSOR));
00366         pJoinCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDJOINCURSOR));
00367         pRubOutCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDRUBOUTCUR));
00368         pStraightCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDLINECURSOR));
00369         pModifyCursor = new Cursor(TOOLID_FREEHAND, _R(IDC_FREEHANDMODIFY));
00370     }
00371 
00372     // See if any of them failed
00373     if ((pRubOutCursor==NULL)   || (!pRubOutCursor->IsValid()) ||
00374         (pJoinCursor==NULL)     || (!pJoinCursor->IsValid()) ||
00375         (pFreeHandCursor==NULL) || (!pFreeHandCursor->IsValid()) ||
00376         (pStraightCursor==NULL) || (!pStraightCursor->IsValid()) ||
00377         (pModifyCursor==NULL)   || (!pModifyCursor->IsValid()))
00378     {
00379         // They did, so Tidy up and exit
00380         delete pFreeHandCursor;
00381         delete pJoinCursor;
00382         delete pStraightCursor;
00383         delete pRubOutCursor;
00384         delete pModifyCursor;
00385 
00386         // Set them all to NULL
00387         pFreeHandCursor = NULL;
00388         pJoinCursor = NULL;
00389         pStraightCursor = NULL;
00390         pRubOutCursor = NULL;
00391         pModifyCursor = NULL;
00392 
00393         // fail
00394         return FALSE;
00395     }
00396 
00397     // It worked
00398     return TRUE;
00399 }

NodePath * OpFreeHand::MakeCopy Node pOriginalNode  )  [protected]
 

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).

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
3/5/94
Parameters:
pOriginalNode - The node to do a 'deep' copy of [INPUTS]
Returns:
The Copied node (NULL if it failed)

Definition at line 1964 of file opfree.cpp.

01965 {
01966     // Make a copy of the node we are joining with
01967     Node* pNode;
01968     if (!pOriginalNode->NodeCopy(&pNode))
01969         return NULL;
01970     
01971     // Check that the new node is OK
01972     ENSURE(pNode!=NULL, "Copied node seems to be NULL in TryToJoinNewPathWithOthers()");
01973     ENSURE(pNode->IsKindOf(CC_RUNTIME_CLASS(NodePath)), "Copied node was not a Path");
01974 
01975     // return it
01976     return (NodePath*) pNode;
01977 }

BOOL OpFreeHand::PrepareTrackDataPath  )  [protected]
 

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.

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

Definition at line 299 of file opfree.cpp.

00300 {
00301     TrackData->ClearPath();
00302     TrackData->FindStartOfPath();
00303 
00304     // Check to see if we want to store pressure, and if so initialise the path.
00305     AddPressureToPath = FALSE;
00306 
00307 // WEBSTER - markn 25/4/97
00308 // No pen stuff required in Webster
00309 // Taken out by vector stroking code Neville 2/10/97
00310 #ifdef VECTOR_STROKING
00311     
00312     CCPen *pPen = GetApplication()->GetPressurePen();
00313     if (IsBrushOp() && pPen != NULL && pPen->GetPressureMode() != PressureMode_None)
00314     {
00315         // Yep, we want the pressure info
00316         AddPressureToPath = TRUE;
00317 
00318         // Lets gets the pressure for the first point in the path.
00319         FreeHandPressure = pPen->GetPenPressure();
00320 
00321         // try and initialise the extra info in the path and fail if not
00322         if (!TrackData->InitExtraInfo(CI_PRESSURE))
00323             return FALSE;
00324     }
00325     
00326 #endif // VECTOR_STROKING
00327 
00328     // insert the initial coord into the path and fail if it fails
00329     if (!TrackData->InsertMoveTo(StartPoint))
00330         return FALSE;
00331 
00332     // and add pressure info if needed
00333     if (AddPressureToPath)
00334         TrackData->AddExtraInfo(CI_PRESSURE, FreeHandPressure);
00335 
00336     // and tell someone that we did it
00337     return TRUE;
00338 }

void OpFreeHand::RemoveCursors  )  [protected]
 

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.

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

Definition at line 414 of file opfree.cpp.

00415 {
00416     // Get rid out my cursor from the top of the cursor stack
00417     CursorStack::GPop(CurrentCursorID);
00418     MyCurrentCursor = NULL;
00419     CurrentCursorID = 0;
00420 
00421     // destroy all the cursors that I had allocated
00422     delete pFreeHandCursor;
00423     delete pJoinCursor;
00424     delete pRubOutCursor;
00425     delete pStraightCursor;
00426     delete pModifyCursor;
00427 
00428     // and make sure that they all point to something sensible
00429     pFreeHandCursor = NULL;
00430     pJoinCursor = NULL;
00431     pStraightCursor = NULL;
00432     pRubOutCursor = NULL;
00433     pModifyCursor = NULL;
00434 }

void OpFreeHand::RenderDragBlobs DocRect  Rect,
Spread pSpread,
BOOL  bSolidDrag
[virtual]
 

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.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
6/9/93
Parameters:
Rect - The Rectangle that needs to be re-rendered [INPUTS] pSpread - The spread on which the rendering is to take place
See also:
OpFreeHand::RenderLineSegment()

Reimplemented from Operation.

Reimplemented in OpDrawBrush.

Definition at line 2332 of file opfree.cpp.

02333 {
02334     // Work out which bits we need to draw
02335     DocRect* pRect;
02336     if (Rect.IsEmpty())
02337         pRect = NULL;
02338     else
02339         pRect = &Rect;
02340 
02341     // Get information on the path we've got to render
02342     INT32 NumCoords = TrackData->GetNumCoords();
02343 //  DocCoord* Coords = TrackData->GetCoordArray();
02344 
02345     // Find out the current line width
02346 //  INT32 LineWidth = GetCurrentLineWidth();
02347 
02348     // Render into all the available rectangles
02349     RenderRegion* pRegion = DocView::RenderOnTop(pRect, pSpread, UnclippedEOR );
02350     while ( pRegion )
02351     {
02352         // Rather than just calling DrawPath, we render each line ourselves - this allows the variable
02353         // width indicator lines to be rendered as well, and ensures all the EORing remains consistent.
02354         for (INT32 n = 1; n < NumCoords; n++)
02355             RenderLine(pRegion, TrackData, n);
02356 
02357         // get the next region to draw in
02358         pRegion = DocView::GetNextOnTop(pRect);
02359     }
02360 
02361     if (IsStraightLineMode)
02362         RenderEorStraightLine(pRect, pSpread);
02363 }

void OpFreeHand::RenderEorStraightLine RenderRegion pRender,
DocCoord pStart,
INT32  StartWidth,
DocCoord pEnd,
INT32  EndWidth
[protected]
 

Simply renders a straight-line segment to the given render region.

However, it may also elect to show width information by rendering parallel straight line segments on either side of the line

Definition at line 2212 of file opfree.cpp.

02215 {
02216     ERROR3IF(pRender == NULL || pStart == NULL || pEnd == NULL, "illegal NUL params");
02217 
02218     // Always draw the blue centreline
02219     pRender->SetLineColour(COLOUR_XORNEW);
02220     pRender->DrawLine(*pStart, *pEnd);
02221 
02222 // Taken out by vector stroking code Neville 2/10/97
02223 #ifdef VECTOR_STROKING
02224 
02225     // If we're showing pressure info, and the points are not coincident, and
02226     // the average line width is large enough to avoid hideous EOR mess, render parallel width lines
02227     if (AddPressureToPath && *pStart != *pEnd && (StartWidth + EndWidth) > pRender->GetPixelWidth() * 8)
02228     {
02229         pRender->SetLineColour(COLOUR_MIDGREY);
02230 
02231         // Get the normal to the centreline and normalise it to unit length
02232         NormCoord Normal( pStart->y - pEnd->y, -(pStart->x - pEnd->x));
02233         Normal.Normalise();
02234 
02235         // Now plot the two parallel edges
02236         DocCoord P1(pStart->x + (INT32)(Normal.x * StartWidth), pStart->y + (INT32)(Normal.y * StartWidth));
02237         DocCoord P2(pEnd->x   + (INT32)(Normal.x * EndWidth),   pEnd->y   + (INT32)(Normal.y * EndWidth));
02238         pRender->DrawLine(P1, P2);
02239 
02240         DocCoord P3(pStart->x - (INT32)(Normal.x * StartWidth), pStart->y - (INT32)(Normal.y * StartWidth));
02241         DocCoord P4(pEnd->x   - (INT32)(Normal.x * EndWidth),   pEnd->y   - (INT32)(Normal.y * EndWidth));
02242         pRender->DrawLine(P3, P4);
02243     }
02244 
02245 #endif // VECTOR_STROKING
02246 }

void OpFreeHand::RenderEorStraightLine DocRect ,
Spread pSpread,
DocCoord Start = NULL,
DocCoord End = NULL
[protected]
 

Renders the EORed rubber band straight line that is used in Straight Line Mode. It simply draws a single Straight Line segment.

Definition at line 2266 of file opfree.cpp.

02267 {
02268     if (pStart == NULL)
02269         pStart = &PreviousPoint;
02270     if (pEnd == NULL)
02271         pEnd = &StraightLinePos;
02272 
02273     INT32 LineWidth = GetCurrentLineWidth();
02274 
02275     RenderRegion* pRegion = DocView::RenderOnTop(Rect, pSpread, UnclippedEOR );
02276     while ( pRegion )
02277     {
02278         RenderEorStraightLine(pRegion, pStart, LineWidth, pEnd, LineWidth);
02279 
02280         // get the next region to draw in
02281         pRegion = DocView::GetNextOnTop(Rect);
02282     }
02283 }

void OpFreeHand::RenderLine DocRect Rect,
Spread pSpread,
Path pPath,
INT32  Index,
BOOL  StartIndex = FALSE
[protected]
 

Renders an EORed line between 2 coordinates in the path. May render pressure information feedback using the pressure stored with the path corrdinates.

Author:
Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
Date:
28/1/97
Parameters:
pRender - the RenderReigon to draw into [INPUTS] pPath - The path to draw a line segment from CoodIndex - The index of the end coordinate of the line to render. This will render an EORed line from Coords[CoordIndex-1] to Coords[CoordIndex] StartIndex - is Index the start index? If not then we simply render between Index-1 and Index, otherwise we render between Index and NumCoords -1

Definition at line 2177 of file opfree.cpp.

02178 {
02179     RenderRegion* pRegion = DocView::RenderOnTop(Rect, pSpread, UnclippedEOR );
02180     while ( pRegion )
02181     {
02182         RenderLine(pRegion, pPath, Index, StartIndex);
02183 
02184         // get the next region to draw in
02185         pRegion = DocView::GetNextOnTop(Rect);
02186     }   
02187 }

void OpFreeHand::RenderLine RenderRegion pRender,
Path pPath,
INT32  Index,
BOOL  StartIndex = FALSE
[protected]
 

Renders an EORed line between 2 coordinates in the path. May render pressure information feedback using the pressure stored with the path corrdinates.

Author:
Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
Date:
28/1/97
Parameters:
pRender - the RenderReigon to draw into [INPUTS] pPath - The path to draw a line segment from CoodIndex - The index of the end coordinate of the line to render. This will render an EORed line from Coords[CoordIndex-1] to Coords[CoordIndex] StartIndex - is Index the start index? If not then we simply render between Index-1 and Index, otherwise we render between Index and NumCoords -1

Definition at line 2092 of file opfree.cpp.

02093 {
02094     ERROR3IF(pRender == NULL || pPath == NULL, "illegal NUL params");
02095     ERROR3IF(Index < 1 || Index >= pPath->GetNumCoords(), "Out of range coordinate");
02096 
02097     INT32 EndIndex = Index;
02098     if (!StartIndex)
02099     {
02100         EndIndex = Index;
02101         
02102     }
02103     else
02104         EndIndex = pPath->GetNumCoords() - 1;
02105 
02106     Index--;
02107     DocCoord *Coord = pPath->GetCoordArray();
02108 
02109     // Always draw the blue centreline
02110     pRender->SetLineColour(COLOUR_XORNEW);
02111     pRender->DrawLine(Coord[Index], Coord[EndIndex]);
02112     
02113 // Taken out by vector stroking code Neville 2/10/97
02114 #ifdef VECTOR_STROKING
02115 
02116     // If we're showing pressure info, and the points are not coincident, and the average
02117     // line width is large enough to avoid hideous EOR mess, render parallel width lines
02118     if (!AddPressureToPath)
02119         return;
02120 
02121     PathWidth *Width = pPath->GetWidthArray();
02122     if (Width == NULL)
02123         return;
02124 
02125     const INT32 CurrentLineWidth = GetCurrentLineWidth();
02126     const double WidthMul = (double)CurrentLineWidth / (double)EXTRAVALUEMAX;
02127     INT32 StartWidth    = (INT32) ((double)Width[Index-1] * WidthMul);
02128     INT32 EndWidth  = (INT32) ((double)Width[Index]   * WidthMul);
02129 
02130     if (StartWidth > CurrentLineWidth)      // Damage control in case of rampant pens giving
02131         StartWidth = CurrentLineWidth;      // out of range values. You may laugh, but that's
02132     if (EndWidth > CurrentLineWidth)        // exactly what the pen code was doing until I
02133         EndWidth = CurrentLineWidth;        // just fixed it. This just stops wild rampancy
02134 
02135     if (Coord[Index-1] != Coord[Index] && (StartWidth + EndWidth) > pRender->GetPixelWidth() * 8)
02136     {
02137         pRender->SetLineColour(COLOUR_MIDGREY);
02138 
02139         // Get the normal to the centreline and normalise it to unit length
02140         NormCoord Normal( Coord[Index-1].y - Coord[Index].y, -(Coord[Index-1].x - Coord[Index].x));
02141         Normal.Normalise();
02142 
02143         // Now plot the two parallel edges
02144         DocCoord P1(Coord[Index-1].x + (INT32)(Normal.x * StartWidth), Coord[Index-1].y + (INT32)(Normal.y * StartWidth));
02145         DocCoord P2(Coord[Index].x   + (INT32)(Normal.x * EndWidth),   Coord[Index].y   + (INT32)(Normal.y * EndWidth));
02146         pRender->DrawLine(P1, P2);
02147 
02148         DocCoord P3(Coord[Index-1].x - (INT32)(Normal.x * StartWidth), Coord[Index-1].y - (INT32)(Normal.y * StartWidth));
02149         DocCoord P4(Coord[Index].x   - (INT32)(Normal.x * EndWidth),   Coord[Index].y   - (INT32)(Normal.y * EndWidth));
02150         pRender->DrawLine(P3, P4);
02151     }
02152 #endif // VECTOR_STROKING
02153 }

BOOL OpFreeHand::ReplaceMiddleOfPath NodePath pNewPath,
INT32  FirstChangedIndex,
INT32  NumElements
[protected]
 

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.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
1/11/94
Parameters:
pNewPath - The New curve that has just been draw [INPUTS] FirstChangedIndex - The first coord in the path that is to be replaced NumElements - The number of elements in the path that need replacing
Returns:
TRUE if it worked, FALSE if not

Definition at line 2741 of file opfree.cpp.

02742 {
02743     // if we are a brush then do our special smoothing
02744     //etroSmoothBrush(pNewPath);
02745 
02746     // if we are editing a brush with timestamp or pressure lists 
02747     EditBrushLists(pNewPath, FirstChangedIndex, NumElements);
02748     
02749     // If there is something to remove from this path, the do it
02750     if (NumElements>0)
02751     {
02752         // Make ourselves an insert action as we are about to delete the
02753         // old bits of the path we no longer want
02754         ActionCode Act;
02755         InsertPathElementAction* ModAction;
02756         Act = InsertPathElementAction::Init(this, &UndoActions, NumElements, FirstChangedIndex,
02757                                            (Action**)(&ModAction));
02758     
02759         // Did we init the action ok
02760         if (Act == AC_FAIL)
02761             return FALSE;
02762 
02763         // If the action will let us, record the details of what we want to delete
02764         if (Act!=AC_NORECORD)
02765         {
02766             // We need to store info about all the elements we are going to delete
02767             PathVerb* ChangedVerbs;
02768             DocCoord* ChangedCoords;
02769             PathFlags* ChangedFlags;
02770 
02771             // Get some memory to put them in
02772             ALLOC_WITH_FAIL(ChangedVerbs, (PathVerb*)CCMalloc(NumElements*sizeof(PathVerb)), this);
02773             ALLOC_WITH_FAIL(ChangedCoords, (DocCoord*)CCMalloc(NumElements*sizeof(DocCoord)), this);
02774             ALLOC_WITH_FAIL(ChangedFlags, (PathFlags*)CCMalloc(NumElements*sizeof(PathFlags)), this);
02775 
02776             // Make sure that we got the memory
02777             if (!ChangedVerbs || !ChangedCoords || !ChangedFlags)
02778             {
02779                 if (ChangedVerbs) CCFree(ChangedVerbs);
02780                 if (ChangedCoords) CCFree(ChangedCoords);
02781                 if (ChangedFlags) CCFree(ChangedFlags);
02782                 return FALSE;
02783             }
02784 
02785             // Get pointers to all the arrays of the path
02786             PathVerb* Verbs = EndPath->InkPath.GetVerbArray();
02787             DocCoord* Coords = EndPath->InkPath.GetCoordArray();
02788             PathFlags* Flags = EndPath->InkPath.GetFlagArray();
02789 
02790             // Now copy the data from the path into the arrays
02791             for (INT32 i=0; i<NumElements; i++)
02792             {
02793                 ChangedVerbs[i] = Verbs[FirstChangedIndex+i];
02794                 ChangedCoords[i] = Coords[FirstChangedIndex+i];
02795                 ChangedFlags[i] = Flags[FirstChangedIndex+i];
02796             }
02797 
02798             // And ask the action to make a note of them
02799             ModAction->RecordPath(ChangedVerbs, ChangedFlags, ChangedCoords, EndPath);
02800         }
02801 
02802         // Delete the elements from the path that we no longer want
02803         EndPath->InkPath.DeleteSection(FirstChangedIndex, NumElements);
02804     }
02805 
02806 
02807     // Next we have to add the new points, so build undo to remove them
02808     Action* UnAction;
02809     INT32 NumCoords = pNewPath->InkPath.GetNumCoords() - 1;
02810     ActionCode Act = RemovePathElementAction::Init(this, &UndoActions, NumCoords,
02811                                                 FirstChangedIndex, (Action**)(&UnAction));
02812 
02813     // if we failed to init the action, then fail this operation
02814     if (Act==AC_FAIL)
02815         return FALSE;
02816 
02817     // record details about the path in the action before we start inserting stuff
02818     ((RemovePathElementAction*)UnAction)->RecordPath(EndPath);
02819 
02820     // Insert stuff here
02821     EndPath->InkPath.InsertSection(FirstChangedIndex, NumCoords);
02822 
02823     // Get pointers to all the arrays of the original path
02824     PathVerb* Verbs = EndPath->InkPath.GetVerbArray();
02825     DocCoord* Coords = EndPath->InkPath.GetCoordArray();
02826     PathFlags* Flags = EndPath->InkPath.GetFlagArray();
02827 
02828     // Get the pointer to all the arrays of the new path data
02829     PathVerb* NewVerbs = pNewPath->InkPath.GetVerbArray();
02830     DocCoord* NewCoords = pNewPath->InkPath.GetCoordArray();
02831     PathFlags* NewFlags = pNewPath->InkPath.GetFlagArray();
02832 
02833     // Copy the new path data into the hole we have made
02834     FirstChangedIndex--;
02835     for (INT32 i=1; i<=NumCoords; i++)
02836     {
02837         Verbs[FirstChangedIndex+i] = NewVerbs[i];
02838         Coords[FirstChangedIndex+i] = NewCoords[i];
02839         Flags[FirstChangedIndex+i] = NewFlags[i];
02840     }
02841 
02842     // set up retro stuff here
02843     SetRetroPath(EndPath, FirstChangedIndex, pNewPath->InkPath.GetNumCoords());
02844 
02845     return TRUE;
02846 }

BOOL OpFreeHand::RetroSmoothBrush NodePath pNewNodePath  )  [protected, virtual]
 

As above, the base class version does nothing.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/3/2000
Parameters:
pNewPath - The New curve that has just been drawn [INPUTS]
Returns:
TRUE

Reimplemented in OpDrawBrush.

Definition at line 3145 of file opfree.cpp.

03146 {
03147     return TRUE;
03148 }

BOOL OpFreeHand::ReverseBrushPressure  )  [protected, virtual]
 

The base class version does nothing, derived class should reverse its pressure cache.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/3/2000
Parameters:
- [INPUTS]
Returns:
TRUE

Reimplemented in OpDrawBrush.

Definition at line 3165 of file opfree.cpp.

03166 {
03167     return TRUE;
03168 }

void OpFreeHand::RubOutPath DocCoord  Pos,
Spread pSpread
[protected]
 

Tries to rub out sections of the path that have already been drawn.

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

Definition at line 1006 of file opfree.cpp.

01007 {
01008     // If the mouse has not moved then do not do anything
01009     if (Pos==PreviousPoint)
01010         return;
01011 
01012     // Get hold of the blob manager for finding out the size of a blob
01013     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
01014     if (pBlobMgr==NULL)
01015         return;
01016 
01017     // Find out about all the coords in the path already
01018     INT32 NumCoords = TrackData->GetNumCoords();
01019     DocCoord* Coords = TrackData->GetCoordArray();
01020 
01021     // Get line width information
01022 //  INT32 LineWidth = GetCurrentLineWidth();
01023 
01024     // Find out about the area round the mouse position to consider for rubbing out
01025     DocRect BlobRect;
01026     pBlobMgr->GetBlobRect(Pos, &BlobRect);
01027 
01028     // We want to look at the last few coords in the path. So work out how far back to look
01029     INT32 StopLookingAt = 1;
01030 
01031     // Loop through the last few coords to see if the mouse is near them
01032     for (INT32 i=NumCoords-2; i>StopLookingAt; i--)
01033     {
01034         if (BlobRect.ContainsCoord(Coords[i]))
01035         {
01036             // Rub out the line from this coord to the end of the line
01037             RenderRegion* pRegion = DocView::RenderOnTop( NULL, pSpread, UnclippedEOR );
01038             while (pRegion)
01039             {
01040                 for (INT32 n = i+1; n < NumCoords; n++)
01041                     RenderLine(pRegion, TrackData, n);
01042 
01043                 // Get the Next render region
01044                 pRegion = DocView::GetNextOnTop( NULL );
01045             }
01046 
01047             // Rub out the actual path Data
01048             if (!TrackData->DeleteFromElement(i+1))
01049             {
01050                 InformError(_R(IDS_PATH_DELETE_ERROR));
01051                 return;
01052             }
01053 
01054             // Change the operations parameters
01055             PreviousPoint = Coords[i];
01056             LineSegmentCount = i;
01057 
01058             // this mouse message is finished with
01059             return;
01060         }
01061     }
01062 }

void OpFreeHand::SetCursorAndStatus CursorType  CurType  )  [protected, virtual]
 

Chanages the cursor to the type requested and sets the status bar message to the appropriate message for the mode we are in.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
9/5/94
Parameters:
CurType - The type of cursor to change to [INPUTS]

Reimplemented in OpDrawBrush.

Definition at line 1349 of file opfree.cpp.

01350 {
01351     // Now, if FlipCursor = TRUE, we flip it!
01352     Cursor* WhichCursor = NULL;
01353     String_256 StatusMsg("");
01354 
01355     switch (CurType)
01356     {
01357         case NORMAL_CURSOR:
01358             WhichCursor = pFreeHandCursor;
01359             StatusMsg.Load(_R(IDS_FREEHANDDRAG));
01360             break;
01361 
01362         case JOIN_CURSOR:
01363             WhichCursor = pJoinCursor;
01364             StatusMsg.Load(_R(IDS_FREEHANDDRAGJOIN));
01365             break;
01366 
01367         case STRAIGHTLINE_CURSOR:
01368             WhichCursor = pStraightCursor;
01369             StatusMsg.Load(_R(IDS_FREEHANDSTRAIGHT));
01370             break;
01371 
01372         case RUBOUT_CURSOR:
01373             WhichCursor = pRubOutCursor;
01374             StatusMsg.Load(_R(IDS_FREEHANDRUBOUT));
01375             break;
01376 
01377         case MODIFY_CURSOR:
01378             WhichCursor = pModifyCursor;
01379             StatusMsg.Load(_R(IDS_FREEHANDMODIFY));
01380             break;
01381 
01382         default:
01383             return;
01384     }
01385 
01386     // Change Status bar message and the cursor
01387     GetApplication()->UpdateStatusBarText(&StatusMsg);
01388     if (WhichCursor != MyCurrentCursor)
01389     {
01390         // set this cursor as the current cursor and immediately display it
01391         CursorStack::GSetTop(WhichCursor, CurrentCursorID);
01392 
01393         // remember this is our current cursor
01394         MyCurrentCursor = WhichCursor;
01395     }
01396 }

void OpFreeHand::SetCursorOnMove ClickModifiers  ClickMods,
Spread pSpread,
DocCoord PointerPos
[protected]
 

Decides which cursor to display depending on which modifiers are pressed and where the mouse is. PointerPos can be changed in this function.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
25/10/94
Parameters:
ClickMods - Which modifiers were pressed [INPUTS] pSpread - The spread that the mouse is over
PointerPos - The position of the mouse [OUTPUTS]

Definition at line 454 of file opfree.cpp.

00455 {
00456     // If the mouse is over an EndPoint, use the Join Cursor
00457     if (CheckMouseOverSelectedPath(PointerPos, pSpread, &IsEndNearEndpoint))
00458     {
00459         // Ok the cursor is near the path
00460         if (IsEndNearEndpoint)
00461         {
00462             // it was near one of the paths open ends
00463             SetCursorAndStatus(JOIN_CURSOR);
00464             return;
00465         }
00466 
00467         // Must be near the middle of the path
00468         // We can only do a modify join if we started the path on the same path
00469         if ((StartPath==EndPath) && (pJoinInfo->IsNearEndPoint==FALSE))
00470         {
00471             SetCursorAndStatus(MODIFY_CURSOR);
00472             return;
00473         }
00474     }
00475 
00476     // See Which of the modifiers are down...   
00477     if (ClickMods.Alternative1)
00478     {
00479         // if Alt is down we are in Straight Line mode
00480         SetCursorAndStatus(STRAIGHTLINE_CURSOR);
00481     }
00482     else
00483     {
00484         if (ClickMods.Adjust)
00485         {
00486             // If Shift is down we are in RubOut mode
00487             SetCursorAndStatus(RUBOUT_CURSOR);
00488         }
00489         else
00490         {
00491             // No Modifiers so use the normal cursor
00492             SetCursorAndStatus(NORMAL_CURSOR);
00493         }
00494     }
00495 }

void OpFreeHand::SetRetroPath NodePath pNodePath,
INT32  Start,
INT32  Len
[protected]
 

Calls the freehand tool with details about the new path, so that future changes in the smoothness will re-build the new path.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
23/5/94
Parameters:
pNodePath - The new path that we want to set up for retro fitting [INPUTS] Start - The Start slot of the new section Len - The number of slots the new section takes up

Definition at line 2058 of file opfree.cpp.

02059 {
02060     // Go find the current tool
02061     Tool* pTool = Tool::GetCurrent();
02062     if (pTool)
02063     {
02064         // Make sure that it is the freehand tool, and if so set the previous path
02065         if (pTool->GetID()==TOOLID_FREEHAND)
02066             ((FreeHandTool*)pTool)->SetPreviousPath(pNodePath, Start, Len);
02067     }
02068 }

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

Joins a Simple 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 1563 of file opfree.cpp.

01564 {
01565     // First we should make a note of the region that we will need redrawing
01566     DocRect InvalidRect = FreePath->GetBlobBoundingRect();
01567     
01568     DocRect OriginalRect = OldPath->GetBoundingRect();
01569 //  Spread* pSpread = Document::GetSelectedSpread();
01570 
01571     InvalidateBrushRegion(OldPath);
01572 
01573 
01574     // Need to know where in the joined path that the new section will be
01575     INT32 NumSlots = FreePath->InkPath.GetNumCoords();
01576     INT32 StartSlot = 0;
01577 //  INT32 OldNumSlots = OldPath->InkPath.GetNumCoords();
01578     
01579 //  DocCoord* pCoords = FreePath->InkPath.GetCoordArray();
01580 //  DocCoord* pOldCoords = OldPath->InkPath.GetCoordArray();
01581 
01582     // Make a copy of the node we are joining with
01583     NodePath* JoinPath = MakeCopy(OldPath);
01584     if (JoinPath == NULL)
01585         return FALSE;
01586 
01587     // first find out if we are editing a brush (ugh, yes I know)
01588     AttrBrushType* pAttrBrush;
01589     NodeAttribute* pAttr = NULL;
01590     BrushHandle Handle = BrushHandle_NoBrush;
01591     JoinPath->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
01592     if (pAttr != NULL)
01593     {
01594         pAttrBrush = (AttrBrushType*)pAttr;
01595         Handle = pAttrBrush->GetBrushHandle();
01596     }
01597 
01598     // find out if the two paths join at the start
01599     BOOL PathReversed = FALSE;
01600 
01601     // before Joining them
01602     BOOL NewPathReversed = FALSE;
01603     
01604     if (Handle != BrushHandle_NoBrush)
01605         SimpleJoinBrush(JoinPath, &(FreePath->InkPath));
01606     
01607     if (!JoinPath->InkPath.SimpleJoin(&FreePath->InkPath, &StartSlot, &NewPathReversed, &PathReversed))
01608         return FALSE;
01609 
01610     // Insert the new object
01611     if (!InsertNewNode(JoinPath, InvalidRect, OldPath))
01612         return FALSE;
01613 
01614     // Reverse the original mouse move data if the fitted path was reversed
01615     if (NewPathReversed)
01616         TrackData->Reverse();
01617     
01618     // if we are a brush then we don't ever want the path reversed, 
01619     if (Handle != BrushHandle_NoBrush && PathReversed)
01620     {
01621         TrackData->Reverse();
01622         JoinPath->InkPath.Reverse();
01623         StartSlot = 0;
01624     }
01625 
01626     // Tell the freehand tool all about it
01627     SetRetroPath(JoinPath, StartSlot, NumSlots);
01628     
01629     m_pNewNodePath = JoinPath;
01630 
01631     InvalidateBrushRegion(JoinPath);
01632     
01633     // It Worked!
01634     return TRUE;
01635 }

BOOL OpFreeHand::SimpleJoinBrush NodePath pInsertedNode,
Path pNewPath
[protected, virtual]
 

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.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/3/2000
Parameters:
pInsertedNode - the nodepath that we have just inserted [INPUTS] pNewPath - The New curve that has just been drawn
Returns:
TRUE if it worked, FALSE if not

Reimplemented in OpDrawBrush.

Definition at line 2996 of file opfree.cpp.

02997 {
02998     if (pInsertedNode == NULL || pNewPath == NULL)
02999     {
03000         ERROR3("Null inputs to OpDrawBrush::SimpleJoinBrush");
03001         return FALSE;
03002     }
03003     
03004     // first check to see if the edited path has a brush applied to it,
03005     // if it doesn't then we'll leave
03006     NodeAttribute* pAttr;
03007     AttrBrushType* pAttrBrush;
03008     pInsertedNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
03009     if (pAttr == NULL)
03010         return TRUE;
03011 
03012     pAttrBrush = (AttrBrushType*)pAttr;
03013     // if our brush has a 'no brush' handle then likewise
03014     if (pAttrBrush->GetBrushHandle() == BrushHandle_NoBrush)
03015         return TRUE;
03016 
03017     // if our brush is not either timestamping or has a pressure list then we will leave aswell
03018     if (!pAttrBrush->ContainsPressureCache() && !pAttrBrush->IsTimeStamping())
03019         return TRUE;
03020 
03021     // Invalidate the brush rect
03022     NodePath* EditPath = StartPath;
03023     if (EditPath == NULL)
03024         EditPath = EndPath;
03025 
03026     if (EditPath != NULL)
03027     {
03028         pAttrBrush->ClearCachedRect();
03029         DocRect BRect = pAttrBrush->GetAttrBoundingRect(EditPath);
03030         EditPath->ReleaseCached();
03031         Spread* pSpread = Document::GetSelectedSpread();
03032         if (pSpread != NULL)
03033             DoInvalidateRegion(pSpread, BRect);
03034     }
03035     else
03036         return FALSE;
03037 
03038     // find out what type of join we have 
03039     SimpleJoinType JoinType = GetSimpleJoinType(pNewPath, &(pInsertedNode->InkPath));
03040     if (JoinType == JOINTYPE_NONE)
03041     {
03042         ERROR3("No join type in OpDrawBrush::SimpleJoinBrush");
03043         return TRUE;
03044     }
03045 
03046     // we need to know the distance along the original path where we want to insert the
03047     // new points.  As this is a simple join it will either be at the beginning or the end.
03048     // we also need to know the distance to the end of the new points, as this is required
03049     // for the undo.
03050     MILLIPOINT NewPathLength = (MILLIPOINT)pNewPath->GetPathLength();
03051     MILLIPOINT OldPathLength = -1;
03052     if (StartPath != NULL)
03053         OldPathLength = (MILLIPOINT)StartPath->InkPath.GetPathLength();
03054     else 
03055     {
03056         if (EndPath != NULL)
03057             OldPathLength = (MILLIPOINT)EndPath->InkPath.GetPathLength();
03058     }
03059 
03060     MILLIPOINT InsertDistance = -1;
03061     MILLIPOINT EndInsertDistance = -1;
03062     INT32 StartIndex = 0; //for the pressure insert
03063 
03064     // ask the attribute for its pressure cache
03065     CDistanceSampler* pAttrData = pAttrBrush->GetPressureCache();
03066     if (pAttrData == NULL)
03067         return FALSE;
03068     // according to the type of join we will want to insert our new data at the beginning or the
03069     // end, we may also want to reverse it.  We need the distance values to give to the pressure
03070     // generator function
03071     switch (JoinType)
03072     {
03073         case JOINTYPE_NEWSTART_TO_OLDSTART:
03074             InsertDistance = -1;
03075             EndInsertDistance = 0; // indicates that we are inserting at the start
03076             StartIndex = 0; 
03077             break;
03078         case JOINTYPE_NEWSTART_TO_OLDEND:
03079             InsertDistance = OldPathLength; // indicates that we are inserting at the end
03080             EndInsertDistance = -1;
03081             StartIndex = pAttrData->GetInternalIndexFromDistance(OldPathLength);
03082             if (StartIndex==-1) StartIndex = pAttrData->GetNumItems();
03083             break;
03084         case JOINTYPE_NEWEND_TO_OLDSTART:  
03085             InsertDistance = -1;
03086             EndInsertDistance = 0;
03087             StartIndex = 0; 
03088             break;
03089         case JOINTYPE_NEWEND_TO_OLDEND:
03090             InsertDistance = OldPathLength;
03091             EndInsertDistance = -1;
03092             StartIndex = pAttrData->GetInternalIndexFromDistance(OldPathLength);    
03093             if (StartIndex==-1) StartIndex = pAttrData->GetNumItems();
03094             
03095             break;
03096         default:
03097             ERROR3("Unknown join type in OpDrawBrush::SimpleJoinBrush");
03098             return FALSE;
03099     }
03100     
03101     
03102     // get ourselves a data cache to insert courtesy of our static function
03103     CDistanceSampler* pSampler = OpDrawBrush::GeneratePressureData(pAttrBrush, InsertDistance, EndInsertDistance, NewPathLength);
03104     if (pSampler == NULL)
03105         return FALSE;
03106     
03107 
03108     // make the action to insert the pressure list
03109     
03110     AddPressurePointsAction* pAction;
03111     UINT32 NumPoints = pSampler->GetNumItems();
03112 
03113     if (AddPressurePointsAction::Init(this, &UndoActions, pAttrBrush, pSampler, 
03114                                     StartIndex, NumPoints, &pAction) == AC_FAIL)
03115         return FALSE;
03116     
03117     // Invalidate the brush rect again
03118     if (EditPath != NULL)
03119     {
03120         pAttrBrush->ClearCachedRect();
03121         DocRect BRect = pAttrBrush->GetAttrBoundingRect(EditPath);
03122         EditPath->ReleaseCached();
03123         Spread* pSpread = Document::GetSelectedSpread();
03124         if (pSpread != NULL)
03125             DoInvalidateRegion(pSpread, BRect);
03126     }
03127 
03128     return TRUE;
03129 }

BOOL OpFreeHand::SmoothNewCurve NodePath pNewNodePath  )  [protected]
 

Sets up the curve fitter and finally smooths the data in the Track data buffer into the new node path.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
25/10/94
Parameters:
pNewNodePath - The NodePath that we want to smooth the track data into [INPUTS]
Returns:
TRUE if it worked, FALSE if something went wrong

Definition at line 891 of file opfree.cpp.

00892 {
00893     // To work out how much we need to smooth the curve by, we must know what
00894     // zoom factor we are currently at
00895     DocView* pDocView = DocView::GetSelected();
00896     if (pDocView==NULL)
00897         return FALSE;
00898 
00899     // When this path is built it will actually replace a section from the middle of the
00900     // original path, so we must make sure that is starts and ends on the original
00901     // The start was done before the drag started, the end can be done now.
00902     if ((StartPath!=NULL) && (StartPath==EndPath) && (pJoinInfo->IsNearEndPoint==FALSE))
00903     {
00904         // Find out the coord on the original path where the endpoint got closest to
00905         PreviousPoint = EndPath->InkPath.ClosestPointTo(Mu, CloseTo);
00906 
00907         // Add it to the path data
00908         if (TrackData->InsertLineTo(PreviousPoint))
00909         {
00910             // and add pressure info if needed
00911             if (AddPressureToPath)
00912                 TrackData->AddExtraInfo(CI_PRESSURE, FreeHandPressure);
00913         }
00914     }
00915 
00916     // Find out how flat they need the curve. This number represents
00917     // how far off the original track data the curve can get before it
00918     // is split and turned into more curves. The smaller the number
00919     // the tighter the fit.
00920     double ScaleFactor = (pDocView->GetViewScale()).MakeDouble();
00921     double ErrorLevel = (64 + (160*Smoothness)) / ScaleFactor;
00922     ErrorLevel = ErrorLevel * ErrorLevel;
00923     
00924     // FitCurve needs the number of points in the path, which is the 
00925     // number of Line Segments + 1
00926     CurveFitObject CurveFitter(&(pNewNodePath->InkPath), ErrorLevel);
00927 
00928     // Try to initialise the curve fitter
00929     if (!CurveFitter.Initialise(TrackData, LineSegmentCount+1))
00930         return FALSE;
00931 
00932     // All went ok, so fit the curve to the track data and return success
00933     CurveFitter.FitCurve();
00934     return TRUE;
00935 }

virtual BOOL OpFreeHand::SnappingDrag  )  [inline, virtual]
 

Reimplemented from Operation.

Definition at line 178 of file opfree.h.

00178 { return FALSE;  }

BOOL OpFreeHand::SplitAtPoint const DocCoord SplitPoint,
INT32 *  SplitAt,
INT32 *  NewElements
[protected]
 

Splits the path, building undo info, at the coord supplied.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
31/10/94
Parameters:
SplitPoint - The coord that we want the split to happen close to [INPUTS]
SplitAt - The Slot number of the new end point [OUTPUTS] NewElements - The number of new elements inserted into the path
Returns:
TRUE if it was able to split the path ok, FALSE if it failed

Definition at line 2508 of file opfree.cpp.

02509 {
02510     // First we need to get some space to put any new coords etc that will be generated
02511     INT32 SplitElement;
02512     UINT32 NumElements;
02513     PathVerb    NewVerbs[6];
02514     DocCoord    NewCoords[6];
02515     PathFlags   NewFlags[6];
02516 
02517     // First we had better go and see if this point is close to any of the existing coords
02518     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
02519     if (pBlobMgr!=NULL)
02520     {
02521         // There was a blob manager, so we could test the coords
02522         // Find out about the area round the mouse position to consider for rubbing out
02523         DocRect BlobRect;
02524         DocCoord BlobPoint = SplitPoint;
02525         pBlobMgr->GetBlobRect(BlobPoint, &BlobRect);
02526 
02527         // Find out about all the coords in the path already
02528         PathFlags* Flags = EndPath->InkPath.GetFlagArray();
02529         DocCoord* Coords = EndPath->InkPath.GetCoordArray();
02530         INT32 NumCoords = EndPath->InkPath.GetNumCoords();
02531 
02532         // Loop through all the coords in the path
02533         for (INT32 i=0; i<NumCoords; i++)
02534         {
02535             // Is this coord close to the point specified
02536             if ((Flags[i].IsEndPoint==TRUE) && (BlobRect.ContainsCoord(Coords[i])))
02537             {
02538                 // Yep, set the outputs to the correct values and return Success
02539                 *SplitAt = i+1;
02540                 *NewElements = 0;
02541 
02542                 // Make sure that the rotate flags are unset around the coord
02543                 Flags[i].IsRotate = FALSE;
02544                 if (i>0)
02545                     Flags[i-1].IsRotate = FALSE;
02546 
02547                 if (i<NumCoords-1)
02548                     Flags[i+1].IsRotate = FALSE;
02549 
02550                 // And tell caller that it worked
02551                 return TRUE;
02552             }
02553         }
02554     }
02555 
02556     // Try and split the path. This returns FALSE if the path did not need splitting
02557     if (EndPath->InkPath.SplitAtPoint(SplitPoint, &SplitElement, &NumElements, NewVerbs, NewCoords))
02558     {
02559         // Make a note of where the new endpoint will be
02560         *SplitAt = SplitElement+(NumElements/2);
02561         *NewElements = NumElements;
02562 
02563         // The path was split, so we know where, and how, so let's party on the path
02564         PathVerb* Verbs = EndPath->InkPath.GetVerbArray();
02565         PathFlags* Flags = EndPath->InkPath.GetFlagArray();
02566         DocCoord* Coords = EndPath->InkPath.GetCoordArray();
02567 //      UINT32  NumCoords = EndPath->InkPath.GetNumCoords();    
02568         INT32 NumToChange;
02569 
02570         // We're adding something, either a line or curve - check which
02571         if ((Verbs[SplitElement] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
02572         {
02573             // Adding a curve - number to change = 6
02574             NumToChange = 6;
02575 
02576             // Initialise the flags appropriately
02577             for (INT32 i=0; i<6; i++)
02578             {
02579                 // copy the flags from the old curve (repeat the flag in the first 3 elements)
02580                 NewFlags[i] = Flags[SplitElement+(i%3)];
02581                 NewFlags[i].IsSelected = FALSE;
02582 
02583                 // Want it to be a cusp in the middle
02584                 if ((i>0) && (i<4))
02585                 {
02586                     NewFlags[i].IsSmooth = FALSE;
02587                     NewFlags[i].IsRotate = FALSE;
02588                 }
02589             }
02590 
02591             // If this was happening at the end of a path,
02592             // then make sure the close figure flag is set correctly
02593             if (Verbs[SplitElement+2] & PT_CLOSEFIGURE)
02594                 NewVerbs[5] |= PT_CLOSEFIGURE;
02595         }
02596         else
02597         {
02598             // The slit happened in a straight line section
02599             NumToChange = 2;
02600             NewFlags[0] = NewFlags[1] = Flags[SplitElement];
02601             NewFlags[0].IsSmooth = NewFlags[0].IsRotate = FALSE;
02602             NewFlags[0].IsSelected = FALSE;
02603 
02604             // Make sure that the close figure flag is maintained
02605             if (Verbs[SplitElement] & PT_CLOSEFIGURE)
02606                 NewVerbs[1] |= PT_CLOSEFIGURE;
02607         }
02608 
02609         // Start performing actions to insert the split point with full undo
02610         Action* UnAction;
02611         ActionCode Act = RemovePathElementAction::Init(this, &UndoActions, NumToChange/2,
02612                                                     SplitElement, (Action**)(&UnAction));
02613 
02614         // if we failed to init the action, then fail this operation
02615         if (Act==AC_FAIL)
02616             return FALSE;
02617 
02618         // record details about the path in the action before we start inserting stuff
02619         ((RemovePathElementAction*)UnAction)->RecordPath(EndPath);
02620 
02621         // Move to the place in the path where the split took place
02622         EndPath->InkPath.SetPathPosition(SplitElement);
02623         PathFlags InsertFlags;
02624         BOOL      InsertWorked;
02625 
02626         // insert a curve or a line, depending on where the split happened
02627         if (NumToChange==6)
02628             InsertWorked = EndPath->InkPath.InsertCurveTo(NewCoords[0], NewCoords[1], NewCoords[2], &InsertFlags);
02629         else
02630             InsertWorked = EndPath->InkPath.InsertLineTo(NewCoords[0], &InsertFlags);
02631 
02632         // see if the insertion worked
02633         if (!InsertWorked)
02634             return FALSE;
02635                 
02636         // Build an action to record the changes we're about to make to the path
02637         ModifyPathAction* ModAction;
02638         Act = ModifyPathAction::Init(this, &UndoActions, NumToChange, (Action**)(&ModAction));
02639         if (Act==AC_FAIL)
02640             return FALSE;
02641     
02642         // If the action is happy...
02643         if (Act!=AC_NORECORD)
02644         {
02645             // Some pointers to what has changed
02646             PathVerb* ChangedVerbs;
02647             PathFlags* ChangedFlags;
02648             DocCoord* ChangedCoords;
02649             INT32* ChangedIndices;
02650 
02651             // memory for the details about what has changed
02652             ALLOC_WITH_FAIL(ChangedVerbs, (PathVerb*) CCMalloc(NumToChange * sizeof(PathVerb)), this);
02653             ALLOC_WITH_FAIL(ChangedFlags, (PathFlags*) CCMalloc(NumToChange* sizeof(PathFlags)), this);
02654             ALLOC_WITH_FAIL(ChangedCoords, (DocCoord*) CCMalloc(NumToChange* sizeof(DocCoord)), this);
02655             ALLOC_WITH_FAIL(ChangedIndices, (INT32*) CCMalloc(NumToChange* sizeof(INT32)), this);
02656 
02657             // See of all the allocations worked
02658             if (!ChangedVerbs || !ChangedFlags || !ChangedCoords || !ChangedIndices)
02659             {
02660                 // They did not, so free them up and fail
02661                 if (ChangedVerbs) CCFree(ChangedVerbs);
02662                 if (ChangedFlags) CCFree(ChangedFlags);
02663                 if (ChangedCoords) CCFree(ChangedCoords);
02664                 if (ChangedIndices) CCFree(ChangedIndices);
02665 
02666                 return FALSE;
02667             }
02668 
02669             // Reread the pointers, in case they've changed
02670             Verbs = EndPath->InkPath.GetVerbArray();
02671             Flags = EndPath->InkPath.GetFlagArray();
02672             Coords = EndPath->InkPath.GetCoordArray();
02673 
02674             // Now record the arrays...
02675             for (INT32 i=0; i<NumToChange; i++)
02676             {
02677                 // record which elements changed and what they used to be
02678                 ChangedIndices[i] = SplitElement+i;
02679                 ChangedVerbs[i] = Verbs[SplitElement+i];
02680                 ChangedFlags[i] = Flags[SplitElement+i];
02681                 ChangedCoords[i] = Coords[SplitElement+i];
02682             }
02683 
02684             // and store the info in the action
02685             ModAction->StoreArrays(ChangedVerbs, ChangedFlags, ChangedCoords, ChangedIndices, EndPath);
02686         }
02687 
02688         // Reread the pointers, in case they've changed
02689         Verbs = EndPath->InkPath.GetVerbArray();
02690         Flags = EndPath->InkPath.GetFlagArray();
02691         Coords = EndPath->InkPath.GetCoordArray();
02692 
02693         // Now that the undo rigmarole has been done, let's change the path
02694         for (INT32 i=0; i<NumToChange; i++)
02695         {
02696             Verbs[SplitElement+i] = NewVerbs[i];
02697             Flags[SplitElement+i] = NewFlags[i];
02698             Coords[SplitElement+i] = NewCoords[i];
02699         }
02700     }
02701     else
02702     {
02703         // else we tried to split at an existing control point (endpoint)
02704         // That means that there were no new endpoints
02705         *NewElements = 0;
02706         *SplitAt = SplitElement;
02707 
02708         // See if we can find the control point in question
02709         DocCoord* Coords = EndPath->InkPath.GetCoordArray();
02710         INT32 NumCoords = EndPath->InkPath.GetNumCoords();
02711 
02712         // see if the split point is over the last point on the curve
02713         if (Coords[NumCoords-1]==SplitPoint)
02714             *SplitAt = NumCoords;
02715     }   
02716 
02717     // all worked
02718     return TRUE;
02719 }

BOOL OpFreeHand::TryToJoinNewPathWithOthers NodePath FreePath  )  [protected]
 

Tries to Join the new path with any of the existing paths.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
4/5/94
Parameters:
FreePath - The new path that we will try to join with other paths [INPUTS]
Returns:
TRUE if it worked, FALSE if it did not

Definition at line 1411 of file opfree.cpp.

01412 {
01413     ObjChangeFlags cFlagsDelete(TRUE);
01414     ObjChangeFlags cFlagsReplace(FALSE,TRUE);
01415     ObjChangeFlags cFlagsChanging;
01416     ObjChangeParam ObjChange;
01417     
01418     if (StartPath != NULL && EndPath != NULL && StartPath != EndPath)
01419     {
01420         // Cope with the case when the two separate paths are being joined together with this freehand line.
01421         // In this case, the EndPath is hidden (effectively deleted), and the StartPath is replaced in the tree
01422         // by hiding it and inserting the resultant NodePath in its place
01423 
01424         // Is it ok to delete the end path?
01425         ObjChange.Define(OBJCHANGE_STARTING,cFlagsDelete,EndPath,this);
01426         if (!EndPath->AllowOp(&ObjChange))
01427             return TRUE;    // Pretend that we've done it.
01428 
01429         // Is it ok to replace the start path?
01430         ObjChange.Define(OBJCHANGE_STARTING,cFlagsReplace,StartPath,this);
01431         if (!StartPath->AllowOp(&ObjChange))
01432             return TRUE;    // Pretend that we've done it.
01433     }
01434     else if (StartPath != NULL || EndPath != NULL)
01435     {
01436         // Some joining will take place with either in StartPath or EndPath
01437 
01438         // Either the StartPath or the EndPath will be hidden and replaced by another NodePath
01439         ObjChange.Define(OBJCHANGE_STARTING,cFlagsChanging,StartPath,this);
01440 
01441         // If we have a start path, the op will happen to the start path
01442         if (StartPath != NULL && !StartPath->AllowOp(&ObjChange))
01443             return TRUE;    // Pretend that we've done it.
01444 
01445         // If we DONT have a start path, the op will happen to the end path
01446         if (StartPath == NULL && !EndPath->AllowOp(&ObjChange))
01447             return TRUE;    // Pretend that we've done it.
01448     }
01449 
01450     if (StartPath == NULL)
01451     {
01452         if (EndPath == NULL)
01453         {
01454             // Trivial Case - No Joining needed
01455             NodePath* JoinPath = MakeCopy(FreePath);
01456             if (JoinPath == NULL)
01457                 return FALSE;
01458 
01459             // Try to close it
01460             JoinPath->InkPath.TryToClose();
01461 
01462             // Apply some attributes
01463             if (!ApplyAttributes(JoinPath, GetWorkingDoc()))
01464                 return FALSE;
01465 
01466 
01467             if (!AddPressureAttribute(JoinPath))
01468                 return(FALSE);
01469 
01470             // Insert the node and invalidate the region 
01471             if (!DoInsertNewNode(JoinPath, StartSpread, FALSE))
01472                 return FALSE;
01473             
01474             if (!DoInvalidateNodeRegion(JoinPath, TRUE))
01475                 return FALSE;
01476 
01477             m_pNewNodePath = JoinPath;
01478             // Tell the freehand tool that it has a previous path
01479             SetRetroPath(JoinPath, 0, JoinPath->InkPath.GetNumCoords());
01480         }
01481         else
01482         {
01483             if (EndPath->InkPath.IsComplexPath())
01484             {
01485                 // Complex Join (End of new path Joined)
01486                 if (!ComplexJoin(FreePath, EndPath))
01487                     return FALSE;
01488             }
01489             else
01490             {
01491                 // Simple Join (End of new path Joined)
01492                 if (!SimpleJoin(FreePath, EndPath))
01493                     return FALSE;
01494             }
01495         }
01496     }
01497     else
01498     {
01499         if (EndPath == NULL)
01500         {
01501             if (StartPath->InkPath.IsComplexPath())
01502             {
01503                 // Complex Join (Start of new path Joined)
01504                 if (!ComplexJoin(FreePath, StartPath))
01505                     return FALSE;
01506             }
01507             else
01508             {
01509                 // Simple Join (Start of New Path Joined)
01510                 if (!SimpleJoin(FreePath, StartPath))
01511                     return FALSE;
01512             }
01513         }
01514         else
01515         {
01516             if (StartPath == EndPath)
01517             {
01518                 if (StartPath->InkPath.IsComplexPath())
01519                 {
01520                     // Complex to Same Complex Join
01521                     if (!ComplexToComplexJoin(FreePath, StartPath))
01522                         return FALSE;
01523                 }
01524                 else
01525                 {
01526                     // Simple to Same Simple Join (Start = End)
01527                     if (!SimpleJoin(FreePath, StartPath))
01528                         return FALSE;
01529                 }
01530             }
01531             else
01532             {
01533                 // Compex To Complex Join (Start and End different and complex)
01534                 // Complex To Simple Join (Start and End different and start complex)
01535                 // Complex To Simple Join (Start and End different and end complex)
01536                 // Simple To Simple Join (Start and End Different)
01537                 if (!VeryComplexToComplexJoin(FreePath, StartPath, EndPath))
01538                     return FALSE;
01539             }
01540         }
01541     }
01542 
01543     // Worked
01544     return TRUE;
01545 }

BOOL OpFreeHand::TryToReplaceSection NodePath pNewNodePath  )  [protected, virtual]
 

Replaces a section from the path that the drag was started and finished on with the newly drawn curve section.

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
27/10/94
Parameters:
pNewNodePath - The New Path that we want to use instead of a section of [INPUTS] an existing path

Definition at line 2381 of file opfree.cpp.

02382 {
02383     // The elements that the split occured on
02384     INT32 FirstElement, SecondElement, NewElements;
02385 
02386     ObjChangeFlags cFlags;
02387     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,EndPath,this);
02388     if (!EndPath->AllowOp(&ObjChange, TRUE))
02389         return TRUE;    // Pretend that we've done it.
02390 
02391     // Invalidate it as it was
02392     if (!DoInvalidateNodeRegion(EndPath, TRUE))
02393         return FALSE;       
02394 
02395     // Split old path at start and end points
02396     if (!SplitAtPoint(StartPoint, &FirstElement, &NewElements))
02397         return FALSE;
02398 
02399     if (!SplitAtPoint(PreviousPoint, &SecondElement, &NewElements))
02400         return FALSE;
02401     
02402     // We may need to adjust the first element if the second element was
02403     // before it in the path
02404     if (SecondElement<=FirstElement)
02405     {
02406         // Swap the elements around and move the first one back a bit
02407         INT32 Temp = FirstElement + (NewElements/2);
02408         FirstElement = SecondElement;
02409         SecondElement = Temp;
02410 
02411         // We will also need to reverse the original curve here
02412         // (as well as the original track data)
02413         pNewNodePath->InkPath.Reverse();
02414         TrackData->Reverse();
02415         ReverseBrushPressure(); // if we are a brush we will want to reverse our pressure data
02416     }
02417 
02418     // Go and see if which part of the path we should be replacing
02419     if (!EndPath->InkPath.IsComplexPath() && EndPath->InkPath.IsFilled)
02420     {
02421         // Here we need to see if we will replace the wrong bit of the path and try and do something
02422         // about it if we are. The wrong bit is defined as follows. If you were to draw a filled shape
02423         // such as an ellipse and modified it by drawing a small loop around one of the control points
02424         // you should get an ellipse with a small notch cut out of it. However, if you do this around
02425         // the Start/End control point you end up with no ellipse and just a small notch.
02426         // To get round this, we always try to replace the smallest number of control points possible.
02427         INT32 HalfElements = EndPath->InkPath.GetNumCoords() / 2;
02428         INT32 ReplaceElements = SecondElement - FirstElement;
02429         if (HalfElements < ReplaceElements)
02430         {
02431             // We were trying to replace more than half of the elements in the path, so actually
02432             // replace the other, large half of the path
02433             
02434             // Here we have to change the the start element of the path, so we have
02435             // to hide the original and make a copy of it
02436             BOOL IsOk = FALSE;
02437             Node* pCopyNode = NULL;
02438             CALL_WITH_FAIL(EndPath->NodeCopy(&pCopyNode), this, IsOk);
02439 
02440             // See if we managed to make a copy of the node
02441             if (!IsOk)
02442                 return FALSE;
02443 
02444             // Insert the copy
02445             if (!DoInsertNewNode((NodeRenderableBounded*)pCopyNode, EndPath, NEXT, FALSE))
02446                 return FALSE;
02447 
02448             // Hide the original
02449             if (!DoHideNode(EndPath, TRUE))
02450                 return FALSE;
02451 
02452             // Copy the pointer back to where it used to be
02453             EndPath = (NodePath*) pCopyNode;
02454             ERROR3IF(!EndPath->IS_KIND_OF(NodePath), "Copy of a Node path was not a node path");
02455 
02456             // Make the Last replace element the start/end of the path
02457             if (EndPath->InkPath.ChangeStartElement(SecondElement-1))
02458             {
02459                 // It worked, so modify the Start and End Elements to match
02460                 FirstElement = 1;
02461                 SecondElement = EndPath->InkPath.GetNumCoords() - ReplaceElements;
02462 
02463                 // We will also need to reverse the original curve here
02464                 // (as well as the original track data)
02465                 pNewNodePath->InkPath.Reverse();
02466                 TrackData->Reverse();
02467             }
02468         }
02469 
02470     }
02471 
02472     // replace section with new path
02473     INT32 NumElements = SecondElement - FirstElement;
02474     if (!ReplaceMiddleOfPath(pNewNodePath, FirstElement, NumElements))
02475         return FALSE;
02476 
02477     // Mark the nodes bounding rect as invalid
02478     EndPath->InvalidateBoundingRect();
02479     
02480     TRACEUSER( "Diccon", _T("Replacing middle of Path\n"));
02481     InvalidateBrushRegion(EndPath);
02482     // and invalidate how it is now
02483     if (!DoInvalidateNodeRegion(EndPath, TRUE))
02484         return FALSE;       
02485 
02486     // Failed to do it
02487     return TRUE;
02488 }

BOOL OpFreeHand::VeryComplexToComplexJoin NodePath FreePath,
NodePath TopPath,
NodePath BotPath
[protected]
 

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.

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] TopPath - The path to join with the new freehand path. This is the path that is highest in the Z order of the two paths BotPath - The path to join with the new freehand path. This is the path that is lowest in the Z order of the two paths
Returns:
TRUE if it worked, FALSE if it failed

Definition at line 1758 of file opfree.cpp.

01759 {
01760     // First we should make a note of the region that we will need redrawing
01761     DocRect InvalidRect = FreePath->GetBlobBoundingRect();
01762 
01763     // Need to know where in the joined path that the new section will be
01764     INT32 NumSlots = FreePath->InkPath.GetNumCoords();
01765     INT32 StartSlot;
01766 
01767     // Make a copy of the of the path we are Joining to
01768     NodePath* JoinPath = MakeCopy(TopPath);
01769     if (JoinPath==NULL)
01770         return FALSE;
01771 
01772     // Merge in the other complex path
01773     if (!JoinPath->InkPath.MergeTwoPaths(BotPath->InkPath))
01774         return FALSE;
01775 
01776     // Join the new path with the one already there
01777     BOOL NewPathReversed = FALSE;
01778     if (!JoinPath->InkPath.ComplexToSameComplexJoin(&FreePath->InkPath, &StartSlot, &NewPathReversed))
01779         return FALSE;
01780 
01781     // Insert the new object
01782     if (!InsertNewNode(JoinPath, InvalidRect, TopPath, BotPath))
01783         return FALSE;
01784 
01785     // Reverse the original mouse move data if the fitted path was reversed
01786     if (NewPathReversed)
01787         TrackData->Reverse();
01788 
01789     // Tell the freehand tool all about it              
01790     SetRetroPath(JoinPath, StartSlot, NumSlots);
01791 
01792     // It Worked
01793     return TRUE;
01794 }


Member Data Documentation

BOOL OpFreeHand::AddPressureToPath [protected]
 

Definition at line 283 of file opfree.h.

BOOL OpFreeHand::CanLineJoin [protected]
 

Definition at line 278 of file opfree.h.

INT32 OpFreeHand::CloseTo [protected]
 

Definition at line 290 of file opfree.h.

INT32 OpFreeHand::CurrentCursorID [protected]
 

Definition at line 301 of file opfree.h.

NodePath* OpFreeHand::EndPath [protected]
 

Definition at line 289 of file opfree.h.

UINT32 OpFreeHand::FreeHandPressure [protected]
 

Definition at line 284 of file opfree.h.

BOOL OpFreeHand::IsEndNearEndpoint [protected]
 

Definition at line 292 of file opfree.h.

BOOL OpFreeHand::IsStraightLineMode [protected]
 

Definition at line 279 of file opfree.h.

INT32 OpFreeHand::LineSegmentCount [protected]
 

Definition at line 277 of file opfree.h.

NodePath* OpFreeHand::m_pNewNodePath [protected]
 

Definition at line 304 of file opfree.h.

double OpFreeHand::Mu [protected]
 

Definition at line 291 of file opfree.h.

Cursor* OpFreeHand::MyCurrentCursor [protected]
 

Definition at line 300 of file opfree.h.

Cursor* OpFreeHand::pFreeHandCursor [protected]
 

Definition at line 295 of file opfree.h.

Cursor* OpFreeHand::pJoinCursor [protected]
 

Definition at line 296 of file opfree.h.

FreeHandJoinInfo* OpFreeHand::pJoinInfo [protected]
 

Definition at line 287 of file opfree.h.

Cursor* OpFreeHand::pModifyCursor [protected]
 

Definition at line 299 of file opfree.h.

DocCoord OpFreeHand::PreviousPoint [protected]
 

Definition at line 276 of file opfree.h.

Spread* OpFreeHand::PreviousSpread [protected]
 

Definition at line 272 of file opfree.h.

Cursor* OpFreeHand::pRubOutCursor [protected]
 

Definition at line 297 of file opfree.h.

Cursor* OpFreeHand::pStraightCursor [protected]
 

Definition at line 298 of file opfree.h.

INT32 OpFreeHand::Smoothness [protected]
 

Definition at line 273 of file opfree.h.

NodePath* OpFreeHand::StartPath [protected]
 

Definition at line 288 of file opfree.h.

DocCoord OpFreeHand::StartPoint [protected]
 

Definition at line 270 of file opfree.h.

Spread* OpFreeHand::StartSpread [protected]
 

Definition at line 271 of file opfree.h.

DocCoord OpFreeHand::StraightLinePos [protected]
 

Definition at line 280 of file opfree.h.

Path* OpFreeHand::TrackData [protected]
 

Definition at line 269 of file opfree.h.


The documentation for this class was generated from the following files:
Generated on Sat Nov 10 03:58:14 2007 for Camelot by  doxygen 1.4.4