nbevcont.cpp

Go to the documentation of this file.
00001 // $Id: nbevcont.cpp 1315 2006-06-14 09:51:34Z 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 /*
00100 //*/
00101 
00102 #include "camtypes.h"
00103 //#include "app.h"
00104 #include "nbevcont.h"
00105 
00106 #ifdef BUILDSHADOWS
00107 
00108 // code headers
00109 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 #include "objchge.h"
00111 //#include "osrndrgn.h"
00112 #include "nodebev.h"
00113 
00114 // resource headers
00115 //#include "simon.h"
00116 //#include "mario.h"
00117 #include "attrappl.h"
00118 #include "opgrad.h"
00119 #include "transop.h"
00120 #include "opbevel.h"
00121 //#include "mrhbits.h"
00122 
00123 // text class includes
00124 #include "nodetxts.h"
00125 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 #include "textops.h"
00127 //#include "arrows.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00128 //#include "bevinfo.h"
00129 
00130 // Save/load
00131 //#include "cxfrec.h"       // CXaraFileRecord - in camtypes.h [AUTOMATICALLY REMOVED]
00132 #include "attrbev.h"
00133 #include "bevtool.h"
00134 #include "blndtool.h"
00135 #include "blobs.h"
00136 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00137 //#include "bevres.h"
00138 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00139 #include "contmenu.h"
00140 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00141 #include "lineattr.h"
00142 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00143 #include "attrbev.h"
00144 #include "nodecont.h"
00145 #include "csrstack.h"
00146 //#include "moldtool.h"
00147 #include "attrmap.h"
00148 #include "opcntr.h"
00149 #include "nodeblnd.h"
00150 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00151 #include "extender.h"
00152 #include "ngcore.h"     // NameGallery, for stretching functionality
00153 #include "blendatt.h"
00154 #include "blndhelp.h"
00155 #include "nodebldr.h"
00156 #include "pathops.h"
00157 #include "brshattr.h"
00158 #include "pathndge.h"
00159 //#include "mrhbits.h"
00160 #include "offscrn.h"
00161 #include "saveeps.h"
00162 //#include "pmaskrgn.h"       
00163 #include "slicehelper.h"       
00164 #include "scanrr.h"
00165 //#include "cmxrendr.h"
00166 //#include "cmxexdc.h"
00167 //#include "ai_epsrr.h"
00168 #include "opnudge.h"
00169 #include "ophist.h"
00170 #include "cutop.h"
00171 
00172 CC_IMPLEMENT_DYNAMIC(NodeBevelController, NodeGroup )  
00173 CC_IMPLEMENT_DYNAMIC(BevelNodeTreeFactory, CompoundNodeTreeFactory )  
00174 
00175 // Give this file in memory dumps
00176 // Declare smart memory handling in Debug builds
00177 #define new CAM_DEBUG_NEW
00178 
00179 /*********************************************************************************************
00180 
00181 >    NodeBevelController::NodeBevelController() 
00182 
00183      Author:    Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00184      Created:   20/11/96
00185      Inputs:    -
00186      Outputs:   
00187      Returns:   -
00188               
00189      Purpose: This constructor creates a NodeBevelController 
00190             
00191      Errors:    -
00192 
00193 **********************************************************************************************/
00194  
00195 NodeBevelController::NodeBevelController() : NodeGroup()
00196 {
00197     // Initialise member vars
00198     m_Contrast = 85;
00199     m_bDrag = FALSE;
00200     m_bIncludeBlobsInBoundingRect = FALSE;
00201     
00202     m_bBoundingRectIsForTool = FALSE;
00203     IsBoundingRectValid = FALSE;
00204     m_LastZoom = FIXED16(0);
00205     m_PerformedExtend = FALSE;
00206     m_LastColoursTransparency = FALSE;
00207     m_HaveDoneApplyTest = FALSE;
00208     m_PathToClipVeiw.Initialise();
00209     m_pBevel = NULL;
00210     m_pClipViewAttribute = NULL;
00211 
00212 #ifdef _DEBUG
00213     myBevelID = -1;
00214     myBevelBecomeAID = -1;
00215 #endif
00216 }
00217 
00218 NodeBevelController::NodeBevelController(Node* ContextNode,  
00219                 AttachNodeDirection Direction,  
00220                 BOOL Locked, 
00221                 BOOL Mangled,  
00222                 BOOL Marked, 
00223                 BOOL Selected
00224                 ) : NodeGroup(ContextNode, Direction, Locked, Mangled, Marked, Selected)
00225 {
00226     m_Contrast = 85;
00227     m_bDrag = FALSE;
00228     m_bIncludeBlobsInBoundingRect = FALSE;
00229     m_bBoundingRectIsForTool = FALSE;
00230     IsBoundingRectValid = FALSE;
00231     m_LastZoom = FIXED16(0);
00232     m_LastColoursTransparency = FALSE;
00233     m_HaveDoneApplyTest = FALSE;
00234     m_PathToClipVeiw.Initialise();
00235     m_pBevel = NULL;
00236     m_pClipViewAttribute = NULL;
00237 
00238 #ifdef _DEBUG
00239     myBevelID = -1;
00240     myBevelBecomeAID = -1;
00241 #endif
00242 }
00243     
00244 NodeBevelController::~NodeBevelController()
00245 {   
00246 #ifdef _DEBUG
00247 
00248     if (myBevelID > -1)
00249     {
00250         TCHAR           strId[100];
00251         camSnprintf( strId, 100, _T("Popping NodeBevelController ID:  %i\n"), myBevelID );
00252             
00253         TRACEUSER( "ChrisS", strId );
00254     }
00255 
00256 #endif
00257 }
00258 
00259 NodeBevel * NodeBevelController::GetBevelNode()
00260 {
00261     NodeBevel * pBob = (NodeBevel *)FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
00262     // ERROR3IF(pBob == NULL, "NodeBevelContoller has no child node");
00263 
00264     return pBob;
00265 }
00266 
00267 /********************************************************************************************
00268 
00269 >   virtual NodeRenderableInk* NodeBevelController::GetInkNodeFromController() const
00270 
00271     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00272     Created:    16/10/2000
00273     Inputs:     -
00274     Outputs:    -
00275     Returns:    The ink node that is used by bevels, shadows and contours
00276     Purpose:    To return the node that we bevel
00277 
00278     Notes:      Karim 23/10/2000
00279                 Rewritten to be as fast as poss, so people feel like using this
00280                 method rather than writing their own bevelled node search.
00281     SeeAlso:    
00282 
00283 ********************************************************************************************/
00284 NodeRenderableInk* NodeBevelController::GetInkNodeFromController() const
00285 {
00286     // We get our NodeBevel and look for the first ink node left or right from it,
00287     // depending on whether it is an inner or an outer bevel.
00288     Node* pKid = NULL;
00289     if (m_pBevel != NULL)
00290     {
00291         if (m_pBevel->m_bOuter)
00292         {
00293             pKid = m_pBevel->FindNext();
00294             while (pKid != NULL && !pKid->IsAnObject())
00295                 pKid = pKid->FindNext();
00296 
00297             // so if our optimised version fails we'll go the other way
00298             if (pKid == NULL)
00299             {
00300                 pKid = m_pBevel->FindPrevious();
00301                 while (pKid != NULL && !pKid->IsAnObject())
00302                     pKid = pKid->FindPrevious();
00303             }
00304         }
00305         else
00306         {
00307             pKid = m_pBevel->FindPrevious();
00308             while (pKid != NULL && !pKid->IsAnObject())
00309                 pKid = pKid->FindPrevious();
00310 
00311             // so if our optimised version fails we'll go the other way
00312             if (pKid == NULL)
00313             {
00314                 pKid = m_pBevel->FindNext();
00315                 while (pKid != NULL && !pKid->IsAnObject())
00316                     pKid = pKid->FindNext();
00317             }
00318         }
00319     }
00320 
00321 //  ERROR3IF(pKid == NULL, "NodeBevelController::GetInkNodeFromController; Can't find bevelled node!");
00322 
00323     return (NodeRenderableInk*)pKid;
00324 }
00325 
00326 
00327 
00328 /*********************************************************************************************
00329 
00330 >    static BOOL NodeBevelController::SelectionHasBevelNode()
00331 
00332      Author:    David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00333      Created:   12/10/98
00334      Inputs:    -
00335      Outputs:   Returns TRUE if the current selection has a bevel node in it
00336      Returns:   -
00337               
00338      Purpose:   Runs through the current selection and returns TRUE if it has a bevel node in it
00339             
00340      Errors:    -
00341 
00342 **********************************************************************************************/
00343 
00344 BOOL NodeBevelController::SelectionHasBevelNode()
00345 {
00346     if (GetSelectionBevelNode())
00347     {
00348         return TRUE;
00349     }
00350 
00351     return FALSE;
00352 }
00353 
00354 /*********************************************************************************************
00355 
00356 >    static BOOL NodeBevelController::SelectionHasBevelInkNode()
00357 
00358      Author:    David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00359      Created:   12/10/98
00360      Inputs:    -
00361      Outputs:   Returns TRUE if the current selection has a bevel node in it
00362      Returns:   -
00363               
00364      Purpose:   Runs through the current selection and returns TRUE if it has a bevel node in it
00365             
00366      Errors:    -
00367 
00368 **********************************************************************************************/
00369 
00370 BOOL NodeBevelController::SelectionHasBevelInkNode()
00371 {
00372     if (GetSelectionBevelInkNode())
00373     {
00374         return TRUE;
00375     }
00376 
00377     return FALSE;
00378 }
00379 
00380 
00381 /*********************************************************************************************
00382 
00383 >    static NodeBevel * NodeBevelController::GetSelectionBevelNode(INT32 num);
00384 
00385      Author:    David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00386      Created:   12/10/98
00387      Inputs:    -
00388      Outputs:   Gets the n'th bevel node in the selection
00389      Returns:   -
00390               
00391      Purpose:   Runs through the current selection and returns TRUE if it has a bevel node in it
00392             
00393      Errors:    -
00394 
00395 **********************************************************************************************/
00396 
00397 NodeBevelController * NodeBevelController::GetSelectionBevelNode(INT32 num /* = 0 */)
00398 {
00399     // build the list
00400     List NodeList;
00401 
00402     if (!BevelTools::BuildListOfSelectedNodes(&NodeList,
00403                                 CC_RUNTIME_CLASS(NodeBevelController)))
00404     {
00405         return NULL;
00406     }
00407 
00408     if (NodeList.GetCount() <= (UINT32)num)
00409     {
00410         return NULL;
00411     }
00412 
00413     INT32 i = 0;
00414     NodeListItem * pItem = (NodeListItem *)NodeList.GetHead();
00415 
00416     NodeBevelController *pBevC = NULL;
00417 
00418     while (pItem)
00419     {
00420         if (pItem->pNode)
00421         {
00422             if (i == num)
00423             {
00424                 pBevC = (NodeBevelController *)pItem->pNode;
00425                 NodeList.DeleteAll();
00426                 return pBevC;
00427             }
00428         }
00429 
00430         pItem = (NodeListItem *)NodeList.GetNext(pItem);
00431         i++;
00432     }
00433     return NULL;
00434 }
00435 
00436 /*********************************************************************************************
00437 
00438 >    static NodeBevelController * NodeBevelController::GetSelectionBevelInkNode(INT32 num);
00439 
00440      Author:    David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00441      Created:   12/10/98
00442      Inputs:    -
00443      Outputs:   Gets the n'th bevel ink node in the selection
00444      Returns:   -
00445               
00446      Purpose:   Runs through the current selection and returns TRUE if it has a bevel node in it
00447             
00448      Errors:    -
00449 
00450 **********************************************************************************************/
00451 
00452 NodeBevel * NodeBevelController::GetSelectionBevelInkNode(INT32 num /* = 0 */)
00453 {
00454     // build the list
00455     List NodeList;
00456 
00457     if (!BevelTools::BuildListOfSelectedNodes(&NodeList,
00458                                 CC_RUNTIME_CLASS(NodeBevel)))
00459     {
00460         return NULL;
00461     }
00462 
00463     if (NodeList.GetCount() <= (UINT32)num)
00464     {
00465         return NULL;
00466     }
00467 
00468     INT32 i = 0;
00469     NodeListItem * pItem = (NodeListItem *)NodeList.GetHead();
00470     NodeBevel * pBev = NULL;
00471 
00472     while (pItem)
00473     {
00474         if (pItem->pNode)
00475         {
00476             if (i == num)
00477             {
00478                 pBev = (NodeBevel *)pItem->pNode;
00479                 NodeList.DeleteAll();
00480                 return pBev;
00481             }
00482         }
00483 
00484         pItem = (NodeListItem *)NodeList.GetNext(pItem);
00485         i++;
00486     } 
00487     return NULL;
00488 }
00489 
00490 /********************************************************************************************
00491 >   BOOL NodeBevelController::WritePreChildrenNative(BaseCamelotFilter* pFilter)
00492 
00493     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00494     Created:    27/10/98
00495     Inputs:     -
00496     Returns:    FALSE for failure
00497     Purpose:    record saving stuff from xar files
00498 ********************************************************************************************/
00499 
00500 BOOL NodeBevelController::WritePreChildrenNative(BaseCamelotFilter* pFilter)
00501 {
00502     UINT32 Tag = TAG_BEVEL;
00503 
00504     NodeBevel * pBevel = GetBevelNode();
00505 
00506     // do the writing
00507     INT32 RecordSize = 24; // bytes
00508     
00509     CamelotFileRecord Rec(pFilter,Tag,RecordSize);
00510     Rec.Init();
00511 
00512     // ok, do the writing of the bevel data 
00513     Rec.WriteINT32((INT32)pBevel->m_BevelType);     // 4 bytes
00514     Rec.WriteINT32((INT32)pBevel->m_Indent);            // 4 bytes
00515     Rec.WriteINT32((INT32)pBevel->m_LightAngle);            // 4 bytes
00516     Rec.WriteINT32((INT32)pBevel->m_bOuter);            // 4 bytes
00517     Rec.WriteINT32((INT32)pBevel->m_Contrast);                  // 4 bytes
00518     Rec.WriteINT32((INT32)pBevel->m_Tilt);          // another 4 bytes
00519 
00520     // export to file
00521     pFilter->Write(&Rec);
00522 
00523     return TRUE;
00524 }   
00525 
00526 BOOL NodeBevelController::WritePostChildrenNative(BaseCamelotFilter* pFilter)
00527 {
00528     // move the attribute node back to where it should be
00529     return TRUE;
00530 }
00531 
00532 
00533 BOOL NodeBevelController::WritePreChildrenWeb(BaseCamelotFilter * pFilter)
00534 {
00535     return WritePreChildrenNative(pFilter);
00536 }
00537 
00538 BOOL NodeBevelController::WritePostChildrenWeb(BaseCamelotFilter * pFilter)
00539 {
00540     return WritePostChildrenNative(pFilter);
00541 }
00542 
00543 /********************************************************************************************
00544 >   BOOL NodeBevelController::LoadBevelFromRecord(CXaraFileRecord* pCXaraFileRecord)
00545 
00546     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00547     Created:    27/10/98
00548     Inputs:     -
00549     Returns:    FALSE for failure
00550     Purpose:    record loading stuff from xar files
00551 ********************************************************************************************/
00552 
00553 BOOL NodeBevelController::LoadBevelFromRecord(CXaraFileRecord* pCXaraFileRecord)
00554 {
00555     INT32 BevelType     = 0;
00556     INT32 Indent            = 0;
00557     INT32 LightAngle        = 0;
00558     INT32 bOuter            = 0;
00559     INT32 Contrast      = 0;
00560     INT32 Tilt          = 45;
00561 //  double dpi          = 96.0;
00562 
00563 
00564     if (!pCXaraFileRecord->ReadINT32(&BevelType))
00565         return FALSE;
00566 
00567     if (!pCXaraFileRecord->ReadINT32(&Indent))
00568         return FALSE;
00569 
00570     if (!pCXaraFileRecord->ReadINT32(&LightAngle))
00571         return FALSE;
00572     
00573     if (!pCXaraFileRecord->ReadINT32(&bOuter))
00574         return FALSE;
00575 
00576     if (!pCXaraFileRecord->ReadINT32(&Contrast))
00577         return FALSE;
00578 
00579     // check for tilt being defined
00580     if (pCXaraFileRecord->GetSize() == 24)
00581         if (!pCXaraFileRecord->ReadINT32(&Tilt))
00582             return FALSE;
00583 
00584     m_BevelType     = BevelType;
00585     m_Indent        = Indent;
00586     m_LightAngle    = LightAngle;
00587     m_bOuter        = bOuter;
00588     m_LType         = LineCapButt;
00589     m_Contrast      = Contrast;
00590     m_Tilt          = Tilt;
00591 
00592     // that'll be it !
00593     
00594     return TRUE;
00595 }
00596 
00597 /********************************************************************************************
00598 >   void NodeBevelController::ReRenderBevelBitmap(double LightAngle)
00599 
00600     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00601     Created:    27/10/98
00602     Inputs:     -
00603     Returns:    FALSE for failure
00604     Purpose:    Renders the bevel bitmap - JUST the bitmap, nothing else !
00605 ********************************************************************************************/
00606 void NodeBevelController::ReRenderBevelBitmap(double LightAngle)
00607 {
00608     // find my bevel node
00609     NodeBevel * pBevel = GetBevelNode();
00610 
00611     double OldLightAngle = pBevel->m_LightAngle;
00612 
00613     if (pBevel)
00614     {
00615         pBevel->m_LightAngle = LightAngle;
00616         pBevel->ReRenderBevelBitmap(FALSE);
00617         pBevel->m_LightAngle = OldLightAngle;
00618     }
00619 }
00620 
00621 /********************************************************************************************
00622 >   BOOL NodeBevelController::PostImport()
00623 
00624     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00625     Created:    27/10/98
00626     Inputs:     -
00627     Returns:    FALSE for failure
00628     Purpose:    set up my nodes after importing
00629 ********************************************************************************************/
00630 
00631 BOOL NodeBevelController::PostImport()
00632 {
00633     // get the bevel node under me to set up its values
00634     NodeBevel* pBevel = GetBevelNode();
00635     ERROR3IF(!pBevel, "NodeBevelController - No bevel ink node\n");
00636 
00637 //  BOOL ok = TRUE;
00638 
00639     // set up the bevel node
00640     pBevel->m_Indent        = m_Indent;
00641     pBevel->m_BevelType     = m_BevelType;
00642     pBevel->m_LightAngle    = m_LightAngle;
00643     pBevel->m_bOuter        = m_bOuter;
00644     pBevel->m_BMPCentre     = m_BMPCentre;
00645     pBevel->m_BMPPoint1     = m_BMPPoint1;
00646     pBevel->m_BMPPoint2     = m_BMPPoint2;
00647     pBevel->m_Contrast      = m_Contrast;
00648     pBevel->m_Tilt          = m_Tilt;
00649 
00650     // create the bevel start node in its correct place
00651     Node * pFirstChild = FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk));
00652     ERROR2IF(pFirstChild == NULL, FALSE, "No renderable children under bevel controller node");
00653     NodeBevelBegin * pBevelBegin = new NodeBevelBegin(pFirstChild, PREV);
00654     ERROR2IF(pBevelBegin == NULL, FALSE, "Can't create bevel begin node");
00655 
00656     // don't ask me why ! It just works this way
00657     Node * pBaseNode = SliceHelper::FindNextOfClass(this,this,CC_RUNTIME_CLASS(BaseTextClass));
00658     Node * pNextAttrNode = NULL;
00659     Node * pCurrentAttrNode = NULL;
00660 //  Node * pTextAttrNode = NULL;
00661     BOOL IsLastToFind = FALSE;
00662 
00663     // What this next bit does is to copy the Text Attributes on this node to all the TextStories and then
00664     // Unlincks them from this! This is basically the Do Localize Attributes Function!!!
00665     while(pBaseNode)
00666     {
00667         IsLastToFind = (SliceHelper::FindNextOfClass(pBaseNode,this,CC_RUNTIME_CLASS(BaseTextClass)) == NULL);
00668         
00669         pCurrentAttrNode = FindFirstChild(CC_RUNTIME_CLASS(AttrTxtBase));
00670         
00671         while(pCurrentAttrNode)
00672         {
00673             pNextAttrNode = pCurrentAttrNode->FindNext(CC_RUNTIME_CLASS(AttrTxtBase));
00674             pCurrentAttrNode->CopyNode(pBaseNode, FIRSTCHILD);
00675             
00676             if(IsLastToFind)
00677             {
00678                 pCurrentAttrNode->UnlinkNodeFromTree();
00679                 delete pCurrentAttrNode;
00680                 pCurrentAttrNode = NULL;
00681             }
00682 
00683             pCurrentAttrNode = pNextAttrNode;
00684         }
00685 
00686         pBaseNode = SliceHelper::FindNextOfClass(pBaseNode,this,CC_RUNTIME_CLASS(BaseTextClass));
00687     }
00688 
00689     // set up my attributes
00690     AttrBevelIndent * pIndent = new AttrBevelIndent(this, FIRSTCHILD);
00691     ERRORIF(pIndent == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
00692     if(m_bOuter)
00693         pIndent->Value.m_Indent = -m_Indent;
00694     else
00695         pIndent->Value.m_Indent = m_Indent;
00696 
00697     AttrBevelLightAngle * pLightAngle = new AttrBevelLightAngle(this, FIRSTCHILD);
00698     ERRORIF(pLightAngle == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
00699     pLightAngle->Value.m_LightAngle = (INT32)m_LightAngle;
00700 
00701     AttrBevelContrast * pContrast = new AttrBevelContrast(this, FIRSTCHILD);
00702     ERRORIF(pContrast == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
00703     pContrast->Value.m_Contrast = m_Contrast;
00704 
00705     AttrBevelType * pType = new AttrBevelType(this, FIRSTCHILD);
00706     ERRORIF(pType == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
00707     pType->Value.m_Type = m_BevelType;
00708 
00709     AttrBevelLightTilt * pTilt = new AttrBevelLightTilt(this, FIRSTCHILD);
00710     ERRORIF(pTilt == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
00711     pTilt->Value.m_LightTilt = static_cast<INT32>(m_Tilt);
00712 
00713     // We need to move the bevel node to the correct position in the tree if it`s an outer
00714     // which should be directly after the BevelBegin Node!
00715     if(m_bOuter)
00716         pBevel->MoveNode(pBevelBegin,NEXT);
00717     else
00718         pBevel->MoveNode(this,LASTCHILD);
00719 
00720     // finally, normalise all of the attributes
00721     OptimiseAttributes();
00722 
00723     // should be it! regen bevel
00724     CreateBevel();
00725 
00726     return TRUE;
00727 }
00728 
00729 /********************************************************************************************
00730 >   String NodeBevelController::Describe(BOOL Plural, BOOL Verbose)
00731 
00732     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00733     Created:    27/10/98
00734     Inputs:     -
00735     Returns:    
00736     Purpose:    Returns my description string
00737 ********************************************************************************************/
00738 
00739 String NodeBevelController::Describe(BOOL Plural, BOOL Verbose)
00740 {
00741     String Name;
00742     Name.Load(_R(IDS_BEVEL_NODE_NAME));
00743 
00744     if (Plural)
00745     {
00746         Name += _T("s");
00747     }
00748 
00749     return Name;
00750 }
00751 
00752 /********************************************************************************************
00753 >   void NodeBevelController::Transform( TransformBase& Trans )
00754 
00755     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00756     Created:    27/10/98
00757     Inputs:     -
00758     Returns:    
00759     Purpose:    Transforms me
00760 ********************************************************************************************/
00761 void NodeBevelController::Transform( TransformBase& Trans )
00762 {
00763     NodeBevel * pBob = (NodeBevel *)FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
00764     
00765     if(!pBob)
00766     {
00767         ERROR3("Failed to find the bevel node!");
00768         return;
00769     }
00770 
00771     // See GroupCanTransformCached()
00772     pBob->m_MustRegenOnChildChange = FALSE;
00773     NodeGroup::Transform(Trans);
00774     pBob->m_MustRegenOnChildChange = TRUE;
00775 
00776     pBob->Transform(Trans);
00777 
00778     InvalidateBoundingRect();
00779     GetBoundingRect(TRUE, FALSE);
00780 }
00781 
00782 /********************************************************************************************
00783 >   DocRect NodeBevelController::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
00784 
00785     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
00786     Created:    23/11/00
00787     Inputs:     DontUseAttrs - TRUE if the attrs associated with the node should be
00788                 ignored. defaults to FALSE
00789                 HitTest      - TRUE if being called during HitTest
00790     Returns:    The bevels bounding rect
00791     Purpose:    If the bevels bounding rect is known then it is returned. If not, it is
00792                 re-calculated.
00793 ********************************************************************************************/
00794 DocRect NodeBevelController::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
00795 {
00796     if (!IsBoundingRectValid || DontUseAttrs)
00797     {
00798         DocRect NewRect(0,0,0,0);
00799 
00800         // If we`ve got a bevel Node get it`s bounding Rect
00801         if (GetBevelNode())
00802             NewRect = GetBevelNode()->GetBoundingRect(DontUseAttrs, HitTest);
00803         else
00804         {
00805             NewRect = GetInsideBoundingRect();
00806             
00807             if (!NewRect.IsEmpty() && m_bOuter)
00808             {
00809                 AttrBevelIndent * pIndent = NULL;
00810                 if (FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBevelIndent), (NodeAttribute **)&pIndent) && pIndent)
00811                     NewRect.Inflate(pIndent->Value.m_Indent);
00812             }
00813         }
00814 
00815         if (DontUseAttrs)
00816             return NewRect; // just return this rectangle as it is not the nodes true bounding rect
00817 
00818         // copy the new docrect into the node bounding rect
00819         BoundingRectangle = NewRect;
00820 
00821         // Mark the bounding rect as valid
00822         IsBoundingRectValid = TRUE;
00823     }
00824 
00825     // Return the resulting bounding rect
00826     return BoundingRectangle;
00827 }
00828 
00829 /********************************************************************************************
00830 >   DocRect NodeBevelController::GetBlobBoundingRect()
00831 
00832     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00833     Created:    27/10/98
00834     Inputs:     -
00835     Returns:    -
00836     Purpose:    Gets the blob bounding rect
00837 ********************************************************************************************/
00838 DocRect NodeBevelController::GetBlobBoundingRect()
00839 {
00840     // now, find out if the blob manager's interest is in artistic blobs or not
00841     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00842     BlobStyle BS = BlobMgr->GetCurrentInterest(TRUE);
00843 
00844     DocRect br ;
00845     
00846     br = BoundingRectangle;
00847     
00848     // is the bevel tool active ?
00849     if (Tool::GetCurrentID() != TOOLID_BEVELTOOL ||
00850         !BevelTool::AmActive())
00851     {
00852         return NodeGroup::GetBlobBoundingRect();
00853     }
00854 
00855     DocRect BlobRect;
00856 
00857     // calculate the position of the light angle blob
00858     DocCoord blob1((br.hi.x + br.lo.x) / 2, 
00859         (br.hi.y + br.lo.y) / 2);
00860     DocCoord blob2;
00861 
00862     View * pView = DocView::GetCurrent();
00863 
00864     if (!pView)
00865     {
00866         return br;
00867     }
00868 
00869     double len = pView->GetScaledPixelWidth().MakeDouble() * 40;
00870     
00871     INT32 iLen = (INT32)(len);
00872 
00873     // get from the blob manager the size of blobs
00874     if (pView)
00875     {
00876         iLen += (INT32)(pView->GetScaledPixelWidth().MakeDouble() * 15);
00877         iLen += GetApplication()->GetBlobManager()->GetBlobSize();
00878     }
00879 
00880     DocRect dr;
00881 
00882     // calculate the rect for the blob
00883     dr.hi.x = blob1.x + iLen;
00884     dr.hi.y = blob1.y + iLen;
00885 
00886     dr.lo.x = blob1.x - iLen;
00887     dr.lo.y = blob1.y - iLen;
00888 
00889     BlobRect = br;
00890 
00891     // finally, union this with the standard group's bounding rect to give
00892     // the true blob bounding rect
00893     BlobRect = BlobRect.Union(dr);
00894 
00895     return BlobRect;
00896 }
00897 
00898 
00899 
00900 /********************************************************************************************
00901 
00902 >   virtual void NodeBevelController::SelectInRect(const DocRect& Rect, SelStateAction st)
00903 
00904     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00905     Created:    14 April 2000
00906     Inputs:     Rect    const reference to the bounding rect to use.
00907                 st      the selection state, ie SET/TOGGLE/CLEAR.
00908     Outputs:    This node may have its selection status changed, depending on the inputs.
00909 
00910     Purpose:    Helper method for the static fn SelectAllInRect() used by marquee select.
00911                 This method changes the selection state of this node according to the given
00912                 bounding rectangle and sel-state action. Most nodes will want to use the
00913                 default behaviour, which is to select themselves if their bounds lie within
00914                 the given bounding rectangle. If you want to do something special with the
00915                 marquee select, then override this method.
00916 
00917     Notes:      This method is meant to be called solely from SelectAllInRect() - if you wish
00918                 to call it from elsewhere, it *may* work as you expect, but it is a good idea
00919                 that you check your assumptions on what preconditions are necessary.
00920 
00921     Errors:     ERROR3 in DEBUG if st holds an invalid state.
00922     See also:   SelectAllInRect().
00923 
00924 ********************************************************************************************/
00925 void NodeBevelController::SelectInRect(const DocRect& Rect, SelStateAction st)
00926 {
00927     // try to select each of our NodeRenderableInk children.
00928     NodeRenderableInk* pInkChild = NULL;
00929     Node* pChild = FindFirstChild();
00930     while (pChild != NULL)
00931     {
00932         if (pChild->IsAnObject())
00933         {
00934             pInkChild = (NodeRenderableInk*)pChild;
00935             if (Rect.ContainsRect(pInkChild->GetBoundingRect()))
00936             {
00937                 switch (st)
00938                 {
00939                 case CLEAR:
00940                     if (pInkChild->MarqueeSelectNode())
00941                     {
00942                         pInkChild->DeSelect(TRUE);
00943                     }
00944                     break;
00945 
00946                 case SET:
00947                     if (pInkChild->MarqueeSelectNode())
00948                     {
00949                         pInkChild->Select(TRUE);
00950                     }
00951                     break;
00952 
00953                 case TOGGLE:
00954                     if (pInkChild->MarqueeSelectNode())
00955                     {
00956                         if (pInkChild->IsSelected())
00957                             pInkChild->DeSelect(TRUE);
00958                         else
00959                             pInkChild->Select(TRUE);
00960                     }
00961                     break;
00962 
00963                 default:
00964                     ERROR3("NodeBevelController::SelectInRect; Unknown SelStateAction!\n");
00965                     return;
00966                 }
00967             }
00968         }
00969         pChild = pChild->FindNext();
00970     }
00971 }
00972 
00973 
00974 
00975 /********************************************************************************************
00976 >   void NodeBevelController::DeleteCache()
00977 
00978     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00979     Created:    27/10/98
00980     Inputs:     -
00981     Returns:    -
00982     Purpose:    Deletes my memory allocation
00983 ********************************************************************************************/
00984 void NodeBevelController::DeleteCache()
00985 {
00986     // tells the bevel node to delete its cache
00987     if (GetBevelNode())
00988     {
00989         GetBevelNode()->DeleteCache();
00990         GetBevelNode()->InvalidateMe();
00991     }
00992 }
00993 
00994 /********************************************************************************************
00995 >   BOOL NodeBevelController::PromoteAttributeApplicationToMe(CCRuntimeClass *pAttrClass)
00996 
00997     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
00998     Created:    07/09/00
00999     Inputs:     Attribute to test
01000     Returns:    true for yes and false for no!
01001     Purpose:    Deals attribute promotions.
01002 ********************************************************************************************/
01003 BOOL NodeBevelController::PromoteAttributeApplicationToMe(CCRuntimeClass *pAttrClass) const
01004 {
01005     NodeBevel * pBevel = (NodeBevel *)FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
01006 
01007     // If the Attribute is a bevel attribute then we must always place it on the controller node!
01008     if(pAttrClass == CC_RUNTIME_CLASS(AttrBevel) ||
01009        pAttrClass == CC_RUNTIME_CLASS(AttrStartArrow) ||
01010        pAttrClass == CC_RUNTIME_CLASS(AttrEndArrow) ||
01011        pAttrClass == CC_RUNTIME_CLASS(AttrStartCap) ||
01012        pAttrClass == CC_RUNTIME_CLASS(AttrDashPattern) ||
01013        pBevel->IsSelected() )
01014     {
01015         return TRUE;
01016     }
01017 
01018     BOOL IsTransparent = (pAttrClass->IsKindOf(CC_RUNTIME_CLASS(AttrTranspFillGeometry)) ||
01019                           pAttrClass == CC_RUNTIME_CLASS(AttrTranspTypeChange) ||
01020                           pAttrClass == CC_RUNTIME_CLASS(AttrTranspChange) ||
01021                           pAttrClass == CC_RUNTIME_CLASS(AttrBitmapTranspFill) ||
01022                           pAttrClass == CC_RUNTIME_CLASS(AttrCircularTranspFill) ||
01023                           pAttrClass == CC_RUNTIME_CLASS(AttrConicalTranspFill) ||
01024                           pAttrClass == CC_RUNTIME_CLASS(AttrFlatTranspFill) ||
01025                           pAttrClass == CC_RUNTIME_CLASS(AttrFourColTranspFill) ||
01026                           pAttrClass == CC_RUNTIME_CLASS(AttrThreeColTranspFill) ||
01027                           pAttrClass == CC_RUNTIME_CLASS(AttrFractalTranspFill) ||
01028                           pAttrClass == CC_RUNTIME_CLASS(AttrLinearTranspFill) ||
01029                           pAttrClass == CC_RUNTIME_CLASS(AttrNoiseTranspFill) ||
01030                           pAttrClass == CC_RUNTIME_CLASS(AttrRadialTranspFill) ||
01031                           pAttrClass == CC_RUNTIME_CLASS(AttrSquareTranspFill) ||
01032                           pAttrClass == CC_RUNTIME_CLASS(AttrTextureTranspFill) ||
01033                           pAttrClass == CC_RUNTIME_CLASS(AttrTranspFillRampChange));
01034 
01035     if(!(pAttrClass == CC_RUNTIME_CLASS(AttrColourChange) ||
01036         pAttrClass == CC_RUNTIME_CLASS(AttrColourDrop) ||
01037         pAttrClass == CC_RUNTIME_CLASS(AttrBitmapChange) ||
01038         pAttrClass == CC_RUNTIME_CLASS(AttrBitmapTessChange) ||
01039         pAttrClass == CC_RUNTIME_CLASS(AttrColFillRampChange) ||
01040         pAttrClass->IsKindOf(CC_RUNTIME_CLASS(AttrFillGeometry)) ||
01041         IsTransparent))
01042     {
01043         return FALSE;
01044     }
01045 
01046     // Ok! Check to see if this node has the applied attribute. If it does then it means that
01047     // the children all have the same attribute (including the bevel).
01048     // first, get all the children
01049     BOOL DoApplyToMe = FALSE;
01050     NodeAttribute* pBevAttr = NULL;
01051     NodeAttribute* pObjAttr = NULL;
01052     List ChildList;
01053     NodeListItem * pItem = NULL;
01054     NodeRenderableInk* pCurrentNode = NULL;
01055 
01056     BevelTools::GetAllNodesUnderNode(this, &ChildList, CC_RUNTIME_CLASS(NodeRenderableInk));
01057     pItem = (NodeListItem *)ChildList.GetHead();
01058 
01059     INT32 Check = ChildList.GetCount();
01060 
01061     if(IsTransparent)
01062     {
01063         if(!pBevel->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrTranspFillGeometry),&pBevAttr))
01064             return FALSE;
01065     }
01066     else
01067     {
01068         if(!pBevel->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry),&pBevAttr))
01069             return FALSE;
01070     }
01071 
01072     while (pItem)
01073     {
01074         pCurrentNode = (NodeRenderableInk*)pItem->pNode;
01075 
01076         // is it selected implying that it is being transformed
01077         if (pCurrentNode && pCurrentNode->IsAnObject() && !pCurrentNode->IsABevel())
01078         {
01079             if(IsTransparent)
01080             {
01081                 if(pCurrentNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrTranspFillGeometry),&pObjAttr))
01082                 {
01083                     if(pObjAttr->IsDifferent(pBevAttr))
01084                         Check--;
01085                 }
01086             }
01087             else
01088             {
01089                 if(pCurrentNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry),&pObjAttr))
01090                 {
01091                     if(pObjAttr->IsDifferent(pBevAttr))
01092                         Check--;
01093                 }
01094             }
01095         }
01096 
01097         pItem = (NodeListItem *)ChildList.GetNext(pItem);
01098     }
01099     
01100     DoApplyToMe = ((ChildList.GetCount() - Check) <= 1);
01101 
01102     ChildList.DeleteAll();
01103     return DoApplyToMe;
01104 }
01105 
01106 /********************************************************************************************
01107 >   ChangeCode  NodeBevelController::OnChildChange(ObjChangeParam* pParam)
01108 
01109     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01110     Created:    15/12/98
01111     Inputs:     -
01112     Returns:    -
01113     Purpose:    Deals with child changes
01114 ********************************************************************************************/
01115 ChangeCode  NodeBevelController::OnChildChange(ObjChangeParam* pParam)
01116 {
01117     // get the op out of the parameter
01118     if (!pParam)
01119         return NodeGroup::OnChildChange(pParam);
01120 
01121     if (!pParam->GetOpPointer())
01122     {
01123         Document * pDoc = Document::GetCurrent();
01124 
01125         if (pDoc)
01126         {
01127             if (pParam->GetChangeFlags ().RegenerateNode)
01128                 RegenerateNode(NULL, FALSE, FALSE);     // CGS:  it is now legitimate for us to
01129                                                         // do this
01130         
01131 //          Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
01132         }
01133 
01134         return CC_OK;       // NodeController::OnChildChange() now regenerates the node when
01135                             // a non-undoable op is passed in (and we don't want this to happen
01136                             // with bevels!).  (Actually NodeCompound::OnChildChange())
01137     }
01138 
01139     UndoableOperation * pUndoOp = NULL;
01140 
01141     if (pParam->GetOpPointer()->IsKindOf(CC_RUNTIME_CLASS(UndoableOperation)))
01142         pUndoOp = pParam->GetOpPointer();
01143 
01144     if (!pUndoOp)
01145     {
01146         return CC_OK;       // NodeController::OnChildChange() now regenerates the node when
01147                             // a non-undoable op is passed in (and we don't want this to happen
01148                             // with bevels!).  (Actually NodeCompound::OnChildChange())
01149     }
01150 
01151     if (pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpCreateBevel)))
01152         return CC_OK;
01153 
01154     // first, invalidate my region
01155 //  if (pParam->GetChangeType() == OBJCHANGE_FINISHED)
01156 //      pUndoOp->DoInvalidateNodeRegion(this, TRUE);
01157 
01158     ChangeCode cd = CC_OK;
01159 
01160     BOOL bCallBase = TRUE;
01161 
01162     Node * pParent = NULL;
01163     List ChildList;
01164     BOOL bRegen = FALSE;
01165     NodeListItem * pItem = NULL;
01166     NodeAttribute * pAttr = NULL;
01167 
01168     if (m_PerformedExtend)
01169     {
01170         // this means that we have done an extend
01171         // which added the regen action into the action list
01172         // so no need to regen the node
01173         // just reset the flag and leave (sjk 4/8/2000)
01174         m_PerformedExtend = FALSE;
01175     }
01176     else
01177     // in bevels, no need to regenerate for tranparency changes, or for fill changes
01178     if (pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpReplaceAttributes)))
01179     {
01180         pAttr = ((OpReplaceAttributes *)pUndoOp)->GetAttribute();
01181         
01182         if (pAttr)
01183         {
01184             if (pAttr->IsAFillAttr() || pAttr->IsATranspFill())
01185                 bCallBase = FALSE;
01186         }
01187     }   
01188     else if (pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpEditFill)))
01189     {
01190         bCallBase = FALSE;
01191     }
01192     else if (pUndoOp->IS_KIND_OF (OpPathNudge))
01193     {
01194         RegenerateNode(pUndoOp, FALSE, FALSE);
01195     }
01196 
01197     // Karim 20/12/2000     added to fix an update bug - shadow & bevel some text,
01198     //                      enter some more & shadow doesn't get correct bounds from bevel.
01199     else if (pUndoOp->IS_KIND_OF (OpTextUndoable))
01200     {
01201         RegenerateNode(pUndoOp, FALSE, FALSE);
01202     }
01203 
01204     else if (pUndoOp->IsKindOf(CC_RUNTIME_CLASS(TransOperation)) &&
01205         !pUndoOp->IS_KIND_OF(OpMovePathPoint))
01206     {
01207         // a trans operation has occured - do nothing & leave it up to
01208         // the transform function
01209         // first, invalidate my region
01210 //      pUndoOp->DoInvalidateNodeRegion(this, TRUE);
01211 
01212         // find out if a select-inside drag has occurred (e.g. when a group is shadowed
01213         // and an object inside the group is selected and dragged then a regeneration 
01214         // should occur. This isn't taken care of by the transform method as this method
01215         // isn't called by the children.
01216 
01217         // first, get all the children
01218         BevelTools::GetAllNodesUnderNode(this, &ChildList, CC_RUNTIME_CLASS(NodeRenderableInk));
01219 
01220         pItem = (NodeListItem *)ChildList.GetHead();
01221 
01222         bRegen = FALSE;
01223         BOOL MustRegenerate = FALSE;
01224 
01225         while (pItem)
01226         {
01227             // is it selected implying that it is being transformed
01228 //          if (pItem->pNode->IsAnObject())
01229 //          {
01230 //              ((NodeRenderableInk *)pItem->pNode)->InvalidateBoundingRect();
01231 //              pUndoOp->DoInvalidateNodeRegion((NodeRenderableBounded *)pItem->pNode, TRUE);
01232 //          }
01233 //
01234             if (pItem->pNode->IsSelected()) // Are you SURE you can use selected here? - left only because it is a shortcut out
01235             {
01236                 // get & test its parent
01237                 pParent = pItem->pNode->FindParent();
01238                 
01239                 while (pParent)
01240                 {
01241                     // does the parent transform with children ?
01242                     if (pParent->ShouldITransformWithChildren())
01243                     {
01244                         // is the parent this object ?
01245                         if (pParent == this)
01246                         {
01247                             bRegen = FALSE; // don't regenerate
01248                             pParent = NULL; // break out of the loop
01249                         }
01250                         else
01251                         {
01252                             // go on to the next parent 
01253                             pParent = pParent->FindParent();
01254                         }
01255                     }
01256                     else
01257                     {
01258                         bRegen = TRUE; // yep, regenerate ! 
01259                         pParent = NULL; // break the loop
01260                     }
01261                 }
01262             }
01263 
01264             pItem = (NodeListItem *)ChildList.GetNext(pItem);
01265         }
01266 
01267         ChildList.DeleteAll();
01268 
01269         if (bRegen || MustRegenerate)
01270         {
01271             pUndoOp->DoInvalidateNodeRegion(this, TRUE);
01272             RegenerateNode(pUndoOp, FALSE, FALSE);
01273         }
01274 
01275         return CC_OK;
01276     }
01277     else if (pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpApplyAttrib)))
01278     {
01279         CCRuntimeClass * pClass = ((OpApplyAttrib *)pUndoOp)->GetValueChangeType();
01280 
01281         if (pClass)
01282         {
01283             if(pClass == CC_RUNTIME_CLASS(AttrBevel))
01284             {
01285                 pUndoOp->DoInvalidateNodeRegion(this, TRUE);
01286                 RegenerateNode(pUndoOp, FALSE, FALSE);
01287                 return CC_OK;
01288             }
01289             else if(pClass == CC_RUNTIME_CLASS(AttrStrokeColourChange))
01290             {
01291                 // If we are changing the outline colour then we need to check to see if it`s
01292                 // been made transparent. If so then we need to regenerate the bevel!
01293                 bCallBase = FALSE;
01294                 OpApplyAttrib* pOpApplyNode = (OpApplyAttrib*)pParam->GetOpPointer();
01295 
01296                 if(pOpApplyNode)
01297                 {
01298                     // We need to compare the current applied stroke colour with the one that`s being applied.
01299                     pAttr = (AttrStrokeColourChange*)pOpApplyNode->GetAttributeToApply();
01300                     
01301                     if(pAttr)
01302                     {
01303                         DocColour* pStrokeColour = ((AttrStrokeColour*)pAttr)->GetStartColour();
01304 
01305                         if(pStrokeColour)
01306                         {
01307                             bCallBase = (pStrokeColour->IsTransparent() != m_LastColoursTransparency);
01308                             m_LastColoursTransparency = pStrokeColour->IsTransparent();
01309                         }
01310                     }
01311                 }
01312             }
01313             else if(pClass == CC_RUNTIME_CLASS(AttrLineWidth))
01314             {
01315                 // Always update the bevel paths when we change the outline of the beveled objects
01316                 bCallBase = TRUE;
01317                 m_LastColoursTransparency = FALSE;
01318             }
01319         }
01320     }           
01321     
01322     if (bCallBase)
01323         cd = NodeGroup::OnChildChange(pParam);
01324 
01325     // If we delete an object then the bevel is nolonger true, therefore regenerate to get back the true form!
01326     if (pParam->GetChangeType() == OBJCHANGE_FINISHED && (
01327                pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpDelete))
01328             || pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpPaste))
01329             || pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpCut))
01330             ))
01331     {
01332         pUndoOp->DoInvalidateNodeRegion(this, TRUE);
01333         RegenerateNode(pUndoOp, FALSE, FALSE);
01334     }
01335     
01336     // return the change code
01337     return cd;
01338 }
01339 
01340 
01341 /********************************************************************************************
01342 >   Node* NodeBevelController::SimpleCopy()
01343 
01344     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01345     Created:    15/12/98
01346     Inputs:     -
01347     Returns:    -
01348     Purpose:    Makes a copy of the node
01349 ********************************************************************************************/
01350 Node* NodeBevelController::SimpleCopy()
01351 {
01352     NodeBevelController *pNewNode = new NodeBevelController();
01353     ERROR3IF(!pNewNode, "Error - Couldn't create new bevel controller node");
01354 
01355     CopyNodeContents(pNewNode);  
01356     
01357     return pNewNode;
01358 }
01359 
01360 
01361 /********************************************************************************************
01362 >   void NodeBevelController::CopyNodeContents(NodeBevelController* pNewNode)
01363 
01364     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01365     Created:    05/01/2004
01366     Inputs:     -
01367     Returns:    
01368     Purpose:    Sort of backwards copy constructor
01369 
01370 ********************************************************************************************/
01371 void NodeBevelController::CopyNodeContents(NodeBevelController* pNewNode)
01372 {
01373     pNewNode->m_LType = m_LType;
01374 
01375     pNewNode->m_Tilt = m_Tilt;
01376     
01377     // boolean which tells GetBoundingRect to include the blobs
01378     pNewNode->m_bIncludeBlobsInBoundingRect = m_bIncludeBlobsInBoundingRect;
01379 
01380     // points determining the fill control points
01381     pNewNode->m_BMPCentre = m_BMPCentre;
01382     pNewNode->m_BMPPoint1 = m_BMPPoint1;
01383     pNewNode->m_BMPPoint2 = m_BMPPoint2;
01384 
01385     // the rect of the (original) selection
01386     pNewNode->m_SelectedRect = m_SelectedRect;
01387 
01388     pNewNode->m_BlobDocRect = m_BlobDocRect;
01389     pNewNode->m_BlobCentre = m_BlobCentre;
01390     
01391     // indicates that a drag is in progress
01392     pNewNode->m_bDrag = m_bDrag;
01393 
01394     pNewNode->m_HaveDoneApplyTest = m_HaveDoneApplyTest;
01395 
01396     // indicates that the last bounding rect passed back was for the tool
01397     // used to see if we need to re-generate the bounding rect 
01398     pNewNode->m_bBoundingRectIsForTool = m_bBoundingRectIsForTool;
01399 
01400     pNewNode->m_LastZoom = m_LastZoom;
01401 
01402     pNewNode->m_PerformedExtend = m_PerformedExtend;
01403 
01404     pNewNode->m_LastColoursTransparency = m_LastColoursTransparency;
01405 
01406     // New ClipView Attribute for rendering Inner bevels
01407 //  if (pNewNode->m_pClipViewAttribute)
01408 //      delete pNewNode->m_pClipViewAttribute;
01409 //  pNewNode->m_pClipViewAttribute = NULL;
01410 //  if (m_pClipViewAttribute)
01411 //  {
01412 //      pNewNode->m_pClipViewAttribute = (AttrClipView*)m_pClipViewAttribute->SimpleCopy();
01413 //  }
01414     
01415     // Remember to copy or blank cached path data without leaking memory...
01416     pNewNode->m_PathToClipVeiw.Initialise(m_PathToClipVeiw.GetNumCoords());
01417     pNewNode->m_PathToClipVeiw.CopyPathDataFrom(&m_PathToClipVeiw);
01418 
01419     pNewNode->m_pBevel = NULL;
01420 
01421     pNewNode->m_LastClipZoom = m_LastClipZoom;
01422 
01423     pNewNode->m_BevelType   =   m_BevelType;
01424     pNewNode->m_Indent      =   m_Indent;
01425     pNewNode->m_LightAngle  =   m_LightAngle;
01426     pNewNode->m_bOuter      =   m_bOuter;                       // NodeController?
01427     pNewNode->m_Contrast    =   m_Contrast;
01428     pNewNode->m_bBlendStartNode = m_bBlendStartNode;            // NodeController?
01429     pNewNode->m_bBlendEndNode   = m_bBlendEndNode;              // NodeController?
01430     pNewNode->m_pBlendCreatedByNode = m_pBlendCreatedByNode;    // NodeController?
01431 
01432     NodeGroup::CopyNodeContents(pNewNode);
01433 }
01434 
01435 
01436 
01437 
01438 /***********************************************************************************************
01439 >   void NodeBevelController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
01440 
01441     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01442     Created:    18/12/2003
01443     Outputs:    -
01444     Purpose:    Polymorphically copies the contents of this node to another
01445     Errors:     An assertion failure will occur if NodeCopy is NULL
01446     Scope:      protected
01447                                      
01448 ***********************************************************************************************/
01449 
01450 void NodeBevelController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
01451 {
01452     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
01453     ENSURE(IS_A(pNodeCopy, NodeBevelController), "PolyCopyNodeContents given wrong dest node type");
01454 
01455     if (IS_A(pNodeCopy, NodeBevelController))
01456         CopyNodeContents((NodeBevelController*)pNodeCopy);
01457 }
01458 
01459 
01460 
01461 /********************************************************************************************
01462 >   BOOL NodeBevelController::PostDuplicate(UndoableOperation* pOp)
01463 
01464     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01465     Created:    1/6/99
01466     Inputs:     -
01467     Returns:    -
01468     Purpose:    Regens the node after a duplication
01469 ********************************************************************************************/
01470 BOOL NodeBevelController::PostDuplicate(UndoableOperation* pOp)
01471 {
01472     CreateBevel();
01473     return TRUE;
01474 }
01475 
01476 
01477 /********************************************************************************************
01478 
01479 >   virtual BOOL NodeBevelController::RequiresAttrib(CCRuntimeClass* pAttribClass,
01480                                             BOOL Search = FALSE)
01481 
01482     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01483     Created:    23/08/2000
01484     Inputs:     pAttribClass    the attr type to test.
01485                 Search          not a clue - look at base class implementation for info.
01486 
01487     Returns:    FALSE if pAttribClass is a brush attr,
01488                 calls base implementation otherwise.
01489 
01490     Purpose:    NodeBevelController does not require brush attrs.
01491 
01492     Errors:     returns FALSE if pAttribClass is NULL.
01493 
01494 ********************************************************************************************/
01495 BOOL NodeBevelController::RequiresAttrib(CCRuntimeClass* pAttribClass, BOOL Search)
01496 {
01497     if (pAttribClass == NULL || pAttribClass == CC_RUNTIME_CLASS(AttrBrushType))
01498         return FALSE;
01499     else
01500         return NodeRenderableInk::RequiresAttrib(pAttribClass, Search);
01501 }
01502 
01503 
01504 
01505 /********************************************************************************************
01506 
01507 >   virtual BOOL NodeBevelController::RequiresAttrib(NodeAttribute* pAttr,
01508                                             BOOL Search = FALSE)
01509 
01510     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01511     Created:    23/08/2000
01512     Inputs:     pAttr       the attr to test.
01513                 Search      not a clue - look at base class implementation for info.
01514 
01515     Returns:    FALSE if pAttr is a brush attr,
01516                 calls base implementation otherwise.
01517 
01518     Purpose:    NodeBevelController does not require brush attrs.
01519 
01520     Errors:     returns FALSE if pAttr is NULL.
01521 
01522 ********************************************************************************************/
01523 BOOL NodeBevelController::RequiresAttrib(NodeAttribute* pAttr, BOOL Search)
01524 {
01525     if (pAttr == NULL || pAttr->IS_KIND_OF(AttrBrushType))
01526         return FALSE;
01527     else
01528         return NodeRenderableInk::RequiresAttrib(pAttr, Search);
01529 }
01530 
01531 
01532 
01533 /********************************************************************************************
01534 
01535 >   virtual BOOL NodeBevelController::CanAttrBeAppliedToMe(CCRuntimeClass* pAttribClass)
01536 
01537     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01538     Created:    22/08/2000
01539     Inputs:     pAttribClass    the attr type to test.
01540     Returns:    FALSE if pAttribClass is a brush attr,
01541                 TRUE otherwise.
01542 
01543     Purpose:    Determine whether the given attr type can be applied directly to me.
01544                 We don't like brushed bevels, so we don't accept brush attrs.
01545 
01546     Errors:     returns FALSE if pAttribClass is NULL.
01547 
01548 ********************************************************************************************/
01549 BOOL NodeBevelController::CanAttrBeAppliedToMe(CCRuntimeClass* pAttribClass)
01550 {
01551     if (pAttribClass == NULL || pAttribClass == CC_RUNTIME_CLASS(AttrBrushType))
01552         return FALSE;
01553     else
01554         return TRUE;
01555 }
01556 
01557 
01558 /***********************************************************************************************
01559 
01560 >   INT32 NodeBevelController::ComplexHide(UndoableOperation* pOp, Node* pNextInRange)
01561 
01562     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01563     Created:    12/8/99
01564     Inputs:     See base class
01565     Purpose:    Hides this node
01566 
01567 ***********************************************************************************************/
01568 INT32 NodeBevelController::ComplexHide(UndoableOperation* pOp, Node* pNextInRange)
01569 {
01570     if(m_pClipViewAttribute)
01571     {
01572         m_pClipViewAttribute->UnlinkNodeFromTree();
01573         delete m_pClipViewAttribute;
01574         m_pClipViewAttribute = NULL;
01575     }
01576 
01577     NodeHidden * pHidden = NULL;
01578     BOOL ok = pOp->DoHideNode(this, TRUE, &pHidden, TRUE);
01579 
01580     if (ok)
01581         return 1;
01582 
01583     return -1;
01584 }
01585 
01586 BOOL NodeBevelController::HidingNode()
01587 {
01588     if(m_pClipViewAttribute)
01589     {
01590         m_pClipViewAttribute->UnlinkNodeFromTree();
01591         delete m_pClipViewAttribute;
01592         m_pClipViewAttribute = NULL;
01593     }
01594     return TRUE;
01595 }
01596 
01597 BOOL NodeBevelController::ShowingNode()
01598 {
01599     return TRUE;
01600 }
01601 
01602 /********************************************************************************************
01603 >   void NodeBevelController::RenderMyBlobs(RenderRegion* pRender)
01604 
01605     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01606     Created:    15/12/98
01607     Inputs:     -
01608     Returns:    -
01609     Purpose:    Renders the bevel blobs (bevel controller node cannot have tiny blobs !!)
01610 ********************************************************************************************/
01611 
01612 void NodeBevelController::RenderMyBlobs(RenderRegion* pRegion)
01613 {
01614 }
01615 
01616 /********************************************************************************************
01617 >   BOOL NodeBevelController::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, 
01618                         ContextMenu* pMenu)
01619 
01620     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01621     Created:    15/12/98
01622     Inputs:     -
01623     Returns:    -
01624     Purpose:    Does the pop-up menu
01625 ********************************************************************************************/
01626 BOOL NodeBevelController::OnNodePopUp(Spread* pSpread, DocCoord PointerPos, ContextMenu* pMenu)
01627 {
01628     return TRUE;
01629     // return pMenu->BuildCommand(OPTOKEN_SELECTBEVEL, TRUE);
01630 }
01631 
01632 /********************************************************************************************
01633 
01634 >   BOOL NodeBevelController::DoBecomeA(BecomeA* pBecomeA);
01635 
01636     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01637     Created:    26/2/99
01638     Inputs:     BecomeA struct
01639     Purpose:    Converts me to a path
01640     SeeAlso:    
01641 
01642 ********************************************************************************************/
01643 
01644 BOOL NodeBevelController::DoBecomeA(BecomeA* pBecomeA)
01645 {
01646     // Check for a NULL entry param                            
01647     ERROR2IF_PF(pBecomeA == NULL,FALSE,("pBecomeA is NULL"));
01648 
01649     if (!pBecomeA->BAPath())
01650     {
01651         return FALSE;
01652     }
01653 
01654     BOOL ValidReason = (pBecomeA->GetReason() == BECOMEA_REPLACE || pBecomeA->GetReason() == BECOMEA_PASSBACK);
01655     ERROR2IF_PF(!ValidReason,FALSE,("Unkown BecomeA reason %d",pBecomeA->GetReason()));
01656 
01657     UndoableOperation* pOp = pBecomeA->GetUndoOp();
01658 
01659 //  NodeGroup           * pGroup        = NULL;
01660     Node                * pNode         = NULL;
01661 //  Node                * pNextNode     = NULL;
01662 //  NodeHidden          * pHidden       = NULL;
01663 //  AttrStrokeColour    * pStrokeColour = NULL;
01664 
01665     DocColour NoColour(COLOUR_NONE);
01666 
01667 //  UINT32 end = 255;
01668 
01669 //  UINT32 NumObjs = 0;
01670 
01671     // If there`s a clipview, remove it!
01672     if(m_pClipViewAttribute)
01673     {
01674         m_pClipViewAttribute->UnlinkNodeFromTree();
01675         delete m_pClipViewAttribute;
01676         m_pClipViewAttribute = NULL;
01677     }
01678 
01679     switch (pBecomeA->GetReason())
01680     {
01681     case BECOMEA_REPLACE:
01682     {
01683         // if any of our children are selected, then we'll have to remember to select
01684         // the group created in our place.
01685         BOOL fSelectBecomeAGroup = IsParentOfSelected();
01686 
01687         // call NodeGroup's implementation first
01688         if (!NodeGroup::DoBecomeA(pBecomeA))
01689             return FALSE;
01690 
01691         // now turn into a group.
01692         NodeGroup* pGroup = BecomeAGroup(pOp);
01693 
01694         // select the group but don't bother redrawing blobs an' suchlike.
01695         if (fSelectBecomeAGroup && pGroup != NULL)
01696             pGroup->Select(FALSE);
01697     }
01698     break;
01699 
01700     case BECOMEA_PASSBACK :
01701         if (pBecomeA->IsBlendBecomeA())
01702         {
01703             CompoundNodeBlendBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath),
01704                 NULL, FALSE, this, pBecomeA);
01705             
01706             // now that the become a is set up call all my sub nodes with this
01707             Node * pChild = FindFirstChild();
01708             
01709             while (pChild)
01710             {
01711                 if (pChild->IsAnObject() && !pChild->NeedsParent(NULL))
01712                 {
01713                     MyBecomeA.ResetCount();
01714                     
01715                     if (pChild->CanBecomeA(&MyBecomeA))
01716                     {
01717                         // tell the "become a" that we're starting a block
01718                         MyBecomeA.SetNumPathNodes(MyBecomeA.GetCount());
01719                         MyBecomeA.ResetCount();
01720                         
01721                         pChild->DoBecomeA(&MyBecomeA);
01722                     }
01723                 }
01724                 
01725                 pChild = pChild->FindNext();
01726             }
01727         }
01728         else if (pBecomeA->IsCompoundBlendBecomeA ())           // test code ....
01729         {
01730             // CGS:  another compound node (of which we are a child) is requesting us to
01731             // BECOMEA_PASSBACK so get on and do it ....
01732             
01733             // this code is only called when blending bevels that have been shadowed
01734             // and when converting to editable shapes (in the forementioned case)
01735 
01736             CompoundNodeBlendBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath),
01737                 NULL, FALSE, this, pBecomeA);
01738             
01739             Node * pChild = FindFirstChild();
01740             
01741             while (pChild)
01742             {
01743                 if (pChild->IsAnObject() && !pChild->NeedsParent(NULL))
01744                 {
01745                     MyBecomeA.ResetCount();
01746                     
01747                     if (pChild->CanBecomeA(&MyBecomeA))
01748                     {
01749                         // tell the become a that we're starting a block
01750                         MyBecomeA.SetNumPathNodes(MyBecomeA.GetCount());
01751                         MyBecomeA.ResetCount();
01752                         
01753                         pChild->DoBecomeA(&MyBecomeA);
01754                     }
01755                 }
01756                 
01757                 pChild = pChild->FindNext();
01758             }
01759         }
01760         else
01761         {
01762             // Sequentially ask the children of the blend to DoBecomeA
01763             // This is all that's required because the child objects are only passing back
01764             // the new node type, and NOT replacing themselves in the tree
01765             // This also ensures the receipient gets the list of paths in render order
01766             pNode = FindFirstChild();
01767             while (pNode != NULL)
01768             {
01769                 if (!pNode->DoBecomeA(pBecomeA))
01770                     return FALSE;
01771 
01772                 pNode = pNode->FindNext();
01773             }
01774         }
01775 
01776         break;
01777         default: break;
01778     }
01779 
01780     return TRUE;
01781 }
01782 
01783 /********************************************************************************************
01784 
01785 >   BOOL NodeBevelController::CanBecomeA(BecomeA* pBecomeA);
01786 
01787     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01788     Created:    26/2/99
01789     Inputs:     Ink class to test
01790     Purpose:    Can become a path !
01791     SeeAlso:    
01792 
01793 ********************************************************************************************/
01794 
01795 BOOL NodeBevelController::CanBecomeA(BecomeA* pBecomeA)
01796 {
01797     // Can only convert to paths
01798     if (!pBecomeA->BAPath())
01799         return FALSE;
01800 
01801     NodeRenderableInk * pNode = (NodeRenderableInk *)FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk));
01802 
01803     while (pNode)
01804     {
01805         if (!pNode->NeedsParent(NULL))
01806         {
01807             // Update number of children that can become a path
01808             pNode->CanBecomeA(pBecomeA);
01809         }
01810         pNode = (NodeRenderableInk *)pNode->FindNext(CC_RUNTIME_CLASS(NodeRenderableInk));
01811     }
01812     
01813     return TRUE;
01814 }
01815 
01816 /********************************************************************************************
01817 
01818 >   void NodeBevelController::CreateBevel()
01819 
01820     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01821     Created:    6/5/99
01822     Inputs:     
01823     Purpose:    Creates the bevel bitmaps immediately, not waiting for the next render-click
01824     SeeAlso:    
01825 
01826 ********************************************************************************************/
01827 
01828 void NodeBevelController::CreateBevel()
01829 {
01830     NodeBevel * pBevel = GetBevelNode();
01831 
01832     if (pBevel)
01833     {
01834         if(pBevel->GenerateBevel())
01835             m_pBevel = pBevel;
01836         else
01837             m_pBevel = NULL;
01838     }
01839     else
01840         ERROR3("Failed to create a new bevel node!");
01841 
01842     return;
01843 }
01844 
01845 /********************************************************************************************
01846 
01847 >   BOOL NodeBevelController::PreApplyClipViewNode(RenderRegion* pRender)
01848 
01849     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
01850     Created:    05/12/00
01851     Inputs:     A Render region to render into
01852     returns:    TRUE to say that we inserted a new ClipViewAttribute, FALSE to say we didn`t!
01853     Purpose:    Inserts a ClipViewAttribute on the Beveled object so that we can illiminate
01854                 Aliasing effects when the bevel is rendered on top.
01855 
01856 ********************************************************************************************/
01857 BOOL NodeBevelController::PreApplyClipViewNode(RenderRegion* pRender)
01858 {
01859     // Setup a return Variable
01860     BOOL Ok = FALSE;
01861 
01862     // First check the pointers and make sure we`re an inner bevel
01863     if(pRender && m_pBevel && !m_pBevel->m_bOuter)
01864     {
01865         // Get the current join type on the bevel!
01866         AttrJoinType * pJoinType = NULL;
01867         m_pBevel->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrJoinType), (NodeAttribute **)(&pJoinType));
01868         
01869         JointType JT = RoundJoin;
01870         if (pJoinType)
01871             JT = pJoinType->Value.JoinType;
01872 
01873         JoinStyles JoinStyle = JOIN_ROUND;
01874         JoinStyle = (JT == MitreJoin) ? JOIN_MITER : (JT == RoundJoin) ? JOIN_ROUND : JOIN_BEVEL;
01875 
01876         double Width = 750.0;
01877         if(pRender->GetScaledPixelWidth() > 0)
01878             Width = 2.0 * pRender->GetScaledPixelWidth();
01879 
01880         // Get the current Path from the Bevel node as this is the path that the
01881         // clipping view will be using.
01882         if(!m_pBevel->GetBevelPath())
01883             m_pBevel->CreateBevelPaths();
01884 
01885         Path* pTemp = m_pBevel->GetBevelPath();
01886         Path BevelPath;
01887         BevelPath.Initialise();
01888         BevelPath.CloneFrom(*pTemp);
01889 
01890         m_PathToClipVeiw.ClearPath();
01891         BevelPath.InitializeContourValues((UINT32)Width,JoinStyle,FALSE);
01892 
01893         // Make sure that the path we want to be doing the clipping is valid
01894         if(BevelPath.GetContourForStep(&m_PathToClipVeiw) > 2)
01895         {
01896             if(!m_pClipViewAttribute)
01897             {
01898                 // Now find the first Renderable Ink Node and try to apply the new Attribute!
01899                 NodeRenderableInk* pFoundNode = FindFirstChildInk();
01900                 while(pFoundNode)
01901                 {
01902                     // Only apply the Attribute to NON bevel objects
01903                     if(!pFoundNode->IsABevel())
01904                     {
01905                         // First do a quick check to see if we`ve already got a clipview on this node!
01906                         Node* pChild = pFoundNode->FindFirstChild();
01907                         while(pChild)
01908                         {
01909                             if(pChild->IsAClipViewAttr())
01910                             {
01911                                 m_pClipViewAttribute = (AttrClipView*)pChild;
01912                                 break;
01913                             }
01914 
01915                             pChild = pChild->FindNext();
01916                         }
01917 
01918                         // If we found one then use it, else create a new one!
01919                         if(!m_pClipViewAttribute)
01920                             m_pClipViewAttribute = new AttrClipView(pFoundNode,FIRSTCHILD);
01921 
01922                         if(m_pClipViewAttribute)
01923                         {
01924                             m_pClipViewAttribute->Value.SetClipPath(&m_PathToClipVeiw);
01925                             Ok = TRUE;
01926                         }
01927                         break;
01928                     }
01929                     else
01930                         pFoundNode = pFoundNode->FindNextInk();
01931                 }
01932             }
01933             else
01934             {
01935                 if(m_pClipViewAttribute)
01936                     m_pClipViewAttribute->Value.SetClipPath(&m_PathToClipVeiw);
01937                 Ok = TRUE;
01938             }
01939         }
01940         else
01941         {
01942             TRACEUSER( "MarkH", _T("Clip Path Came to Less Than 2 Points!"));
01943             Ok = TRUE;
01944         }
01945     }
01946 
01947     // return Ok!
01948     ERROR3IF(!Ok,"Didn`t Apply a ClipViewAttribute for inner bevels!");
01949     return Ok;
01950 }
01951 
01952 /********************************************************************************************
01953 
01954 >   SubtreeRenderState NodeBevelController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip = TRUE)
01955 
01956     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01957     Created:    28/9/00
01958     Purpose:    Inserts a ClipViewAttribute on the Beveled object so that we can illiminate
01959                 Aliasing effects when the bevel is rendered on top.
01960     See Also:   PreApplyClipViewNode();
01961 
01962 ********************************************************************************************/
01963 SubtreeRenderState NodeBevelController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip)
01964 {
01965     if (pRender && pRender->IsPrinting())
01966     {
01967         TRACEUSER( "MarkH", _T("Rendering PrePrintChildren NODEBEVEL! <------------------------\n"));
01968 
01969         // Make sure we`ve got valid pointers and we`re not hittesting!
01970         if(pRender && !pRender->IsHitDetect() && m_pBevel && !m_pBevel->m_bOuter)
01971             PreApplyClipViewNode(pRender);
01972 
01973         return NodeGroup::RenderSubtree(pRender, ppNextNode, bClip);
01974     }
01975     else
01976     {
01977         TRACEUSER( "MarkH", _T("Rendering PreChildren NODEBEVEL! <------------------------\n"));
01978 
01979     #ifdef _DEBUG
01980     //  if(pRender)
01981     //      pRender->m_TraceOutContextLevels = TRUE;
01982     #endif
01983 
01984         if(m_pClipViewAttribute)
01985             m_pClipViewAttribute->EnableRendering();
01986 
01987         if(!m_pBevel)
01988             m_pBevel = GetBevelNode();
01989 
01990         // Make sure we`ve got valid pointers and we`re not hittesting!
01991         if(pRender && !pRender->IsHitDetect() && m_pBevel && !pRender->IsPrinting())
01992         {
01993             if(!m_pBevel->m_bOuter)
01994                 PreApplyClipViewNode(pRender);
01995             else if(m_pBevel->m_bOuter && m_pClipViewAttribute)
01996             {
01997                 m_pClipViewAttribute->UnlinkNodeFromTree();
01998                 delete m_pClipViewAttribute;
01999                 m_pClipViewAttribute = NULL;
02000             }
02001         }
02002 
02003         return NodeGroup::RenderSubtree(pRender, ppNextNode, bClip);
02004     }
02005     return SUBTREE_ROOTANDCHILDREN;
02006 }
02007 
02008 /********************************************************************************************
02009 
02010 >   void NodeBevelController::PreExportRender( RenderRegion* pRender )
02011 
02012     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
02013     Created:    28/9/00
02014     Inputs:     A render region to render into
02015     Purpose:    Inserts a ClipViewAttribute on the Beveled object so that we can illiminate
02016                 Aliasing effects when the bevel is rendered on top.
02017     See Also:   PreApplyClipViewNode();
02018 
02019 ********************************************************************************************/
02020 void NodeBevelController::PreExportRender( RenderRegion* pRender )
02021 {
02022     TRACEUSER( "MarkH", _T("PreExport Rendering NODEBEVEL! <------------------------\n"));
02023 
02024 #ifdef DO_EXPORT
02025     if (pRender->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
02026     {
02027         // Output "start group" token
02028         EPSExportDC *pDC = (EPSExportDC *) pRender->GetRenderDC();
02029 
02030         // (ChrisG 16/01/01) don't export the ClipViewAttribute if it's an inner bevel,
02031         //  doing so can cause the internal object to be clipped to a path that is smaller
02032         //  than the hole left in the bitmap (especially if it is exported at a low zoom
02033         //  factor, e.g. 10%).
02034         if(m_pBevel)
02035         {
02036             if (!m_pBevel->m_bOuter && m_pClipViewAttribute)
02037             {
02038                 m_pClipViewAttribute->EnableRendering(FALSE);
02039             }
02040             else if (m_pBevel->m_bOuter)
02041             {
02042                 pDC->OutputToken(_T("u"));
02043                 pDC->OutputNewLine();
02044             }
02045         }
02046     }
02047 PORTNOTE("cmx", "Removed use of CMXRenderRegion")
02048 #ifndef EXCLUDE_FROM_XARALX
02049     else if(pRender->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
02050     {
02051         // mark start of a group...
02052         CMXExportDC *pDC = (CMXExportDC *) pRender->GetRenderDC();
02053         DocRect BBox = GetBoundingRect(FALSE,FALSE);
02054         pDC->StartGroup(&BBox);
02055     }
02056 #endif
02057 #endif
02058 }
02059 
02060 BOOL NodeBevelController::ExportRender(RenderRegion* pRegion) 
02061 {
02062 #ifdef DO_EXPORT
02063     if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
02064     {
02065         // Output "end group" token
02066         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
02067 
02068         if(m_pBevel && m_pBevel->m_bOuter)
02069             pDC->OutputToken(_T("U"));
02070 
02071         pDC->OutputNewLine();
02072         
02073         // Tell caller we rendered ourselves ok
02074         return TRUE;
02075     }
02076 PORTNOTE("cmx", "Removed use of CMXRenderRegion")
02077 #ifndef EXCLUDE_FROM_XARALX
02078     else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
02079     {
02080         // mark start of a group...
02081         CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC();
02082         pDC->EndGroup();
02083 
02084         return TRUE;
02085     }
02086 #endif
02087     if(m_pBevel)
02088         m_pBevel->SetConvertingFlag();
02089 
02090 #endif
02091     // Render this node in the normal way
02092     return FALSE;
02093 }
02094 
02095 /********************************************************************************************
02096 
02097 >   void NodeBevelController::Render(RenderRegion* pRender)
02098 
02099     Author:     Mark_Howitt (Xara Group Ltd) <camelotdev@xara.com>
02100     Created:    28/9/00
02101     Inputs:     A render region to render into
02102     Purpose:    Main purpose is to remove any clipping attributes from the objects if
02103                 we are doing inner bevels!
02104 
02105 ********************************************************************************************/
02106 void NodeBevelController::Render(RenderRegion* pRender)
02107 {
02108     TRACEUSER( "MarkH", _T("Finished Rendering NODEBEVEL! <------------------------\n"));
02109 
02110     if(m_pClipViewAttribute && !m_pClipViewAttribute->GetRenderStatus())
02111         m_pClipViewAttribute->EnableRendering();
02112 #ifdef _DEBUG
02113 //  if(pRender)
02114 //      pRender->m_TraceOutContextLevels = FALSE;
02115 #endif
02116 }
02117 
02118 /********************************************************************************************
02119 
02120 >   BOOL NodeBevelController::RegenerateNode(UndoableOperation * pOp)
02121 
02122     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02123     Created:    6/5/99
02124     Inputs:     
02125     Returns:    TRUE to indicate that the node was changed, FALSE if nothing changed
02126     Purpose:    Forces a regeneration of the node in an undoable way
02127     SeeAlso:    
02128 
02129 ********************************************************************************************/
02130 BOOL NodeBevelController::RegenerateNode(UndoableOperation * pOp, BOOL bCacheRender,
02131                                          BOOL bInformParents)
02132 {
02133     // we don't regenerate for printing (yet ! - we actually regenerate when rendered)
02134     NodeBevel * pBevel = GetBevelNode();
02135 
02136     if (!pBevel)
02137     {
02138         ERROR3("Cannot find bevel node (RegenerateNode)");
02139         return FALSE;
02140     }       
02141 
02142     List BevList;
02143     NodeListItem * pItem = NULL;
02144     RegenerateBevelAction* pRegenAction = NULL;
02145 
02146     Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
02147 
02148     if (!pSpread)
02149         pSpread = Document::GetSelectedSpread();
02150 
02151 //  Document * pDoc = Document::GetCurrent();
02152 
02153     ERROR2IF(pSpread == NULL, FALSE, "No spread found for regeneration of node");
02154     
02155     if (pOp)
02156     {
02157         pItem = new NodeListItem;
02158 
02159         if (!pItem)
02160         {
02161             ERROR3("Couldn't create list item");
02162             return FALSE;
02163         }
02164 
02165         pItem->pNode = pBevel;
02166 
02167         BevList.AddTail(pItem);
02168 
02169         Document * pDoc     = Document::GetCurrent();
02170 
02171         if (!pDoc)
02172             return FALSE;
02173 
02174         Spread * pSpread    = this->FindParentSpread();
02175 
02176         if (!pSpread)
02177             return FALSE;
02178 
02179         // invalidate my region first
02180         ReleaseCached();
02181         pOp->DoInvalidateRegion(pSpread, BoundingRectangle);
02182 
02183         // set up the action to regenerate the bevel on this undo operation
02184         if (RegenerateBevelAction::Init(pOp, pOp->GetUndoActionList(),&BevList,&pRegenAction,
02185             bCacheRender)  == AC_FAIL)
02186         {
02187             ERROR3("RegenerateBevelAction::Init failed !\n");
02188             return FALSE;
02189         }
02190 
02191         BevList.DeleteAll();
02192     }
02193     else
02194     {
02195         // inform parents of change before
02196         if (bInformParents)
02197         {
02198             PreInformParentsOfRegenerate();
02199         }
02200 
02201         CreateBevel();
02202         InvalidateBoundingRect(TRUE);
02203 
02204         // inform parents of change
02205         if (bInformParents)
02206         {
02207             PostInformParentsOfRegenerate();
02208         }
02209     }
02210 
02211     // make sure we kick the sel range
02212     GetApplication()->UpdateSelection();
02213 
02214     return TRUE;
02215 }
02216 
02217 /********************************************************************************************
02218 
02219 >   BOOL NodeBevelController::RegenerateBevelBitmap(UndoableOperation * pOp, 
02220                 BOOL bInformParents = TRUE);
02221 
02222 
02223     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02224     Created:    6/5/99
02225     Inputs:     
02226     Purpose:    Forces a regeneration of the bevel, but doesn't regenerate the path
02227     SeeAlso:    
02228 
02229 ********************************************************************************************/
02230 BOOL NodeBevelController::RegenerateBevelBitmap(UndoableOperation * pOp, 
02231                 BOOL bInformParents)
02232 {
02233     NodeBevel * pBevel = GetBevelNode();
02234 
02235     if (!pBevel)
02236     {
02237         ERROR3("Cannot find bevel node (RegenerateNode)");
02238         return TRUE;
02239     }       
02240 
02241     List BevList;
02242     NodeListItem * pItem = NULL;
02243     RegenerateBevelBitmapAction* pRegenAction = NULL;
02244     
02245     if (pOp)
02246     {
02247         // invalidate the bevel node
02248         pBevel->InvalidateBoundingRect();
02249         pOp->DoInvalidateNodeRegion(pBevel, TRUE, TRUE);
02250 
02251         pItem = new NodeListItem;
02252 
02253         if (!pItem)
02254         {
02255             ERROR3("Couldn't create list item");
02256             return FALSE;
02257         }
02258 
02259         pItem->pNode = pBevel;
02260         BevList.AddTail(pItem);
02261         
02262         Document * pDoc     = Document::GetCurrent();
02263 
02264         if (!pDoc)
02265             return FALSE;
02266 
02267         Spread * pSpread    = this->FindParentSpread();
02268 
02269         if (!pSpread)
02270             return FALSE;
02271 
02272         // invalidate my region first
02273         ReleaseCached();
02274         pOp->DoInvalidateRegion(pSpread, BoundingRectangle);
02275 
02276         // set up the action to regenerate the bevel on this undo operation
02277         if (RegenerateBevelBitmapAction::Init(pOp, pOp->GetUndoActionList(),&BevList,&pRegenAction)  
02278             != AC_OK)
02279         {
02280             ERROR3("RegenerateBevelAction::Init failed !\n");
02281             return FALSE;
02282         }
02283 
02284         BevList.DeleteAll();
02285     }
02286     else
02287     {
02288         // inform parents of change before
02289         if (bInformParents)
02290         {
02291             PreInformParentsOfRegenerate();
02292         }
02293 
02294         if (pBevel)
02295         {
02296             if (!pBevel->SetupVariables())
02297                 return FALSE;
02298             
02299             if (!pBevel->ReRenderBevelBitmap(TRUE))
02300                 return FALSE;
02301         }
02302 
02303         // inform parents of change
02304         if (bInformParents)
02305         {
02306             PostInformParentsOfRegenerate();
02307         }
02308     }
02309 
02310     // make sure we kick the sel range
02311     GetApplication()->UpdateSelection();
02312 
02313     return TRUE;
02314 }
02315 
02316 
02317 /********************************************************************************************
02318 
02319 >   virtual BOOL NodeBevelController::AllowOp(  ObjChangeParam *pParam,
02320                                                 BOOL SetOpPermissionState,
02321                                                 BOOL DoPreTriggerEdit = TRUE )
02322 
02323     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>; Karim 20/01/2000
02324     Created:    21/6/99
02325     Inputs:     pParam                  describes the way an op wants to change the node
02326                 SetOpPermissionState    if TRUE the OpPermission of nodes should be set
02327                 DoPreTriggerEdit        if TRUE then NameGallery::PreTriggerEdit is called.
02328                                         *Must* be TRUE if the calling Op may make any nodes
02329                                         change their bounds, eg move, line width, cut.
02330                                         Use TRUE if unsure.
02331     Purpose:    
02332     SeeAlso:    
02333 
02334 ********************************************************************************************/
02335 BOOL NodeBevelController::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState,
02336                                                           BOOL DoPreTriggerEdit)
02337 {
02338     ERROR2IF(pParam == NULL, FALSE, "NodeBevelController::AllowOp; NULL pParam");
02339 
02340     // Set up a flag to see if any of the child objects get changed
02341     BOOL allowed = TRUE;
02342 
02343     UndoableOperation* pOp = pParam->GetOpPointer();
02344 
02345     if (pOp)
02346     {
02347         // can't contour a bevelled object
02348         if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpCreateContour)) ||
02349             pOp->IsKindOf(CC_RUNTIME_CLASS(OpChangeContourWidth)))
02350         {
02351             allowed = FALSE;
02352         }
02353         else if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpRemoveBlend)))
02354         {
02355             allowed = FALSE;
02356             pParam->SetReasonForDenial(_R(IDS_CANT_REMOVE_BLEND_WHEN_BEVELLED));
02357         }
02358     }
02359 
02360     if (pParam->GetDirection() != OBJCHANGE_CALLEDBYCHILD ||
02361         pParam->GetCallingChild() != NULL)
02362     {
02363         BOOL AnyAllowed = AllowOp_AccountForCompound( pParam,
02364                                                       SetOpPermissionState,
02365                                                       DoPreTriggerEdit );
02366 
02367         // if called by a parent, just pass this result back.
02368         if (pParam->GetDirection() == OBJCHANGE_CALLEDBYPARENT)
02369             return AnyAllowed;
02370     }
02371 
02372     // if we allowed it, see if our parents do ...
02373     if (allowed && Parent != NULL && pParam->GetDirection() != OBJCHANGE_CALLEDBYPARENT)
02374     {
02375         ObjChangeDirection OldDirection = pParam->GetDirection();
02376         pParam->SetCallingChild(this);
02377         pParam->SetDirection(OBJCHANGE_CALLEDBYCHILD);
02378         allowed = Parent->AllowOp(pParam, SetOpPermissionState, DoPreTriggerEdit);
02379         pParam->SetDirection(OldDirection);
02380     }
02381 
02382     // if necessary, set permissions for OnChildChange.
02383     if (SetOpPermissionState)
02384         SetOpPermission(allowed ? PERMISSION_ALLOWED : PERMISSION_DENIED, TRUE);
02385 
02386     // if we're ok so far and were asked to do a PreTriggerEdit, then
02387     // determine whether the Op may change the bounds of some nodes.
02388     // If it may, then call NameGallery::PreTriggerEdit.
02389     if (allowed && DoPreTriggerEdit)
02390     {
02391         // if the Op is non-NULL then query its MayChangeNodeBounds() method.
02392         UndoableOperation* pChangeOp = pParam->GetOpPointer();
02393         if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds() && NameGallery::Instance())
02394         {
02395             NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this);
02396         }
02397     }
02398 
02399     return allowed;
02400 }
02401 
02402 /********************************************************************************************
02403 
02404 >   void NodeBevelController::RenderEorDrag(RenderRegion * pRegion)
02405 
02406     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02407     Created:    19/7/99
02408     Inputs:     
02409     Purpose:    Renders the Eor drag stuff
02410     SeeAlso:    
02411 
02412 ********************************************************************************************/
02413 void NodeBevelController::RenderEorDrag(RenderRegion * pRegion)
02414 {
02415     BOOL bChildrenSelected = FALSE;
02416 
02417     Node * pNode = FindFirstChild();
02418     Node * pInsideNode = NULL;
02419 
02420     // check for a select inside drag
02421     while (pNode)
02422     {
02423         if (pNode->IsSelected())
02424         {
02425             bChildrenSelected = TRUE;
02426             break;
02427         }
02428 
02429         pNode = pNode->FindNext();
02430     }
02431 
02432     // check whether my parents are selected
02433     // if one of my parents are selected then this can't be a select inside !
02434     if (!bChildrenSelected)
02435     {
02436         Node * pSelParent = FindParent();
02437     
02438         while (pSelParent && pSelParent->IsAnObject())
02439         {
02440             if (pSelParent->IsSelected())
02441             {
02442                 bChildrenSelected = TRUE;
02443                 break;
02444             }
02445 
02446             pSelParent = pSelParent->FindParent();
02447         }
02448     }
02449 
02450     if (bChildrenSelected || IsSelected())
02451     {
02452         NodeBevel * pBevel = GetBevelNode();
02453 
02454         if (pBevel)
02455         {
02456             pBevel->RenderEorDrag(pRegion);
02457         }   
02458     }
02459     else
02460     {
02461         // render all selected nodes eor drags
02462         pInsideNode = FindFirstDepthFirst();
02463 
02464         while (pInsideNode != this && pInsideNode != NULL)
02465         {
02466             if (pInsideNode->IsSelected() && pInsideNode->IsAnObject())
02467             {
02468                 ((NodeRenderableInk *)pInsideNode)->RenderEorDrag(pRegion);
02469             }
02470 
02471             pInsideNode = pInsideNode->FindNextDepthFirst(this);
02472         }
02473     }
02474 }
02475 
02476 
02477 
02478 /********************************************************************************************
02479 
02480 >   virtual DocRect NodeBevelController::ValidateExtend(const ExtendParams& ExtParams)
02481 
02482     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02483     Created:    06/12/1999
02484     Inputs:     ExtParams       description parameters for the extension.
02485     Outputs:    
02486     Returns:    TRUE if extending this Node and its children is a reversible operation,
02487                 FALSE otherwise.
02488     Purpose:    Tests the reversibility of an Extend operation applied to this node.
02489 
02490                 In the case of a NodeBevelController, this function is identical to Node's
02491                 implementation, except that this controller's own NodeBevel child is ignored.
02492                 This allows the NodeBevel to tell its parent to extend without fear of
02493                 infinite recursion.
02494     Errors:     
02495     See also:   IsTypeExtendible(), Extend().
02496 
02497 ********************************************************************************************/
02498 DocRect NodeBevelController::ValidateExtend(const ExtendParams& ExtParams)
02499 {
02500     Node* pBob = GetBevelNode();
02501     DocRect drMinExtend(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX), drThisMinExtend;
02502     for (   Node* pChildNode = FindFirstChild();
02503             pChildNode != NULL;
02504             pChildNode = pChildNode->FindNext() )
02505     {
02506         if (pChildNode == pBob)
02507             continue;
02508 
02509         drThisMinExtend = pChildNode->ValidateExtend(ExtParams);
02510         if (drMinExtend.lo.x > drThisMinExtend.lo.x) drMinExtend.lo.x = drThisMinExtend.lo.x;
02511         if (drMinExtend.lo.y > drThisMinExtend.lo.y) drMinExtend.lo.y = drThisMinExtend.lo.y;
02512         if (drMinExtend.hi.x > drThisMinExtend.hi.x) drMinExtend.hi.x = drThisMinExtend.hi.x;
02513         if (drMinExtend.hi.y > drThisMinExtend.hi.y) drMinExtend.hi.y = drThisMinExtend.hi.y;
02514     }
02515     return drMinExtend;
02516 }
02517 
02518 
02519 
02520 /********************************************************************************************
02521 
02522 >   virtual void NodeBevelController::Extend(const ExtendParams& ExtParams)
02523 
02524     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02525     Created:    06/12/1999
02526     Inputs:     ExtParams       description parameters for the extension.
02527     Outputs:    Some of this node's children may have their dimensions altered.
02528     Returns:    
02529     Purpose:    Perform an Extend operation on this Node, and its children if appropriate.
02530 
02531                 In the case of a NodeBevelController, this function is identical to Node's
02532                 implementation, except that this controller's own NodeBevel child is ignored.
02533                 This allows the NodeBevel to tell its parent to extend without fear of
02534                 infinite recursion.
02535     Errors:     
02536     See also:   
02537 
02538 ********************************************************************************************/
02539 void NodeBevelController::Extend(const ExtendParams& ExtParams)
02540 {
02541     Node* pBob = GetBevelNode();
02542     for (   Node* pChildNode = FindFirstChild();
02543             pChildNode != NULL;
02544             pChildNode = pChildNode->FindNext() )
02545     {
02546         if (pChildNode == pBob)
02547             continue;
02548 
02549         pChildNode->Extend(ExtParams);
02550     }
02551 
02552     // Add an action to regen the bevel here
02553     // and instead of regening the bev when the m_PerformedExtend occurs in the child change
02554     // cut out and let the action do the stuff
02555     m_PerformedExtend = TRUE; 
02556 
02557     RegenerateNode(ExtParams.pOp, FALSE, FALSE);
02558 }
02559 
02560 /********************************************************************************************
02561 
02562 >   virtual NodeRenderableInk * 
02563             NodeBevelController::CreateTreeFromNodeToBlend(NodeRenderableInk * pNode)
02564 
02565     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02566     Created:    20/2/2000
02567     Inputs:     The node to wrap up
02568     Outputs:    NULL for failure
02569     Returns:    The root of the new tree
02570     Purpose:    Creates a copy of my tree, wrapping up the given node
02571     
02572 ********************************************************************************************/
02573 NodeRenderableInk * 
02574             NodeBevelController::CreateTreeFromNodeToBlend(NodeRenderableInk * pNode, 
02575             CCAttrMap * pAttrMap)
02576 {
02577     // remove all attributes from the node's subtree
02578     ERROR2IF(pNode->FindParent() != NULL, NULL, "Node shouldn't have parents");
02579 
02580     if (pNode->IsNodePath())
02581     {
02582         pNode->CascadeDelete();
02583     }
02584     
02585     // apply the attribute map to this node
02586     if (pNode->IsNodePath())
02587     {
02588         pNode->ApplyAttributes(pAttrMap, FALSE);
02589     }
02590 
02591     // now, lets make a copy of myself & the bevel node
02592     NodeBevelController * pControl = NULL;
02593 
02594     pControl = (NodeBevelController *)this->PublicCopy();
02595     ERRORIF(pControl == NULL, _R(IDE_NOMORE_MEMORY), NULL);
02596 
02597     NodeBevel * pBevel = (NodeBevel *)FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
02598     ERROR2IF(pBevel == NULL, NULL, "Can't find bevel node");
02599 
02600     // apply the attribute map of the original bevel node to the bevel copy node
02601     NodeBevel * pCopyBevel = (NodeBevel *)pBevel->PublicCopy();
02602     ERRORIF(pCopyBevel == NULL, _R(IDE_NOMORE_MEMORY), NULL);
02603     
02604     CCAttrMap * pBevMap = CCAttrMap::MakeAppliedAttrMap(pBevel);
02605     ERRORIF(pBevMap == NULL, _R(IDE_NOMORE_MEMORY), NULL);
02606 
02607     pCopyBevel->ApplyAttributes(pBevMap, FALSE);
02608 
02609     delete pBevMap;
02610 
02611     NodeBevelBegin * pNewBevelBegin = new NodeBevelBegin;
02612     ERRORIF(pNewBevelBegin == NULL, _R(IDE_NOMORE_MEMORY), NULL);
02613     pNewBevelBegin->AttachNode(pControl, FIRSTCHILD);
02614 
02615     pNode->AttachNode(pNewBevelBegin, NEXT);
02616     
02617     if(m_bOuter)
02618         pCopyBevel->AttachNode(pNewBevelBegin, NEXT);
02619     else
02620         pCopyBevel->AttachNode(pControl, LASTCHILD);
02621 
02622     // Karim 24/11/2000
02623     // The returned node will be blended, which means its bounds will be incorrect.
02624     // Therefore, invalidate them here and hope that somebody doesn't ask for them
02625     // before the blend happens.
02626     pControl->InvalidateBoundingRect(TRUE);
02627 
02628     return pControl;
02629 }
02630 
02631 void HandleOneToNoneAttributeRelationship (NodeRenderableInk* pBevel, CCAttrMap* pMap)
02632 {
02633     NodeAttribute* pAttr = NULL;
02634     
02635     pBevel->FindAppliedAttribute (CC_RUNTIME_CLASS (AttrTranspFillGeometry), &pAttr);
02636     NodeAttribute* pNew = (NodeAttribute*) pAttr->SimpleCopy ();
02637     delete (pMap->ReplaceAttribute (pNew));
02638 
02639 
02640     pBevel->FindAppliedAttribute (CC_RUNTIME_CLASS (AttrTranspFillMapping), &pAttr);
02641     pNew = (NodeAttribute*) pAttr->SimpleCopy ();
02642     delete (pMap->ReplaceAttribute (pNew));
02643 
02644     pBevel->FindAppliedAttribute (CC_RUNTIME_CLASS (AttrFillGeometry), &pAttr);
02645     pNew = (NodeAttribute*) pAttr->SimpleCopy ();
02646     delete (pMap->ReplaceAttribute (pNew));
02647 
02648     pBevel->FindAppliedAttribute (CC_RUNTIME_CLASS (AttrFillMapping), &pAttr);
02649     pNew = (NodeAttribute*) pAttr->SimpleCopy ();
02650     delete (pMap->ReplaceAttribute (pNew));
02651 }
02652 
02653 
02654 
02655 /********************************************************************************************
02656 
02657 >   void ApplyRelevantAttributes (CCAttrMap* ptrBevelMap, CCAttrMap* oldPathMap,
02658                                   CCAttrMap* newPathMap, CCAttrMap* pMap)
02659 
02660     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02661     Created:    9/10/2000
02662     Inputs:     ptrBevelMap - the attribute map for the intermediate NodeBevel
02663                 oldPathMap - the attribute map for the node that were replacing
02664                 newPathMap - the attribute map for the node that were replacing by
02665                 pMap - the resulting attribute map for the for the intermediate NodeBevel
02666                 pSingleMap - the resulting attribute map for the for the intermediate NodeBevel
02667                 (when blending from one to none)
02668     Outputs:    -
02669     Returns:    -
02670     Purpose:    Determins which attributes need to be applied to the bevel, so that we use
02671                 appropriate attributes.  For example, if bevel and old path both have the same
02672                 AttrFlatColourFill; then apply the one in newPathMap to the NodeBevel (via pMap).
02673     
02674 ********************************************************************************************/
02675 void NodeBevelController::ApplyRelevantAttributes (CCAttrMap* ptrBevelMap, CCAttrMap* oldPathMap,
02676                                                    CCAttrMap* newPathMap, CCAttrMap* pMap,
02677                                                    CCAttrMap* pSingleMap)
02678 {
02679     ERROR3IF(!ptrBevelMap,  "Bogus bevel!");
02680     ERROR3IF(!oldPathMap,   "Bogus old path attributes!");
02681     ERROR3IF(!newPathMap,   "Bogus newpath!");
02682     ERROR3IF(!(pMap || pSingleMap), "Bogus result attribute maps!");
02683 
02684     NodeAttribute* bevAttr = NULL, *oldPAttr = NULL, *newPAttr = NULL, *newAttr = NULL;
02685 
02686     BOOL done = FALSE;
02687     INT32 i = 0;
02688     CCRuntimeClass* pClass = NULL;  
02689 
02690     while (!done)
02691     {
02692         switch (i)
02693         {
02694             case 0:     pClass = CC_RUNTIME_CLASS (AttrFillGeometry);           break;
02695             case 1:     pClass = CC_RUNTIME_CLASS (AttrFillMapping);            break;
02696             case 2:     pClass = CC_RUNTIME_CLASS (AttrTranspFillGeometry);     break;
02697             case 3:     pClass = CC_RUNTIME_CLASS (AttrTranspFillMapping);      break;
02698             default:    done = TRUE;
02699         }
02700 
02701         if (!done)
02702         {
02703             ptrBevelMap->Lookup( pClass, (void*&) bevAttr);
02704             oldPathMap->Lookup( pClass, (void*&) oldPAttr);
02705 
02706             if (bevAttr && oldPAttr)
02707             {
02708                 if (*bevAttr == *oldPAttr)
02709                 {
02710                     // both the bevel and the original path have the same value for this attribute class
02711                     // we MUST generate the bevel with respect to the attribute in newPathMap (for this attribute class)
02712 
02713                     newPathMap->Lookup( pClass, (void*&) newPAttr);
02714                     
02715                     if (newPAttr)
02716                     {
02717                         newAttr = (NodeAttribute*) newPAttr->SimpleCopy ();
02718                         if (pMap)
02719                         {
02720                             delete (pMap->ReplaceAttribute (newAttr));
02721                         }
02722                         else if (pSingleMap)
02723                         {
02724                             delete (pSingleMap->ReplaceAttribute (newAttr));
02725                         }
02726                     }
02727                 }
02728             }
02729 
02730             i++;
02731 
02732             bevAttr = NULL, oldPAttr = NULL, newPAttr = NULL, newAttr = NULL;
02733         }
02734     }
02735 }
02736 
02737 
02738 
02739 /********************************************************************************************
02740 
02741 >   BOOL NodeBevelController::EndBlendStep(BlendNodeParam * pParam)
02742 
02743     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
02744     Created:    9/10/2000
02745     Inputs:     pParam  -   the blend node param (see NodeBlender) for the end of blending
02746     Outputs:    
02747     Returns:    TRUE for success, FALSE for failure
02748     Purpose:    Informs the node of the rendering of the blend step finished    
02749     
02750 ********************************************************************************************/
02751 BOOL NodeBevelController::EndBlendStep(BlendNodeParam * pParam)
02752 {
02753     // CGS:  firstly, are we just rendering, or are we making shapes?
02754 
02755     if (!pParam->GetHandleBecomeA ())
02756     {
02757         // were just rendering ....
02758 
02759 //      BOOL done = FALSE;
02760 
02761         BOOL okToRender = TRUE;
02762         
02763         // get hold of the current path processor ....
02764         if (pParam->GetPathProcessor ())
02765         {
02766             ERROR2IF(!pParam->GetPathProcessor ()->IS_KIND_OF(SumAllPathsPathProcessor),
02767                      FALSE, "First path processor isn't a sum all paths path processor");
02768 
02769             // grab the path processor
02770             SumAllPathsPathProcessor * pProc = (SumAllPathsPathProcessor *) pParam->GetPathProcessor ();
02771 
02772             pProc->SetEnabled(FALSE);       // disable it
02773 
02774             // we can now get on and generate intermediate blend steps.  NOTE:  this node contour
02775             // controller should not be touched at all - since it is held within the tree
02776             // so we make a copy that we can play with ....
02777                 
02778             Node* copy = NULL;
02779             this->NodeCopy (&copy);
02780 
02781             NodeBevelController* ptrCopy = (NodeBevelController*) copy;
02782 
02783             if (!ptrCopy) { return (FALSE); }       // out of memory
02784 
02785             // find the contour
02786             NodeBevel * pBevelCopy = (NodeBevel *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
02787             ERROR2IF(pBevelCopy == NULL, FALSE, "Can't find the contour node");
02788 
02789 #ifdef _DEBUG       // special debug info
02790 //          INT32 id = ++NodeBevelController::contourLastID;
02791 //          ptrCopy->myContourID = id;
02792 //          char strId [100];
02793 //          wsprintf (strId, "Pushing NodeBevelController ID:  %i\n", ptrCopy->myContourID);    
02794 //          TRACEUSER ("ChrisS", strId);
02795 #endif
02796 
02798 
02799             // now the fun begins, lest blend attributes for this particular blend step ....
02800             
02801             CCAttrMap * pMap = NULL;                        // if pEndControl is a contour as well
02802             CCAttrMap * pPrimaryBevelAttrMap = NULL;        // if pEndControl is not a contour
02803 
02804             // blend the contour's attributes
02805             NodeBevelController * pEndControl = (NodeBevelController *)FindAssociatedBlendNode(pParam);
02806 
02807             if (pEndControl)
02808             {
02809                 // we are blending two bevels YIPEE!        
02810                 
02811                 NodeBevel * pEndBevel = (NodeBevel *)pEndControl->FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
02812                 NodeRenderableInk * pOrigEndNode = pEndControl->GetBlendCreatedByNode();
02813 
02814                 // somethings a bit wrong if either of these fire ....
02815                 ERROR2IF(pOrigEndNode == NULL, FALSE, "Cant find the original contour node which this is based on");
02816                 ERROR2IF(!pOrigEndNode->IS_KIND_OF(NodeBevelController), FALSE, "Original node isn't a NodeBevelController");
02817 
02818                 pOrigEndNode = (NodeBevel *)pOrigEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
02819 
02820                 BlendPath MyStartPath;
02821                 MyStartPath.Initialise((NodeRenderableInk *)pBevelCopy->PublicCopy(), -1, pBevelCopy, 1, 0, NULL);
02822                 
02823                 BlendPath MyEndPath;
02824                 MyEndPath.Initialise((NodeRenderableInk *)pEndBevel->PublicCopy(), -1, pOrigEndNode, 1, 0, NULL);
02825                 
02826                 BlendNodeParam BNParam;
02827                 BNParam.Init(pParam, &MyStartPath, &MyEndPath);
02828                 
02829                 pMap = new CCAttrMap(30);
02830 
02831                 BlendHelpers * pHelp = new BlendHelpers;
02832                 ERRORIF(pHelp == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
02833                 
02834                 // now, perform the blend of the attributes
02835                 if (!pHelp->BlendAttributes(&BNParam, pMap))
02836                 {
02837                     pParam->GetRenderRegion()->PopPathProcessor();
02838                     okToRender = FALSE;
02839                 }
02840                 
02841                 delete pHelp;
02842             }
02843             else
02844             {
02845                 // were blending a bevelled node to a non-bevelled node.
02846                 // since we have not generated an attribute map, shadows that are generated
02847                 // render as white ....
02848 
02849                 // we need to make a attribute map for *this* NodeBevel, and apply it to
02850                 // the copy ....
02851 
02852                 // build an attribute map for the contour
02853                 
02854                 CCAttrMap* temp = CCAttrMap::MakeAppliedAttrMap(pBevelCopy);
02855                 
02856                 pPrimaryBevelAttrMap = temp->Copy ();       // MUST copy, cause those in temp point to attributes
02857                                                             // that will soon be deleted
02858 
02859                 if (temp) { delete temp; temp = NULL; }
02860             }
02861 
02862             // scan for the path that original contour was generated with ....
02863             NodePath* currentPath = (NodePath *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodePath));
02864             
02865             // in-order that we can make things work correctly, we MUST obtain attribute maps for
02866             // both the bevel and the path; and take a copy of both of these (so that we can still
02867             // use their attributes without them referencing attributes in our subtree that will be deleted).
02868             
02869             CCAttrMap* currentPathAttrMap = CCAttrMap::MakeAppliedAttrMap (currentPath);        // path
02870             CCAttrMap* currentBevelAttrMap = CCAttrMap::MakeAppliedAttrMap (pBevelCopy);    // bevel
02871             CCAttrMap* currentPathAttrMapC = currentPathAttrMap->Copy ();           // we MUST copy the attrmap!
02872             CCAttrMap* currentBevelAttrMapC = currentBevelAttrMap->Copy ();         // we MUST copy the attrmap!
02873 
02874             // NOTE:  don;t call DeleteAttributes () on either of the following!  If you do, then you will
02875             // delete things that MUST be deleted correctly later
02876             if (currentPathAttrMap)     {   delete currentPathAttrMap;  currentPathAttrMap = NULL; }
02877             if (currentBevelAttrMap)    {   delete currentBevelAttrMap; currentBevelAttrMap = NULL; }
02878             
02879             pBevelCopy->DeleteChildren (pBevelCopy->FindFirstChild ()); // our copy is unaffected by this
02880 
02881             // if we find one, we know get on and merge all the paths so that the contour can
02882             // be generated correctly.
02883 
02884             // we do this by:
02885 
02886             // replacing currentPath by a group that contains all paths on the path processors list
02887             // (i.e.  we replace it by the blended paths - who have their blended attribute maps
02888             // applied to them)
02889 
02890             if (currentPath)
02891             {
02892                 currentPath->CascadeDelete ();
02893                 delete (currentPath);
02894                 currentPath = NULL;
02895 
02896                 NodeGroup* newGroup = new NodeGroup ();
02897 
02898                 ListItem* pItem = pProc->GetList ()->GetHead ();
02899                 
02900                 Path* blendPath = NULL;
02901                 CCAttrMap* blendMap = NULL;
02902 
02903                 while (pItem)
02904                 {
02905                     blendPath = ((SumAllPathsElem *) pItem)->GetPath ();
02906                     blendMap = ((SumAllPathsElem *) pItem)->GetAttrMap ();
02907                     
02908                     NodePath* newPath = new NodePath ();
02909                     INT32 numCoords = blendPath->GetNumCoords ();
02910                     newPath->InkPath.ClearPath ();
02911                     newPath->InkPath.Initialise (numCoords);
02912                     newPath->InkPath.CopyPathDataFrom (blendPath);
02913                     ApplyRelevantAttributes (currentBevelAttrMapC, currentPathAttrMapC, blendMap, pMap, pPrimaryBevelAttrMap);
02914                     blendMap->ApplyAttributesToNode (newPath);
02915                     newPath->AttachNode(newGroup, LASTCHILD);
02916 
02917                     pItem = pProc->GetList ()->GetNext (pItem);
02918                     delete ((SumAllPathsElem *) pProc->GetList()->RemoveHead ());
02919                 }
02920 
02921                 if (ptrCopy->m_bOuter)      // an outer contour
02922                 {
02923                     newGroup->AttachNode(pBevelCopy, NEXT);
02924                 }
02925                 else if (!ptrCopy->m_bOuter)
02926                 {
02927                     newGroup->AttachNode(pBevelCopy, PREV);
02928                 }
02929             }
02930 
02931             // apply blended contour attributes ....
02932 
02933             if (pMap)
02934             {
02935                 pMap->ApplyAttributesToNode (pBevelCopy);
02936             }
02937 
02938             if (pPrimaryBevelAttrMap)
02939             {
02940                 pPrimaryBevelAttrMap->ApplyAttributesToNode (pBevelCopy);
02941             }
02942 
02943             // generate the contour (which will correctly be generated for merged paths if needed)
02944             
02945             if (!pBevelCopy->GenerateBevel ())
02946             {
02947                 okToRender = FALSE;
02948             }
02949 
02950             // now render the contour controller as a complete node (just like the document renders)
02951             
02952             if (okToRender)
02953             {
02954                 RenderRegion* pRegion = pParam->GetRenderRegion();
02955                 if (pRegion)
02956                     pRegion->RenderTreeNoCache(ptrCopy);
02957             }
02958 
02959             if (pMap)                   { pMap->DeleteAttributes ();                    delete pMap; }
02960             if (pPrimaryBevelAttrMap)   { pPrimaryBevelAttrMap->DeleteAttributes ();    delete pPrimaryBevelAttrMap; }
02961             if (currentPathAttrMapC)    { currentPathAttrMapC->DeleteAttributes ();     delete currentPathAttrMapC; }
02962             if (currentBevelAttrMapC)   { currentBevelAttrMapC->DeleteAttributes ();    delete currentBevelAttrMapC; }
02963 
02964             // if the contour is NOT shadowed, then we can safely delete the copy of it
02965 
02966             Node* parent = FindParent ();
02967 
02968             if (parent != NULL)
02969             {
02970                 if (!(IS_A (parent, NodeShadowController)))
02971                 {
02972                     ptrCopy->CascadeDelete ();
02973                     delete (ptrCopy);
02974                 }
02975                 else
02976                 {
02977                     SetShadowDeleteThisNode (ptrCopy);
02978                 }
02979             }
02980             else
02981             {
02982                 ptrCopy->CascadeDelete ();
02983                 delete (ptrCopy);
02984             }
02985         }
02986     }
02987     else    // were doing a make shapes (i.e.  become a .....)
02988     {   
02989         // get hold of the current path processor ....
02990         if (pParam->GetPathProcessor ())
02991         {
02992             ERROR2IF(!pParam->GetPathProcessor ()->IS_KIND_OF(SumAllPathsPathProcessor),
02993                      FALSE, "First path processor isn't a sum all paths path processor");
02994 
02995             // grab the path processor
02996             SumAllPathsPathProcessor * pProc = (SumAllPathsPathProcessor *) pParam->GetPathProcessor ();
02997 
02998             pProc->SetEnabled(FALSE);       // disable it
02999 
03000             // we can now get on and generate intermediate blend steps.  NOTE:  this node contour
03001             // controller should not be touched at all - since it is held within the tree
03002             // so we make a copy that we can play with ....
03003                 
03004             Node* copy = NULL;
03005             this->NodeCopy (&copy);
03006 
03007             NodeBevelController* ptrCopy = (NodeBevelController*) copy;
03008 
03009             if (!ptrCopy) { return (FALSE); }       // out of memory
03010 
03011             // find the contour
03012             NodeBevel * pBevelCopy = (NodeBevel *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
03013             ERROR2IF(pBevelCopy == NULL, FALSE, "Can't find the contour node");
03014 
03016 
03017             // now the fun begins, lest blend attributes for this particular blend step ....
03018             
03019             CCAttrMap * pMap = NULL;                        // if pEndControl is a contour as well
03020             CCAttrMap * pPrimaryBevelAttrMap = NULL;        // if pEndControl is not a contour
03021 
03022             // blend the contour's attributes
03023             NodeBevelController * pEndControl = (NodeBevelController *)FindAssociatedBlendNode(pParam);
03024 
03025             if (pEndControl)
03026             {
03027                 // we are blending two bevels YIPEE!        
03028                 
03029                 NodeBevel * pEndBevel = (NodeBevel *)pEndControl->FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
03030                 NodeRenderableInk * pOrigEndNode = pEndControl->GetBlendCreatedByNode();
03031 
03032                 // somethings a bit wrong if either of these fire ....
03033                 ERROR2IF(pOrigEndNode == NULL, FALSE, "Cant find the original contour node which this is based on");
03034                 ERROR2IF(!pOrigEndNode->IS_KIND_OF(NodeBevelController), FALSE, "Original node isn't a NodeBevelController");
03035 
03036                 pOrigEndNode = (NodeBevel *)pOrigEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeBevel));
03037 
03038                 BlendPath MyStartPath;
03039                 MyStartPath.Initialise((NodeRenderableInk *)pBevelCopy->PublicCopy(), -1, pBevelCopy, 1, 0, NULL);
03040                 
03041                 BlendPath MyEndPath;
03042                 MyEndPath.Initialise((NodeRenderableInk *)pEndBevel->PublicCopy(), -1, pOrigEndNode, 1, 0, NULL);
03043                 
03044                 BlendNodeParam BNParam;
03045                 BNParam.Init(pParam, &MyStartPath, &MyEndPath);
03046                 
03047                 pMap = new CCAttrMap(30);
03048 
03049                 BlendHelpers * pHelp = new BlendHelpers;
03050                 ERRORIF(pHelp == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
03051                 
03052                 // now, perform the blend of the attributes
03053                 if (!pHelp->BlendAttributes(&BNParam, pMap))
03054                 {
03055                     delete (pMap);
03056                     return (FALSE);
03057                 }
03058                 
03059                 delete pHelp;
03060             }
03061             else
03062             {
03063                 // were blending a bevelled node to a non-bevelled node.
03064                 // since we have not generated an attribute map, shadows that are generated
03065                 // render as white ....
03066 
03067                 // we need to make a attribute map for *this* NodeBevel, and apply it to
03068                 // the copy ....
03069 
03070                 CCAttrMap* temp = CCAttrMap::MakeAppliedAttrMap(pBevelCopy);
03071                 
03072                 pPrimaryBevelAttrMap = temp->Copy ();       // MUST copy, cause those in temp point to attributes
03073                                                             // that will soon be deleted
03074 
03075                 if (temp) { delete temp; temp = NULL; }
03076             }
03077 
03078             // scan for the path that original contour was generated with
03079             NodePath* currentPath = (NodePath *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodePath));
03080 
03081             // in-order that we can make things work correctly, we MUST obtain attribute maps for
03082             // both the bevel and the path; and take a copy of both of these (so that we can still
03083             // use their attributes without them referencing attributes in our subtree that will be deleted).
03084             
03085             CCAttrMap* currentPathAttrMap = CCAttrMap::MakeAppliedAttrMap (currentPath);    // path
03086             CCAttrMap* currentBevelAttrMap = CCAttrMap::MakeAppliedAttrMap (pBevelCopy);    // bevel
03087             CCAttrMap* currentPathAttrMapC = currentPathAttrMap->Copy ();           // we MUST copy the attrmap!
03088             CCAttrMap* currentBevelAttrMapC = currentBevelAttrMap->Copy ();         // we MUST copy the attrmap!
03089 
03090             // NOTE:  don;t call DeleteAttributes () on either of the following!  If you do, then you will
03091             // delete things that MUST be deleted correctly later
03092             if (currentPathAttrMap)     {   delete currentPathAttrMap;  currentPathAttrMap = NULL; }
03093             if (currentBevelAttrMap)    {   delete currentBevelAttrMap; currentBevelAttrMap = NULL; }
03094             
03095             pBevelCopy->DeleteChildren (pBevelCopy->FindFirstChild ()); // our copy is unaffected by this
03096 
03097             // if we find one, we know get on and merge all the paths so that the contour can
03098             // be generated correctly.
03099 
03100             // we do this by:
03101 
03102             // replacing currentPath by a group that contains all paths on the path processors list
03103             // (i.e.  we replace it by the blended paths - who have their blended attribute maps
03104             // applied to them)
03105 
03106             std::list<NodePath*> pathPtrs;              // a list of the new paths (so that we may call normaliseattributes)
03107 
03108             if (currentPath)
03109             {
03110                 currentPath->CascadeDelete ();
03111                 delete (currentPath);
03112                 currentPath = NULL;
03113 
03114                 NodeGroup* newGroup = new NodeGroup ();
03115 
03116                 ListItem* pItem = pProc->GetList ()->GetHead ();
03117                 
03118                 Path* blendPath = NULL;
03119                 CCAttrMap* blendMap = NULL;
03120 
03121                 while (pItem)
03122                 {
03123                     blendPath = ((SumAllPathsElem *) pItem)->GetPath ();
03124                     blendMap = ((SumAllPathsElem *) pItem)->GetAttrMap ();
03125                     
03126                     NodePath* newPath = new NodePath ();
03127                     INT32 numCoords = blendPath->GetNumCoords ();
03128                     newPath->InkPath.ClearPath ();
03129                     newPath->InkPath.Initialise (numCoords);
03130                     newPath->InkPath.CopyPathDataFrom (blendPath);
03131                     ApplyRelevantAttributes (currentBevelAttrMapC, currentPathAttrMapC, blendMap, pMap, pPrimaryBevelAttrMap);
03132                     blendMap->ApplyAttributesToNode (newPath);
03133                     newPath->AttachNode(newGroup, LASTCHILD);
03134 
03135                     pathPtrs.push_back (newPath);
03136                     
03137                     pItem = pProc->GetList ()->GetNext (pItem);
03138                     delete ((SumAllPathsElem *) pProc->GetList()->RemoveHead ());
03139                 }
03140 
03141                 if (ptrCopy->m_bOuter)      // an outer contour
03142                 {
03143                     newGroup->AttachNode(pBevelCopy, NEXT);
03144                 }
03145                 else if (!ptrCopy->m_bOuter)
03146                 {
03147                     newGroup->AttachNode(pBevelCopy, PREV);
03148                 }
03149             }
03150 
03151             // apply blended contour attributes ....
03152 
03153             if (pMap)
03154             {
03155                 pMap->ApplyAttributesToNode (pBevelCopy);
03156             }
03157 
03158             if (pPrimaryBevelAttrMap)
03159             {
03160                 pPrimaryBevelAttrMap->ApplyAttributesToNode (pBevelCopy);
03161             }
03162 
03163             // generate the contour (which will correctly be generated for merged paths if needed)
03164             
03165             pBevelCopy->GenerateBevel ();
03166 
03167             // now insert into the tree or passback ....
03168 
03169             BecomeA* accessPtr = pParam->GetHandleBecomeA ()->GetBecomeA ();
03170             UndoableOperation* pUndoOp = accessPtr->GetUndoOp ();
03171 
03172             Node* parent = NULL;
03173             NodeRenderableInk* pCreator = GetBlendCreatedByNode ();
03174             ERROR2IF(pCreator == NULL, FALSE, "Cant find the original bevel node which this is based on");
03175             
03176             parent = pCreator->FindParent (CC_RUNTIME_CLASS (NodeShadowController));
03177 
03178             // we MUST also scan to see if the blend was shadowed (and not the bevel) ....
03179             
03180             Node* pBlenderParent = NULL;
03181             Node* pBlender = pParam->GetNodeBlend ();
03182             ERROR2IF(pBlender == NULL, FALSE, "Cant find the original node blend which this node is based on!");
03183 
03184             pBlenderParent = pBlender->FindParent (CC_RUNTIME_CLASS (NodeShadowController));
03185 
03186             BOOL parentIsNodeShadow = FALSE;
03187 
03188             if (parent)
03189             {
03190                 if (!pBlenderParent)    // if the blend was NOT shadowed
03191                 {
03192                     parentIsNodeShadow = TRUE;
03193                 }
03194                 // else the blend was shadowed we need to insert into the tree
03195             }
03196 
03197             // are we wanting to be inserted into the tree?
03198             if (accessPtr->GetReason () == BECOMEA_REPLACE)
03199             {
03200                 if (!parentIsNodeShadow)
03201                 {
03202                     if (!pUndoOp->DoInsertNewNode (ptrCopy,pParam->GetHandleBecomeA ()->GetContextNode (),PREV,TRUE,FALSE,FALSE,TRUE))
03203                     {
03204                         return FALSE;
03205                     }
03206 
03207                     // now normalise all attributes ....
03208                     
03209                     pBevelCopy->NormaliseAttributes ();
03210 
03211                     std::list<NodePath*>::iterator i;
03212 
03213                     for (i = pathPtrs.begin (); i != pathPtrs.end (); i++)
03214                     {
03215                         (*i)->NormaliseAttributes ();
03216                     }
03217 
03218                     // if we are required to insert paths into the tree - THEN we MUST call DoBecomeA on
03219                     // ptrCopy.  This call now also creates the bevel bitmap for us
03220 
03221                     if (accessPtr->GetInsertComplexBlendStepsAsPaths ())
03222                     {
03223                         ptrCopy->DoBecomeA (accessPtr);
03224                     }
03225                 }
03226                 else
03227                 {
03228                     SetShadowThisNode (ptrCopy);    // we need to set this so that NodeShadowController
03229                                                     // can shadow ptrCopy
03230 
03231                     // um, NodeShadowController now needs to be responsible for NormaliseAttributes for
03232                     // the paths that are within our list.  Um I say, exactly how does one do this?
03233                 }
03234             }
03235             // or do we just want our paths?
03236             else if (accessPtr->GetReason () == BECOMEA_PASSBACK)
03237             {
03238                 if (parentIsNodeShadow)
03239                 {
03240                     SetShadowThisNode (ptrCopy);    // we need to set this so that NodeShadowController
03241                                                     // can shadow ptrCopy
03242                 }
03243                 ptrCopy->DoBecomeA (accessPtr);
03244                 ptrCopy->SetBlenderNode (pParam->GetNodeBlend ());
03245 
03246 #ifdef _DEBUG
03247 //              INT32 id = ++NodeBevelController::bevelLastBecomeAID;
03248 //              ptrCopy->myBevelBecomeAID = id;
03249 //              char strId [100];
03250 //              wsprintf (strId, "Pushing NodeBevelController PASSBACK ID:  %i\n", ptrCopy->myBevelBecomeAID);          
03251 //              TRACEUSER ("ChrisS", strId);
03252 #endif
03253 
03254                 // setup necessary stuff so that we can delete ptrCopy at the relevant time ....
03255                 
03256                 if (AllocatedBlendConsList (LT_BECOMEA_BEVELSLIST))
03257                 {   
03258                     BlendConsListInsert (LT_BECOMEA_BEVELSLIST, ptrCopy);
03259                 }
03260                 else
03261                 {
03262                     AllocBlendConsList (LT_BECOMEA_BEVELSLIST);
03263                     BlendConsListInsert (LT_BECOMEA_BEVELSLIST, ptrCopy);
03264                 }
03265             }   
03266 
03267             if (pMap)                   { pMap->DeleteAttributes ();                    delete pMap; }
03268             if (pPrimaryBevelAttrMap)   { pPrimaryBevelAttrMap->DeleteAttributes ();    delete pPrimaryBevelAttrMap; }
03269             if (currentPathAttrMapC)    { currentPathAttrMapC->DeleteAttributes ();     delete currentPathAttrMapC; }
03270             if (currentBevelAttrMapC)   { currentBevelAttrMapC->DeleteAttributes ();    delete currentBevelAttrMapC; }
03271         }
03272     }
03273     
03274     return TRUE;
03275 }
03276 
03277 
03278 
03279 /********************************************************************************************
03280 
03281 >   void NodeGroup::RenderTinyBlobs(RenderRegion* pRender)
03282 
03283     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03284     Created:    23/6/94
03285     Inputs:     pRender - The region to draw the blobs in
03286     Purpose:    Renders the tiny blobs for a group (A Single blob on the topmost child object
03287                 in the group)
03288 
03289 ********************************************************************************************/
03290 
03291 void NodeBevelController::RenderTinyBlobs(RenderRegion* pRegion)
03292 {
03293 #if !defined(EXCLUDE_FROM_RALPH)
03294     // get the topmost object in this group and tell it to render its tiny blobs.
03295     Node* pNode = FindLastChild();
03296     while (pNode != NULL && !pNode->IsAnObject())
03297         pNode = pNode->FindPrevious();
03298 
03299     // empty groups are not allowed!
03300     if (pNode == NULL)
03301     {
03302         ERROR3("NodeGroup::RenderTinyBlobs; This group is empty! Shouldn't be!");
03303         return;
03304     }
03305     else
03306     {
03307         ((NodeRenderableInk*)pNode)->RenderTinyBlobs(pRegion);
03308     }
03309 
03310 #endif
03311 }
03312 
03313 
03314 
03315 /****************************************************************************
03316 
03317 >   void NodeBevelController::GetDebugDetails( StringBase* Str )
03318 
03319     Author:     Gerry_Iles (Xara Group Ltd) <camelotdev@xara.com>
03320     Created:    07/07/2005
03321 
03322     Inputs:     Str         - pointer to a StringBase
03323     Purpose:    Builds debug info for camelot tree dialog
03324 
03325 ****************************************************************************/
03326 
03327 void NodeBevelController::GetDebugDetails( StringBase* Str )
03328 {
03329 #if DEBUG_TREE
03330     // Call base class
03331     NodeRenderableInk::GetDebugDetails( Str );
03332     
03333     String_256 TempStr;
03334     String_256 TempStr2;
03335         
03336     (*Str) += TEXT( "\r\nNodeBevelController Data Dump\r\n" );
03337 
03338     DocRect BlobRect = GetBlobBoundingRect();
03339     TempStr._MakeMsg( TEXT("Blob Bounding Rect :\r\n\t#1%ld,\t#2%ld\r\n\t#3%ld,\t#4%ld\r\n"),
03340                       BlobRect.lo.x, BlobRect.lo.y, BlobRect.hi.x, BlobRect.hi.y );
03341     (*Str) += TempStr;
03342 
03343     TempStr._MakeMsg( TEXT("Type :\t#1%d\r\n"), m_BevelType);
03344     (*Str) += TempStr;
03345 
03346 #endif
03347 }
03348 
03349 
03350 
03352 // BevelNodeTreeFactory implementation
03353 
03354 /********************************************************************************************
03355 
03356 >   BevelNodeTreeFactory::BevelNodeTreeFactory(NodeBevelController * pControl)
03357 
03358     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03359     Created:    10/8/99
03360     Inputs:     The controller node which to set up the values from
03361     Purpose:    Sets up the class factory so that it can recreate the bevel with the
03362                 same parameters as the controller node passed in
03363     SeeAlso:    
03364 
03365 ********************************************************************************************/
03366 BevelNodeTreeFactory::BevelNodeTreeFactory(NodeBevelController * pControl)
03367 {
03368     // first, get the bevel node
03369     NodeBevel * pBevel = pControl->GetBevelNode();
03370     
03371     m_BevelType = pBevel->m_BevelType;
03372     m_BevelIndent = pBevel->m_Indent;
03373     m_BevelContrast = pBevel->m_Contrast;
03374     m_BevelLightAngle = (INT32)pBevel->m_LightAngle;
03375 
03376     if (pBevel->m_bOuter)
03377     {
03378         m_BevelIndent = -m_BevelIndent;
03379     }
03380     
03381     /*NodeBevelBegin * pBevelBegin = */
03382         (NodeBevelBegin *)pControl->FindFirstChild(CC_RUNTIME_CLASS(NodeBevelBegin));
03383 
03384     // now, get the attribute maps for the bevel controller node and the bevel node
03385     // first, set up the attribute map
03386     CCAttrMap *pMap = CCAttrMap::MakeAppliedAttrMap((NodeRenderableInk *)pControl);
03387 
03388     CCAttrMap::iterator pos = pMap->GetStartPosition();
03389     CCAttrMap::iterator end = pMap->GetEndPosition();
03390 
03391     NodeAttribute      *pAttrNode = NULL;
03392     NodeListItem       *pItem = NULL;
03393     
03394     // run through the map, creating copies of the attributes and adding them to the list
03395     while( pos != end )
03396     {
03397         CCRuntimeClass *pKey;
03398         void           *pVal;
03399         pMap->GetNextAssoc(pos, pKey, pVal);
03400 
03401         pAttrNode = ((NodeAttribute *)pVal);
03402 
03403         pItem = new NodeListItem;
03404 
03405         if (!pItem)
03406         {
03407             ERROR3("Couldn't create list item");
03408             return;
03409         }
03410 
03411         pAttrNode->NodeCopy(&(pItem->pNode));
03412 
03413         m_ControllerAttributeList.AddTail(pItem);
03414     }
03415 
03416     delete pMap;
03417     pMap = NULL;
03418 
03419     // now, get the attribute maps for the bevel node
03420     pMap = CCAttrMap::MakeAppliedAttrMap((NodeRenderableInk *)pBevel);
03421 
03422     pos = pMap->GetStartPosition();
03423 
03424     pAttrNode = NULL;
03425     
03426     // run through the map, creating copies of the attributes and adding them to the list
03427     while( pos != end )
03428     {
03429         CCRuntimeClass *pKey;
03430         void           *pVal;
03431         pMap->GetNextAssoc( pos, pKey, pVal );
03432 
03433         pAttrNode = ((NodeAttribute *)pVal);
03434 
03435         pItem = new NodeListItem;
03436 
03437         if (!pItem)
03438         {
03439             ERROR3("Couldn't create list item");
03440             return;
03441         }
03442 
03443         pAttrNode->NodeCopy(&(pItem->pNode));
03444 
03445         m_BevelNodeAttributeList.AddTail(pItem);
03446     }
03447 
03448     delete pMap;
03449     pMap = NULL;
03450 
03451 }
03452 
03453 /********************************************************************************************
03454 
03455 >   BevelNodeTreeFactory::~BevelNodeTreeFactory()
03456 
03457     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03458     Created:    10/8/99
03459     Inputs:     
03460     Purpose:    Destructor
03461     SeeAlso:    
03462 
03463 ********************************************************************************************/
03464 BevelNodeTreeFactory::~BevelNodeTreeFactory()
03465 {
03466     NodeListItem * pItem = (NodeListItem *)m_ControllerAttributeList.GetHead();
03467 
03468     while (pItem)
03469     {
03470         delete pItem->pNode;
03471         pItem->pNode = NULL;
03472 
03473         pItem = (NodeListItem *)m_ControllerAttributeList.GetNext(pItem);
03474     }
03475 
03476     m_ControllerAttributeList.DeleteAll();
03477 
03478     pItem = (NodeListItem *)m_BevelNodeAttributeList.GetHead();
03479 
03480     while (pItem)
03481     {
03482         delete pItem->pNode;
03483         pItem->pNode = NULL;
03484 
03485         pItem = (NodeListItem *)m_BevelNodeAttributeList.GetNext(pItem);
03486     }
03487 }
03488 
03489 /********************************************************************************************
03490 
03491 >   NodeCompound *BevelNodeTreeFactory::CreateNode(List *pList, UndoableOperation * pOp = NULL)
03492 
03493     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03494     Created:    9/8/99
03495     Inputs:     The list of nodes to bevels, and an undoable operation (if necessary for 
03496                 undo to work)
03497     Returns:    The compound node created (NULL for failure)
03498     Purpose:    Given the list of nodes, bevels them with my parameters
03499 
03500 ********************************************************************************************/
03501 NodeCompound * BevelNodeTreeFactory::CreateNode(List *pList, UndoableOperation * pOp /* = NULL*/)
03502 {
03503     if (!pList)
03504         return NULL;
03505 
03506     if (pList->IsEmpty())
03507         return NULL;
03508 
03509     if (pOp == NULL)
03510         return NULL;
03511     
03512     NodeListItem * pItem = (NodeListItem *)pList->GetHead();
03513 
03514     NodeListItem * pAttrItem = NULL;
03515 
03516     NodeBevelController * pControl = NULL;
03517     NodeBevel           * pBevel   = NULL;
03518     NodeBevelBegin      * pBevelBegin= NULL;
03519 
03520     BOOL ok = TRUE;
03521 
03522     if (pOp)
03523     {
03524         ALLOC_WITH_FAIL(pControl, new NodeBevelController, pOp);
03525         ALLOC_WITH_FAIL(pBevel, new NodeBevel, pOp);
03526         ALLOC_WITH_FAIL(pBevelBegin, new NodeBevelBegin, pOp);
03527 
03528         // assign all the attributes to this controller node
03529         pAttrItem = (NodeListItem *)m_ControllerAttributeList.GetHead();
03530 
03531         while (pAttrItem)
03532         {
03533             if (((NodeAttribute *)pAttrItem->pNode)->CanBeAppliedToObject())
03534             {
03535                 pAttrItem->pNode->CopyNode(pControl, LASTCHILD);
03536             }
03537 
03538             pAttrItem = (NodeListItem *)m_ControllerAttributeList.GetNext(pAttrItem);
03539         }
03540 
03541         // insert the controller node first
03542         ok = pOp->DoInsertNewNode(pControl, pItem->pNode, NEXT, TRUE);
03543 
03544         // now, assign all the bevel node's attributes
03545         pAttrItem = (NodeListItem *)m_BevelNodeAttributeList.GetHead();
03546 
03547         while (pAttrItem)
03548         {
03549             if (((NodeAttribute *)pAttrItem->pNode)->CanBeAppliedToObject())
03550             {
03551                 pAttrItem->pNode->CopyNode(pBevel, LASTCHILD);
03552             }
03553 
03554             pAttrItem = (NodeListItem *)m_BevelNodeAttributeList.GetNext(pAttrItem);
03555         }
03556 
03557         // insert the bevel begin node next
03558         if (ok)
03559             ok = pOp->DoInsertNewNode(pBevelBegin, pControl, LASTCHILD, TRUE);      
03560 
03561         // move all nodes to being children of this node
03562         while (pItem && ok)
03563         {
03564             ok = pOp->DoMoveNode(pItem->pNode, pControl, LASTCHILD);
03565 
03566             pItem = (NodeListItem *)pList->GetNext(pItem);
03567         }
03568 
03569         // now, insert the bevel begin & end nodes
03570         if (ok)
03571             ok = pOp->DoInsertNewNode(pBevel, pControl, LASTCHILD, TRUE);
03572 
03573         if (ok)
03574         {
03575             ok = pOp->DoInvalidateNodeRegion(pControl, TRUE);
03576         }
03577 
03578         // now, remove & re-apply all the bevel attributes
03579         if (ok)
03580             ok = pOp->DoRemoveAttrTypeFromSubtree(pControl, CC_RUNTIME_CLASS(AttrBevelIndent));
03581 
03582         if (ok)
03583             ok = pOp->DoRemoveAttrTypeFromSubtree(pControl, CC_RUNTIME_CLASS(AttrBevelLightAngle));
03584 
03585         if (ok)
03586             ok = pOp->DoRemoveAttrTypeFromSubtree(pControl, CC_RUNTIME_CLASS(AttrBevelContrast));
03587 
03588         if (ok)
03589             ok = pOp->DoRemoveAttrTypeFromSubtree(pControl, CC_RUNTIME_CLASS(AttrBevelType));
03590 
03591         pBevel->m_BevelType = m_BevelType;
03592         pBevel->m_LightAngle = m_BevelLightAngle;
03593         pBevel->m_Contrast = m_BevelContrast;
03594         pBevel->m_Indent   = abs(m_BevelIndent);
03595 
03596         pControl->m_BevelType = m_BevelType;
03597         pControl->m_LightAngle = m_BevelLightAngle;
03598         pControl->m_Contrast = m_BevelContrast;
03599         pControl->m_Indent   = abs(m_BevelIndent);
03600 
03601         if (m_BevelIndent < 0)
03602         {
03603             pBevel->m_bOuter = TRUE;
03604             pControl->m_bOuter = TRUE;
03605         }
03606         else
03607         {
03608             pBevel->m_bOuter = FALSE;
03609             pControl->m_bOuter = FALSE;
03610         }
03611 
03612         // create the new attribute nodes
03613         AttrBevelIndent * pIndent = NULL;
03614         ALLOC_WITH_FAIL(pIndent, new AttrBevelIndent, pOp);
03615         pIndent->Value.m_Indent = m_BevelIndent;
03616 
03617         AttrBevelLightAngle * pLightAngle = NULL;
03618         ALLOC_WITH_FAIL(pLightAngle, new AttrBevelLightAngle, pOp);
03619         pLightAngle->Value.m_LightAngle = m_BevelLightAngle;
03620 
03621         AttrBevelContrast * pContrast = NULL;
03622         ALLOC_WITH_FAIL(pContrast, new AttrBevelContrast, pOp);
03623         pContrast->Value.m_Contrast = m_BevelContrast;
03624 
03625         AttrBevelType * pType = NULL;
03626         ALLOC_WITH_FAIL(pType, new AttrBevelType, pOp);
03627         pType->Value.m_Type = m_BevelType;
03628 
03629         // attach all attribute nodes to the controller node
03630         pContrast->AttachNode(pControl, FIRSTCHILD);
03631         pType->AttachNode(pControl, FIRSTCHILD);
03632         pLightAngle->AttachNode(pControl, FIRSTCHILD);
03633         pIndent->AttachNode(pControl, FIRSTCHILD);
03634 
03635         if (ok)
03636             ok = pOp->DoFactorOutCommonChildAttributes(pControl);
03637 
03638         if (ok)
03639             pControl->RegenerateNode(NULL, FALSE);
03640 
03641         if (ok)
03642             ok = pOp->DoInvalidateNodeRegion(pControl, TRUE);
03643 
03644     }
03645 
03646     // that's it !
03647     if (!ok)
03648         return NULL;
03649 
03650     return pControl;
03651 }
03652 
03653 #endif

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