nodepath.cpp

Go to the documentation of this file.
00001 // $Id: nodepath.cpp 1688 2006-08-10 12:05:20Z gerry $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // The Renderable Path Code
00099 
00100 /*
00101 */
00102 
00103 #include "camtypes.h"
00104 #include "nodepath.h"
00105 
00106 #include <math.h>
00107 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 
00109 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 #include "blobs.h"
00111 #include "contmenu.h"
00112 #include "lineattr.h"
00113 #include "ophist.h"
00114 #include "opbreak.h"
00115 #include "pathedit.h"
00116 #include "snap.h"
00117 #include "pathops.h"
00118 #include "nodetxts.h"
00119 #include "saveeps.h"
00120 
00121 //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00122 #include "cxftags.h"
00123 #include "cxfile.h"
00124 //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 #include "rechpath.h"
00126 
00127 //#include "simon.h"
00128 //#include "rik.h"
00129 //#include "jim.h"
00130 //#include "tim.h"
00131 
00132 #include "gclips.h"
00133 //#include "mario.h"
00134 
00135 #include "extender.h"
00136 #include "ngcore.h"     // NameGallery, for stretching functionality
00137 
00138 #include "gdraw.h"
00139 //#include "rndrgn.h"
00140 #include "brshattr.h"
00141 #include "qualattr.h"
00142 
00143 //#include "blendatt.h"
00144 #include "nodebldr.h"
00145 //#include "ndbldpth.h"
00146 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00147 #include "gblend.h"
00148 #include "blndhelp.h"
00149 #include "opdrbrsh.h"
00150 #include "rsmooth.h"    //For retrosmoothing
00151 #include "strkattr.h"   //For attrstroketype
00152 #include "ppstroke.h"
00153 #include "nodeclip.h"  // for pathbecomea
00154 #include "attrmap.h"
00155 
00156 // Put my version Number into the About box
00157 DECLARE_SOURCE( "$Revision: 1688 $" );
00158 
00159 CC_IMPLEMENT_DYNCREATE( NodePath, NodeRenderableInk )
00160 
00161 CC_IMPLEMENT_DYNAMIC(PathRecordHandler,CamelotRecordHandler)
00162 CC_IMPLEMENT_DYNAMIC(PathFlagsRecordHandler,CamelotRecordHandler)
00163 
00164 // Declare smart memory handling in Debug builds
00165 #define new CAM_DEBUG_NEW
00166 #define CONVERTSTROKES // comment this out to disable converting strokes for bevelling/contouring
00167 
00168 /***********************************************************************************************
00169 
00170 >   NodePath::NodePath(
00171     Node* ContextNode,  
00172     AttachNodeDirection Direction,  
00173     const DocRect& BoundingRect, 
00174     BOOL Locked = FALSE, 
00175     BOOL Mangled = FALSE,  
00176     BOOL Marked = FALSE, 
00177     BOOL Selected = FALSE, 
00178     BOOL Renderable = FALSE
00179     )
00180 
00181     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00182     Created:    26/4/93             
00183     
00184     Inputs:     ContextNode: Pointer to a node which this node is to be attached to.     
00185         
00186                 Direction: 
00187             
00188                 Specifies the direction in which the node is to be attached to the 
00189                 ContextNode. The values this variable can take are as follows: 
00190                                   
00191                 PREV      : Attach node as a previous sibling of the context node
00192                 NEXT      : Attach node as a next sibling of the context node
00193                 FIRSTCHILD: Attach node as the first child of the context node
00194                 LASTCHILD : Attach node as a last child of the context node                               
00195                           
00196                 BoundingRect: Bounding rectangle  
00197                                   
00198                 The remaining inputs specify the status of the node: 
00199             
00200                 Locked:     Is node locked ?
00201                 Mangled:    Is node mangled ?
00202                 Marked:     Is node marked ?
00203                 Selected:   Is node selected ?
00204     Purpose:    This constructor initialises the nodes flags and links it to ContextNode in the
00205                 direction specified by Direction. All neccesary tree links are updated.         
00206             
00207     Note:       SetUpPath() must be called before the NodePath is in a state in which it can be 
00208                 used.       
00209                 
00210     SeeAlso:    NodePath::SetUpPath()
00211     
00212     Errors:     An assertion error will occur if ContextNode is NULL
00213 
00214 ***********************************************************************************************/
00215 
00216 NodePath::NodePath(Node* ContextNode,  
00217                     AttachNodeDirection Direction,  
00218                     BOOL Locked, 
00219                     BOOL Mangled,  
00220                     BOOL Marked, 
00221                     BOOL Selected    
00222               ):NodeRenderableInk(ContextNode, Direction, Locked, Mangled, Marked, Selected )  
00223 {
00224     CurrentRotation = 0.0;
00225 }                        
00226  
00227 /*********************************************************************************************
00228 
00229 >    NodePath::NodePath() 
00230 
00231      Author:    Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00232      Created:   04/6/93
00233      Inputs:    -
00234      Outputs:   - 
00235      Returns:   -
00236               
00237      Purpose:   This constructor creates a NodePath linked to no other with all status
00238                 flags false and an uninitialized bounding rectangle.           
00239             
00240      Note:    SetUpPath() must be called before the NodePath is in a state in which it can be 
00241               used.         
00242                 
00243      SeeAlso: SetUpPath                                                        
00244 
00245 **********************************************************************************************/
00246 /* Technical Notes:   
00247 
00248 The default constructor is required so that SimpleCopy will work 
00249 */  
00250 
00251 NodePath::NodePath(): NodeRenderableInk()
00252 {
00253     CurrentRotation = 0.0;
00254 }
00255 
00256 
00257 
00258 /********************************************************************************************
00259 
00260 >   virtual BOOL NodePath::IsNodePath()
00261 
00262     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00263     Created:    20/03/95
00264     Returns:    TRUE => This node is an instance of NodePath.
00265                 FALSE => otherwise.
00266     Purpose:    Override the node virtual function IsNodePath() and return TRUE as we
00267                 definitely are one.
00268 
00269 ********************************************************************************************/
00270 
00271 BOOL NodePath::IsNodePath() const
00272 {
00273     return TRUE;
00274 }
00275 
00276 
00277 
00278 /***********************************************************************************************
00279 
00280 >   BOOL NodePath::SetUpPath( INT32 RequiredSize = 32, INT32 BlockSize = 32 )
00281 
00282     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00283     Created:    04/5/93
00284     Inputs:     RequiredSize - The size of the initial block of memory to be allocated to the
00285                 path.
00286                 BlockSize - The size of the block asked for by the path object when it
00287                 runs out of space. 
00288     Returns:    TRUE if the path was init'ed ok, FALSE otherwise
00289     Purpose:    To initialise the Nodepath into a state that can be used, by allocating memory,
00290                 setting up member variables properly and inserting an EndPath element into
00291                 the path
00292 
00293 ***********************************************************************************************/
00294 
00295 
00296 BOOL NodePath::SetUpPath(INT32 RequiredSize, INT32 BlockSize)
00297 {
00298     CurrentRotation = 0.0;
00299     return (InkPath.Initialise(RequiredSize, BlockSize)); 
00300 }
00301 
00302 
00303 /***********************************************************************************************
00304 
00305 >   void NodePath::ShowDebugTreeDetails() const
00306 
00307     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00308     Created:    26/5/93
00309     Purpose:    Displays debugging info of the tree
00310     SeeAlso:    NodeRenderableInk::ShowDebugTreeDetails
00311 
00312 ***********************************************************************************************/
00313 #ifdef _DEBUG
00314 void NodePath::ShowDebugTreeDetails() const
00315 {                     
00316     TRACE( _T("Path ")); 
00317     // Display a bit of debugging info
00318     // For now, we will just call the base class version
00319     NodeRenderableInk::ShowDebugTreeDetails();  
00320     
00321     //InkPath.DumpWholePath();
00322 }
00323 #endif
00324 
00325 /********************************************************************************************
00326 
00327 >   void* NodeRenderable::GetDebugDetails(StringBase* Str) 
00328 
00329     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00330     Created:    21/9/93
00331     Outputs:    Str: String giving debug info about the node
00332     Purpose:    For obtaining debug information about the Node
00333 
00334 ********************************************************************************************/
00335 
00336 void NodePath::GetDebugDetails( StringBase* Str )
00337 {
00338 #ifdef _DEBUG
00339     // Call base class
00340     NodeRenderableInk::GetDebugDetails( Str );
00341     
00342     InkPath.FindStartOfPath();
00343     String_256 TempStr;
00344         
00345     (*Str) += TEXT( "\r\nPath Data Dump\r\n" );
00346     TempStr._MakeMsg( TEXT( "#1%ld bytes used\r\n"), InkPath.GetPathByteLength() );
00347     (*Str) += TempStr;
00348 
00349 #if !defined(EXCLUDE_FROM_RALPH)
00350     DocRect BlobRect = GetBlobBoundingRect();
00351     TempStr._MakeMsg( TEXT("Blob Bounding Rect :-\r\n\t#1%ld,\t#2%ld\r\n\t#3%ld,\t#4%ld\r\n"),
00352                       BlobRect.lo.x, BlobRect.lo.y, BlobRect.hi.x, BlobRect.hi.y );
00353     (*Str) += TempStr;
00354 #endif
00355 
00356     TempStr._MakeMsg(TEXT("The Path is #1%sFilled and #2%sStroked\r\n"), InkPath.IsFilled ? "" : "not ", InkPath.IsStroked ? "" : "not ");
00357     (*Str) += TempStr;
00358 
00359     (*Str) += TEXT( "\r\nNum\tType\tX Coord\tY Coord\r\n" );
00360     PathVerb* Verbs  = InkPath.GetVerbArray();
00361     DocCoord* Coords = InkPath.GetCoordArray();
00362     PathFlags* Flags = InkPath.GetFlagArray();
00363     INT32 numcoords = InkPath.GetNumCoords();
00364     for (INT32 i=0; i<numcoords; i++)
00365     {
00366         // Add the info to the string
00367         TempStr._MakeMsg( TEXT("#1%d.\t#2%d\t#3%ld,\t#4%ld\t"),
00368                           i, Verbs[i], Coords[i].x, Coords[i].y );
00369         
00370         // Add useful flags to the end of the string
00371         if (Flags[i].IsSmooth)
00372             TempStr += TEXT("S");
00373 
00374         if (Flags[i].IsRotate)
00375             TempStr += TEXT("R");
00376 
00377         if (Flags[i].IsEndPoint)
00378             TempStr += TEXT("E");
00379 
00380         if (Flags[i].IsSelected)
00381             TempStr += TEXT("(sel)");
00382 
00383         TempStr += TEXT("\r\n");
00384     
00385         (*Str) += TempStr;
00386 
00387     }
00388 #endif
00389 }
00390 
00391 
00392 /********************************************************************************************
00393 
00394 >   virtual UINT32 NodePath::GetNodeSize() const
00395 
00396     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00397     Created:    6/10/93
00398     Inputs:     -
00399     Outputs:    -
00400     Returns:    The size of the path node in bytes
00401     Purpose:    For finding the size of the node 
00402                 
00403     SeeAlso:    Node::GetSubtreeSize
00404 
00405 ********************************************************************************************/
00406 
00407 UINT32 NodePath::GetNodeSize() const 
00408 {     
00409     return (sizeof(NodePath)+InkPath.GetPathByteLength());  
00410 }  
00411 
00412 
00413 
00414 /***********************************************************************************************
00415 
00416 >   virtual Node* NodePath::SimpleCopy()
00417 
00418     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00419     Created:    26/5/93
00420     Returns:    Pointer to a Node
00421     Purpose:    Makes a copy of all the data in the node
00422 
00423 ***********************************************************************************************/
00424 
00425 Node* NodePath::SimpleCopy()
00426 {
00427 
00428     NodePath* NodeCopy = new NodePath();
00429     if (NodeCopy)
00430         CopyNodeContents(NodeCopy);
00431     
00432     return NodeCopy;
00433 }            
00434 
00435 
00436 /***********************************************************************************************
00437 
00438 >   void NodePath::CopyNodeContents( Node* NodeCopy)
00439 
00440     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00441     Created:    26/5/93
00442     Inputs:     NodeCopy - The node to copy
00443     Purpose:    Copies the data in the node by first calling the base class to get it to
00444                 copy its stuff, and then copying its own stuff
00445     Scope:      protected
00446     SeeAlso:    NodeRenderableInk::CopyNodeContents
00447 
00448 ***********************************************************************************************/
00449 
00450 void NodePath::CopyNodeContents( NodePath* NodeCopy)
00451 {
00452     NodeRenderableInk::CopyNodeContents( NodeCopy );
00453     
00454     //Copy contents specific to derived class here
00455     if (NodeCopy->InkPath.Initialise(InkPath.GetNumCoords(),12))
00456         NodeCopy->InkPath.CopyPathDataFrom(&InkPath);
00457 }
00458 
00459 
00460 
00461 
00462 /***********************************************************************************************
00463 >   void NodePath::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00464 
00465     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00466     Created:    18/12/2003
00467     Outputs:    -
00468     Purpose:    Polymorphically copies the contents of this node to another
00469     Errors:     An assertion failure will occur if NodeCopy is NULL
00470     Scope:      protected
00471                                      
00472 ***********************************************************************************************/
00473 
00474 void NodePath::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00475 {
00476     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
00477     ENSURE(IS_A(pNodeCopy, NodePath), "PolyCopyNodeContents given wrong dest node type");
00478 
00479     if (IS_A(pNodeCopy, NodePath))
00480         CopyNodeContents((NodePath*)pNodeCopy);
00481 }
00482 
00483 
00484 
00485 /***********************************************************************************************
00486 
00487 >   virtual void NodePath::Render(RenderRegion* pRRegion)
00488 
00489     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
00490     Created:    31/5/93
00491     Inputs:     Pointer to a render region
00492     Purpose:    Will render the path contained within the object to the given render region
00493 
00494 ***********************************************************************************************/
00495 
00496 void NodePath::Render(RenderRegion* pRender)
00497 {
00498     // render the path.
00499     TRACEUSER( "Diccon", _T("Rendering nodepath\n"));
00500     pRender->DrawPath(&InkPath);
00501 }
00502 
00503 
00504 
00505 /********************************************************************************************
00506 
00507 >   void NodePath::RenderEorDrag( RenderRegion* pRender )
00508 
00509     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00510     Created:    22/10/93
00511     Inputs:     pRender - A Pointer to the current RenderRegion
00512     Purpose:    Renders a version of the path for EORed dragging of shapes.
00513     SeeAlso:    NodePath::Render; NodeRenderableInk::RenderEorDrag
00514 
00515 ********************************************************************************************/
00516 
00517 void NodePath::RenderEorDrag( RenderRegion* pRender )
00518 {
00519     // Currently this function performs an identical task to the
00520     // Render() function
00521     
00522     // render the path
00523     pRender -> DrawPath( &InkPath );
00524 }
00525 
00526 
00527 
00528 /********************************************************************************************
00529 
00530 >   void NodePath::RenderObjectBlobs(RenderRegion* pRender)
00531 
00532     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00533     Created:    23/8/94
00534     Inputs:     pRender - The region to render the blobs into
00535     Purpose:    Draws the paths object blobs into the render region supplied
00536 
00537 ********************************************************************************************/
00538 
00539 void NodePath::RenderObjectBlobs(RenderRegion* pRender)
00540 {
00541     // Find the parent spread
00542     Spread* pSpread = FindParentSpread();
00543     ENSURE(pSpread != NULL, "NodePath does not have a parent spread");
00544     
00545     // Render the blobs on the path
00546     if (pSpread!=NULL)
00547         InkPath.RenderPathBlobs(pRender);
00548 
00549     // New Diccon 13/6/2000 If we are a brush and we are selected then we want to 
00550     // render the path on top
00551     NodeAttribute* pAttr = NULL;
00552     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
00553     if (pAttr!= NULL)
00554     {
00555         if (((AttrBrushType*)pAttr)->GetBrushHandle() != BrushHandle_NoBrush)
00556         {
00557 
00558             // --- If the quality is set low enough, strokes are just rendered as centrelines
00559             // "low enough" is defined as the same point that Blends show their middle parts
00560             // BLOCK
00561             {
00562                 View* pRenderView = NULL;
00563                 pRenderView = pRender->GetRenderView();
00564                 if(pRenderView != NULL)
00565                 {
00566                     Quality viewQuality = pRenderView->RenderQuality;
00567                     if (viewQuality.GetBlendQuality() == Quality::FullBlend)
00568                     {
00569                         pRender->SetLineColour(COLOUR_BEZIERLINE);
00570                         pRender->SetFillColour(COLOUR_NONE);
00571                         pRender->DrawPath(&InkPath);
00572                     }
00573                 }
00574             }
00575         }
00576     }
00577 }
00578 
00579 /********************************************************************************************
00580 
00581 >   void NodePath::RenderPenBlobs(RenderRegion* pRender)
00582 
00583     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00584     Created:    23/8/94
00585     Inputs:     pRender - The region to render the blobs into
00586     Purpose:    Draws the paths pen blobs into the render region supplied
00587 
00588 ********************************************************************************************/
00589 
00590 void NodePath::RenderPenBlobs(RenderRegion* pRender)
00591 {
00592     // Find the parent spread
00593     Spread* pSpread = FindParentSpread();
00594     ENSURE(pSpread != NULL, "NodePath does not have a parent spread");
00595     
00596     // Render the blobs on the path
00597     if (pSpread!=NULL)
00598         InkPath.RenderPathPenBlobs(pSpread);
00599 }
00600 
00601 
00602 /********************************************************************************************
00603 
00604 >   void NodePath::RenderTinyBlobs(RenderRegion* pRender)
00605 
00606     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00607     Created:    23/8/94
00608     Inputs:     pRender - The region to render the blobs into
00609     Purpose:    Draws the paths Tiny blob into the render region supplied
00610 
00611 ********************************************************************************************/
00612 
00613 void NodePath::RenderTinyBlobs(RenderRegion* pRender)
00614 {
00615     // Set the line colours etc as we need them
00616     pRender->SetLineColour(COLOUR_NONE);
00617     pRender->SetFillColour(COLOUR_UNSELECTEDBLOB);
00618 
00619     // Render the blobs on the path
00620     DocCoord* Coords = InkPath.GetCoordArray();
00621     pRender->DrawBlob(Coords[0], BT_UNSELECTED);
00622 }
00623 
00624 
00625 
00626 /********************************************************************************************
00627 
00628 >   virtual ChangeCode NodePath::OnChildChange(ObjChangeParam* pParam)
00629 
00630     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00631     Created:    09/01/95
00632     Inputs:     pParam  = pointer to a object change parameter class
00633     Returns:    CC_OK   
00634                 
00635     Purpose:    It is now necessary to know when the nodepath has been updated because
00636                 if it has a brush applied to it the brush needs to clear its cache
00637 
00638 ********************************************************************************************/
00639 
00640 ChangeCode NodePath::OnChildChange(ObjChangeParam* pParam)
00641 {
00642     // look for an applied brush attribute
00643     NodeAttribute* pAttr = NULL;
00644     AttrBrushType* pAttrBrush = NULL;
00645     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
00646     if (pAttr!= NULL)
00647     {
00648         pAttrBrush = (AttrBrushType*)pAttr;
00649         
00650         // make an undoable action
00651         UpdateBrushAction* pAction;
00652         UndoableOperation* pOp = pParam->GetOpPointer();
00653         if (pOp != NULL)
00654             UpdateBrushAction::Init(pOp, pOp->GetUndoActionList(), this, &pAction);
00655     }
00656     return CC_OK;
00657 }
00658 
00659 /********************************************************************************************
00660 
00661 >   void NodePath::ClearSubSelection(Spread* pSpread)
00662 
00663     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00664     Created:    21/4/94
00665     Inputs:     pSpread - Pointer to the nodes parent spread
00666     Purpose:    Clears the paths sub-selection and re-draws the blobs to show this
00667 
00668 ********************************************************************************************/
00669 
00670 void NodePath::ClearSubSelection(Spread* pSpread)
00671 {
00672 #if !defined(EXCLUDE_FROM_RALPH)
00673     ENSURE( pSpread!=NULL, "Parent Spread was NULL in NodePath::ClearSubSelection" );
00674 
00675     if ((pSpread!=NULL) && (InkPath.IsSubSelection()))
00676     {
00677         InkPath.RenderPathBlobs(pSpread);
00678         InkPath.ClearSubSelection();
00679         InkPath.RenderPathBlobs(pSpread);
00680     }
00681 #endif
00682 }
00683 
00684 
00685 
00686 /********************************************************************************************
00687 
00688 >   virtual BOOL NodePath::OnClick( DocCoord PointerPos, ClickType Click, 
00689                                      ClickModifiers ClickMods )
00690 
00691     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00692     Created:    13/10/93
00693     Inputs:     PointerPos - The Location of the mouse pointer at the time of the click
00694                 Click - The type of click received (single, double, drag etc)
00695                 ClickMods - The modifiers to the click (eg shift, control etc )
00696     Returns:    BOOL - TRUE if the node claims the click as its own and FLASE if it is
00697                 not interested in the click
00698     Purpose:    Allows the Node to respond to clicks by selecting its blobs or starting
00699                 drags etc.
00700                 This functions should be overridden in the all the NodeRenderableInk classes
00701                 so that this verion never gets called. Eg the NodePath class might claim
00702                 the click if it happened over one of its unselected blobs.
00703 
00704 ********************************************************************************************/
00705 
00706 BOOL NodePath::OnClick( DocCoord PointerPos, ClickType Click, 
00707                         ClickModifiers ClickMods, Spread *pSpread )
00708 {
00709 #ifndef STANDALONE
00710 
00711     DocRect    BlobRect;
00712     DocCoord*  Coords = InkPath.GetCoordArray();
00713     PathFlags* Flags  = InkPath.GetFlagArray();
00714 //  PathVerb*  Verbs  = InkPath.GetVerbArray();
00715 
00716     // Should always be able to get selected view
00717     DocView *pDocView = DocView::GetSelected();
00718     ERROR3IF(pDocView == NULL, "NodePath::OnClick: Could not get selected DocView");
00719     if (pDocView == NULL)
00720         return FALSE;
00721 
00722     // we only handle the click if we can confirm that object blobs are being displayed.
00723     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
00724     if (pBlobMgr == NULL)
00725         return FALSE;
00726     if (!pBlobMgr->GetCurrentInterest().Object)
00727         return FALSE;
00728 
00729     INT32 NumCoords = InkPath.GetNumCoords();
00730     INT32 i;
00731     if (InkPath.FindNearestPoint(   PointerPos, 
00732                                     POINTFLAG_ENDPOINTS | 
00733                                     POINTFLAG_CONTROLPOINTS | 
00734                                     POINTFLAG_ENDSFIRST,
00735                                     &i)
00736         )
00737     {
00738         // we had a hit on a blob, so do something with it
00739         if ( Click == CLICKTYPE_SINGLE )
00740         {
00741             // Clicks on control points have no effect
00742             // but clicks on endpoints do have an effect
00743             if (Flags[i].IsEndPoint)
00744             {
00745                 if (ClickMods.Adjust && ClickMods.Constrain)
00746                 {
00747                     HandleBlobClick(Coords, Flags, i, NumCoords, TRUE, TRUE);
00748                 }
00749                 else
00750                 {
00751                     if ((!Flags[i].IsSelected) || ((Flags[i].IsSelected) && (ClickMods.Adjust)))                           
00752                     {
00753                         HandleBlobClick(Coords, Flags, i, NumCoords, ClickMods.Adjust, FALSE);
00754                     }                                              
00755                     else
00756                     {
00757                         // In order to deselect all but the clicked on point and still have dragging of
00758                         // multiple points we need to start the drag op on the click and handle a click
00759                         // at the end of the drag
00760                         if (Flags[i].IsSelected && !ClickMods.Menu)
00761                         {
00762                             OpNodePathEditBlob* pOpNodePath = new OpNodePathEditBlob;
00763                             if (pOpNodePath == NULL)
00764                                 InformError();
00765                             else
00766                                 pOpNodePath->DoStartDragEdit(this, Coords[i], pSpread);
00767                         }
00768                     }
00769                 }
00770             }
00771             else
00772             {
00773                 // Detected single click on Control Point
00774             }
00775         }
00776 
00777         if ( Click == CLICKTYPE_DOUBLE)
00778         {
00779             // A point has been double-clicked. Only if it's selected and an endpoint can we
00780             // do anything about it. What we do is the toggle smooth operation
00781 
00782             if (Flags[i].IsSelected && Flags[i].IsEndPoint)
00783             {
00784                 // Try and create the toggle operation
00785                 OpToggleSmooth* pOpToggle = new OpToggleSmooth;
00786                 if (!pOpToggle)
00787                 {
00788                     // Inform the user that we are out of memory
00789                     InformError();
00790                 }
00791                 else
00792                 {
00793                     // Call the function that actually does something
00794                     pOpToggle->DoTogglePoint(this, i, pSpread, FALSE, !ClickMods.Constrain);    // invert constrain
00795                 }
00796             }
00797         }
00798 
00799         // Check for drags, but only when the click isn't due to a menu click.
00800         if ( Click == CLICKTYPE_DRAG && !ClickMods.Menu )
00801         {
00802             
00803             //if (Flags[i].IsSelected)
00804             {
00805                 if (Flags[i].IsEndPoint)
00806                 {
00807                     // Need to do a drag on the selected points,
00808                     // so we had better start an operation
00809                     OpNodePathEditBlob* pOpNodePath = new OpNodePathEditBlob;
00810                     if (pOpNodePath == NULL)
00811                     {
00812                         // Inform the user that we are out of memory
00813                         InformError();
00814                     }
00815                     else
00816                     {
00817                         // Start the drag operation and pass in the Anchor Point to the operation
00818 
00819                         // The anchor point MUST be the true coord of the path point for
00820                         // snapping to work correctly (Markn 30/9/94)
00821 
00822                         pOpNodePath->DoStartDragEdit(this, Coords[i], pSpread);
00823                     }
00824                 }
00825                 else
00826                 {
00827                     // This must be a drag on a control point, so start an operation to
00828                     // handle that situation
00829                     OpNodePathEditControlBlob* pOpNodePath = new OpNodePathEditControlBlob;
00830                     if (pOpNodePath==NULL)
00831                     {
00832                         // Failed to get the mem I needed
00833                         InformError();
00834                     }
00835                     else
00836                     {
00837                         // Start the drag operation and pass in the Anchor Point to the operation
00838 
00839                         // The anchor point MUST be the true coord of the path point for
00840                         // snapping to work correctly (Markn 30/9/94)
00841 
00842                         pOpNodePath->DoStartDragEdit(this, Coords[i], pSpread, i);
00843                     }
00844                 }
00845             }
00846         }                   
00847 
00848         return TRUE;
00849     }
00850 
00851 #endif
00852     // we do not want to claim the click, so return FALSE
00853     return FALSE;
00854 }
00855 
00856 
00857 
00858 /********************************************************************************************
00859 
00860 >   virtual BOOL NodePath::OnBlobPopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
00861 
00862     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00863     Created:    10/04/95
00864     Inputs:     pSpread     The spread in which things are happening
00865                 PointerPos  The Location of the mouse pointer at the time of the click
00866                 pMenu       The menu to which items should be added
00867     Returns:    BOOL - TRUE if the node claims the click as its own and FALSE if it is
00868                 not interested in the click
00869     Purpose:    Allows the Node to respond to pop up menu clicks on blobs.
00870 
00871 ********************************************************************************************/
00872 
00873 BOOL NodePath::OnBlobPopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
00874 {
00875 #if !defined(EXCLUDE_FROM_RALPH)
00876     DocRect    BlobRect;
00877 //  DocCoord*  Coords = InkPath.GetCoordArray();
00878 //  PathFlags* Flags  = InkPath.GetFlagArray();
00879 //  PathVerb*  Verbs  = InkPath.GetVerbArray();
00880 
00881     // Should always be able to get selected view
00882     DocView *pDocView = DocView::GetSelected();
00883     ERROR3IF(pDocView == NULL, "NodePath::OnClick: Could not get selected DocView");
00884     if (pDocView == NULL)
00885         return FALSE;
00886 
00887     // If there are no selected points we won't put up our menu because its
00888     // items are entirely to do with points (a useful thing in a Blobs menu!).
00889     if (!InkPath.IsSubSelection())
00890         return FALSE;
00891 
00892 //  INT32 NumCoords = InkPath.GetNumCoords();
00893     INT32 i;
00894     if (InkPath.FindNearestPoint(   PointerPos, 
00895                                     POINTFLAG_ENDPOINTS | 
00896                                     POINTFLAG_CONTROLPOINTS | 
00897                                     POINTFLAG_ENDSFIRST,
00898                                     &i)
00899         )
00900     {
00901         pMenu->BuildCommand(TOOL_OPTOKEN_BEZTOOL, TRUE);
00902 
00903         pMenu->BuildCommand(OPTOKEN_MAKELINESOP);
00904         pMenu->BuildCommand(OPTOKEN_MAKECURVESOP, TRUE);
00905 
00906         pMenu->BuildCommand(OPTOKEN_DELETEPOINTSOP);
00907         pMenu->BuildCommand(OPTOKEN_BREAKATPOINTS, TRUE);
00908 
00909         pMenu->BuildCommand(OPTOKEN_SELECTALLPATHPOINTS);
00910         pMenu->BuildCommand(OPTOKEN_DESELECTALLPATHPOINTS, TRUE);
00911 
00912         return TRUE;
00913     }
00914 
00915 #endif
00916     // we do not want to claim the click, so return FALSE
00917     return FALSE;
00918 }
00919 
00920 
00921 
00922 /********************************************************************************************
00923 
00924 >   virtual BOOL NodePath::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
00925 
00926     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00927     Created:    13/05/95
00928     Inputs:     pSpread     The spread in which things are happening
00929                 PointerPos  The Location of the mouse pointer at the time of the click
00930                 pMenu       The menu to which items should be added
00931     Returns:    BOOL - TRUE if the node claims the click as its own and FALSE if it is
00932                 not interested in the click
00933     Purpose:    Allows the Node to respond to pop up menu clicks on it (rather than its blobs).
00934 
00935 ********************************************************************************************/
00936 BOOL NodePath::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
00937 {
00938 #if !defined(EXCLUDE_FROM_RALPH)
00939     BOOL ok = TRUE;
00940     
00941     ok = ok && pMenu->BuildCommand(TOOL_OPTOKEN_BEZTOOL, TRUE);
00942 
00943     return ok;
00944 #else
00945     return FALSE;
00946 #endif
00947 }
00948 
00949 
00950 
00951 /********************************************************************************************
00952 
00953 >   void NodePath::HandleBlobClick(DocCoord* Coords, PathFlags* Flags, INT32 Pos, 
00954                                    INT32 NumCoords, BOOL Adjust, BOOL Constrain)
00955 
00956     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00957     Created:    11/11/93
00958     Inputs:     Coords - The array of coordinates
00959                 Flags - The array of flags
00960                 Pos - The item click on
00961                 Adjust - TRUE if this is an Adjust click
00962                 Constrain - TRIE if this is a Constrain click.
00963     Outputs:    The values in the flags array are modified by this function
00964     Purpose:    This will handle the selection and deselection of points on a path. For curve
00965                 segments all the Bezier control points etc will be drawn in. This routine will
00966                 also spot closed paths and select endpoints as well
00967     Scope:      Private
00968 
00969 ********************************************************************************************/
00970 
00971 void NodePath::HandleBlobClick(DocCoord* Coords, PathFlags* Flags, INT32 Pos, INT32 NumCoords,
00972                                                                 BOOL Adjust, BOOL Constrain)
00973 {
00974 #if !defined(EXCLUDE_FROM_RALPH)
00975     PathVerb* Verbs = InkPath.GetVerbArray();
00976     
00977     // clear the selection state of all the other selected objects in the tree (but not this one)
00978     if (!Adjust && !Constrain)
00979     {
00980         SelRange* Selected = GetApplication()->FindSelection();
00981         Node* pNode = Selected->FindFirst();
00982         while (pNode!=NULL)
00983         {
00984             if ((pNode!=this) && (pNode->IsKindOf(CC_RUNTIME_CLASS(NodePath))))
00985             {
00986                 // This node needs to get rid of all its selected control points
00987                 NodePath* pNodePath = (NodePath*) pNode;
00988                 if (pNodePath->InkPath.IsSubSelection())
00989                 {
00990                     Spread* pSpread = pNodePath->FindParentSpread();
00991                     ENSURE(pSpread!=NULL, "Node did not have a parent spread in NodePath::HandleBlobClick");
00992                     if (pSpread!=NULL)
00993                     {
00994                         pNodePath->InkPath.RenderPathSelectedControlBlobs(pSpread);
00995                         pNodePath->InkPath.ClearSubSelection();
00996                         pNodePath->InkPath.RenderPathSelectedControlBlobs(pSpread);
00997                     }
00998                 }
00999             }
01000 
01001             // Get the next selected node
01002             pNode = Selected->FindNext(pNode);
01003         }
01004     }
01005 
01006     // Find out what the current selection state is, so that we can toggle it
01007     // Selection state will hold the value we want to set
01008     BOOL NewSelState;
01009     BOOL OldSelState = Flags[Pos].IsSelected;
01010     if (OldSelState)
01011         NewSelState = FALSE;
01012     else
01013         NewSelState = TRUE;
01014 
01015     Spread *pSpread = FindParentSpread();
01016     ENSURE(pSpread != NULL, "NodePath does not have a parent spread");
01017 
01018     // Remove the current blobs from this path before we try and change selection
01019     InkPath.RenderPathSelectedControlBlobs(pSpread);
01020 
01021     // If we're not adjusting, clear the subselection in this path
01022     if (!Adjust && !Constrain)
01023         InkPath.ClearSubSelection();
01024 
01025     // if we are adjusting and constraining then (de)select all the endpoints on the path
01026     if (Adjust && Constrain)
01027     {
01028         BOOL NewSelState = !Flags[Pos].IsSelected;
01029         for (INT32 loop = 0; loop < InkPath.GetNumCoords(); loop++)
01030         {
01031             Flags[loop].IsSelected = NewSelState;
01032         }
01033     }
01034     else
01035     {
01036         // Now change the selection of this point
01037         Flags[Pos].IsSelected = NewSelState;
01038         // If the previous point is a control point then deal with it
01039         if ((Pos>0) && (!Flags[Pos-1].IsEndPoint))
01040         {
01041             // Change the control point's selection state
01042             Flags[Pos-1].IsSelected = NewSelState;
01043         }
01044 
01045         // If the next point is a control point then deal with it
01046         if ((Pos+1<NumCoords) && (!Flags[Pos+1].IsEndPoint))
01047         {
01048             // Change the control point's selection state
01049             Flags[Pos+1].IsSelected = NewSelState;
01050         }
01051     
01052         // Check for this being the first element in a closed subpath
01053         // If this element is a moveto, and the end of the path has the
01054         // CLOSEFIGURE flag set, we should select the endpoint as well
01055         if (Verbs[Pos] == PT_MOVETO)
01056         {
01057             INT32 j;
01058             // This for loop will find either the end of the path, or the next moveto
01059             for (j=Pos+1;j<NumCoords && Verbs[j] != PT_MOVETO;j++); // ; is intentional!
01060             j--;
01061             if (Verbs[j] & PT_CLOSEFIGURE)
01062             {
01063                 //HandleBlobClick(Coords,Flags,j,NumCoords,TRUE);
01064                 Flags[j].IsSelected = NewSelState;
01065                 // If the previous point is a control point then deal with it
01066                 if ((j>0) && (!Flags[j-1].IsEndPoint))
01067                 {
01068                     // Change the control point's selection state
01069                     Flags[j-1].IsSelected = NewSelState;
01070                 }
01071             }
01072 
01073         }
01074     }
01075 
01076     InkPath.RenderPathSelectedControlBlobs(pSpread);
01077 #endif
01078 }
01079 
01080 
01081 
01082 
01083 
01084 /********************************************************************************************
01085 
01086 >   virtual void NodePath::Transform( TransformBase& Trans )
01087 
01088     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01089     Created:    20/10/93
01090     Inputs:     Trans - The transform to be applied to the path
01091     Purpose:    Will Transform all the coords in the path with the transform 
01092                 provided
01093     SeeAlso:    NodeRenderableInk::Transform()
01094 
01095 ********************************************************************************************/
01096 
01097 void NodePath::Transform( TransformBase& Trans )
01098 {
01099     // Change all the coords
01100     Trans.Transform( (DocCoord*)InkPath.GetCoordArray(), InkPath.GetNumCoords() );
01101 
01102     // and keep the bounding rectangle up to date.
01103     InvalidateBoundingRect();
01104 
01105     // Transform all the children...
01106     TransformChildren(Trans);
01107 
01108     // Also update our CurrentRotation variable
01109 
01110 }
01111 
01112 
01113 /********************************************************************************************
01114 
01115 >   virtual String NodePath::Describe(BOOL Plural, BOOL Verbose = TRUE)
01116 
01117     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01118     Created:    25/6/93
01119     Inputs:     Plural: Flag indicating if the string description should be plural or
01120                         singular. 
01121     Retuns:     Description of the object 
01122     Purpose:    To return a description of the NodePath object in either the singular or the 
01123                 plural. This method is called by the DescribeRange method.
01124                 The description will always begin with a lower case letter.   
01125     Errors:     A resource exception will be thrown if a problem occurs when loading the 
01126                 string resource. 
01127 
01128 ********************************************************************************************/
01129               
01130 String NodePath::Describe(BOOL Plural, BOOL Verbose) 
01131 {     
01132     // if the first subpath is closed, this is a shape rather than a line
01133     if (InkPath.IsSubPathClosed(0))
01134     {
01135         if (Plural)
01136             return(String(_R(IDS_DESCRIBE_SHAPES)));  
01137         else
01138             return(String(_R(IDS_DESCRIBE_SHAPE))); 
01139     }
01140     else
01141     {
01142         if (Plural)
01143             return(String(_R(IDS_PATH_DESCRP)));  
01144         else
01145             return(String(_R(IDS_PATH_DESCRS))); 
01146     }
01147 }; 
01148 
01149 
01150 
01151 
01152 /********************************************************************************************
01153 
01154 >   DocRect NodePath::GetBoundingRect(BOOL DontUseAttrs=FALSE, BOOL HitTest=FALSE)
01155 
01156     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01157     Created:    17/02/94
01158     Inputs:     DontUseAttrs - TRUE if you don't want to use the nodes attrs to calculate
01159                 the bounding rect (defaults will be used). Defaults to FALSE.
01160                 HitTest      - TRUE if being called during HitTest
01161     Returns:    The paths bounding rectangle.
01162     Purpose:    returns the paths bounding rectangle and recalculates it if it is invalid
01163     SeeAlso:    NodePath::GetBlobBoundingRect; NodeRenderableBounded::GetBoundingRect
01164 
01165 ********************************************************************************************/
01166 
01167 DocRect NodePath::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
01168 {
01169     // go and recalculate the bounding rect if it is not known
01170     if (!IsBoundingRectValid || DontUseAttrs)
01171     {
01172         // a rect to put the new version of the bounding rect into
01173         DocRect NewBoundingRect;
01174 
01175         // Find out what the paths bounding rect is now
01176         if (!CalculatePathBoundingRect(InkPath, DontUseAttrs, &NewBoundingRect))
01177         {
01178             // GDraw failed to find out how big the bounding rect
01179             // we will have to make do with the bounding rect of the coords
01180             NewBoundingRect = InkPath.GetBoundingRect();
01181         }
01182 
01183         // If the line is vertical/horizontal with zero width then we will have a 0 width/height rect
01184         // Inflate this so rectangle calculations work.
01185         if (NewBoundingRect.Width() == 0)
01186             NewBoundingRect.hi.x++;
01187         if (NewBoundingRect.Height() == 0)
01188             NewBoundingRect.hi.y++;
01189 
01190         // we have a new bounding rect - decide what to do with it
01191         if (DontUseAttrs)
01192         {
01193             // but it is not the real bounding rect, so just return it
01194             return NewBoundingRect;
01195         }
01196         else
01197         {
01198             // We need to go though the attributes applied to this path,
01199             // and see if any of them effect the bounding rect
01200             // (eg. ArrowHeads)
01201             CCAttrMap AttribMap(30);
01202             if (FindAppliedAttributes(&AttribMap))
01203             {
01204                 void* pType;
01205                 void* pVal;
01206 
01207                 // iterating all (key, value) pairs
01208                 CCAttrMap::iterator end = AttribMap.GetEndPosition();
01209                 for( CCAttrMap::iterator Pos = AttribMap.GetStartPosition(); Pos != end; ++Pos )
01210                 {
01211                     // Get attr at position Pos
01212                     pType = Pos->first;
01213                     pVal = Pos->second;
01214 
01215                     if (pVal != NULL)
01216                     {
01217                         if ( ((NodeAttribute*)pVal)->EffectsParentBounds() )
01218                         {
01219                             // Get the bounds of the attribute and Union it with
01220                             // the path bounds
01221                             DocRect AttrBounds = 
01222                                 ((NodeAttribute*)pVal)->GetAttrBoundingRect(this, &AttribMap);
01223                             NewBoundingRect = NewBoundingRect.Union(AttrBounds);
01224                         }
01225                     }
01226                 }
01227             }
01228 
01229             // Update the Nodes bounding rectangle
01230             BoundingRectangle = NewBoundingRect;
01231 
01232             // Mark the rect as valid
01233             IsBoundingRectValid = TRUE;
01234         }
01235     }
01236 
01237     // return the current bounding rect
01238     return BoundingRectangle;
01239 }
01240 
01241 
01242 
01243 /********************************************************************************************
01244 
01245 >   DocRect NodePath::GetBlobBoundingRect()
01246 
01247     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01248     Created:    17/02/94
01249     Returns:    DocRect - Returns the bounding rect of the path and its blobs
01250     Purpose:    This calculates the bounding box of the path and adds in the influence of
01251                 the selection blobs. It does not consider if the blobs are visible or not,
01252                 it just gives the bounding box that they would occupy if they were visible
01253 
01254 ********************************************************************************************/
01255 
01256 DocRect NodePath::GetBlobBoundingRect()
01257 {
01258     // Also, scan through all the points to find the bounds of the blobs
01259     PathVerb* Verbs = InkPath.GetVerbArray();
01260     PathFlags* Flags = InkPath.GetFlagArray();
01261     DocCoord* Coords = InkPath.GetCoordArray();
01262     INT32 NumCoords = InkPath.GetNumCoords();
01263 
01264     DocRect BlobRect;
01265     DocRect Rect;
01266 
01267     //Go get the blob manager and just return the bounding rect
01268     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01269     if (BlobMgr==NULL)
01270         return GetBoundingRect();
01271 
01272     // loop through all the coords adding them to the bounding rect
01273     for (INT32 i=0; i<NumCoords; i++)
01274     {
01275         if (i==0)
01276         {
01277             // Set up initial rect to be just the first point
01278             Rect = DocRect(Coords[0].x,      Coords[0].y,
01279                            Coords[0].x + 1L, Coords[0].y + 1L);
01280         }
01281         else
01282         {
01283             // Expand the rectangle to include this point
01284             Rect.IncludePoint(Coords[i]);
01285         }
01286     }
01287 
01288     // Put blobs at the bottom-left and top-right corners, and include these in the rectangle
01289     BlobMgr->GetBlobRect(Rect.lo, &BlobRect);
01290     Rect = Rect.Union(BlobRect);
01291     BlobMgr->GetBlobRect(Rect.hi, &BlobRect);
01292     Rect = Rect.Union(BlobRect);
01293 
01294     // Now make sure we include any extra blobs dependent on the
01295     // blob type
01296 
01297     BlobStyle Blobs = BlobMgr->GetCurrentInterest(TRUE);
01298     if (Blobs.Pen)
01299     {
01300         // ok, if its the pen blobs, we need to do extra things over
01301         // and above the standard coord blobs. We need to add an extra
01302         // ghost point at the end of the path, if there is a curveto
01303 
01304         PathVerb LastVerb = Verbs[NumCoords-1];
01305         PathFlags LastFlag = Flags[NumCoords-1];
01306 
01307         if ((LastVerb == PT_BEZIERTO) && (LastFlag.IsRotate))
01308         {
01309             DocCoord Pt0 = Coords[NumCoords-2];
01310             DocCoord Pt1 = Coords[NumCoords-1];
01311 
01312             DocCoord GhostPt;
01313             GhostPt.x = Pt1.x - (Pt0.x - Pt1.x);
01314             GhostPt.y = Pt1.y - (Pt0.y - Pt1.y);
01315 
01316             BlobMgr->GetBlobRect(GhostPt,&BlobRect);
01317             Rect = Rect.Union(BlobRect);
01318         }
01319     }
01320 
01321     // Make sure we include the Bounds of our children
01322     IncludeChildrensBoundingRects(&Rect);
01323 
01324     // return the rectangle with the blobs included
01325     return Rect;
01326 }
01327 
01328 //----------------------------------------------------------------------------------------------
01329 //----------------------------------------------------------------------------------------------
01330 //----------------------------------------------------------------------------------------------
01331 //----------------------------------------------------------------------------------------------
01332 
01333 /********************************************************************************************
01334 
01335 >   virtual BOOL NodePath::CanBecomeA(BecomeA* pBecomeA)
01336 
01337     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01338     Created:    10/10/94
01339     Inputs:     pClass = runtime class to node you wish this node to become
01340                 pNumObjects = ptr to place number of objects of type pClass that will be created (Note: can be NULL).
01341                               *pNumObects in undefined on entry
01342     Outputs:    -
01343     Returns:    TRUE if it can, FALSE if it can't 
01344     Purpose:    Interrogation routine to see if a node can be changed into a different node type.
01345                 Returns TRUE if pClass is a NodePath, FALSE otherwise
01346 
01347                 The number you put into pNumObjects (if it's not NULL) should exactly equal the total number
01348                 of pClass objects you create.  It should NOT contain any additional objects you may produce
01349                 such as group objects for containing the pClass object, or attributes.
01350 
01351                 Also, the entry value of *pNumObjects cannot be assumed to be 0.
01352 
01353     Errors:     -
01354     SeeAlso:    NodePath::DoBecomeA()
01355 
01356 ********************************************************************************************/
01357 
01358 BOOL NodePath::CanBecomeA(BecomeA* pBecomeA)
01359 {
01360     if (pBecomeA->BAPath())
01361     {
01362         pBecomeA->AddCount(1);
01363 
01364         return TRUE;
01365     }
01366 
01367     return FALSE;
01368 }
01369 
01370 /********************************************************************************************
01371 
01372 >   virtual BOOL NodePath::DoBecomeA(BecomeA* pBecomeA)
01373 
01374     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01375     Created:    10/10/94
01376     Inputs:     pBecomeA =  ptr to info class containing everything a node needs to be able
01377                             to become something else
01378     Outputs:    -
01379     Returns:    TRUE if successful, FALSE otherwise
01380     Purpose:    Actually tries to change the node into a different node type.
01381                 This responds to BECOMEA_PASSBACK reason by making a duplicate NodePath and passing
01382                 it to the pfnPassBack func provided in the pBecomeA class
01383     Errors:     -
01384     SeeAlso:    NodePath::CanBecomeA()
01385 
01386 ********************************************************************************************/
01387 
01388 BOOL NodePath::DoBecomeA(BecomeA* pBecomeA)
01389 {
01390     // Check for a NULL entry param
01391     ERROR2IF_PF(pBecomeA == NULL,FALSE,("pBecomeA is NULL"));
01392 
01393     // This lump checks that the Reason is one that we understand
01394     // It also makes sure that we don't have a NULL UndoOp ptr
01395     BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK);
01396     ERROR2IF_PF(!ValidReason,FALSE,("Unkown BecomeA reason %d",pBecomeA->GetReason()));
01397 
01398     BOOL        Success = TRUE;         // Our success flag (Important that this defaults to TRUE)
01399     NodePath*   pNewNodePath = NULL;    // Ptr to a new NodePath, if we get to make one.
01400     
01401     if (pBecomeA->BAPath())
01402     {
01403         switch (pBecomeA->GetReason())
01404         {
01405             case BECOMEA_REPLACE:
01406                 // We do nothing as we are already a NodePath in the tree
01407                 // except tell the caller where we are in the tree...
01408                 pBecomeA->PassBack(this, this);
01409                 break;
01410 
01411             case BECOMEA_PASSBACK :
01412             {
01413                 // Make a copy of this NodePath
01414                 CALL_WITH_FAIL(((pNewNodePath = (NodePath*)SimpleCopy()) != NULL), pBecomeA->GetUndoOp(), Success);
01415 
01416                 // If successful, pass it back with my attribute map
01417                 if (Success) Success = pBecomeA->PassBack(pNewNodePath,this);
01418             }
01419             break;
01420             default: break;
01421         }
01422     }
01423 
01424     if (!Success)
01425     {
01426         if (pNewNodePath != NULL)
01427         {
01428             // Delete all the NodePath's children (if it has any) and unlink it from the tree (if it's linked)
01429             // This is all done by CascadeDelete()
01430             pNewNodePath->CascadeDelete(); 
01431             delete pNewNodePath;
01432             pNewNodePath = NULL;
01433         }
01434     }
01435 
01436     return Success;
01437 }
01438 
01439 
01440 /***********************************************************************************************
01441 >   virtual NodePath* NodePath::GetVariableWidthStrokePath()
01442     
01443     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01444     Created:    15-5-2000
01445     Inputs:     -  
01446     Outputs:    -
01447     Returns:    a nodepath containing the path that is generated by any variable width strokes that
01448                 are applied to us, or NULL if there aren't any
01449     Purpose:    If we have a variable width stroke applied to us then this will get the path generated
01450                 by that stroke.  This base class version returns NULL, overridden versions must supply
01451                 their own outline path.
01452 
01453                 In this version we simply use our inkpath
01454 
01455     See also:   NodePath::GetVariableWidthStrokePath, NodeRegularShape::GetVariableWidthStrokePath
01456                 
01457     
01458 ***********************************************************************************************/
01459 
01460 NodePath* NodePath::GetVariableWidthStrokePath()
01461 {
01462     // first find out if we actually have a variable width applied to us, if not then we don't do anything
01463     AttrVariableWidth* pVarWidth = NULL;
01464     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute**)&pVarWidth);
01465     if (pVarWidth && ((VariableWidthAttrValue*)pVarWidth->GetAttributeValue())->GetWidthFunction() == NULL)
01466         return NULL;
01467 
01468     // next find the stroke attribute that actually does the work
01469     AttrStrokeType* pStroke = NULL;
01470     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute**)&pStroke);
01471     
01472     NodePath* pRetNode = NULL;
01473     if (pStroke && pStroke->HasPathProcessor())
01474     {
01475         PathProcessorStroke* pPPS = pStroke->GetPathProcessor();
01476             
01477         pRetNode = pPPS->GetProcessedPath(&InkPath, this);
01478     }
01479         
01480     return pRetNode;    
01481 }
01482 
01483 
01484 /***********************************************************************************************
01485 >   virtual NodePath* NodePath::GetSmoothVariableWidthStrokePath()
01486     
01487     Author:     Priestley (Xara Group Ltd) <camelotdev@xara.com> ripped from Diccon
01488     Created:    18-11-2000
01489     Inputs:     -  
01490     Outputs:    -
01491     Returns:    a nodepath containing the path that is generated by any variable width strokes that
01492                 are applied to us, or NULL if there aren't any
01493     Purpose:    If we have a variable width stroke applied to us then this will get the path generated
01494                 by that stroke.  This base class version returns NULL, overridden versions must supply
01495                 their own outline path.
01496 
01497                 THIS FUNCTION IS THE SAME AS THAT ABOVE, BUT JUST RETURNS THE SMOOTHED PATH
01498 
01499     See also:   NodePath::GetVariableWidthStrokePath, NodeRegularShape::GetVariableWidthStrokePath
01500                 
01501     
01502 ***********************************************************************************************/
01503 
01504 NodePath* NodePath::GetSmoothVariableWidthStrokePath()
01505 {
01506     // first find out if we actually have a variable width applied to us, if not then we don't do anything
01507     AttrVariableWidth* pVarWidth = NULL;
01508     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute**)&pVarWidth);
01509     if (pVarWidth && ((VariableWidthAttrValue*)pVarWidth->GetAttributeValue())->GetWidthFunction() == NULL)
01510         return NULL;
01511 
01512     // next find the stroke attribute that actually does the work
01513     AttrStrokeType* pStroke = NULL;
01514     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute**)&pStroke);
01515     
01516     NodePath* pRetNode = NULL;
01517     if (pStroke && pStroke->HasPathProcessor())
01518     {
01519         PathProcessorStroke* pPPS = pStroke->GetPathProcessor();
01520             
01521         pRetNode = pPPS->GetSmoothProcessedPath(&InkPath, this);
01522     }
01523         
01524     return pRetNode;    
01525 }
01526 
01527 
01528 
01529 /***********************************************************************************************
01530 
01531 >   NodePath * NodePath::MakeNodePathFromAttributes(MILLIPOINT Flatness, CCAttrMap * pAttrMap,
01532                             BOOL bIncludeClosedPaths = FALSE, BOOL IncludeWidth = TRUE)
01533 
01534     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01535     Created:    6/12/99
01536     Inputs:     Flatness    -   The flatness to use in the stroking
01537                 pAttrMap    -   The attribute map to use (if NULL then use the attribute map
01538                                 applied to this object)
01539     Outputs:     
01540     Returns:    A new node path, stroked + with all line attributes applied to it (i.e.
01541                 line width, dash patterns, arrowheads etc)
01542     Purpose:    See returns
01543     Errors:        
01544     Scope:      public
01545            
01546 **********************************************************************************************/
01547 NodePath * NodePath::MakeNodePathFromAttributes(double Flatness, CCAttrMap * pAttrMap,
01548                                                 BOOL bIncludeClosedPaths, BOOL IncludeWidth)
01549 {
01550     // Find the applied attributes
01551     AttrLineWidth * pAttrLineWidth = NULL;
01552     AttrStartArrow * pAttrStartArrow = NULL;
01553     AttrEndArrow * pAttrEndArrow = NULL;
01554     AttrJoinType * pAttrJoinType = NULL;
01555     AttrStartCap * pAttrStartCap = NULL;
01556     AttrDashPattern * pAttrDashPattern = NULL;
01557     AttrStrokeColour* pAttrStrokeColour = NULL;
01558     AttrStrokeType* pAttrStrokeType = NULL;
01559     AttrVariableWidth* pAttrVariableWidth = NULL;
01560 
01561     if (!pAttrMap)
01562     {
01563         // get all the attributes out of the node
01564         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth),(NodeAttribute **)(&pAttrLineWidth));
01565         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStartArrow),(NodeAttribute **)(&pAttrStartArrow));
01566         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrEndArrow),(NodeAttribute **)(&pAttrEndArrow));
01567         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType),(NodeAttribute **)(&pAttrJoinType));
01568         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStartCap),(NodeAttribute **)(&pAttrStartCap));    
01569         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrDashPattern),(NodeAttribute **)(&pAttrDashPattern));  
01570         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour),(NodeAttribute **)(&pAttrStrokeColour));    
01571         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute **)(&pAttrStrokeType));
01572         FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute **)(&pAttrVariableWidth));
01573     }
01574     else
01575     {
01576         // get all the attributes out of the attribute map
01577         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrLineWidth), (void *&)pAttrLineWidth );
01578         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartArrow), (void *&)pAttrStartArrow );
01579         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrEndArrow), (void *&)pAttrEndArrow);
01580         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrJoinType), (void *&)pAttrJoinType);
01581         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStartCap), (void *&)pAttrStartCap);
01582         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrDashPattern), (void *&)pAttrDashPattern);
01583         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeColour), (void *&)pAttrStrokeColour);
01584         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeType), (void *&)pAttrStrokeType);
01585         pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrVariableWidth), (void *&)pAttrVariableWidth);
01586 
01587         if (!pAttrLineWidth)
01588             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth),(NodeAttribute **)(&pAttrLineWidth));
01589         
01590         if (!pAttrStartArrow)
01591             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStartArrow),(NodeAttribute **)(&pAttrStartArrow));
01592 
01593         if (!pAttrEndArrow)
01594             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrEndArrow), (NodeAttribute **)(&pAttrEndArrow));
01595         
01596         if (!pAttrJoinType)
01597             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType),(NodeAttribute **)(&pAttrJoinType));
01598         
01599         if (!pAttrStartCap)
01600             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStartCap),(NodeAttribute **)(&pAttrStartCap));
01601         
01602         if (!pAttrDashPattern)
01603             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrDashPattern),(NodeAttribute **)(&pAttrDashPattern));  
01604         
01605         if (!pAttrStrokeColour)
01606             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour),(NodeAttribute **)(&pAttrStrokeColour));    
01607 
01608         if (!pAttrStrokeType)
01609             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute **)(&pAttrStrokeType));
01610 
01611         if(!pAttrVariableWidth)
01612             FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute **)(&pAttrVariableWidth));
01613     }
01614 
01615 // ********************************************************************************************
01616 // Matt 20/10/2000
01617 // At this point if stroke shapes are used, we may have a path consisting of a number of points
01618 // joined by lines - we can represent this much more efficiently as a path of a fewer number of
01619 // points joined by curves: This is achieved by smoothing the path with RetroSmooth
01620 // ********************************************************************************************
01621 
01622     // Mark Howitt 18/12/00 - I`ve now made it check to see if the Stroke colour is transparent, as
01623     // the outline of the last line width is returned which is not correct.
01624     // When the outline is made NONE it does not contribute to the objects outline.
01625     BOOL IsTransparentStroke = FALSE;
01626 
01627     if(pAttrStrokeColour)
01628     {
01629         DocColour* pCol = pAttrStrokeColour->Value.GetStartColour();
01630         IsTransparentStroke = pCol->IsTransparent();
01631     }
01632 
01633     // If this nodepath belongs to an object with a stroke type attribute, then we should smooth it...
01634     if (pAttrStrokeType && !IsTransparentStroke)
01635     {
01636         // Check to see if the Stroke is the default type by checking the Variable width Attribute
01637         // Width function pointer!
01638         if(pAttrVariableWidth && ((VariableWidthAttrValue*)pAttrVariableWidth->GetAttributeValue())->GetWidthFunction() != NULL)
01639         {
01640 //          double Smooth = 15.0;
01641 
01642 #ifdef CONVERTSTROKES  // vector stroking in here
01643 
01644             PathProcessorStroke* pPPS = pAttrStrokeType->GetPathProcessor();
01645             if (pPPS)
01646             {
01647                 BOOL OriginalIsClosed = InkPath.IsClosed();
01648 
01649                 Path UnStrokedPath;
01650                 UnStrokedPath.Initialise();
01651 
01652                 if(OriginalIsClosed)
01653                     UnStrokedPath.CloneFrom(InkPath);
01654 
01655                 // path processor stroke always wants stroked unfilled path
01656                 Path ClonePath;
01657                 ClonePath.Initialise();
01658                 ClonePath.CloneFrom(InkPath);
01659 
01660                 ClonePath.IsFilled = FALSE;
01661                 ClonePath.IsStroked = TRUE;
01662 
01663                 // Path processor makes a nodepath for us
01664                 NodePath* pNewNodePath = pPPS->GetSmoothProcessedPath(&ClonePath, this);
01665 
01666                 if (pNewNodePath == NULL)
01667                 {
01668                     ERROR3("Urk - Cannae get yer stroak path lassie");
01669                 }
01670                 else
01671                 {
01672                     double SFlat = pNewNodePath->InkPath.CalculateFlatnessValueFromPath(500.0,2.0,375.0);
01673                     double CFlat = UnStrokedPath.CalculateFlatnessValueFromPath(500.0,2.0,375.0);
01674                     DWORD ClipRule = OriginalIsClosed ? 7 : 3;
01675                     if(UnStrokedPath.ClipPathToPath(pNewNodePath->InkPath,&pNewNodePath->InkPath,ClipRule|CLIPPING_SOURCE_WINDING,20,SFlat,CFlat) < 0)
01676                     {
01677                         ERROR3("Urk - Cannae get yer stroak path lassie");
01678                     }
01679                 }
01680 
01681                 return pNewNodePath;
01682             }
01683 #else               
01684             if (RetroSmoothMe(Smooth))
01685             {
01686                 // make a copy to return
01687                 NodePath * pRetnNode = new NodePath;
01688                 ERROR2IF(!pRetnNode,NULL,"Failed to create a new Path Node!");
01689                 pRetnNode->InkPath.Initialise();
01690                 pRetnNode->InkPath.CloneFrom(InkPath);
01691                 return pRetnNode;
01692             }
01693 #endif
01694         }
01695     }
01696 
01697 // ********************************************************************************************
01698 // End of RetroSmoothing...
01699 // ********************************************************************************************
01700 
01701     // Make the return node and initialize it!
01702     NodePath * pRetnNode = new NodePath;
01703     ERROR2IF(!pRetnNode,NULL,"Failed to create a new Path Node!");
01704     pRetnNode->InkPath.Initialise();
01705     
01706     Path SrcPath;
01707     SrcPath.Initialise();
01708     SrcPath.CloneFrom(InkPath);
01709 
01710     DashPatternAttribute* pDashPattern = NULL;
01711     StartArrowAttribute* pStartArrow = NULL;
01712     LineWidthAttribute* pLineWidth = NULL;
01713     EndArrowAttribute* pEndArrow = NULL;
01714     JoinTypeAttribute* pJoinType = NULL;
01715     StartCapAttribute* pStartCap = NULL;
01716 
01717     // set up the attribute values
01718     if (pAttrLineWidth)
01719         pLineWidth = &(pAttrLineWidth->Value);
01720 
01721     if (pAttrStartArrow)
01722         pStartArrow = &(pAttrStartArrow->Value);
01723 
01724     if (pAttrEndArrow)
01725         pEndArrow = &(pAttrEndArrow->Value);
01726 
01727     if (pAttrJoinType)
01728         pJoinType = &(pAttrJoinType->Value);
01729 
01730     if (pAttrStartCap)
01731         pStartCap = &(pAttrStartCap->Value);
01732 
01733     if (pAttrDashPattern)
01734         pDashPattern = &(pAttrDashPattern->Value);
01735 
01736     // go through each path checking for non-closed paths
01737     INT32 StartIndex = 0;
01738     INT32 EndIndex = 0;
01739 
01740     Path SubPath;
01741     SubPath.Initialise();
01742 
01743     Path FlatPath;
01744     FlatPath.Initialise();
01745 
01746     UINT32 SubPathNum = 0;
01747     MILLIPOINT Width = 750;
01748     MILLIPOINT EndsWidth = 750;
01749 
01750     if(pLineWidth != NULL)
01751         Width = EndsWidth = pLineWidth->LineWidth;
01752 
01753     if(pAttrStrokeColour && pAttrStrokeColour->GetStartColour()->IsTransparent())
01754     {
01755         Width = 0;
01756         pDashPattern = NULL;
01757     }
01758 
01759     JoinStyles JoinS = JOIN_ROUND;
01760 
01761     if(pJoinType != NULL)
01762         JoinS = (pJoinType->JoinType == MitreJoin) ? JOIN_MITER : (pJoinType->JoinType == RoundJoin) ? JOIN_ROUND : JOIN_BEVEL;
01763 
01765     // Dash pattern - convert to Gavin-ness...
01766     DashType* pDashRec = NULL;
01767     DashType GavinDash;
01768     GavinDash.Length = 0;
01769     GavinDash.Offset = 0;
01770     GavinDash.Array[0] = 0;
01771 
01772     // Do do dash patten if there is one and the line width is greater than 0!
01773     if (pDashPattern && pDashPattern->DashPattern.Elements > 0 && pDashPattern->DashPattern.LineWidth > 0)
01774     {
01775         INT32 Length = pDashPattern->DashPattern.Elements;
01776 
01777         if (Length > 8) Length = 8;
01778 
01779         BOOL DoScale = pDashPattern->DashPattern.ScaleWithLineWidth;
01780         FIXED16 Scale = DoScale ? (double(Width) / double(pDashPattern->DashPattern.LineWidth)) : 1;
01781 
01782         GavinDash.Length = Length;
01783         GavinDash.Offset = LongMulFixed16(pDashPattern->DashPattern.DashStart, Scale);
01784 
01785         for (INT32 el = 0; el < Length; el++)
01786         {
01787             GavinDash.Array[el] = LongMulFixed16(pDashPattern->DashPattern.ElementData[el], Scale);
01788         }
01789     }
01790 
01791     // Set the dash pointer to the gavin dash structure!
01792     pDashRec = &GavinDash;
01793 
01795 
01796     // Make sure that we put the Width to a suitable value if we don`t require line widths in
01797     // the calculations
01798     if(!IncludeWidth && pAttrStrokeColour && !pAttrStrokeColour->GetStartColour()->IsTransparent())
01799         Width = 50;
01800     
01801     Path BlankPath;
01802     BlankPath.Initialise();
01803 
01804     Path StrokedPath;
01805     StrokedPath.Initialise();
01806 
01807     while (StartIndex < InkPath.GetNumCoords())
01808     {
01809         // get the end of the sub-path
01810         EndIndex = StartIndex;
01811         InkPath.FindEndElOfSubPath(&EndIndex);
01812         
01813         // only do this if the sub-path isn't closed
01814 //      if (!InkPath.IsSubPathClosed(StartIndex) || bIncludeClosedPaths)
01815         if (!InkPath.IsClosed() || bIncludeClosedPaths)
01816         {       
01817             // we have found the last element of the sub-path
01818             // therefore make a new path
01819             SubPath.ClearPath();
01820             InkPath.MakePathFromSubPath(SubPathNum, &SubPath);
01821             FlatPath.ClearPath();
01822             FlatPath.CloneFrom(SubPath);
01823             
01824             // first, stroke the path           
01825             SrcPath.IsFilled = FALSE;
01826             SrcPath.IsStroked = FALSE;
01827 
01828             if (pStartCap && pJoinType)
01829             {
01830                 SubPath.StrokePathToPath(Width,
01831                     pStartCap->StartCap,
01832                     pJoinType->JoinType,
01833                     pDashRec,
01834                     &StrokedPath,
01835                     Flatness,
01836                     FALSE);
01837             }
01838             else
01839             {
01840                 SubPath.StrokePathToPath(Width,
01841                     LineCapRound,
01842                     RoundJoin,
01843                     pDashRec,
01844                     &StrokedPath,
01845                     Flatness,
01846                     FALSE);
01847             }
01848 
01849             // ensure the validity of the path
01850             Path ClipPath;
01851             ClipPath.Initialise();
01852             ClipPath.CloneFrom(StrokedPath);
01853             StrokedPath.ClearPath();
01854 
01855             if(ClipPath.ClipPathToPath(BlankPath, &StrokedPath, 6 | CLIPPING_CLIP_WINDING,30, Flatness, Flatness) < 2)
01856                 StrokedPath.CloneFrom(ClipPath);
01857             
01858             // now, do the arrowheads
01859             INT32 Index = 0;
01860             DocCoord Centre;
01861             DocCoord Direction;
01862             Trans2DMatrix ArrowMatrix;
01863             
01864             Path ArrowPath;
01865             ArrowPath.Initialise();
01866             
01867             Path DestPathStartArrow;
01868             DestPathStartArrow.Initialise();
01869             
01870             Path DestPathEndArrow;
01871             DestPathEndArrow.Initialise();
01872 
01873             ClipPath.ClearPath();
01874 
01875             // do the start arrow
01876             if (pStartArrow)
01877             {
01878                 if (pStartArrow->StartArrow.GetArrowPath())
01879                 {
01880                     Centre = FlatPath.GetCoordArray()[0];
01881                     Direction = FlatPath.GetCoordArray()[1];
01882                     
01883                     pStartArrow->StartArrow.GetArrowMatrix(Centre, Direction,
01884                                         EndsWidth, &ArrowMatrix);
01885                     
01886                     // build the arrow's path and add it to the existing path
01887                     ArrowPath.ClearPath();
01888                     ArrowPath.CloneFrom(*pStartArrow->StartArrow.GetArrowPath());
01889                     
01890                     ArrowMatrix.Transform(ArrowPath.GetCoordArray(), ArrowPath.GetNumCoords());
01891                     
01892                     DestPathStartArrow.ClearPath();
01893                     ClipPath.CloneFrom(StrokedPath);
01894                     if(ClipPath.ClipPathToPath(ArrowPath,&StrokedPath, 7 | CLIPPING_SOURCE_WINDING,30, Flatness, Flatness) < 2)
01895                     {
01896                         TRACEUSER( "MarkH", _T("ClipFailed so copying path! NODEPATH\n"));
01897                     }
01898 
01899                     pRetnNode->InkPath.MergeTwoPaths(DestPathStartArrow);
01900                 }
01901             }
01902             
01903             // do the end arrow
01904             if (pEndArrow)
01905             {
01906                 if (pEndArrow->EndArrow.GetArrowPath())
01907                 {
01908                     Index = 0;
01909 
01910                     Centre = FlatPath.GetCoordArray()[FlatPath.GetNumCoords() - 1];
01911                     Direction.x = FlatPath.GetCoordArray()[FlatPath.GetNumCoords() - 1].x -
01912                         FlatPath.GetCoordArray()[FlatPath.GetNumCoords() - 2].x;
01913 
01914                     Direction.y = FlatPath.GetCoordArray()[FlatPath.GetNumCoords() - 1].y -
01915                         FlatPath.GetCoordArray()[FlatPath.GetNumCoords() - 2].y;
01916 
01917                     Direction.x = Centre.x - Direction.x;
01918                     Direction.y = Centre.y - Direction.y;
01919                     
01920                     pEndArrow->EndArrow.GetArrowMatrix(Centre, Direction,
01921                         EndsWidth, &ArrowMatrix);
01922                     
01923                     // build the arrow's path and add it to the existing path
01924                     ArrowPath.ClearPath();
01925                     ArrowPath.CloneFrom(*pEndArrow->EndArrow.GetArrowPath());
01926                     
01927                     ArrowMatrix.Transform(ArrowPath.GetCoordArray(), ArrowPath.GetNumCoords());
01928                     
01929                     DestPathEndArrow.ClearPath();
01930                     ClipPath.CloneFrom(StrokedPath);
01931                     if(ClipPath.ClipPathToPath(ArrowPath,&StrokedPath, 7 | CLIPPING_SOURCE_WINDING,30, Flatness, Flatness) < 2)
01932                     {
01933                         ArrowPath.CloneFrom(ClipPath);
01934                         TRACEUSER( "MarkH", _T("ClipFailed so copying path! NODEPATH\n"));
01935                     }
01936 
01937                     pRetnNode->InkPath.MergeTwoPaths(DestPathEndArrow);
01938                 }
01939             }
01940 
01941             if (pRetnNode->InkPath.GetBoundingRect().Union(StrokedPath.GetBoundingRect()).IsEmpty())
01942             {
01943                 pRetnNode->InkPath.MergeTwoPaths(StrokedPath);
01944             }
01945             else
01946             {
01947                 ClipPath.CloneFrom(pRetnNode->InkPath);
01948                 pRetnNode->InkPath.ClearPath();
01949                 if(StrokedPath.ClipPathToPath(ClipPath, &(pRetnNode->InkPath), 7 | CLIPPING_SOURCE_WINDING,30, Flatness, Flatness) < 2)
01950                 {
01951                     pRetnNode->InkPath.CloneFrom(ClipPath);
01952                     TRACEUSER( "MarkH", _T("ClipFailed so copying path! NODEPATH\n"));
01953                 }
01954             }
01955         }
01956         else
01957         {
01958             SubPath.ClearPath();
01959 
01960             // make sure that if we do require line widths to be taken into account then we need to stroke using the line width!
01961             if(IncludeWidth)
01962             {
01963                 // First do the clipping to make sure the windings come out correct!
01964                 SubPath.CloneFrom(InkPath);
01965                 if(BlankPath.ClipPathToPath(SubPath, &StrokedPath, 3, 10, Flatness, Flatness) < 2)
01966                 {
01967                     StrokedPath.CloneFrom(SubPath);
01968                     TRACEUSER( "MarkH", _T("ClipFailed so copying path! NODEPATH\n"));
01969                 }
01970 
01971                 // Now get the contour step for the full width!
01972                 StrokedPath.InitializeContourValues(Width,JoinS,true,Flatness);
01973                 if(StrokedPath.GetContourForStep(&pRetnNode->InkPath,1.0) < 2)
01974                 {
01975                     pRetnNode->InkPath.CloneFrom(StrokedPath);
01976                     TRACEUSER( "MarkH", _T("ClipFailed so copying path! NODEPATH\n"));
01977                 }
01978             }
01979             else
01980             {
01981                 // Just clone the original!
01982                 InkPath.MakePathFromSubPath(SubPathNum, &SubPath);
01983                 pRetnNode->InkPath.MergeTwoPaths(SubPath);
01984             }
01985         }                           
01986 
01987         StartIndex = EndIndex+1; 
01988         SubPathNum++;
01989     }
01990 
01991     if (pRetnNode)
01992     {
01993         pRetnNode->InkPath.IsFilled = TRUE;
01994         pRetnNode->InkPath.IsStroked = FALSE;
01995     }
01996 
01997     return pRetnNode;
01998 }
01999 
02000 
02001 /***********************************************************************************************
02002 
02003 > BOOL NodePath::RetroSmoothMe(double Smoothness)
02004 
02005     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02006     Created:    20/9/94
02007     Inputs:     Smoothness - the amount to smooth by
02008     Outputs:    
02009     Returns:    TRUE if everything went ok, FALSE if not.
02010                                                          
02011     Purpose:    Use and abuse the retrosmoother to smooth our ink path.  The Retrosmoother is normally
02012                 activated from the gadget in the shape editor tool however we are hijacking it here for our
02013                 purposes.
02014     Errors:        
02015     Scope:      public
02016            
02017 **********************************************************************************************/
02018 
02019 BOOL NodePath::RetroSmoothMe(double Smoothness)
02020 {
02021     if (Smoothness <= 0)
02022     {
02023         ERROR3("Zero smoothing value in NodePath::RetroSmoothMe");
02024         return TRUE;
02025     }
02026 
02027     Spread *pSpread = Document::GetSelectedSpread();
02028     ERROR2IF(pSpread == NULL, FALSE, "No spread in NodePath::RetroSmoothMe");
02029 
02030     //Make a retro smoother and tell it not to bother with EOR rendering on top
02031     RetroSmooth rSmoother;
02032     rSmoother.Initialise();
02033     rSmoother.SetRenderFlag(false);
02034 
02035     // all path points must be selected
02036     InkPath.SetAllSubSelection();
02037 
02038     // set the smoother to work
02039     rSmoother.Changing(this, pSpread, Smoothness);
02040     BOOL ok = rSmoother.FinishedNoUndo(this);
02041     
02042     InkPath.ClearSubSelection();
02043 
02044     return ok;
02045 }
02046 
02047 
02048 //----------------------------------------------------------------------------------------------
02049 //----------------------------------------------------------------------------------------------
02050 //----------------------------------------------------------------------------------------------
02051 //----------------------------------------------------------------------------------------------
02052 
02053 /***********************************************************************************************
02054 
02055 > BOOL NodePath::Snap(DocCoord* pDocCoord)
02056 
02057     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02058     Created:    20/9/94
02059     Inputs:     pDocCoord   = a coord in Spread coords
02060     Outputs:    
02061     Returns:    TRUE    - the DocCoord has been snapped to the path.
02062                 FALSE   - the DocCoord has not been processed.
02063                                                          
02064     Purpose:    Snaps to given coord to the nearest point on the path.  If it is not appropriate to snap
02065                 the coord to the path (at the moment this means the coord is too far awawy), then FALSE is returned.
02066     Errors:        
02067     Scope:      public
02068            
02069 **********************************************************************************************/
02070 
02071 BOOL NodePath::Snap(DocCoord* pDocCoord)
02072 {
02073 #if !defined(EXCLUDE_FROM_RALPH)
02074     return CSnap::SnapCoordToPath(pDocCoord, &InkPath);
02075 #else
02076     return FALSE;
02077 #endif
02078 }
02079 
02080 
02081 
02082 /***********************************************************************************************
02083 
02084 > BOOL NodePath::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord)
02085 
02086     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02087     Created:    21/9/94
02088     Inputs:     pDocCoord   - the rectangle to snap
02089                 StartDrag   - Start coord of drag
02090                 EndDrag     - End coord of drag
02091     Outputs:    
02092     Returns:    TRUE    - the DocRect been snapped to the grid.
02093                 FALSE   - the DocRect has not been processed.
02094                                                          
02095     Purpose:    Snaps the given rect to the nearest position on the grid, preserving its width
02096                 and height.
02097                 The coords of the rect used for the snapping are determined by the PrevCoord and
02098                 CurCoord coords supplied.  This is done to allow the user to control how a
02099                 selection rectangle is snapped to the grid by the direction of his/her last mouse 
02100                 movement.
02101                 To force the bottom left hand corner of the rect to be snapped, 
02102                 supply PrevCoord=(0,0) and CurCoord(-1,-1).
02103     Scope:      public
02104            
02105 **********************************************************************************************/
02106 
02107 BOOL NodePath::Snap(DocRect* pDocRect,const DocCoord& PrevCoord,const DocCoord& CurCoord)
02108 {
02109 #if !defined(EXCLUDE_FROM_RALPH)
02110     TRACEUSER( "MarkN", _T("NodePath::Snap(DocRect)\n") );
02111 #endif
02112     return FALSE;
02113 }
02114 
02115 
02116 
02117 
02118 /********************************************************************************************
02119 
02120 >   BOOL NodePath::SnapToCoords(DocCoord* pDocCoord)
02121 
02122     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02123     Created:    21/11/94
02124     Inputs:     pDocCoord - the point to magnetically snap
02125     Outputs:    pDocCoord - if the point was able to magnetically snap to a coord then
02126                 it is changed to that coord
02127     Returns:    TRUE if it snapped to a coord, FALSE if not
02128     Purpose:    Snaps the point to all the magnetic points in a NodePath. These include
02129                 all the endpoint in the path (ie MoveTos, LineTos and the last point in
02130                 CurveTos). If it is close enough to any of these points in the path
02131                 then the coord ischanged to match the point on the path and TRUE is returned.
02132                 pDocCoord is not effected if it is not close enough to any of the points
02133                 in the path
02134     SeeAlso:    MagneticPointRadius; NodeRenderableBounded::SnapToCoords
02135 
02136 ********************************************************************************************/
02137 
02138 BOOL NodePath::SnapToCoords(DocCoord* pDocCoord)
02139 {
02140 #if !defined(EXCLUDE_FROM_RALPH)
02141     DocCoord* Coords = InkPath.GetCoordArray();
02142     PathFlags* Flags = InkPath.GetFlagArray();
02143     INT32 NumCoords = InkPath.GetNumCoords();
02144 
02145     // loop through all the coords in the path
02146     for (INT32 i=0; i<NumCoords; i++)
02147     {
02148         // Only test the endpoints
02149         if (Flags[i].IsEndPoint)
02150         {
02151             // Now test the coord to see if it is close
02152             if (IsMagneticallyClose(&Coords[i], pDocCoord))
02153                 return TRUE;
02154         }
02155     }
02156 #endif
02157     // Did not find a match
02158     return FALSE;
02159 }
02160 
02161 
02162 
02163 /********************************************************************************************
02164 
02165 >   double NodePath::GetRotationAngle()
02166 
02167     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02168     Created:    24/1/95
02169     Inputs:     -
02170     Outputs:    -
02171     Returns:    The current angle of rotation of the path
02172     Purpose:    The overridden function for determining the current angle of rotation of this 
02173                 path
02174     SeeAlso:    NodeRenderableBounded::GetRotationAngle
02175 
02176 ********************************************************************************************/
02177 
02178 double NodePath::GetRotationAngle()
02179 {
02180     return CurrentRotation;
02181 }
02182 
02183 
02184 /********************************************************************************************
02185 
02186 >   virtual void NodePath::DeSelect(BOOL ReDraw, BOOL bDeselectChildren)
02187 
02188     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02189     Created:    24/1/95
02190     Inputs:     ReDraw
02191     Outputs:    -
02192     Returns:    -
02193     Purpose:    When you select a NodePath we can to clear all the selection flags in the 
02194                 path before deselecting the node.
02195     SeeAlso:    NodeRenderable::DeSelect
02196 
02197 ********************************************************************************************/
02198 
02199 void NodePath::DeSelect(BOOL ReDraw, BOOL bDeselectChildren)
02200 {
02201     if (ReDraw && GetApplication()->GetBlobManager()->GetCurrentInterest().Object)
02202         InkPath.RenderPathSelectedControlBlobs(FindParentSpread());
02203 
02204     InkPath.ClearSubSelection();
02205 
02206     // Now call the parent class
02207     NodeRenderableInk::DeSelect(ReDraw, bDeselectChildren);
02208 }
02209 
02210 
02211 
02212 /********************************************************************************************
02213 
02214 >   virtual BOOL NodePath::IsPathAllowable()
02215 
02216     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02217     Created:    30/4/95
02218     Inputs:     -
02219     Outputs:    -
02220     Returns:    TRUE if the path is valid
02221                 FALSE if is not valid
02222     Purpose:    This function following some operations on the path.  It allows the path (and
02223                 objects derived from NodePath) to say wether the operation has left them
02224                 in an invalid state.  For example, following a delete a closed path of one
02225                 segment is not allowed.
02226                 Further example, Mould paths can override this function to ensure there is
02227                 at least four points on the path after a delete 
02228     SeeAlso:    -
02229 
02230 ********************************************************************************************/
02231 BOOL NodePath::IsPathAllowable()
02232 {
02233     INT32 NumCoords = InkPath.GetNumCoords();
02234     PathVerb* Verbs = NULL;
02235     PathFlags* Flags = NULL;
02236     DocCoord* Coords = NULL;
02237     InkPath.GetPathArrays(&Verbs, &Coords, &Flags);
02238 
02239     // It's a moveto/lineto combination
02240     if ( (NumCoords == 2) && (Verbs[1] & PT_CLOSEFIGURE) && ((Verbs[1] & ~PT_CLOSEFIGURE) == PT_LINETO) )
02241     {
02242         ERROR3IF(Verbs[0] != PT_MOVETO,"Path didn't start with a moveto!");
02243         return FALSE;
02244     }
02245 
02246     // It's a moveto/curveto combination
02247     if ( (NumCoords == 4) && (Verbs[3] & PT_CLOSEFIGURE) && ((Verbs[3] & ~PT_CLOSEFIGURE) == PT_BEZIERTO) ) 
02248     {
02249         ERROR3IF(Verbs[0] != PT_MOVETO,"Path didn't start with a moveto!");
02250         ERROR3IF(Verbs[1] != PT_BEZIERTO,"Path didn't continue with a Bezierto!");
02251         return FALSE;
02252     }
02253 
02254     return TRUE;
02255 }
02256 
02257 /********************************************************************************************
02258 
02259 >   BOOL NodePath::NeedsToExport(RenderRegion* pRender, BOOL VisibleLayersOnly = FALSE,
02260                                  BOOL CheckSelected = FALSE)
02261 
02262     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02263     Created:    23/03/95
02264     Inputs:     pRender - A pointer to the current export region
02265                 VisibleLayersOnly - TRUE => remove nodes which are on invisible layers
02266                                    - FALSE => export everything
02267                 CheckSelected - TRUE => we check if object selected and only export selected bjects
02268                               - FALSE => we don't bother checking for selection or not
02269     Returns:    TRUE => please export me.
02270     Purpose:    Virtual function - this version currently returns false if the path is
02271                 an immediate child of a text story otherwise its always exportable
02272 
02273 ********************************************************************************************/
02274 
02275 BOOL NodePath::NeedsToExport(RenderRegion* pRender, BOOL VisibleLayersOnly, BOOL CheckSelected)
02276 {
02277 #ifdef DO_EXPORT
02278     if (pRender->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
02279     {
02280         Node* pParent=FindParent();
02281         if (pParent!=NULL && IS_A(pParent,TextStory))
02282             return FALSE;
02283     }
02284 
02285     // If we have the check selection flag on then see if this node is:-
02286     // - selected or not = render it
02287     // - a child of the selection e.g. part of selected group where we are an item in the
02288     //   group and hence are not directly selected but still need to be exported
02289     // - a parent of the selected item e.g. selected inside item of group and we are at the
02290     //   group and hence need to include the group in the range 
02291     // Otherwise just return True as this is a path node and always needs to be exported
02292     // unless of course some node overrides this.
02293     if (CheckSelected)
02294         return (IsSelected() || IsChildOfSelected() || IsParentOfSelected());
02295     else
02296         return TRUE;
02297 #else
02298     return FALSE;
02299 #endif
02300 }
02301 
02302 
02303 
02304 /********************************************************************************************
02305 >   virtual BOOL NodePath::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState = TRUE,
02306                                                            BOOL DoPreTriggerEdit = TRUE)
02307 
02308     Author:     Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>; Karim 19/01/2000
02309     Created:    8/5/95
02310     Inputs:     pParam - pointer to the change parameter object
02311                 SetOpPermissionState - TRUE to set the Nodes permission flags
02312                 DoPreTriggerEdit     - if TRUE then calls NameGallery::PreTriggerEdit.
02313                                        *Must* be TRUE if the calling Op may make any nodes
02314                                        change their bounds, eg move, line width, cut.
02315                                        Use TRUE if unsure.
02316     Returns:    TRUE if the operation can proceede, FALSE to stop it
02317     Purpose:    To get node paths inside text objects to reformat themselves correctly
02318                 when their paths change.
02319     Also see:   TextStory::AllowOp()
02320 ********************************************************************************************/
02321 
02322 BOOL NodePath::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState,
02323                                                BOOL DoPreTriggerEdit)
02324 {
02325     // call its default AllowOp(); we never want to do a PreTriggerEdit for this.
02326     BOOL allowed = NodeRenderableInk::AllowOp(pParam, SetOpPermissionState, FALSE);
02327 
02328 /*
02329  *  Karim 15/11/2000
02330  *  Commented this out, as I see no point in pretending to be an Op. *Any* parent of ours
02331  *  will have its AllowOp() called anyway, via our base class.
02332  *  
02333     // if allowed, and our parent is a textstory, call the TextStory's AllowOp() as though called directly by the op
02334     Node* pParent=FindParent();
02335     if (allowed && pParent!=NULL && IS_A(pParent,TextStory))
02336         allowed=pParent->AllowOp(pParam,SetOpPermissionState,DoPreTriggerEdit);
02337  */
02338 
02339     // if we're ok so far and were asked to do a PreTriggerEdit, then
02340     // determine whether the Op may change the bounds of some nodes.
02341     // If it may, then call NameGallery::PreTriggerEdit.
02342     if (allowed && DoPreTriggerEdit)
02343     {
02344         // if the Op is non-NULL then query its MayChangeNodeBounds() method.
02345         UndoableOperation* pChangeOp = pParam->GetOpPointer();
02346         if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds() && NameGallery::Instance())
02347         {
02348             allowed = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this);
02349         }
02350     }
02351     if (SetOpPermissionState)
02352         SetOpPermission(PERMISSION_ALLOWED);
02353 
02354     return allowed;
02355 }
02356 
02357 
02358 /********************************************************************************************
02359 
02360 >   virtual UINT32 NodePath::ChooseTagValue()
02361 
02362     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02363     Created:    22/8/96
02364     Inputs:     -
02365     Returns:    The tag value that should be used for saving this path
02366     Purpose:    Writes the path record to the filter
02367     SeeAlso:    This function chooses the tag to use based on the filled & stroked flags of the
02368                 path
02369 
02370 ********************************************************************************************/
02371 
02372 UINT32 NodePath::ChooseTagValue()
02373 {
02374 #ifdef DO_EXPORT
02375     UINT32 Tag = TAG_UNDEFINED;
02376 
02377     if (BaseCamelotFilter::WritePathsInRelativeFormat())
02378     {
02379         Tag = TAG_PATH_RELATIVE;
02380         if (InkPath.IsFilled && !InkPath.IsStroked) 
02381             Tag = TAG_PATH_RELATIVE_FILLED;
02382         else if (!InkPath.IsFilled && InkPath.IsStroked) 
02383             Tag = TAG_PATH_RELATIVE_STROKED;
02384         else if (InkPath.IsFilled && InkPath.IsStroked) 
02385             Tag = TAG_PATH_RELATIVE_FILLED_STROKED;
02386     }
02387     else
02388     {
02389         Tag = TAG_PATH;
02390         if (InkPath.IsFilled && !InkPath.IsStroked) 
02391             Tag = TAG_PATH_FILLED;
02392         else if (!InkPath.IsFilled && InkPath.IsStroked) 
02393             Tag = TAG_PATH_STROKED;
02394         else if (InkPath.IsFilled && InkPath.IsStroked) 
02395             Tag = TAG_PATH_FILLED_STROKED;
02396     }
02397 
02398     return Tag;
02399 #else
02400     return TAG_UNDEFINED;
02401 #endif
02402 }
02403 
02404 
02405 
02406 /********************************************************************************************
02407 
02408 >   virtual INT32 NodePath::EstimateNodeComplexity (OpParam* details)
02409 
02410     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02411     Created:    6/09/2000
02412 
02413     Inputs:     details     any data that should be used for the calculation
02414 
02415     Outputs:    -
02416 
02417     Returns:    an estimate of the nodes complexity
02418 
02419     Purpose:    This function estimates a complexity value for the node.  The complexity
02420                 value is based upon the total length of all paths in the node.
02421 
02422     See Also:   OpBlendNodes::DeterminBlendObjectsProcessorHit ()
02423 
02424 ********************************************************************************************/
02425 
02426 INT32 NodePath::EstimateNodeComplexity (OpParam* details)
02427 {
02428     return (InkPath.GetUsedSlots ());
02429 }
02430 
02431 /********************************************************************************************
02432 
02433 >   virtual INT32 NodePath::CalcPathRecordSize()
02434 
02435     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02436     Created:    6/9/96
02437     Inputs:     -
02438     Returns:    The size of the record in bytes
02439     Purpose:    This func is the centre point at which the size of the record that will contain
02440                 the path is calculated
02441     SeeAlso:    BaseCamelotFilter::WritePathsInRelativeFormat()
02442 
02443 ********************************************************************************************/
02444 
02445 INT32 NodePath::CalcPathRecordSize()
02446 {
02447     INT32 NumCoords = InkPath.GetNumCoords();
02448 
02449     if (BaseCamelotFilter::WritePathsInRelativeFormat())
02450         return (sizeof(PathVerb)*NumCoords)+(sizeof(DocCoord)*NumCoords);
02451     else
02452         return sizeof(INT32)+(sizeof(PathVerb)*NumCoords)+(sizeof(DocCoord)*NumCoords);
02453 }
02454 
02455 /********************************************************************************************
02456 
02457 >   virtual BOOL NodePath::WritePathToRecord(CamelotFileRecord* pRecord)
02458 
02459     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02460     Created:    6/9/96
02461     Inputs:     pRecord = ptr to a record to write to
02462     Returns:    TRUE if ok, FALSE otherwise
02463     Purpose:    Central func writes out the correct path record.
02464     SeeAlso:    BaseCamelotFilter::WritePathsInRelativeFormat()
02465 
02466 ********************************************************************************************/
02467 
02468 BOOL NodePath::WritePathToRecord(CamelotFileRecord* pRecord)
02469 {
02470     ERROR2IF(pRecord == NULL,FALSE,"NULL record ptr");
02471 
02472     if (BaseCamelotFilter::WritePathsInRelativeFormat())
02473         return pRecord->WritePathRelative(&InkPath);
02474     else
02475         return pRecord->WritePath(&InkPath);
02476 }
02477 
02478 /********************************************************************************************
02479 
02480 >   virtual BOOL NodePath::WritePathRecord(BaseCamelotFilter* pFilter)
02481 
02482     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02483     Created:    22/5/96
02484     Inputs:     pFilter = ptr to the filter
02485     Returns:    TRUE if record is written, FALSE if not
02486     Purpose:    Writes the path record to the filter
02487     SeeAlso:    -
02488 
02489 ********************************************************************************************/
02490 
02491 BOOL NodePath::WritePathRecord(BaseCamelotFilter* pFilter)
02492 {
02493 #ifdef DO_EXPORT
02494     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
02495 
02496     INT32 RecordSize = CalcPathRecordSize();
02497 
02498     BOOL ok = TRUE;
02499 
02500     // Which type of path is it to be?
02501     UINT32 Tag = ChooseTagValue();
02502 
02503     CamelotFileRecord Rec(pFilter,Tag,RecordSize);
02504 
02505     ok = Rec.Init();
02506     if (ok) ok = WritePathToRecord(&Rec);
02507 
02508     UINT32 RecordNumber;
02509     if (ok) RecordNumber = pFilter->WriteDefinitionRecord(&Rec);
02510     if (ok) ok = (RecordNumber != 0);
02511     if (ok) pFilter->AddPathRecordRefToList(this,RecordNumber);
02512 
02513     if (!ok)
02514         pFilter->GotError(_R(IDE_FILE_WRITE_ERROR));
02515 
02516     return ok;
02517 #else
02518     return FALSE;
02519 #endif
02520 }
02521 
02522 /********************************************************************************************
02523 
02524 >   virtual BOOL NodePath::WritePathRefRecord(BaseCamelotFilter* pFilter,UINT32 SrcPathRecNum,Matrix* pTransform);
02525 
02526     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02527     Created:    3/9/96
02528     Inputs:     pFilter = ptr to the filter
02529                 SrcPathRecNum = the record that contains the path that's similar to this path
02530                 pTransform    = How to transform the path in SrcPathRecNum to get this path bac
02531     Returns:    TRUE if record is written, FALSE if not
02532     Purpose:    Writes the path reference record to the filter
02533     SeeAlso:    -
02534 
02535 ********************************************************************************************/
02536 
02537 BOOL NodePath::WritePathRefRecord(BaseCamelotFilter* pFilter,UINT32 SrcPathRecNum,Matrix* pTransform)
02538 {
02539     ERROR2IF(pTransform == NULL,FALSE,"NULL transform matrix ptr");
02540 
02541     BOOL ok = TRUE;
02542 
02543     UINT32 Tag  = TAG_PATHREF_TRANSFORM;
02544     INT32  Size = TAG_PATHREF_TRANSFORM_SIZE;
02545 
02546     CamelotFileRecord Rec(pFilter,Tag,Size);
02547     if (ok) ok = Rec.Init();
02548     if (ok) ok = Rec.WriteReference(SrcPathRecNum);
02549     if (ok) ok = Rec.WriteMatrixTrans(*pTransform,0,0);
02550     if (ok) ok = (pFilter->Write(&Rec) != 0);
02551 
02552     return ok;
02553 }
02554 
02555 
02556 /********************************************************************************************
02557 
02558   > virtual BOOL NodePath::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
02559 
02560     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02561     Created:    22/5/96
02562     Inputs:     pFilter = ptr to the filter
02563     Returns:    TRUE if record is written, FALSE if not
02564     Purpose:    Writes the path record to the filter
02565     SeeAlso:    -
02566 
02567 ********************************************************************************************/
02568 
02569 BOOL NodePath::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
02570 {
02571 #ifdef DO_EXPORT
02572 
02573     BOOL ok = TRUE;
02574     UINT32 SrcPathRecNum;
02575     Matrix Transform;
02576 
02577     INT32 RecordSize = CalcPathRecordSize();
02578 
02579     // Only call FindSimilarPath() if the path is bigger than the path reference record.
02580     // there's no point in creating a reference if the ref is bigger than the actual path
02581 
02582     if (RecordSize > TAG_PATHREF_TRANSFORM_SIZE && pFilter->FindSimilarPath(this,&SrcPathRecNum,&Transform))
02583         ok = WritePathRefRecord(pFilter,SrcPathRecNum,&Transform);
02584     else
02585         ok = WritePathRecord(pFilter);
02586 
02587     return ok;
02588 
02589 #else
02590     return FALSE;
02591 #endif
02592 }
02593 
02594 /********************************************************************************************
02595 
02596   > virtual BOOL NodePath::WritePreChildrenNative(BaseCamelotFilter* pFilter)
02597 
02598     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02599     Created:    22/5/96
02600     Inputs:     pFilter = ptr to the filter
02601     Returns:    TRUE if record is written, FALSE if not
02602     Purpose:    Writes the path record to the filter
02603     SeeAlso:    -
02604 
02605 ********************************************************************************************/
02606 
02607 BOOL NodePath::WritePreChildrenNative(BaseCamelotFilter* pFilter)
02608 {
02609 #ifdef DO_EXPORT
02610 
02611     // If it's the compact native filter (i.e. the non-minimal web format) OR the path
02612     // similarity check has been selected to apply to the native format,
02613     // do as you would for the web format.
02614     if (pFilter->IsCompactNativeFilter() || pFilter->GetNativeCheckSimilarPaths())
02615         return WritePreChildrenWeb(pFilter);
02616     else
02617         return WritePathRecord(pFilter);
02618 #else
02619     return FALSE;
02620 #endif
02621 }
02622 
02623 
02624 /********************************************************************************************
02625 
02626 >   BOOL NodePath::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter)
02627 
02628     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02629     Created:    8/8/96
02630     Inputs:     pFilter
02631     Returns:    TRUE if ok, FALSE otherwise
02632     Purpose:    Begin to write out you child records, in the native format
02633 
02634                 This will write out a TAG_DOWN record, followed by a path flags record
02635     SeeAlso:    -
02636 
02637 ********************************************************************************************/
02638 
02639 BOOL NodePath::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter)
02640 {
02641 #ifdef DO_EXPORT
02642     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
02643 
02644     BOOL ok = pFilter->WriteZeroSizedRecord(TAG_DOWN);
02645 
02646     if (ok)
02647     {
02648         INT32 NumCoords = InkPath.GetNumCoords();
02649         INT32 RecordSize = sizeof(BYTE)*NumCoords;
02650 
02651         CamelotFileRecord Rec(pFilter,TAG_PATH_FLAGS,RecordSize);
02652         ok = Rec.Init();
02653 
02654         PathFlags* pFlags = InkPath.GetFlagArray();
02655 
02656         if (ok && pFlags != NULL)
02657         {
02658             BYTE Flags;
02659             for (INT32 i=0; ok && i < NumCoords;i++)
02660             {
02661                 Flags = 0;
02662 
02663                 if (pFlags[i].IsSmooth)     Flags |= TAG_PATH_FLAGS_SMOOTH;
02664                 if (pFlags[i].IsRotate)     Flags |= TAG_PATH_FLAGS_ROTATE;
02665                 if (pFlags[i].IsEndPoint)   Flags |= TAG_PATH_FLAGS_ENDPOINT;
02666 
02667                 ok = Rec.WriteBYTE(Flags);
02668             }
02669 
02670             ok = pFilter->Write(&Rec);
02671         }
02672 
02673         if (ok)
02674         {
02675             if (pFilter->GetBoundsWriteLevel() >= BWL_ALL)
02676                 ok = WriteBoundsRecord(pFilter);
02677         }
02678     }
02679 
02680     return ok;
02681 #else
02682     return FALSE;
02683 #endif
02684 }
02685 
02686 /********************************************************************************************
02687 
02688 >   BOOL NodePath::WriteEndChildRecordsNative(BaseCamelotFilter* pFilter)
02689 
02690     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02691     Created:    8/8/96
02692     Inputs:     pFilter
02693     Returns:    TRUE if ok, FALSE otherwise
02694     Purpose:    End writing out you child records, in the native format
02695 
02696                 This will write out a TAG_UP record
02697     SeeAlso:    -
02698 
02699 ********************************************************************************************/
02700 
02701 BOOL NodePath::WriteEndChildRecordsNative(BaseCamelotFilter* pFilter)
02702 {
02703 #ifdef DO_EXPORT
02704     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
02705 
02706     return pFilter->WriteZeroSizedRecord(TAG_UP);
02707 #else
02708     return FALSE;
02709 #endif
02710 }
02711 
02712 
02713 
02714 /********************************************************************************************
02715 
02716 >   virtual BOOL NodePath::IsDifferent(Node *pOther)
02717 
02718     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02719     Created:    28/2/97
02720 
02721     Inputs:     pOther - The node to compare this one to
02722 
02723     Returns:    TRUE if this is considered different from pOther,
02724                 FALSE if they are the same
02725 
02726     Purpose:    Determine if 2 nodes are considered different.
02727 
02728     Notes:      **** IMPORTANT - May not function well enough for your needs! ****
02729 
02730                 This was written to allow the StrokeComponent class to merge stroke
02731                 definitions which share identical clipart subtrees. Stroke subtrees
02732                 are special in that they have had Make Shapes applied to them, and
02733                 so only contain paths. Hence, I have only defined functions in
02734                 Node, NodeRenderableBounded, and NodePath - most objects in the tree
02735                 thus use base class functionality (are they of the same class, and
02736                 do they have identical bounding boxes). This suffices for my needs,
02737                 but you may need to implement this function for a lot more node
02738                 types before it's of use to you.
02739 
02740                 NodePath checks that the 2 InkPaths contain the same set of Coords
02741                 and Verbs - currently IGNOREs everything else
02742 
02743 ********************************************************************************************/
02744 
02745 BOOL NodePath::IsDifferent(Node *pOther)
02746 {
02747     // First, check with the base class - this checks the classes are the same type
02748     if (NodeRenderableInk::IsDifferent(pOther))
02749         return(TRUE);
02750 
02751     ERROR3IF(GetRuntimeClass() != pOther->GetRuntimeClass(),
02752             "Node::IsDifferent base class method seems to have been broken");
02753 
02754     // Now, check the path info
02755     if (InkPath.GetNumCoords() != ((NodePath *)pOther)->InkPath.GetNumCoords())
02756         return(TRUE);
02757 
02758     // Check if all the coords and verbs are the same
02759     DocCoord *pCoord1 = InkPath.GetCoordArray();
02760     DocCoord *pCoord2 = ((NodePath *)pOther)->InkPath.GetCoordArray();
02761 
02762     PathVerb *pVerb1  = InkPath.GetVerbArray();
02763     PathVerb *pVerb2  = ((NodePath *)pOther)->InkPath.GetVerbArray();
02764 
02765     for (INT32 i = 0; i < InkPath.GetNumCoords(); i++)
02766     {
02767         if (pVerb1[i] != pVerb2[i] || pCoord1[i] != pCoord2[i])
02768             return(TRUE);
02769     }
02770 
02771     return(FALSE);
02772 }
02773 
02774 
02775 
02776 /********************************************************************************************
02777 
02778 >   virtual BOOL NodePath::IsANodeBlendPath()
02779 
02780     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02781     Created:    14/11/99
02782     Inputs:     -
02783     Returns:    FALSE;
02784     Purpose:    virtual identifier function,will move to node.h/.cpp when i have time to wait 
02785                 for the rebuild
02786 
02787 
02788 ********************************************************************************************/
02789 
02790 BOOL NodePath::IsANodeBlendPath()
02791 {
02792     return FALSE;
02793 }
02794 
02795 
02796 
02797 /********************************************************************************************
02798 
02799 >   virtual DocRect NodePath::ValidateExtend(const ExtendParams& ExtParams) const
02800 
02801     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02802     Created:    25/11/1999
02803     Inputs:     ExtParams       parameters describing the extension.
02804     Outputs:    
02805     Returns:    TRUE if this path can validly extend,
02806                 FALSE otherwise.
02807     Purpose:    Tests to see whether this path's control points are positioned so as to
02808                 make an extend operation irreversible.
02809     Errors:     
02810     See also:   
02811 
02812 ********************************************************************************************/
02813 DocRect NodePath::ValidateExtend(const ExtendParams& ExtParams)
02814 {
02815     INT32 numPoints = InkPath.GetNumCoords();
02816     DocCoord* doccArray = InkPath.GetCoordArray();
02817     DocRect drMinExtend = Extender::ValidateControlPoints(numPoints, doccArray, ExtParams);
02818 
02819     // if we didn't invalidate the extension, we must call the base class
02820     // implementation, which will validate our children.
02821     if (drMinExtend.lo.x == INT32_MAX &&
02822         drMinExtend.lo.y == INT32_MAX &&
02823         drMinExtend.hi.x == INT32_MAX &&
02824         drMinExtend.hi.y == INT32_MAX)
02825         drMinExtend = Node::ValidateExtend(ExtParams);
02826 
02827     return drMinExtend;
02828 }
02829 
02830 
02831 
02832 /********************************************************************************************
02833 
02834 >   virtual void NodePath::Extend(const ExtendParams& ExtParams) const
02835     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02836     Created:    25/11/1999
02837     Inputs:     ExtParams       parameters describing the extension.
02838     Outputs:    this NodePath will be extended in accordance with ExtParams.
02839     Returns:    
02840     Purpose:    Perform an Extend operation on this path. Behaviour is as follows:
02841 
02842                 * the path extends separately along each axis.
02843                 * if the path is asked to stretch, it scales along the corresponding axes.
02844                 * if the path is asked to extend, its control points translate, as described
02845                                                                                 by ExtParams.
02846     Errors:     
02847     See also:   class Extender
02848 
02849 ********************************************************************************************/
02850 void NodePath::Extend(const ExtendParams& ExtParams)
02851 {
02852     TransformTranslateObject(ExtParams);
02853     TransformStretchObject(ExtParams);
02854 
02855     // do the base-class implementation to extend our children.
02856     Node::Extend(ExtParams);
02857 }
02858 
02859 /********************************************************************************************
02860 
02861 >   virtual void NodePath::TransformTranslateObject(const ExtendParams& ExtParams)
02862 
02863     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02864     Created:    01/12/1999
02865     Inputs:     ExtParams       description of how this path should extend.
02866     Outputs:    This path's control points may be extended, depending on ExtParams' flags.
02867     Purpose:    Do an extend (as opposed to stretch) operation on this path, using ExtParams
02868                 as the source of the extend data, together with the extend-centre of this
02869                 path, defined in NodeRenderable::FindExtendCentre().
02870 
02871                 This function does nothing unless ExtParams uses X_EXTEND or Y_EXTEND.
02872     See also:   The Extender class; FindExtendCentre().
02873 
02874 ********************************************************************************************/
02875 void NodePath::TransformTranslateObject(const ExtendParams& ExtParams)
02876 {
02877     // get the path's control points.
02878     INT32 numPoints = InkPath.GetNumCoords();
02879     DocCoord* doccArray = InkPath.GetCoordArray();
02880 
02881     // x-extension behaviour.
02882     if (ExtParams.fExtendFlags & X_EXTEND)
02883     {
02884         // translate the whole path by the offset from start- to end- centres.
02885         Trans2DMatrix baseXoffset(ExtParams.doccOffset.x, 0);
02886         Transform(baseXoffset);
02887 
02888         for (INT32 i = 0; i < numPoints; i ++)
02889         {
02890             if (doccArray[i].x > (ExtParams.doccEndCentre.x + ExtParams.xincExtendBuffer))
02891                 doccArray[i].x += ExtParams.xinc;
02892             else if (doccArray[i].x < (ExtParams.doccEndCentre.x - ExtParams.xdecExtendBuffer))
02893                 doccArray[i].x -= ExtParams.xdec;
02894         }
02895     }
02896 
02897     // y-extension behaviour.
02898     if (ExtParams.fExtendFlags & Y_EXTEND)
02899     {
02900         // translate the whole path by the offset from start- to end- centres.
02901         Trans2DMatrix baseYoffset(0, ExtParams.doccOffset.y);
02902         Transform(baseYoffset);
02903 
02904         for (INT32 i = 0; i < numPoints; i ++)
02905         {
02906             if (doccArray[i].y > (ExtParams.doccEndCentre.y + ExtParams.yincExtendBuffer))
02907                 doccArray[i].y += ExtParams.yinc;
02908             else if (doccArray[i].y < (ExtParams.doccEndCentre.y - ExtParams.ydecExtendBuffer))
02909                 doccArray[i].y -= ExtParams.ydec;
02910         }
02911     }
02912 }
02913 
02914 
02915 
02916 /********************************************************************************************
02917 
02918 >   virtual DocRect NodePath::GetExtendTargetBounds(const ExtendParams& ExtParams)
02919 
02920     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02921     Created:    02/06/2000
02922     Inputs:     ExtParams       const ref to an ExtendParams class describing the extension.
02923     Outputs:    
02924     Returns:    The DocRect to treat as the bounding rect of this node when extending.
02925     Purpose:    Return a DocRect which contains the bounds of this node as defined and
02926                 required by the Extending mechanism.
02927 
02928                 We treat x- and y- directions separately, and we always return our true
02929                 bounds, unless we're extending in a direction, in which case we return the
02930                 bounds of our extend control points, which are currently all of our inkpath's
02931                 control points.
02932     Errors:     
02933     See also:   SliceHelper::BoundingNodeSize().
02934 
02935 ********************************************************************************************/
02936 DocRect NodePath::GetExtendTargetBounds(const ExtendParams& ExtParams)
02937 {
02938     // think twice about using InkPath::GetTrueBoundingRect() here - using
02939     // NodePath::GetBoundingRect takes factors such as attributes into account.
02940     DocRect drBounds        = GetBoundingRect();
02941     DocRect drPointBounds   = InkPath.GetBoundingRect();
02942     if (ExtParams.fExtendFlags & X_EXTEND)
02943     {
02944         drBounds.lo.x = drPointBounds.lo.x;
02945         drBounds.hi.x = drPointBounds.hi.x;
02946     }
02947     if (ExtParams.fExtendFlags & Y_EXTEND)
02948     {
02949         drBounds.lo.y = drPointBounds.lo.y;
02950         drBounds.hi.y = drPointBounds.hi.y;
02951     }
02952 
02953     return drBounds;
02954 }
02955 
02956 
02957 
02958 /********************************************************************************************
02959 
02960 >   static BOOL NodePath::CreateFromPath(NodePath** ppNewPath,  Path* pSrcPath = NULL,
02961                                                                 Operation* pOp = NULL);
02962     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02963     Created:    30/10/2000
02964     Inputs:     ppNewPath   ptr to the ptr holding any new NodePath.
02965 
02966                 pSrcPath    the path which the new NodePath will copy its data from.
02967                             if NULL, then you just get back a fresh NodePath, ready for use.
02968 
02969                 pOp         an Op ptr - if we run out of memory, then we can reclaim memory
02970                             via this ptr; ok if this is NULL.
02971 
02972     Outputs:    *pNewPath holds a new NodePath, if successful, NULL otherwise.
02973 
02974     Returns:    TRUE if success, FALSE otherwise.
02975 
02976     Purpose:    Create and initialise a new NodePath, using the given path information if
02977                 provided.
02978 
02979     Errors:     ERROR2 with FALSE if ppNewPath is NULL.
02980 
02981 ********************************************************************************************/
02982 BOOL NodePath::CreateFromPath(NodePath** ppNewPath, Path* pSrcPath, Operation* pOp)
02983 {
02984     ERROR2IF(ppNewPath == NULL, FALSE, "NodePath::CreateFromPath; called with NULL param!");
02985 
02986     NodePath* pNewPath = NULL;
02987     ALLOC_WITH_FAIL(pNewPath, new NodePath, pOp);
02988     if (pNewPath != NULL)
02989     {
02990         if (pSrcPath != NULL)
02991         {
02992             BOOL    ok = pNewPath->InkPath.Initialise(pSrcPath->GetNumCoords(), 1);
02993             if (ok)
02994                 CALL_WITH_FAIL(pNewPath->InkPath.CopyPathDataFrom(pSrcPath), pOp, ok);
02995 
02996             if (!ok)
02997             {
02998                 delete pNewPath;
02999                 pNewPath = NULL;
03000             }
03001         }
03002         else
03003         {
03004             pNewPath->SetUpPath();
03005         }
03006     }
03007 
03008     *ppNewPath = pNewPath;
03009     return (pNewPath != NULL);
03010 }
03011 
03012 
03013 /********************************************************************************************
03014 
03015 >   Path* NodePath::GetPathCopy()
03016 
03017     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03018     Created:    18/11/2000
03019 
03020     Returns:    ptr to a copy of our path if successful,
03021                 NULL otherwise.
03022 
03023     Purpose:    A quick'n'easy way to get a permanent copy of our path.
03024 
03025     Notes:      The copy is allocated on the heap, so it's *your* responsibility
03026                 to dispose of it.
03027 
03028 ********************************************************************************************/
03029 Path* NodePath::GetPathCopy()
03030 {
03031     Path* pResult = new Path;
03032     if (pResult != NULL)
03033     {
03034         if (!pResult->Initialise(InkPath.GetNumCoords()) ||
03035             !pResult->CloneFrom(InkPath))
03036         {
03037             delete pResult;
03038             pResult = NULL;
03039         }
03040     }
03041 
03042     return pResult;
03043 }
03044 
03045 
03046 
03047 /********************************************************************************************
03048 
03049 >   virtual BOOL NodePath::GetAreaDetails(XLONG* pxlArea, XLONG* pXLPerimeter)
03050 
03051 
03052     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03053     Created:    12/03/2005
03054     Outputs:    xlArea
03055                 xlPerimeter
03056     Returns:    TRUE if calcs succeeded
03057     Purpose:    Work out 2-dimensional properties of this node
03058 
03059 ********************************************************************************************/
03060 BOOL NodePath::GetAreaDetails(XLONG* pxlArea, XLONG* pxlPerimeter)
03061 {
03062     BOOL bOK = TRUE;
03063 
03064     *pxlArea = InkPath.CalcArea();
03065     double dDistance = 0;
03066     bOK = InkPath.DistanceTo(InkPath.GetNumCoords()-1, 1, &dDistance);
03067     *pxlPerimeter = (INT32)dDistance;
03068 
03069     return TRUE;
03070 }
03071 
03072 
03073 
03074 //-------------------------------------------------------------------------------------
03075 //-------------------------------------------------------------------------------------
03076 //-------------------------------------------------------------------------------------
03077 
03078 
03079 
03080 /********************************************************************************************
03081 
03082 >   PathRecordHandler::PathRecordHandler()
03083 
03084     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03085     Created:    8/8/96
03086     Inputs:     -
03087     Returns:    -
03088     Purpose:    Default constructor
03089     SeeAlso:    -
03090 
03091 ********************************************************************************************/
03092 
03093 //PathRecordHandler::PathRecordHandler()
03094 //{
03095 //  pLastInsertedPath = NULL;
03096 //}
03097 
03098 /********************************************************************************************
03099 
03100 >   BOOL PathRecordHandler::BeginImport()
03101 
03102     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03103     Created:    8/8/96
03104     Inputs:     -
03105     Returns:    TRUE if ok, FALSE otherwise
03106     Purpose:    Informs the handler that the filter's about to start importing
03107     SeeAlso:    -
03108 
03109 ********************************************************************************************/
03110 
03111 //BOOL PathRecordHandler::BeginImport()
03112 //{
03113 //  pLastInsertedPath = NULL;
03114 //  return TRUE;
03115 //}
03116 
03117 /********************************************************************************************
03118 
03119 >   virtual UINT32* PathRecordHandler::GetTagList()
03120 
03121     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03122     Created:    30/5/96
03123     Inputs:     -
03124     Returns:    Ptr to a list of tag values, terminated by CXFRH_TAG_LIST_END
03125     Purpose:    Provides the record handler system with a list of records handled by this
03126                 handler
03127     SeeAlso:    -
03128 
03129 ********************************************************************************************/
03130 
03131 UINT32* PathRecordHandler::GetTagList()
03132 {
03133     static UINT32 TagList[] = { TAG_PATH,
03134                                 TAG_PATH_FILLED,
03135                                 TAG_PATH_STROKED,
03136                                 TAG_PATH_FILLED_STROKED,
03137                                 TAG_PATH_RELATIVE,
03138                                 TAG_PATH_RELATIVE_FILLED,
03139                                 TAG_PATH_RELATIVE_STROKED,
03140                                 TAG_PATH_RELATIVE_FILLED_STROKED,
03141 //                              TAG_PATH_FLAGS,
03142                                 TAG_PATHREF_IDENTICAL,
03143                                 TAG_PATHREF_TRANSFORM,
03144                                 CXFRH_TAG_LIST_END};
03145 
03146     return (UINT32*)&TagList;
03147 }
03148 
03149 /********************************************************************************************
03150 
03151 >   virtual BOOL PathRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
03152 
03153     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03154     Created:    30/5/96
03155     Inputs:     pCXaraFileRecord = ptr to record to handle
03156     Returns:    TRUE if handled successfuly
03157                 FALSE otherwise
03158     Purpose:    Handles the given record.
03159     SeeAlso:    -
03160 
03161 ********************************************************************************************/
03162 
03163 BOOL PathRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
03164 {
03165     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
03166     
03167     BOOL ok = TRUE;
03168 
03169     switch (pCXaraFileRecord->GetTag())
03170     {
03171         case TAG_PATH:                          ok = HandlePathRecord(pCXaraFileRecord,FALSE,FALSE);break;
03172         case TAG_PATH_FILLED:                   ok = HandlePathRecord(pCXaraFileRecord,TRUE,FALSE); break;
03173         case TAG_PATH_STROKED:                  ok = HandlePathRecord(pCXaraFileRecord,FALSE,TRUE); break;
03174         case TAG_PATH_FILLED_STROKED:           ok = HandlePathRecord(pCXaraFileRecord,TRUE,TRUE);  break;
03175 
03176         case TAG_PATH_RELATIVE:                 ok = HandlePathRelativeRecord(pCXaraFileRecord,FALSE,FALSE);break;
03177         case TAG_PATH_RELATIVE_FILLED:          ok = HandlePathRelativeRecord(pCXaraFileRecord,TRUE,FALSE); break;
03178         case TAG_PATH_RELATIVE_STROKED:         ok = HandlePathRelativeRecord(pCXaraFileRecord,FALSE,TRUE); break;
03179         case TAG_PATH_RELATIVE_FILLED_STROKED:  ok = HandlePathRelativeRecord(pCXaraFileRecord,TRUE,TRUE);  break;
03180 
03181 //      case TAG_PATH_FLAGS:                    ok = HandlePathFlagsRecord(pCXaraFileRecord,GetLastInsertedPath()); break;
03182 
03183         case TAG_PATHREF_IDENTICAL:
03184         case TAG_PATHREF_TRANSFORM:             ok = HandlePathRefRecord(pCXaraFileRecord);     break;
03185 
03186         default:
03187             ok = FALSE;
03188             ERROR3_PF(("I don't handle records with the tag (%d)\n",pCXaraFileRecord->GetTag()));
03189             break;
03190     }
03191 
03192     return ok;
03193 }
03194 
03195 
03196 /********************************************************************************************
03197 
03198 >   BOOL PathRecordHandler::HandlePathRecord(CXaraFileRecord* pCXaraFileRecord,BOOL Filled,BOOL Stroked)
03199 
03200     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03201     Created:    30/5/96
03202     Inputs:     pCXaraFileRecord = ptr to record to handle
03203                 Filled = TRUE if the path should be filled
03204                 Stroked= TRUE if the path should be stroked
03205     Returns:    TRUE if handled successfuly
03206                 FALSE otherwise
03207     Purpose:    Handles the given record.
03208                 The record has to be a path record
03209     SeeAlso:    -
03210 
03211 ********************************************************************************************/
03212 
03213 BOOL PathRecordHandler::HandlePathRecord(CXaraFileRecord* pCXaraFileRecord,BOOL Filled,BOOL Stroked)
03214 {
03215     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
03216     ERROR3IF(!IsTagInList(pCXaraFileRecord->GetTag()),"I don't handle this tag type");
03217 
03218     BOOL ok = FALSE;
03219 
03220     NodePath* pNodePath = new NodePath;
03221 
03222     if (pNodePath != NULL && pNodePath->SetUpPath())
03223     {
03224         ok = pCXaraFileRecord->ReadPath(&pNodePath->InkPath); 
03225 
03226         if (ok)
03227         {
03228             pNodePath->InkPath.IsFilled = Filled;   // Set the filled flag
03229             pNodePath->InkPath.IsStroked= Stroked;  // Set the stroked flag
03230             pNodePath->InkPath.InitialiseFlags();   // Init the path flags array to something sensible
03231         }
03232     }
03233             
03234     if (ok) ok = InsertNode(pNodePath);
03235     if (ok) SetLastNodePathInserted(pNodePath);
03236 
03237     if (ok) AddPathRecordRefToList(pNodePath,pCXaraFileRecord->GetRecordNumber());
03238 
03239     return ok;
03240 }
03241 
03242 /********************************************************************************************
03243 
03244 >   BOOL PathRecordHandler::HandlePathRelativeRecord(CXaraFileRecord* pCXaraFileRecord,BOOL Filled,BOOL Stroked)
03245 
03246     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03247     Created:    22/8/96
03248     Inputs:     pCXaraFileRecord = ptr to record to handle
03249                 Filled = TRUE if the path should be filled
03250                 Stroked= TRUE if the path should be stroked
03251     Returns:    TRUE if handled successfuly
03252                 FALSE otherwise
03253     Purpose:    Handles the given record.
03254                 The record has to be a path relative record
03255     SeeAlso:    -
03256 
03257 ********************************************************************************************/
03258 
03259 BOOL PathRecordHandler::HandlePathRelativeRecord(CXaraFileRecord* pCXaraFileRecord,BOOL Filled,BOOL Stroked)
03260 {
03261     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
03262     ERROR3IF(!IsTagInList(pCXaraFileRecord->GetTag()),"I don't handle this tag type");
03263 
03264     BOOL ok = FALSE;
03265 
03266     NodePath* pNodePath = new NodePath;
03267 
03268     if (pNodePath != NULL && pNodePath->SetUpPath())
03269     {
03270         ok = pCXaraFileRecord->ReadPathRelative(&pNodePath->InkPath); 
03271 
03272         if (ok)
03273         {
03274             pNodePath->InkPath.IsFilled = Filled;   // Set the filled flag
03275             pNodePath->InkPath.IsStroked= Stroked;  // Set the stroked flag
03276             pNodePath->InkPath.InitialiseFlags();   // Init the path flags array to something sensible
03277         }
03278     }
03279             
03280     if (ok) ok = InsertNode(pNodePath);
03281     if (ok) SetLastNodePathInserted(pNodePath);
03282 
03283     if (ok) AddPathRecordRefToList(pNodePath,pCXaraFileRecord->GetRecordNumber());
03284 
03285     return ok;
03286 }
03287 
03288 /********************************************************************************************
03289 
03290 >   BOOL PathRecordHandler::HandlePathRefRecord(CXaraFileRecord* pCXaraFileRecord)
03291 
03292     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03293     Created:    3/9/96
03294     Inputs:     pCXaraFileRecord = ptr to record to handle
03295     Returns:    TRUE if handled successfuly
03296                 FALSE otherwise
03297     Purpose:    Handles the given record.
03298                 The record has to be a path reference record
03299     SeeAlso:    -
03300 
03301 ********************************************************************************************/
03302 
03303 BOOL PathRecordHandler::HandlePathRefRecord(CXaraFileRecord* pCXaraFileRecord)
03304 {
03305     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
03306 
03307     ERROR3IF(pCXaraFileRecord->GetTag() != TAG_PATHREF_TRANSFORM,"I don't handle this tag type");
03308 
03309     BOOL ok = TRUE;
03310 
03311     UINT32  SrcRecNum;
03312     Matrix  Transform;
03313 
03314     if (ok) ok = pCXaraFileRecord->ReadUINT32(&SrcRecNum);
03315     if (ok) ok = pCXaraFileRecord->ReadMatrixTrans(&Transform,0,0);
03316 
03317     if (ok)
03318     {    
03319         ok = FALSE; // Default to fail state
03320 
03321         NodePath* pSrcPath = FindPathRecordRefPath(SrcRecNum);
03322         if (pSrcPath != NULL)
03323         {
03324             NodePath* pNodePath = new NodePath;
03325             if (pNodePath != NULL && pNodePath->SetUpPath())
03326             {
03327                 if (pNodePath->InkPath.MergeTwoPaths(pSrcPath->InkPath))
03328                 {
03329                     pNodePath->InkPath.IsFilled = pSrcPath->InkPath.IsFilled;
03330                     pNodePath->InkPath.IsStroked= pSrcPath->InkPath.IsStroked;
03331 
03332                     // See note in BaseCamelotFilter::FindSimilarPath() for an explaination of why we need 
03333                     // to translate the path before transforming it.
03334 
03335                     // Translate the source path so that it has the same coords as that stored in the file
03336                     DocCoord Origin = GetCoordOrigin();
03337                     {
03338                         Matrix          TranslateMat(-Origin.x,-Origin.y);
03339                         Trans2DMatrix   Translate(TranslateMat);
03340                         pNodePath->Transform(Translate);
03341                     }
03342 
03343                     // Transform the path with the read in matrix
03344                     Trans2DMatrix Trans(Transform);
03345                     pNodePath->Transform(Trans);
03346 
03347                     // Translate the source path back to it's origin position
03348                     {
03349                         Matrix          TranslateMat(Origin.x,Origin.y);
03350                         Trans2DMatrix   Translate(TranslateMat);
03351                         pNodePath->Transform(Translate);
03352                     }
03353 
03354                     ok = InsertNode(pNodePath);
03355                     if (ok) SetLastNodePathInserted(pNodePath);
03356                 }
03357             }
03358         }
03359     }
03360 
03361     return ok;
03362 }
03363 
03364 /********************************************************************************************
03365 
03366 >   virtual void PathRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
03367 
03368     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03369     Created:    12/6/96
03370     Inputs:     pRecord = ptr to a record
03371                 pStr = ptr to string to update
03372     Returns:    -
03373     Purpose:    This is the base class record description generator.
03374                 It doesn't do anything yet.
03375     Errors:     -
03376     SeeAlso:    -
03377 
03378 ********************************************************************************************/
03379 
03380 #ifdef XAR_TREE_DIALOG
03381 void PathRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
03382 {
03383     if (pStr == NULL || pRecord == NULL)
03384         return;
03385 
03386     TCHAR s[256];
03387 
03388     // Call base class first
03389     CamelotRecordHandler::GetRecordDescriptionText(pRecord,pStr);
03390 
03391     switch (pRecord->GetTag())
03392     {
03393         case TAG_PATH:
03394         case TAG_PATH_FILLED:
03395         case TAG_PATH_STROKED:
03396         case TAG_PATH_FILLED_STROKED:
03397             // Use standard base class func for describing the path textually
03398             DescribePath(pRecord,pStr);
03399             break;
03400 
03401         case TAG_PATH_RELATIVE:
03402         case TAG_PATH_RELATIVE_FILLED:
03403         case TAG_PATH_RELATIVE_STROKED:
03404         case TAG_PATH_RELATIVE_FILLED_STROKED:
03405             // Use standard base class func for describing the path textually
03406             DescribePathRelative(pRecord,pStr);
03407             break;
03408 
03409 /*      case TAG_PATH_FLAGS:
03410         {
03411             UINT32 NumFlags  = pRecord->GetSize()*sizeof(BYTE);
03412 
03413             BYTE Flags;
03414             for (UINT32 i=0;i < NumFlags;i++)
03415             {
03416                 pRecord->ReadBYTE(&Flags);
03417 
03418                 camSprintf(s,_T("Flags\r\n")); (*pStr) += s;
03419                 camSprintf(s,_T("Smooth:\t%d\r\n"), (Flags & TAG_PATH_FLAGS_SMOOTH)   != 0); (*pStr) += s;
03420                 camSprintf(s,_T("Rotate:\t%d\r\n"), (Flags & TAG_PATH_FLAGS_ROTATE)    != 0); (*pStr) += s;
03421                 camSprintf(s,_T("EndPoint:\t%d\r\n"),   (Flags & TAG_PATH_FLAGS_ENDPOINT) != 0); (*pStr) += s;
03422                 camSprintf(s,_T("\r\n")); (*pStr) += s;
03423 
03424             }
03425         }
03426         break;
03427 */
03428         case TAG_PATHREF_TRANSFORM:
03429         {
03430             UINT32  SrcRecNum;
03431             Matrix  Transform;
03432 
03433             pRecord->ReadUINT32(&SrcRecNum);
03434             pRecord->ReadMatrix(&Transform);
03435 
03436             camSprintf(s,_T("Src path rec num:\t%d\r\n"),SrcRecNum); (*pStr) += s;
03437 
03438             FIXED16 abcd[4];
03439             INT32    ef[2];
03440             double  d;
03441             Transform.GetComponents(abcd,ef);
03442 
03443             d = abcd[0].MakeDouble(); camSprintf(s,_T("a = %g\r\n"),d); (*pStr) += s;
03444             d = abcd[1].MakeDouble(); camSprintf(s,_T("b = %g\r\n"),d); (*pStr) += s;
03445             d = abcd[2].MakeDouble(); camSprintf(s,_T("c = %g\r\n"),d); (*pStr) += s;
03446             d = abcd[3].MakeDouble(); camSprintf(s,_T("d = %g\r\n"),d); (*pStr) += s;
03447 
03448             camSprintf(s,_T("e = %d\r\n"),ef[0]); (*pStr) += s;
03449             camSprintf(s,_T("f = %d\r\n"),ef[1]); (*pStr) += s;     
03450         }
03451         break;
03452     }
03453 }
03454 #endif
03455 //-------------------------------------------------------------------------------------
03456 //-------------------------------------------------------------------------------------
03457 //-------------------------------------------------------------------------------------
03458 //-------------------------------------------------------------------------------------
03459 
03460 
03461 /********************************************************************************************
03462 
03463 >   virtual UINT32* PathFlagsRecordHandler::GetTagList()
03464 
03465     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03466     Created:    11/5/99
03467     Inputs:     -
03468     Returns:    Ptr to a list of tag values, terminated by CXFRH_TAG_LIST_END
03469     Purpose:    Provides the record handler system with a list of records handled by this
03470                 handler
03471     SeeAlso:    -
03472 
03473 ********************************************************************************************/
03474 
03475 UINT32* PathFlagsRecordHandler::GetTagList()
03476 {
03477     static UINT32 TagList[] = { TAG_PATH_FLAGS,
03478                                 CXFRH_TAG_LIST_END};
03479 
03480     return (UINT32*)&TagList;
03481 }
03482 
03483 /********************************************************************************************
03484 
03485 >   BOOL PathFlagsRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
03486 
03487     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03488     Created:    11/5/99
03489     Inputs:     pCXaraFileRecord = ptr to record to handle
03490     Returns:    TRUE if handled successfuly
03491                 FALSE otherwise
03492     Purpose:    Handles the given record.
03493                 The record has to be a path flags record
03494 
03495                 NOTE: moved from PathRecordHandler class by Markn 12/5/99
03496     SeeAlso:    -
03497 
03498 ********************************************************************************************/
03499 
03500 BOOL PathFlagsRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
03501 {
03502     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
03503     ERROR3IF(pCXaraFileRecord->GetTag() != TAG_PATH_FLAGS,"I don't handle this tag type");
03504 
03505     BOOL ok = TRUE;
03506 
03507     NodePath* pNodePath = GetLastNodePathInserted();
03508 
03509     if (pNodePath != NULL)
03510     {
03511         UINT32 NumFlags  = pCXaraFileRecord->GetSize()*sizeof(BYTE);
03512         UINT32 NumCoords = pNodePath->InkPath.GetNumCoords();
03513 
03514         if (NumCoords == NumFlags)
03515         {
03516             PathFlags* pFlags = pNodePath->InkPath.GetFlagArray();
03517 
03518             BYTE Flags;
03519             for (UINT32 i=0; ok && i < NumFlags;i++)
03520             {
03521                 ok = pCXaraFileRecord->ReadBYTE(&Flags);
03522 
03523                 if (ok)
03524                 {
03525                     pFlags[i].IsSmooth  = (Flags & TAG_PATH_FLAGS_SMOOTH)   != 0;
03526                     pFlags[i].IsRotate  = (Flags & TAG_PATH_FLAGS_ROTATE)   != 0;
03527                     pFlags[i].IsEndPoint= (Flags & TAG_PATH_FLAGS_ENDPOINT) != 0;
03528                 }
03529             }
03530         }
03531     }
03532     else
03533     {
03534         ERROR3("NULL node path ptr");   // Only complain a little in debug builds
03535     }
03536 
03537     return ok;
03538 }
03539 
03540 /********************************************************************************************
03541 
03542 >   virtual void PathFlagsRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
03543 
03544     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03545     Created:    11/5/99
03546     Inputs:     pRecord = ptr to a record
03547                 pStr = ptr to string to update
03548     Returns:    -
03549     Purpose:    This is the base class record description generator.
03550                 It doesn't do anything yet.
03551     Errors:     -
03552     SeeAlso:    -
03553 
03554 ********************************************************************************************/
03555 
03556 #ifdef XAR_TREE_DIALOG
03557 void PathFlagsRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
03558 {
03559     if (pStr == NULL || pRecord == NULL)
03560         return;
03561 
03562     TCHAR s[256];
03563 
03564     // Call base class first
03565     CamelotRecordHandler::GetRecordDescriptionText(pRecord,pStr);
03566 
03567     switch (pRecord->GetTag())
03568     {
03569         case TAG_PATH_FLAGS:
03570         {
03571             UINT32 NumFlags  = pRecord->GetSize()*sizeof(BYTE);
03572 
03573             BYTE Flags;
03574             for (UINT32 i=0;i < NumFlags;i++)
03575             {
03576                 pRecord->ReadBYTE(&Flags);
03577 
03578                 camSprintf(s,_T("Flags\r\n")); (*pStr) += s;
03579                 camSprintf(s,_T("Smooth:\t%d\r\n"), (Flags & TAG_PATH_FLAGS_SMOOTH)   != 0); (*pStr) += s;
03580                 camSprintf(s,_T("Rotate:\t%d\r\n"), (Flags & TAG_PATH_FLAGS_ROTATE)    != 0); (*pStr) += s;
03581                 camSprintf(s,_T("EndPoint:\t%d\r\n"),   (Flags & TAG_PATH_FLAGS_ENDPOINT) != 0); (*pStr) += s;
03582                 camSprintf(s,_T("\r\n")); (*pStr) += s;
03583 
03584             }
03585         }
03586         break;
03587     }
03588 }
03589 #endif

Generated on Sat Nov 10 03:46:05 2007 for Camelot by  doxygen 1.4.4