ncntrcnt.cpp

Go to the documentation of this file.
00001 // $Id: ncntrcnt.cpp 1361 2006-06-25 16:43:38Z alex $
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 #include "camtypes.h"
00100 //#include "node.h"
00101 //#include "ink.h"
00102 //#include "nodecomp.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00103 #include "saveeps.h"
00104 
00105 // Save/load
00106 //#include "cxfrec.h"       // CXaraFileRecord - in camtypes.h [AUTOMATICALLY REMOVED]
00107 
00108 #include "cxftags.h"
00109 //#include "cxfdefs.h"
00110 
00111 
00112 
00113 #include "nodecntr.h"
00114 #include "ncntrcnt.h"
00115 //#include "undoop.h"
00116 //#include "cntres.h"
00117 //#include "bevres.h"
00118 #include "nodecont.h"
00119 //#include "rndrgn.h"
00120 //#include "attrmap.h"
00121 #include "opcntr.h"
00122 //#include "app.h"
00123 #include "opbevel.h"
00124 #include "objchge.h"
00125 #include "transop.h"
00126 #include "nodetext.h"
00127 #include "cutop.h"
00128 #include "textops.h"
00129 #include "ophist.h"
00130 #include "attrappl.h"
00131 //#include "view.h"
00132 //#include "docview.h"
00133 //#include "becomea.h"
00134 #include "lineattr.h"
00135 //#include "fillattr.h"
00136 //#include "fillval.h"
00137 //#include "attrval.h"
00138 //#include "arrows.h"
00139 #include "gclips.h"
00140 #include "blobs.h"
00141 #include "nodeblnd.h"
00142 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00143 //#include "quality.h"
00144 //#include "qualattr.h"
00145 #include "gdraw.h"
00146 #include "ppbevel.h"
00147 //#include "cstatbar.h"
00148 #include "extender.h"
00149 #include "ngcore.h"     // NameGallery, for stretching functionality
00150 //#include "blendatt.h"
00151 #include "blndhelp.h"
00152 //#include "mario.h"
00153 #include "nodebldr.h"
00154 #include "nodepath.h"
00155 #include "pathops.h"
00156 #include "fthrattr.h"
00157 #include "pathndge.h"
00158 //#include "mrhbits.h"
00159 #include "blndtool.h"
00160 #include "cmxrendr.h"
00161 #include "attrmap.h"
00162 //#include "cmxexdc.h"
00163 
00164 CC_IMPLEMENT_DYNCREATE(NodeContourController, NodeGroup)
00165 CC_IMPLEMENT_DYNCREATE(ContourNodeTreeFactory, CompoundNodeTreeFactory)
00166 CC_IMPLEMENT_DYNCREATE(ContourNodePathProcessor, PathProcessor)
00167 CC_IMPLEMENT_DYNCREATE(ContourRecordHandler, CamelotRecordHandler)
00168 CC_IMPLEMENT_DYNAMIC(InsetPathPathProcessor, PathProcessor);
00169 
00170 // Declare smart memory handling in Debug builds
00171 #define new CAM_DEBUG_NEW
00172 
00173 // the amount to increase the RGB values of the original object to obtain 
00174 // the default colour of the contour
00175 #define DEFAULTCOLOURINCREASE 180
00176 
00177 /***********************************************************************************************
00178 
00179 >   NodeContourController::NodeContourController()
00180 
00181     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00182     Created:    12/8/99
00183     Purpose:    Constructor
00184 
00185 ***********************************************************************************************/
00186 
00187 NodeContourController::NodeContourController()
00188 {
00189     m_Steps = 5;
00190     m_Width = 5000;
00191     m_BlendType = COLOURBLEND_FADE;
00192     m_bInsetPath = FALSE;   
00193     m_pPathProc = NULL;
00194     m_PerformedExtend = FALSE;
00195 
00196 #ifdef _DEBUG   
00197     myContourID = -1;
00198     myContourBecomeAID = -1;
00199 #endif
00200 }
00201 
00202 /***********************************************************************************************
00203 
00204 >   NodeContourController::NodeContourController(Node* ContextNode,  
00205                 AttachNodeDirection Direction,  
00206                 BOOL Locked = FALSE, 
00207                 BOOL Mangled = FALSE,  
00208                 BOOL Marked = FALSE, 
00209                 BOOL Selected = FALSE    
00210                 ) ;
00211 
00212 
00213     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00214     Created:    12/8/99
00215     Purpose:    Constructor
00216 
00217 ***********************************************************************************************/
00218 NodeContourController::NodeContourController(Node* ContextNode,  
00219                 AttachNodeDirection Direction,  
00220                 BOOL Locked, 
00221                 BOOL Mangled,  
00222                 BOOL Marked, 
00223                 BOOL Selected    
00224                 ) 
00225 {
00226     m_Steps = 5;
00227     m_Width = 5000;
00228     m_BlendType = COLOURBLEND_FADE;
00229     m_bInsetPath = FALSE;   
00230     m_pPathProc = NULL;
00231 
00232 #ifdef _DEBUG   
00233     myContourID = -1;
00234     myContourBecomeAID = -1;
00235 #endif
00236 }
00237 
00238 /***********************************************************************************************
00239 
00240 >   NodeContourController::~NodeContourController()
00241 
00242     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00243     Created:    12/8/99
00244     Purpose:    Destructor
00245 
00246 ***********************************************************************************************/
00247 
00248 NodeContourController::~NodeContourController()
00249 {
00250 #ifdef _DEBUG
00251 
00252     if (myContourID > -1)
00253     {
00254         TCHAR           strId[100];
00255         camSnprintf( strId, 100, _T("Popping NodeContourController ID:  %i\n"), myContourID );
00256             
00257         TRACEUSER( "ChrisS", strId );
00258     }
00259 
00260 #endif
00261 }
00262 
00263 /***********************************************************************************************
00264 
00265 >   DocRect NodeContourController::GetBoundingRect(BOOL DontUseAttrs = FALSE, BOOL HitTest = FALSE)
00266 
00267     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00268     Created:    12/8/99
00269     Inputs:     See base class
00270     Purpose:    Get the bounding rect of this node
00271 
00272 ***********************************************************************************************/
00273 DocRect NodeContourController::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
00274 {
00275     // run through all the children of me, summing their bounding rects
00276     if (!(!IsBoundingRectValid || DontUseAttrs))
00277     {   
00278         return BoundingRectangle;
00279     }   
00280 
00281     NodeContour * pContour = GetContourNode();
00282 
00283     DocRect dr;
00284 
00285     if (!pContour)
00286     {   
00287         dr = GetInsideBoundingRect();
00288 
00289         if (m_Width < 0)
00290         {
00291             dr.lo.x -= -m_Width;
00292             dr.lo.y -= -m_Width;
00293             dr.hi.x += -m_Width;
00294             dr.hi.y += -m_Width;
00295         }
00296     }
00297     else
00298     {
00299         dr = pContour->GetBoundingRect(DontUseAttrs, HitTest);
00300         dr = dr.Union(GetInsideBoundingRect());
00301     }
00302     
00303     IsBoundingRectValid = TRUE;
00304     BoundingRectangle = dr;
00305 
00306     return dr;
00307 
00308 }
00309 
00310 /***********************************************************************************************
00311 
00312 >   DocRect NodeContourController::GetBlobBoundingRect()
00313 
00314     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00315     Created:    12/8/99
00316     Inputs:     See base class
00317     Purpose:    Get the blob bounding rect of this node
00318 
00319 ***********************************************************************************************/
00320 DocRect NodeContourController::GetBlobBoundingRect()
00321 {
00322     DocRect br = GetBoundingRect(FALSE, FALSE);
00323 
00324     // get the blob size & do something with it
00325     BlobManager * pBlobMgr = GetApplication()->GetBlobManager();
00326 
00327     INT32 Width = 0;
00328 
00329     if (pBlobMgr)
00330     {
00331         Width = pBlobMgr->GetBlobSize();
00332     }
00333 
00334     br.lo.x -= Width;
00335     br.lo.y -= Width;
00336     br.hi.x += Width;
00337     br.hi.y += Width;
00338 
00339     return br;
00340 }
00341 
00342 
00343 
00344 /********************************************************************************************
00345 
00346 >   virtual void NodeContourController::SelectInRect(const DocRect& Rect, SelStateAction st)
00347 
00348     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00349     Created:    14 April 2000
00350     Inputs:     Rect    const reference to the bounding rect to use.
00351                 st      the selection state, ie SET/TOGGLE/CLEAR.
00352     Outputs:    This node may have its selection status changed, depending on the inputs.
00353 
00354     Purpose:    Helper method for the static fn SelectAllInRect() used by marquee select.
00355                 This method changes the selection state of this node according to the given
00356                 bounding rectangle and sel-state action. Most nodes will want to use the
00357                 default behaviour, which is to select themselves if their bounds lie within
00358                 the given bounding rectangle. If you want to do something special with the
00359                 marquee select, then override this method.
00360 
00361     Notes:      This method is meant to be called solely from SelectAllInRect() - if you wish
00362                 to call it from elsewhere, it *may* work as you expect, but it is a good idea
00363                 that you check your assumptions on what preconditions are necessary.
00364 
00365     Errors:     ERROR3 in DEBUG if st holds an invalid state.
00366     See also:   SelectAllInRect().
00367 
00368 ********************************************************************************************/
00369 void NodeContourController::SelectInRect(const DocRect& Rect, SelStateAction st)
00370 {
00371     // try to select each of our NodeRenderableInk children.
00372     NodeRenderableInk* pInkChild = NULL;
00373     Node* pChild = FindFirstChild();
00374     while (pChild != NULL)
00375     {
00376         if (pChild->IsAnObject())
00377         {
00378             pInkChild = (NodeRenderableInk*)pChild;
00379             if (Rect.ContainsRect(pInkChild->GetBoundingRect()))
00380             {
00381                 switch (st)
00382                 {
00383                 case CLEAR:
00384                     if (pInkChild->MarqueeSelectNode())
00385                     {
00386                         pInkChild->DeSelect(TRUE);
00387                     }
00388                     break;
00389 
00390                 case SET:
00391                     if (pInkChild->MarqueeSelectNode())
00392                     {
00393                         pInkChild->Select(TRUE);
00394                     }
00395                     break;
00396 
00397                 case TOGGLE:
00398                     if (pInkChild->MarqueeSelectNode())
00399                     {
00400                         if (pInkChild->IsSelected())
00401                             pInkChild->DeSelect(TRUE);
00402                         else
00403                             pInkChild->Select(TRUE);
00404                     }
00405                     break;
00406 
00407                 default:
00408                     ERROR3("NodeContourController::SelectInRect; Unknown SelStateAction!\n");
00409                     return;
00410                 }
00411             }
00412         }
00413         pChild = pChild->FindNext();
00414     }
00415 }
00416 
00417 
00418 
00419 /***********************************************************************************************
00420 
00421 >   void NodeContourController::Render(RenderRegion* pRender)
00422 
00423     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00424     Created:    12/8/99
00425     Inputs:     See base class
00426     Purpose:    Renders this node
00427 
00428 ***********************************************************************************************/
00429 void NodeContourController::Render(RenderRegion* pRender)
00430 {
00431     if (m_bInsetPath)
00432     {
00433         PathProcessor * pProc = pRender->GetFirstPathProcessor();
00434 
00435         if (pProc)
00436         {
00437             if (!pProc->IS_KIND_OF(InsetPathPathProcessor))
00438             {
00439                 ERROR3("Top path processor isn't an inset path processor");
00440                 m_pPathProc = NULL;
00441             }
00442             else
00443             {
00444                 pRender->PopPathProcessor();
00445                 m_pPathProc = NULL;
00446             }
00447         }
00448     }
00449 }
00450 
00451 /***********************************************************************************************
00452 
00453 >   void NodeContourController::DisableInsetPathPathProcessor()
00454 
00455     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00456     Created:    31/3/2000
00457     Inputs:     See base class
00458     Purpose:    Disables the insert path path processor (which means things are drawn)
00459 
00460 ***********************************************************************************************/
00461 void NodeContourController::DisableInsetPathPathProcessor()
00462 {
00463     if (m_pPathProc)
00464         m_pPathProc->SetActive(FALSE);
00465 }
00466 
00467 /***********************************************************************************************
00468 
00469 >   void NodeContourController::EnableInsetPathPathProcessor()
00470 
00471     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00472     Created:    31/3/2000
00473     Inputs:     See base class
00474     Purpose:    Enables the insert path path processor (which means things are drawn)
00475 
00476 ***********************************************************************************************/
00477 void NodeContourController::EnableInsetPathPathProcessor()
00478 {
00479     if (m_pPathProc)
00480         m_pPathProc->SetActive(TRUE);
00481 }
00482 
00483 
00484 
00485 /***********************************************************************************************
00486 
00487 >   SubtreeRenderState NodeContourController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip = TRUE)
00488 
00489     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00490     Created:    22/06/2004
00491     Inputs:     See base class
00492     Purpose:    Pre renders this node
00493 
00494 ***********************************************************************************************/
00495 SubtreeRenderState NodeContourController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip)
00496 {
00497     if (m_bInsetPath && pRender!=NULL && !pRender->IsPrinting())
00498     {
00499         // create & push the path processor
00500         m_pPathProc = new InsetPathPathProcessor;
00501 
00502         ERROR2IF(m_pPathProc == NULL, SUBTREE_NORENDER, _R(IDE_NOMORE_MEMORY));
00503         pRender->PushPathProcessor(m_pPathProc);
00504     }
00505     
00506 //  return SUBTREE_ROOTANDCHILDREN;
00507     return NodeGroup::RenderSubtree(pRender, ppNextNode, bClip);
00508 }
00509 
00510 /***********************************************************************************************
00511 
00512 >   BOOL NodeContourController::DoBecomeA(BecomeA* pBecomeA)
00513 
00514     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00515     Created:    12/8/99
00516     Inputs:     See base class
00517     Purpose:    Turns this node into another node of a particular type
00518 
00519 ***********************************************************************************************/
00520 BOOL NodeContourController::DoBecomeA(BecomeA* pBecomeA)
00521 {
00522     // Check for a NULL entry param                            
00523     ERROR2IF_PF(pBecomeA == NULL,FALSE,("pBecomeA is NULL"));
00524 
00525     if (!pBecomeA->BAPath())
00526     {
00527         return FALSE;
00528     }
00529 
00530     BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK);
00531     ERROR2IF_PF(!ValidReason,FALSE,("Unkown BecomeA reason %d",pBecomeA->GetReason()));
00532 
00533     UndoableOperation* pOp = pBecomeA->GetUndoOp();
00534 
00535 //  NodeGroup           * pGroup        = NULL;
00536     Node                * pNode         = NULL;
00537 //  Node                * pNextNode     = NULL;
00538     NodeHidden          * pHidden       = NULL;
00539 
00540 //  NodeListItem * pItem = NULL;
00541     List MyList;
00542 //  RegenerateContourAction * pAction = NULL;
00543 
00544     NodeContour * pContour = (NodeContour *)FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
00545 
00546     BOOL ok = TRUE;
00547 
00548     switch (pBecomeA->GetReason())
00549     {
00550     case BECOMEA_REPLACE:
00551 //      ERROR3IF(pOp == NULL, "No operation specified");
00552 
00553         if (!m_bInsetPath)
00554         {
00555             // if any of our children are selected, then we'll have to remember to select
00556             // the group created in our place.
00557             BOOL fSelectBecomeAGroup = IsParentOfSelected();
00558 
00559             if (!NodeGroup::DoBecomeA(pBecomeA))
00560                 return FALSE;
00561 
00562             // now turn into a group.
00563             NodeGroup* pGroup = BecomeAGroup(pOp);
00564 
00565             // select the group but don't bother redrawing blobs an' suchlike.
00566             if (pGroup == NULL)
00567                 ok = FALSE;
00568             else if (fSelectBecomeAGroup)
00569                 pGroup->Select(FALSE);
00570         }
00571         else
00572         {
00573             // move the contour node to being next to me
00574             if (pContour)
00575             {
00576                 // localise the attributes
00577                 if (ok)
00578                 {
00579                     if (pOp)
00580                         ok = pOp->DoLocaliseCommonAttributes(this, TRUE);
00581                     else
00582                         ok = LocaliseCommonAttributes(TRUE, FALSE, NULL);
00583                 }
00584                 
00585 //              if (ok) ok = pOp->DoMoveNode(pContour, this, NEXT);
00586 
00587                 // convert it to editable shapes
00588                 if (ok) ok = pContour->DoBecomeA(pBecomeA);
00589 
00590                 // hide me
00591                 if (ok)
00592                 {
00593                     if (pOp)
00594                         ok = pOp->DoHideNode(this, TRUE, &pHidden, TRUE);
00595                     else
00596                     {
00597                         CascadeDelete();
00598                         delete this;        // Scary!
00599                     }
00600                 }
00601             }
00602         }   
00603         break;
00604     case BECOMEA_PASSBACK :
00605         if (pBecomeA->IsBlendBecomeA())
00606         {
00607             CompoundNodeBlendBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath),
00608                 NULL, FALSE, this, pBecomeA);
00609             
00610             // now that the become a is set up call all my sub nodes with this
00611             Node * pChild = FindFirstChild();
00612             
00613             while (pChild)
00614             {
00615                 if (pChild->IsAnObject() && !pChild->NeedsParent(NULL))
00616                 {
00617                     MyBecomeA.ResetCount();
00618                     
00619                     if (pChild->CanBecomeA(&MyBecomeA))
00620                     {
00621                         // tell the become a that we're starting a block
00622                         MyBecomeA.SetNumPathNodes(MyBecomeA.GetCount());
00623                         MyBecomeA.ResetCount();
00624                         
00625                         pChild->DoBecomeA(&MyBecomeA);
00626                     }
00627                 }
00628                 
00629                 pChild = pChild->FindNext();
00630             }
00631         }
00632         else if (pBecomeA->IsCompoundBlendBecomeA ())           // test code ....
00633         {
00634             // CGS:  another compound node (of which we are a child) is requesting us to
00635             // BECOMEA_PASSBACK so get on and do it ....
00636             
00637             // this code is only called when blending contours that have been shadowed
00638             // and when converting to editable shapes (in the forementioned case)
00639             
00640             CompoundNodeBlendBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath),
00641                 NULL, FALSE, this, pBecomeA);
00642             
00643             Node * pChild = FindFirstChild();
00644             
00645             while (pChild)
00646             {
00647                 if (pChild->IsAnObject() && !pChild->NeedsParent(NULL))
00648                 {
00649                     MyBecomeA.ResetCount();
00650                     
00651                     if (pChild->CanBecomeA(&MyBecomeA))
00652                     {
00653                         // tell the become a that we're starting a block
00654                         MyBecomeA.SetNumPathNodes(MyBecomeA.GetCount());
00655                         MyBecomeA.ResetCount();
00656                         
00657                         pChild->DoBecomeA(&MyBecomeA);
00658                     }
00659                 }
00660                 
00661                 pChild = pChild->FindNext();
00662             }
00663         }
00664         else
00665         {
00666             pNode = FindFirstChild();
00667             while (pNode != NULL)
00668             {
00669                 if (pNode->CanBecomeA(pBecomeA))
00670                 {
00671                     if (!pNode->DoBecomeA(pBecomeA))
00672                         return FALSE;
00673                 }
00674                 
00675                 pNode = pNode->FindNext();
00676             }
00677         }
00678             
00679         break;
00680         default: break;
00681     }
00682 
00683     return ok;
00684 }
00685 
00686 /********************************************************************************************
00687 
00688 >   virtual NodeRenderableInk* NodeContourController::GetInkNodeFromController() const
00689 
00690     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00691     Created:    16/10/2000
00692     Inputs:     -
00693     Outputs:    -
00694     Returns:    The ink node that is used by bevels, shadows and contours
00695     Purpose:    To return the node that we contour
00696                 
00697     SeeAlso:    
00698 
00699 ********************************************************************************************/
00700 
00701 NodeRenderableInk* NodeContourController::GetInkNodeFromController() const
00702 {
00703     // Our contoured node is the only ink node child that is not a contour
00704     Node* pKid = FindFirstChild();
00705 
00706     while (pKid != NULL)
00707     {
00708         if (pKid->IsAnObject() && !pKid->IsAContour())
00709             break;
00710 
00711         pKid = pKid->FindNext();
00712 
00713     }
00714 
00715     // pKid is now either NULL, or is the node we're looking for.
00716     ERROR3IF(pKid == NULL, "NodeContourController::GetInkNodeFromController; Can't find contoured node!");
00717     return (NodeRenderableInk*)pKid;
00718 
00719 
00720 }
00721 
00722 
00723 /********************************************************************************************
00724 
00725 >   BOOL NodeContourController::PromoteAttributeApplicationToMe(CCRuntimeClass *pAttrClass)
00726                                                                          const
00727 
00728     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00729     Created:    10/4/2000
00730     Inputs:     The attribute's runtime class
00731     Outputs:    TRUE to promote, FALSE for not promote
00732     Purpose:    Indicates whether an attribute should be promoted to me
00733     
00734 ********************************************************************************************/
00735 
00736 BOOL NodeContourController::PromoteAttributeApplicationToMe(CCRuntimeClass *pAttrClass) const
00737 {
00738     if (pAttrClass == CC_RUNTIME_CLASS(AttrJoinType))
00739         return TRUE;
00740 
00741     return FALSE;
00742 }
00743 
00744 /********************************************************************************************
00745 
00746 >   virtual NodeRenderableInk * 
00747             NodeContourController::CreateTreeFromNodeToBlend(NodeRenderableInk * pNode)
00748 
00749     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00750     Created:    20/2/2000
00751     Inputs:     The node to wrap up
00752     Outputs:    NULL for failure
00753     Returns:    The root of the new tree
00754     Purpose:    Creates a copy of my tree, wrapping up the given node
00755     
00756 ********************************************************************************************/
00757 NodeRenderableInk * 
00758             NodeContourController::CreateTreeFromNodeToBlend(NodeRenderableInk * pNode, 
00759             CCAttrMap * pAttrMap)
00760 {
00761     // remove all attributes from the node's subtree
00762     ERROR2IF(pNode->FindParent() != NULL, NULL, "Node shouldn't have parents");
00763 
00764     if (pNode->IsNodePath())
00765     {
00766         pNode->CascadeDelete();
00767     }
00768     
00769     // apply the attribute map to this node
00770     if (pNode->IsNodePath() && pAttrMap)
00771     {
00772         pNode->ApplyAttributes(pAttrMap, FALSE);
00773     }
00774 
00775     // now, lets make a copy of myself & the bevel node
00776     NodeContourController * pControl = NULL;
00777 
00778     pControl = (NodeContourController *)this->PublicCopy();
00779     ERRORIF(pControl == NULL, _R(IDE_NOMORE_MEMORY), NULL);
00780 
00781     NodeContour * pContour = (NodeContour *)FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
00782     ERROR2IF(pContour == NULL, NULL, "Can't find bevel node");
00783 
00784     // apply the attribute map of the original bevel node to the bevel copy node
00785     NodeContour * pCopyContour = (NodeContour *)pContour->PublicCopy();
00786     ERRORIF(pCopyContour == NULL, _R(IDE_NOMORE_MEMORY), NULL);
00787     
00788     CCAttrMap * pContourMap = CCAttrMap::MakeAppliedAttrMap(pContour);
00789 //  pContourMap->RemoveIndirectlyAppliedLayeredAttributes(pContour,FALSE);
00790 
00791     ERRORIF(pContourMap == NULL, _R(IDE_NOMORE_MEMORY), NULL);
00792 
00793     pCopyContour->ApplyAttributes(pContourMap, FALSE);
00794 
00795     delete pContourMap;
00796     
00797     pNode->AttachNode(pControl, FIRSTCHILD);
00798 
00799     if (m_Width < 0)
00800     {
00801         pCopyContour->AttachNode(pControl, FIRSTCHILD);
00802     }
00803     else
00804     {
00805         pCopyContour->AttachNode(pControl, LASTCHILD);
00806     }
00807 
00808     // Feathers and other GLA's get attached to the NodeCompoundController
00809 /*  Node* pN = FindFirstChild();
00810     while(pN->IsAnAttribute())
00811     {
00812         NodeAttribute* pNA = (NodeAttribute*) pN->PublicCopy();
00813         pControl->AttachNode(pNA, FIRSTCHILD);
00814         pNA->LinkToGeometry(pControl);
00815         pN = pN->FindNext();
00816     }*/
00817 
00818     return pControl;
00819 }
00820 
00821 
00822 /***********************************************************************************************
00823 
00824 >   BOOL NodeContourController::CanBecomeA(BecomeA* pBecomeA)
00825 
00826     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00827     Created:    12/8/99
00828     Inputs:     See base class
00829     Purpose:    Can this node turn itself into a node of a particular type ?
00830 
00831 ***********************************************************************************************/
00832 BOOL NodeContourController::CanBecomeA(BecomeA* pBecomeA)
00833 {
00834     // Can only convert to paths
00835     if (!pBecomeA->BAPath())
00836         return FALSE;
00837 
00838     if (pBecomeA->IsCounting())
00839     {
00840         NodeRenderableInk* pNode = (NodeRenderableInk*)FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk));
00841         while (pNode)
00842         {
00843             if (!pNode->NeedsParent(NULL))
00844             {
00845                 // Update number of children that can become a path
00846                 pNode->CanBecomeA(pBecomeA);
00847             }
00848 
00849             pNode = (NodeRenderableInk*)pNode->FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
00850         }
00851     }
00852     
00853     return TRUE;
00854 }
00855 
00856 /***********************************************************************************************
00857 
00858 >   String NodeContourController::Describe(BOOL Plural, BOOL Verbose)
00859 
00860     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00861     Created:    12/8/99
00862     Inputs:     See base class
00863     Purpose:    Gets the string descriptor for this object
00864 
00865 ***********************************************************************************************/
00866 String NodeContourController::Describe(BOOL Plural, BOOL Verbose)
00867 {
00868     String Name;
00869     Name.Load(_R(IDS_CONTOUR_NODE_NAME));
00870 
00871     if (Plural)
00872     {
00873         Name += _T("s");
00874     }
00875 
00876     return Name;
00877 }
00878 
00879 /***********************************************************************************************
00880 
00881 >   ChangeCode NodeContourController::OnChildChange(ObjChangeParam* pParam)
00882 
00883     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00884     Created:    12/8/99
00885     Inputs:     See base class
00886     Purpose:    Regenerates the node
00887 
00888 ***********************************************************************************************/
00889 ChangeCode NodeContourController::OnChildChange(ObjChangeParam* pParam)
00890 {
00891     BOOL bRegen = TRUE;
00892     BOOL bCache = FALSE;
00893 
00894     if (m_PerformedExtend)
00895     {
00896         // this means that we have done an extend
00897         // which added the regen action into the action list
00898         // so no need to regen the node
00899         // just reset the flag and leave (sjk 4/8/2000)
00900         m_PerformedExtend = FALSE;
00901         return CC_OK;
00902     }
00903 
00904 //  Node * pChild = NULL;
00905 
00906 //  Node * pNextChild = NULL;
00907 
00908     UndoableOperation* pOp = NULL;
00909     if (pParam->GetOpPointer())
00910     {
00911         if (pParam->GetOpPointer()->IsKindOf(CC_RUNTIME_CLASS(UndoableOperation)))
00912         {
00913             pOp = pParam->GetOpPointer();
00914         }
00915     }
00916 
00917     Document * pDoc = Document::GetCurrent();
00918 
00919     Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
00920 
00921     if (!pOp)
00922     {
00923         // invalidate me & return
00924         if (pDoc)
00925         {
00926             if (pParam->GetChangeFlags ().RegenerateNode)
00927             {
00928                 RegenerateNode(pOp, bCache, FALSE);     // CGS:  it is now legitimate for us to
00929                                                         // do this
00930             }
00931 
00932             pDoc->ForceRedraw(pSpread, GetBoundingRect(FALSE, FALSE), FALSE, this);
00933         }
00934 
00935         return CC_OK;
00936     }
00937 
00938 //  CCRuntimeClass * pClass = NULL;
00939 
00940 //  ActionList * pActionList = NULL;
00941 
00942 //  Action *pAction = NULL;
00943 
00944 //  BOOL bDelete = FALSE;
00945 //  INT32 NumChildrenThatNeedMe = 0;
00946 
00947     List TextNodeList;
00948 //  NodeListItem * pItem = NULL;
00949 
00950     List ChildList;
00951 
00952 //  Node * pParent = NULL;
00953 
00954     DocRect Bounds;
00955 
00956 //  BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00957 //  INT32 BlobSize = 0;
00958 
00959     TRACEUSER( "DavidM", _T("Contour - onchildchange\n"));
00960 
00961     if (pParam->GetChangeType() == OBJCHANGE_FINISHED)
00962     {
00963         // always force a redraw
00964 //      if (pDoc && pSpread)
00965 //      {
00966 //          // do the cached bounding rect first
00967 //          pDoc->ForceRedraw(pSpread, BoundingRectangle, FALSE, this);
00968 //          pDoc->ForceRedraw(pSpread, GetBoundingRect(), FALSE, this);
00969 //      }
00970         
00971         if (pOp != NULL)
00972         { 
00973             // Ok! Tell all my children to regenerate (in some cases !)
00974 
00975             // whatever happens, invalidate this region
00976 //          ReleaseCached();
00977 //          pOp->DoInvalidateRegion(pSpread, BoundingRectangle);
00978             
00979             if( pOp->IS_KIND_OF(OpPathNudge) )
00980             {
00981                 bRegen = TRUE;
00982             }
00983             // check for ops which we do nothing with
00984             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(TransOperation)) &&
00985                 !pOp->IS_KIND_OF(OpMovePathPoint))
00986             {
00987                 if (IsSelected())
00988                     return CC_OK;
00989 
00990                 return NodeGroup::OnChildChange(pParam);
00991             }
00992             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpApplyAttrib)))
00993             {
00994                 if (pSpread)
00995                 {
00996                     ReleaseCached();
00997                     pOp->DoInvalidateRegion( pSpread, GetBoundingRect() );
00998                 }
00999                 
01000                 CCRuntimeClass * pClass = ((OpApplyAttrib *)pOp)->GetValueChangeType();
01001 
01002                 bRegen = FALSE;
01003 
01004                 // if we don't have a class for the attribute being applied, try another
01005                 // method
01006                 if (pClass)
01007                 {
01008                     // only regen for text changes, or for arrow head / tail changes
01009                     if (pClass->IsKindOf(CC_RUNTIME_CLASS(AttrTxtBase)) ||
01010                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrStartArrow)) ||
01011                         pClass->IsKindOf(CC_RUNTIME_CLASS(AttrEndArrow)))
01012                     {
01013                                                 
01014                         bRegen = TRUE;
01015                     }
01016                     else if (pClass->IS_KIND_OF(AttrStrokeColour))
01017                     {
01018                         bRegen = FALSE;
01019                     }
01020                     else if (pClass->IsKindOf(CC_RUNTIME_CLASS(AttrLineWidth)))
01021                     {
01022                         // need to check the selection to see if there are any non-closed
01023                         // paths in it - if so, we need to regenerate
01024                         bRegen = FALSE;
01025                         
01026                         Range * pSel = GetApplication()->FindSelection();
01027 
01028                         if (pSel)
01029                         {
01030                             Node * pStepNode = pSel->FindFirst();
01031 
01032                             while (pStepNode)
01033                             {
01034                                 if (pStepNode->IsNodePath())
01035                                 {
01036                                     if ( !((NodePath*)pStepNode)->InkPath.IsClosed() )
01037                                     {
01038                                         bRegen = TRUE;
01039                                         break;
01040                                     }
01041                                 }
01042 
01043                                 pStepNode = pSel->FindNext(pStepNode);
01044                             }
01045                         }
01046                     }
01047                     else if (pClass->IS_KIND_OF(AttrJoinType))
01048                     {
01049                         // join type attribute being applied ? then apply it
01050                         // to me !
01051                         if (DealWithJoinTypeChange(pOp))
01052                             return CC_OK;
01053 
01054                         return CC_FAIL;
01055                     }
01056                 }
01057             }
01058             // this operation is called for changes in fills (e.g. dragging a linear fill blob,
01059             // and then dropping it
01060             // we need to stop regeneration for normal fills, but regen for transparency fills
01061             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpReplaceAttributes)))
01062             {
01063                 // find out the op name
01064                 String_256 OpName;
01065 
01066                 if (pSpread)
01067                 {
01068                     ReleaseCached();
01069                     pOp->DoInvalidateRegion(pSpread, GetBoundingRect());
01070                 }
01071 
01072                 // don't do colour fills, but do transparent fills
01073                 NodeAttribute * pAttr = ((OpReplaceAttributes *)pOp)->GetAttribute();
01074 
01075                 if (pAttr)
01076                 {
01077                     if (pAttr->IsAFillAttr())
01078                     {
01079                         bRegen = FALSE;
01080                     }
01081 
01082                     if (pAttr->IsATranspFill())
01083                     {
01084                         bRegen = TRUE;
01085                     }                       
01086                 }
01087             }
01088             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpDelete)))
01089             {
01090                 // MRH 1/7/00 - If this is kept false then we do not get the right results when deleting an object out of a group!
01091 //              bRegen = FALSE;
01092                 bRegen = TRUE;
01093             }
01094             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpCopy)))
01095             {
01096                 bRegen = FALSE; // copying doesn't change the appearance of the contour
01097             }
01098             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpCut)))
01099             {
01100                 bRegen = TRUE; //FALSE; // cutting can change the contours appearance as shapes/letters are removed
01101             }
01102             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpPaste)))
01103             {
01104                 bRegen = TRUE; //FALSE; // pasting can change the contours appearance as shapes/letters are added
01105             }
01106             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpTextFormat)))
01107             {
01108                 return NodeGroup::OnChildChange(pParam);            
01109             } 
01110             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpDeleteTextStory)))
01111             {
01112                 // regen for all other ops
01113                 return NodeGroup::OnChildChange(pParam);
01114             }
01115             else if (pOp->IsKindOf(CC_RUNTIME_CLASS(CarbonCopyOp)))
01116             {
01117                 bRegen = FALSE; // copying doesn't change the appearance of the contour
01118             }
01119 
01120             if (bRegen)
01121             {
01122                 // invalidate the old region
01123                 if (pSpread)
01124                 {
01125                     ReleaseCached();
01126                     pOp->DoInvalidateRegion(pSpread, BoundingRectangle);                    
01127                 }
01128                 
01129                 RegenerateNode(pOp, bCache, FALSE);
01130                 return CC_OK;
01131 
01132             }
01133         }
01134         else
01135         {
01136             // do nothing
01137         }           
01138     }
01139 
01140     return NodeGroup::OnChildChange(pParam);
01141 }
01142 
01143 /***********************************************************************************************
01144 
01145 >   BOOL NodeContourController::DealWithJoinTypeChange(UndoableOperation * pOp)
01146 
01147     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01148     Created:    12/8/99
01149     Inputs:     See base class
01150     Purpose:    Promotes any join type change messages up to me
01151 
01152 ***********************************************************************************************/
01153 BOOL NodeContourController::DealWithJoinTypeChange(UndoableOperation * pOp)
01154 {
01155     // first, find the attribute which has been applied
01156     /*
01157     Node * pNode = FindFirstDepthFirst();
01158     Node * pAttr = NULL;
01159 
01160     while (pNode)
01161     {
01162         if (pNode->IsAnAttribute() && pNode->FindParent()->IsSelected())
01163         {
01164             if (pNode->IS_KIND_OF(AttrJoinType))
01165             {
01166                 pAttr = pNode;
01167                 break;
01168             }
01169         }
01170 
01171         pNode = pNode->FindNextDepthFirst(this);
01172     }
01173 
01174     // failed to find an therefore must be a default attribute or the attribute has
01175     // been promoted
01176     if (!pAttr)
01177     {
01178         // get the default attribute applied to me
01179         if (!FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType), (NodeAttribute **)&pAttr))
01180         {
01181             // still can't find an attribute ? Regen anyway
01182             return RegenerateNode(pOp, FALSE, TRUE);
01183         }
01184 
01185         if (!pAttr)
01186         {
01187             // still can't find an attribute ? Regen anyway
01188             return RegenerateNode(pOp, FALSE, TRUE);
01189         }
01190     }
01191 
01192     // now we've found the attribute which has been applied, then apply this to me !
01193 
01194     JoinTypeAttribute jt;
01195     jt.JoinType = ((AttrJoinType *)pAttr)->Value.JoinType;
01196 
01197     if (!NodeAttribute::ApplyAttributeToNode(this, CC_RUNTIME_CLASS(AttrJoinType),
01198         &jt, pOp))
01199     {
01200         return FALSE;
01201     }
01202 
01203     BOOL ok = RegenerateNode(pOp, FALSE, TRUE);
01204     */
01205 
01206 // Changed this because RegenerateNode rteurn value now means "done something" or "not done something"
01207 // both of which are OK
01208 //  BOOL ok = RegenerateNode(pOp, FALSE, TRUE);
01209 //
01210 //  return ok;
01211     RegenerateNode(pOp, FALSE, TRUE);
01212     return TRUE;
01213 }
01214 
01215 
01216 
01217 
01218 /***********************************************************************************************
01219 
01220 >   BOOL NodeContourController::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState = TRUE,
01221                                                                 BOOL DoPreTriggerEdit = TRUE)
01222 
01223     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>; Karim 20/01/2000
01224     Created:    12/8/99
01225     Inputs:     pParam                  describes the way an op wants to change the node
01226                 SetOpPermissionState    if TRUE the OpPermission of nodes should be set
01227                 DoPreTriggerEdit        if TRUE then NameGallery::PreTriggerEdit is called.
01228                                         *Must* be TRUE if the calling Op may make any nodes
01229                                         change their bounds, eg move, line width, cut.
01230                                         Use TRUE if unsure.
01231     Purpose:    Does the allow op mechanism
01232 
01233 ***********************************************************************************************/
01234 
01235 BOOL NodeContourController::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState,
01236                                                             BOOL DoPreTriggerEdit)
01237 {
01238     ERROR2IF(pParam==NULL,FALSE,"NodeContourController::AllowOp() - pParam==NULL");
01239 
01240     // Set up a flag to see if any of the child objects get changed
01241     BOOL allowed=TRUE;
01242 
01243     UndoableOperation* pOp = pParam->GetOpPointer();
01244 
01245     if (pOp)
01246     {
01247         // can't bevel a contoured object
01248         if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpCreateBevel)))
01249             allowed = FALSE;
01250         else
01251         if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpRemoveBlend)))
01252         {
01253             allowed = FALSE;
01254             pParam->SetReasonForDenial(_R(IDS_CANT_REMOVE_BLEND_WHEN_CONTOURED));
01255         }
01256     }
01257 
01258     if (pParam->GetDirection() != OBJCHANGE_CALLEDBYCHILD ||
01259         pParam->GetCallingChild() != NULL)
01260     {
01261         BOOL AnyAllowed = AllowOp_AccountForCompound( pParam,
01262                                                       SetOpPermissionState,
01263                                                       DoPreTriggerEdit );
01264 
01265         // if called by a parent, just pass this result back
01266         if (pParam->GetDirection() == OBJCHANGE_CALLEDBYPARENT)
01267             return AnyAllowed;
01268     }
01269 
01270     Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
01271     if (!pSpread)
01272         pSpread = Document::GetSelectedSpread();
01273 
01274     // decide if we allow it ... err we do!
01275     // if we allowed it, see if our parents do ...
01276     if (allowed && Parent!=NULL && pParam->GetDirection()!=OBJCHANGE_CALLEDBYPARENT)
01277     {
01278         ObjChangeDirection OldDirection=pParam->GetDirection();
01279         pParam->SetCallingChild(this);
01280         pParam->SetDirection(OBJCHANGE_CALLEDBYCHILD);
01281         allowed=Parent->AllowOp(pParam,SetOpPermissionState,DoPreTriggerEdit);
01282         pParam->SetDirection(OldDirection);
01283     }
01284 
01285     // if setting permisions ...
01286     if (SetOpPermissionState)
01287     {
01288         // if allowed, say it's ok to proceed
01289         if (allowed)
01290         {
01291             SetOpPermission(PERMISSION_ALLOWED, TRUE);
01292             
01293             if (pParam->GetDirection()==OBJCHANGE_CALLEDBYCHILD || pParam->GetChangeFlags().Attribute)
01294             {
01295                 if (pOp!=NULL)
01296                 {
01297                     if (allowed) 
01298                         allowed=pOp->DoInvalidateNodeRegion(this,TRUE);
01299                     if (allowed) 
01300                         allowed=pOp->DoInvalidateNodeRegion((NodeContour *)this->FindFirstChild(CC_RUNTIME_CLASS(NodeContour)),TRUE);
01301                     if (allowed && pSpread) 
01302                         allowed=pOp->DoInvalidateRegion(pSpread, GetBoundingRect());
01303                 }
01304             }
01305         }
01306         else
01307             SetOpPermission(PERMISSION_DENIED,TRUE);
01308     }
01309 
01310     // if we're ok so far and were asked to do a PreTriggerEdit, then
01311     // determine whether the Op may change the bounds of some nodes.
01312     // If it may, then call NameGallery::PreTriggerEdit.
01313     if (allowed && DoPreTriggerEdit)
01314     {
01315         // if the Op is non-NULL then query its MayChangeNodeBounds() method.
01316         UndoableOperation* pChangeOp = pParam->GetOpPointer();
01317         if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds())
01318         {
01319             if (NameGallery::Instance())
01320                 allowed = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this);
01321         }
01322     }
01323 
01324     return allowed;
01325 }
01326 
01327 /***********************************************************************************************
01328 
01329 >   BOOL NodeContourController::RegenerateNode(UndoableOperation * pOp = NULL,
01330         BOOL bCacheRender = FALSE);
01331 
01332     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01333     Created:    12/8/99
01334     Inputs:     See base class
01335     Returns:    TRUE to indicate that the node was changed, FALSE if nothing changed
01336     Purpose:    Regenerates the node
01337 
01338 ***********************************************************************************************/
01339 BOOL NodeContourController::RegenerateNode(UndoableOperation * pOp, BOOL bCacheRender,
01340                                            BOOL bInformParents)
01341 {
01342     // no need to regenerate if we're printing !
01343     if (IsPrinting())
01344         return FALSE;
01345     
01346     Document * pDoc = Document::GetCurrent();
01347 
01348     NodeContour * pContour = GetContourNode();
01349 
01350     // can't regenerate if we don't have a contour node
01351     if (!pContour)
01352         return FALSE;
01353 
01354     //Spread * pSpread = FindParentSpread(); // this can die if the item has been deleted as it is in no tree!
01355     Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
01356     if (!pSpread)
01357         pSpread = Document::GetSelectedSpread();
01358 
01359     ERROR2IF(pSpread==NULL,FALSE,"Failed to get a spread pointer!"); // MRH 1/7/00
01360 
01361     if (!pOp)
01362     {   
01363         // inform parents a change is about to happen
01364         if (bInformParents)
01365             PreInformParentsOfRegenerate();
01366 
01367         if (pDoc && pSpread)
01368             pDoc->ForceRedraw(pSpread, BoundingRectangle, FALSE, this);
01369     
01370         pContour->GenerateContour();
01371 
01372         InvalidateBoundingRect(TRUE);
01373         
01374         if (pDoc)
01375             pDoc->ForceRedraw(pSpread, GetBoundingRect(TRUE, FALSE), FALSE, this);
01376 
01377         // inform parents that change has happened
01378         if (bInformParents)
01379             PostInformParentsOfRegenerate();
01380     }
01381     else
01382     {
01383         List ContList;
01384         NodeListItem * pItem = NULL;
01385         RegenerateContourAction* pRegenAction = NULL;
01386 
01387         if (pOp)
01388         {
01389             ALLOC_WITH_FAIL(pItem, new NodeListItem, pOp);
01390 
01391             pItem->pNode = this;
01392             ContList.AddTail(pItem);
01393             
01394             // invalidate my region first
01395             ReleaseCached();
01396             if (!pOp->DoInvalidateRegion(pSpread, BoundingRectangle))
01397                 return FALSE;
01398             
01399             // set up the action to regenerate the bevel on this undo operation
01400             if (RegenerateContourAction::Init(pOp, pOp->GetUndoActionList(),&ContList,&pRegenAction,
01401                 bCacheRender)  == AC_FAIL)
01402             {
01403                 ERROR3("RegenerateContourAction::Init failed !\n");
01404                 return FALSE;
01405             }
01406             ContList.DeleteAll();
01407 
01408             // invalidate my region again
01409             ReleaseCached();
01410             if (!pOp->DoInvalidateRegion(pSpread, BoundingRectangle))
01411                 return FALSE;
01412         }
01413     }
01414         
01415     // make sure we kick the sel range
01416     GetApplication()->UpdateSelection();
01417 
01418     return TRUE;
01419 }
01420 
01421 /***********************************************************************************************
01422 
01423 >   NodeContour * NodeContourController::GetContourNode()
01424 
01425     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01426     Created:    12/8/99
01427     Inputs:     See base class
01428     Purpose:    Regenerates the node
01429 
01430 ***********************************************************************************************/
01431 NodeContour * NodeContourController::GetContourNode()
01432 {
01433     NodeContour * pNode = (NodeContour *)FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
01434 
01435     // ERROR2IF(pNode == NULL, NULL, "Can't find contour node");
01436 
01437     return pNode;
01438 }
01439 
01440 /***********************************************************************************************
01441 
01442 >   void NodeContourController::RenderEORDrag(RenderRegion * pRegion)
01443 
01444     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01445     Created:    12/8/99
01446     Inputs:     See base class
01447     Purpose:    Regenerates the node
01448 
01449 ***********************************************************************************************/
01450 void NodeContourController::RenderEorDrag(RenderRegion * pRender)
01451 {
01452     BOOL bChildrenSelected = FALSE;
01453 
01454     Node * pNode = FindFirstChild();
01455     Node * pInsideNode = NULL;
01456 
01457     // check for a select inside drag
01458     while (pNode)
01459     {
01460         if (pNode->IsSelected() && pNode->IsAnObject())
01461         {
01462             bChildrenSelected = TRUE;
01463         }
01464 
01465         pNode = pNode->FindNext();
01466     }
01467 
01468     if (!bChildrenSelected)
01469     {
01470         Node * pSelParent = FindParent();
01471         
01472         while (pSelParent && pSelParent->IsAnObject())
01473         {
01474             if (pSelParent->IsSelected())
01475             {
01476                 bChildrenSelected = TRUE;
01477                 break;
01478             }
01479 
01480             pSelParent = pSelParent->FindParent();
01481         }
01482     }
01483     
01484     NodeContour * pContour = GetContourNode();
01485 
01486     if ((bChildrenSelected || IsSelected()))
01487     {
01488         if (GetWidth() < 0)
01489         {
01490             if (pContour)
01491             {   
01492                 pContour->RenderEorDrag(pRender);
01493             }   
01494         }
01495         else
01496         {
01497             // render all selected nodes eor drags
01498             pInsideNode = FindFirstDepthFirst();
01499             
01500             while (pInsideNode && pInsideNode != this)
01501             {
01502                 if (pInsideNode->IsAnObject())
01503                 {
01504                     ((NodeRenderableInk *)pInsideNode)->RenderEorDrag(pRender);
01505                 }
01506                 
01507                 pInsideNode = pInsideNode->FindNextDepthFirst(this);
01508             }
01509         }
01510 
01511     }
01512     else
01513     {
01514         // render all selected nodes eor drags
01515         pInsideNode = FindFirstDepthFirst();
01516 
01517         while (pInsideNode && pInsideNode != this)
01518         {
01519             if (pInsideNode->IsSelected() && pInsideNode->IsAnObject())
01520             {
01521                 ((NodeRenderableInk *)pInsideNode)->RenderEorDrag(pRender);
01522             }
01523 
01524             pInsideNode = pInsideNode->FindNextDepthFirst(this);
01525         }
01526     }
01527 }
01528 
01529 /***********************************************************************************************
01530 
01531 >   INT32 NodeContourController::ComplexHide(UndoableOperation* pOp, Node* pNextInRange)
01532 
01533     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01534     Created:    12/8/99
01535     Inputs:     See base class
01536     Purpose:    Hides this node
01537 
01538 ***********************************************************************************************/
01539 INT32 NodeContourController::ComplexHide(UndoableOperation* pOp, Node* pNextInRange)
01540 {
01541     NodeHidden * pHidden = NULL;
01542     BOOL ok = pOp->DoHideNode(this, TRUE, &pHidden, TRUE);
01543 
01544     if (ok)
01545         return 1;
01546 
01547     return -1;
01548 }
01549 
01550 /***********************************************************************************************
01551 
01552 >   void NodeContourController::Transform(TransformBase &Trans)
01553 
01554     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01555     Created:    12/8/99
01556     Inputs:     See base class
01557     Purpose:    Regenerates the node
01558 
01559 ***********************************************************************************************/
01560 void NodeContourController::Transform(TransformBase &Trans)
01561 {
01562     // See GroupCanTransformCached()
01563     NodeGroup::Transform(Trans);
01564 
01565     // regenerate for transforms which aren't scales or translations
01566     TransformBase *pTrans = &Trans;
01567 
01568     if (pTrans->IsKindOf(CC_RUNTIME_CLASS(Trans2DMatrix)))
01569     {
01570         Trans2DMatrix* pMat = (Trans2DMatrix*)pTrans;
01571         BOOL HasBeenFlipped = FALSE;
01572 
01573         if(pMat->GetWorkingQuadrant() == 2 || pMat->GetWorkingQuadrant() == 4)
01574         {
01575             HasBeenFlipped = TRUE;
01576         }
01577 
01578         // scale the width too
01579         if (pMat->GetAspect().MakeDouble() == 1.0 && pMat->GetSkew() == 0 && !HasBeenFlipped)
01580         {
01581             m_Width = (MILLIPOINT)(((double)m_Width) * pMat->GetScale().MakeDouble());
01582             BoundingRectangle = GetBoundingRect();
01583         }
01584         else
01585         {
01586             RegenerateNode(NULL, FALSE);
01587             BoundingRectangle = GetBoundingRect();
01588         }
01589     }
01590 }
01591 
01592 
01593 
01594 /***********************************************************************************************
01595 
01596 >   BOOL NodeContourController::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
01597 
01598     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01599     Created:    12/8/99
01600     Inputs:     See base class
01601     Purpose:    Writes the node to a file
01602 
01603 ***********************************************************************************************/
01604 BOOL NodeContourController::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
01605 {
01606     CXaraFileRecord Rec(TAG_CONTOURCONTROLLER, TAG_CONTOUR_CONTROLLER_SIZE);
01607     //TAG_CONTOUR_CONTROLLER_SIZE       = 4 + 4 + 1 + 8 + 8 + 8 + 8;
01608     BOOL ok = TRUE;
01609 
01610     if (ok) ok = Rec.Init();
01611     
01612     if (ok) ok = Rec.WriteINT32(m_Steps);
01613     if (ok) ok = Rec.WriteINT32(m_Width);
01614 
01615     BYTE Type = (BYTE)m_BlendType;
01616 
01617     if (m_bInsetPath)
01618         Type |= 128;
01619 
01620     if (ok) ok = Rec.WriteBYTE(Type);
01621     if (ok) ok = Rec.WriteDOUBLE((double)m_ObjBiasGain.GetBias());
01622     if (ok) ok = Rec.WriteDOUBLE((double)m_ObjBiasGain.GetGain());
01623     if (ok) ok = Rec.WriteDOUBLE((double)m_AttrBiasGain.GetBias());
01624     if (ok) ok = Rec.WriteDOUBLE((double)m_AttrBiasGain.GetGain());
01625 
01626     if (ok) ok = pFilter->Write(&Rec);
01627 
01628     return ok;
01629 }
01630 
01631 /***********************************************************************************************
01632 
01633 >   BOOL NodeContourController::WritePreChildrenNative(BaseCamelotFilter* pFilter)
01634 
01635     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01636     Created:    12/8/99
01637     Inputs:     See base class
01638     Purpose:    Writes the node to a file
01639 
01640 ***********************************************************************************************/
01641 BOOL NodeContourController::WritePreChildrenNative(BaseCamelotFilter* pFilter)
01642 {
01643     return WritePreChildrenWeb(pFilter);
01644 }
01645 
01646 /***********************************************************************************************
01647 
01648 >   BOOL NodeContourController::PostImport()
01649 
01650     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01651     Created:    12/8/99
01652     Inputs:     See base class
01653     Purpose:    Creates the contour after import
01654 
01655 ***********************************************************************************************/
01656 
01657 
01658 /***********************************************************************************************
01659 
01660 >   BOOL NodeContourController::PostImport()
01661 
01662     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01663     Created:    12/8/99
01664     Inputs:     See base class
01665     Purpose:    Creates the contour after import
01666 
01667 ***********************************************************************************************/
01668 BOOL NodeContourController::PostImport()
01669 {
01670     RegenerateNode(NULL, FALSE);
01671     return TRUE;
01672 }
01673 
01674 /***********************************************************************************************
01675 
01676 >   BOOL NodeContourController::EndBlendStep(BlendNodeParam * pParam)
01677 
01678     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01679     Created:    12/8/99
01680     Inputs:     See base class
01681     Purpose:    Starts the blending of a step
01682 
01683 ***********************************************************************************************/
01684 BOOL NodeContourController::EndBlendStep(BlendNodeParam * pParam)
01685 {
01686     // CGS:  firstly, are we just rendering, or are we making shapes?
01687 
01688     if (!pParam->GetHandleBecomeA ())
01689     {
01690         // were just rendering ....
01691 
01692         BOOL okToRender = TRUE;
01693         
01694         // get hold of the current path processor ....
01695         if (pParam->GetPathProcessor ())
01696         {
01697             ERROR2IF(!pParam->GetPathProcessor ()->IS_KIND_OF(SumAllPathsPathProcessor),
01698                      FALSE, "First path processor isn't a sum all paths path processor");
01699 
01700             // grab the path processor
01701             SumAllPathsPathProcessor * pProc = (SumAllPathsPathProcessor *) pParam->GetPathProcessor ();
01702 
01703             pProc->SetEnabled(FALSE);       // disable it
01704 
01705             // we can now get on and generate intermediate blend steps.  NOTE:  this node contour
01706             // controller should not be touched at all - since it is held within the tree
01707             // so we make a copy that we can play with ....
01708                 
01709             Node* copy = NULL;
01710             this->NodeCopy (&copy);
01711 
01712             NodeContourController* ptrCopy = (NodeContourController*) copy;
01713 
01714             if (!ptrCopy) { return (FALSE); }       // out of memory
01715 
01716             // find the contour
01717             NodeContour * pContourCopy = (NodeContour *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
01718             ERROR2IF(pContourCopy == NULL, FALSE, "Can't find the contour node");
01719 
01721 
01722             // now the fun begins, lest blend attributes for this particular blend step ....
01723             
01724             CCAttrMap * pMap = NULL;                        // if pEndControl is a contour as well
01725             CCAttrMap * pPrimaryContourAttrMap = NULL;      // if pEndControl is not a contour
01726 
01727             // blend the contour's attributes
01728             NodeContourController * pEndControl = (NodeContourController *)FindAssociatedBlendNode(pParam);
01729 
01730             if (pEndControl)
01731             {
01732                 // we are blending two contours YIPEE!      
01733                 
01734                 NodeContour * pEndContour = (NodeContour *)pEndControl->FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
01735                 NodeRenderableInk * pOrigEndNode = pEndControl->GetBlendCreatedByNode();
01736 
01737                 // somethings a bit wrong if either of these fire ....
01738                 ERROR2IF(pOrigEndNode == NULL, FALSE, "Cant find the original contour node which this is based on");
01739                 ERROR2IF(!pOrigEndNode->IS_KIND_OF(NodeContourController), FALSE, "Original node isn't a NodeContourController");
01740 
01741                 pOrigEndNode = (NodeContour *)pOrigEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
01742 
01743                 BlendPath MyStartPath;
01744                 MyStartPath.Initialise((NodeRenderableInk *)pContourCopy->PublicCopy(), -1, pContourCopy, 1, 0, NULL);
01745                 
01746                 BlendPath MyEndPath;
01747                 MyEndPath.Initialise((NodeRenderableInk *)pEndContour->PublicCopy(), -1, pOrigEndNode, 1, 0, NULL);
01748                 
01749                 BlendNodeParam BNParam;
01750                 BNParam.Init(pParam, &MyStartPath, &MyEndPath);
01751                 
01752                 pMap = new CCAttrMap(30);
01753 
01754                 BlendHelpers * pHelp = new BlendHelpers;
01755                 ERRORIF(pHelp == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
01756                 
01757                 // now, perform the blend of the attributes
01758                 if (!pHelp->BlendAttributes(&BNParam, pMap))
01759                 {
01760                     pParam->GetRenderRegion()->PopPathProcessor();
01761                     okToRender = FALSE;
01762                 }
01763                 
01764                 delete pHelp;
01765 
01766                 // also blend my attributes & my node contour's attributes
01767                 ptrCopy->BlendParameters ((NodeContourController*) m_pBlendCreatedByNode, (NodeContourController*) pOrigEndNode->FindParent(), pParam->GetAttrBlendRatio ());
01768             }
01769             else
01770             {
01771                 // were blending a contoured node to a non-contoured node.
01772                 // since we have not generated an attribute map, shadows that are generated
01773                 // render as white ....
01774 
01775                 // we need to make a attribute map for *this* NodeShadow, and apply it to
01776                 // the copy ....
01777 
01778                 NodeContour * pContour = (NodeContour *) FindFirstChild (CC_RUNTIME_CLASS (NodeContour));
01779 
01780                 ERROR2IF (pContour == NULL, FALSE, "Can't find Contour node");
01781 
01782                 // build an attribute map for the contour
01783                 pPrimaryContourAttrMap = CCAttrMap::MakeAppliedAttrMap(pContour);
01784 
01785                 // we don't have another node to blend to - but lets not let a little thing like that deter us! 
01786                 ptrCopy->BlendParameters ((NodeContourController *) m_pBlendCreatedByNode, pParam->GetAttrBlendRatio ());
01787             }
01788 
01789             // scan for the path that original contour was generated with
01790             NodePath* currentPath = (NodePath *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodePath));
01791 
01792             // if we find one, we know get on and merge all the paths so that the contour can
01793             // be generated correctly.
01794 
01795             // we do this by:
01796 
01797             // replacing currentPath by a group that contains all paths on the path processors list
01798             // (i.e.  we replace it by the blended paths - who have their blended attribute maps
01799             // applied to them)
01800 
01801             if (currentPath)
01802             {
01803                 currentPath->CascadeDelete ();
01804                 delete (currentPath);
01805                 currentPath = NULL;
01806 
01807                 NodeGroup* newGroup = new NodeGroup ();
01808 
01809                 ListItem* pItem = pProc->GetList ()->GetHead ();
01810                 
01811                 Path* blendPath = NULL;
01812                 CCAttrMap* blendMap = NULL;
01813 
01814                 while (pItem)
01815                 {
01816                     blendPath = ((SumAllPathsElem *) pItem)->GetPath ();
01817                     blendMap = ((SumAllPathsElem *) pItem)->GetAttrMap ();
01818                     
01819                     NodePath* newPath = new NodePath ();
01820                     INT32 numCoords = blendPath->GetNumCoords ();
01821                     newPath->InkPath.ClearPath ();
01822                     newPath->InkPath.Initialise (numCoords);
01823                     newPath->InkPath.CopyPathDataFrom (blendPath);
01824                     blendMap->ApplyAttributesToNode (newPath);
01825                     newPath->AttachNode(newGroup, LASTCHILD);
01826                     
01827                     pItem = pProc->GetList ()->GetNext (pItem);
01828                     delete ((SumAllPathsElem *) pProc->GetList()->RemoveHead ());
01829                 }
01830 
01831                 if (ptrCopy->m_Width < 0)       // an outer contour
01832                 {
01833                     newGroup->AttachNode(pContourCopy, NEXT);
01834                 }
01835                 else if (ptrCopy->m_Width > 0)
01836                 {
01837                     newGroup->AttachNode(pContourCopy, PREV);
01838                 }
01839             }
01840 
01841             // apply blended contour attributes ....
01842 
01843             if (pMap)
01844             {
01845                 pMap->ApplyAttributesToNode (pContourCopy);
01846             }
01847 
01848             if (pPrimaryContourAttrMap)
01849             {
01850                 pPrimaryContourAttrMap->ApplyAttributesToNode (pContourCopy);
01851             }
01852 
01853             // generate the contour (which will correctly be generated for merged paths if needed)
01854             
01855             if (!pContourCopy->GenerateContour())
01856             {
01857                 okToRender = FALSE;
01858             }
01859 
01860             // now render the contour controller as a complete node (just like the document renders)
01861             
01862             if (okToRender)
01863             {
01864                 // before we render this compound node, check to see if our parent is a shadow
01865                 // if it is, then delay rendering - since the shadow should be responsible
01866 
01867                 Node* parent = FindParent ();
01868 
01869                 if (parent != NULL)
01870                 {
01871                     if (!IS_A (parent, NodeShadowController))
01872                     {
01873                         RenderRegion* pRegion = pParam->GetRenderRegion();
01874 
01875                         // render the node
01876                         if (pRegion)
01877                             pRegion->RenderTreeNoCache(ptrCopy);
01878                     }
01879                     else
01880                     {
01881                         // the shadow MUST be made responsible
01882                         SetShadowDeleteThisNode (ptrCopy);
01883                     }
01884                 }
01885                 else
01886                 {
01887                     RenderRegion* pRegion = pParam->GetRenderRegion();
01888 
01889                     // render the node
01890                     if (pRegion)
01891                         pRegion->RenderTreeNoCache(ptrCopy);
01892                 }
01893             }
01894 
01895             if (pMap)
01896             {
01897                 pMap->DeleteAttributes ();
01898                 delete pMap;
01899             }
01900 
01901             if (pPrimaryContourAttrMap)
01902             {
01903                 delete pPrimaryContourAttrMap;
01904             }
01905 
01906             // if the contour is NOT shadowed, then we can safely delete the copy of it
01907 
01908             Node* parent = FindParent ();
01909 
01910             if (parent != NULL)
01911             {
01912                 if (!(IS_A (parent, NodeShadowController)))
01913                 {
01914                     ptrCopy->CascadeDelete ();
01915                     delete (ptrCopy);
01916                 }
01917             }
01918             else
01919             {
01920                 ptrCopy->CascadeDelete ();
01921                 delete (ptrCopy);
01922             }
01923         }
01924     }
01925     else    // were doing a make shapes (i.e.  become a .....)
01926     {   
01927         // get hold of the current path processor ....
01928         if (pParam->GetPathProcessor ())
01929         {
01930             ERROR2IF(!pParam->GetPathProcessor ()->IS_KIND_OF(SumAllPathsPathProcessor),
01931                      FALSE, "First path processor isn't a sum all paths path processor");
01932 
01933             // grab the path processor
01934             SumAllPathsPathProcessor * pProc = (SumAllPathsPathProcessor *)pParam->GetPathProcessor ();
01935 
01936             pProc->SetEnabled(FALSE);       // disable it
01937 
01938             // we can now get on and generate intermediate blend steps.  NOTE:  this node contour
01939             // controller should not be touched at all - since it is held within the tree
01940             // so we make a copy that we can play with ....
01941                 
01942             Node* copy = NULL;
01943             this->NodeCopy (&copy);
01944 
01945             NodeContourController* ptrCopy = (NodeContourController*) copy;
01946 
01947             if (!ptrCopy) { return (FALSE); }       // out of memory
01948 
01949             // find the contour
01950             NodeContour * pContourCopy = (NodeContour *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
01951             ERROR2IF(pContourCopy == NULL, FALSE, "Can't find the contour node");
01952 
01954 
01955             // now the fun begins, lest blend attributes for this particular blend step ....
01956             
01957             CCAttrMap * pMap = NULL;                        // if pEndControl is a contour as well
01958             CCAttrMap * pPrimaryContourAttrMap = NULL;      // if pEndControl is not a contour
01959 
01960             // blend the contour's attributes
01961             NodeContourController * pEndControl = (NodeContourController *)FindAssociatedBlendNode(pParam);
01962 
01963             if (pEndControl)
01964             {
01965                 // we are blending two contours YIPEE!      
01966                 
01967                 NodeContour * pEndContour = (NodeContour *)pEndControl->FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
01968                 NodeRenderableInk * pOrigEndNode = pEndControl->GetBlendCreatedByNode();
01969 
01970                 // somethings a bit wrong if either of these fire ....
01971                 ERROR2IF(pOrigEndNode == NULL, FALSE, "Cant find the original contour node which this is based on");
01972                 ERROR2IF(!pOrigEndNode->IS_KIND_OF(NodeContourController), FALSE, "Original node isn't a NodeContourController");
01973 
01974                 pOrigEndNode = (NodeContour *)pOrigEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeContour));
01975 
01976                 BlendPath MyStartPath;
01977                 MyStartPath.Initialise((NodeRenderableInk *)pContourCopy->PublicCopy(), -1, pContourCopy, 1, 0, NULL);
01978                 
01979                 BlendPath MyEndPath;
01980                 MyEndPath.Initialise((NodeRenderableInk *)pEndContour->PublicCopy(), -1, pOrigEndNode, 1, 0, NULL);
01981                 
01982                 BlendNodeParam BNParam;
01983                 BNParam.Init(pParam, &MyStartPath, &MyEndPath);
01984                 
01985                 pMap = new CCAttrMap(30);
01986 
01987                 BlendHelpers * pHelp = new BlendHelpers;
01988                 ERRORIF(pHelp == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
01989                 
01990                 // now, perform the blend of the attributes
01991                 if (!pHelp->BlendAttributes(&BNParam, pMap))
01992                 {
01993                     pParam->GetRenderRegion()->PopPathProcessor();
01994                 }
01995                 
01996                 delete pHelp;
01997 
01998                 // also blend my attributes & my node contour's attributes
01999                 ptrCopy->BlendParameters ((NodeContourController*) m_pBlendCreatedByNode, (NodeContourController*) pOrigEndNode->FindParent(), pParam->GetAttrBlendRatio ());
02000             }
02001             else
02002             {
02003                 // were blending a contoured node to a non-contoured node.
02004                 // since we have not generated an attribute map, shadows that are generated
02005                 // render as white ....
02006 
02007                 // we need to make a attribute map for *this* NodeShadow, and apply it to
02008                 // the copy ....
02009 
02010                 NodeContour * pContour = (NodeContour *) FindFirstChild (CC_RUNTIME_CLASS (NodeContour));
02011 
02012                 ERROR2IF (pContour == NULL, FALSE, "Can't find Contour node");
02013 
02014                 // build an attribute map for the contour
02015                 pPrimaryContourAttrMap = CCAttrMap::MakeAppliedAttrMap(pContour);
02016 
02017                 // we don't have another node to blend to - but lets not let a little thing like that deter us! 
02018                 ptrCopy->BlendParameters ((NodeContourController *) m_pBlendCreatedByNode, pParam->GetAttrBlendRatio ());
02019             }
02020 
02021             // scan for the path that original contour was generated with
02022             NodePath* currentPath = (NodePath *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodePath));
02023 
02024             // if we find one, we know get on and merge all the paths so that the contour can
02025             // be generated correctly.
02026 
02027             // we do this by:
02028 
02029             // replacing currentPath by a group that contains all paths on the path processors list
02030             // (i.e.  we replace it by the blended paths - who have their blended attribute maps
02031             // applied to them)
02032 
02033             std::list<NodePath*> pathPtrs;              // a list of the new paths (so that we may call normaliseattributes)
02034 
02035             if (currentPath)
02036             {
02037                 currentPath->CascadeDelete ();
02038                 delete (currentPath);
02039                 currentPath = NULL;
02040 
02041                 NodeGroup* newGroup = new NodeGroup ();
02042 
02043                 ListItem* pItem = pProc->GetList ()->GetHead ();
02044                 
02045                 Path* blendPath = NULL;
02046                 CCAttrMap* blendMap = NULL;
02047 
02048                 while (pItem)
02049                 {
02050                     blendPath = ((SumAllPathsElem *) pItem)->GetPath ();
02051                     blendMap = ((SumAllPathsElem *) pItem)->GetAttrMap ();
02052                     
02053                     NodePath* newPath = new NodePath ();
02054                     INT32 numCoords = blendPath->GetNumCoords ();
02055                     newPath->InkPath.ClearPath ();
02056                     newPath->InkPath.Initialise (numCoords);
02057                     newPath->InkPath.CopyPathDataFrom (blendPath);
02058                     blendMap->ApplyAttributesToNode (newPath);
02059                     newPath->AttachNode(newGroup, LASTCHILD);
02060 
02061                     pathPtrs.push_back (newPath);
02062                     
02063                     pItem = pProc->GetList ()->GetNext (pItem);
02064                     delete ((SumAllPathsElem *) pProc->GetList()->RemoveHead ());
02065                 }
02066 
02067                 if (ptrCopy->m_Width < 0)       // an outer contour
02068                 {
02069                     newGroup->AttachNode(pContourCopy, NEXT);
02070                 }
02071                 else if (ptrCopy->m_Width > 0)
02072                 {
02073                     newGroup->AttachNode(pContourCopy, PREV);
02074                 }
02075 
02076 //              newGroup->NormaliseAttributes ();
02077             }
02078 
02079             // apply blended contour attributes ....
02080 
02081             if (pMap)
02082             {
02083                 pMap->ApplyAttributesToNode (pContourCopy);
02084             }
02085 
02086             if (pPrimaryContourAttrMap)
02087             {
02088                 pPrimaryContourAttrMap->ApplyAttributesToNode (pContourCopy);
02089             }
02090 
02091             // generate the contour (which will correctly be generated for merged paths if needed)
02092             
02093             pContourCopy->GenerateContour();
02094 
02095             // now insert into the tree or passback ....
02096 
02097             BecomeA* accessPtr = pParam->GetHandleBecomeA ()->GetBecomeA ();
02098             UndoableOperation* pUndoOp = accessPtr->GetUndoOp ();
02099 
02100             Node* parent = NULL;
02101             NodeRenderableInk* pCreator = GetBlendCreatedByNode ();
02102             ERROR2IF(pCreator == NULL, FALSE, "Cant find the original bevel node which this is based on");
02103             
02104             parent = pCreator->FindParent (CC_RUNTIME_CLASS (NodeShadowController));
02105 
02106             // we MUST also scan to see if the blend was shadowed (and not the bevel) ....
02107             
02108             Node* pBlenderParent = NULL;
02109             Node* pBlender = pParam->GetNodeBlend ();
02110             ERROR2IF(pBlender == NULL, FALSE, "Cant find the original node blend which this node is based on!");
02111 
02112             pBlenderParent = pBlender->FindParent (CC_RUNTIME_CLASS (NodeShadowController));
02113 
02114             BOOL parentIsNodeShadow = FALSE;
02115 
02116             if (parent)
02117             {
02118                 if (!pBlenderParent)    // if the blend was NOT shadowed
02119                 {
02120                     parentIsNodeShadow = TRUE;
02121                 }
02122                 // else the blend was shadowed we need to insert into the tree
02123             }
02124 
02125             // are we wanting to be inserted into the tree?
02126             if (accessPtr->GetReason () == BECOMEA_REPLACE)
02127             {
02128                 if (!parentIsNodeShadow)
02129                 {
02130                     if (!pUndoOp->DoInsertNewNode (ptrCopy,pParam->GetHandleBecomeA ()->GetContextNode (),PREV,TRUE,FALSE,FALSE,TRUE))
02131                     {
02132                         return FALSE;
02133                     }
02134 
02135                     // now normalise all attributes ....
02136                     
02137                     pContourCopy->NormaliseAttributes ();
02138 
02139                     std::list<NodePath*>::iterator i;
02140 
02141                     for (i = pathPtrs.begin (); i != pathPtrs.end (); i++)
02142                     {
02143                         (*i)->NormaliseAttributes ();
02144                     }
02145 
02146                     // if we are required to insert paths into the tree - THEN we MUST call DoBecomeA on
02147                     // ptrCopy.  This call now also creates the bevel bitmap for us
02148 
02149                     if (accessPtr->GetInsertComplexBlendStepsAsPaths ())
02150                     {
02151                         ptrCopy->DoBecomeA (accessPtr);
02152                     }
02153                 }
02154                 else
02155                 {
02156                     SetShadowThisNode (ptrCopy);    // we need to set this so that NodeShadowController
02157                                                     // can shadow ptrCopy
02158                 }
02159             }
02160             // or do we just want our paths?
02161             else if (accessPtr->GetReason () == BECOMEA_PASSBACK)
02162             {
02163                 if (parentIsNodeShadow)
02164                 {
02165                     SetShadowThisNode (ptrCopy);    // we need to set this so that NodeShadowController
02166                                                     // can shadow ptrCopy
02167                 }
02168                 ptrCopy->DoBecomeA (accessPtr);
02169                 ptrCopy->SetBlenderNode (pParam->GetNodeBlend ());
02170 
02171                 // setup necessary stuff so that we can delete ptrCopy at the relevant time ....
02172                     
02173                 if (AllocatedBlendConsList (LT_BECOMEA_CONTOURSLIST))
02174                 {   
02175                     BlendConsListInsert (LT_BECOMEA_CONTOURSLIST, ptrCopy);
02176                 }
02177                 else
02178                 {
02179                     AllocBlendConsList (LT_BECOMEA_CONTOURSLIST);
02180                     BlendConsListInsert (LT_BECOMEA_CONTOURSLIST, ptrCopy);
02181                 }
02182             }
02183 
02184             if (pMap)
02185             {
02186                 pMap->DeleteAttributes ();
02187                 delete pMap;
02188             }
02189 
02190             if (pPrimaryContourAttrMap)
02191             {
02192                 delete pPrimaryContourAttrMap;
02193             }
02194         }
02195     }
02196     
02197     return TRUE;
02198 }
02199 
02200 /********************************************************************************************
02201 
02202 >   BOOL NodeContourController::BlendParameters (NodeContourController * pStart,
02203                                                  NodeContourController * pEnd, double Scale)
02204 
02205     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02206     Created:    7/7/2000
02207     Inputs:     pStart  -   the original start contour controller node (the one in the tree)
02208                 pEnd    -   the original end contour controller node (the one in the tree)
02209                 Scale   -   Scale factor to blend with
02210     Outputs:    Sets my & my shadow node child with the appropriate variables
02211     Returns:    
02212     Purpose:    Sets my & my shadow node child with the appropriate variables given the blend
02213                 step
02214     
02215 ********************************************************************************************/
02216 
02217 BOOL NodeContourController::BlendParameters (NodeContourController * pStart, NodeContourController * pEnd, double Scale)
02218 {
02219     // First, lets get hold of the shadows that we need, and check.....
02220     
02221     NodeContour * pStartContour = (NodeContour *) pStart->GetContour ();
02222     ERROR2IF(!pStartContour, FALSE, "Can't find Contour child node for start");
02223 
02224     NodeContour * pEndContour = (NodeContour *) pEnd->GetContour ();
02225     ERROR2IF(!pEndContour, FALSE, "Can't find Contour child node for end");
02226 
02227     NodeContour * pThisContour = (NodeContour *) GetContour();
02228     ERROR2IF(!pThisContour, FALSE, "Can't find Contour child node for blend node");
02229 
02230     // lets blend the number of steps first ....
02231 
02232     double startSteps = pStart->m_Steps;
02233     double endSteps = pEnd->m_Steps;
02234 
02235     double newSteps = ((endSteps - startSteps) * Scale) + startSteps;
02236     // need to manually round newSteps - cause if we just cast straight to a UINT32, things look bad
02237 
02238     if (newSteps < 0) { newSteps = 1; }     // if negative, the cast screws up
02239 
02240     UINT32 temp = (UINT32) newSteps;
02241     double remainder = (double) (newSteps - temp);
02242     if (remainder <= 0.5)   { m_Steps = temp; }
02243     else                    { m_Steps = temp + 1; }
02244 
02245     if (m_Steps <= 0) { m_Steps = 1; }      // cannot allow this to happen
02246 
02247     // now lets blend the contour width ....
02248 
02249     double startWidth = pStart->m_Width;
02250     double endWidth = pEnd->m_Width;
02251 
02252     double newWidth = ((endWidth - startWidth) * Scale) + startWidth;
02253     m_Width =  (INT32) newWidth;
02254 
02255     // now lets blend the profiles ....
02256 
02257     double StartBias = pStart->m_AttrBiasGain.GetBias();
02258     double EndBias   = pEnd->m_AttrBiasGain.GetBias();
02259     double NewBias   = ((EndBias - StartBias) * Scale) + StartBias;
02260 
02261     double StartGain = pStart->m_AttrBiasGain.GetGain();
02262     double EndGain   = pEnd->m_AttrBiasGain.GetGain();
02263     double NewGain   = ((EndGain - StartGain) * Scale) + StartGain;
02264 
02265     m_AttrBiasGain.SetBiasGain (NewBias, NewGain);
02266 
02267     StartBias = pStart->m_ObjBiasGain.GetBias();
02268     EndBias   = pEnd->m_ObjBiasGain.GetBias();
02269     NewBias   = ((EndBias - StartBias) * Scale) + StartBias;
02270 
02271     StartGain = pStart->m_ObjBiasGain.GetGain();
02272     EndGain   = pEnd->m_ObjBiasGain.GetGain();
02273     NewGain   = ((EndGain - StartGain) * Scale) + StartGain;
02274 
02275     m_ObjBiasGain.SetBiasGain (NewBias, NewGain);
02276 
02277     return (TRUE);
02278 }
02279 
02280 /********************************************************************************************
02281 
02282 >   BOOL NodeContourController::BlendParameters(NodeContourController * pStart, double Scale)
02283 
02284     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02285     Created:    7/7/2000
02286     Inputs:     pStart  -   the original start contour controller node (the one in the tree)
02287                 Scale   -   Scale factor to blend with
02288     Outputs:    Sets my & my shadow node child with the appropriate variables
02289     Returns:    
02290     Purpose:    Sets my & my shadow node child with the appropriate variables given the blend
02291                 step
02292     
02293 ********************************************************************************************/
02294 
02295 BOOL NodeContourController::BlendParameters(NodeContourController * pStart, double Scale)
02296 {
02297     // First, lets get hold of the shadows that we need, and check ....
02298     
02299     NodeContour * pStartContour = (NodeContour *) pStart->GetContour ();
02300     ERROR2IF(!pStartContour, FALSE, "Can't find Contour child node for start");
02301 
02302     NodeContour * pThisContour = (NodeContour *) GetContour();
02303     ERROR2IF(!pThisContour, FALSE, "Can't find Contour child node for blend node");
02304 
02305     // lets blend the number of steps first ....
02306 
02307     double startSteps = pStart->m_Steps;
02308     double endSteps = 0;//pStart->m_Steps / 2;
02309 
02310     double startSteps2 = pStart->m_Steps;
02311     double endSteps2 = 0;//pStart->m_Steps / 2;
02312 
02313     //double newSteps = 
02314     
02315     double newSteps = -((endSteps - startSteps) * Scale);
02316     double newSteps2 = -((endSteps2 - startSteps2) * (1-Scale));
02317     // need to manually round newSteps - cause if we just cast straight to a UINT32, things look bad
02318 
02319     newSteps = newSteps2 - newSteps;
02320 
02321     if (newSteps < 0) { newSteps = 1; }     // if negative, the cast screws up
02322 
02323     UINT32 temp = (UINT32) newSteps;
02324     double remainder = (double) (newSteps - temp);
02325     if (remainder <= 0.5)   { m_Steps = temp; }
02326     else                    { m_Steps = temp + 1; }
02327 
02328     if (m_Steps <= 0) { m_Steps = 1; }          // cannot allow this to happen
02329 
02330     // now lets blend the contour width ....
02331 
02332     double startWidth = pStart->m_Width;
02333     double endWidth = 0;
02334 
02335 /*  double startWidth2 = pStart->m_Width;
02336     double endWidth2 = 0;
02337 
02338     double newWidth = -((endWidth - startWidth) * Scale);
02339     double newWidth2 = -((endWidth2 - startWidth2) * (1-Scale));
02340 
02341     newWidth = - (newWidth2 - newWidth);*/
02342 
02343     double newWidth = ((endWidth - startWidth) * Scale) + startWidth;
02344     m_Width =  (INT32) newWidth;
02345 
02346     // now lets blend the profiles ....
02347 
02348     CProfileBiasGain defaultBiasGain;
02349     
02350     double StartBias = pStart->m_AttrBiasGain.GetBias();
02351     double EndBias   = defaultBiasGain.GetBias();
02352     double NewBias   = ((EndBias - StartBias) * Scale) + StartBias;
02353 
02354     double StartGain = pStart->m_AttrBiasGain.GetGain();
02355     double EndGain   = defaultBiasGain.GetGain();
02356     double NewGain   = ((EndGain - StartGain) * Scale) + StartGain;
02357 
02358     m_AttrBiasGain.SetBiasGain (NewBias, NewGain);
02359 
02360     StartBias = pStart->m_ObjBiasGain.GetBias();
02361     EndBias   = defaultBiasGain.GetBias();
02362     NewBias   = ((EndBias - StartBias) * Scale) + StartBias;
02363 
02364     StartGain = pStart->m_ObjBiasGain.GetGain();
02365     EndGain   = defaultBiasGain.GetGain();
02366     NewGain   = ((EndGain - StartGain) * Scale) + StartGain;
02367 
02368     m_ObjBiasGain.SetBiasGain (NewBias, NewGain);
02369 
02370     return (TRUE);
02371 }
02372 
02373 
02374 /********************************************************************************************
02375 >   NodeShadow* NodeContourController::GetShadow()
02376 
02377     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02378     Created:    8/7/2000
02379     Inputs:     -
02380     Returns:    Pointer to child contour (NULL if not found!)
02381     Purpose:    Returns a type correct pointer to child contour
02382 ********************************************************************************************/
02383 
02384 NodeContour* NodeContourController::GetContour ()
02385 {
02386     NodeContour* pBob = (NodeContour*) FindFirstChild (CC_RUNTIME_CLASS (NodeContour));
02387 
02388     return pBob;
02389 }
02390 
02391 
02392 /***********************************************************************************************
02393 
02394 >   Node* NodeContourController::SimpleCopy()
02395 
02396     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02397     Created:    12/8/99
02398     Inputs:     See base class
02399     Purpose:    Creates a copy of this node
02400 
02401 ***********************************************************************************************/
02402 Node* NodeContourController::SimpleCopy()
02403 {
02404     NodeContourController* pNode = new NodeContourController;
02405     ERRORIF(pNode == NULL, _R(IDE_NOMORE_MEMORY), NULL);
02406 
02407     if (pNode)
02408     {
02409         CopyNodeContents(pNode);
02410     }
02411 
02412     return pNode;
02413 }
02414 
02415 /********************************************************************************************
02416 >   void NodeContourController::CopyNodeContents(NodeContourController* pNewNode)
02417 
02418     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02419     Created:    05/01/2004
02420     Inputs:     -
02421     Returns:    
02422     Purpose:    Sort of backwards copy constructor
02423 
02424 ********************************************************************************************/
02425 void NodeContourController::CopyNodeContents(NodeContourController* pNewNode)
02426 {
02427     pNewNode->m_Steps = m_Steps;
02428     pNewNode->m_Width = m_Width;
02429     pNewNode->m_BlendType = m_BlendType;
02430     pNewNode->m_AttrBiasGain = m_AttrBiasGain;
02431     pNewNode->m_ObjBiasGain = m_ObjBiasGain;
02432     pNewNode->m_bInsetPath = m_bInsetPath;
02433     pNewNode->m_PerformedExtend = m_PerformedExtend;
02434 
02435     if (pNewNode->m_pPathProc)
02436         delete pNewNode->m_pPathProc;
02437     pNewNode->m_pPathProc = NULL;
02438 
02439     // These shouldn't be here!
02440     pNewNode->m_bBlendStartNode = m_bBlendStartNode;
02441     pNewNode->m_bBlendEndNode = m_bBlendEndNode;
02442     pNewNode->m_pBlendCreatedByNode = m_pBlendCreatedByNode;
02443 
02444     NodeGroup::CopyNodeContents(pNewNode);
02445 }
02446 
02447 
02448 
02449 
02450 /***********************************************************************************************
02451 >   void NodeContourController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
02452 
02453     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02454     Created:    18/12/2003
02455     Outputs:    -
02456     Purpose:    Polymorphically copies the contents of this node to another
02457     Errors:     An assertion failure will occur if NodeCopy is NULL
02458     Scope:      protected
02459                                      
02460 ***********************************************************************************************/
02461 
02462 void NodeContourController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
02463 {
02464     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
02465     ENSURE(IS_A(pNodeCopy, NodeContourController), "PolyCopyNodeContents given wrong dest node type");
02466 
02467     if (IS_A(pNodeCopy, NodeContourController))
02468         CopyNodeContents((NodeContourController*)pNodeCopy);
02469 }
02470 
02471 
02472 
02473    
02474 /***********************************************************************************************
02475 
02476 >   BOOL NodeContourController::AreOpenPathsInChildren()
02477 
02478     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02479     Created:    12/8/99
02480     Inputs:     
02481     Purpose:    Returns TRUE if any of my children are open paths
02482 
02483 ***********************************************************************************************/
02484 BOOL NodeContourController::AreOpenPathsInChildren()
02485 {
02486     Node * pNode = FindFirstChild();
02487 
02488     while (pNode)
02489     {
02490         if (pNode->IsNodePath())
02491         {
02492             if (!((NodePath *)pNode)->InkPath.IsSubPathClosed(0))
02493             {
02494                 return TRUE;
02495             }
02496         }
02497 
02498         pNode = pNode->FindNext();
02499     }
02500 
02501     return FALSE;
02502 }
02503 
02504 
02505 
02506 /********************************************************************************************
02507 
02508 >   virtual DocRect NodeContourController::ValidateExtend(const ExtendParams& ExtParams)
02509 
02510     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02511     Created:    06/12/1999
02512     Inputs:     ExtParams       description parameters for the extension.
02513     Outputs:    
02514     Returns:    TRUE if extending this Node and its children is a reversible operation,
02515                 FALSE otherwise.
02516     Purpose:    Tests the reversibility of an Extend operation applied to this node.
02517 
02518                 In the case of a NodeContourController, this function is identical to
02519                 Node's implementation, except that this controller's own NodeContour
02520                 child is ignored. This allows the NodeContour to tell its parent to
02521                 extend, without fear of infinite recursion.
02522     Errors:     
02523     See also:   IsTypeExtendible(), Extend().
02524 
02525 ********************************************************************************************/
02526 DocRect NodeContourController::ValidateExtend(const ExtendParams& ExtParams)
02527 {
02528     Node* pBob = GetContourNode();
02529     DocRect drMinExtend(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX), drThisMinExtend;
02530     for (   Node* pChildNode = FindFirstChild();
02531             pChildNode != NULL;
02532             pChildNode = pChildNode->FindNext() )
02533     {
02534         if (pChildNode == pBob)
02535             continue;
02536 
02537         drThisMinExtend = pChildNode->ValidateExtend(ExtParams);
02538         if (drMinExtend.lo.x < drThisMinExtend.lo.x) drMinExtend.lo.x = drThisMinExtend.lo.x;
02539         if (drMinExtend.lo.y < drThisMinExtend.lo.y) drMinExtend.lo.y = drThisMinExtend.lo.y;
02540         if (drMinExtend.hi.x < drThisMinExtend.hi.x) drMinExtend.hi.x = drThisMinExtend.hi.x;
02541         if (drMinExtend.hi.y < drThisMinExtend.hi.y) drMinExtend.hi.y = drThisMinExtend.hi.y;
02542     }
02543     return drMinExtend;
02544 }
02545 
02546 
02547 
02548 /********************************************************************************************
02549 
02550 >   virtual void NodeContourController::Extend(const ExtendParams& ExtParams)
02551 
02552     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02553     Created:    06/12/1999
02554     Inputs:     ExtParams       description parameters for the extension.
02555     Outputs:    Some of this node's children may have their dimensions altered.
02556     Returns:    
02557     Purpose:    Perform an Extend operation on this Node, and its children if appropriate.
02558 
02559                 In the case of a NodeContourController, this function is identical to
02560                 Node's implementation, except that this controller's own NodeContour
02561                 child is ignored. This allows the NodeContour to tell its parent to
02562                 extend, without fear of infinite recursion.
02563     Errors:     
02564     See also:   
02565 
02566 ********************************************************************************************/
02567 void NodeContourController::Extend(const ExtendParams& ExtParams)
02568 {
02569     Node* pBob = GetContourNode();
02570     for (   Node* pChildNode = FindFirstChild();
02571             pChildNode != NULL;
02572             pChildNode = pChildNode->FindNext() )
02573     {
02574         if (pChildNode == pBob)
02575             continue;
02576 
02577         pChildNode->Extend(ExtParams);
02578     }
02579 
02580     // Add an action to regen the Contour here
02581     // and instead of regening the Contour when the m_PerformedExtend occurs in the child change
02582     // cut out and let the action do the stuff
02583     m_PerformedExtend = TRUE; 
02584     RegenerateNode(ExtParams.pOp, FALSE, FALSE);
02585 }
02586 
02587 
02588 
02589 void NodeContourController::PreExportRender(RenderRegion* pRegion)
02590 {
02591 #ifdef DO_EXPORT
02592     if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
02593     {
02594         // Output "start group" token
02595         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
02596         pDC->OutputToken(_T("u"));
02597         pDC->OutputNewLine();
02598     }
02599 PORTNOTE("cmx", "Removed use of CMXRenderRegion")
02600 #ifndef EXCLUDE_FROM_XARALX
02601     else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
02602     {
02603         // mark start of a group...
02604         CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC();
02605         DocRect BBox = GetBoundingRect();
02606         pDC->StartGroup(&BBox);
02607     }
02608 #endif
02609 #endif
02610 }
02611 
02612 BOOL NodeContourController::ExportRender(RenderRegion* pRegion) 
02613 {
02614 #ifdef DO_EXPORT
02615     if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
02616     {
02617         // Output "end group" token
02618         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
02619         pDC->OutputToken(_T("U"));
02620         pDC->OutputNewLine();
02621         
02622         // Tell caller we rendered ourselves ok
02623         return TRUE;
02624     }
02625 PORTNOTE("cmx", "Removed use of CMXRenderRegion")
02626 #ifndef EXCLUDE_FROM_XARALX
02627     else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
02628     {
02629         // mark start of a group...
02630         CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC();
02631         pDC->EndGroup();
02632 
02633         return TRUE;
02634     }
02635 #endif
02636 #endif
02637     // Render thid node in the normal way
02638     return FALSE;
02639 }
02640 
02641 /********************************************************************************************
02642 
02643 >   void NodeGroup::RenderTinyBlobs(RenderRegion* pRender)
02644 
02645     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
02646     Created:    23/6/94
02647     Inputs:     pRender - The region to draw the blobs in
02648     Purpose:    Renders the tiny blobs for a group (A Single blob on the topmost child object
02649                 in the group)
02650 
02651 ********************************************************************************************/
02652 
02653 void NodeContourController::RenderTinyBlobs(RenderRegion* pRegion)
02654 {
02655 #if !defined(EXCLUDE_FROM_RALPH)
02656     // get the topmost object in this group and tell it to render its tiny blobs.
02657     Node* pNode = FindLastChild();
02658     while (pNode != NULL && !pNode->IsAnObject())
02659         pNode = pNode->FindPrevious();
02660 
02661     // empty groups are not allowed!
02662     if (pNode == NULL)
02663     {
02664         ERROR3("NodeGroup::RenderTinyBlobs; This group is empty! Shouldn't be!");
02665         return;
02666     }
02667     else
02668     {
02669         ((NodeRenderableInk*)pNode)->RenderTinyBlobs(pRegion);
02670     }
02671 
02672 #endif
02673 }
02674 
02675 
02677 // ContourNodeTreeFactory implementation
02678 /***********************************************************************************************
02679 
02680 >   ContourNodeTreeFactory::ContourNodeTreeFactory()
02681 
02682     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02683     Created:    12/8/99
02684     Inputs:     
02685     Purpose:    Constructor
02686 
02687 ***********************************************************************************************/
02688 ContourNodeTreeFactory::ContourNodeTreeFactory()
02689 {
02690     m_Steps = 5;
02691     m_Width = 5000;
02692     m_pMap = NULL;
02693     m_bInsetPath = FALSE;
02694 }
02695 
02696 /***********************************************************************************************
02697 
02698 >   ContourNodeTreeFactory::~ContourNodeTreeFactory()
02699 
02700     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02701     Created:    12/8/99
02702     Inputs:     
02703     Purpose:    Destructor
02704 
02705 ***********************************************************************************************/
02706 ContourNodeTreeFactory::~ContourNodeTreeFactory()
02707 {
02708     if (m_pMap)
02709     {
02710         delete m_pMap;
02711         m_pMap = NULL;
02712     }
02713 }
02714 
02715 /***********************************************************************************************
02716 
02717 >   NodeCompound * ContourNodeTreeFactory::CreateNode(List * pList, UndoableOperation * pOp = NULL);
02718 
02719     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02720     Created:    12/8/99
02721     Inputs:     
02722     Purpose:    Creates the node tree given the parameters & the list of nodes to address
02723 
02724 ***********************************************************************************************/
02725 NodeCompound * ContourNodeTreeFactory::CreateNode(List * pList, UndoableOperation * pOp)
02726 {
02727     ERROR2IF(pList == NULL, NULL, "List is null");
02728     ERROR2IF(pList->IsEmpty(), NULL, "List is empty");
02729 
02730     String_256 StatusString;
02731     StatusString.Load(_R(IDS_CONTOUR_CREATE_STATUS_STRING));
02732     GetApplication()->UpdateStatusBarText(&StatusString, FALSE);
02733 PORTNOTE("status","Removed UpdateStatusLineFont usage")
02734 #ifndef EXCLUDE_FROM_XARALX
02735     GetApplication()->GetpCCStatusBar()->UpdateStatusLineFont();
02736 #endif
02737     
02738     NodeContourController * pController = NULL;
02739     ALLOC_WITH_FAIL(pController, new NodeContourController, pOp);
02740 
02741     pController->SetNumberOfSteps(m_Steps);
02742     pController->SetWidth(m_Width);
02743     pController->SetInsetPathFlag(m_bInsetPath);
02744 
02745     NodeContour * pContour = NULL;
02746 
02747     NodeListItem * pItem = (NodeListItem *)pList->GetHead();
02748 
02749     CCAttrMap * pMap = m_pMap;
02750 
02751     if (!m_pMap)
02752     {
02753         if (!pItem->pNode->IS_KIND_OF(NodeBlend))
02754         {
02755             pMap = CCAttrMap::MakeAppliedAttrMap((NodeRenderableInk *)pItem->pNode);
02756         }
02757         else
02758         {
02759             pMap = CCAttrMap::MakeAppliedAttrMap((NodeRenderableInk *)
02760                 (pItem->pNode->FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk))));
02761         }
02762 
02763         if (!pMap)
02764         {
02765             ERROR3("can't create attribute map");
02766             return NULL;
02767         }
02768     }
02769 
02770     List AttrList;
02771 
02772     pMap->BuildListOfAttributes(&AttrList);
02773     
02774     // insert the controller into the tree before the first element in the list
02775     BOOL bOK = TRUE;
02776 
02777     // send a message indicating that the light angle has changed
02778     ObjChangeFlags flgs(FALSE, FALSE, FALSE, FALSE);
02779     flgs.RegenerateNode = TRUE;
02780     ObjChangeParam OP(OBJCHANGE_FINISHED, flgs, pItem->pNode, pOp, OBJCHANGE_CALLEDBYOP);
02781 
02782 //  NodeAttribute * pAttrNode = NULL;
02783 
02784     Node * pNodeCopy = NULL;
02785 
02786     DocColour *pStartColour = NULL;
02787 
02788 //  INT32 R = 0;
02789 //  INT32 B = 0;
02790 //  INT32 G = 0;
02791 
02792     INT32 H = 0;
02793     INT32 S = 0;
02794     INT32 V = 0;
02795 
02796     BOOL bFoundColourFill = FALSE;
02797 
02798 //  BOOL bFirst = TRUE;
02799     if (pOp && pItem)
02800     {
02801         // can we do a contour to this node ?
02802         bFoundColourFill = FALSE;
02803         if (pItem->pNode->AllowOp(&OP, FALSE))
02804         {
02805             if (bOK)
02806             {
02807                 bOK = pOp->DoInsertNewNode(pController, pItem->pNode, PREV, TRUE, FALSE, FALSE, TRUE);
02808             }
02809 
02810             while (pItem && bOK)
02811             {
02812                 bOK = pOp->DoMoveNode(pItem->pNode, pController, LASTCHILD);
02813 
02814                 if (pItem->pNode->IsAnObject() && bOK)
02815                 {
02816                     bOK = pOp->DoSelectNode((NodeRenderableInk *)pItem->pNode);
02817                 }
02818                 
02819                 pItem = (NodeListItem *)pList->GetNext(pItem);
02820             }
02821 
02822             if (bOK)
02823             {
02824                 ALLOC_WITH_FAIL(pContour, new NodeContour, pOp);
02825                 
02826                 if (!pContour)
02827                 {
02828                     ERROR3("cant' create contour node");
02829                     return NULL;
02830                 }
02831                 
02832                 // apply the attribute list
02833                 pItem = (NodeListItem *)AttrList.GetHead();
02834                 
02835                 while (pItem && bOK)
02836                 {
02837                     // if (((NodeAttribute *)pItem->pNode)->CanBeAppliedToObject())
02838                     // only apply these attributes
02839                     NodeAttribute * pNodeAttr = (NodeAttribute *)pItem->pNode;
02840                     if (pNodeAttr->IsAColourFill() || pNodeAttr->IsATranspFill() ||
02841                         pNodeAttr->IsAStrokeColour() || pNodeAttr->IsAStrokeTransp() ||
02842                         pNodeAttr->IS_KIND_OF(AttrLineWidth) ||
02843                         pNodeAttr->IS_KIND_OF(AttrJoinType) ||
02844                         pNodeAttr->IS_KIND_OF(AttrFillMapping) ||
02845                         pNodeAttr->IS_KIND_OF(AttrTranspFillMapping))
02846                     {
02847                         pItem->pNode->NodeCopy(&pNodeCopy);
02848 
02849                         if (pNodeCopy)
02850                         {
02851                             if (((NodeAttribute *)pNodeCopy)->IsAColourFill() && 
02852                                 !((NodeAttribute *)pNodeCopy)->IsATranspFill())
02853                             {
02854                                 // invert the colour fill
02855                                 pStartColour = ((AttrFillGeometry *)pNodeCopy)->GetStartColour();
02856 
02857                                 if (pStartColour)
02858                                 {
02859                                     if (!pStartColour->IsTransparent())
02860                                     {
02861                                         bFoundColourFill = TRUE;
02862 
02863                                         pStartColour->GetHSVValue(&H, &S, &V);
02864 
02865                                         if (!m_bInsetPath)
02866                                         {
02867                                             V = (4 * (255 - V) / 5) + V;
02868                                             
02869                                             S = S / 5;
02870                                         }
02871                                         
02872                                         pStartColour->SetHSVValue(H,S,V);
02873                                     }
02874                                 }
02875                             }
02876                             
02877                             pNodeCopy->AttachNode(pContour, FIRSTCHILD, FALSE, FALSE);
02878                         }
02879                         else
02880                         {
02881                             bOK = FALSE;
02882                         }
02883                     }
02884                     
02885                     
02886                     pItem = (NodeListItem *)AttrList.GetNext(pItem);
02887                 }
02888                 
02889                 AttrList.DeleteAll();
02890                 
02891                 // do the winding rule
02892                 AttrWindingRule * pWinding = (AttrWindingRule *)pContour->FindFirstChild(
02893                     CC_RUNTIME_CLASS(AttrWindingRule));
02894                 
02895                 if (pWinding)
02896                 {
02897                     pWinding->Value.WindingRule = NegativeWinding;
02898                 }
02899 
02900                 if (bOK)
02901                 {
02902                     // insert the node, factoring out all the common attributes
02903                     // outer contours come before, inners after 
02904                     if (pController->GetWidth() < 0)
02905                     {
02906                         Node * pInsertNode = pController->FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk));
02907 
02908                         if (pInsertNode)
02909                             bOK = pOp->DoInsertNewNode(pContour, pInsertNode, PREV, TRUE, FALSE, FALSE, TRUE);
02910                         else
02911                             ERROR2(NULL, "No insertion node !");
02912                     }
02913                     else
02914                     {
02915                         bOK = pOp->DoInsertNewNode(pContour, pController, LASTCHILD, TRUE, FALSE, FALSE, TRUE);
02916                     }
02917 
02918                 }
02919             }
02920 
02921             if (bOK && !bFoundColourFill)
02922             {
02923                 // does the controller node have a non-default fill ?
02924                 // right, we must choose one then
02925                 Node * pColourNode = pController->FindFirstDepthFirst();
02926 
02927                 while (pColourNode && pColourNode != pController)
02928                 {
02929                     if (pColourNode->IsAnAttribute())
02930                     {
02931                         // found it !
02932                         // clone it onto the contour node
02933                         if (((NodeAttribute *)pColourNode)->IsAColourFill() && 
02934                             !((NodeAttribute *)pColourNode)->IsATranspFill())
02935                         {
02936                             // invert the colour fill
02937                             pColourNode->NodeCopy(&pNodeCopy);
02938                             pStartColour = ((AttrFillGeometry *)pNodeCopy)->GetStartColour();
02939                             
02940                             if (pStartColour)
02941                             {
02942                                 if (!pStartColour->IsTransparent())
02943                                 {
02944                                     bFoundColourFill = TRUE;
02945                                     pStartColour->GetHSVValue(&H, &S, &V);
02946 
02947                                     if (!m_bInsetPath)
02948                                     {                                   
02949                                         V = (4 * (255 - V) / 5) + V;
02950                                     
02951                                         S = S / 5;
02952                                     }
02953                                     
02954                                     pStartColour->SetHSVValue(H,S,V);
02955                                 }
02956 
02957                                 pNodeCopy->AttachNode(pContour, FIRSTCHILD, FALSE, FALSE);
02958 
02959                                 break;
02960                             }
02961                             else
02962                             {
02963                                 delete pNodeCopy;
02964                             }
02965                         }
02966                     }
02967 
02968                     pColourNode = pColourNode->FindNextDepthFirst(pController);
02969                 }
02970             }
02971             
02972             if (bOK)
02973             {
02974                 // now, factor out all the children         
02975                 bOK = pOp->DoFactorOutCommonChildAttributes(pController, TRUE);
02976             }
02977 
02978             if (bOK)
02979             {
02980                 //  Karim 28/06/2000
02981                 //  NodeContourController should never be selected.
02982 //              pController->SetSelected(TRUE);
02983 
02984                 // check the controller width on inner bevels so it can't be greater
02985                 // than a certain amount
02986                 if (pController->GetWidth() > 0)
02987                 {
02988                     MILLIPOINT MaxWidth = ContourNodePathProcessor::GetMaxInnerContourWidth(pController);
02989                     if (pController->GetWidth() > MaxWidth)
02990                     {
02991                         pController->SetWidth(MaxWidth);
02992                     }
02993                 }
02994 
02995                 pController->RegenerateNode(pOp, FALSE);
02996             }
02997             
02998             if (bOK)
02999             {
03000                 bOK = pOp->DoInvalidateNodeRegion(pController, TRUE);
03001             }
03002         }
03003     }   
03004 
03005     AttrList.DeleteAll();
03006 
03007     if (m_bInsetPath)
03008     {
03009         // change the selection for inset path stuff
03010         pController->SetSelected(FALSE);
03011         pContour->SetSelected(TRUE);
03012     }
03013 
03014     GetApplication()->UpdateSelection();
03015 
03016     if (pMap)
03017     {
03018         delete pMap;
03019         m_pMap = NULL;
03020     }
03021 
03022     if (bOK)
03023         return pController;
03024 
03025     return NULL;
03026 }
03027 
03029 // ContourNodePathProcessor implementation
03030 /***********************************************************************************************
03031 
03032 >   ContourNodePathProcessor ::ContourNodePathProcessor ()
03033 
03034     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03035     Created:    18/8/99
03036     Inputs:     
03037     Purpose:    Constructor
03038 
03039 ***********************************************************************************************/
03040 ContourNodePathProcessor::ContourNodePathProcessor()
03041 {
03042     TRACEUSER( "DavidM", _T("Created a contour node path processor\n"));    
03043     m_bActive = TRUE;
03044     m_bLineWidthTest = TRUE;
03045     m_bPrinting = FALSE;
03046     m_bRespectJoinType = FALSE;
03047 
03048 }
03049 
03050 /***********************************************************************************************
03051 
03052 >   ContourNodePathProcessor ::~ContourNodePathProcessor ()
03053 
03054     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03055     Created:    18/8/99
03056     Inputs:     
03057     Purpose:    Destructor
03058 
03059 ***********************************************************************************************/
03060 ContourNodePathProcessor::~ContourNodePathProcessor()
03061 {
03062     TRACEUSER( "DavidM", _T("Killed a contour node path processor\n")); 
03063 }
03064 
03065 /***********************************************************************************************
03066 
03067 >   void ContourNodePathProcessor::ProcessPath ( Path * pPath,
03068                                                  RenderRegion * pRegion,
03069                                                  PathShape ShapePath = PATHSHAPE_PATH )
03070 
03071     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03072     Created:    18/8/99
03073     Inputs:     
03074     Purpose:    Sums all paths which are passed to it
03075 
03076 ***********************************************************************************************/
03077 
03078 // Graeme ( 14/12/99 ) - Added the PathShape ShapePath field to the arguments. This is used by
03079 // the Flash render region to determine whether or not the object is circular.
03080 void ContourNodePathProcessor::ProcessPath ( Path * pPath,
03081                                              RenderRegion * pRegion,
03082                                              PathShape ShapePath )
03083 {
03084     UINT32 StrokeWidth = 0;
03085     
03086     if (m_bActive && pPath->IsClosed())
03087     {
03088         // get the pixel width
03089         // stroke the original path
03090         MILLIPOINT PixelWidth = pRegion->GetScaledPixelWidth();
03091 
03092         if (m_bPrinting)
03093         {
03094             PixelWidth = pRegion->GetPixelWidth();
03095         }
03096 
03097         StrokeWidth = PixelWidth * 2;
03098 
03099         BOOL bStrokePath = FALSE;
03100         
03101         // if we have a transparent line width then we need to stroke the path slightly
03102         // to kill off holes
03103         if (m_bLineWidthTest)
03104         {
03105             LineWidthAttribute * pLineWidth = (LineWidthAttribute *)pRegion->GetCurrentAttribute(ATTR_LINEWIDTH);
03106 
03107             if (pLineWidth)
03108             {
03109                 if (pLineWidth->LineWidth < PixelWidth)
03110                 {
03111                     StrokeWidth = PixelWidth;
03112                     bStrokePath = TRUE;
03113                 }
03114             }
03115             
03116             StrokeColourAttribute * pLineColour = 
03117                 (StrokeColourAttribute *)pRegion->GetCurrentAttribute(ATTR_STROKECOLOUR);
03118             
03119             if (pLineColour)
03120             {
03121                 DocColour * pDocColourLine = pLineColour->GetStartColour();
03122                 
03123                 if (pDocColourLine)
03124                 {
03125                     if (pDocColourLine->IsTransparent())
03126                     {
03127                         bStrokePath = TRUE;
03128                     }
03129                 }
03130             }
03131         }
03132         else
03133         {
03134             bStrokePath = TRUE;
03135         }
03136 
03137         // stroke the original path
03138         if (bStrokePath)
03139         {       
03140             pRegion->SaveContext();
03141             
03142             Path StrokedPath;
03143             StrokedPath.Initialise();
03144             
03145             pPath->StrokePathToPath(StrokeWidth,
03146                 LineCapRound,
03147                 RoundJoin,
03148                 NULL,
03149                 &StrokedPath,
03150                 200,
03151                 FALSE);
03152             
03153             StrokedPath.TryToClose();
03154             StrokedPath.IsFilled = TRUE;
03155             StrokedPath.IsStroked = TRUE;
03156 
03157             pRegion->SetLineColour(COLOUR_NONE);
03158 
03159             pRegion->DrawPath(&StrokedPath, this, ShapePath);
03160             pRegion->RestoreContext();
03161         }
03162     }
03163     
03164     pRegion->DrawPath(pPath, this, ShapePath);
03165 }
03166 
03168 // class to handle loading in of bevel records
03169 
03170 /********************************************************************************************
03171 >   ContourRecordHandler::ContourRecordHandler()
03172 
03173     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03174     Created:    27/10/98
03175     Inputs:     -
03176     Returns:    
03177     Purpose:    Destructor
03178 ********************************************************************************************/
03179 ContourRecordHandler::ContourRecordHandler()
03180 {
03181 
03182 }
03183 
03184 
03185 /********************************************************************************************
03186 >   ContourRecordHandler::~ContourRecordHandler()
03187 
03188     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03189     Created:    27/10/98
03190     Inputs:     -
03191     Returns:    
03192     Purpose:    Destructor
03193 ********************************************************************************************/
03194 ContourRecordHandler::~ContourRecordHandler()
03195 {
03196 }
03197 
03198 /********************************************************************************************
03199 >   UINT32* ContourRecordHandler::GetTagList()
03200 
03201     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03202     Created:    27/10/98
03203     Inputs:     -
03204     Returns:    
03205     Purpose:    Gets the tag list for this handler to handle 
03206 ********************************************************************************************/
03207 UINT32* ContourRecordHandler::GetTagList()
03208 {
03209     static UINT32 TagList[] = { TAG_CONTOURCONTROLLER,
03210                                 TAG_CONTOUR,
03211                                 CXFRH_TAG_LIST_END};
03212 
03213     return (UINT32*)&TagList;
03214 }
03215 
03216 /********************************************************************************************
03217 >   BOOL ContourRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
03218 
03219     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03220     Created:    27/10/98
03221     Inputs:     -
03222     Returns:    
03223     Purpose:    Handles the bevel record - basically creates a BevelController node &
03224                 creates all the attributes
03225 ********************************************************************************************/
03226 BOOL ContourRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
03227 {
03228     INT32 Steps = 0;
03229     INT32 Width = 0;
03230     BYTE Type = 0;
03231 
03232     BOOL ok = TRUE;
03233 
03234     double ObjBias = 0;
03235     double ObjGain = 0;
03236     double AttrBias = 0;
03237     double AttrGain = 0;
03238     
03239     if (pCXaraFileRecord->GetTag() == TAG_CONTOURCONTROLLER)
03240     {
03241         ok = pCXaraFileRecord->ReadINT32(&Steps);
03242 
03243         if (ok) ok = pCXaraFileRecord->ReadINT32(&Width);
03244         if (ok) ok = pCXaraFileRecord->ReadBYTE(&Type);
03245         if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&ObjBias);
03246         if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&ObjGain);
03247         if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AttrBias);
03248         if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&AttrGain);
03249 
03250         NodeContourController * pNode = new NodeContourController;
03251 
03252         if (!pNode)
03253         {
03254             ERROR3("Can't create contour controller");
03255             return FALSE;
03256         }
03257 
03258         pNode->SetWidth(Width);
03259         pNode->SetNumberOfSteps(Steps);
03260 
03261         if ((Type & 128) != 0)
03262         {
03263             Type = Type - 128;
03264             pNode->SetInsetPathFlag(TRUE);
03265         }
03266 
03267         pNode->SetColourBlendType((ColourBlendType)Type);
03268 
03269         pNode->SetObjectProfile( CProfileBiasGain( (AFp)ObjBias, (AFp)ObjGain ) );
03270         pNode->SetAttrProfile( CProfileBiasGain( (AFp)AttrBias, (AFp)AttrGain ) );
03271 
03272         if (ok)
03273         {
03274             InsertNode(pNode);
03275         }
03276     }
03277     else if (pCXaraFileRecord->GetTag() == TAG_CONTOUR)
03278     {
03279         NodeContour * pNode = new NodeContour;
03280 
03281         if (!pNode)
03282         {
03283             ERROR3("Can't create NodeContour");
03284             return FALSE;
03285         }
03286 
03287         InsertNode(pNode);
03288     }
03289     else
03290     {
03291         ERROR3("Contour file record handler - unrecognised tag");
03292         ok = FALSE;
03293     }
03294 
03295     return ok;
03296 }
03297 
03298 /***********************************************************************************************
03299 
03300 >   MILLIPOINT ContourNodePathProcessor::GetMaxInnerContourWidth(NodeCompound * pCompound);
03301 
03302     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03303     Created:    23/11/99
03304     Inputs:     See base class
03305     Purpose:    Calculates the maximum inner bevel allowed, given the node
03306 
03307 ***********************************************************************************************/
03308 MILLIPOINT ContourNodePathProcessor::GetMaxInnerContourWidth(NodeCompound * pCompound)
03309 {
03310     Node * pNode = pCompound->FindFirstDepthFirst();
03311 
03312     DocRect dr;
03313 
03314     MILLIPOINT MaxInnerBevel = -1;
03315     MILLIPOINT Len = 0;
03316 
03317     while (pNode && pNode != pCompound)
03318     {
03319         // ignore all compound nodes, non-object nodes and needs parent nodes
03320         if (pNode->IsAnObject() && !pNode->NeedsParent(NULL) && !pNode->IsCompound())
03321         {
03322             dr = ((NodeRenderableInk *)pNode)->GetBoundingRect(FALSE, FALSE);
03323 
03324             // get the maximum out of these two
03325             if (dr.Width() > dr.Height())
03326             {
03327                 Len = dr.Width();
03328             }
03329             else
03330             {
03331                 Len = dr.Height();
03332             }
03333 
03334             Len /= 2;
03335 
03336             if (MaxInnerBevel < 0)
03337             {
03338                 MaxInnerBevel = Len;
03339             }
03340             else if (MaxInnerBevel < Len)
03341             {
03342                 MaxInnerBevel = Len;
03343             }
03344         }
03345 
03346         pNode = pNode->FindNextDepthFirst(pCompound);
03347     }
03348 
03349     return MaxInnerBevel;
03350 }
03351 
03353 // The InsetPathPathProcessor
03354 
03355 /***********************************************************************************************
03356 
03357 >   InsetPathPathProcessor::InsetPathPathProcessor()
03358 
03359     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03360     Created:    31/3/2000
03361     Inputs:     See base class
03362     Purpose:    Constructor
03363 
03364 ***********************************************************************************************/
03365 InsetPathPathProcessor::InsetPathPathProcessor()
03366 {
03367     m_bActive = TRUE;
03368 }
03369 
03370 /***********************************************************************************************
03371 
03372 >   InsetPathPathProcessor::~InsetPathPathProcessor()
03373 
03374     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03375     Created:    31/3/2000
03376     Inputs:     See base class
03377     Purpose:    Destructor
03378 
03379 ***********************************************************************************************/
03380 InsetPathPathProcessor::~InsetPathPathProcessor()
03381 {
03382     
03383 }
03384 
03385 /***********************************************************************************************
03386 
03387 >   void InsetPathPathProcessor::ProcessPath(Path *pPath,
03388                              RenderRegion *pRender,
03389                              PathShape ShapePath)
03390 
03391     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03392     Created:    31/3/2000
03393     Inputs:     See base class
03394     Purpose:    Destructor
03395 
03396 ***********************************************************************************************/
03397 void InsetPathPathProcessor::ProcessPath(Path *pPath,
03398                              RenderRegion *pRender,
03399                              PathShape ShapePath)
03400 {
03401     if (m_bActive)
03402         return;
03403 
03404     pRender->DrawPath( pPath, this );
03405 }

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