nodecomp.cpp

Go to the documentation of this file.
00001 // $Id: nodecomp.cpp 1385 2006-06-28 16:48:01Z 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 
00099 /*
00100 */
00101 
00102 #include "camtypes.h"
00103 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00104 //#include "node.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "nodecomp.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 #include "objchge.h"
00108 
00109 // Resource headers
00110 //#include "resource.h"
00111 
00112 // operation headers
00113 #include "ophist.h"
00114 #include "transop.h"
00115 #include "attrappl.h"
00116 
00117 // attribute headers
00118 #include "lineattr.h"
00119 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00120 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00121 #include "attrbev.h"
00122 #include "cutop.h"
00123 #include "nodetxts.h"
00124 #include "nodetext.h"
00125 #include "opbevel.h"
00126 #include "textops.h"
00127 #include "blobs.h"
00128 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00129 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00130 #include "nodebldr.h"
00131 #include "blendatt.h"
00132 //#include "mario.h"
00133 #include "pathops.h"
00134 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00135 
00136 DECLARE_SOURCE( "$Revision: 1385 $" );
00137 
00138 CC_IMPLEMENT_DYNCREATE(CompoundNodeTreeFactory, CCObject)
00139 CC_IMPLEMENT_DYNCREATE(NodeCompound, NodeRenderableInk)
00140 
00141 // Declare smart memory handling in Debug builds
00142 #define new CAM_DEBUG_NEW
00143 
00144 // static variables that are used by the blending system when blending compound nodes ....
00145 
00146 NodeCompound* NodeCompound::shadowThisNode = NULL;
00147 NodeCompound* NodeCompound::shadowDeleteThis = NULL;
00148 
00149 List* NodeCompound::blndConsBecomeAShadows = NULL;      // list of shadow controllers that were generated when blending (internally via PASSBACK)
00150 List* NodeCompound::blndConsBecomeABevels = NULL;       // list of bevel controllers that were generated when blending (internally via PASSBACK)
00151 List* NodeCompound::blndConsBecomeAContours = NULL; // list of contour controllers that were generated when blending (internally via PASSBACK)
00152 
00153 BOOL NodeCompound::isForPrinting = FALSE;
00154 
00155 #ifdef _DEBUG
00156 
00157 INT32 NodeCompound::shadowLastID = 0;
00158 INT32 NodeCompound::bevelLastID = 0;
00159 INT32 NodeCompound::contourLastID = 0;
00160 
00161 INT32 NodeCompound::shadowLastBecomeAID = 0;
00162 INT32 NodeCompound::bevelLastBecomeAID = 0;
00163 INT32 NodeCompound::contourLastBecomeAID = 0;
00164 
00165 #endif
00166 
00167 /***********************************************************************************************
00168 
00169 >   NodeCompound::NodeCompound(Node*    ContextNode,
00170                         AttachNodeDirection Direction,
00171                         const DocRect&      BoundingRect,
00172                         BOOL                Locked = FALSE,
00173                         BOOL                Mangled = FALSE,
00174                         BOOL                Marked = FALSE,
00175                         BOOL                Selected = FALSE,
00176                         BOOL                Renderable = FALSE
00177                         )
00178 
00179     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00180     Created:    11/5/99
00181     Inputs:     ContextNode: Pointer to a node which this node is to be attached to.
00182                 MonoOn Direction: MonoOff
00183                 Specifies the direction in which the node is to be attached to the
00184                 ContextNode. The values this variable can take are as follows:
00185                                   
00186                 PREV      : Attach node as a previous sibling of the context node
00187                 NEXT      : Attach node as a next sibling of the context node
00188                 FIRSTCHILD: Attach node as the first child of the context node
00189                 LASTCHILD : Attach node as a last child of the context node
00190 
00191                 BoundingRect: Bounding rectangle
00192 
00193                 The remaining inputs specify the status of the node:
00194             
00195                 Locked:     Is node locked ?
00196                 Mangled:    Is node mangled ?
00197                 Marked:     Is node marked ?
00198                 Selected:   Is node selected ?
00199 
00200     Purpose:    This constructor initialises the nodes flags and links it to ContextNode in the
00201                 direction specified by Direction. All neccesary tree links are updated.
00202     Note:       SetUpShape() must be called before the NodeRegularShape is in a state in which
00203                 it can be used.
00204     SeeAlso:    NodeRegularShape::SetUpShape
00205     Errors:     An ENSURE will occur if ContextNode is NULL
00206 
00207 ***********************************************************************************************/
00208 NodeCompound::NodeCompound(Node* ContextNode,  
00209                     AttachNodeDirection Direction,  
00210                     BOOL Locked, 
00211                     BOOL Mangled,  
00212                     BOOL Marked, 
00213                     BOOL Selected    
00214               ) : NodeRenderableInk(ContextNode, Direction, Locked, Mangled, Marked, Selected )
00215 {                         
00216     m_DPI = 96.0;
00217     m_bPrinting = FALSE;
00218 
00219     // blending attributes
00220     m_bBlendStartNode = FALSE;
00221     m_bBlendEndNode = FALSE;
00222     m_pBlendCreatedByNode = NULL;
00223 }                        
00224  
00225 /*********************************************************************************************
00226 
00227 >   NodeCompound::NodeCompound() 
00228 
00229     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00230     Created:    11/5/99
00231     Purpose:    
00232     Note:       
00233     SeeAlso:    
00234 
00235 **********************************************************************************************/
00236 NodeCompound::NodeCompound() : NodeRenderableInk()
00237 {
00238     m_DPI = 96.0;
00239     m_bPrinting = FALSE;
00240     m_bBlendStartNode = FALSE;
00241     m_bBlendEndNode = FALSE;
00242     m_pBlendCreatedByNode = NULL;
00243 }
00244 
00245 /*********************************************************************************************
00246 
00247 >   NodeCompound::~NodeCompound() 
00248 
00249     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00250     Created:    11/5/99
00251     Purpose:    Destructor
00252     Note:       
00253     SeeAlso:    
00254 
00255 **********************************************************************************************/
00256 NodeCompound::~NodeCompound()
00257 {
00258 
00259 }
00260 
00261 /*********************************************************************************************
00262 
00263 >   ChangeCode NodeCompound::OnChildChange(ObjChangeParam* pParam)
00264 
00265     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00266     Created:    11/5/99
00267     Purpose:    Processes child change messages
00268     Note:       
00269     SeeAlso:    
00270 
00271 **********************************************************************************************/
00272 ChangeCode NodeCompound::OnChildChange(ObjChangeParam* pParam)
00273 {
00274     if (IsNodeHidden())
00275         return CC_OK;
00276     
00277     ERROR2IF(pParam == NULL,CC_FAIL,"pParam == NULL");
00278 
00279     BOOL bRegen = TRUE;
00280     BOOL bCache = FALSE;
00281 
00282 //  Node * pChild = NULL;
00283 
00284 //  Node * pThis = this;
00285 
00286 //  Node * pNextChild = NULL;
00287 
00288     UndoableOperation* pOp = NULL;
00289     if (pParam->GetOpPointer())
00290     {
00291         if (pParam->GetOpPointer()->IsKindOf(CC_RUNTIME_CLASS(UndoableOperation)))
00292         {
00293             pOp = pParam->GetOpPointer();
00294         }
00295     }
00296 
00297 //  CCRuntimeClass * pClass = NULL;
00298 
00299 //  ActionList * pActionList = NULL;
00300 
00301 //  Action *pAction = NULL;
00302 
00303 //  BOOL bDelete = FALSE;
00304 //  INT32 NumChildrenThatNeedMe = 0;
00305 
00306     // the last code failled to give a spread for nodes recently divorced
00307     Spread * pSpread = Document::GetSelectedSpread(); //(Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
00308     ERROR2IF(pSpread==NULL, CC_FAIL, "Can't find selected spread");
00309 
00310     List TextNodeList;
00311     NodeListItem * pItem = NULL;
00312 
00313     DocRect Bounds = BoundingRectangle;
00314 
00315     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00316     INT32 BlobSize = 0;
00317 
00318     if (!pOp && pParam->GetChangeType() == OBJCHANGE_FINISHED &&
00319         pParam->GetDirection() == OBJCHANGE_CALLEDBYCHILD &&
00320         pParam->GetChangeFlags().RegenerateNode)
00321     {
00322         RegenerateNode(NULL, FALSE, FALSE);
00323         return CC_OK;
00324     }
00325         
00326     if (BlobMgr)
00327     {
00328         BlobSize = BlobMgr->GetBlobSize();
00329     }
00330 
00331     if (pParam->GetChangeType() == OBJCHANGE_STARTING)
00332     {
00333         
00334     }
00335     else if (pParam->GetChangeType() == OBJCHANGE_FINISHED)
00336     {
00337         if (pParam->GetChangeFlags().DeleteNode)
00338         {
00339             TRACEUSER( "DavidM", _T("Delete node\n"));
00340         }
00341         
00342         if (pOp != NULL)
00343         { 
00344             // Ok! Tell all my children to regenerate (in some cases !)
00345 
00346             // check for ops which we do nothing with
00347             if (pOp->IsKindOf(CC_RUNTIME_CLASS(TransOperation)) &&
00348                 !pOp->IS_KIND_OF(OpMovePathPoint))
00349             {
00350                 // ok, we have a transform operation
00351                 TRACEUSER( "DavidM", _T("Transform operation!"));
00352 
00353                 if (((TransOperation *)pOp)->GetCurrentMatrix().IsTranslation())
00354                 {
00355                     // find out if the selected object is a child of myself
00356                     Range Sel(*(GetApplication()->FindSelection()));
00357 
00358                     Node * pNode = Sel.FindFirst(FALSE);
00359 
00360                     bRegen = TRUE;
00361 
00362                     while (pNode && bRegen)
00363                     {
00364                         if (pNode->FindParent())
00365                         {
00366                             if (pNode->FindParent()->ShouldITransformWithChildren())
00367                             {
00368                                 bRegen = FALSE;
00369                             }
00370                         }
00371 
00372                         pNode = Sel.FindNext(pNode, FALSE);
00373                     }
00374                 }
00375                 else
00376                 {
00377                     // re-format all text nodes under me
00378                     ReleaseCached();
00379                     pOp->DoInvalidateRegion(pSpread, BoundingRectangle);
00380 
00381                     BevelTools::GetAllNodesUnderNode(this, &TextNodeList, 
00382                         CC_RUNTIME_CLASS(TextStory));
00383 
00384                     pItem = (NodeListItem *)TextNodeList.GetHead();
00385 
00386                     while (pItem)
00387                     {
00388                         if (pItem->pNode)
00389                         {
00390                             PrePostTextAction::DoFormatStory(pOp, (TextStory *) pItem->pNode, TRUE);
00391                         }
00392 
00393                         pItem = (NodeListItem *)TextNodeList.GetNext(pItem);
00394                     }
00395 
00396                     TextNodeList.DeleteAll();
00397                                 
00398                     bRegen = TRUE;
00399                     bCache = FALSE;
00400                 }
00401             }
00402             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpApplyAttrib)))
00403             {
00404                 // has there been a change which causes a geometry change ?
00405                 // if so, we need to regen
00406                 CCRuntimeClass * pClass = ((OpApplyAttrib *)pOp)->GetValueChangeType();
00407 
00408                 if (pClass)
00409                 {
00410                     if (pClass->IsKindOf(CC_RUNTIME_CLASS(AttrTxtBase)) ||
00411                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrBevelIndent)) ||
00412                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrTranspFillGeometry)) ||
00413                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrLineWidth)) ||
00414                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeColour)) ||
00415                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrStartCap)) ||
00416                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrStartArrow)) ||
00417                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrDashPattern)) ||
00418                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrEndArrow)) ||
00419                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrJoinType)) ||
00420                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeColourChange)) ||
00421                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeTranspChange)) ||
00422                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeTranspTypeChange))
00423                         )
00424                     {
00425                         bRegen = TRUE;
00426                     }
00427                     else
00428                     {
00429                         bRegen = FALSE;
00430                     
00431                     }
00432                 }
00433             }
00434             // this operation is called for changes in fills (e.g. dragging a linear fill blob,
00435             // and then dropping it
00436             // we need to stop regeneration for normal fills, but regen for transparency fills
00437             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpReplaceAttributes)))
00438             {
00439                 // find out the op name
00440                 String_256 OpName;
00441 
00442                 // don't do colour fills, but do transparent fills
00443                 NodeAttribute * pAttr = ((OpReplaceAttributes *)pOp)->GetAttribute();
00444 
00445                 if (pAttr)
00446                 {
00447                     if (pAttr->IsAFillAttr())
00448                     {
00449                         bRegen = FALSE;
00450                     }
00451 
00452                     if (pAttr->IsATranspFill())
00453                     {
00454                         bRegen = TRUE;
00455                     }
00456                         
00457                 }
00458             }
00459             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpDelete)))
00460             {
00461                 bRegen = FALSE;
00462             }
00463             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpCopy)))
00464             {
00465                 bRegen = FALSE;
00466             }
00467             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpCut)))
00468             {
00469                 bRegen = FALSE;
00470             }
00471             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpPaste)))
00472             {
00473                 bRegen = FALSE;
00474             }
00475             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpTextFormat)))
00476             {
00477                 // indicates a change in the text (e.g. the user has typed in)
00478                 // find out if my children have all disappeared
00479                 // invalidate and hide me
00480                 Bounds.lo.x = BoundingRectangle.lo.x - BlobSize;
00481                 Bounds.lo.y = BoundingRectangle.lo.y - BlobSize;
00482                 Bounds.hi.x = BoundingRectangle.hi.x + BlobSize;
00483                 Bounds.hi.y = BoundingRectangle.hi.y + BlobSize;
00484                 
00485                 ReleaseCached();
00486                 pOp->DoInvalidateRegion(pSpread, Bounds);
00487 
00488                 // regen if we've still got a valid interior
00489                 if (GetInsideBoundingRect().IsEmpty())
00490                 {
00491                     bRegen = FALSE;
00492                 }
00493                 else
00494                 {
00495                     bRegen = TRUE;
00496                 }
00497 
00498                 bCache = TRUE;
00499             }
00500             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpDeleteTextStory)))
00501             {
00502                 // indicates that a text story has been deleted - so if my inside
00503                 // bounding rect is empty then hide me
00504                 if (GetInsideBoundingRect().IsEmpty())
00505                 {
00506                     // hide me !
00507                     pOp->DoHideNode(this, TRUE, NULL, TRUE);
00508                     bRegen = FALSE;
00509                 }
00510             }
00511             else
00512             {
00513                 // regen for all other ops
00514                 bRegen = TRUE;
00515             }
00516 
00517             if (bRegen)
00518             {
00519 //              Document * pDoc = Document::GetCurrent();
00520 
00521                 pOp->DoInvalidateRegion(pSpread, Bounds);   // Invalidate original bounds
00522 
00523                 BOOL bDidRegen = RegenerateNode(pOp, bCache, FALSE);    // ForceRedraw is called internally by RegenerateNode...
00524 
00525                 if (bDidRegen)
00526                 {
00527                     ReleaseCached();
00528                     pOp->DoInvalidateRegion(pSpread, GetBoundingRect());
00529                 }
00530             }
00531         }
00532         else
00533         {
00534             // were being called by with a pOp of NULL (i.e.  the change is occuring
00535             // in a none undoable way)
00536 
00537             // NOTE:  we need to regenerate here as well, because we will be called with
00538             // a pOp of NULL when being called through OpRepeatApplyAttribToSelected ....
00539 
00540 //          Document * pDoc = Document::GetCurrent();
00541 
00542             RegenerateNode(NULL, bCache, FALSE);    // ForceRedraw is called internally by RegenerateNode...
00543             
00544             // we should also probably check whether it really is OpRepeatApplyAttribToSelected
00545             // that is causing this block of code to execute (BUT, there is no way of doing this!)
00546         }       
00547     }
00548 
00549     // exit (bearing in mind this has probably been called by my inherited classes)
00550     return NodeRenderableInk::OnChildChange(pParam);
00551 }
00552 
00553 /*********************************************************************************************
00554 
00555 >   BOOL NodeCompound::RegenChildren()
00556 
00557     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00558     Created:    11/5/99
00559     Purpose:    Calls all the regenerate functions on all children (bottom to top)
00560     Note:       
00561     SeeAlso:    
00562 
00563 **********************************************************************************************/
00564 /*
00565 BOOL NodeCompound::RegenChildren(UndoableOperation * pOp)
00566 {
00567     // runs through all children doing a regeneration
00568     RegenerateNode(pOp);
00569 
00570     return TRUE;
00571 }
00572 */
00573 
00574 /*********************************************************************************************
00575 
00576 >   BOOL NodeCompound::RegenerateNode(Node * pNode, UndoableOperation * pOp)
00577 
00578     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00579     Created:    11/5/99
00580     Purpose:    Recursive function to regenerate all nodes
00581     Note:       
00582     SeeAlso:    
00583 
00584 **********************************************************************************************/
00585 /*
00586 BOOL NodeCompound::RegenerateNode(Node * pNode, UndoableOperation * pOp)
00587 {
00588     Node * pChildNode = pNode->FindFirstChild();
00589 
00590     while (pChildNode)
00591     {
00592         if (!RegenerateNode(pChildNode, pOp))
00593             return FALSE;
00594 
00595         pChildNode = pChildNode->FindNext();
00596     }
00597 
00598     return pNode->RegenerateNode(pOp);
00599 }
00600 */
00601 
00602 
00603 
00604 /********************************************************************************************
00605 
00606 >   void NodeCompound::SetSelected(BOOL Status)
00607 
00608     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00609     Created:    17/07/2000
00610     Inputs:     Status      whether we should become selected or deselected.
00611 
00612     Outputs:    If (Status) then all children are selected.
00613 
00614     Purpose:    Selects this node.
00615                 Note that in most cases you'll want to call Select() or DeSelect(), which
00616                 are virtual methods in NodeRenderable.
00617 
00618                 This override of Node::SetSelected() contains special case code for the
00619                 usual suspects - shadows, bevels and contours - which sometimes behave like
00620                 groups, but try to fool the user into thinking they aren't. Any attempt to
00621                 select NodeBevelController or its sisters of murphy will result in their
00622                 *children* being selected instead.
00623 
00624                 It's generally better to use Ranges with PromoteToParent to do rendering
00625                 and similar with these nodes - eg to iterate over the selection, don't step
00626                 through the tree, querying each node - use a SelRange instead. It should
00627                 be quicker, and you can choose to have controller nodes automatically
00628                 included.
00629 
00630 ********************************************************************************************/
00631 void NodeCompound::SetSelected(BOOL Status)
00632 {
00633     // normal nodes get a normal set-selected.
00634     if (PromoteHitTestOnChildrenToMe() || !Status)
00635         Node::SetSelected(Status);
00636 
00637     // contour, bevel & shadow controller nodes instead
00638     // select all of their ink-children (arbitrary).
00639     // NOTE - doesn't happen if Status is FALSE.
00640     else
00641     {
00642         Node* pKid = FindFirstChild();
00643         while (pKid != NULL)
00644         {
00645             if (pKid->IsAnObject())
00646                 ((NodeRenderableInk*)pKid)->SetSelected(Status);
00647 
00648             pKid = pKid->FindNext();
00649         }
00650     }
00651 }
00652 
00653 // Karim 17/07/2000 - I left these in for a while in case I needed to implement them within
00654 // this class. It seems like I don't, so they should probably be removed at some point.
00655 // This will cause a large rebuild due to their declarations in nodecomp.h .
00656 // Simon 9/8/2000 - The controllers (bev/shadow/contour) never want to be drawn so dont try it matey
00657 void NodeCompound::Select(BOOL ReDraw)
00658 {
00659     NodeRenderableInk::Select(ReDraw && PromoteHitTestOnChildrenToMe());
00660 }
00661 
00662 void NodeCompound::DeSelect(BOOL ReDraw, BOOL bDeselectChildren)
00663 {
00664     NodeRenderableInk::DeSelect(ReDraw && PromoteHitTestOnChildrenToMe(), bDeselectChildren);
00665 }
00666 
00667 
00668 
00669 /********************************************************************************************
00670 
00671 >   virtual DocRect NodeCompound::GetInsideBoundingRect()
00672 
00673     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00674     Created:    21/6/99
00675     Inputs:     
00676     Purpose:    Gets the bounding rectangle of all nodes under me, except for the 'needs
00677                 parent' nodes
00678     SeeAlso:    
00679 
00680 ********************************************************************************************/
00681 DocRect NodeCompound::GetInsideBoundingRect()
00682 {
00683     Node * pNode = FindFirstChild();
00684 
00685     DocRect dr;
00686 
00687     while (pNode)
00688     {
00689         if (pNode->IsAnObject())
00690         {
00691             if (!pNode->NeedsParent(NULL))
00692             {
00693                 dr = dr.Union(((NodeRenderableInk *)pNode)->GetBoundingRect(FALSE, FALSE));
00694             }
00695         }
00696 
00697         pNode = pNode->FindNext();
00698     }
00699 
00700     return dr;
00701 }
00702 
00703 /********************************************************************************************
00704 
00705 >   void NodeCompound::PreInformParentsOfRegenerate()
00706 
00707     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00708     Created:    6/9/99
00709     Inputs:     None
00710     Purpose:    Informs all parents of a regeneration of the node
00711     Notes:      Call after the RegenerateNode function in each compound node
00712     SeeAlso:    
00713 
00714 ********************************************************************************************/
00715 void NodeCompound::PreInformParentsOfRegenerate()
00716 {
00717     ObjChangeFlags flgs;
00718     flgs.RegenerateNode = TRUE;
00719 
00720     ObjChangeParam OP(OBJCHANGE_STARTING, flgs, this, NULL, OBJCHANGE_CALLEDBYCHILD);
00721 
00722     Node * pParent = FindParent();
00723 
00724     while (pParent)
00725     {
00726         pParent->OnChildChange(&OP);
00727 
00728         pParent = pParent->FindParent();
00729     }
00730     
00731     return;
00732 }
00733 
00734 /********************************************************************************************
00735 
00736 >   virtual void NodeCompound::PostInformParentsOfRegenerate()
00737 
00738     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00739     Created:    6/9/99
00740     Inputs:     None
00741     Purpose:    Informs all parents of a regeneration of the node
00742     Notes:      Call after the RegenerateNode function in each compound node
00743     SeeAlso:    
00744 
00745 ********************************************************************************************/
00746 void NodeCompound::PostInformParentsOfRegenerate()
00747 {
00748     ObjChangeFlags flgs;
00749     flgs.RegenerateNode = TRUE;
00750 
00751     ObjChangeParam OP(OBJCHANGE_FINISHED, flgs, this, NULL, OBJCHANGE_CALLEDBYCHILD);
00752 
00753     Node * pParent = FindParent();
00754 
00755     while (pParent)
00756     {
00757         if (pParent->IsAnObject())
00758         {
00759             pParent->OnChildChange(&OP);
00760         }
00761 
00762         pParent = pParent->FindParent();
00763     }
00764     
00765     return;
00766 }
00767 
00768 /********************************************************************************************
00769 
00770 >   NodeCompound * NodeCompound::FindAssociatedBlendNode(BlendNodeParam * pParam)
00771 
00772     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00773     Created:    5/3/2000
00774     Inputs:     The blend node param (see blender & blendatt.h)
00775     Returns:    The node compound that has been found
00776     Purpose:    Gets the associated compound node I am to blend to, in the end blend path
00777                 structure in the blend node param
00778                 NULL if non found
00779     Notes:      Only applies to blends
00780                 Use in conjunction with overrided Blend functions in derived classes
00781     SeeAlso:    
00782 
00783 ********************************************************************************************/
00784 NodeCompound * NodeCompound::FindAssociatedBlendNode(BlendNodeParam * pParam)
00785 {
00786     // count how many parents I have
00787     Node * pParent = FindParent();
00788 
00789     UINT32 NumParents = 0;
00790     UINT32 NumSameNodesAbove = 0;
00791 
00792     while (pParent)
00793     {
00794         if (pParent->GetRuntimeClass() == this->GetRuntimeClass())
00795         {
00796             NumSameNodesAbove++;
00797         }
00798         
00799         NumParents ++;
00800         pParent = pParent->FindParent();
00801     }
00802 
00803     UINT32 NumSameNodesBelow = 0;
00804 
00805     Node * pChild = FindFirstChild(CC_RUNTIME_CLASS(NodeCompound));
00806 
00807     while (pChild)
00808     {
00809         if (pChild->GetRuntimeClass() == this->GetRuntimeClass())
00810         {
00811             NumSameNodesBelow ++;
00812         }
00813 
00814         pChild = pChild->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound));
00815     }
00816 
00817     // thus deduce how many similar nodes to myself are in the tree (including myself)
00818     UINT32 NumSameNodesInStartTree = NumSameNodesAbove + NumSameNodesBelow + 1;
00819 
00820     // ok, lets search the end node for similar nodes
00821     Node * pEndNode = pParam->GetEndBlendPath()->GetBlendNode();
00822 
00823     // if the end node isn't a compound node, then I can't find anything
00824     if (!pEndNode->IsCompoundClass())
00825         return NULL;
00826 
00827     // find the node with the closest number of parents to mine
00828     Node * pEndNodeStep = pEndNode;
00829     
00830     // build a list of all similar nodes in the end tree
00831     List EndNodeList;
00832 
00833     NodeListItem * pItem = NULL;
00834 
00835     while (pEndNodeStep)
00836     {
00837         if (pEndNodeStep->GetRuntimeClass() == this->GetRuntimeClass())
00838         {
00839             pItem = new NodeListItem(pEndNodeStep);
00840             ERRORIF(pItem == NULL, _R(IDE_NOMORE_MEMORY), NULL);
00841 
00842             EndNodeList.AddTail(pItem);
00843         }
00844 
00845         pEndNodeStep = pEndNodeStep->FindFirstChild(CC_RUNTIME_CLASS(NodeCompound));
00846     }
00847 
00848     // ok, now we have a list of nodes we have found in the end node list,
00849     // and we know how many exist above me in the start node list
00850     // therefore, pick a node in the list to match to
00851     if (EndNodeList.IsEmpty())
00852     {
00853         // can't match to any !
00854         return NULL;
00855     }
00856 
00857     NodeCompound * pRetnNode = NULL;
00858 
00859     if (NumSameNodesInStartTree == EndNodeList.GetCount())
00860     {
00861         // 1 to 1 relationship therefore easy !
00862         UINT32 Count = 0;
00863 
00864         pItem = (NodeListItem *)EndNodeList.GetHead();
00865 
00866         while (pItem)
00867         {
00868             if (Count == NumSameNodesAbove)
00869             {
00870                 // get the node to return
00871                 pRetnNode = (NodeCompound *)pItem->pNode;
00872 
00873                 EndNodeList.DeleteAll();
00874 
00875                 return pRetnNode;
00876             }
00877         }
00878 
00879         ERROR3("Number of nodes in list isn't the same as the number of nodes in start tree");
00880         EndNodeList.DeleteAll();
00881         return NULL;
00882     }
00883 
00884     EndNodeList.DeleteAll();
00885 
00886     // many to one and one to many relations are not implemented yet    
00887     return NULL;
00888 }
00889 
00890 /********************************************************************************************
00891 
00892 >   virtual NodeRenderableInk * NodeCompound::GetNodeToBlend()
00893 
00894     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00895     Created:    6/9/99
00896     Inputs:     None
00897     Purpose:    Gets the blend node inside of this compound node
00898     Notes:      Only applies to blends
00899     SeeAlso:    
00900 
00901 ********************************************************************************************/
00902 NodeRenderableInk * NodeCompound::GetNodeToBlend()
00903 {
00904     Node * pChild = FindFirstChild();
00905 
00906     while (pChild)
00907     {
00908         if (pChild->IsAnObject())
00909         {
00910             if (!pChild->NeedsParent(NULL))
00911             {
00912                 // we've found it
00913                 if (pChild->IsCompoundClass())
00914                 {
00915                     return ((NodeCompound*)pChild)->GetNodeToBlend();
00916                 }
00917                 
00918                 return (NodeRenderableInk*)pChild;
00919             }
00920         }
00921 
00922         pChild = pChild->FindNext();
00923     }
00924 
00925     // failure !
00926     return NULL;
00927 }
00928 
00929 
00930 
00931 /*********************************************************************************************
00932 
00933 >    BOOL NodeCompound::AllocBlendConsList (ListType listType)
00934 
00935      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
00936      Created:   22/6/2000
00937      Inputs:    The type of list to allocate
00938      Outputs:   
00939      Returns:   -
00940               
00941      Purpose:   Allocates the relevant list
00942             
00943      Errors:    If the blendConstructedCompoundChildren list has already been allocated.
00944 
00945 **********************************************************************************************/
00946 
00947 BOOL NodeCompound::AllocBlendConsList (ListType listType)
00948 {
00949 #ifdef _DEBUG   
00950     List* accessList = InitListPtr (listType);
00951     if (accessList != NULL)
00952     {
00953         ERROR3IF (accessList->GetCount () > 0, "Trying to re-allocate a list that still has items in it!");
00954     }
00955 #endif
00956     
00957     switch (listType)
00958     {
00959         case LT_BECOMEA_SHADOWSLIST:
00960             blndConsBecomeAShadows = new List;
00961         break;
00962         case LT_BECOMEA_BEVELSLIST:
00963             blndConsBecomeABevels = new List;
00964         break;
00965         case LT_BECOMEA_CONTOURSLIST:
00966             blndConsBecomeAContours = new List;
00967         break;
00968         default:
00969             ERROR3 ("NodeCompound::AllocBlendConsList - You wan't what??? !!!");
00970             return (TRUE);
00971     }
00972     
00973     return (TRUE);
00974 }
00975 
00976 /*********************************************************************************************
00977 
00978 >    BOOL NodeCompound::AllocatedBlendConsList (ListType listType)
00979 
00980      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
00981      Created:   22/6/2000
00982      Inputs:    The type of list to check
00983      Outputs:   
00984      Returns:   TRUE if list is not empty, FALSE otherwise
00985               
00986      Purpose:   Determins whether blending has generated intermediate compound nodes
00987             
00988      Errors:    -
00989 
00990 **********************************************************************************************/
00991     
00992 BOOL NodeCompound::AllocatedBlendConsList (ListType listType)
00993 {   
00994     List* accessList = InitListPtr (listType);
00995 
00996     if (accessList == NULL)
00997     {
00998         return (FALSE);
00999     }
01000     else
01001     {
01002         if (accessList->GetCount () > 0)
01003         {
01004             return (TRUE);
01005         }
01006     }
01007     
01008     return (FALSE);
01009 }
01010 
01011 /*********************************************************************************************
01012 
01013 >    BOOL NodeCompound::KillBlendConsList (ListType listType, BOOL killAll)
01014 
01015      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01016      Created:   22/6/2000
01017      Inputs:    The type of list that we are killing
01018                 killAll - do we kill all items in the list, or just the ones that are a part
01019                 of the current document?
01020      Outputs:   
01021      Returns:   TRUE if success, FALSE otherwise
01022               
01023      Purpose:   Clears out the list of nodes that were generated when blending compound nodes.
01024             
01025      Errors:    ERROR3IF the list == NULL
01026 
01027 **********************************************************************************************/
01028 
01029 BOOL NodeCompound::KillBlendConsList (ListType listType, BOOL killAll, BOOL isPrinting)
01030 {
01031     List* accessList = InitListPtr (listType);
01032 
01033     ERROR3IF(accessList == NULL, "KillBlendConsList ():  Trying to delete an empty list! - fruitless function call!");
01034 
01035     NodeListItemWithDocPtr* pCurrent = (NodeListItemWithDocPtr*) accessList->GetHead ();
01036 
01037     if (!killAll)
01038     {
01039         if (!isPrinting)
01040         {
01041             // user has closed a document - clear out any data that was required for rendering
01042 
01043             Document* pCurrentDoc = Document::GetCurrent ();
01044 
01045             ERROR3IF (pCurrentDoc == NULL, "No current document!");
01046             
01047             while (pCurrent)
01048             {
01049                 if (pCurrent->pOwner == pCurrentDoc)
01050                 {
01051                     NodeListItemWithDocPtr* pNext = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01052                     
01053                     NodeListItemWithDocPtr* markedForDelete = (NodeListItemWithDocPtr*) accessList->RemoveItem (pCurrent);
01054 
01055                     markedForDelete->pNode->CascadeDelete ();
01056                     delete (markedForDelete->pNode);
01057 
01058                     pCurrent = pNext;
01059                 }
01060                 else
01061                 {
01062                     // the node is NOT linked to the current document - so we can't delete it ....
01063                     
01064                     pCurrent = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01065                 }
01066             }
01067         }
01068         else
01069         {
01070             // were printing ....
01071             
01072             while (pCurrent)
01073             {
01074                 if (pCurrent->isForPrinting)
01075                 {
01076                     NodeListItemWithDocPtr* pNext = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01077                     
01078                     NodeListItemWithDocPtr* markedForDelete = (NodeListItemWithDocPtr*) accessList->RemoveItem (pCurrent);
01079 
01080                     markedForDelete->pNode->CascadeDelete ();
01081                     delete (markedForDelete->pNode);
01082 
01083                     pCurrent = pNext;
01084                 }
01085                 else
01086                 {
01087                     // the node was not generated for printing - so we can't delete it ....
01088                     
01089                     pCurrent = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01090                 }
01091             }
01092         }
01093     }
01094     else
01095     {
01096         // camelot is dying - clear out all data ....
01097         
01098         while (pCurrent)
01099         {
01100             pCurrent->pNode->CascadeDelete ();
01101             delete (pCurrent->pNode);
01102             
01103             pCurrent = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01104         }
01105 
01106         accessList->DeleteAll ();
01107         delete (accessList);
01108 
01109         accessList = NULL;
01110 
01111         switch (listType)
01112         {
01113             case LT_BECOMEA_SHADOWSLIST:
01114                 blndConsBecomeAShadows = NULL;
01115             break;
01116             case LT_BECOMEA_BEVELSLIST:
01117                 blndConsBecomeABevels = NULL;
01118             break;
01119             case LT_BECOMEA_CONTOURSLIST:
01120                 blndConsBecomeAContours = NULL;
01121             break;
01122             default:
01123                 ERROR3 ("NodeCompound::AllocBlendConsList - You wan't what??? !!!");
01124                 return (FALSE);
01125         }
01126     }
01127     
01128     return (TRUE);
01129 }
01130 
01131 /*********************************************************************************************
01132 
01133 >    BOOL NodeCompound::KillBlendConsListItem (ListType listType, NodeBlend* nodeBlend)
01134 
01135      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01136      Created:   22/6/2000
01137      Inputs:    The type of list that we are killing, the blend that the item is associated with
01138      Outputs:   
01139      Returns:   TRUE if success, FALSE otherwise
01140               
01141      Purpose:   Clears out the list of nodes that were generated when blending compound nodes
01142                 which are associated (i.e.  created) with the supplied NodeBlend
01143 
01144      NOTE:      The associated blend node is set by calling NodeCompound::SetBlenderNode ()
01145             
01146      Errors:    ERROR3IF the list == NULL
01147 
01148 **********************************************************************************************/
01149 
01150 BOOL NodeCompound::KillBlendConsListItem (ListType listType, NodeBlend* nodeBlend)
01151 {
01152     List* accessList = InitListPtr (listType);
01153 
01154     ERROR3IF(accessList == NULL, "KillBlendConsList ():  Trying to delete an empty list! - fruitless function call!");
01155 
01156     NodeListItemWithDocPtr* pCurrent = (NodeListItemWithDocPtr*) accessList->GetHead ();
01157 
01158     while (pCurrent)
01159     {
01160         if (pCurrent->pNode->IsCompoundClass())
01161         {
01162             NodeCompound* ptrGroup = (NodeCompound*) pCurrent->pNode;
01163             NodeBlend* ptrBlender = (NodeBlend*) ptrGroup->GetBlenderNode();
01164 
01165             if (ptrBlender == nodeBlend)
01166             {
01167                 NodeListItemWithDocPtr* pNext = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01168                 
01169                 NodeListItemWithDocPtr* markedForDelete = (NodeListItemWithDocPtr*) accessList->RemoveItem (pCurrent);
01170 
01171                 markedForDelete->pNode->CascadeDelete ();
01172                 delete (markedForDelete->pNode);
01173 
01174                 pCurrent = pNext;
01175             }
01176             else
01177             {
01178                 pCurrent = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01179             }
01180         }
01181         else
01182         {
01183             pCurrent = (NodeListItemWithDocPtr*) accessList->GetNext (pCurrent);
01184         }
01185     }
01186     
01187     return (TRUE);
01188 }
01189 
01190 /*********************************************************************************************
01191 
01192 >    BOOL NodeCompound::KillAllBlendBecomeAConsLists (BOOL killAll = FALSE)
01193 
01194      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01195      Created:   29/6/2000
01196      Inputs:    killAll - do we kill all items in the list, or just the ones that are a part
01197                 of the current document?
01198      Outputs:   
01199      Returns:   TRUE if success, FALSE otherwise
01200               
01201      Purpose:   Clears out the list of nodes that were generated when blending compound nodes
01202                 (that were instructed to DOBECOMEA).
01203             
01204      Errors:    
01205 
01206 **********************************************************************************************/
01207 
01208 BOOL NodeCompound::KillAllBlendBecomeAConsLists (BOOL killAll /*= FALSE*/, BOOL isPrinting /*= FALSE*/)
01209 {
01210     if (NodeCompound::AllocatedBlendConsList (LT_BECOMEA_BEVELSLIST))
01211     {
01212         NodeCompound::KillBlendConsList (LT_BECOMEA_BEVELSLIST, killAll, isPrinting);
01213     }
01214 
01215     if (NodeCompound::AllocatedBlendConsList (LT_BECOMEA_CONTOURSLIST))
01216     {
01217         NodeCompound::KillBlendConsList (LT_BECOMEA_CONTOURSLIST, killAll, isPrinting);
01218     }
01219 
01220     if (NodeCompound::AllocatedBlendConsList (LT_BECOMEA_SHADOWSLIST))
01221     {
01222         NodeCompound::KillBlendConsList (LT_BECOMEA_SHADOWSLIST, killAll, isPrinting);
01223     }
01224 
01225     return (TRUE);
01226 }
01227 
01228 /*********************************************************************************************
01229 
01230 >    BOOL NodeCompound::KillAllBlendBecomeAConsListItem ()
01231 
01232      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01233      Created:   29/6/2000
01234      Inputs:    The blend that the item is associated with
01235      Outputs:   
01236      Returns:   TRUE if success, FALSE otherwise
01237               
01238      Purpose:   Clears out the list of nodes (dobecomea) that were generated when blending
01239                 compound nodes which are associated (i.e.  created) with the supplied NodeBlend
01240 
01241      NOTE:      The associated blend node is set by calling NodeCompound::SetBlenderNode ()
01242 
01243 **********************************************************************************************/
01244 
01245 BOOL NodeCompound::KillAllBlendBecomeAConsListItem (NodeBlend* associatedWith)
01246 {
01247     if (NodeCompound::AllocatedBlendConsList (LT_BECOMEA_BEVELSLIST))
01248     {
01249         TRACEUSER( "ChrisS", _T("Invoking delete all on CALLBACK BEVELS\n") );
01250         NodeCompound::KillBlendConsListItem (LT_BECOMEA_BEVELSLIST, associatedWith);
01251     }
01252 
01253     if (NodeCompound::AllocatedBlendConsList (LT_BECOMEA_CONTOURSLIST))
01254     {
01255         TRACEUSER ("ChrisS", _T("Invoking delete all on CALLBACK CONTOURS\n") );
01256         NodeCompound::KillBlendConsListItem (LT_BECOMEA_CONTOURSLIST, associatedWith);
01257     }
01258 
01259     // shadows MUST be deleted last (cause they could be shadowing a bevel, and if we
01260     // calls cascade delete on the shadow then this also deletes the bevel (leaving the
01261     // bevels list bevel vaped - but the list reference to it still valid).
01262     
01263     if (NodeCompound::AllocatedBlendConsList (LT_BECOMEA_SHADOWSLIST))
01264     {
01265         TRACEUSER ("ChrisS", _T("Invoking delete all on CALLBACK SHADOWS\n") );
01266         NodeCompound::KillBlendConsListItem (LT_BECOMEA_SHADOWSLIST, associatedWith);
01267     }
01268 
01269     return (TRUE);
01270 }
01271 
01272 /*********************************************************************************************
01273 
01274 >    BOOL NodeCompound::BlendConsListInsert (ListType listType, Node* insertNode)
01275 
01276      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01277      Created:   22/6/2000
01278      Inputs:    The type of list to insert into, and the node to be inserted
01279      Outputs:   
01280      Returns:   TRUE
01281               
01282      Purpose:   Inserts a blends intermediate node into our list.  This is necessary so that
01283                 they may be deleted at the correct time.
01284             
01285      Errors:    ERROR3IF insertItem == NULL, ERROR3IF list == NULL
01286 
01287 **********************************************************************************************/
01288 
01289 BOOL NodeCompound::BlendConsListInsert (ListType listType, Node* insertNode)
01290 {   
01291     ERROR3IF(insertNode == NULL, "BlendConsListInsert ():  Trying to insert a NULL item into a list!");
01292     
01293     List* accessList = InitListPtr (listType);
01294 
01295     ERROR3IF(accessList == NULL, "BlendConsListInsert ():  Trying to insert item into a list which is NULL!");
01296     
01297     NodeListItemWithDocPtr* pInsert = new NodeListItemWithDocPtr ();
01298                     
01299     if (pInsert)    // and insert into the paths list ....
01300     {
01301         pInsert->pNode = insertNode;
01302         pInsert->pOwner = Document::GetCurrent ();
01303         pInsert->isForPrinting = isForPrinting;
01304         accessList->AddHead (pInsert);
01305 
01306         TRACEUSER ("ChrisS", _T("Blend constructed list just inserted a node\n") );
01307     }
01308     
01309     return (TRUE);
01310 }
01311 
01312 
01313 
01314 
01315 /*********************************************************************************************
01316 
01317 >    List* NodeCompound::InitListPtr (ListType listType)
01318 
01319      Author:    Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01320      Created:   22/6/2000
01321      Inputs:    The type of list to retrieve
01322      Outputs:   
01323      Returns:   Ptr to the relevant list
01324               
01325      Purpose:   Returns a ptr to the relevant list of nodes (that when allocated when blending).
01326             
01327      Errors:    -
01328 
01329 **********************************************************************************************/
01330 
01331 List* NodeCompound::InitListPtr (ListType listType)
01332 {
01333     List* list = NULL;
01334     
01335     switch (listType)
01336     {
01337         case LT_BECOMEA_SHADOWSLIST:
01338             list = blndConsBecomeAShadows;
01339         break;
01340         case LT_BECOMEA_BEVELSLIST:
01341             list = blndConsBecomeABevels;
01342         break;
01343         case LT_BECOMEA_CONTOURSLIST:
01344             list = blndConsBecomeAContours;
01345         break;
01346         default:
01347             list = NULL;
01348     }
01349     
01350     return (list);
01351 }
01352 
01353 
01354     
01355 
01356 /********************************************************************************************
01357 
01358 >   virtual INT32 NodeCompound::EstimateNodeComplexity(OpParam* details)
01359 
01360     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
01361     Created:    6/09/2000
01362     Inputs:     details     any data that should be used for the calculation
01363     Outputs:    -
01364     Returns:    an estimate of the nodes complexity
01365     Purpose:    This function estimates a complexity value for the node.  The complexity
01366                 value is based upon the total length of all paths in the node.
01367     See Also:   OpBlendNodes::DeterminBlendObjectsProcessorHit ()
01368 
01369 ********************************************************************************************/
01370 
01371 INT32 NodeCompound::EstimateNodeComplexity(OpParam* details)
01372 {
01373     INT32 total = 0;
01374     
01375     // Call DoBecomeA on all the group's children
01376     Node* Current = FindFirstChild(); 
01377     Node* Next;
01378 
01379     while (Current != NULL)
01380     {
01381         Next = Current->FindNext();
01382         total += Current->EstimateNodeComplexity (details);
01383         Current = Next;
01384     }
01385 
01386     return (total);
01387 }
01388 
01389 
01390 
01391 
01392 /********************************************************************************************
01393 
01394 >   virtual BOOL NodeCompound::HasVisibleChildren() const
01395 
01396     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01397     Created:    01/04/2005
01398     Inputs:     -
01399     Outputs:    -
01400     Returns:    TRUE if this compound is empty and can thus be deleted or hidden without
01401                 the user seeing any difference
01402     Purpose:    Determine whether this compound node is really doing anything useful
01403 
01404 ********************************************************************************************/
01405 
01406 BOOL NodeCompound::HasVisibleChildren() const
01407 {
01408     if (!this->IsRenderable())
01409         return FALSE;
01410 
01411     Node* pChild = FindFirstChild(); 
01412     while (pChild)
01413     {
01414         if (pChild->IsAnObject() && pChild->IsRenderable())
01415         {
01416             if (!pChild->NeedsParent((Node*)this))
01417                 return TRUE;
01418         }
01419 
01420         pChild = pChild->FindNext();
01421     }
01422 
01423     return FALSE;
01424 }
01425 
01426 
01427 
01428 
01429 /********************************************************************************************
01430 
01431 >   DocRect NodeCompound::GetExtendTargetBounds(const ExtendParams& ExtParams)
01432 
01433     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01434     Created:    02/06/2000
01435     Inputs:     ExtParams       const ref to an ExtendParams class describing the extension.
01436     Outputs:    
01437     Returns:    The DocRect to treat as the bounding rect of this node when extending.
01438     Purpose:    Return a DocRect which contains the bounds of this node as defined and
01439                 required by the Extending mechanism.
01440 
01441                 We treat x- and y- directions separately, and we always return our true
01442                 bounds, unless we're extending in a direction, in which case we return the
01443                 bounds of our extend control points, which currently means the union of the
01444                 ExtendTargetBounds of our children.
01445 
01446     Notes:      Note that we're assuming that we always have kids. No kids => pear-shapey.
01447 
01448     See also:   SliceHelper::BoundingNodeSize().
01449 
01450 ********************************************************************************************/
01451 DocRect NodeCompound::GetExtendTargetBounds(const ExtendParams& ExtParams)
01452 {
01453     DocRect drBounds(0, 0, 0, 0);
01454     Node* pKid = FindFirstChild();
01455     while (pKid != NULL)
01456     {
01457         if (pKid->IsAnObject())
01458             drBounds = drBounds.Union(((NodeRenderableInk*)pKid)->GetExtendTargetBounds(ExtParams));
01459     }
01460 
01461     return drBounds;
01462 }
01463 
01464 
01465 
01466 
01467 /********************************************************************************************
01468 
01469 >   virtual String_32& NodeCompound::GetName()
01470 
01471     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01472     Created:    29/6/94
01473     Inputs:     -
01474     Outputs:    -
01475     Returns:    The name of the group
01476     Purpose:    This routine is used to obtain the name of a group
01477     Errors:     -
01478     SeeAlso:    -
01479 
01480 ********************************************************************************************/
01481 
01482 String_32& NodeCompound::GetName()
01483 {
01484     return CompoundName; 
01485 } 
01486 
01487 /********************************************************************************************
01488 
01489 >   virtual void SetName(String_32& Name)
01490 
01491     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01492     Created:    29/6/94
01493     Inputs:     Name: The new group name
01494     Outputs:    -
01495     Returns:    -
01496     Purpose:    For setting the group's name
01497     Errors:     -
01498     SeeAlso:    -
01499 
01500 ********************************************************************************************/
01501 
01502 void NodeCompound::SetName(String_32& Name) 
01503 {
01504     CompoundName = Name; 
01505 }
01506 
01507 
01508    
01509 /********************************************************************************************
01510 
01511 >   virtual BOOL NodeCompound::AllowOp_AccountForCompound(ObjChangeParam* pParam,
01512                                                             BOOL SetOpPermissionState,
01513                                                             BOOL DoPreTriggerEdit)
01514     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01515     Created:    24/10/2000
01516     Inputs:     pParam                  information about the requested change, eg which Op.
01517                 SetOpPermissionState    whether flags should be set ready for OnChildChange.
01518                 DoPreTriggerEdit        if TRUE then NameGallery::PreTriggerEdit is called.
01519                                         *Must* be TRUE if the calling Op may make any nodes
01520                                         change their bounds, eg move, line width, cut.
01521                                         Use TRUE if unsure.
01522 
01523     Returns:    TRUE if ANY of the objects in the compound node have allowed the op.
01524 
01525     Purpose:    Overrides Node::AllowOp_AccountForCompound - see definition of that function
01526                 for a general explanation.
01527 
01528                 Controller nodes need to inform all of their other children whenever one
01529                 child is queried via AllowOp. This is because unlike a normal group, editing
01530                 one child may well affect its siblings, who must be informed of the edit.
01531 
01532     See Also:   Node::AllowOp, Node::AllowOp_AccountForCompound
01533 
01534     Notes:      Karim 20/12/2000
01535                 Added the OpTextFormat bodge for feathers on bevelled text - sorry!
01536 
01537 ********************************************************************************************/
01538 BOOL NodeCompound::AllowOp_AccountForCompound(ObjChangeParam* pParam,
01539                                                 BOOL SetOpPermissionState,
01540                                                 BOOL DoPreTriggerEdit)
01541 {
01542     BOOL AnyAllowed = FALSE;
01543 
01544     Node* pCallingChild = (pParam->GetDirection() == OBJCHANGE_CALLEDBYCHILD) ?
01545                                                     pParam->GetCallingChild() : NULL;
01546     pParam->SetCallingChild(NULL);
01547 
01548     ObjChangeFlags Flags = pParam->GetChangeFlags();
01549     UndoableOperation* pChangeOp = pParam->GetOpPointer();
01550 
01551     if( Flags.Attribute || 
01552         Flags.TransformNode || 
01553         Flags.RegenerateNode ||
01554         ( pChangeOp != NULL 
01555           && pChangeOp->IS_KIND_OF( OpTextUndoable )
01556          ) )
01557     {
01558 
01559         ObjChangeDirection OldDirection = pParam->GetDirection();
01560         pParam->SetDirection(OBJCHANGE_CALLEDBYPARENT);
01561         Node* pNode = FindFirstChild();
01562 
01563         BOOL InformGeomLinkedAttrs =    SetOpPermissionState &&
01564                                         pChangeOp != NULL &&
01565                                         pChangeOp->MayChangeNodeBounds();
01566 
01567         while (pNode != NULL)
01568         {
01569             if (pNode != pCallingChild)
01570             {
01571                 if (pNode->IsAnObject())
01572                     AnyAllowed |= pNode->AllowOp(pParam, SetOpPermissionState, DoPreTriggerEdit);
01573                 else
01574                 {
01575                     if (pNode->IsAnAttribute() && ((NodeAttribute*)pNode)->IsLinkedToNodeGeometry())
01576                         if (InformGeomLinkedAttrs)
01577                             ((NodeAttribute*)pNode)->LinkedNodeGeometryHasChanged(pChangeOp);
01578                 }
01579             }
01580 
01581             pNode = pNode->FindNext();
01582         }
01583 
01584         pParam->SetDirection(OldDirection);
01585     }
01586 
01587     // if setting flags and any child allowed it, set this permission allowed
01588     if (SetOpPermissionState && AnyAllowed)
01589         SetOpPermission(PERMISSION_ALLOWED);
01590 
01591     return AnyAllowed;
01592 }
01593 
01594 
01595 
01596 
01597 /********************************************************************************************
01598 
01599 >   virtual BOOL NodeCompound::CanBecomeA(BecomeA* pBecomeA)
01600 
01601 
01602     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01603     Created:    29/4/94
01604     Inputs:     InkClass: The class of object
01605                 pNumObjects = ptr to place number of objects of type pClass that will be created (Note: can be NULL).
01606                               *pNumObects in undefined on entry
01607     Outputs:    -
01608     Returns:    TRUE if the node, or any of its children can transmogrify themselves to become 
01609                 an InkClass object
01610 
01611     Purpose:    This function is used by the convert to shapes operation. It determines if 
01612                 the node or any of its children can convert themselves into an InkClass object. 
01613 
01614                 The number you put into pNumObjects (if it's not NULL) should exactly equal the total number
01615                 of pClass objects you create.  It should NOT contain any additional objects you may produce
01616                 such as group objects for containing the pClass object, or attributes.
01617 
01618                 Also, the entry value of *pNumObjects cannot be assumed to be 0.
01619 
01620     Errors:     -
01621 
01622 ********************************************************************************************/
01623 
01624 BOOL NodeCompound::CanBecomeA(BecomeA* pBecomeA)
01625 {
01626     BOOL bOK=FALSE;
01627 
01628     Node* Current = FindFirstChild(); 
01629     while (Current != NULL)
01630     {
01631         if (Current->CanBecomeA(pBecomeA))              // Increments count
01632         {
01633             bOK = TRUE;
01634             if (!pBecomeA->IsCounting())
01635                 return TRUE;                            // Don't want total, so return now
01636         }
01637 
01638         Current = Current->FindNext();
01639     }
01640 
01641     return bOK;
01642 }
01643 
01644 
01645 /********************************************************************************************
01646 
01647 >   virtual BOOL NodeCompound::DoBecomeA(BecomeA* pBecomeA)
01648 
01649     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> (and Markn)
01650     Created:    29/4/94
01651     Inputs:     pBecomeA =  ptr to a class that contains all the info needed to become a new
01652                             type of node.
01653     Outputs:    -
01654     Returns:    TRUE if the object has been transformed, FALSE if we run out of memory
01655 
01656     Purpose:    Transforms the object into another type of object. 
01657                 Note: changed 7/10/94 by MarkN to take the pBecomeA param, so that more data could be passed
01658                 to these functions in the future without causing massive rebuilds due to the editing of
01659                 node.h
01660 
01661                 14/6/95 (Markn): This now localises attributes before calling it's children, then factors them out
01662                 at the end.  This is so that common attrs trickle down to ALL leaf nodes, no matter how deep 
01663                 they may be.  This is important when a child node is a blend, because the action of DoBecomeA()
01664                 in the blend's case creates new child attrs for the path it creates, hence making any common
01665                 attrs illegal.
01666     Errors:     -
01667     SeeAlso:    Node::CanBecomeA
01668 
01669 ********************************************************************************************/
01670 
01671 BOOL NodeCompound::DoBecomeA(BecomeA* pBecomeA)
01672 {
01673     ERROR2IF(pBecomeA == NULL,FALSE,"NULL BecomeA ptr");
01674 
01675     UndoableOperation* pOp = pBecomeA->GetUndoOp();
01676     BOOL ok;
01677 
01678     if (pBecomeA->GetReason() == BECOMEA_REPLACE)
01679     {
01680 //      ERROR2IF(pBecomeA->GetUndoOp() == NULL,FALSE,"Trying to replace a NodeGroup, but pUndoOp == NULL");
01681 
01682         // Make all attrs children of the child nodes
01683         if (pOp)
01684             ok = pOp->DoLocaliseCommonAttributes(this);
01685         else
01686             ok = LocaliseCommonAttributes();
01687         if (!ok)
01688             return FALSE;
01689     }
01690 
01691     // Call DoBecomeA on all the group's children
01692     Node* Current = FindFirstChild(); 
01693     Node* Next;
01694 
01695     while (Current != NULL)
01696     {
01697         Next = Current->FindNext();
01698         if(!(Current->DoBecomeA(pBecomeA)))
01699         {   
01700             return FALSE;       // Out of memory
01701         } 
01702         Current = Next; 
01703     }
01704 
01705     if (pBecomeA->GetReason() == BECOMEA_REPLACE)
01706     {
01707 //      ERROR2IF(pBecomeA->GetUndoOp() == NULL,FALSE,"Trying to replace a NodeGroup, but pUndoOp == NULL");
01708 
01709         // Factor out the attrs (needed after a call to DoLocaliseCommonAttributes
01710         if (pOp)
01711             ok = pOp->DoFactorOutCommonChildAttributes(this);
01712         else
01713             ok = FactorOutCommonChildAttributes();
01714         if (!ok)
01715             return FALSE;
01716     }
01717 
01718     return TRUE; 
01719 }
01720 
01721 
01722 
01723 /********************************************************************************************
01724 
01725 >   virtual NodeGroup* NodeCompound::BecomeAGroup(UndoableOperation* pUndoOp)
01726 
01727     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01728     Created:    20/07/2000
01729 
01730     Inputs:     pUndoOp     ptr to an Op to use for all the undoable bits.
01731 
01732     Outputs:    This node is converted into a group.
01733 
01734     Returns:    A pointer to the new group node if successful,
01735                 NULL otherwise.
01736 
01737     Purpose:    This method is for the convenience of any compound node descended from
01738                 NodeGroup. It 'changes' the node into a group by following these steps:
01739                     *   Attributes are undoably localised on this node.
01740                     *   A group node is created.
01741                     *   All children of this node are undoably moved under the group node.
01742                     *   The group is undoably inserted into the tree as the next sibling.
01743                     *   Attributes are undoably factored out on the group node.
01744                     *   This node is undoably hidden.
01745 
01746     Notes:      The group's selection state is maintained - if this node is selected now,
01747                 then the new group will be selected - although this may not be enough
01748                 for some nodes, eg NodeShadowController et al.
01749 
01750                 Although calling this method on a normal NodeGroup should have no visible
01751                 effect, it *is* wasteful of time and memory. So DON'T call this method
01752                 on a plain NodeGroup!
01753 
01754     Errors:     We require a valid Op ptr, so will ERROR3 and return FALSE
01755                 if we don't get one (maybe this should be an ERROR2).
01756 
01757 ********************************************************************************************/
01758 NodeGroup* NodeCompound::BecomeAGroup(UndoableOperation* pUndoOp)
01759 {
01760     // we can't do a thing without an Op...
01761 //  if (pUndoOp == NULL)
01762 //  {
01763 //      ERROR3("NodeGroup::BecomeAGroup; NULL Op pointer!");
01764 //      return NULL;
01765 //  }
01766 
01767     // localise any attributes into our children, to ensure that none of our direct children
01768     // are attributes. we do this because we'll be moving all our children, and directly
01769     // moving attrs is forbidden (see DoMoveNode() for more info).
01770     BOOL    ok;
01771     
01772     if (pUndoOp)
01773         ok = pUndoOp->DoLocaliseCommonAttributes(this);
01774     else
01775         ok = LocaliseCommonAttributes(FALSE, FALSE, NULL);
01776 
01777     // create the new group node for our children.
01778     NodeGroup* pGroup = NULL;
01779     if (ok)
01780     {
01781         ALLOC_WITH_FAIL(pGroup, new NodeGroup, pUndoOp);
01782         ok = (pGroup != NULL);
01783     }
01784 
01785     // insert the new group into the tree as our right-sibling.
01786     if (ok)
01787     {
01788         if (pUndoOp)
01789             ok = pUndoOp->DoInsertNewNode(pGroup, this, NEXT, TRUE, FALSE, IsSelected(), FALSE);
01790         else
01791             pGroup->AttachNode(this, NEXT, FALSE, FALSE);
01792     }
01793 
01794     // move all our children into the new group.
01795     // some of our children may be non-optimisable attributes, which DoMoveNode() won't move.
01796     // In these cases, we non-undoably put copies of them under the new group node (which is
01797     // undoably inserted anyway, so the non-undoable stuff should be ok).
01798     if (ok)
01799     {
01800         Node* pNextKid  = NULL;
01801         Node* pThisKid  = FindFirstChild();
01802         while (ok && pThisKid != NULL)
01803         {
01804             pNextKid = pThisKid->FindNext();
01805 
01806             if (pThisKid->IsAnObject())
01807             {
01808                 if (pUndoOp)
01809                 {
01810                     CALL_WITH_FAIL(pUndoOp->DoMoveNode(pThisKid, pGroup, LASTCHILD), pUndoOp, ok)
01811                 }
01812                 else
01813                 {
01814                     pThisKid->MoveNode(pGroup, LASTCHILD);
01815                 }
01816                 if (ok && pThisKid->IsSelected())
01817                     pGroup->SetParentOfSelected(TRUE);
01818             }
01819             else if (   pThisKid->IsAnAttribute() &&
01820                         !((NodeAttribute*)pThisKid)->ShouldBeOptimized() )
01821                 CALL_WITH_FAIL(pThisKid->CopyNode(pGroup, LASTCHILD), pUndoOp, ok)
01822 
01823             pThisKid = pNextKid;
01824         }
01825     }
01826 
01827     // factor out attributes on the new group - this matches the localise call up top.
01828     if (ok)
01829     {
01830         if (pUndoOp)
01831             ok = pUndoOp->DoFactorOutCommonChildAttributes(pGroup);
01832         else
01833             ok = pGroup->FactorOutCommonChildAttributes();
01834     }
01835 
01836     // invalidate the group's bounding box.
01837     if (ok) pGroup->InvalidateBoundingRect();
01838 
01839     // finally, hide ourself.
01840     if (ok)
01841     {
01842         if (pUndoOp)
01843             ok = pUndoOp->DoHideNode(this, TRUE);
01844         else
01845         {
01846             CascadeDelete();
01847             delete this;        // Scary!
01848         }
01849     }
01850 
01851     return ok ? pGroup : NULL;
01852 }
01853 
01854 
01855 
01856 
01857 /********************************************************************************************
01858 
01859 >   virtual NodeRenderableInk* NodeCompound::GetInkNodeFromController() const
01860 
01861     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01862     Created:    16/10/2000
01863     Inputs:     -
01864     Outputs:    -
01865     Returns:    The ink node that is used by bevels, shadows and contours
01866     Purpose:    If we had a Controller node inherited class from NodeGroup then this 
01867                 function really belongs there.  But we don't, so this function only 
01868                 returns anything if your node also returns TRUE to IsAController().
01869                 If so it will return the node that is shadowed, bevelled or contoured.
01870                 
01871     SeeAlso:    
01872 
01873 ********************************************************************************************/
01874 
01875 NodeRenderableInk* NodeCompound::GetInkNodeFromController() const
01876 {
01877     return NULL;
01878 }
01879 
01880 
01881 
01882 
01883 /********************************************************************************************
01884 
01885 >   DocRect NodeCompound::GetChildBoundingRect(BOOL bIncludeAttrs = TRUE)
01886 
01887     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01888     Created:    21/09/2004
01889     Returns:    The bounding box of the children of the live effect
01890     Purpose:    Get the bounding rect of the children without updating BOundingRectangle
01891                 or IsBOundingRectValid for this node...
01892 
01893 ********************************************************************************************/
01894 
01895 DocRect NodeCompound::GetChildBoundingRect(BOOL bIncludeAttrs)
01896 {
01897     // just set it to be an empty rectangle
01898     DocRect BoundRect(0,0,0,0);
01899     
01900     Node* pNode = FindFirstChild();
01901     while (pNode!=NULL)
01902     {
01903         // Add in the bounding rect of this node with all the others
01904         if (pNode->IsBounded())
01905             BoundRect = BoundRect.Union(((NodeRenderableBounded*)pNode)->GetBoundingRect(!bIncludeAttrs));
01906 
01907         // And find the next node
01908         pNode = pNode->FindNext();
01909     }
01910 
01911     return BoundRect;
01912 }
01913 
01914 
01915 
01916 
01917 /********************************************************************************************
01918 
01919 >   virtual BOOL NodeCompound::HasEffectAttrs() const
01920 
01921     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01922     Created:    11/01/2005
01923     Returns:    TRUE if the node has effect attributes
01924                 FALSE otherwise
01925     Purpose:    Detect Effect Attributes
01926 
01927 ********************************************************************************************/
01928 
01929 BOOL NodeCompound::HasEffectAttrs() const
01930 {
01931     Node* pn = FindLastChild();
01932     while (pn && !pn->IsBounded())
01933     {
01934         if (pn->IsAnAttribute() && ((NodeAttribute*)pn)->IsEffectAttribute())
01935             return TRUE;
01936 
01937         pn = pn->FindPrevious();
01938     }
01939     return FALSE;
01940 }
01941 
01942     
01943     
01944     
01945 /********************************************************************************************
01946 
01947 >   virtual BOOL NodeCompound::ContainsEffects() const
01948 
01949     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01950     Created:    17/08/2005
01951     Returns:    TRUE if the node contains effects nodes
01952                 FALSE otherwise
01953     Purpose:    Detect Effects
01954 
01955 ********************************************************************************************/
01956 
01957 BOOL NodeCompound::ContainsEffects() const
01958 {
01959     Node* pn = FindFirstChild();
01960     while (pn)
01961     {
01962         if (pn->IsEffect())
01963             return TRUE;
01964 
01965         if (pn->IsCompoundClass() && ((NodeCompound*)pn)->ContainsEffects())
01966             return TRUE;
01967 
01968         pn = pn->FindNext();
01969     }
01970 
01971     return FALSE;
01972 }
01973 
01974     
01975     
01976     
01977 /********************************************************************************************
01978 
01979 >   void NodeCompound::TransformEffectAttrs(TransformBase& Trans)
01980 
01981     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01982     Created:    30/06/2005
01983     Inputs:     Trans - The transform that should be applied to all the children
01984     Purpose:    Transforms the Child nodes of this node. This allows fills etc to move
01985                 with the objects.
01986 
01987 ********************************************************************************************/
01988 
01989 void NodeCompound::TransformEffectAttrs(TransformBase& Trans)
01990 {
01991     // Find all Effect Attributes (in subtree after last bounded node
01992     // and transform them.
01993     Node* pn = FindLastChild();
01994     while (pn && !pn->IsBounded())
01995     {
01996         if (pn->IsAnAttribute() && ((NodeAttribute*)pn)->IsEffectAttribute())
01997             ((NodeAttribute*)pn)->Transform( Trans );
01998 
01999         pn = pn->FindPrevious();
02000     }
02001 }
02002 
02003 
02004 

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