nodeblnd.cpp

Go to the documentation of this file.
00001 // $Id: nodeblnd.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 
00099 /*
00100 */
00101 
00102 #include "camtypes.h"
00103 #include "nodeblnd.h"
00104 //#include "markn.h"
00105 //#include "mario.h"
00106 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "undoop.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 #include "ophist.h"
00110 //#include "rndrgn.h"
00111 #include "aw_eps.h"
00112 #include "cameleps.h"
00113 #include "objchge.h"
00114 #include "nodebldr.h"
00115 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 #include "blobs.h"
00117 #include "contmenu.h"
00118 #include "blndtool.h"
00119 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00120 #include "ndbldpth.h"
00121 #include "attrmap.h"
00122 
00123 //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 #include "cxftags.h"
00125 //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 #include "rechblnd.h"
00127 #include "nodershp.h"
00128 //#include "opshadow.h"
00129 //#include "opbevel.h"
00130 #include "extender.h"
00131 #include "ngcore.h"     // NameGallery, for stretching functionality
00132 #include "brshattr.h"
00133 #include "slicehelper.h"
00134 
00135 DECLARE_SOURCE("$Revision: 1688 $");
00136 
00137 CC_IMPLEMENT_DYNAMIC(NodeBlend,NodeGroup)
00138 CC_IMPLEMENT_DYNCREATE(InitBlendAction,Action)
00139 CC_IMPLEMENT_DYNAMIC(BlendRecordHandler, CamelotRecordHandler)
00140 
00141 #define new CAM_DEBUG_NEW
00142 
00143 #define NUM_DEFAULT_BLENDSTEPS 5
00144 
00145 BOOL NodeBlend::s_DefaultNotAntialiased = FALSE;
00146 NodeBlender* BlendRecordHandler::m_pLastInsertedNodeBlender = NULL;
00147 NodeBlend* BlendRecordHandler::m_pLastInsertedNodeBlend = NULL;
00148 NodeBlendPath* BlendRecordHandler::m_pLastNodeBlendPath = NULL;
00149 CProfileBiasGain BlendRecordHandler::m_LastObjectProfile;
00150 CProfileBiasGain BlendRecordHandler::m_LastAttrProfile;
00151 CProfileBiasGain BlendRecordHandler::m_LastPositionProfile;
00152 BOOL BlendRecordHandler::m_bLoadedProfiles = FALSE;
00153 
00154 /*********************************************************************************************
00155 
00156 >    NodeBlend::NodeBlend() 
00157 
00158      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00159      Created:   6/10/94
00160      Inputs:    
00161      Outputs:   
00162      Returns:   
00163      Purpose:   This constructor creates a NodeBlend linked to no other, with all status
00164                 flags false and an uninitialised bounding rectangle.           
00165             
00166      Errors:    
00167 
00168 **********************************************************************************************/
00169  
00170 NodeBlend::NodeBlend(): NodeGroup()
00171 {
00172     ResetVars();
00173 }
00174     
00175 /***********************************************************************************************
00176 
00177 >   void NodeBlend::NodeBlend
00178     (
00179         Node* ContextNode,  
00180         AttachNodeDirection Direction,  
00181         BOOL Locked = FALSE, 
00182         BOOL Mangled = FALSE,  
00183         BOOL Marked = FALSE, 
00184         BOOL Selected = FALSE, 
00185     )
00186 
00187     Author:  Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00188     Created:    6/10/94
00189     Inputs: ContextNode: Pointer to a node which this node is to be attached to.     
00190     
00191             Direction: 
00192             
00193                 Specifies the direction in which this node is to be attached to the 
00194                 ContextNode. The values this variable can take are as follows: 
00195                                   
00196                 PREV      : Attach node as a previous sibling of the context node
00197                 NEXT      : Attach node as a next sibling of the context node
00198                 FIRSTCHILD: Attach node as the first child of the context node
00199                 LASTCHILD : Attach node as a last child of the context node                               
00200                           
00201             The remaining inputs specify the status of the node: 
00202             
00203             Locked:     Is node locked ?
00204             Mangled:    Is node mangled ?
00205             Marked:     Is node marked ?
00206             Selected:   Is node selected ?
00207             
00208     Outputs:   -
00209     Returns:   - 
00210     Purpose: This method initialises the node and links it to ContextNode in the
00211              direction specified by Direction. All necessary tree links are
00212              updated.     
00213              
00214     Errors:  An assertion error will occur if ContextNode is NULL
00215 
00216 
00217 ***********************************************************************************************/
00218 
00219 NodeBlend::NodeBlend(Node* ContextNode,  
00220                      AttachNodeDirection Direction,  
00221                      BOOL Locked, 
00222                      BOOL Mangled,  
00223                      BOOL Marked, 
00224                      BOOL Selected   
00225                ):NodeGroup(ContextNode, Direction, Locked, Mangled, Marked, 
00226                 Selected) 
00227 { 
00228     ResetVars();
00229 } 
00230 
00231 /*********************************************************************************************
00232 
00233 >    void NodeBlend::ResetVars() 
00234 
00235      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00236      Created:   6/10/94
00237      Inputs:    
00238      Outputs:   
00239      Returns:   
00240      Purpose:   This sets all the member vars to the state they should be in on construction.
00241                 All constructors should call this func.
00242      Errors:    
00243 
00244 **********************************************************************************************/
00245  
00246 void NodeBlend::ResetVars()
00247 {
00248     m_NumBlendSteps         = NUM_DEFAULT_BLENDSTEPS;
00249     m_OneToOne              = FALSE;
00250     m_NotAntialiased        = s_DefaultNotAntialiased;
00251     m_ColBlendType          = COLOURBLEND_FADE;
00252     m_Tangential            = FALSE;
00253     m_BlendedOnCurve        = FALSE;
00254     m_Edit                  = EDIT_STEPS;
00255     m_DistanceEntered       = -1;
00256     m_LastEdited            = NONE;
00257     m_NumNodeBlendPaths     = 0;
00258     objectProfileProcessing = FALSE;
00259     UpdateStepDistance();
00260 }
00261 
00262 /********************************************************************************************
00263 
00264 >   virtual String NodeBlend::Describe(BOOL Plural, BOOL Verbose = TRUE)
00265 
00266     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00267     Created:    6/10/94
00268     Inputs:     Plural: Flag indicating if the string description should be plural or
00269                         singular. 
00270     Outputs:    -
00271     Retuns:     Description of the blend node 
00272     Purpose:    To return a description of the Blend object in either the singular or the 
00273                 plural. This method is called by the DescribeRange method.
00274                 
00275                 The description will always begin with a lower case letter.   
00276                 
00277     Errors:     -
00278     SeeAlso:    -
00279 
00280 ********************************************************************************************/
00281 
00282 String NodeBlend::Describe(BOOL Plural, BOOL Verbose) 
00283 {     
00284     if (Plural)
00285         return(String(_R(IDS_BLEND_DESCRP)));  
00286     else
00287         return(String(_R(IDS_BLEND_DESCRS))); 
00288 }; 
00289 
00290 
00291 /***********************************************************************************************
00292 
00293 > Node* NodeBlend::SimpleCopy()  
00294 
00295     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00296     Created:    6/10/94
00297     Inputs:     -  
00298     Outputs:    -
00299     Returns:    A copy of the node, or NULL if memory has run out 
00300          
00301     Purpose:    This method returns a shallow copy of the node with all Node pointers NULL. 
00302                 The function is virtual, and must be defined for all derived classes.  
00303                 
00304     Errors:     If memory runs out when trying to copy, then ERROR is called with an out of memory
00305                 error and the function returns NULL.                                                                      
00306                                                                                  
00307 **********************************************************************************************/
00308 
00309 Node* NodeBlend::SimpleCopy()
00310 {
00311     NodeBlend* pCopyOfNode = new NodeBlend();
00312     ERROR1IF(pCopyOfNode == NULL,NULL,_R(IDE_NOMORE_MEMORY)); 
00313     CopyNodeContents(pCopyOfNode);
00314     return (pCopyOfNode);
00315 }   
00316 
00317 /***********************************************************************************************
00318 
00319 >   void NodeBlend::CopyNodeContents(Node* pCopyOfNode)
00320 
00321     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00322     Created:    11/10/94
00323     Inputs:     pCopyOfNode - The node to copy data to
00324     Outputs:    -
00325     Returns:    -
00326     Purpose:    Copies the data from this node to pCopyOfNode by first calling the base class to get it to
00327                 copy its stuff, and then copying its own stuff
00328     Scope:      protected
00329     SeeAlso:    NodeGroup::CopyNodeContents
00330 
00331 ***********************************************************************************************/
00332 
00333 void NodeBlend::CopyNodeContents(NodeBlend* pCopyOfNode)
00334 {
00335     NodeGroup::CopyNodeContents(pCopyOfNode);
00336 
00337     // Copy member vars here
00338     pCopyOfNode->m_NumBlendSteps    = m_NumBlendSteps;
00339     pCopyOfNode->m_OneToOne         = m_OneToOne;
00340     pCopyOfNode->m_NotAntialiased   = m_NotAntialiased;
00341     pCopyOfNode->m_ColBlendType     = m_ColBlendType;
00342     pCopyOfNode->m_Tangential       = m_Tangential;
00343     pCopyOfNode->m_BlendedOnCurve   = m_BlendedOnCurve;
00344     pCopyOfNode->m_Edit             = m_Edit;
00345     pCopyOfNode->m_DistanceEntered  = m_DistanceEntered;
00346     pCopyOfNode->m_NumNodeBlendPaths = m_NumNodeBlendPaths;
00347     pCopyOfNode->m_ObjectProfile     = m_ObjectProfile;
00348     pCopyOfNode->m_AttrProfile       = m_AttrProfile;
00349     pCopyOfNode->m_PositionProfile   = m_PositionProfile;
00350 }
00351    
00352 
00353 
00354 /***********************************************************************************************
00355 >   void NodeBlend::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00356 
00357     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00358     Created:    18/12/2003
00359     Outputs:    -
00360     Purpose:    Polymorphically copies the contents of this node to another
00361     Errors:     An assertion failure will occur if NodeCopy is NULL
00362     Scope:      protected
00363                                      
00364 ***********************************************************************************************/
00365 
00366 void NodeBlend::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00367 {
00368     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
00369     ENSURE(IS_A(pNodeCopy, NodeBlend), "PolyCopyNodeContents given wrong dest node type");
00370 
00371     if (IS_A(pNodeCopy, NodeBlend))
00372         CopyNodeContents((NodeBlend*)pNodeCopy);
00373 }
00374 
00375 
00376 
00377 /********************************************************************************************
00378 
00379 >   virtual UINT32 NodeBlend::GetNodeSize() const
00380 
00381     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00382     Created:    6/10/94
00383     Inputs:     -
00384     Outputs:    -
00385     Returns:    The size of the node in bytes
00386     Purpose:    For finding the size of the node 
00387                 
00388     SeeAlso:    Node::GetSubtreeSize
00389 
00390 ********************************************************************************************/
00391 
00392 UINT32 NodeBlend::GetNodeSize() const 
00393 {     
00394     return (sizeof(NodeBlend)); 
00395 }  
00396 
00397 
00398 /********************************************************************************************
00399 
00400 >   BOOL NodeBlend::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00401                              Spread* pSpread )
00402 
00403     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00404     Created:    6/10/94
00405     Inputs:     PointerPos  - The Location of the mouse pointer at the time of the click
00406                 Click       - The type of click received (single, double, drag etc)
00407                 ClickMods   - The modifiers to the click (eg shift, control etc )
00408     Returns:    TRUE if the node claims the click as its own and FALSE if it is
00409                 not interested in the click
00410     Purpose:    Does nothing at the moment - Just returns FALSE.
00411         
00412 ********************************************************************************************/
00413 
00414 BOOL NodeBlend::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00415                         Spread* pSpread )
00416 {
00417     // we did not use the click, so let someone else try
00418     return FALSE;
00419 }
00420 
00421 /***********************************************************************************************
00422 
00423 >   BOOL NodeBlend::HidingNode()
00424 
00425     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00426     Created:    14/11/94
00427     Inputs:     -
00428     Outputs:    -
00429     Purpose:    Called whenever the node gets hidden.
00430                 It calls the Deinit() member function
00431 
00432 ***********************************************************************************************/
00433 
00434 BOOL NodeBlend::HidingNode()
00435 {
00436     // Call the base class first
00437     if (!NodeGroup::HidingNode())
00438         return FALSE;
00439 
00440     BOOL ok = TRUE;
00441     Node* pNode = FindFirstChild();
00442     while (pNode != NULL && ok)
00443     {
00444         ok = pNode->HidingNode();
00445         pNode = pNode->FindNext();
00446     }
00447 
00448     return ok;
00449 }
00450 
00451 /***********************************************************************************************
00452 
00453 >   BOOL NodeBlend::ShowingNode()
00454 
00455     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00456     Created:    14/11/94
00457     Inputs:     -
00458     Outputs:    -
00459     Purpose:    Called whenever the node gets shown after it's been hidden.
00460                 It calls the Reinit() member function
00461 
00462 ***********************************************************************************************/
00463 
00464 BOOL NodeBlend::ShowingNode()
00465 {
00466     // Call the base class first
00467     if (!NodeGroup::ShowingNode())
00468         return FALSE;
00469 
00470 // This doesn't need to be done!!!
00471 // The caller of ShowingNode recurses through the subtree by itself
00472 //  BOOL ok = TRUE;
00473 //  Node* pNode = FindFirstChild();
00474 //  while (pNode != NULL && ok)
00475 //  {
00476 //      ok = pNode->ShowingNode();
00477 //      pNode = pNode->FindNext();
00478 //  }
00479 //
00480 //  return ok;
00481     return TRUE;
00482 }
00483 
00484 /********************************************************************************************
00485 
00486 >   virtual BOOL NodeBlend::DoBecomeA(BecomeA* pBecomeA)
00487 
00488     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00489     Created:    18/10/94
00490     Inputs:     pBecomeA =  ptr to a class that contains all the info needed to become a new
00491                             type of node.
00492     Outputs:    -
00493     Returns:    TRUE if the object has been transformed, FALSE if we run out of memory
00494 
00495     Purpose:    Transforms the object into another type of object. 
00496                 This converts all its children, and replaces itself in the tree with a NodeGroup
00497     Errors:     -
00498     SeeAlso:    Node::CanBecomeA
00499 
00500 ********************************************************************************************/
00501 
00502 BOOL NodeBlend::DoBecomeA(BecomeA* pBecomeA)
00503 {
00504     if (!Reinit()) return FALSE;
00505 
00506     switch (pBecomeA->GetReason())
00507     {
00508         case BECOMEA_REPLACE:
00509         {
00510 //          ERROR2IF(pBecomeA->GetUndoOp() == NULL,FALSE,"Trying to replace a NodeBlend, but pUndoOp == NULL");
00511             UndoableOperation* pUndoOp = pBecomeA->GetUndoOp();
00512 
00513             // Make all attrs children of the child nodes
00514             if (pUndoOp)
00515             {
00516                 if (!pUndoOp->DoLocaliseCommonAttributes(this))
00517                     return FALSE;
00518             }
00519             else
00520             {
00521                 if (!LocaliseCommonAttributes())
00522                     return FALSE;
00523             }
00524 
00525             // If being repaced in the tree, deselect the node
00526             BOOL IsSelected = this->IsSelected();
00527             SetSelected(FALSE);
00528 
00529             // When becoming a new type of node AND replacing ourselves in the tree, we must first ask all the 
00530             // blender child nodes, then the rest of the children, in that order.
00531             // This is because the blender nodes generate new nodes from other children in the tree, so
00532             // they have to do their stuff before the other nodes get replaced and hence disappear.
00533 
00534             // We need a list of all non-blender child nodes
00535             std::list<Node *>   ListOfNodes; //(30);
00536 
00537             // Scan the children of the blend
00538             Node* pNode = FindFirstChild();
00539             while (pNode != NULL)
00540             {
00541                 Node* pThisNode = pNode;
00542                 pNode = pNode->FindNext();
00543 
00544                 // If this node is a blender, call its DoBecomeA() method
00545                 // otherwise add the node to the list of non-blender nodes to use later
00546 
00547                 if (IS_A(pThisNode,NodeBlender))
00548                     pThisNode->DoBecomeA(pBecomeA);
00549                 else
00550                     ListOfNodes.push_back(pThisNode);
00551             }
00552 
00553             while( !ListOfNodes.empty() )
00554             {   
00555                 // Here we must ask all the non-blender nodes to DoBecomeA()
00556                 pNode = ListOfNodes.back(); ListOfNodes.pop_back();
00557                 
00558                 // CGS:  prevent NodeShadowControllers, NodeBevelControllers and NodeCountourControllers from
00559                 // converting to paths when inside of a blend ....
00560 
00561                 if (!pNode->IsController ())
00562                 {
00563                     pNode->DoBecomeA(pBecomeA);
00564                 }
00565                 else
00566                 {
00567                     // BUT if we need them converted to paths, then do it ....
00568                     if (pBecomeA->GetInsertComplexBlendStepsAsPaths ())
00569                     {
00570                         pNode->DoBecomeA(pBecomeA);
00571                     }
00572                 }
00573             }
00574 
00575             // All the children have been delt with, so now's the time to deal with this blend node
00576 
00577             // When replacing the blend with shapes, hide the blend, create a group, and move all the children
00578             // so they become children of the new group node
00579 
00580             // Allocate a new NodeGroup node
00581             NodeGroup* pNodeGroup;
00582             ALLOC_WITH_FAIL(pNodeGroup, (new NodeGroup), pUndoOp); 
00583             if (pNodeGroup == NULL)
00584                 return FALSE;
00585 
00586             // Insert the NodeGroup where the NodeBlend used to be
00587             if (pUndoOp)
00588             {
00589                 if (!pUndoOp->DoInsertNewNode(pNodeGroup,this,NEXT,FALSE,FALSE,FALSE,FALSE))
00590                     return FALSE;
00591             }
00592             else
00593             {
00594                 pNodeGroup->AttachNode(this, NEXT);
00595             }
00596 
00597             // Select the group, but only if the caller wants us too (moulds don't, for example)
00598             if (IsSelected && pUndoOp)
00599             {
00600                 if (!pUndoOp->DoSelectNode(pNodeGroup,FALSE))
00601                     return FALSE;
00602             }
00603 
00604             // Karim 15/05/2000 - rewrote to handle non-optimising attrs.
00605             // Move our children into the new group node.
00606             //
00607             // NOTE: The IsEffectAttribute function won't work when called from ShouldBeOptimized below
00608             // because it looks along the tree for bounded nodes and this function will have removed them
00609             // all by the time that call is made.
00610             // So bInEffectArea duplicates that logic by being set as soon as any bounded object
00611             // is met as we traverse the child list. Not nice...
00612             //
00613             BOOL ok = TRUE;
00614             BOOL bInEffectArea = FALSE;
00615             Node* pNextKid  = NULL;
00616             Node* pThisKid  = FindFirstChild();
00617             while (ok && pThisKid != NULL)
00618             {
00619                 pNextKid = pThisKid->FindNext();
00620 
00621                 if (pThisKid->IsBounded())
00622                     bInEffectArea = TRUE;
00623 
00624                 // ink nodes just get moved.
00625                 if (pThisKid->IsAnObject())
00626                 {
00627                     if (pUndoOp)
00628                     {
00629                         CALL_WITH_FAIL(pUndoOp->DoMoveNode(pThisKid, pNodeGroup, LASTCHILD), pUndoOp, ok)
00630                     }
00631                     else
00632                     {
00633                         pThisKid->MoveNode(pNodeGroup, LASTCHILD);
00634                     }
00635                 }
00636                 // non-optimising attrs just get copied, as DoMoveNode() doesn't like them.
00637                 else
00638                     if (pThisKid->IsAnAttribute() &&
00639                         (!((NodeAttribute*)pThisKid)->ShouldBeOptimized() ||
00640                         bInEffectArea)
00641                         )
00642                     CALL_WITH_FAIL(pThisKid->CopyNode(pNodeGroup, LASTCHILD), pUndoOp, ok)
00643 
00644                 pThisKid = pNextKid;
00645             }
00646             if (!ok)
00647                 return FALSE;
00648 
00649             // Hide the blend node
00650             NodeHidden* pNodeHidden; 
00651             if (pUndoOp)
00652             {
00653                 if(!pUndoOp->DoHideNode(this, FALSE, &pNodeHidden, FALSE))
00654                     return FALSE;                
00655             }
00656             else
00657             {
00658                 CascadeDelete();
00659                 delete this;
00660             }
00661 
00662             // Factor out the attrs (needed after a call to DoLocaliseCommonAttributes
00663             if (pUndoOp)
00664             {
00665                 if (!pUndoOp->DoFactorOutCommonChildAttributes(pNodeGroup))
00666                     return FALSE;
00667             }
00668             else
00669             {
00670                 if (!pNodeGroup->FactorOutCommonChildAttributes())
00671                     return FALSE;
00672             }
00673         }
00674         break;
00675 
00676         case BECOMEA_PASSBACK:
00677         // the following case is executed when doing the blend of two grouped blends
00678         {
00679             // Sequentially ask the children of the blend to DoBecomeA
00680             // This is all that's required because the child objects are only passing back
00681             // the new node type, and NOT replacing themselves in the tree
00682             // This also ensures the receipient gets the list of paths in render order
00683             Node* pNode = FindFirstChild();
00684             while (pNode != NULL)
00685             {
00686                 if (!pNode->DoBecomeA(pBecomeA))
00687                     return FALSE;
00688 
00689                 pNode = pNode->FindNext();
00690             }
00691         }
00692         break;
00693 
00694         default:
00695             ERROR3_PF(("Unknown BecomeA reason %d",pBecomeA->GetReason()));
00696             break;
00697     }
00698 
00699     return TRUE;
00700 }
00701 
00702 /********************************************************************************************
00703 
00704 >   void NodeBlend::SetNumBlendSteps(UINT32 NumSteps)
00705 
00706     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00707     Created:    4/11/94
00708     Inputs:     NumSteps = num steps to set in this blend
00709     Outputs:    -
00710     Returns:    -
00711     Purpose:    Sets the number of blend steps in this blend
00712     SeeAlso:    -
00713 
00714 ********************************************************************************************/
00715 
00716 void NodeBlend::SetNumBlendSteps(UINT32 NumSteps)
00717 {
00718     m_NumBlendSteps = NumSteps;
00719 }
00720 
00721 /********************************************************************************************
00722 
00723 >   UINT32 NodeBlend::GetNumBlendSteps()
00724 
00725     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00726     Created:    4/11/94
00727     Inputs:     -
00728     Outputs:    -
00729     Returns:    Num blend steps in this blend
00730     Purpose:    Gets the number of blend steps in this blend
00731     SeeAlso:    -
00732 
00733 ********************************************************************************************/
00734 
00735 UINT32 NodeBlend::GetNumBlendSteps()
00736 {
00737     return m_NumBlendSteps;
00738 }
00739 
00740 /********************************************************************************************
00741 
00742 >   void NodeBlend::SetColourBlendType(ColourBlendType Type)
00743 
00744     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00745     Created:    17/3/95
00746     Inputs:     Type = the way colours are blended (i.e. fade, rainbow, or alt rainbow)
00747     Outputs:    -
00748     Returns:    -
00749     Purpose:    Sets up the way this blend blends colours
00750     SeeAlso:    -
00751 
00752 ********************************************************************************************/
00753 
00754 void NodeBlend::SetColourBlendType(ColourBlendType Type)
00755 {
00756     m_ColBlendType = Type;
00757 }
00758 
00759 /********************************************************************************************
00760 
00761 >   ColourBlendType NodeBlend::GetColourBlendType()
00762 
00763     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00764     Created:    17/3/95
00765     Inputs:     -
00766     Outputs:    -
00767     Returns:    The way the blend blends colours
00768     Purpose:    How exactly will this blend blend colours? Use this func to find out.
00769     SeeAlso:    -
00770 
00771 ********************************************************************************************/
00772 
00773 ColourBlendType NodeBlend::GetColourBlendType()
00774 {
00775     return m_ColBlendType;
00776 }
00777 
00778 /********************************************************************************************
00779 
00780 >   BOOL NodeBlend::IsOneToOne()
00781 
00782     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00783     Created:    29/11/94
00784     Inputs:     -
00785     Outputs:    -
00786     Returns:    TRUE if blend should be done using one-to-one mapping
00787     Purpose:    Get the state of the OneToOne flag for this blend
00788     SeeAlso:    -
00789 
00790 ********************************************************************************************/
00791 
00792 BOOL NodeBlend::IsOneToOne()
00793 {
00794     return m_OneToOne;
00795 }
00796 
00797 /********************************************************************************************
00798 
00799 >   void NodeBlend::SetOneToOne(BOOL state)
00800 
00801     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00802     Created:    29/11/94
00803     Inputs:     state = TRUE if blend should be done using one-to-one
00804     Outputs:    -
00805     Returns:    -
00806     Purpose:    Set the state of the OneToOne flag for this blend
00807     SeeAlso:    -
00808 
00809 ********************************************************************************************/
00810 
00811 void NodeBlend::SetOneToOne(BOOL state)
00812 {
00813     m_OneToOne = state;
00814 }
00815 
00816 /********************************************************************************************
00817 
00818 >   BOOL NodeBlend::IsNotAntialiased()
00819 
00820     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00821     Created:    29/11/94
00822     Inputs:     -
00823     Outputs:    -
00824     Returns:    TRUE if the blend steps should NOT be rendered anti-aliased
00825     Purpose:    Get the state of the NotAntialiased flag for this blend
00826     SeeAlso:    -
00827 
00828 ********************************************************************************************/
00829 
00830 BOOL NodeBlend::IsNotAntialiased()
00831 {
00832     return m_NotAntialiased;
00833 }
00834 
00835 /********************************************************************************************
00836 
00837 >   void NodeBlend::SetNotAntialiased(BOOL state)
00838 
00839     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00840     Created:    29/11/94
00841     Inputs:     state = TRUE if the blend steps should NOT be rendered anti-aliased
00842     Outputs:    -
00843     Returns:    -
00844     Purpose:    Set the state of the NotAntialiased flag for this blend
00845     SeeAlso:    -
00846 
00847 ********************************************************************************************/
00848 
00849 void NodeBlend::SetNotAntialiased(BOOL state)
00850 {
00851     m_NotAntialiased = state;
00852     s_DefaultNotAntialiased = state;
00853 }
00854 
00855 /********************************************************************************************
00856 
00857 >   BOOL NodeBlend::IsTangential()
00858 
00859     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00860     Created:    20/5/99
00861     Inputs:     -
00862     Outputs:    -
00863     Returns:    TRUE if it blends on a curve tangentially
00864     Purpose:    Get the state of the m_Tangential flag for this blend
00865     SeeAlso:    -
00866 
00867 ********************************************************************************************/
00868 
00869 BOOL NodeBlend::IsTangential()
00870 {
00871     return m_Tangential;
00872 }
00873 
00874 /********************************************************************************************
00875 
00876 >   void NodeBlend::SetTangential(BOOL state)
00877 
00878     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00879     Created:    29/11/94
00880     Inputs:     state = TRUE if it blends on a curve tangentially
00881     Outputs:    -
00882     Returns:    -
00883     Purpose:    Set the state of the m_Tangential flag for this blend
00884     SeeAlso:    -
00885 
00886 ********************************************************************************************/
00887 
00888 void NodeBlend::SetTangential(BOOL state)
00889 {
00890     m_Tangential = state;
00891 }
00892 
00893 
00894 /********************************************************************************************
00895 
00896 >   BOOL NodeBlend::IsOnACurve()
00897 
00898     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00899     Created:    23/9/99
00900     Inputs:     -
00901     Outputs:    -
00902     Returns:    TRUE if it blends on a curve 
00903     Purpose:    Get the state of the m_blendedoncurve flag
00904     SeeAlso:    -
00905 
00906 ********************************************************************************************/
00907 
00908 BOOL NodeBlend::IsOnACurve()
00909 {
00910     return m_BlendedOnCurve;
00911 }
00912 
00913 
00914 /********************************************************************************************
00915 
00916 >   void NodeBlend::SetBlendedOncurve(BOOL state)
00917 
00918     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00919     Created:    23/9/99
00920     Inputs:     state = TRUE if it blends on a curve 
00921     Outputs:    -
00922     Returns:    -
00923     Purpose:    Set the state of the m_BlendedOnCurve flag for this blend
00924     SeeAlso:    -
00925 
00926 ********************************************************************************************/
00927 
00928 void NodeBlend::SetBlendedOnCurve(BOOL state)
00929 {
00930     m_BlendedOnCurve = state;
00931 }
00932 
00933 
00934 /********************************************************************************************
00935 
00936 >   EditState NodeBlend::GetEditState()
00937 
00938     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00939     Created:    27/9/99
00940     Inputs:     -
00941     Outputs:    -
00942     Returns:    The current state of the m_Edit flag 
00943     Purpose:    lets us know whether to edit the number of steps, 
00944                 or the distance between steps
00945     SeeAlso:    -
00946 
00947 ********************************************************************************************/
00948 
00949 EditState NodeBlend::GetEditState()
00950 {
00951     return m_Edit;
00952 
00953 }
00954 
00955 
00956 
00957 /********************************************************************************************
00958 
00959 >   void NodeBlend::SetEditType(EditType TypeToEdit)
00960 
00961     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00962     Created:    27/9/99
00963     Inputs:     the type to edit (can be EDIT_DISTANCE or EDIT_STEPS)
00964     Outputs:    -
00965     Returns:    - 
00966     Purpose:    sets the type to edit according to which button
00967                 is selected on the infobar.  Also initialises the 
00968                 m_DistanceEntered member if it has not already been initialised.
00969                 This is in case the user selected distance edit but does not enter a 
00970                 value. In this case, if the user has not selected a previous value I 
00971                 assume that they wish to keep the value generated.
00972     SeeAlso:    -
00973 
00974 ********************************************************************************************/
00975 
00976 void NodeBlend::SetEditState(EditState State)
00977 {
00978     m_Edit = State;
00979     if (m_DistanceEntered == -1.0)
00980     {
00981         // first update in case something has changed
00982         UpdateStepDistance();
00983         m_DistanceEntered = GetStepDistance();
00984     }
00985 }
00986 
00987 
00988 /********************************************************************************************
00989 
00990 >   BOOL NodeBlend::IsChildOfGroup()
00991 
00992     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00993     Created:    21/9/99
00994     Inputs:     -
00995     Outputs:    -
00996     Returns:    TRUE if this is a child of a nodegroup, FALSE otherwise
00997     Purpose:    to determine whether this nodeblend is part of a nodegroup
00998     SeeAlso:    -
00999 
01000 ********************************************************************************************/
01001 
01002 BOOL NodeBlend::IsChildOfGroup()
01003 {
01004     Node* pParent = ((Node*)this)->FindParent(CC_RUNTIME_CLASS(NodeGroup));
01005     if (pParent == NULL)
01006     {
01007     //  ERROR3("This blend has no parent");
01008         return FALSE;
01009     }
01010     return TRUE;
01011     /*
01012     if (pParent->IS_KIND_OF(NodeBevelController) ||
01013         pParent->IS_KIND_OF(NodeContourController) ||
01014         pParent->IS_KIND_OF(NodeShadowController))
01015         return FALSE;
01016 
01017     else if (pParent->IS_KIND_OF(NodeGroup))
01018         return TRUE
01019 
01020     return FALSE;*/
01021 }
01022 
01023 
01024 /********************************************************************************************
01025 
01026 >   BOOL NodeBlend::ContainsBrushedNode()
01027 
01028     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01029     Created:    21/9/99
01030     Inputs:     -
01031     Outputs:    -
01032     Returns:    TRUE if this blend contains an object with a brush attribute
01033     Purpose:    as above
01034     SeeAlso:    -
01035 
01036 ********************************************************************************************/
01037 
01038 BOOL NodeBlend::ContainsBrushedNode()
01039 {
01040     Node* pNode = FindFirstChild();
01041     NodeAttribute* pAttr = NULL;
01042     while (pNode != NULL)
01043     {
01044         if (pNode->IsAnObject())
01045         {
01046             ((NodeRenderableInk*)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
01047             if (pAttr != NULL)
01048             {
01049                 AttrBrushType* pAttrBrush = (AttrBrushType*)pAttr;
01050                 if (pAttrBrush->GetBrushHandle() != BrushHandle_NoBrush)
01051                     return TRUE;
01052             }
01053         }
01054         pNode = pNode->FindNext();
01055     }       
01056     return FALSE;
01057 }
01058 
01059 /********************************************************************************************
01060 
01061 >   UINT32 NodeBlend::GetNumBlenders()
01062 
01063     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01064     Created:    21/11/94
01065     Inputs:     -
01066     Outputs:    -
01067     Returns:    Num child NodeBlenders for this blend
01068     Purpose:    This counts the number of child nodes that are NodeBlenders
01069     SeeAlso:    -
01070 
01071 ********************************************************************************************/
01072 
01073 UINT32 NodeBlend::GetNumBlenders()
01074 {
01075     UINT32 Count = 0;
01076     Node* pNode = FindFirstChild();
01077     while (pNode != NULL)
01078     {
01079         if (IS_A(pNode,NodeBlender)) Count++;
01080         pNode = pNode->FindNext();
01081     }
01082 
01083     return Count;
01084 }
01085 
01086 /********************************************************************************************
01087 
01088 >   BOOL NodeBlend::Reinit(BOOL ProgressBar)
01089 
01090     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01091     Created:    26/1/95
01092     Inputs:     ProgressBar = if TRUE, a progress bar will be displayed
01093     Outputs:    -
01094     Returns:    TRUE if all child blenders reinit OK
01095     Purpose:    This calls Reinit() on all child blenders.
01096     SeeAlso:    -
01097 
01098 ********************************************************************************************/
01099 
01100 BOOL NodeBlend::Reinit(BOOL ProgressBar)
01101 {
01102     Node* pNode = FindFirstChild();
01103     while (pNode != NULL)
01104     {
01105         if (IS_A(pNode,NodeBlender))
01106         {
01107             if (!((NodeBlender*)pNode)->Reinit(NULL,NULL,ProgressBar))
01108                 return FALSE;
01109         }
01110         pNode = pNode->FindNext();
01111     }
01112 
01113     return TRUE;
01114 }
01115 
01116 /********************************************************************************************
01117 
01118 >   void NodeBlend::Deinit(BOOL bNodesMayBeChanged = FALSE)
01119 
01120     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01121     Created:    6/2/95
01122     Inputs:     -
01123     Outputs:    -
01124     Returns:    -
01125     Purpose:    This calls Deinit() on all child blenders.
01126     SeeAlso:    -
01127 
01128 ********************************************************************************************/
01129 
01130 void NodeBlend::Deinit(BOOL bNodesMayBeChanged)
01131 {
01132     Node* pNode = FindFirstChild();
01133     while (pNode != NULL)
01134     {
01135         if (IS_A(pNode,NodeBlender))
01136             ((NodeBlender*)pNode)->Deinit(bNodesMayBeChanged);
01137 
01138         pNode = pNode->FindNext();
01139     }
01140 }
01141 
01142 /********************************************************************************************
01143 
01144 >   BOOL NodeBlend::PostImport()
01145 
01146     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01147     Created:    23/5/95
01148     Inputs:     -
01149     Outputs:    -
01150     Returns:    -
01151     Purpose:    This is called after the entire doc has been imported
01152                 It first deinits the blend (in case they are initialised currently), then
01153                 reinits the blend, returning the result.
01154     SeeAlso:    -
01155 
01156 ********************************************************************************************/
01157 
01158 BOOL NodeBlend::PostImport()
01159 {
01160     Deinit();
01161     InvalidateBlendBoundingRect();
01162     return Reinit(FALSE);
01163 }
01164 
01165 /********************************************************************************************
01166 
01167 >   void NodeBlend::InvalidateBlendBoundingRect()
01168 
01169     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01170     Created:    9/2/95
01171     Inputs:     -
01172     Outputs:    -
01173     Returns:    -
01174     Purpose:    This calls InvalidateBoundingRect() on all bounded children of this blend.
01175     SeeAlso:    -
01176 
01177 ********************************************************************************************/
01178 
01179 void NodeBlend::InvalidateBlendBoundingRect()
01180 {
01181     Node* pNode = FindFirstChild();
01182     while (pNode != NULL)
01183     {
01184         if (pNode->IsBounded())
01185             ((NodeRenderableBounded*)pNode)->InvalidateBoundingRect();
01186 
01187         pNode = pNode->FindNext();
01188     }
01189 }
01190 
01191 /********************************************************************************************
01192 
01193 >   DocRect NodeBlend::GetBlobBoundingRect()
01194 
01195     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01196     Created:    6/3/95
01197     Returns:    DocRect - The bounding rect of the node and its blobs
01198     Purpose:    This calls the base classes GetBlobBoundingRect(), and inflates the result by the width
01199                 of a blob
01200 
01201 ********************************************************************************************/
01202 
01203 DocRect NodeBlend::GetBlobBoundingRect()
01204 {
01205 #if !defined(EXCLUDE_FROM_RALPH)
01206     // Find the base class blob bounding rectangle
01207     DocRect Rect = NodeGroup::GetBlobBoundingRect();
01208 
01209     // inflate it by the width of a blob (plus a bit)
01210     DocRect TempRect;
01211     GetApplication()->GetBlobManager()->GetBlobRect(Rect.lo,&TempRect);
01212     INT32 Delta = ((TempRect.hi.x - TempRect.lo.x)*1)/1;
01213     Rect.Inflate(Delta);
01214 
01215     return Rect;
01216 #else
01217     return DocRect(0,0,0,0);
01218 #endif
01219 }
01220 
01221 /********************************************************************************************
01222 
01223 >   DocRect NodeBlender::GetBoundingRect(BOOL DontUseAttrs=FALSE, BOOL HitTest=FALSE)
01224 
01225     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01226     Created:    11/10/94
01227     Inputs:     DontUseAttrs - TRUE if we should ignore the nodes attributes.
01228                 Defaults to FALSE
01229                 HitTest      - TRUE if being called during HitTest
01230     Returns:    The nodes bounding rect
01231     Purpose:    if the bounding rect is valid it is returned, if not, it is recalculated
01232                 and then returned.
01233     SeeAlso:    NodeBlender::GetBlobBoundingRect
01234 
01235 ********************************************************************************************/
01236 
01237 DocRect NodeBlend::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
01238 {
01239     if (IsBoundingRectValid && !DontUseAttrs)
01240         return BoundingRectangle;
01241 
01242     // NOTE! NodeGroup:;GetBoundingRect doesn't set BoundingRectangle if DontUseAttrs is TRUE
01243     // It returns a computed value instead.
01244     // So this routine must use the return value from calling NodeGroup::GetBoundingRectangle
01245     // to return a correct value.
01246     return NodeGroup::GetBoundingRect(DontUseAttrs, HitTest);
01247 }
01248 
01249 /********************************************************************************************
01250 
01251 >   void NodeBlend::RenderObjectBlobs(RenderRegion* pRender)
01252 
01253     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01254     Created:    6/3/95
01255     Inputs:     pRender - The region to draw the blobs in
01256     Purpose:    Renders the object blobs
01257 
01258 ********************************************************************************************/
01259 
01260 void NodeBlend::RenderObjectBlobs(RenderRegion* pRegion)
01261 {
01262 #if !defined(EXCLUDE_FROM_RALPH)
01263     // Find out about the groups bounding rect
01264     DocRect BoundingRect = GetBoundingRect();
01265 
01266     // Inflate the bounds by the width of a blob
01267     DocRect TempRect;
01268     GetApplication()->GetBlobManager()->GetBlobRect(BoundingRect.lo,&TempRect);
01269     INT32 Delta = ((TempRect.hi.x - TempRect.lo.x)*3)/4;
01270     BoundingRect.Inflate(Delta);
01271 
01272     // Find out where to draw the blobs
01273     DocCoord Low  = BoundingRect.LowCorner();
01274     DocCoord High = BoundingRect.HighCorner();
01275 
01276     // Set the colours of the blobs
01277     pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB);
01278     pRegion->SetLineColour(COLOUR_NONE);
01279 
01280     // Draw all the blobs
01281     pRegion->DrawBlob(Low, BT_UNSELECTED);  
01282     pRegion->DrawBlob(High, BT_UNSELECTED); 
01283     pRegion->DrawBlob(DocCoord(Low.x, High.y), BT_UNSELECTED); 
01284     pRegion->DrawBlob(DocCoord(High.x, Low.y), BT_UNSELECTED); 
01285 
01286 /*  NodeBlendPath* pNodeBlendPath = GetNodeBlendPath();
01287     if (pNodeBlendPath)
01288         pNodeBlendPath->RenderObjectBlobs(pRegion); */
01289 #endif
01290 }
01291 
01292 
01293 /********************************************************************************************
01294 
01295 >   virtual void NodeBlend::RenderBlendBlobs(pRender)
01296 
01297     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01298     Created:    9/11/94
01299     Inputs:     pRender = render region to render into
01300     Outputs:    -
01301     Returns:    -
01302     Purpose:    This renders all the blobs associated with this blend node
01303                 This node doesn't do any rendering itself. All it does is ask its child blender nodes
01304                 to render their blend blobs
01305     SeeAlso:    -
01306 
01307 ********************************************************************************************/
01308 
01309 void NodeBlend::RenderBlendBlobs(RenderRegion* pRender)
01310 {
01311 #if !defined(EXCLUDE_FROM_RALPH)
01312     // The problem we have with this routine is that two adjacent blenders will reference the same
01313     // object, hence if we ask all blenders to render blobs for both its objects, blobs will get
01314     // rendered twice, EORing themselves off again.
01315 
01316     // So, we ask all but the last blender to just render the start object, then get the last one
01317     // to render both.
01318 
01319     Node* pLast  = NULL;
01320 
01321     // Find the last blender
01322     Node* pNode = FindFirstChild();
01323     while (pNode != NULL)
01324     {
01325         if (IS_A(pNode,NodeBlender)) pLast = pNode;
01326         pNode = pNode->FindNext();
01327     }
01328 
01329     // Render the blender blobs
01330     pNode = FindFirstChild();
01331 
01332     // altered by Diccon 3/9/99 to render the blobs of a blend on a path differently
01333     // so that it is apparent that the position of the end objects can be altered.
01334     // probably deserves a separate function but for now..
01335     if (m_BlendedOnCurve == FALSE)
01336     {
01337         while (pNode != NULL)
01338         {
01339             if (IS_A(pNode,NodeBlender))
01340             {
01341                 BOOL RenderStart = TRUE;
01342                 BOOL RenderEnd   = FALSE;
01343 
01344                 if (pNode == pLast)  RenderEnd = TRUE;
01345                 
01346                 ((NodeBlender*)pNode)->RenderBlendBlobs(pRender,RenderStart,RenderEnd);
01347             }
01348         pNode = pNode->FindNext();
01349         }
01350     }
01351     else
01352         RenderBlendOnCurveBlobs(pRender);
01353 #endif
01354 }
01355 
01356 
01357 /********************************************************************************************
01358 
01359 >   virtual void NodeBlend::RenderBlendOnCurveBlobs(RenderRegion* pRender)
01360 
01361     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01362     Created:    21/9/99
01363     Inputs:     pRender = render region to render into
01364     Outputs:    -
01365     Returns:    -
01366     Purpose:    This renders all the blobs associated with this blend node if it is 
01367                 on a curve.  It needs to render blobs on the editable nodes, i.e. if the whole
01368                 thing is on a curve then the first and last nodes are editable.  if a blend on curve
01369                 is blended to a straight blend then only the end on the curve is editable.
01370     SeeAlso:    -
01371 
01372 ********************************************************************************************/
01373 
01374 void NodeBlend::RenderBlendOnCurveBlobs(RenderRegion* pRegion)
01375 {
01376     NodeBlender* pNodeBlender = FindFirstBlender();
01377 
01378     while (pNodeBlender != NULL)
01379     {
01380         NodeBlender* pNextBlender = FindNextBlender(pNodeBlender);
01381         if (pNodeBlender->IsBlendedOnCurve())
01382         {
01383             Node* pStart = pNodeBlender->GetNodeStart();
01384             Node* pEnd = pNodeBlender->GetNodeEnd();
01385 
01386             if (pStart == NULL || pEnd == NULL)
01387             {
01388                 ERROR3("This blender has no start and end");
01389                 return;
01390             }
01391             else
01392             {
01393                 BOOL bFinish = FALSE;
01394                 Node* pNodeToRender = pStart;
01395                 DocCoord FirstPoint(0,0);
01396                 while (bFinish == FALSE)
01397                 {
01398 
01399                     if (pNodeToRender->IsABaseTextClass())
01400                     {
01401                         pRegion->SetLineColour(COLOUR_BEZIERBLOB);
01402                         pRegion->SetFillColour(COLOUR_BEZIERBLOB);
01403                     }
01404                     else
01405                     {
01406 
01407                         pRegion->SetLineColour(COLOUR_UNSELECTEDBLOB);
01408                         pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB);
01409                     }   
01410                     
01411                     // Draw a blob at the centre point
01412                     DocRect BlobSize;
01413                     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
01414                     if (pBlobMgr != NULL)
01415                     {
01416                         DocRect BoundingRect = ((NodeRenderableBounded*)pNodeToRender)->GetBoundingRect();
01417                         DocCoord Point = BoundingRect.Centre();
01418                         
01419                         // we may be blended on a circle in which case the start and end nodes will be in the same
01420                         // place.  In this case only do the first one
01421                         if (pNodeToRender == pStart)
01422                             FirstPoint = Point;
01423                         else
01424                         {
01425                             // in fact as it turns out they may not be exactly in the same place, but very close
01426                             double Distance = Point.Distance((Coord)FirstPoint);
01427                             if (Distance < 10)
01428                                 break;
01429                         }
01430                         pBlobMgr->GetBlobRect(Point, &BlobSize);
01431 
01432                         if (pNodeToRender->IsABaseTextClass())
01433                         {
01434                             pRegion->DrawBlob(Point, BT_UNSELECTED);
01435                         }
01436                             else
01437                         {
01438                             pRegion->DrawLine(DocCoord(BlobSize.hi.x, BlobSize.hi.y), DocCoord(BlobSize.lo.x, BlobSize.lo.y));
01439                             pRegion->DrawLine(DocCoord(BlobSize.lo.x, BlobSize.hi.y), DocCoord(BlobSize.hi.x, BlobSize.lo.y));
01440                             pRegion->DrawPixel(DocCoord(BlobSize.hi.x, BlobSize.lo.y));
01441                             pRegion->DrawPixel(DocCoord(BlobSize.lo.x, BlobSize.lo.y));
01442                         }
01443                     }   
01444                     if (pNodeToRender == pStart && pNextBlender == NULL)
01445                         pNodeToRender = pEnd;
01446                     else
01447                         bFinish = TRUE;
01448                 }
01449             } // end while
01450         }  // End if on curve
01451         pNodeBlender = pNextBlender;
01452     }
01453     return;
01454 }
01455     
01456 
01457 
01458     /*
01459     NodeBlender* pFirstBlender = NULL;
01460     NodeBlender* pLastBlender = NULL;
01461 
01462     BOOL ok = FindFirstAndLastBlenders(&pFirstBlender, &pLastBlender);
01463 
01464     if (!ok)
01465     {
01466         ERROR3("Couldn't get first and last blenders");
01467         return;
01468     }
01469 
01470     Node* pFirstNode = NULL;
01471     Node* pLastNode = NULL;
01472 
01473     ok = GetStartAndEndNodes(&pFirstNode, &pLastNode);
01474     if (!ok)
01475         {
01476         ERROR3("Couldn't get first and last nodes");
01477         return;
01478     }
01479 
01480     // aah, the poetry of C++
01481     NodeBlender* pBlenderToRender = pFirstBlender;
01482     Node* pNodeToRender = pFirstNode;
01483     if (pBlenderToRender == NULL || pNodeToRender == NULL)
01484         return;
01485     BOOL bFinish = FALSE;
01486     while (bFinish == FALSE)
01487     {
01488         if (pBlenderToRender->IsBlendedOnCurve())
01489         {
01490             // if it is a regular shape then that class can do it for us
01491             if (IS_A(pNodeToRender, NodeRegularShape))
01492                 ((NodeRegularShape*)pNodeToRender)->RenderObjectBlobs(pRegion);
01493             
01494             // if its a group or other type we need to do it by hand.  If it is text then we want
01495             // a red rectangle instead of a black cross
01496             else if (pNodeToRender->IsCompound())
01497             {
01498                 if (pNodeToRender->IsABaseTextClass())
01499                 {
01500                     pRegion->SetLineColour(COLOUR_BEZIERBLOB);
01501                     pRegion->SetFillColour(COLOUR_BEZIERBLOB);
01502                 }
01503                 else
01504                 {
01505                     pRegion->SetLineColour(COLOUR_UNSELECTEDBLOB);
01506                     pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB);
01507                 }
01508                     
01509                 // Draw a blob at the centre point
01510                 DocRect BlobSize;
01511                 BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
01512                 if (pBlobMgr != NULL)
01513                 {
01514                     DocRect BoundingRect = ((NodeRenderableBounded*)pNodeToRender)->GetBoundingRect();
01515                     DocCoord Point = BoundingRect.Centre();
01516                     pBlobMgr->GetBlobRect(Point, &BlobSize);
01517 
01518                     if (pNodeToRender->IsABaseTextClass())
01519                     {
01520                         pRegion->DrawBlob(Point, BT_UNSELECTED);
01521                     }
01522                     else
01523                     {
01524                         pRegion->DrawLine(DocCoord(BlobSize.hix, BlobSize.hiy), DocCoord(BlobSize.lox, BlobSize.loy));
01525                         pRegion->DrawLine(DocCoord(BlobSize.lox, BlobSize.hiy), DocCoord(BlobSize.hix, BlobSize.loy));
01526                         pRegion->DrawPixel(DocCoord(BlobSize.hix, BlobSize.loy));
01527                         pRegion->DrawPixel(DocCoord(BlobSize.lox, BlobSize.loy));
01528                     }
01529                 }
01530             }
01531         }
01532         if (pNodeToRender == pFirstNode)
01533                 pNodeToRender = pLastNode;
01534             else
01535                 bFinish = TRUE;
01536             
01537     }
01538 
01539 }*/
01540 
01541 
01542 /********************************************************************************************
01543 
01544 > BOOL NodeBlend::IsPointOverBlob(  DocCoord* pPointerPos,
01545                                     BlendPath** ppBlendPath,
01546                                     INT32* pIndex,
01547                                     BOOL* pAStart,
01548                                     UINT32* pRemapRef)
01549 
01550     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01551     Created:    10/11/94
01552     Inputs:     pPointerPos = point to check against
01553                 ppBlendPath= ptr to place to store blend path ptr if a match is found
01554                 pIndex     = ptr to place to store index if match is found
01555                 pAStart    = ptr to place to store bool indicating start or end object flag
01556                 pRemapRef  = ptr to place remap ref that's passed into NodeBlend::Remap()
01557     Outputs:    *ppBlendPath & *pIndex & *pAStart & *pRemapRef updated if TRUE returned, undefined otherwise
01558     Returns:    TRUE if match  found, FALSE otherwise
01559     Purpose:    This asks all its child blenders if the point given lies on a selected blob.
01560                 If a match is found, the ptr to the blend path and index to blob element is returned.
01561                 Also, if found,*pAStart = TRUE if blob belongs to start node, FALSE if end node.
01562                 Also, if found,*pRemapRef contains a value that should be passed into NodeBlend::Remap()
01563 
01564                 Markn 26-5-99:  Modified so that if the function returns TRUE, *pPointerPos is updated
01565                                 so that it contains the coords of the blob it is over
01566     SeeAlso:    -
01567 
01568 ********************************************************************************************/
01569 
01570 BOOL NodeBlend::IsPointOverBlob(DocCoord* pPointerPos,
01571                                 BlendPath** ppBlendPath,
01572                                 INT32* pIndex,
01573                                 BOOL* pAStart,
01574                                 UINT32* pRemapRef)
01575 {
01576     if (pPointerPos == NULL)
01577         return FALSE;
01578 
01579 #if !defined(EXCLUDE_FROM_RALPH)
01580     BOOL Found = FALSE;
01581     Node* pNode = FindFirstChild();
01582     while (pNode != NULL && !Found)
01583     {
01584         if (IS_A(pNode,NodeBlender))
01585         {
01586             Found = ((NodeBlender*)pNode)->IsPointOverBlob(pPointerPos,ppBlendPath,pIndex,pAStart);
01587             if (Found) *pRemapRef = pNode->GetTag();
01588         }
01589         pNode = pNode->FindNext();
01590     }
01591 
01592     return Found;
01593 #else
01594     return FALSE;
01595 #endif
01596 }
01597 
01598 /********************************************************************************************
01599 
01600 > BOOL NodeBlend::Remap(UINT32 RemapRef,DocCoord PosStart,DocCoord PosEnd,
01601                         DocCoord * pInvPosStart,DocCoord * pInvPosEnd)
01602 
01603     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01604     Created:    10/11/94
01605     Inputs:     RemapRef    = special remap ref value allowing blend to identify blender to remap
01606                 PosStart    = Coord of element to remap start node to
01607                 PosEnd      = Coord of element to remap end node to
01608                 pInvPosStart= ptr to place to put the coord to use to invert the start mapping
01609                 pInvPosEnd  = ptr to place to put the coord to use to invert the end mapping
01610     Outputs:    -
01611     Returns:    TRUE if remapped, FALSE otherwise
01612     Purpose:    This will ask the child blender referenced by RemapRef to remap its blend paths with the given
01613                 params.
01614                 If the child blender manages to remap a pair of blend paths, then this will return
01615                 TRUE.
01616     SeeAlso:    -
01617 
01618 ********************************************************************************************/
01619 
01620 BOOL NodeBlend::Remap(UINT32 RemapRef,DocCoord PosStart,DocCoord PosEnd,DocCoord* pInvPosStart,DocCoord* pInvPosEnd)
01621 {
01622     BOOL Mapped = FALSE;
01623     Node* pNode = FindFirstChild();
01624     while (pNode != NULL && !Mapped)
01625     {
01626         if (pNode->GetTag() == RemapRef && IS_A(pNode,NodeBlender))
01627             Mapped = ((NodeBlender*)pNode)->Remap(PosStart,PosEnd,pInvPosStart,pInvPosEnd);
01628         pNode = pNode->FindNext();
01629     }
01630 
01631     return Mapped;
01632 }
01633 
01634 //----------------------------------------------------------------------------------
01635 //----------------------------------------------------------------------------------
01636 //----------------------------------------------------------------------------------
01637 // Export code...
01638 
01639 /*********************************************************************************************
01640 
01641 >    void NodeBlend::PreExportRender(RenderRegion* pRegion)
01642 
01643      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01644      Created:   28/11/94
01645      Inputs:    pRegion = ptr to the export render region to export to
01646      Outputs:   
01647      Returns:   
01648      Purpose:   Called before this node or any of its children have been rendered to the export region.
01649                 This outputs the "start blend" command.
01650                 Supports ArtWorks EPS and Camelot EPS
01651      Errors:    
01652 
01653 **********************************************************************************************/
01654  
01655 void NodeBlend::PreExportRender(RenderRegion* pRegion)
01656 {
01657 #ifdef DO_EXPORT
01658     if (IS_A(pRegion, CamelotEPSRenderRegion))
01659         // We ust want the paths in EPS.
01660         return;
01661 
01662     if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion))
01663     {
01664         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
01665 
01666         if (pRegion->IS_KIND_OF(CamelotEPSRenderRegion))
01667         {
01668             // Output the Camelot format for blends
01669             pDC->OutputValue(INT32(0));             // Reserved INT32 1
01670             pDC->OutputValue(INT32(0));             // Reserved INT32 2
01671             pDC->OutputValue(INT32(0));             // Reserved INT32 3
01672             pDC->OutputValue(INT32(m_ColBlendType));    // Colour blend type (fade, rainbow or alt rainbow)
01673             pDC->OutputValue(INT32(m_OneToOne));        // The "blend using a one to one mapping" flag
01674             pDC->OutputValue(INT32(m_NotAntialiased));  // The "don't antialias blend steps" flag
01675             pDC->OutputValue(INT32(m_NumBlendSteps));   // Num blend steps
01676             pDC->OutputValue(INT32(1));             // Version (Important that this is the last param!!!)
01677             pDC->OutputToken(_T("csbd"));               // Camelot "start blend" token
01678             pDC->OutputNewLine();
01679         }
01680         else 
01681         {
01682             // Use a cached flag set up by the next call to IsArtWorksEPSCompatible()
01683             // The same function is called by every child blender, so we don't want to do this (possibly)
01684             // time consuming task more than once per blend.
01685             m_AWEPSCompatibleCache = FALSE;
01686 
01687             if (IsArtWorksEPSCompatible())
01688             {
01689                 // Output the AW format for blends, if this blend is ArtWorks-compatible
01690                 pDC->OutputValue(INT32(1));             // Version
01691                 pDC->OutputValue(INT32(0));             // Expanded flag
01692                 pDC->OutputValue(GetNumInkObjects());   // Num objects we are blending
01693                 pDC->OutputToken(_T("asbd"));               // ArtWorks "start blend" token
01694                 pDC->OutputNewLine();
01695             }
01696         }
01697     }
01698 #endif
01699 }
01700 
01701 /*********************************************************************************************
01702 
01703 >    BOOL NodeBlend::ExportRender(RenderRegion* pRegion)
01704 
01705      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01706      Created:   28/11/94
01707      Inputs:    pRegion = ptr to the export render region to export to
01708      Outputs:   
01709      Returns:   TRUE if ok, FALSE if something went wrong
01710      Purpose:   Called afterCamelotEPSFilter this node and all of its children have been rendered to the export region.
01711                 This outputs the "end blend" command.
01712                 Supports ArtWorks EPS and Camelot EPS
01713      Errors:    
01714 
01715 **********************************************************************************************/
01716  
01717 BOOL NodeBlend::ExportRender(RenderRegion* pRegion) 
01718 {
01719 #ifdef DO_EXPORT
01720     if (IS_A(pRegion, CamelotEPSRenderRegion))
01721         // We just want the paths in EPS.
01722         return FALSE;
01723 
01724     if (pRegion->IS_KIND_OF(ArtWorksEPSRenderRegion))
01725     {
01726         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
01727 
01728         if (pRegion->IS_KIND_OF(CamelotEPSRenderRegion))
01729         {
01730             pDC->OutputToken(_T("cebd"));               // Camelot "end blend" token
01731             pDC->OutputNewLine();
01732         }
01733         else if (IsArtWorksEPSCompatible())
01734         {
01735             pDC->OutputToken(_T("aebd"));               // ArtWorks "end blend" token
01736             pDC->OutputNewLine();
01737         }
01738         // Tell caller we rendered ourselves ok
01739         return TRUE;
01740     }
01741 #endif
01742     // Render this node in the normal way
01743     return FALSE;
01744 }
01745 
01746 
01747 /*********************************************************************************************
01748 
01749 >    BOOL NodeBlend::IsArtWorksEPSCompatible()
01750 
01751      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01752      Created:   28/11/94
01753      Inputs:    
01754      Outputs:   
01755      Returns:   TRUE if this blend is AW compatible
01756      Purpose:   This asks all the child node blenders if they are AW compatible, and
01757                 returns TRUE if ALL of them are AW compatible.
01758                 If the m_AWEPSCompatibleCache flag is TRUE, the current setting of m_AWEPSCompatible
01759                 is returned, else it is recalculated (setting m_AWEPSCompatibleCache to TRUE in the process)
01760 
01761                 A NodeBlend is only AWEPS compatible if the colour blend type is FADE (i.e. not rainbow
01762                 or alt rainbow)
01763 
01764                 See NodeBlender::IsArtWorksEPSCompatible() for the other things that must be true for this
01765                 blend to be AWEPS compatible
01766      Errors:    
01767      SeeAlso:   NodeBlender::IsArtWorksEPSCompatible()
01768 
01769 **********************************************************************************************/
01770  
01771 BOOL NodeBlend::IsArtWorksEPSCompatible()
01772 {
01773     if (!m_AWEPSCompatibleCache)
01774     {
01775         m_AWEPSCompatible = (m_ColBlendType == COLOURBLEND_FADE);
01776         Node* pNode = FindFirstChild();
01777 
01778         while (pNode != NULL && m_AWEPSCompatible)
01779         {
01780             if (IS_A(pNode,NodeBlender))
01781                 m_AWEPSCompatible = ((NodeBlender*)pNode)->IsArtWorksEPSCompatible();
01782 
01783             pNode = pNode->FindNext();
01784         }
01785 
01786         m_AWEPSCompatibleCache = TRUE;
01787     }
01788 
01789     return m_AWEPSCompatible;
01790 }
01791 
01792 /*********************************************************************************************
01793 
01794 >    INT32 NodeBlend::GetNumInkObjects()
01795 
01796      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01797      Created:   28/11/94
01798      Inputs:    
01799      Outputs:   
01800      Returns:   Returns the number of child ink objects that are NOT blender nodes
01801      Purpose:   Used by the AW EPS blend export code to store the number of shapes this blend is blending.
01802      Errors:    
01803 
01804 **********************************************************************************************/
01805  
01806 INT32 NodeBlend::GetNumInkObjects()
01807 {
01808     INT32  Count = 0;
01809     Node* pNode = FindFirstChild();
01810 
01811     while (pNode != NULL)
01812     {
01813         if (!IS_A(pNode,NodeBlender) && pNode->IS_KIND_OF(NodeRenderableInk))
01814             Count++;
01815 
01816         pNode = pNode->FindNext();
01817     }
01818 
01819     return Count;
01820 }
01821     
01822 //----------------------------------------------------------------------------
01823 
01824 /********************************************************************************************
01825 
01826 >   virtual BOOL NodeBlend::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState = TRUE,
01827                                                             BOOL DoPreTriggerEdit = TRUE)
01828 
01829     Author:     Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>; Karim 20/01/2000
01830     Created:    6/5/95
01831     Inputs:     pParam               = describes the way an op wants to change the node
01832                 SetOpPermissionState = if TRUE the Op permission state of this node will be set according to
01833                                         the outcome of the call
01834                 DoPreTriggerEdit     = if TRUE then calls NameGallery::PreTriggerEdit.
01835                                        *Must* be TRUE if the calling Op may make any nodes
01836                                        change their bounds, eg move, line width, cut.
01837                                        Use TRUE if unsure.
01838 Outputs:    -
01839     Returns:    TRUE means the node and all its parents are happy with this op, FALSE means don't do it
01840     Purpose:    In this instance, the func gives the blend a chance to stop an op from happening to one
01841                 of its children.
01842 
01843                 Current implementation specs that blends will only allow you to change the appearence of a blend's
01844                 child (e.g. change colour,translate, edit path).
01845 
01846                 This must be called *before* the op is performed.
01847     SeeAlso:    Node::AllowOp(),GetOpPermission(),SetOpPermission();
01848 
01849 ********************************************************************************************/
01850 
01851 BOOL NodeBlend::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState,
01852                                                 BOOL DoPreTriggerEdit)
01853 {
01854     ERROR2IF(pParam==NULL,FALSE,"NodeBlend::AllowOp() - pParam==NULL");
01855 
01856     // clean out the calling-child ptr, so it doesn't get passed around unintentionally.
01857     pParam->SetCallingChild(NULL);
01858 
01859     // decide if we allow it ... (and if we'll still exist after the op)
01860     BOOL allowed=TRUE;
01861     ObjChangeFlags Flags=pParam->GetChangeFlags();
01862     if (pParam->GetDirection()==OBJCHANGE_CALLEDBYCHILD)
01863     {
01864         if  (Flags.DeleteNode || Flags.MultiReplaceNode || Flags.MoveNode)
01865         {
01866             pParam->SetReasonForDenial(_R(IDS_BLEND_CANT_OP_ON_CHILDREN));
01867             allowed=FALSE;
01868         }
01869     }
01870 
01871     // if we allow it, (and our parents weren't calling us) see if our parents do ...
01872     if (allowed && pParam->GetDirection()!=OBJCHANGE_CALLEDBYPARENT && Parent!=NULL)
01873     {
01874         ObjChangeDirection OldDirection=pParam->GetDirection();
01875         pParam->SetCallingChild(this);
01876         pParam->SetDirection(OBJCHANGE_CALLEDBYCHILD);
01877         allowed=Parent->AllowOp(pParam,SetOpPermissionState,DoPreTriggerEdit);
01878         pParam->SetDirection(OldDirection);
01879     }
01880 
01881     if (allowed && pParam->GetDirection() == OBJCHANGE_CALLEDBYCHILD)
01882     {
01883         // Ilan 5/4/00
01884         // Inform attrs attached at compound level that children have changed
01885         UndoableOperation* pChangeOp = pParam->GetOpPointer();
01886         BOOL InformGeomLinkedAttrs = SetOpPermissionState && pChangeOp && pChangeOp->MayChangeNodeBounds();
01887         if(InformGeomLinkedAttrs)
01888         {
01889             NodeAttribute* pNA = FindFirstGeometryLinkedAttr();
01890             while(pNA)
01891             {
01892                 pNA->LinkedNodeGeometryHasChanged(pChangeOp);
01893                 pNA = pNA->FindNextGeometryLinkedAttr();
01894             }
01895         }
01896     }
01897 
01898     // if setting permissions ... (and possibly cause post-op code to be called)
01899     if (SetOpPermissionState)
01900     {
01901         if (allowed)
01902         {
01903             if (Parent!=NULL)
01904                 Parent->SetOpPermission(PERMISSION_ALLOWED);
01905 
01906             // if post process required, ensure our OnChildChange is called (by setting permission on ourself),
01907             // insert an inverse action in the undo
01908             if (pParam->GetDirection()==OBJCHANGE_CALLEDBYCHILD || Flags.Attribute || Flags.RegenerateNode)
01909             {
01910                 SetOpPermission(PERMISSION_ALLOWED);
01911                 UndoableOperation* pOp=pParam->GetOpPointer();
01912                 if (pOp!=NULL)
01913                 {
01914                     if (allowed) allowed=(InitBlendAction::Init(pOp,pOp->GetUndoActionList(),this)!=AC_FAIL);
01915                     if (allowed) allowed=pOp->DoInvalidateNodeRegion(this, TRUE, FALSE, FALSE, !pParam->GetRetainCachedData());
01916                 }
01917             }
01918         }
01919         else
01920             SetOpPermission(PERMISSION_DENIED,TRUE);
01921     }
01922 
01923     // if the op was allowed, inform our children by calling their AllowOp()s
01924     // this must be done after we've inserted our undo actions so that any child undo actions will be undone first!
01925     if (allowed && pParam->GetDirection()!=OBJCHANGE_CALLEDBYCHILD)
01926         AllowOp_AccountForCompound(pParam, SetOpPermissionState, DoPreTriggerEdit);
01927 
01928     if (allowed && Flags.Attribute && SetOpPermissionState)
01929         Deinit();
01930 
01931     // if we're ok so far and were asked to do a PreTriggerEdit, then
01932     // determine whether the Op may change the bounds of some nodes.
01933     // If it may, then call NameGallery::PreTriggerEdit.
01934     if (allowed && DoPreTriggerEdit)
01935     {
01936         // if the Op is non-NULL then query its MayChangeNodeBounds() method.
01937         UndoableOperation* pChangeOp = pParam->GetOpPointer();
01938         if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds())
01939         {
01940             if (NameGallery::Instance())
01941                 allowed = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this);
01942         }
01943     }
01944 
01945     // return result (directly, or indirectly via a child AllowOp()) to op
01946     return allowed;
01947 }
01948 
01949 /********************************************************************************************
01950 
01951 >   virtual ChangeCode NodeBlend::OnChildChange(ObjChangeParam* pParam)
01952 
01953     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01954     Created:    06/02/95
01955     Inputs:     pParam  = pointer to a object change parameter class
01956     Returns:    CC_OK       if we have successfully processed the change.
01957                 CC_FAIL     if we cannot handle this particular change and must prevent the
01958                             child from continuing
01959     Purpose:    This function should be overridden in derived object classes.
01960                 Composite objects can use this function to respond to one of their children
01961                 undergoing a change. They should return CC_FAIL whenever they are unable to
01962                 cope with the change.
01963 
01964                 Blends deinitialise, then reinitialise themselves.  Hopefully that should do the trick.
01965 
01966     SeeAlso:    WarnParentOfChange(),AllowOp();
01967 
01968 ********************************************************************************************/
01969 
01970 ChangeCode NodeBlend::OnChildChange(ObjChangeParam* pParam)
01971 {
01972     ERROR2IF(pParam == NULL,CC_FAIL,"pParam == NULL");
01973 
01974     ObjChangeType cType  = pParam->GetChangeType();
01975     ObjChangeFlags cFlags = pParam->GetChangeFlags();
01976 
01977     if (cType == OBJCHANGE_FINISHED)
01978     {
01979         UndoableOperation* pUndoOp = pParam->GetOpPointer();
01980 
01981 //      Node* pNodeChanging = pParam->GetChangeObj();
01982         UINT32 NBPCounter = 0;
01983         NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(NBPCounter++);
01984 //      BOOL NodeBlendPathEdited = (pNodeChanging != NULL && pNodeChanging == pNodeBlendPath);
01985 
01986 
01987         // if the op has finished, and we're either the parent of a selected node OR an attribute 
01988         // was applied OR our node blend path has been edited, deinit the blend in an undoable way
01989 
01990         while (pNodeBlendPath != NULL)
01991         {
01992             pNodeBlendPath->OnEdited(pParam);
01993             if (!TransformBlendEndObjects(pUndoOp))
01994                 return CC_FAIL;
01995             pNodeBlendPath = GetNodeBlendPath(NBPCounter++);    
01996         }   
01997 
01998         if (pUndoOp != NULL)
01999         {
02000             if (InitBlendAction::Init(pUndoOp,pUndoOp->GetUndoActionList(),this, FALSE, FALSE, cFlags.RegenerateNode) == AC_FAIL)
02001                 return CC_FAIL;
02002 
02003             if (!pUndoOp->DoInvalidateNodeRegion(this, TRUE, FALSE, FALSE, !pParam->GetRetainCachedData()))
02004                 return CC_FAIL;
02005         }
02006         else
02007         {
02008             // If no undo op ptr, deinit the blend in a non-undoable way
02009             InvalidateBlendBoundingRect();
02010             Deinit(cFlags.RegenerateNode);
02011 
02012             DocRect rect = GetUnionBlobBoundingRect();
02013             Document* pDoc = (Document*)FindOwnerDoc();
02014             Spread* pSpread = FindParentSpread();
02015             if (pDoc != NULL && pSpread != NULL && pDoc->IS_KIND_OF(Document))
02016                 pDoc->ForceRedraw(pSpread, rect, FALSE, this);
02017         }
02018     
02019 
02020     }
02021 
02022     return CC_OK;
02023 }
02024 
02025 
02026 /********************************************************************************************
02027 
02028 >   virtual BOOL NodeBlend::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
02029 
02030     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02031     Created:    12/5/95
02032     Inputs:     pSpread     The spread in which things are happening
02033                 PointerPos  The Location of the mouse pointer at the time of the click
02034                 pMenu       The menu to which items should be added
02035     Returns:    BOOL - TRUE if the node claims the click as its own and FALSE if it is
02036                 not interested in the click
02037     Purpose:    Allows the Blend to respond to pop up menu clicks on itself.
02038 
02039 ********************************************************************************************/
02040 
02041 BOOL NodeBlend::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
02042 {
02043 //WEBSTER-ranbirr-01/12/96  
02044 #ifndef WEBSTER
02045 
02046 #if !defined(EXCLUDE_FROM_RALPH)
02047     BOOL ok = TRUE;
02048     
02049     ok = ok && pMenu->BuildCommand(TOOL_OPTOKEN_BLEND,      TRUE);
02050 //  ok = ok && pMenu->BuildCommand(OPTOKEN_ADDBLENDPATH,    TRUE);
02051 //  ok = ok && pMenu->BuildCommand(OPTOKEN_REMOVEBLEND,     FALSE);
02052 //  ok = ok && pMenu->BuildCommand(OPTOKEN_BLENDONETOONE,   FALSE);
02053 //  ok = ok && pMenu->BuildCommand(OPTOKEN_BLENDANTIALIAS,  TRUE);
02054 
02055     return ok;
02056 #else
02057     return FALSE;
02058 #endif
02059 
02060 #else   //webster
02061     return FALSE;
02062 
02063 #endif //webster
02064 }
02065 
02066 /********************************************************************************************
02067 
02068 >   NodeBlendPath*  NodeBlend::GetNodeBlendPath(UINT32 Index)
02069 
02070     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02071     Created:    29/4/99, changed DY 23/9/99
02072     Inputs:     Index - number of the NodeBlendPath that you want
02073     Returns:    Ptr to the NodeBlendPath associated with this blend, or NULL if it aint got one
02074     Purpose:    Finds the path the blend with try and follow, if it's got one
02075 
02076 ********************************************************************************************/
02077 
02078 NodeBlendPath*  NodeBlend::GetNodeBlendPath(UINT32 Index)
02079 {
02080     Node* pNode = FindFirstChild();
02081     UINT32 NBPCounter = 0;
02082     while (pNode)
02083     {
02084         if (IS_A(pNode,NodeBlendPath))
02085         {   
02086             //if (((NodeBlendPath*)pNode)->GetPathIndex() == Index)
02087             if (NBPCounter == Index)
02088                 return (NodeBlendPath*)pNode;
02089 
02090             NBPCounter++;
02091         }
02092 
02093         pNode = pNode->FindNext();
02094     }
02095 
02096     return NULL;
02097 }
02098 
02099 /********************************************************************************************
02100 
02101 >   BOOL NodeBlend::BlendAlongThisPath(NodePath* pPath,CCAttrMap* pAttrMap,UndoableOperation* pUndoOp);
02102 
02103     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02104     Created:    29/4/99
02105     Inputs:     pPath   = ptr to the definition of the path to blend alone
02106                 pAttrMap = ptr to attrs to apply to given path (can be NULL)
02107                 pUndoOp = ptr to undo op performing th operation.
02108     Returns:    TRUE if all went to plan
02109                 FALSE otherwise
02110     Purpose:    Makes this blend object blend along the given path
02111 
02112                 It creates a NodeBlendPath from the given NodePath, and inserts it a
02113                 the last child of the blend
02114 
02115 ********************************************************************************************/
02116 
02117 BOOL NodeBlend::BlendAlongThisPath(NodePath* pNodePath,CCAttrMap* pAttrMap,UndoableOperation* pUndoOp, BOOL BlendPathOnTop)
02118 {
02119     ERROR2IF(pUndoOp   == NULL,FALSE,"I need an undoable op to perform this task");
02120     ERROR2IF(pNodePath == NULL,FALSE,"Er, where is the path then?");
02121 
02122     BOOL ok = TRUE;
02123     // DY at this point there should only be one nodeblendpath, hence the zero index
02124     NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(0);
02125     if (pNodeBlendPath != NULL)
02126         ok = pUndoOp->DoHideNode(pNodeBlendPath,TRUE);
02127 
02128     if (ok) pNodeBlendPath = new NodeBlendPath();
02129     if (ok) ok = (pNodeBlendPath != NULL);
02130     if (ok) ok = pNodeBlendPath->CopyNodePath(pNodePath);
02131     if (ok) ok = pUndoOp->DoLocaliseCommonAttributes(this);
02132     
02133     if (ok)  //DY 16/9/99, previously blendpath was always inserted as last child
02134     {
02135         if (BlendPathOnTop)
02136             ok = pUndoOp->DoInsertNewNode(pNodeBlendPath,this,LASTCHILD,FALSE,FALSE,FALSE,FALSE);
02137         else
02138             ok = pUndoOp->DoInsertNewNode(pNodeBlendPath,this,FIRSTCHILD,FALSE,FALSE,FALSE,FALSE);
02139     }
02140 
02141     if (ok) ok = pNodeBlendPath->ApplyAttributes(pAttrMap);
02142     if (ok) ok = pUndoOp->DoFactorOutCommonChildAttributes(this);
02143 
02144     if (ok) ok = InitBlenderPathProportions(pUndoOp);
02145     if (ok) ok = TransformBlendEndObjects(pUndoOp);
02146 
02147 //  Dont' need to do these two steps as the call to AllowOp() will take care of these things.
02148 //  Deinit();
02149 //  InvalidateBlendBoundingRect();
02150 
02151 
02152     return ok;
02153 }
02154 
02155 BOOL NodeBlend::InitBlenderPathProportions(UndoableOperation* pUndoOp)
02156 {
02157     BOOL ok = TRUE;
02158 
02159     UINT32 NumBlenders = GetNumBlenders();
02160     UINT32 CurBlender  = 0;
02161     double CurProp = 0.0;
02162     double PropIncrement = 1.0 / NumBlenders;
02163     NodeBlender* pNodeBlender = FindFirstBlender();
02164     while (ok && pNodeBlender != NULL)
02165     {
02166         CurBlender++;
02167         pNodeBlender->SetBlendedOnCurve(TRUE);
02168         pNodeBlender->SetProportionOfPathDistStart(CurProp);
02169         CurProp += PropIncrement;
02170         pNodeBlender->SetProportionOfPathDistEnd(CurProp);
02171         pNodeBlender = FindNextBlender(pNodeBlender);
02172     
02173     }
02174 
02175     return ok;
02176 }
02177 
02178 BOOL NodeBlend::RotateBlendEndObjectsBack(UndoableOperation* pUndoOp)
02179 {
02180     BOOL ok = TRUE;
02181 
02182     Node* pLast = NULL;
02183     NodeBlender* pNodeBlender = FindFirstBlender();
02184     while (ok && pNodeBlender != NULL)
02185     {
02186         NodeRenderableInk* pStart = pNodeBlender->GetNodeStart();
02187         NodeRenderableInk* pEnd   = pNodeBlender->GetNodeEnd();
02188 
02189         if (pStart && pStart != pLast)
02190         {
02191             double AngleChange = -pNodeBlender->GetAngleStart();
02192             ok = TransformNodeToPoint(pStart,NULL,pUndoOp,AngleChange);
02193             if (ok && pUndoOp && AngleChange != 0.0)
02194             {
02195                 ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLESTART);
02196                 ChangeParam.m_NewAngleStart = 0.0;
02197                 ActionCode Ac = ChangeBlenderAction::Init(  pUndoOp,pUndoOp->GetUndoActionList(),
02198                                                             pNodeBlender,ChangeParam);
02199                 ok = (Ac !=AC_FAIL);
02200             }
02201         }
02202 
02203         if (pEnd && ok)
02204         {
02205             double AngleChange = -pNodeBlender->GetAngleEnd();
02206             ok = TransformNodeToPoint(pEnd,NULL,pUndoOp,AngleChange);
02207             if (ok && pUndoOp && AngleChange != 0.0)
02208             {
02209                 ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLEEND);
02210                 ChangeParam.m_NewAngleEnd = 0.0;
02211                 ActionCode Ac = ChangeBlenderAction::Init(  pUndoOp,pUndoOp->GetUndoActionList(),
02212                                                             pNodeBlender,ChangeParam);
02213                 ok = (Ac !=AC_FAIL);
02214             }
02215             pLast = pEnd;
02216         }
02217 
02218         pNodeBlender = FindNextBlender(pNodeBlender);
02219     }
02220 
02221     return ok;
02222 }
02223 
02224 BOOL NodeBlend::TransformBlendEndObjects(UndoableOperation* pUndoOp)
02225 {
02226     BOOL ok = TRUE;
02227 
02228     Node* pLast = NULL;
02229     NodeBlender* pNodeBlender = FindFirstBlender();
02230     while (ok && pNodeBlender != NULL)
02231     {
02232         NodeRenderableInk* pStart = pNodeBlender->GetNodeStart();
02233         NodeRenderableInk* pEnd   = pNodeBlender->GetNodeEnd();
02234         ERROR3IF(pStart   == NULL,"No start node");
02235         ERROR3IF(pEnd   == NULL,"No end node");
02236 
02237         if ((pStart == NULL) || (pEnd == NULL)) { return (FALSE); }
02238 
02239     
02240         DocCoord Point;
02241         double Angle = 0.0;
02242         if (pStart && pStart != pLast)
02243         {
02244             if (pNodeBlender->GetPointOnNodeBlendPath(0.0,&Point,&Angle))
02245             {
02246                 double AngleStart = pNodeBlender->GetAngleStart();
02247                 double AngleChange = Angle - AngleStart;
02248                 ok = TransformNodeToPoint(pStart,&Point,pUndoOp,AngleChange);
02249                 if (ok && pUndoOp && AngleChange != 0.0)
02250                 {
02251                     ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLESTART);
02252                     ChangeParam.m_NewAngleStart = AngleStart + AngleChange;
02253                     ActionCode Ac = ChangeBlenderAction::Init(  pUndoOp,pUndoOp->GetUndoActionList(),
02254                                                                 pNodeBlender,ChangeParam);
02255                     ok = (Ac !=AC_FAIL);
02256                 }
02257             }
02258         }
02259 
02260         if (pEnd && ok)
02261         {
02262             if (pNodeBlender->GetPointOnNodeBlendPath(1.0,&Point,&Angle))
02263             {
02264                 double AngleEnd = pNodeBlender->GetAngleEnd();
02265                 double AngleChange = Angle - AngleEnd;
02266                 ok = TransformNodeToPoint(pEnd,&Point,pUndoOp,AngleChange);
02267 
02268                 ERROR3IF (!ok, "one does not find this amusing!");
02269 
02270                 if (ok && pUndoOp && AngleChange != 0.0)
02271                 {
02272                     ChangeBlenderOpParam ChangeParam(CHANGEBLENDER_ANGLEEND);
02273                     ChangeParam.m_NewAngleEnd = AngleEnd + AngleChange;
02274                     ActionCode Ac = ChangeBlenderAction::Init(  pUndoOp,pUndoOp->GetUndoActionList(),
02275                                                                 pNodeBlender,ChangeParam);
02276                     ok = (Ac !=AC_FAIL);
02277                 }
02278             }
02279             pLast = pEnd;
02280         }
02281 
02282         pNodeBlender = FindNextBlender(pNodeBlender);
02283     }
02284 
02285     return ok;
02286 }
02287 
02288 BOOL NodeBlend::TransformNodeToPoint(NodeRenderableInk* pNode,DocCoord* pPoint,UndoableOperation* pUndoOp,double Angle)
02289 {
02290     ERROR2IF(pNode   == NULL,FALSE,"Null node ptr");
02291 
02292     BOOL ok = TRUE;
02293 
02294 
02295     // Get the node's bounds
02296     pNode->InvalidateBoundingRect(TRUE);
02297     DocRect Rect = pNode->GetBoundingRect();
02298 
02299     // Work out the translation that will move the centre of the bounds onto the given point 
02300     DocCoord centre = Rect.Centre(); //(Rect.lox + (Rect.Width()/2),Rect.loy + (Rect.Height()/2));
02301 
02302     INT32 dx = 0;
02303     INT32 dy = 0;
02304     if (pPoint)
02305     {
02306         dx = pPoint->x - centre.x;
02307         dy = pPoint->y - centre.y;
02308     }
02309 
02310     if (pUndoOp)
02311     {
02312         // DoTransformNode() needs an instance of the matrix.  It gets deleted when the action is destroyed
02313         if (Angle != 0.0)
02314         {
02315             Trans2DMatrix* pRotate = new Trans2DMatrix(centre,Angle);
02316             if (ok)
02317             {
02318                 RangeControl rc(TRUE,TRUE);
02319                 rc.SiblingsOnly = TRUE;         // Don't look at children - stops children getting 
02320                                                 // transformed twice when select-inside is present
02321                 Range r(pNode,pNode, rc);
02322                 ok = pUndoOp->DoTransformNodes(r,pRotate);
02323             }
02324         }
02325 
02326         if (dx != 0 || dy != 0)
02327         {
02328             Trans2DMatrix* pTrans = new Trans2DMatrix(dx,dy);
02329             if (ok)
02330             {
02331                 RangeControl rc(TRUE,TRUE);
02332                 rc.SiblingsOnly = TRUE;         // Don't look at children - stops children getting 
02333                                                 // transformed twice when select-inside is present
02334                 Range r(pNode,pNode, rc);
02335                 ok = pUndoOp->DoTransformNodes(r,pTrans);
02336             }
02337         
02338         }
02339     }
02340     else
02341     {
02342         // Non-undoable form of the transform
02343         if (Angle != 0.0)
02344         {
02345             Trans2DMatrix Rotate(centre,Angle);
02346             pNode->Transform(Rotate);
02347         }
02348 
02349         if (dx != 0 || dy != 0)
02350         {
02351             Trans2DMatrix Trans(dx,dy);
02352             pNode->Transform(Trans);
02353         }
02354     }
02355 
02356     return ok;
02357 }
02358 
02359 /*********************************************************************************************
02360 
02361 >   BOOL NodeBlend::DetachNodeBlendPath(Node* pContextNode, AttachNodeDirection AttDir,UndoableOperation* pUndoOp)
02362 
02363      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02364      Created:   29/4/99 //changed Diccon 9/99
02365      Inputs:    -
02366      Outputs:   -
02367      Returns:   -
02368      Purpose:   Removes all nodeblendpaths
02369 
02370 **********************************************************************************************/
02371 
02372 BOOL NodeBlend::DetachNodeBlendPath(Node* pContextNode, AttachNodeDirection AttDir,UndoableOperation* pUndoOp)
02373 {
02374     ERROR2IF(pContextNode == NULL,FALSE,"NULL pContextNode");
02375     ERROR2IF(pUndoOp      == NULL,FALSE,"NULL pUndoOp");
02376 
02377     BOOL ok = TRUE;
02378     UINT32 NBPCounter = 0;
02379     NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(NBPCounter++);
02380     while (pNodeBlendPath != NULL)
02381     {
02382         NodePath* pNodePath = pNodeBlendPath->CreateNodePathCopy();
02383         if (pNodePath != NULL)
02384         {
02385             CCAttrMap* pAttrMap = new CCAttrMap(30);
02386                     ok = (pAttrMap != NULL);
02387             if (ok) ok = pNodeBlendPath->FindAppliedAttributes(pAttrMap);
02388             if (ok) ok = pUndoOp->DoInsertNewNode(pNodePath,pContextNode,AttDir,FALSE,FALSE,FALSE,FALSE);
02389             if (ok) ok = pUndoOp->DoHideNode(pNodeBlendPath,TRUE);
02390             if (ok) ok = pUndoOp->DoSelectNode(pNodePath);              // Select it, because it's nicer that way
02391             if (ok) pNodePath->ApplyAttributes(pAttrMap,TRUE);
02392 
02393             if (pAttrMap != NULL)
02394             {
02395                 delete pAttrMap;
02396                 pAttrMap = NULL;
02397             }
02398         }
02399         // go through all the blenders and set their index flags to -1
02400         // NOTE - need to make this undoable
02401         NodeBlender* pBlender = FindFirstBlender();
02402         while (pBlender != NULL)
02403         {   
02404             pBlender->SetNodeBlendPathIndex(-1);
02405             pBlender = FindNextBlender(pBlender);
02406         
02407         }
02408         pNodeBlendPath = GetNodeBlendPath(NBPCounter++);
02409     }
02410     return ok;
02411 }
02412 
02413 /*********************************************************************************************
02414 
02415 >   NodeBlender* NodeBlend::FindFirstBlender()
02416 
02417      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02418      Created:   29/4/99
02419      Inputs:    -
02420      Outputs:   -
02421      Returns:   Pointer to the first NodeBlender belonging to this blend object
02422                 or NULL if there aren't any
02423      Purpose:   Convenience function.  A drive-thru NodeBlender takeaway service
02424      SeeAlso:   FindNextBlender()
02425 
02426 **********************************************************************************************/
02427 
02428 NodeBlender* NodeBlend::FindFirstBlender()
02429 {
02430     Node* pNode = FindFirstChild();
02431     while (pNode != NULL)
02432     {
02433         if (IS_A(pNode,NodeBlender))
02434             return ((NodeBlender*)pNode);
02435 
02436         pNode = pNode->FindNext();
02437     }
02438 
02439     return NULL;
02440 }
02441 
02442 
02443 /*********************************************************************************************
02444 
02445 >   NodeBlender* NodeBlend::FindNextBlender(NodeBlender* pNodeBlender)
02446 
02447      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02448      Created:   29/4/99
02449      Inputs:    pNodeBlender = ptr to previous NodeBlender found
02450      Outputs:   -
02451      Returns:   Pointer to the next NodeBlender belonging to this blend object, in rendering order
02452                 or NULL if there aren't any
02453      Purpose:   Convenience function.  A drive-thru NodeBlender takeaway service
02454      SeeAlso:   FindFirstBlender()
02455 
02456 **********************************************************************************************/
02457 
02458 NodeBlender* NodeBlend::FindNextBlender(NodeBlender* pNodeBlender)
02459 {
02460     if (pNodeBlender)
02461     {
02462         Node* pNode = pNodeBlender->FindNext();
02463 
02464         while (pNode != NULL)
02465         {
02466             if (IS_A(pNode,NodeBlender))
02467                 return ((NodeBlender*)pNode);
02468 
02469             pNode = pNode->FindNext();
02470         }
02471     }
02472 
02473     return NULL;
02474 }
02475 
02476 
02477 /*********************************************************************************************
02478 
02479 >   NodeBlender* NodeBlend::FindLastBlender()
02480 
02481      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
02482      Created:   16/9/99
02483      Inputs:    -
02484      Outputs:   -
02485      Returns:   Pointer to the last NodeBlender belonging to this blend object, in rendering order
02486                 or NULL if there aren't any
02487      Purpose:   Convenience function.  A drive-thru NodeBlender takeaway service
02488      SeeAlso:   FindFirstBlender(), FindNextBlender()
02489 
02490 **********************************************************************************************/
02491 
02492 NodeBlender* NodeBlend::FindLastBlender()
02493 {
02494     Node* pNode = FindLastChild();
02495     while (pNode != NULL)
02496     {
02497         if (pNode->IS_KIND_OF(NodeBlender))
02498         {
02499             return (NodeBlender*)pNode;
02500         }
02501         pNode = pNode->FindPrevious();
02502     }
02503     return NULL;
02504 
02505 }
02506 
02507 
02508 /*********************************************************************************************
02509 
02510 >   BOOL NodeBlend::FindFirstAndLastBlender(NodeBlender** pFirstBlender, NodeBlender** ppLastBlender)
02511 
02512      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
02513      Created:   16/9/99
02514      Inputs:    -
02515      Outputs:   pFirstBlender, pLastBlender, as the names suggest
02516      Returns:   TRUE if successful, FALSE otherwise
02517      Purpose:   Convenience function.  A drive-thru NodeBlender takeaway service
02518      SeeAlso:   FindFirstBlender(), FindNextBlender()
02519 
02520 **********************************************************************************************/
02521 
02522 BOOL NodeBlend::FindFirstAndLastBlenders(NodeBlender** ppFirstBlender, NodeBlender** ppLastBlender)
02523 {
02524     NodeBlender* pTempBlender = FindFirstBlender();
02525     if (pTempBlender == NULL)
02526         return FALSE;
02527     else
02528         *ppFirstBlender = pTempBlender;
02529 
02530     pTempBlender = NULL;
02531     pTempBlender = FindLastBlender();
02532     if (pTempBlender == NULL)
02533         return FALSE;
02534     
02535     *ppLastBlender = pTempBlender;
02536 
02537     return TRUE;
02538 }
02539 
02540 
02541 
02542 /*********************************************************************************************
02543 
02544 >   Node* NodeBlend::HasEditableChild(CCRuntimeClass* ChildClass, Node* pPreviousChild)
02545 
02546      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02547      Created:   29/4/99
02548      Inputs:    ChildClass      = the runtime class of the editable object
02549                 pPreviousChild  = a pointer to the previous editable child 
02550                                   returned by 'this' node, NULL if this is the first
02551                                   call to this node.
02552      Outputs:   -
02553      Returns:   A node pointer, to an object which forms part of the editable surface of
02554                 its parent (this node).
02555      Purpose:   This function returns our edit node (the NodeBlendPath)
02556 
02557 **********************************************************************************************/
02558 
02559 Node* NodeBlend::HasEditableChild(CCRuntimeClass* ChildClass, Node* pPreviousChild)
02560 {
02561     if (ChildClass != CC_RUNTIME_CLASS(NodePath))
02562         return NULL;
02563 
02564     NodeBlendPath* pNodeBlendPath = GetNodeBlendPath(0);
02565     // check to see if this has already been asked for once
02566     if (((Node*)pNodeBlendPath) == pPreviousChild)
02567         return NULL;
02568 
02569     return pNodeBlendPath;
02570 }
02571 
02572 /*********************************************************************************************
02573 
02574 >    BOOL NodeBlend::GetBlendDistance(BOOL FullDistance, double* Distance)
02575 
02576      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02577      Created:   26/9/99
02578      Inputs:    boolean indicating whether to return the full length of the blendpath
02579                 or just the length that is currently occupied by the blend
02580      Outputs:   length of the blend
02581      Returns:   TRUE if successful, FALSE otherwise
02582      Purpose:   To find out how long the blend is.
02583 
02584 **********************************************************************************************/
02585 
02586 BOOL NodeBlend::GetBlendDistance( BOOL FullDistance, double* Distance)
02587 {
02588     NodeBlender* pBlender = FindFirstBlender();
02589     double TempDistance = 0.0;
02590     
02591     while (pBlender != NULL)
02592     {
02593         if (pBlender->IsBlendedOnCurve()) 
02594         {
02595             NodeBlendPath* pPath = pBlender->GetNodeBlendPath();
02596             if (pPath == NULL) 
02597                 return FALSE;
02598 
02599             double PathDistance = pPath->GetPathLength();
02600             double StartProportion = 0.0;
02601             double EndProportion = 1.0;
02602 
02603             if (FullDistance ==FALSE)
02604             {
02605                 // the blend may only go part way along the curve, calculate how
02606                 // much distance the actual blend takes up 
02607                 
02608                 StartProportion = pBlender->GetProportionOfPathDistStart();
02609                 EndProportion = pBlender->GetProportionOfPathDistEnd();
02610             }
02611 
02612             TempDistance += (PathDistance * (EndProportion - StartProportion));
02613 
02614         }
02615         else
02616         {
02617             TempDistance += pBlender->GetLinearDistance();
02618         }
02619         pBlender = FindNextBlender(pBlender);
02620     }
02621     
02622     ERROR2IF((TempDistance < 0), FALSE, "Negative path length");
02623 
02624     *Distance = TempDistance;
02625     return TRUE;
02626 }
02627 
02628 
02629 /*********************************************************************************************
02630 
02631 >    BOOL NodeBlend::GetNumStepsFromDistance(double StepDistance, UINT32* NumSteps)
02632 
02633      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02634      Created:   26/9/99
02635      Inputs:    the desired distance between steps
02636      Outputs:   number of steps required to achieve the distance closest to the specified value
02637      Returns:   TRUE if successful, FALSE otherwise
02638      Purpose:   To determine the number of steps that will achieve a distance between steps close
02639                 to the value passed.
02640 
02641 **********************************************************************************************/
02642 
02643 BOOL NodeBlend::GetNumStepsFromDistance(double Distance, UINT32* NumSteps)
02644 {
02645     ASSERT(Distance);
02646     double BlendLength = 0.0;
02647     BOOL Valid = GetBlendDistance(TRUE, &BlendLength);
02648 
02649     if (!Valid)
02650         return FALSE;
02651 
02652     UINT32 TempNumSteps = (UINT32)(BlendLength / Distance);
02653 
02654     ERROR3IF((TempNumSteps < 0), "Negative number of blend steps");
02655     *NumSteps = TempNumSteps;
02656     return TRUE;
02657 }
02658 
02659 
02660 
02661 /*********************************************************************************************
02662 
02663 >    double NodeBlend::GetStepDistance()
02664 
02665      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02666      Created:   26/9/99
02667      Inputs:    -
02668      Outputs:   -
02669      Returns:   the distance between steps of the blend
02670      Purpose:   works out the distance between each step in the blend
02671 **********************************************************************************************/
02672 
02673 double NodeBlend::GetStepDistance()
02674 {
02675     double BlendLength = 0.0;
02676     GetBlendDistance(FALSE, &BlendLength);
02677     
02678     double StepDistance = BlendLength/m_NumBlendSteps;
02679 
02680     ERROR2IF((StepDistance < 0), 0, "Negative step distance");
02681     return StepDistance;
02682 }
02683 
02684 /*********************************************************************************************
02685 
02686 >    void NodeBlend::UpdateStepDistance()
02687 
02688      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02689      Created:   26/9/99
02690      Inputs:    -
02691      Outputs:   -
02692      Returns:   -
02693      Purpose:   works out the distance between each step in the blend 
02694                 and updates the member variable
02695 **********************************************************************************************/
02696 
02697 void NodeBlend::UpdateStepDistance()
02698 {
02699     double BlendLength = 0.0;
02700     GetBlendDistance(FALSE, &BlendLength);
02701     
02702     m_StepDistance = BlendLength/m_NumBlendSteps;
02703 }
02704 
02705 
02706 
02707 /*********************************************************************************************
02708 
02709 >    double NodeBlend::GetDistanceEntered()
02710 
02711      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02712      Created:   7/9/99
02713      Inputs:    -
02714      Outputs:   -
02715      Returns:   the last distance that the user entered in the edit box,
02716                 this will be initialised to -1 so if the user has not 
02717                 entered any value then -1 will be returned
02718      Purpose:   to return the last distance between blend steps that user 
02719                 entered
02720 **********************************************************************************************/
02721 
02722 double NodeBlend::GetDistanceEntered()
02723 {
02724     return m_DistanceEntered;
02725 }
02726 
02727 /*********************************************************************************************
02728 
02729 >    void NodeBlend::SetDistanceEntered(double Distance)
02730 
02731      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02732      Created:   7/9/99
02733      Inputs:    Distance between steps to set
02734      Outputs:   -
02735      Returns:   -
02736      Purpose:   to set the distance between blend steps that user 
02737                 entered
02738 **********************************************************************************************/
02739 
02740 void NodeBlend::SetDistanceEntered(double Distance)
02741 {
02742      m_DistanceEntered = Distance;
02743 }
02744 
02745 
02746 /*********************************************************************************************
02747 
02748 >    EndObject NodeBlend::GetLastEdited()
02749 
02750      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02751      Created:   15/9/99
02752      Inputs:    -
02753      Outputs:   -
02754      Returns:   the last object edited, either FIRST, LAST or NONE.
02755      Purpose:   Access function, lets you know which end object of a blend on a path 
02756                 was last edited. 
02757 **********************************************************************************************/
02758 
02759 EndObject NodeBlend::GetLastEdited()
02760 {
02761     return m_LastEdited;
02762 }
02763 
02764 
02765 /*********************************************************************************************
02766 
02767 >    void NodeBlend::SetLastEdited(EndObject ObjectEdited)
02768 
02769      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02770      Created:   15/9/99
02771      Inputs:    Object last edited
02772      Outputs:   -
02773      Returns:   -
02774      Purpose:   Access function 
02775 **********************************************************************************************/
02776 
02777 void NodeBlend::SetLastEdited(EndObject LastEdited)
02778 {
02779     m_LastEdited = LastEdited;
02780 }
02781 
02782 
02783 
02784 /*********************************************************************************************
02785 
02786 >    UINT32 NodeBlend::GetNumNodeBlendPaths()
02787 
02788      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02789      Created:   23/9/99
02790      Inputs:    -
02791      Outputs:   -
02792      Returns:   the number of nodeblend paths this blend is using
02793      Purpose:   Access function
02794 **********************************************************************************************/
02795 
02796 UINT32 NodeBlend::GetNumNodeBlendPaths()
02797 {
02798     UINT32 NBPCounter = 0;
02799     Node* pNode = FindFirstChild();
02800     while (pNode != NULL)
02801     {
02802         if (pNode->IS_KIND_OF(NodeBlendPath))
02803             NBPCounter++;
02804         pNode = pNode->FindNext();
02805     }
02806 
02807     return NBPCounter;
02808 }
02809 
02810 
02811 /*********************************************************************************************
02812 
02813 >    void NodeBlend::SetNumNodeBlendPathsInc(BOOL Increase)
02814 
02815      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02816      Created:   23/9/99
02817      Inputs:    Increase - TRUE if you wish to add, FALSE if you want to take away
02818      Outputs:   -
02819      Returns:   -
02820      Purpose:   Record a change in the number of nodeblendpaths
02821      Errors:    If m_NumNodeBlendPaths = 0 and Increment = FALSE
02822 
02823 **********************************************************************************************/
02824 
02825 void NodeBlend::SetNumNodeBlendPathsInc(BOOL Increase)
02826 {
02827     if (Increase)
02828         m_NumNodeBlendPaths++;
02829     else
02830     {
02831         if (m_NumNodeBlendPaths == 0)
02832         { 
02833             ERROR3("Blend has no nodeblend paths");
02834             return;
02835         }
02836         m_NumNodeBlendPaths--;
02837     }
02838 }
02839 
02840 
02841 
02842 /*********************************************************************************************
02843 
02844 >    void NodeBlend::SetNumNodeBlendPaths(INT32 Index)
02845 
02846      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02847      Created:   23/9/99
02848      Inputs:    Index - number that you wish to set
02849      Outputs:   -
02850      Returns:   -
02851      Purpose:   Record a change in the number of nodeblendpaths
02852      Errors:    If m_NumNodeBlendPaths = 0 and Increment = FALSE
02853 **********************************************************************************************/
02854 
02855 void NodeBlend::SetNumNodeBlendPaths(INT32 Index)
02856 {
02857     if (Index < 0)
02858     {
02859         ERROR3("Index is less than zero");
02860         return;
02861     }
02862     else
02863         m_NumNodeBlendPaths = Index;
02864 }
02865 
02866 
02867 
02868 
02869 /*********************************************************************************************
02870 
02871 >    BOOL NodeBlend::GetStartAndEndProportions(double* StartProp, double* EndProp)
02872 
02873      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02874      Created:   15/9/99
02875      Inputs:    -
02876      Outputs:   StartProp - the proportion along the nodeblendpath where the first blend object is
02877                 EndProp - the proportion along the nodeblendpath where the last blend object is
02878      Returns:   TRUE if successful, FALSE if not, or if the blend is not on a path
02879      Purpose:   To find the proportions along the path where the first and last objects are located
02880 **********************************************************************************************/
02881 
02882 BOOL NodeBlend::GetStartAndEndProportions(double *StartProp, double* EndProp)
02883 {
02884     if (m_BlendedOnCurve == FALSE)
02885     {
02886         ERROR3("Blend is not on a curve");
02887         return FALSE;
02888     }
02889     NodeBlender* pNodeBlender = FindFirstBlender();
02890 
02891     if (pNodeBlender == NULL)
02892     {
02893         ERROR3("Can't find first blender");
02894         return FALSE;
02895     }
02896 
02897     double TempStartProp = pNodeBlender->GetProportionOfPathDistStart();
02898     
02899     if (TempStartProp < 0.0 || TempStartProp > 1.0)
02900     {
02901         ERROR3("Invalid value for start proportion");
02902         return FALSE;
02903     }
02904 
02905     double TempEndProp = 1.0;
02906 
02907     while (pNodeBlender != NULL)
02908     {
02909         TempEndProp = pNodeBlender->GetProportionOfPathDistEnd();
02910         pNodeBlender = FindNextBlender(pNodeBlender);
02911     }
02912 
02913     if (TempEndProp < 0.0 || TempEndProp > 1.0)
02914     {
02915         ERROR3("Invalid value for end proportion");
02916         return FALSE;
02917     }
02918 
02919     *StartProp = TempStartProp;
02920     *EndProp = TempEndProp;
02921     return TRUE;
02922 }
02923 
02924 
02925 /*********************************************************************************************
02926 
02927 >    BOOL NodeBlend::GetStartAndEndNodes(Node** ppStart, Node** ppEnd)
02928 
02929      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02930      Created:   15/9/99
02931      Inputs:    -
02932      Outputs:   pStart - the first blend object 
02933                 pEnd   - the last blend object 
02934      Returns:   TRUE if successful, FALSE if not
02935      Purpose:   To find the first and last objects in a blend 
02936 **********************************************************************************************/
02937 
02938 BOOL NodeBlend::GetStartAndEndNodes(Node** ppStart, Node** ppEnd)
02939 {
02940 
02941     NodeBlender* pFirstBlender = FindFirstBlender();
02942     if (pFirstBlender == NULL)
02943     {
02944         ERROR3("No first blender");
02945         return FALSE;
02946     }
02947 
02948     *ppStart = pFirstBlender->GetNodeStart();
02949     if (*ppStart == NULL)
02950     {
02951         ERROR3("No start node");
02952         return FALSE;
02953     }
02954 
02955 
02956     NodeBlender* pLastBlender = FindLastBlender();
02957     if (pLastBlender == NULL)
02958     {
02959         ERROR3("No last blender");
02960         return FALSE;
02961     }
02962 
02963     *ppEnd = pLastBlender->GetNodeEnd();
02964     if (*ppEnd == NULL)
02965     {
02966         ERROR3("No last node");
02967         return FALSE;
02968     }
02969     
02970     return TRUE;
02971 }
02972 
02973 
02974 /*********************************************************************************************
02975 
02976 >    BOOL NodeBlend::NonLinearObjectProfile()
02977 
02978      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02979      Created:   4/10/99
02980      Inputs:    -
02981      Outputs:   - 
02982      Returns:   TRUE if the blend currently has a non-linear object profile, otherwise false
02983      Purpose:   as above
02984 **********************************************************************************************/
02985 
02986 BOOL NodeBlend::NonLinearObjectProfile()
02987 {
02988     if (m_ObjectProfile.GetBias() != 0.0)
02989         return TRUE;
02990     if (m_ObjectProfile.GetGain() != 0.0)
02991         return TRUE;
02992     return FALSE;
02993 }
02994 
02995 
02996 /*********************************************************************************************
02997 
02998 >    BOOL NodeBlend::HitOnEndDragBlob(DocCoord Point, Node** ppHitNode)
02999 
03000      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03001      Created:   20/9/99
03002      Inputs:    Point  - the point to test
03003      Outputs:   ppHitNode - the node that was hit, if there was one
03004     Returns:    TRUE if Point is within the central drag blob of an end node, otherwise false
03005                 also FALSE if blend is not on a curve
03006      Purpose:   To determine whether or not Point is over the central blob of one of the end 
03007                 objects of a blend on a curve.
03008 **********************************************************************************************/
03009 
03010 BOOL NodeBlend::HitOnEndDragBlob(DocCoord Point, Node** ppHitNode)
03011 {
03012 
03013     NodeBlender* pBlender = FindFirstBlender();
03014     BOOL ok = FALSE;
03015     while (pBlender != NULL)
03016     {
03017         /*NodeBlender* pNextBlender =*/ FindNextBlender(pBlender);
03018         // only the end objects of blends on curves are editable
03019         if (pBlender->IsBlendedOnCurve())
03020         {
03021             Node* pFirstNode = pBlender->GetNodeStart();
03022             Node* pLastNode = pBlender->GetNodeEnd();
03023             if (pFirstNode == NULL || pLastNode == NULL)
03024             {
03025                 return FALSE;
03026             }
03027             ok = HitOnDragBlob((NodeRenderableInk*)pFirstNode, Point);
03028             if (ok)
03029             {
03030                 *ppHitNode = pFirstNode;
03031                 return TRUE;
03032             }
03033             ok = HitOnDragBlob((NodeRenderableInk*)pLastNode, Point);
03034             if (ok)
03035             {
03036                 *ppHitNode = pLastNode;
03037                 return TRUE;
03038             }
03039         }
03040         pBlender = FindNextBlender(pBlender);
03041     }
03042 
03043 
03044     return ok;
03045 }
03046 
03047 
03048 /*********************************************************************************************
03049 
03050 >    BOOL NodeBlend::HitOnDragBlob(NodeRenderableInk* pInk, DocCoord PointerPos)
03051 
03052      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03053      Created:   15/9/99
03054      Inputs:    pInk - the node to test
03055                 PointerPos - the point to test
03056      Outputs:   - 
03057      Returns:   TRUE if PointerPos is within the central blob of pink, else FALSE
03058      Purpose:   To find out if the pointer position is within the bounds of the blob
03059                 in the middle of this node.
03060 **********************************************************************************************/
03061 
03062 BOOL NodeBlend::HitOnDragBlob(NodeRenderableInk* pInk, DocCoord PointerPos)
03063 {
03064     if (pInk == NULL)
03065     {
03066         ERROR3("Ink node is NULL");
03067         return FALSE;
03068     }
03069     BOOL ok = FALSE;
03070     // find the bounding rect of the centre blob
03071     DocRect BlobSize;
03072     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
03073     if (pBlobMgr != NULL)
03074     {
03075         DocRect BoundingRect = pInk->GetBoundingRect();
03076         DocCoord CentrePoint = BoundingRect.Centre();
03077         pBlobMgr->GetBlobRect(CentrePoint, &BlobSize);
03078 
03079         ok = BlobSize.ContainsCoord(PointerPos);
03080     }
03081 
03082     return ok;
03083 }
03084 
03085 
03086 
03087 /********************************************************************************************
03088 
03089 >   virtual INT32 NodeBlend::EstimateNodeComplexity (OpParam* details)
03090 
03091     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
03092     Created:    6/09/2000
03093 
03094     Inputs:     details     any data that should be used for the calculation
03095 
03096     Outputs:    -
03097 
03098     Returns:    an estimate of the nodes complexity
03099 
03100     Purpose:    This function estimates a complexity value for the node.  The complexity
03101                 value is based upon the total length of all paths in the node.
03102 
03103     See Also:   OpBlendNodes::DeterminBlendObjectsProcessorHit ()
03104 
03105 ********************************************************************************************/
03106 
03107 INT32 NodeBlend::EstimateNodeComplexity (OpParam* details)
03108 {
03109     NodeBlender* pBlender = FindFirstBlender();
03110     INT32 total = 0;
03111 
03112     while (pBlender)
03113     {
03114         total += pBlender->EstimateNodeComplexity (details);
03115         pBlender = FindNextBlender(pBlender);
03116     }
03117 
03118     return (total);
03119 }
03120 
03121 /*********************************************************************************************
03122 
03123 >    NodeBlender* NodeBlend::NodeIsPartOfBlender( Node* pNode)
03124 
03125      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03126      Created:   15/9/99
03127      Inputs:    
03128                 pNode - the node to look for
03129      Outputs:   - 
03130      Returns:   the nodeblender which uses pNode at the specified position
03131      Purpose:   To find out if pNode is used by a blender in this nodeblend at the position
03132                 specified. Useful if you are changing the node through a different blender and
03133                 wish to update related blends
03134 **********************************************************************************************/
03135 
03136 NodeBlender* NodeBlend::NodeIsPartOfBlender(Node* pNode, BOOL FirstNode)
03137 {
03138     if (pNode == NULL)
03139     {
03140         ERROR3("Node is NULL");
03141         return NULL;
03142     }
03143 
03144     NodeBlender* pNodeBlender = FindFirstBlender();
03145 
03146     if (pNodeBlender == NULL)
03147     {
03148         ERROR3("No nodeblender");
03149         return NULL;
03150     }
03151 
03152     while (pNodeBlender != NULL)
03153     {
03154         if (FirstNode)
03155         {
03156             Node* pCheckFirstNode = pNodeBlender->GetNodeStart();
03157             if (pCheckFirstNode == pNode)
03158                 return pNodeBlender;
03159         }
03160         else
03161         {
03162             Node* pCheckLastNode = pNodeBlender->GetNodeEnd();
03163             if (pCheckLastNode == pNode)
03164                 return pNodeBlender;
03165         }
03166         
03167         pNodeBlender = FindNextBlender(pNodeBlender);
03168     }
03169     return NULL;
03170 }
03171 
03172 
03173 /*********************************************************************************************
03174 
03175 >    NodeBlender* NodeBlend::NodeIsPartOfBlender( Node* pNode, Nodeblender* pBlender, BOOL* First)
03176 
03177      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03178      Created:   15/9/99
03179      Inputs:    pNode - the node to look for
03180                 pBlender - the blender that we already know pNode belongs to
03181      Outputs:   First, whether the node is first on last in the newly found blender
03182      Returns:   the nodeblender which uses pNode and is not pBlender
03183      Purpose:   Overridden version of above, now that we have multi stage blends which can go
03184                 to and from arbitrary end points we can't always rely on knowing the end position
03185 **********************************************************************************************/
03186 
03187 NodeBlender* NodeBlend::NodeIsPartOfBlender(Node* pNode, NodeBlender* pBlender, BOOL* First)
03188 {
03189     if (pNode == NULL)
03190     {
03191         ERROR3("Node is NULL");
03192         return NULL;
03193     }
03194 
03195     NodeBlender* pNodeBlender = FindFirstBlender();
03196 
03197     if (pNodeBlender == NULL)
03198     {
03199         ERROR3("No nodeblender");
03200         return NULL;
03201     }
03202 
03203 
03204     while (pNodeBlender != NULL)
03205     {
03206         if (pNodeBlender != pBlender)
03207         {
03208             Node* pStart = pNodeBlender->GetNodeStart();
03209             Node* pEnd = pNodeBlender->GetNodeEnd();
03210 
03211             if (pStart == NULL || pEnd == NULL)
03212             {
03213                 ERROR3("Start or end node is NULL");
03214                 return NULL;
03215             }
03216 
03217             if (pStart == pNode)
03218             {
03219                 *First = TRUE;
03220                 return pNodeBlender;
03221             }
03222             else if (pEnd == pNode)
03223             {
03224                 *First = FALSE;
03225                 return pNodeBlender;
03226             }
03227         }
03228         pNodeBlender = FindNextBlender(pNodeBlender);
03229     }
03230     return NULL;
03231 }
03232 
03233 
03234 /*********************************************************************************************
03235 
03236 >    virtual NodeCompound* NodeBlend::GetParentController() const
03237 
03238      Author:    Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
03239      Created:   15/9/99
03240      Inputs:    -
03241      Outputs:   - 
03242      Returns:   Controller node if there is one, or NULL
03243      Purpose:   If the blend is part of a bevel, shadow, or contour then
03244                 this returns the parent controller
03245 **********************************************************************************************/
03246 
03247 NodeCompound* NodeBlend::GetParentController() const
03248 {
03249     Node* pParent = (Node*)this->FindParent();
03250 
03251     if (pParent == NULL)
03252     {
03253         ERROR3("This blend has no parents");
03254         return NULL;
03255     }
03256 
03257     while ((pParent != NULL) && (!pParent->IsLayer()))
03258     {
03259         if (pParent->IsCompoundClass() && pParent->IsController())
03260             return static_cast<NodeCompound*>(pParent);
03261 
03262         pParent = pParent->FindParent();
03263     }
03264     return NULL;
03265 
03266 }
03267 
03268 
03269 
03270 //-----------------------------------------------
03271 
03272 /********************************************************************************************
03273 
03274 >   InitBlendAction::InitBlendAction()
03275 
03276     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03277     Created:    8/2/95
03278     Inputs:     -
03279     Outputs:    -
03280     Returns:    -
03281     Purpose:    Constructor for the action
03282     Errors:     -
03283     SeeAlso:    -
03284 
03285 ********************************************************************************************/
03286 
03287 InitBlendAction::InitBlendAction()
03288 {
03289     m_pNodeBlend = NULL;
03290     m_pNodeBlender = NULL;
03291     m_pStartNode = NULL;
03292     m_pEndNode = NULL;
03293     m_bReverse = FALSE;
03294     m_bStoreStartAndEndNodes = FALSE;
03295     m_enInitType = IBAT_INVALID;
03296 }
03297 
03298 
03299 /********************************************************************************************
03300 
03301 >   ActionCode InitBlendAction::Init(UndoableOperation* pOp,ActionList* pActionList,NodeBlend* pThisNodeBlend,
03302                                         BOOL StoreStartAndEndNodes, BOOL bReverse)
03303 
03304     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03305     Created:    8/2/95
03306     Inputs:     pOp             = ptr to the operation to which this action belongs
03307                 pThisNodeBlend  = ptr to blend to init.
03308                 pActionList     = ptr to action list to which this action should be added
03309                 StoreStartAndEndNodes = Whether to store the start & end nodes or not
03310                 bReverse        =   Whether to insert this action into the head or the tail
03311                                     of the action list (if FALSE it's treated normally)
03312     Outputs:    -
03313     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
03314     Purpose:    This is the function which creates an instance of this action. If there is no room 
03315                 in the undo buffer (which is determined by the base class Init function called within)
03316                 the function will either return AC_NO_RECORD which means the operation can continue, 
03317                 but no undo information needs to be stored, or AC_OK which means the operation should
03318                 continue AND record undo information. If the function returns AC_FAIL, there was not 
03319                 enough memory to record the undo information, and the user has decided not to continue
03320                 with the operation.
03321 
03322                 Records an action that, when executed, will Deinit() then Reinit() the blend, then 
03323                 invalidate its bounds.
03324     Errors:     -
03325     SeeAlso:    Action::Init()
03326 
03327 ********************************************************************************************/
03328 
03329 
03330 
03331 ActionCode InitBlendAction::Init(UndoableOperation* pOp,ActionList* pActionList,NodeBlend* pThisNodeBlend,
03332                                  BOOL StoreStartAndEndNodes, BOOL bReverse, BOOL bNodesMayBeChanged)
03333 {
03334     UINT32 ActSize = sizeof(InitBlendAction);
03335 
03336     InitBlendAction* pNewAction;
03337     ActionCode Ac = Action::Init(pOp,pActionList,ActSize,CC_RUNTIME_CLASS(InitBlendAction),(Action**)&pNewAction);
03338 
03339     NodeBlender * pBlender = (NodeBlender *)pThisNodeBlend->FindFirstChild(CC_RUNTIME_CLASS(NodeBlender));
03340 
03341     if (pNewAction)
03342         pNewAction->m_enInitType = IBAT_INIT;
03343 
03344     if (Ac != AC_FAIL && pNewAction != NULL)
03345     {
03346         pNewAction->m_pNodeBlend = pThisNodeBlend;
03347 
03348         if (StoreStartAndEndNodes && pBlender)
03349         {
03350             pNewAction->m_pStartNode = pBlender->GetNodeStart();
03351             pNewAction->m_pEndNode   = pBlender->GetNodeEnd();
03352         }
03353 
03354         pNewAction->m_bReverse = bReverse;
03355         pNewAction->m_bStoreStartAndEndNodes = StoreStartAndEndNodes;
03356         pNewAction->m_bNodesMayBeChanged = bNodesMayBeChanged;
03357 
03358         if (bReverse)
03359         {
03360             // reverse this action in the action list
03361             pActionList->RemoveItem(pNewAction);
03362             pActionList->AddHead(pNewAction);
03363         }
03364 
03365         pThisNodeBlend->InvalidateBlendBoundingRect();
03366         pThisNodeBlend->Deinit(bNodesMayBeChanged);
03367 //      if (!pOp->DoInvalidateNodeRegion(pThisNodeBlend,TRUE))
03368 //          Ac = AC_FAIL;
03369     }
03370 
03371     return Ac;
03372 }
03373 
03374 ActionCode InitBlendAction::InitOnBlender(UndoableOperation* pOp,ActionList* pActionList,
03375                                           NodeBlender* pThisNodeBlender, BOOL StoreStartAndEndNodes, BOOL bReverse)
03376 {
03377     UINT32 ActSize = sizeof(InitBlendAction);
03378 
03379     InitBlendAction* pNewAction;
03380     ActionCode Ac = Action::Init(pOp,pActionList,ActSize,CC_RUNTIME_CLASS(InitBlendAction),(Action**)&pNewAction);
03381     
03382     NodeBlend* pThisNodeBlend = (NodeBlend *)pThisNodeBlender->FindParent(CC_RUNTIME_CLASS(NodeBlend));
03383 
03384     pNewAction->m_enInitType = IBAT_INITONBLENDER;
03385     pNewAction->m_pNodeBlender = pThisNodeBlender;
03386 
03387     if (Ac != AC_FAIL && pNewAction != NULL)
03388     {
03389         pNewAction->m_pNodeBlend = pThisNodeBlend;
03390 
03391         if (StoreStartAndEndNodes && pThisNodeBlender)
03392         {
03393             pNewAction->m_pStartNode = pThisNodeBlender->GetNodeStart();
03394             pNewAction->m_pEndNode   = pThisNodeBlender->GetNodeEnd();
03395         }
03396 
03397         pNewAction->m_bReverse = bReverse;
03398         pNewAction->m_bStoreStartAndEndNodes = StoreStartAndEndNodes;
03399         pNewAction->m_bNodesMayBeChanged = FALSE;
03400 
03401         if (bReverse)
03402         {
03403             // reverse this action in the action list
03404             pActionList->RemoveItem(pNewAction);
03405             pActionList->AddHead(pNewAction);
03406         }
03407 
03408         pThisNodeBlend->InvalidateBlendBoundingRect();
03409         pThisNodeBlend->Deinit();
03410 //      if (!pOp->DoInvalidateNodeRegion(pThisNodeBlend,TRUE))
03411 //          Ac = AC_FAIL;
03412     }
03413 
03414     return Ac;
03415 }
03416 
03417 
03418 /********************************************************************************************
03419 
03420 >   ActionCode InitBlendAction::Execute();
03421 
03422     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03423     Created:    8/2/95
03424     Inputs:     -
03425     Outputs:    -
03426     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
03427     Purpose:    Executes the action.  
03428                 It calls Deinit() then Reinit() on the blend, then invalidate its bounds.
03429     Errors:     -
03430     SeeAlso:    Action::Init()
03431 
03432 ********************************************************************************************/
03433 
03434 ActionCode InitBlendAction::Execute()
03435 {
03436     if (pOperation->IS_KIND_OF(UndoableOperation))
03437     {
03438         switch (m_enInitType)
03439         {
03440             case IBAT_INIT:
03441             {
03442                 if (InitBlendAction::Init((UndoableOperation*)pOperation,pOppositeActLst,m_pNodeBlend,
03443                     m_bStoreStartAndEndNodes, m_bReverse, m_bNodesMayBeChanged) != AC_OK)
03444                     return AC_FAIL;
03445             }
03446             break;
03447             case IBAT_INITONBLENDER:
03448             {
03449                 if (InitBlendAction::InitOnBlender((UndoableOperation*)pOperation,pOppositeActLst,m_pNodeBlender,
03450                     m_bStoreStartAndEndNodes, m_bReverse) != AC_OK)
03451                     return AC_FAIL;
03452             }
03453             break;
03454             default:
03455             {
03456                 ERROR3 ("Er - I don't know what to do!");
03457                 return (AC_FAIL);
03458             }
03459             break;
03460         }
03461 
03462         // DMc
03463         // ok, if we've got either the start or the end nodes then re-init
03464         if (m_pStartNode != NULL && m_pEndNode != NULL)
03465         {
03466             NodeBlender * pBlender = NULL;
03467 
03468             switch (m_enInitType)
03469             {
03470                 case IBAT_INIT:
03471                 {
03472                     pBlender = (NodeBlender *)m_pNodeBlend->FindFirstChild(CC_RUNTIME_CLASS(NodeBlender));
03473                 }
03474                 break;
03475                 case IBAT_INITONBLENDER:
03476                 {
03477                     pBlender = m_pNodeBlender;//(NodeBlender *)m_pNodeBlend->FindFirstChild(CC_RUNTIME_CLASS(NodeBlender));
03478                 }
03479                 break;
03480                 default:
03481                 {
03482                     ERROR3 ("Er - I don't know what to do!");
03483                     return (AC_FAIL);
03484                 }
03485                 break;
03486             }
03487 
03488             if (pBlender)
03489             {
03490                 pBlender->Reinit((NodeRenderableInk *)m_pStartNode, (NodeRenderableInk *)m_pEndNode, FALSE);
03491             }
03492         }
03493 
03494         return AC_OK;
03495     }
03496     else
03497     {
03498         ERROR3("InitBlendAction::Execute() called with op that's not an undoable op");
03499         return AC_FAIL;
03500     }
03501 }
03502 
03503 InitBlendAction::~InitBlendAction()
03504 {
03505 }
03506 
03507 //-------------------------------------------------------------------------
03508 //-------------------------------------------------------------------------
03509 //-------------------------------------------------------------------------
03510 
03511 /********************************************************************************************
03512 
03513   > virtual BOOL NodeBlend::WritePreChildren(BaseCamelotFilter* pFilter)
03514 
03515     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03516     Created:    30/5/96
03517     Inputs:     pFilter = ptr to the filter
03518     Returns:    TRUE if record is written, FALSE if not
03519     Purpose:    Writes the blend record to the filter
03520     SeeAlso:    -
03521 
03522 ********************************************************************************************/
03523 
03524 BOOL NodeBlend::WritePreChildren(BaseCamelotFilter* pFilter)
03525 {
03526 #ifdef DO_EXPORT
03527     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
03528 
03529     BOOL ok = TRUE;
03530 
03531         // Diccon 9/99 added profile record 
03532     if (ok)
03533     {
03534         CXaraFileRecord ProfileRec(TAG_BLENDPROFILES, TAG_BLENDPROFILES_SIZE);
03535                 ok = ProfileRec.Init();
03536         if (ok) ok = ProfileRec.WriteDOUBLE(m_ObjectProfile.GetBias());
03537         if (ok) ok = ProfileRec.WriteDOUBLE(m_ObjectProfile.GetGain());
03538         if (ok) ok = ProfileRec.WriteDOUBLE(m_AttrProfile.GetBias());
03539         if (ok) ok = ProfileRec.WriteDOUBLE(m_AttrProfile.GetGain());
03540         if (ok) ok = ProfileRec.WriteDOUBLE(/*m_PositionProfile.GetBias()*/0.0);
03541         if (ok) ok = ProfileRec.WriteDOUBLE(/*m_PositionProfile.GetGain()*/0.0);
03542         if (ok) ok = pFilter->Write(&ProfileRec);
03543 
03544     }
03545 
03546     CXaraFileRecord Rec(TAG_BLEND,TAG_BLEND_SIZE);
03547 
03548     UINT16 NumSteps = UINT16(m_NumBlendSteps);
03549     BYTE Flags =( m_OneToOne << 0)              | 
03550                 ((m_NotAntialiased == 0) << 1)  | 
03551                 ( m_Tangential << 2);
03552 
03553     
03554     ERROR3IF(BYTE(m_ColBlendType) > (1 << TAG_BLEND_COLEFFECT_SHIFT),"Col blend type will not fit into the flags byte");
03555     BYTE ColEffect = (BYTE(m_ColBlendType) << TAG_BLEND_COLEFFECT_SHIFT) & TAG_BLEND_COLEFFECT_MASK;
03556 
03557     Flags = Flags | ColEffect;
03558 
03559     if (ok) ok = Rec.Init();
03560     if (ok) ok = Rec.WriteUINT16(NumSteps);
03561     if (ok) ok = Rec.WriteBYTE(Flags);
03562     if (ok) ok = pFilter->Write(&Rec);
03563 
03564 
03565     
03566 
03567     return ok;
03568 #else
03569     return FALSE;
03570 #endif
03571 }
03572 
03573 /********************************************************************************************
03574 
03575   > virtual BOOL NodeBlend::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
03576 
03577     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03578     Created:    30/5/96
03579     Inputs:     pFilter = ptr to the filter
03580     Returns:    TRUE if record is written, FALSE if not
03581     Purpose:    Writes the blend record to the filter
03582 
03583                 WEBSTER - markn 29/1/97
03584                 Changed so that it reacts to the "Convert blends to outlines" switch
03585                 
03586     SeeAlso:    -
03587 
03588 ********************************************************************************************/
03589 
03590 BOOL NodeBlend::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
03591 {
03592 #ifdef DO_EXPORT
03593     if (pFilter->GetConvertBlendsToOutlines())
03594         return pFilter->WriteNodeAsOutlines(this);
03595 
03596     return WritePreChildren(pFilter);
03597 #else
03598     return FALSE;
03599 #endif
03600 }
03601 
03602 //--------------------------------------------------------------
03603 // See NodeBlend::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
03604 //
03605 BOOL NodeBlend::WritePreChildrenNative(BaseCamelotFilter* pFilter)
03606 {
03607 #ifdef DO_EXPORT
03608     if (pFilter->IsCompactNativeFilter())
03609     {
03610         if (pFilter->GetConvertBlendsToOutlines())
03611             return pFilter->WriteNodeAsOutlines(this);
03612     }
03613 
03614     return WritePreChildren(pFilter);
03615 #else
03616     return FALSE;
03617 #endif
03618 }
03619 
03620 /********************************************************************************************
03621 
03622   > virtual BOOL NodeBlend::CanWriteChildrenWeb(BaseCamelotFilter* pFilter)
03623 
03624     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03625     Created:    29/1/97
03626     Inputs:     pFilter = ptr to the filter
03627     Returns:    TRUE if record is written, FALSE if not
03628     Purpose:    Determines whether the blend's children are to be saved or not.
03629 
03630                 WEBSTER - markn 29/1/97
03631                 
03632     SeeAlso:    -
03633 
03634 ********************************************************************************************/
03635 
03636 BOOL NodeBlend::CanWriteChildrenWeb(BaseCamelotFilter* pFilter)
03637 {
03638 #ifdef DO_EXPORT
03639 
03640     if (pFilter->GetConvertBlendsToOutlines())
03641         return FALSE;
03642 
03643     return TRUE;
03644 #else
03645     return FALSE;
03646 #endif
03647 }
03648 
03649 /********************************************************************************************
03650 
03651   > virtual BOOL NodeBlend::CanWriteChildrenNative(BaseCamelotFilter* pFilter)
03652 
03653     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03654     Created:    29/1/97
03655     Inputs:     pFilter = ptr to the filter
03656     Returns:    TRUE if record is written, FALSE if not
03657     Purpose:    Determines whether the blend's children are to be saved or not.
03658 
03659                 WEBSTER - markn 29/1/97
03660                 
03661     SeeAlso:    -
03662 
03663 ********************************************************************************************/
03664 
03665 BOOL NodeBlend::CanWriteChildrenNative(BaseCamelotFilter* pFilter)
03666 {
03667 #ifdef DO_EXPORT
03668 
03669     if (pFilter->IsCompactNativeFilter())
03670     {
03671         if (pFilter->GetConvertBlendsToOutlines())
03672             return FALSE;
03673     }
03674 
03675     return TRUE;
03676 #else
03677     return FALSE;
03678 #endif
03679 }
03680 
03681 
03682 
03683 /********************************************************************************************
03684 
03685 >   BOOL NodeBlend::ReinitialiseOnChildChanged( UndoableOperation* pOp,
03686                                                 NodeRenderableInk* pOldChild,
03687                                                 NodeRenderableInk* pNewChild )
03688     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03689     Created:    08 September 2000
03690     Inputs:     pOp         we need an undoable operation to do this undoably.
03691                 pOldChild   the node which the blend used to (currently) point to.
03692                 pNewChild   the node which the blend should now point to.
03693 
03694     Returns:    TRUE if success, FALSE otherwise.
03695 
03696     Purpose:    Used by the shadowing code to reinitialise the blend when one of the blended
03697                 nodes has had a shadow applied to it. In this case, the tree structure has
03698                 changed, and the blend framework needs to know this.
03699 
03700     Errors:     ERROR2 if any parameters are NULL.
03701 
03702 ********************************************************************************************/
03703 BOOL NodeBlend::ReinitialiseOnChildChanged( UndoableOperation* pOp,
03704                                             NodeRenderableInk* pOldChild,
03705                                             NodeRenderableInk* pNewChild )
03706 {
03707     // validate input params.
03708     ERROR2IF(pOp == NULL || pOldChild == NULL || pNewChild == NULL, FALSE,
03709             "NodeBlend::ReinitialiseOnChildChanged; NULL input parameters!");
03710 
03711     // iterate over our NodeBlenders, looking for a ptr to the old child.
03712     NodeBlender* pBlender = FindFirstBlender();
03713     while (pBlender != NULL)
03714     {
03715         // reinitialise the first blended node.
03716         if (pBlender->GetNodeStart() == pOldChild)
03717         {
03718             if (InitBlendAction::InitOnBlender(pOp, pOp->GetUndoActionList(), pBlender, TRUE, TRUE) == AC_FAIL)
03719             {
03720                 ERROR3("NodeBlend::ReinitialiseOnChildChanged; couldn't init blend action");
03721             }
03722 
03723             pBlender->Reinit(pNewChild, NULL, FALSE);
03724         }
03725 
03726         // reinitialise the second blended node.
03727         // (should be 'else if' here, but for robustness, we'll just use 'if').
03728         if (pBlender->GetNodeEnd() == pOldChild)
03729         {
03730             if (InitBlendAction::InitOnBlender(pOp, pOp->GetUndoActionList(), pBlender, TRUE, TRUE) == AC_FAIL)
03731             {
03732                 ERROR3("NodeBlend::ReinitialiseOnChildChanged; couldn't init blend action");
03733             }
03734 
03735             pBlender->Reinit(NULL, pNewChild, FALSE);
03736         }
03737 
03738         pBlender = FindNextBlender(pBlender);
03739     }
03740 
03741     return TRUE;
03742 }
03743 
03744 
03745 
03746 /********************************************************************************************
03747 
03748 >   virtual DocRect NodeBlend::ValidateExtend(const ExtendParams& ExtParams)
03749 
03750     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03751     Created:    26/11/1999
03752     Inputs:     ExtParams       extend description parameters.
03753     Outputs:    
03754     Returns:    TRUE if this node and its children cannot be validly extended,
03755                 FALSE otherwise.
03756     Purpose:    Checks whether or not performing an extend operation on this node will leave
03757                 it in a state from which it would be impossible to return.
03758 
03759                 In the case of a NodeBlend, we consider its children, which will be the ones
03760                 to move under a blend. Under extension, the choice of which child nodes to
03761                 extend is fairly complicated - see NodeBlend::FindExtendingChildren for more
03762                                                                                 information.
03763     Errors:     
03764     See also:   NodeBlend::Extend, NodeBlend::FindExtendingChildren.
03765 
03766 ********************************************************************************************/
03767 DocRect NodeBlend::ValidateExtend(const ExtendParams& ExtParams)
03768 {
03769     // the routine for finding extending nodes may take a while, so check first
03770     // whether we're actually doing any extending, and if not, leave now.
03771     if (!(ExtParams.fExtendFlags & X_EXTEND) && !(ExtParams.fExtendFlags & Y_EXTEND))
03772         return DocRect(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX);
03773 
03774     // okay, compile lists of child nodes to validate for extension.
03775     std::list<NodeRenderableInk*> lpInks;
03776     std::list<NodeBlendPath*> lpBlendPaths;
03777     FindExtendingChildren(lpInks, lpBlendPaths);
03778 
03779     // under non-stretch extension, a blend's child nodes behave in two ways.
03780     // if the child node is a blend-path, it is extended, and requires validation, as a path.
03781     // we test blend-paths first, as these are more likely to invalidate the extension.
03782     DocRect drMinExtend(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX), drThisMinExtend;
03783     for (   std::list<NodeBlendPath*>::iterator iterPath = lpBlendPaths.begin();
03784             iterPath != lpBlendPaths.end();
03785             iterPath ++ )
03786     {
03787         drThisMinExtend = ((NodeBlendPath*)(*iterPath))->ValidateExtend(ExtParams);
03788         if (drMinExtend.lo.x > drThisMinExtend.lo.x) drMinExtend.lo.x = drThisMinExtend.lo.x;
03789         if (drMinExtend.lo.y > drThisMinExtend.lo.y) drMinExtend.lo.y = drThisMinExtend.lo.y;
03790         if (drMinExtend.hi.x > drThisMinExtend.hi.x) drMinExtend.hi.x = drThisMinExtend.hi.x;
03791         if (drMinExtend.hi.y > drThisMinExtend.hi.y) drMinExtend.hi.y = drThisMinExtend.hi.y;
03792     }
03793 
03794     // Karim 17/04/2000
03795     // We're changing things so that blended objects actually extend, rather than translate.
03796     // This _may_ cause some havoc with blend-on-a-path...
03797 
03798 /*  // if it is any other ink node, it is translated as a whole, so its centre needs testing.
03799     INT32 i = 0;
03800     INT32 numInks = lpInks.size();
03801     DocCoord* doccArray = new DocCoord[numInks];
03802     for (   std::list<NodeRenderableInk*>::iterator iterInk = lpInks.begin();
03803             iterInk != lpInks.end();
03804             iterInk ++ )
03805     {
03806         doccArray[i ++] = ((NodeRenderableInk*)(*iterInk))->FindExtendCentre();
03807     }
03808 
03809     drThisMinExtend = Extender::ValidateControlPoints(numInks, doccArray, ExtParams);
03810     if (drMinExtend.lox > drThisMinExtend.lox) drMinExtend.lox = drThisMinExtend.lox;
03811     if (drMinExtend.loy > drThisMinExtend.loy) drMinExtend.loy = drThisMinExtend.loy;
03812     if (drMinExtend.hix > drThisMinExtend.hix) drMinExtend.hix = drThisMinExtend.hix;
03813     if (drMinExtend.hiy > drThisMinExtend.hiy) drMinExtend.hiy = drThisMinExtend.hiy;
03814     delete doccArray;
03815 */
03816     // if it is any other ink node, it is extended as if it is not even in a blend,
03817     // so we validate it as normal.
03818     /*size_t                numInks = */lpInks.size();
03819     for (   std::list<NodeRenderableInk*>::iterator iterInk = lpInks.begin();
03820             iterInk != lpInks.end();
03821             iterInk ++ )
03822     {
03823         drThisMinExtend = ((NodeRenderableInk*)(*iterInk))->ValidateExtend(ExtParams);
03824         if (drMinExtend.lo.x > drThisMinExtend.lo.x) drMinExtend.lo.x = drThisMinExtend.lo.x;
03825         if (drMinExtend.lo.y > drThisMinExtend.lo.y) drMinExtend.lo.y = drThisMinExtend.lo.y;
03826         if (drMinExtend.hi.x > drThisMinExtend.hi.x) drMinExtend.hi.x = drThisMinExtend.hi.x;
03827         if (drMinExtend.hi.y > drThisMinExtend.hi.y) drMinExtend.hi.y = drThisMinExtend.hi.y;
03828     }
03829 
03830     return drMinExtend;
03831 }
03832 
03833 
03834 
03835 /********************************************************************************************
03836 
03837 >   virtual void NodeBlend::Extend(const ExtendParams& ExtParams)
03838 
03839     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03840     Created:    26/11/1999
03841     Inputs:     ExtParams       extend description parameters.
03842     Outputs:    This node and its children may undergo change in the process of extension.
03843     Returns:    
03844     Purpose:    Applies an Extend operation to this node.
03845                 If we're just stretching, then we simply do a stretch transform on ourself.
03846                 If the extend operation involves any actual extension, we must determine
03847                 which of the nodes lying below us we must extend. The child nodes which are
03848                 extended will fall into two categories:
03849                     * NodeBlendPaths will take care of themselves, via their Extend() fn.
03850                     * Nodes which are blended between will be translated appropriately.
03851                     (Karim 17/04/2000 - behaviour has changed so that Nodes instead have
03852                                         their Extend() methods called)
03853                 Note however that the choice of which nodes to translate is a little complex;
03854                                                     look at FindExtendingChildren() for info.
03855     Errors:     
03856     See also:   NodeBlend::FindExtendingChildren()
03857 
03858 ********************************************************************************************/
03859 void NodeBlend::Extend(const ExtendParams& ExtParams)
03860 {
03861     // try to perform a stretch first of all.
03862     TransformStretchObject(ExtParams);
03863 
03864     // figuring out which children to do what to is complicated and may take time,
03865     // so only continue if we _have_ to extend along either axis, otherwise quit now.
03866     if ( !(ExtParams.fExtendFlags & (X_EXTEND | Y_EXTEND)) )
03867         return;
03868 
03869     // okay, compile lists of child nodes which will be extended.
03870     std::list<NodeRenderableInk*> lpInks;
03871     std::list<NodeBlendPath*> lpBlendPaths;
03872     FindExtendingChildren(lpInks, lpBlendPaths);
03873     
03874     // extend the blend-paths.
03875     for (   std::list<NodeBlendPath*>::iterator iterPath = lpBlendPaths.begin();
03876             iterPath != lpBlendPaths.end();
03877             iterPath ++ )
03878     {
03879         ((NodeBlendPath*)(*iterPath))->Extend(ExtParams);
03880     }
03881 
03882     // Karim 17/04/2000
03883     // We're changing things so that blended objects actually extend, rather than translate.
03884     // This _may_ cause some havoc with blend-on-a-path...
03885 
03886     // translate the objects.
03887     for (   std::list<NodeRenderableInk*>::iterator iterInk = lpInks.begin();
03888             iterInk != lpInks.end();
03889             iterInk ++ )
03890     {
03891 //      ((NodeRenderableInk*)(*iterInk))->TransformTranslateObject(ExtParams);
03892         ((NodeRenderableInk*)(*iterInk))->Extend(ExtParams);
03893     }
03894 }
03895 
03896 
03897 
03898 /********************************************************************************************
03899 
03900 >   void NodeBlend::FindExtendingChildren(  std::list<NodeRenderableInk*>& lpInks,
03901                                             std::list<NodeBlendPath*>& lpBlendPaths )
03902 
03903     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03904     Created:    26/11/1999
03905     Inputs:     lpInks          a NodeRenderableInk list, to fill with translating objects.
03906                 lpBlendPaths    a NodeBlendPath list, to fill with extending blend-paths.
03907     Outputs:    The two lists passed will be filled with pointers to Nodes in the tree.
03908     Returns:    
03909     Purpose:    Performs a search of the children below this blend node, returning a list of
03910                 object nodes which should be translated in an extension, and another list of
03911                 blend-path nodes, which should be extended appropriately.
03912                 
03913                 We travel down the tree, building a list of objects which belong to normal
03914                 blender nodes but do not also belong to blender-on-a-path nodes, and a list
03915                 of blender-on-a-path nodes also. This is a recursive algorithm, as blends can
03916                 lie within other blends. This recursion is carried out in a loop within this
03917                 function.
03918     Errors:     
03919     See also:   
03920 
03921 ********************************************************************************************/
03922 void NodeBlend::FindExtendingChildren(  std::list<NodeRenderableInk*>& lpInks,
03923                                         std::list<NodeBlendPath*>& lpBlendPaths )
03924 {
03925     std::list<NodeBlend*> lpBlends;             // a stack of blends, used for recursion.
03926     std::list<NodeGroup*> lpGroups;             // a stack of groups, used for recursion.
03927 
03928     // push ourself onto the blend stack, to start all the fun!
03929     lpBlends.push_back(this);
03930 
03931     NodeBlend* pNodeBlend = NULL;           //
03932     NodeGroup* pNodeGroup = NULL;           //
03933     NodeRenderableInk* pInk = NULL;         //
03934     NodeBlendPath* pNodeBlendPath = NULL;   //
03935 
03936     // loop until both stacks are empty.
03937     while (!lpBlends.empty() || !lpGroups.empty())
03938     {
03939         // deal with the blends first, as these are the complicated beasties.
03940         if (!lpBlends.empty())
03941         {
03942             // pull the next blend off our blend stack, for processing.
03943             pNodeBlend = lpBlends.front();
03944             lpBlends.pop_front();
03945 
03946             // process each of the blend's blender objects.
03947             // in our first pass, we add each blender's paths and objects to separate
03948             // lists. we need to discard any objects belonging to both a path-blender
03949             // and a normal blender, hence the two object-lists below.
03950             std::list<NodeRenderableInk*> lpBlendedObjects;
03951             std::list<NodeRenderableInk*> lpBlendPathObjects;
03952             NodeBlender* pBlender = pNodeBlend->FindFirstBlender();
03953             while (pBlender != NULL)
03954             {
03955                 // does this blender follow a path?
03956                 pNodeBlendPath = pBlender->GetNodeBlendPath();
03957                 if (pNodeBlendPath != NULL)
03958                 {
03959                     // yes - stick its path on the list of paths to extend.
03960                     lpBlendPaths.push_back(pNodeBlendPath);
03961 
03962                     // note down its nodes, so they don't land on the extending list later.
03963                     pInk = pBlender->GetNodeStart();
03964                     if (pInk != NULL)           // should never happen, but test anyway.
03965                         lpBlendPathObjects.push_back(pInk);
03966                     pInk = pBlender->GetNodeEnd();
03967                     if (pInk != NULL)           // should never happen, but test anyway.
03968                         lpBlendPathObjects.push_back(pInk);
03969                 }
03970 
03971                 // no - ok, remember its nodes in our short-list of blended objects.
03972                 else
03973                 {
03974                     pInk = pBlender->GetNodeStart();
03975                     if (pInk != NULL)           // should never happen, but test anyway.
03976                         lpBlendedObjects.push_back(pInk);
03977                     pInk = pBlender->GetNodeEnd();
03978                     if (pInk != NULL)           // should never happen, but test anyway.
03979                         lpBlendedObjects.push_back(pInk);
03980                 }
03981 
03982                 pBlender = pNodeBlend->FindNextBlender(pBlender);
03983             }
03984 
03985             // ok, we now have a list of blended objects, some of which we must discard
03986             // if they are also blended along a path.
03987             lpBlendPathObjects.sort();
03988             lpBlendedObjects.sort();
03989             std::list<NodeRenderableInk*>::iterator iterNode = lpBlendedObjects.begin();
03990             while (!lpBlendPathObjects.empty() && !lpBlendPathObjects.empty())
03991             {
03992                 // pop the next object to discard from the front of its list.
03993                 pInk = lpBlendPathObjects.front();
03994                 lpBlendPathObjects.pop_front();
03995 
03996                 // move through the object-list until we find a pointer whose value
03997                 // is not less than our discard-pointer.
03998                 while (iterNode != lpBlendedObjects.end() && (*iterNode) < pInk)
03999                     iterNode ++;
04000 
04001                 // discard any elements from the object list whose pointer value
04002                 // matches our discard pointer.
04003                 while (iterNode != lpBlendedObjects.end() && (*iterNode) == pInk)
04004                     iterNode = lpBlendedObjects.erase(iterNode);
04005             }
04006 
04007             // we move the remaining objects onto our group, blend and object lists.
04008             // if there were two or more blenders, then some nodes may occur twice
04009             // in our lists, so we test for these and only move them across once.
04010             pInk = NULL;
04011             while (!lpBlendedObjects.empty())
04012             {
04013                 if (pInk == lpBlendedObjects.front())
04014                 {
04015                     lpBlendedObjects.pop_front();
04016                     continue;
04017                 }
04018 
04019                 pInk = lpBlendedObjects.front();
04020                 lpBlendedObjects.pop_front();
04021 
04022                 if (IS_A(pInk, NodeGroup))          // another group on the groups stack.
04023                     lpGroups.push_front((NodeGroup*)pInk);
04024 
04025                 else if (IS_A(pInk, NodeBlend))             // don't think a blender can hold
04026                     lpBlends.push_front((NodeBlend*)pInk);  // a blend directly, but only 99%
04027                                                             // certain, so we check for it.
04028 
04029                 else if (pInk->IsAnObject())        // wahey! got another object to extend.
04030                     lpInks.push_back(pInk);
04031             }
04032         }
04033 
04034         // now groups - we add any renderable-ink children to the objects list,
04035         // and any group or blend children to the appropriate recursion list.
04036         if (!lpGroups.empty())
04037         {
04038             // we needn't worry about avoiding hidden nodes in this loop,
04039             // as we explicitly check the type of all nodes iterated over.
04040             pNodeGroup = lpGroups.front();
04041             lpGroups.pop_front();
04042             Node* pChild = pNodeGroup->FindFirstChild();
04043             while (pChild != NULL)
04044             {
04045                 if (IS_A(pChild, NodeGroup))        // another group on the groups stack.
04046                     lpGroups.push_front((NodeGroup*)pChild);
04047 
04048                 else if (IS_A(pChild, NodeBlend))   // another blend on the blends stack.
04049                     lpBlends.push_front((NodeBlend*)pChild);
04050 
04051                 else if (pChild->IsAnObject())      // wahey! got another object to extend.
04052                     lpInks.push_back((NodeRenderableInk*)pChild);
04053 
04054                 pChild = pChild->FindNext();
04055             }
04056         }
04057     }
04058 }
04059 
04060 
04061 
04062 //---------------------------------------------------------------------------------------
04063 //---------------------------------------------------------------------------------------
04064 //---------------------------------------------------------------------------------------
04065 
04066 /********************************************************************************************
04067 
04068 >   virtual UINT32* BlendRecordHandler::GetTagList()
04069 
04070     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04071     Created:    19/6/96
04072     Inputs:     -
04073     Returns:    Ptr to a list of tag values, terminated by CXFRH_TAG_LIST_END
04074     Purpose:    Provides the record handler system with a list of records handled by this
04075                 handler
04076     SeeAlso:    -
04077 
04078 ********************************************************************************************/
04079 UINT32* BlendRecordHandler::GetTagList()
04080 {
04081     static UINT32 TagList[] = { TAG_BLEND,
04082                                 TAG_BLENDER,
04083                                 TAG_BLENDER_CURVEPROP,
04084                                 TAG_BLENDER_CURVEANGLES,
04085                                 TAG_BLEND_PATH,
04086                                 TAG_BLENDPROFILES,
04087                                 TAG_BLENDERADDITIONAL,
04088                                 TAG_NODEBLENDPATH_FILLED,
04089                                 CXFRH_TAG_LIST_END};
04090 
04091     return (UINT32*)&TagList;
04092 }
04093 
04094 
04095 
04096 /********************************************************************************************
04097 
04098 >   virtual BOOL BlendRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
04099 
04100     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04101     Created:    19/6/96
04102     Inputs:     pCXaraFileRecord = ptr to record to handle
04103     Returns:    TRUE if handled successfuly
04104                 FALSE otherwise
04105     Purpose:    Handles the given record.
04106     SeeAlso:    -
04107 
04108 ********************************************************************************************/
04109 BOOL BlendRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
04110 {
04111     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04112 
04113     BOOL ok = TRUE;
04114 
04115     switch (pCXaraFileRecord->GetTag())
04116     {
04117         case TAG_BLEND:
04118             ok = HandleBlendRecord(pCXaraFileRecord);
04119             break;
04120 
04121         case TAG_BLENDER:
04122             ok = HandleBlenderRecord(pCXaraFileRecord);
04123             break;
04124 
04125         case TAG_BLENDER_CURVEPROP:
04126             ok = HandleBlenderCurvePropRecord(pCXaraFileRecord);
04127             break;
04128         
04129         case TAG_BLENDER_CURVEANGLES:
04130             ok = HandleBlenderCurveAnglesRecord(pCXaraFileRecord);
04131             break;
04132         
04133         case TAG_BLEND_PATH:
04134             ok = HandleBlendPathRecord(pCXaraFileRecord);
04135             break;
04136         case TAG_NODEBLENDPATH_FILLED:
04137             ok = HandleBlendPathFlags(pCXaraFileRecord);
04138             break;
04139         case TAG_BLENDPROFILES:
04140             ok = HandleBlendProfileRecord(pCXaraFileRecord);
04141             break;
04142         case TAG_BLENDERADDITIONAL:
04143             ok = HandleBlenderExtraRecord(pCXaraFileRecord);
04144             break;
04145         default:
04146             ok = FALSE;
04147             ERROR3_PF(("I don't handle records with the tag (%d)\n",pCXaraFileRecord->GetTag()));
04148             break;
04149     }
04150 
04151     return ok;
04152 }
04153 
04154 
04155 
04156 /********************************************************************************************
04157 
04158 BOOL BlendRecordHandler::HandleBlendRecord(CXaraFileRecord* pCXaraFileRecord)
04159 
04160     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04161     Created:    19/6/96
04162     Inputs:     pCXaraFileRecord = ptr to record to handle
04163     Returns:    TRUE if handled successfuly
04164                 FALSE otherwise
04165     Purpose:    Handles the given record.
04166                 The record has to be a blend record
04167     SeeAlso:    -
04168 
04169 ********************************************************************************************/
04170 BOOL BlendRecordHandler::HandleBlendRecord(CXaraFileRecord* pCXaraFileRecord)
04171 {
04172     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04173     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLEND,FALSE,"I don't handle this tag type");
04174 
04175     UINT16 NumSteps = 0;
04176     BYTE Flags = 0;
04177 
04178     BOOL    ok = pCXaraFileRecord->ReadUINT16(&NumSteps);
04179     if (ok) ok = pCXaraFileRecord->ReadBYTE(&Flags);
04180 
04181     if (ok)
04182     {
04183         NodeBlend* pBlend = new NodeBlend;
04184         if (pBlend != NULL)
04185         {
04186             BOOL OneToOne  =  Flags & TAG_BLEND_FLAG_ONETOONE;
04187             BOOL Antialias =  Flags & TAG_BLEND_FLAG_ANTIALIAS;
04188             BOOL Tangential=  Flags & TAG_BLEND_FLAG_TANGENTIAL;
04189             BYTE ColEffect = (Flags & TAG_BLEND_COLEFFECT_MASK) >> TAG_BLEND_COLEFFECT_SHIFT;
04190 
04191             pBlend->SetOneToOne(OneToOne != 0);
04192             pBlend->SetNotAntialiased(Antialias == 0);
04193             pBlend->SetTangential(Tangential != 0);
04194             pBlend->SetNumBlendSteps(NumSteps);
04195             pBlend->SetColourBlendType(ColourBlendType(ColEffect));
04196 
04197             ok = InsertNode(pBlend);
04198             if (ok) 
04199                 SetLastInsertedNodeBlend(pBlend);
04200 
04201             // load the profiles, if we have some
04202             if (m_bLoadedProfiles && ok)
04203             {
04204                 CProfileBiasGain* pObjProfile = pBlend->GetObjectProfile();
04205                 if (pObjProfile == NULL)
04206                     ok = FALSE;
04207                 else
04208                 {
04209                     pObjProfile->SetBiasGain(m_LastObjectProfile.GetBias(), m_LastObjectProfile.GetGain());
04210                     pBlend->RequestObjectProfileProcessing (TRUE);
04211                 }
04212                 if (ok)
04213                 {
04214                     CProfileBiasGain* pAttrProfile = pBlend->GetAttrProfile();
04215                     if (pAttrProfile == NULL)
04216                         ok = FALSE;
04217                     else
04218                     {
04219                         pAttrProfile->SetBiasGain(m_LastAttrProfile.GetBias(), m_LastAttrProfile.GetGain());
04220                         pBlend->RequestObjectProfileProcessing (TRUE);
04221                     }
04222                 }
04223                 if (ok)
04224                 {
04225                     CProfileBiasGain* pPosProfile = pBlend->GetPositionProfile();
04226                     if (pPosProfile == NULL)
04227                         ok = FALSE;
04228                     else
04229                         pPosProfile->SetBiasGain(m_LastPositionProfile.GetBias(), m_LastPositionProfile.GetGain());
04230                 }
04231             }
04232 
04233 
04234         }
04235         else
04236             ok = FALSE;
04237     }
04238     
04239     m_bLoadedProfiles = FALSE;
04240     return ok;
04241 }
04242 
04243 
04244 
04245 /********************************************************************************************
04246 
04247 BOOL BlendRecordHandler::HandleBlenderRecord(CXaraFileRecord* pCXaraFileRecord)
04248 
04249     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04250     Created:    19/6/96
04251     Inputs:     pCXaraFileRecord = ptr to record to handle
04252     Returns:    TRUE if handled successfuly
04253                 FALSE otherwise
04254     Purpose:    Handles the given record.
04255                 The record has to be a blender record
04256     SeeAlso:    -
04257 
04258 ********************************************************************************************/
04259 BOOL BlendRecordHandler::HandleBlenderRecord(CXaraFileRecord* pCXaraFileRecord)
04260 {
04261     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04262     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDER,FALSE,"I don't handle this tag type");
04263 
04264     INT32 PathIndexStart= -1;
04265     INT32 PathIndexEnd  = -1;
04266 
04267     BOOL    ok = pCXaraFileRecord->ReadINT32(&PathIndexStart);
04268     if (ok) ok = pCXaraFileRecord->ReadINT32(&PathIndexEnd);
04269 
04270     if (ok)
04271     {
04272         NodeBlender* pBlender = new NodeBlender;
04273         if (pBlender != NULL)
04274         {
04275             pBlender->SetPathIndexStart(PathIndexStart);
04276             pBlender->SetPathIndexEnd(PathIndexEnd);
04277 
04278             ok = InsertNode(pBlender);
04279             if (ok) SetLastInsertedNodeBlender(pBlender);
04280         
04281         }
04282         else
04283             ok = FALSE;
04284     }
04285 
04286     return ok;
04287 }
04288 
04289 
04290 
04291 /********************************************************************************************
04292 
04293 >   BOOL BlendRecordHandler::HandleBlenderCurvePropRecord(CXaraFileRecord* pCXaraFileRecord);
04294 
04295     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04296     Created:    12/5/99
04297     Inputs:     pCXaraFileRecord = ptr to record to handle
04298     Returns:    TRUE if handled successfuly
04299                 FALSE otherwise
04300     Purpose:    Handles the given record.
04301                 The record has to be a blender curve proportion record
04302     SeeAlso:    -
04303 
04304 ********************************************************************************************/
04305 BOOL BlendRecordHandler::HandleBlenderCurvePropRecord(CXaraFileRecord* pCXaraFileRecord)
04306 {
04307     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04308     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDER_CURVEPROP,FALSE,"I don't handle this tag type");
04309 
04310     BOOL ok = TRUE;
04311     NodeBlender* pNodeBlender = GetLastInsertedNodeBlender();
04312 
04313     if (pNodeBlender != NULL)
04314     {
04315         double ProportionOfPathDistStart = -1.0;
04316         double ProportionOfPathDistEnd = -1.0;
04317 
04318                 ok = pCXaraFileRecord->ReadDOUBLE(&ProportionOfPathDistStart);
04319         if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&ProportionOfPathDistEnd);
04320 
04321         if (ok)
04322         {
04323             pNodeBlender->SetProportionOfPathDistStart(ProportionOfPathDistStart);
04324             pNodeBlender->SetProportionOfPathDistEnd(ProportionOfPathDistEnd);
04325         }
04326     }
04327 
04328     return ok;
04329 }
04330 
04331 
04332 
04333 /********************************************************************************************
04334 
04335 >   BOOL BlendRecordHandler::HandleBlenderCurveAnglesRecord(CXaraFileRecord* pCXaraFileRecord);
04336 
04337     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04338     Created:    2/6/99
04339     Inputs:     pCXaraFileRecord = ptr to record to handle
04340     Returns:    TRUE if handled successfuly
04341                 FALSE otherwise
04342     Purpose:    Handles the given record.
04343                 The record has to be a blender curve angles record
04344     SeeAlso:    -
04345 
04346 ********************************************************************************************/
04347 BOOL BlendRecordHandler::HandleBlenderCurveAnglesRecord(CXaraFileRecord* pCXaraFileRecord)
04348 {
04349     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04350     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDER_CURVEANGLES,FALSE,"I don't handle this tag type");
04351 
04352     BOOL ok = TRUE;
04353     NodeBlender* pNodeBlender = GetLastInsertedNodeBlender();
04354 
04355     if (pNodeBlender != NULL)
04356     {
04357         double AngleStart = 0.0;
04358         double AngleEnd   = 0.0;
04359 
04360                 ok = pCXaraFileRecord->ReadDOUBLE(&AngleStart);
04361         if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AngleEnd);
04362 
04363         if (ok)
04364         {
04365             pNodeBlender->SetAngleStart(AngleStart);
04366             pNodeBlender->SetAngleEnd(AngleEnd);
04367         }
04368     }
04369 
04370     return ok;
04371 }
04372 
04373 
04374 
04375 /********************************************************************************************
04376 
04377 >   BOOL BlendRecordHandler::HandleBlendPathRecord(CXaraFileRecord* pCXaraFileRecord)
04378 
04379     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04380     Created:    12/5/99
04381     Inputs:     pCXaraFileRecord = ptr to record to handle
04382     Returns:    TRUE if handled successfuly
04383                 FALSE otherwise
04384     Purpose:    Handles the given record.
04385                 The record has to be a blend path record
04386     SeeAlso:    -
04387 
04388 ********************************************************************************************/
04389 BOOL BlendRecordHandler::HandleBlendPathRecord(CXaraFileRecord* pCXaraFileRecord)
04390 {
04391     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04392     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLEND_PATH,FALSE,"I don't handle this tag type");
04393 
04394     BOOL ok = FALSE;
04395 
04396     NodeBlendPath* pNodeBlendPath = new NodeBlendPath;
04397 
04398     if (pNodeBlendPath != NULL && pNodeBlendPath->SetUpPath())
04399     {
04400         ok = pCXaraFileRecord->ReadPath(&pNodeBlendPath->InkPath); 
04401         if (ok) pNodeBlendPath->InkPath.InitialiseFlags();  // Init the path flags array to something sensible
04402         if (ok) ok = InsertNode(pNodeBlendPath);
04403         if (ok) 
04404         {
04405             SetLastNodePathInserted(pNodeBlendPath);
04406             SetLastInsertedNodeBlendPath(pNodeBlendPath);
04407         }
04408         if (ok)
04409         {
04410             Node* pParent = pNodeBlendPath->FindParent();
04411             if ((pParent != NULL) && (pParent->IS_KIND_OF(NodeBlend)))
04412             {
04413                 NodeBlend* pNodeBlend = (NodeBlend*)pParent;
04414                 pNodeBlend->SetBlendedOnCurve(TRUE);
04415                 pNodeBlend->SetNumNodeBlendPathsInc(TRUE);
04416                 
04417                 // this deals with the case of older files that contain a blend on a curve.
04418                 // these blends will contain only one NBP and all blenders will refer to that 
04419                 // curve.  
04420                 // We can tell the old files because their Index values will not have been changed
04421                 // from the default value of -2.
04422                 NodeBlender* pBlender = pNodeBlend->FindFirstBlender();
04423                 while (pBlender != NULL)
04424                 {
04425                     if (pBlender->GetNodeBlendPathIndex() == -2)
04426                         pBlender->SetNodeBlendPathIndex(0);
04427 
04428                     pBlender = pNodeBlend->FindNextBlender(pBlender);
04429                 }
04430             }
04431         }
04432     }
04433 
04434     return ok;
04435 }
04436 
04437 
04438 
04439 /********************************************************************************************
04440 
04441 >   BOOL BlendRecordHandler::HandleNodeBlendPathFlags(CXaraFileRecord* pCXaraFileRecord)
04442 
04443     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04444     Created:    12/5/99
04445     Inputs:     pCXaraFileRecord = ptr to record to handle
04446     Returns:    TRUE if handled successfuly
04447                 FALSE otherwise
04448     Purpose:    Handles the given record.
04449                 The record has to be a blend path filled record
04450     SeeAlso:    -
04451 
04452 ********************************************************************************************/
04453 BOOL BlendRecordHandler::HandleBlendPathFlags(CXaraFileRecord* pXFRecord)
04454 {
04455     ERROR2IF(pXFRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04456     ERROR2IF(pXFRecord->GetTag() != TAG_NODEBLENDPATH_FILLED,FALSE,"I don't handle this tag type");
04457 
04458     INT32 Flag;
04459     BOOL ok     = pXFRecord->ReadINT32(&Flag);
04460     
04461     if (ok)
04462     {
04463         NodeBlendPath* pNodeBlendPath = GetLastInsertedNodeBlendPath();
04464         ERROR2IF(pNodeBlendPath == NULL, FALSE, "No NodeBlendPath");
04465 
04466         pNodeBlendPath->InkPath.IsFilled = Flag;
04467     }
04468 
04469     return ok;
04470 }
04471 
04472 
04473 
04474 /********************************************************************************************
04475 
04476 >   BOOL BlendRecordHandler::HandleBlenderExtraRecord(CXaraFileRecord* pCXaraFileRecord)
04477 
04478     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
04479     Created:    20/9/99
04480     Inputs:     pCXaraFileRecord = ptr to record to handle
04481     Returns:    TRUE if handled successfuly
04482                 FALSE otherwise
04483     Purpose:    Handles the given record.
04484                 The record has to be a a blender extra info record
04485                 (exact contents yet to be finalised).
04486     SeeAlso:    -
04487 
04488     Notes:      Karim 05/02/2001
04489                 Added BitField parameter to extra blend record.
04490                 This stores whether or not a blend-on-path is reversed.
04491                 Unfortunately, the extra information needs to go at the end of
04492                 an existing record, so must use the dangerous ReadBYTEnoError() fn.
04493 
04494 ********************************************************************************************/
04495 BOOL BlendRecordHandler::HandleBlenderExtraRecord(CXaraFileRecord* pRecord)
04496 {
04497     ERROR2IF(pRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04498     ERROR2IF(pRecord->GetTag() != TAG_BLENDERADDITIONAL,FALSE,"I don't handle this tag type");
04499 
04500     INT32 Flag;
04501     INT32 Index;
04502     INT32 ObjIndexStart;
04503     INT32 ObjIndexEnd;
04504     BYTE BitField;
04505 
04506     BOOL ok     = pRecord->ReadINT32(&Flag);
04507     if (ok)  ok = pRecord->ReadINT32(&Index);
04508     if (ok)  ok = pRecord->ReadINT32(&ObjIndexStart);
04509     if (ok)  ok = pRecord->ReadINT32(&ObjIndexEnd);
04510     if (ok)
04511     {
04512         // if this fn fails, it means we read in an old file without BitField
04513         // info, so we arbitrarily set it to 0.
04514         if (!pRecord->ReadBYTEnoError(&BitField))
04515             BitField = 0;
04516     }
04517 
04518     if (ok)
04519     {
04520         NodeBlender* pBlender = GetLastInsertedNodeBlender();
04521         ERROR2IF(pBlender == NULL, FALSE, "No NodeBlender");
04522 
04523         pBlender->SetBlendedOnCurve(Flag);
04524         pBlender->SetNodeBlendPathIndex(Index);
04525         pBlender->SetObjIndexStart(ObjIndexStart);
04526         pBlender->SetObjIndexEnd(ObjIndexEnd);
04527         pBlender->SetReversed((BitField & 1) == 1);
04528 
04529         NodeBlend* pNodeBlend = pBlender->GetNodeBlend();
04530         ERROR2IF(pNodeBlend == NULL, FALSE, "Parent is not NodeBlend");
04531         
04532         INT32 CurrentNodeBlendPaths = (INT32)pNodeBlend->GetNumNodeBlendPaths();
04533         if (Index >  CurrentNodeBlendPaths)
04534             pNodeBlend->SetNumNodeBlendPaths(Index+1);
04535         
04536     }
04537     return ok;
04538 }
04539 
04540 
04541 
04542 /********************************************************************************************
04543 
04544 >   BOOL BlendRecordHandler::HandleBlendProfileRecord(CXaraFileRecord* pCXaraFileRecord)
04545 
04546     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
04547     Created:    20/9/99
04548     Inputs:     pCXaraFileRecord = ptr to record to handle
04549     Returns:    TRUE if handled successfuly
04550                 FALSE otherwise
04551     Purpose:    Handles the given record.
04552                 The record has to be a blend profile record
04553     SeeAlso:    -
04554 
04555     NOTE:      12/7/2000 in order to fix a bug loading v.3 blends into the browser plugin I had
04556                to reverse the order in which blend data was saved.  Thus in order to load it in
04557                again we have to cache the profile data and wait for our next blend to be loaded.
04558                The cause of the plug-in problem is still unresolved...
04559 ********************************************************************************************/
04560 BOOL BlendRecordHandler::HandleBlendProfileRecord(CXaraFileRecord* pCXaraFileRecord)
04561 {
04562     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
04563     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_BLENDPROFILES,FALSE,"I don't handle this tag type");
04564 
04565     double ObjBias = 0.0;
04566     double ObjGain = 0.0;
04567     double AttrBias = 0.0;
04568     double AttrGain = 0.0;
04569     double PosBias = 0.0;
04570     double PosGain = 0.0;
04571 
04572     BOOL    ok = pCXaraFileRecord->ReadDOUBLE(&ObjBias);
04573     if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&ObjGain);
04574     if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AttrBias);
04575     if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AttrGain);
04576     if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&PosBias);
04577     if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&PosGain);
04578 
04579     m_LastObjectProfile.SetBiasGain(ObjBias, ObjGain);
04580     m_LastAttrProfile.SetBiasGain(AttrBias, AttrGain);
04581     m_LastPositionProfile.SetBiasGain(PosBias, PosGain);
04582 
04583     m_bLoadedProfiles = TRUE;
04584 
04585     return ok;
04586 }
04587 
04588 
04589 
04590 /********************************************************************************************
04591 
04592 >   virtual void BlendRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
04593 
04594     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
04595     Created:    19/6/96
04596     Inputs:     pRecord = ptr to a record
04597                 pStr = ptr to string to update
04598     Returns:    -
04599     Purpose:    This provides descriptions for the blend records.
04600     Errors:     -
04601     SeeAlso:    -
04602 
04603 ********************************************************************************************/
04604 
04605 #ifdef XAR_TREE_DIALOG
04606 void BlendRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
04607 {
04608     if (pStr == NULL || pRecord == NULL)
04609         return;
04610 
04611     TCHAR s[256];
04612 
04613     //   Call base class first
04614     CamelotRecordHandler::GetRecordDescriptionText(pRecord,pStr);
04615 
04616     switch (pRecord->GetTag())
04617     {
04618         case TAG_BLEND:
04619         {
04620             UINT16 NumSteps = 0;
04621             BYTE Flags = 0;
04622 
04623             pRecord->ReadUINT16(&NumSteps);
04624             pRecord->ReadBYTE(&Flags);
04625 
04626             camSprintf(s,_T("NumSteps\t= %hu\r\n"),NumSteps);
04627             (*pStr) += s;
04628             camSprintf(s,_T("Flags\t\t= %d\r\n"),INT32(Flags));
04629             (*pStr) += s;
04630             (*pStr) += _T("\r\n");
04631 
04632             camSprintf(s,_T("One to One\t= %d\r\n"),Flags & TAG_BLEND_FLAG_ONETOONE);
04633             (*pStr) += s;
04634             camSprintf(s,_T("Antialiased\t= %d\r\n"),Flags & TAG_BLEND_FLAG_ANTIALIAS);
04635             (*pStr) += s;
04636             camSprintf(s,_T("Tangential\t= %d\r\n"),Flags & TAG_BLEND_FLAG_TANGENTIAL);
04637             (*pStr) += s;
04638 
04639             BYTE ColEffect = (Flags & TAG_BLEND_COLEFFECT_MASK) >> TAG_BLEND_COLEFFECT_SHIFT;
04640             camSprintf(s,_T("Colour Effect\t= %d\r\n"),INT32(ColEffect));
04641             (*pStr) += s;
04642         }
04643         break;
04644 
04645         case TAG_BLENDER:
04646         {
04647             INT32 PathIndexStart = -1;
04648             INT32 PathIndexEnd = -1;
04649 
04650             pRecord->ReadINT32(&PathIndexStart);
04651             pRecord->ReadINT32(&PathIndexEnd);
04652 
04653             camSprintf(s,_T("PathIndexStart\t= %d\r\n"),PathIndexStart);
04654             (*pStr) += s;
04655             camSprintf(s,_T("PathIndexEnd\t= %d\r\n"),PathIndexEnd);
04656             (*pStr) += s;
04657 
04658         }
04659         break;
04660 
04661         case TAG_BLENDER_CURVEPROP:
04662         {
04663             double PropStart = -1.0;
04664             double PropEnd   = -1.0;
04665             pRecord->ReadDOUBLE(&PropStart);
04666             pRecord->ReadDOUBLE(&PropEnd);
04667             camSprintf(s,_T("Proportion of path distance start\t= %f\r\n"),PropStart);
04668             (*pStr) += s;
04669             camSprintf(s,_T("Proportion of path distance end\t= %f\r\n"),PropEnd);
04670             (*pStr) += s;
04671         }
04672         break;
04673 
04674         case TAG_BLENDER_CURVEANGLES:
04675         {
04676             double AngleStart = -1.0;
04677             double AngleEnd   = -1.0;
04678             pRecord->ReadDOUBLE(&AngleStart);
04679             pRecord->ReadDOUBLE(&AngleEnd);
04680             camSprintf(s,_T("Angle start\t= %f\r\n"),AngleStart);
04681             (*pStr) += s;
04682             camSprintf(s,_T("Angle end\t= %f\r\n"),AngleEnd);
04683             (*pStr) += s;
04684         }
04685         break;
04686 
04687         case TAG_BLEND_PATH:
04688             // Use standard base class func for describing the path textually
04689             DescribePath(pRecord,pStr);
04690             break;
04691         case TAG_BLENDERADDITIONAL:
04692         {
04693             INT32 BlendedOnCurve = FALSE;
04694             pRecord->ReadINT32(&BlendedOnCurve);
04695             camSprintf(s,_T("BlendedOnCurve\t= %f\r\n"),BlendedOnCurve);
04696             (*pStr) += s;
04697         }
04698         break;
04699         case TAG_NODEBLENDPATH_FILLED:
04700         {
04701             INT32 Filled = FALSE;
04702             pRecord->ReadINT32(&Filled);
04703             camSprintf(s,_T("Filled \t= %f\r\n"),Filled);
04704             (*pStr) += s;
04705         }
04706         break;
04707         case TAG_BLENDPROFILES:
04708             double ObjBias = 0.0;
04709             double ObjGain = 0.0;
04710             double AttrBias = 0.0;
04711             double AttrGain = 0.0;
04712             double PosBias = 0.0;
04713             double PosGain = 0.0;
04714             BOOL ok    = pRecord->ReadDOUBLE(&ObjBias);
04715             if (ok) 
04716             {
04717                 camSprintf(s,_T("Object Bias\t= %f\r\n"),ObjBias);
04718                 (*pStr) += s;
04719                 ok = pRecord->ReadDOUBLE(&ObjGain);
04720             }
04721             if (ok) 
04722             {
04723                 camSprintf(s,_T("Object Gain\t= %f\r\n"),ObjGain);
04724                 (*pStr) += s;   
04725                 ok = pRecord->ReadDOUBLE(&AttrBias);
04726             }
04727             if (ok) 
04728             {
04729                 camSprintf(s,_T("Attribute Bias\t= %f\r\n"),AttrBias);
04730                 (*pStr) += s;
04731                 ok = pRecord->ReadDOUBLE(&AttrGain);
04732             }
04733             if (ok) 
04734             {
04735                 camSprintf(s,_T("Attribute Gain\t= %f\r\n"),AttrGain);
04736                 (*pStr) += s;
04737                 ok = pRecord->ReadDOUBLE(&PosBias);
04738             }
04739             if (ok) 
04740             {
04741                 camSprintf(s,_T("Position Bias\t= %f\r\n"),PosBias);
04742                 (*pStr) += s;
04743                 ok = pRecord->ReadDOUBLE(&PosGain);
04744             }
04745             if (ok)
04746             {
04747                 camSprintf(s,_T("Position Gain\t= %f\r\n"),PosGain);
04748                 (*pStr) += s;
04749             }
04750             break;
04751     }
04752 }
04753 #endif

Generated on Sat Nov 10 03:45:58 2007 for Camelot by  doxygen 1.4.4