nodecont.cpp

Go to the documentation of this file.
00001 // $Id: nodecont.cpp 1688 2006-08-10 12:05:20Z gerry $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 
00099 /*
00100 //*/
00101 
00102 #include "camtypes.h"
00103 #include "nodecont.h"
00104 #include "saveeps.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 
00113 // resource headers
00114 //#include "simon.h"
00115 //#include "mario.h"
00116 #include "transop.h"
00117 
00118 // Save/load
00119 //#include "cxfdefs.h"  // TAG_SHADOWCONTROLLER_SIZE - in camtypes.h [AUTOMATICALLY REMOVED]
00120 #include "cxftags.h"    // TAG_SHADOWCONTROLLER
00121 //#include "cxfrec.h"       // CXaraFileRecord - in camtypes.h [AUTOMATICALLY REMOVED]
00122 #include "rechshad.h"   // ShadowRecordHandler
00123 //#include "shadres.h"
00124 #include "opshadow.h"
00125 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00127 #include "attrbev.h"
00128 #include "lineattr.h"
00129 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00130 #include "attrappl.h"
00131 #include "blobs.h"
00132 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00133 #include "opgrad.h"
00134 #include "opbevel.h"
00135 #include "nbevcont.h"
00136 #include "moldtool.h"
00137 #include "attrmap.h"
00138 #include "nodetext.h"
00139 //#include "opcntr.h"
00140 #include "blndtool.h"
00141 #include "ncntrcnt.h"
00142 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00143 #include "extender.h"
00144 #include "ngcore.h"     // NameGallery, for stretching functionality
00145 #include "cmxrendr.h"
00146 //#include "cmxexdc.h"
00147 
00148 #include "nodemold.h"
00149 #include "nodecntr.h" // for ContourBecomeA
00150 #include "textops.h"
00151 
00152 #include "blendatt.h"
00153 #include "blndhelp.h"
00154 #include "nodebldr.h"
00155 #include "pathops.h"
00156 #include "brshattr.h"
00157 #include "fthrattr.h"
00158 #include "pathndge.h"
00159 //#include "mrhbits.h"
00160 #include "slicehelper.h"
00161 
00162 #include "pbecomea.h"
00163 #include "nodeshad.h"
00164 #include "nodeliveeffect.h"
00165 #include "ophist.h"
00166 #include "nodebev.h"
00167 
00168 CC_IMPLEMENT_DYNAMIC(NodeShadowController, NodeEffect)
00169 CC_IMPLEMENT_DYNAMIC(ShadowRecordHandler, CamelotRecordHandler)
00170 CC_IMPLEMENT_DYNAMIC(ShadowNodeTreeFactory, CompoundNodeTreeFactory)
00171  
00172 // Give this file in memory dumps
00173 // Declare smart memory handling in Debug builds
00174 #define new CAM_DEBUG_NEW
00175 
00176 // Karim 26/07/2000 uncomment this line for better-but-slower glow eor-drags.
00177 //#define GLOW_EOR_DRAG_PATHS
00178 
00179 // eor drag path flatness
00180 const MILLIPOINT GlowShadowDragPathFlatness = 2048;
00181 
00182 // code which uses 'default' shadow settings should really use these constants.
00183 const ShadowType    NodeShadowController::DefaultType           = SHADOWTYPE_WALL;  // Wall shadow.
00184 const MILLIPOINT    NodeShadowController::DefaultWallOffsetX    = 5 * 750;  // 5 pixels.
00185 const MILLIPOINT    NodeShadowController::DefaultWallOffsetY    = -5 * 750; // 5 pixels.
00186 const MILLIPOINT    NodeShadowController::DefaultGlowWidth      = 4 * 750;  // 4 pixels.
00187 const double        NodeShadowController::DefaultFloorAngle     = PI / 4.0; // 45 degrees clockwise.
00188 const float         NodeShadowController::DefaultFloorHeight    = 0.5;      // half shadowed object's height.
00189 const MILLIPOINT    NodeShadowController::DefaultBlurSize       = 6 * 750;  // 6 pixels.
00190 const double        NodeShadowController::DefaultDarkness       = 0.25;     // 25% darkness (75% transparent).
00191 const double        NodeShadowController::DefaultScale          = 1.0;      // actually unused - always set at 1.0 .
00192 const MILLIPOINT    NodeShadowController::DefaultFeatherWidth   = 2 * 750;  // 2 pixels.
00193 
00194 //MILLIPOINT            NodeShadowController::DefaultPixelsPerInch  = 0;        // Automatic pixels per inch for capturing/cacheing
00195 
00196 /*********************************************************************************************
00197 
00198 >    NodeShadowController::NodeShadowController() 
00199 
00200      Author:    Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00201      Created:   20/11/96
00202      Inputs:    -
00203      Outputs:   
00204      Returns:   -
00205               
00206      Purpose: This constructor creates a NodeShadowController 
00207             
00208      Errors:    -
00209 
00210 **********************************************************************************************/
00211  
00212 NodeShadowController::NodeShadowController() : NodeEffect()
00213 {
00214     // Initialise member vars.
00215     m_OffsetX   = DefaultWallOffsetX;
00216     m_OffsetY   = DefaultWallOffsetY;
00217     m_FloorShadowAngle  = DefaultFloorAngle;
00218     m_FloorShadowHeight = DefaultFloorHeight;
00219     m_GlowWidth         = DefaultGlowWidth;
00220     m_FeatherWidth      = DefaultFeatherWidth;
00221 
00222     SetPenumbraWidth(DefaultBlurSize);
00223     m_ShadowType    = DefaultType;
00224     m_Scale         = DefaultScale;
00225 
00226     m_ShadowSelectionChanged = FALSE;
00227     m_PreviousRect.MakeEmpty();
00228 
00229     m_GlowDragPath.Initialise();
00230     m_PerformedExtend = FALSE;
00231 
00232     m_strPostProID = POSTPRO_ID_SHADOW;
00233 
00234 #ifdef _DEBUG
00235     myShadowID = -1;
00236     myShadowBecomeAID = -1;
00237 #endif
00238 
00239     TRACEUSER( "MarkH", _T("Just Made a NodeShadowController Class!\n"));
00240 }
00241     
00242 NodeShadowController::~NodeShadowController()
00243 {
00244 #ifdef _DEBUG
00245 
00246     if (myShadowID > -1)
00247     {
00248         TCHAR           strId[100];
00249         camSnprintf( strId, 100, _T("Popping NodeShadowController ID:  %i\n"), myShadowID );
00250             
00251         TRACEUSER( "ChrisS", strId);
00252     }
00253 
00254 #endif
00255 }
00256 
00257 /***********************************************************************************************
00258 
00259 >   void NodeShadowController::NodeShadowController()
00260     (
00261         Node* ContextNode,  
00262         AttachNodeDirection Direction,  
00263         BOOL Locked = FALSE, 
00264         BOOL Mangled = FALSE,  
00265         BOOL Marked = FALSE, 
00266         BOOL Selected = FALSE, 
00267     )
00268 
00269     Author:  Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00270     Created: 20/11/96
00271     
00272     Inputs: ContextNode: Pointer to a node which this node is to be attached to.     
00273     
00274             Direction: 
00275             
00276                 Specifies the direction in which this node is to be attached to the 
00277                 ContextNode. The values this variable can take are as follows: 
00278                                   
00279                 PREV      : Attach node as a previous sibling of the context node
00280                 NEXT      : Attach node as a next sibling of the context node
00281                 FIRSTCHILD: Attach node as the first child of the context node
00282                 LASTCHILD : Attach node as a last child of the context node                               
00283                           
00284             The remaining inputs specify the status of the node: 
00285             
00286             Locked:     Is node locked ?
00287             Mangled:    Is node mangled ?
00288             Marked:     Is node marked ?
00289             Selected:   Is node selected ?
00290             
00291     Outputs:   -
00292     Returns:   - 
00293     Purpose: This method initialises the node and links it to ContextNode in the
00294              direction specified by Direction. All necessary tree links are
00295              updated.     
00296              
00297     Errors:  An assertion error will occur if ContextNode is NULL
00298 
00299 
00300 ***********************************************************************************************/
00301 
00302 NodeShadowController::NodeShadowController(Node* ContextNode,  
00303                      AttachNodeDirection Direction,  
00304                      BOOL Locked, 
00305                      BOOL Mangled,  
00306                      BOOL Marked, 
00307                      BOOL Selected   
00308                ) : NodeEffect(ContextNode, Direction, Locked, Mangled, Marked, 
00309                 Selected) 
00310 { 
00311     // Initialise member vars
00312     m_OffsetX           = DefaultWallOffsetX;
00313     m_OffsetY           = DefaultWallOffsetY;
00314     m_GlowWidth         = DefaultGlowWidth;
00315     m_FeatherWidth      = DefaultFeatherWidth;
00316 
00317     SetPenumbraWidth (4 * 750);             // 4 Pixels
00318     m_ShadowSelectionChanged = FALSE;
00319     m_PreviousRect.MakeEmpty();
00320     m_ShadowType = SHADOWTYPE_WALL;
00321     m_Scale     = 1.0;
00322 
00323     m_GlowDragPath.Initialise();
00324 
00325     m_strPostProID = POSTPRO_ID_SHADOW;
00326 
00327 #ifdef _DEBUG
00328     myShadowID = -1;
00329     myShadowBecomeAID = -1;
00330 #endif
00331 
00332     TRACEUSER( "MarkH", _T("Just Made a NodeShadowController Class!\n"));
00333 } 
00334 
00335 
00336 /********************************************************************************************
00337 
00338 >   virtual void NodeShadowController::RenderEorDrag( RenderRegion* pRegion )
00339 
00340     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00341     Created:    16/8/99
00342     Inputs:     pRegion - A Pointer to a RenderRegion to render to
00343     Purpose:    Renders the EOR drag for shadows
00344     SeeAlso:    NodeRenderableInk::RenderEorDrag
00345 
00346 ********************************************************************************************/
00347 void NodeShadowController::RenderEorDrag( RenderRegion* pRender )
00348 {
00349     // invoke the dragging code for the whole shadow - first, render all children
00350     // depth first
00351     // Shadows will render themselves
00352     RenderEorDragChildren(pRender);
00353 }
00354 
00355 
00356 
00357 /********************************************************************************************
00358 
00359 >   virtual BOOL NodeShadowController::AllowOp( ObjChangeParam *pParam,
00360                                                 BOOL SetOpPermissionState = TRUE,
00361                                                 BOOL DoPreTriggerEdit = TRUE)
00362 
00363     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>; Karim 20/01/2000
00364     Created:    20/11/96
00365     Inputs:     pParam                  describes the way an op wants to change the node
00366                 SetOpPermissionState    if TRUE the OpPermission of nodes should be set
00367                 DoPreTriggerEdit        if TRUE then NameGallery::PreTriggerEdit is called.
00368                                         *Must* be TRUE if the calling Op may make any nodes
00369                                         change their bounds, eg move, line width, cut.
00370                                         Use TRUE if unsure.
00371     Purpose:    
00372     SeeAlso:    
00373 
00374 ********************************************************************************************/
00375 BOOL NodeShadowController::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState,
00376                                                            BOOL DoPreTriggerEdit)
00377 {
00378     ERROR2IF(pParam == NULL, FALSE, "NodeShadowController::AllowOp(); NULL input(s) !!");
00379 
00380     // Flags whether or not we decide to allow the Op.
00381     BOOL allowed = TRUE;
00382 
00383     // Get a ptr to the changing op - it will be used in a few places.
00384     UndoableOperation* pChangeOp = pParam->GetOpPointer();
00385 
00386     if (pParam->GetDirection() != OBJCHANGE_CALLEDBYCHILD ||
00387         pParam->GetCallingChild() != NULL)
00388     {
00389         BOOL AnyAllowed = AllowOp_AccountForCompound( pParam,
00390                                                       SetOpPermissionState,
00391                                                       DoPreTriggerEdit );
00392 
00393         // If called by a parent, just pass this result back.
00394         if (pParam->GetDirection() == OBJCHANGE_CALLEDBYPARENT)
00395             return AnyAllowed;
00396     }
00397 
00398     // At this point we must have been called directly by the op or via a child AllowOp().
00399 
00400     // See whether our parent allows the Op.
00401     if (allowed && Parent != NULL)
00402     {
00403         ObjChangeDirection OldDirection = pParam->GetDirection();
00404         pParam->SetCallingChild(this);
00405         pParam->SetDirection(OBJCHANGE_CALLEDBYCHILD);
00406         allowed = Parent->AllowOp(pParam, SetOpPermissionState, DoPreTriggerEdit);
00407         pParam->SetDirection(OldDirection);
00408     }
00409 
00410     // ok, Shadows dislike certain types of Op.
00411     if (pChangeOp != NULL)
00412     {
00413         // no direct moulding, please.
00414         if (pChangeOp->IS_KIND_OF(OpCreateNewMould))
00415         {
00416             if (pParam->GetDirection() == OBJCHANGE_CALLEDBYOP)
00417                 allowed = FALSE;
00418         }
00419 
00420         // if asked to remove a blend, tell the user why we cannot.
00421         // we check SetOpPermissionState so that the user is allowed
00422         // to attempt the blend, instead of it being greyed out.
00423         else if (SetOpPermissionState)
00424         {
00425             if (pChangeOp->IS_KIND_OF(OpRemoveBlend))
00426             {
00427 				::InformWarning(_R(IDS_BLENDINSIDESHADOW));
00428                 allowed = FALSE;
00429             }
00430         }
00431     }
00432 
00433     else if (   pParam->GetChangeFlags().MultiReplaceNode && 
00434                 pParam->GetDirection() == OBJCHANGE_CALLEDBYOP  )
00435     {
00436         allowed = FALSE;
00437     }
00438 
00439     // if necessary, set permissions for OnChildChange.
00440     if (SetOpPermissionState)
00441         SetOpPermission(allowed ? PERMISSION_ALLOWED : PERMISSION_DENIED, TRUE);
00442 
00443     // If we're ok so far and were asked to do a PreTriggerEdit, then
00444     // determine whether the Op may change the bounds of some nodes.
00445     // If it may, then call NameGallery::PreTriggerEdit.
00446     if (allowed && DoPreTriggerEdit)
00447     {
00448         // if the Op is non-NULL then query its MayChangeNodeBounds() method.
00449         UndoableOperation* pChangeOp = pParam->GetOpPointer();
00450         if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds() && NameGallery::Instance())
00451         {
00452             allowed = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this);
00453         }
00454     }
00455 
00456     return allowed;
00457 }
00458 
00459 
00460 
00461 
00462 /********************************************************************************************
00463 
00464 >   void NodeShadowController::Transform( TransformBase& Trans )
00465 
00466     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00467     Created:    20/11/96
00468     Inputs:     Trans - A transformation object
00469     Purpose:    Transforms the group object and all its children.
00470 
00471 ********************************************************************************************/
00472 
00473 void NodeShadowController::Transform( TransformBase& Trans )
00474 {
00475 #ifndef NEW_SHADOW_RENDER
00476     TransformBase *pTrans = &Trans;
00477 
00478     Trans2DMatrix *p2D = NULL;
00479 
00480     if (pTrans->IS_KIND_OF(Trans2DMatrix))
00481     {
00482         p2D = (Trans2DMatrix *)pTrans;
00483         
00484         if (p2D->GetMatrix().IsTranslation())
00485         {
00486             // do nothing
00487             InvalidateBoundingRect();
00488 
00489             Trans.Transform(&BoundingRectangle.lo, 1);
00490             Trans.Transform(&BoundingRectangle.hi, 1);
00491             NodeEffect::Transform(Trans);
00492 
00493             // and transform the glow drag path if necessary
00494             if (m_ShadowType == SHADOWTYPE_GLOW)
00495             {
00496                 Trans.Transform(m_GlowDragPath.GetCoordArray(), m_GlowDragPath.GetNumCoords());
00497             }
00498 
00499             return;
00500         }
00501         else
00502         {
00503             Matrix pMat = p2D->GetMatrix();
00504             FIXED16 Elem[4];
00505             INT32 LElem[2];
00506 
00507             pMat.GetComponents(Elem, LElem);
00508 
00509             double ScaleX = Elem[0].MakeDouble();
00510             double ScaleY = Elem[3].MakeDouble();
00511             
00512             if (Elem[1].MakeDouble() == 0 && Elem[2].MakeDouble() == 0)
00513             {
00514                 m_OffsetX = (MILLIPOINT)(((double)m_OffsetX) * ScaleX);
00515                 m_OffsetY = (MILLIPOINT)(((double)m_OffsetY) * ScaleY);
00516 
00517                 if (ScaleX == ScaleY)
00518                 {
00519                     SetPenumbraWidth ((MILLIPOINT)(((double)m_PenumbraWidth) * fabs(ScaleX)));
00520                     m_GlowWidth = (MILLIPOINT)(((double)m_GlowWidth) * fabs(ScaleX));
00521                     m_FeatherWidth = (MILLIPOINT)(((double)m_FeatherWidth) * fabs(ScaleX));
00522                 }
00523             }
00524         }
00525     }
00526 
00527     // Delete the cached shadow infomation
00528 
00529     NodeShadow* pShadow = GetShadow();
00530     if (pShadow != NULL)
00531     {
00532         // Transform all the children
00533         NodeEffect::Transform(Trans);
00534 
00535         // now, reformat all text nodes under me
00536         List TextNodeList;
00537         NodeListItem * pItem = NULL;
00538                             
00539         BevelTools::GetAllNodesUnderNode(this, &TextNodeList, 
00540                     CC_RUNTIME_CLASS(TextStory));
00541 
00542         pItem = (NodeListItem *)TextNodeList.GetHead();
00543         
00544         while (pItem)
00545         {
00546             if (pItem->pNode)
00547             {
00548                 ((TextStory *)pItem->pNode)->FormatAndChildren(NULL, FALSE);
00549             }
00550             
00551             pItem = (NodeListItem *)TextNodeList.GetNext(pItem);
00552         }
00553         
00554         TextNodeList.DeleteAll();
00555 
00556         RegenerateNode(NULL, FALSE, FALSE);
00557     }   
00558 #else   // NEW_SHADOW_RENDER
00559 
00560     if (IsCapturingChildren())
00561     {
00562         CBitmapCache* pBitmapCache = Camelot.GetBitmapCache();
00563         ERROR3IF(pBitmapCache==NULL, "Can't find BitmapCache");
00564 
00565         // Translate the cached bitmaps
00566         CCachedBitmap cbmp;
00567         CBitmapCacheKey inky(this, GetPixelWidth(), 0);                     // Get cached BMP for this ORIGINAL node at our dpi
00568         BOOL bFound = pBitmapCache->Lookup(inky, cbmp);
00569         if (bFound)
00570         {
00571             cbmp.Transform(Trans);
00572             pBitmapCache->StoreBitmap(inky, cbmp);
00573         }
00574         else
00575             Trans.bHaveTransformedCached = FALSE;
00576     }
00577     else
00578     {
00579         // Old-style shadows need the transformed outlines of their objects during dragging
00580         // So we must ensure transformation...
00581         Trans.bTransformYourChildren = TRUE;
00582     }
00583 
00584     // Preserve current state of transformed cache flag
00585 //  BOOL bTransCache = Trans.bHaveTransformedCached;
00586 
00587     // Transform all the children and invalidate my bounds...
00588     NodeEffect::Transform(Trans);
00589 
00590 //  Trans.bHaveTransformedCached = bTransCache;
00591 
00592     if (Trans.IS_KIND_OF(Trans2DMatrix))
00593     {
00594         Trans2DMatrix* p2D = (Trans2DMatrix*)&Trans;
00595         
00596         if (Trans.IsTranslation())
00597         {
00598             // do nothing
00599             InvalidateBoundingRect();
00600 
00601             Trans.Transform(&BoundingRectangle.lo, 1);
00602             Trans.Transform(&BoundingRectangle.hi, 1);
00603 
00604             // and transform the glow drag path if necessary
00605             if (m_ShadowType == SHADOWTYPE_GLOW)
00606             {
00607                 Trans.Transform(m_GlowDragPath.GetCoordArray(), m_GlowDragPath.GetNumCoords());
00608             }
00609         }
00610         else
00611         {
00612             Matrix pMat = p2D->GetMatrix();
00613             FIXED16 Elem[4];
00614             INT32 LElem[2];
00615 
00616             pMat.GetComponents(Elem, LElem);
00617 
00618             double ScaleX = Elem[0].MakeDouble();
00619             double ScaleY = Elem[3].MakeDouble();
00620             
00621             if (Elem[1].MakeDouble() == 0 && Elem[2].MakeDouble() == 0)
00622             {
00623                 m_OffsetX = (MILLIPOINT)(((double)m_OffsetX) * ScaleX);
00624                 m_OffsetY = (MILLIPOINT)(((double)m_OffsetY) * ScaleY);
00625 
00626                 if (ScaleX == ScaleY)
00627                 {
00628                     SetPenumbraWidth ((MILLIPOINT)(((double)m_PenumbraWidth) * fabs(ScaleX)));
00629                     m_GlowWidth = (MILLIPOINT)(((double)m_GlowWidth) * fabs(ScaleX));
00630                     m_FeatherWidth = (MILLIPOINT)(((double)m_FeatherWidth) * fabs(ScaleX));
00631                 }
00632             }
00633         }
00634     }
00635 
00636     // Update the cached shadow information and reformat
00637     NodeShadow* pShadow = NULL;
00638     if (!Trans.IsTranslation())
00639     {
00640         // -------------------------------------------------
00641         // now, reformat all text nodes under me
00642         List TextNodeList;
00643         NodeListItem * pItem = NULL;
00644         BevelTools::GetAllNodesUnderNode(this, &TextNodeList, 
00645                     CC_RUNTIME_CLASS(TextStory));
00646 
00647         pItem = (NodeListItem *)TextNodeList.GetHead();
00648         
00649         while (pItem)
00650         {
00651             if (pItem->pNode)
00652                 ((TextStory *)pItem->pNode)->FormatAndChildren(NULL, FALSE);
00653             
00654             pItem = (NodeListItem *)TextNodeList.GetNext(pItem);
00655         }
00656         
00657         TextNodeList.DeleteAll();
00658 
00659         // -------------------------------------------------
00660         // Ensure shadow gets rebuilt
00661         if (!IsCapturingChildren())
00662             RegenerateNode(NULL, FALSE, FALSE);
00663     }
00664 
00665     if (IsCapturingChildren())
00666     {
00667         // Recreate shadows using the cached (transformed) bitmap
00668         CBitmapCache* pBitmapCache = Camelot.GetBitmapCache();
00669         if (this->IsTopShadowController())
00670         {
00671             // And we must remake all of our shadows whether we remade the basic bitmap
00672             // or not...
00673             pShadow = FindBottomShadow();
00674             BOOL bRelease = FALSE;
00675             while (pShadow)
00676             {
00677                 // First try to transform the shadow without regenerating
00678                 BOOL bOK = pShadow->TransformShadow(Trans);
00679 
00680                 // If that didn't work then we must regenerate (by simply releasing all cached data)
00681                 if (!bOK)
00682                 {
00683                     pShadow->DeleteCache();
00684                     bRelease = TRUE;            // Remains TRUE for all controllers to the top of the stack
00685                 }
00686 
00687                 if (bRelease)
00688                 {
00689                     NodeCompound* pController = pShadow->GetParentController();
00690                     if (pController)
00691                     {
00692                         // Can't call ReleaseCached because it will ignore the bSelf flag because it is being dragged
00693                         // So release the data directly... ARGH!!!!
00694                         // pController->ReleaseCached(FALSE, FALSE, TRUE, TRUE);    // Self and Derived data
00695                         if (pBitmapCache!=NULL)
00696                         {
00697                             CBitmapCacheKey inky(pController, 42);
00698                             pBitmapCache->RemoveAllOwnedBitmaps(inky, FALSE, CACHEPRIORITY_PERMANENT);
00699                         }
00700                     }
00701                 }
00702 
00703                 pShadow = FindShadowAbove(pShadow);
00704             }
00705         }
00706     }
00707 
00708     // If the current transform is a translation then we have successfully
00709     // transformed our cached info so don't allow our children to modify the state of
00710     // bHaveTransformedCached
00711 //  if (IsCapturingChildren())
00712 //  {
00713 //      if (bTransCache && Trans.IsTranslation())
00714 //          Trans.bHaveTransformedCached = TRUE;
00715 //      //else leave the flag alone!
00716 //
00717 //  }
00718 //  else
00719 //  {
00720 //      Trans.bHaveTransformedCached = FALSE;
00721 //  }
00722 #endif
00723 }
00724 
00725 
00726 
00727 
00728 /********************************************************************************************
00729 >   DocRect NodeShadowController::GetBlobBoundingRect()
00730 
00731     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00732     Created:    20/11/96
00733     Returns:    DocRect - The bounding rect of the group and its blobs
00734     Purpose:    Finds the bounding rect of the group along with its blobs. Its main use is
00735                 to determine which areas of the document to invalidate
00736 ********************************************************************************************/
00737 
00738 DocRect NodeShadowController::GetBlobBoundingRect()
00739 {
00740     DocRect dr = GetBoundingRect(TRUE, FALSE);
00741 
00742     dr = dr.Union(NodeRenderableInk::GetBlobBoundingRect());
00743 
00744     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00745 
00746 //  DocView * pView = DocView::GetCurrent();
00747 
00748     if (BlobMgr)
00749     {
00750         if (m_ShadowType == SHADOWTYPE_GLOW)
00751         {
00752             dr.Inflate(BlobMgr->GetBlobSize() * 3);
00753         }
00754         else
00755         {
00756             dr.Inflate(BlobMgr->GetBlobSize());
00757         }
00758     }
00759 
00760     return dr;
00761 
00762 
00763 }
00764 
00765 
00766 
00767 
00768 /********************************************************************************************
00769 
00770 >   virtual String NodeShadowController::Describe(BOOL Plural, BOOL Verbose = TRUE)
00771 
00772     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00773     Created:    20/11/96
00774     Inputs:     Plural: Flag indicating if the string description should be plural or
00775                         singular. 
00776     Outputs:    -
00777     Retuns:     Description of the group node 
00778     Purpose:    To return a description of the Group object in either the singular or the 
00779                 plural. This method is called by the DescribeRange method.
00780                 
00781                 The description will always begin with a lower case letter.   
00782                 
00783     Errors:     (Need to do this)
00784     SeeAlso:    -
00785 
00786 ********************************************************************************************/
00787 
00788 String NodeShadowController::Describe(BOOL Plural, BOOL Verbose)
00789 {
00790     if (Plural)
00791         return(String(_R(IDS_SHADOWNODENAME_PLURAL)));
00792     else
00793         return(String(_R(IDS_SHADOWNODENAME)));
00794 };
00795 
00796 
00797 
00798 
00799 /***********************************************************************************************
00800 > Node* NodeShadowController::SimpleCopy()  
00801 
00802     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00803     Created:    20/11/96
00804     Inputs:     -  
00805     Outputs:    
00806     Returns:    A copy of the node, or NULL if memory has run out 
00807          
00808     Purpose:    This method returns a shallow copy of the node with all Node pointers NULL. 
00809                 The function is virtual, and must be defined for all derived classes.  
00810                 
00811     Errors:     If memory runs out when trying to copy, then ERROR is called with an out of memory
00812                 error and the function returns NULL.                                                                      
00813                                                                                  
00814 **********************************************************************************************/
00815 
00816 Node* NodeShadowController::SimpleCopy()
00817 {
00818     NodeShadowController* NodeCopy; 
00819     NodeCopy = new NodeShadowController();
00820     ERRORIF(NodeCopy == NULL, _R(IDE_NOMORE_MEMORY), NULL); 
00821 
00822     CopyNodeContents(NodeCopy);
00823 
00824     return (NodeCopy);
00825 }   
00826 
00827 
00828 
00829 
00830 /********************************************************************************************
00831 >   void NodeShadowController::CopyNodeContents(NodeShadowController* pNewNode)
00832 
00833     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00834     Created:    05/01/2004
00835     Inputs:     -
00836     Returns:    
00837     Purpose:    Sort of backwards copy constructor
00838 
00839 ********************************************************************************************/
00840 void NodeShadowController::CopyNodeContents(NodeShadowController* pNewNode)
00841 {
00842     pNewNode->m_ShadowSelectionChanged = m_ShadowSelectionChanged;
00843 
00844     pNewNode->m_sShadowName = m_sShadowName;
00845 
00846     pNewNode->m_PreviousRect = m_PreviousRect;
00847 
00848     pNewNode->m_PerformedExtend = m_PerformedExtend;
00849 
00850     // Remember to copy or blank cached path data without leaking memory...
00851     pNewNode->m_GlowDragPath.Initialise(m_GlowDragPath.GetNumCoords());
00852     pNewNode->m_GlowDragPath.CopyPathDataFrom(&m_GlowDragPath);
00853 
00854     pNewNode->SetOffsetX(m_OffsetX);
00855     pNewNode->SetOffsetY(m_OffsetY);
00856 
00857     pNewNode->SetPenumbraWidth(m_PenumbraWidth);
00858 
00859     pNewNode->SetFloorShadowAngle(m_FloorShadowAngle);
00860     pNewNode->SetFloorShadowHeight(m_FloorShadowHeight);
00861     pNewNode->SetShadowType(m_ShadowType);
00862     pNewNode->SetWallShadowScale(m_Scale);
00863     pNewNode->SetGlowWidth(m_GlowWidth);
00864     pNewNode->SetFeatherWidth(m_FeatherWidth);
00865 
00866     // These member assignments shouldn't be here! They are members of NodeCompound!
00867     pNewNode->m_bBlendStartNode = m_bBlendStartNode;
00868     pNewNode->m_bBlendEndNode   = m_bBlendEndNode;
00869     pNewNode->m_pBlendCreatedByNode = m_pBlendCreatedByNode;    
00870 
00871     NodeEffect::CopyNodeContents(pNewNode);
00872 
00873 #ifdef NEW_SHADOW_RENDER
00874     if (IsCapturingChildren())
00875     {
00876         CopyCached(pNewNode, GetPixelWidth(), 0);   // Copy cached bitmaps, options 0 and 1 (original and processed)
00877     }
00878 #endif
00879 }
00880 
00881 
00882 
00883 
00884 /***********************************************************************************************
00885 >   void NodeShadowController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00886 
00887     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00888     Created:    18/12/2003
00889     Outputs:    -
00890     Purpose:    Polymorphically copies the contents of this node to another
00891     Errors:     An assertion failure will occur if NodeCopy is NULL
00892     Scope:      protected
00893                                      
00894 ***********************************************************************************************/
00895 
00896 void NodeShadowController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
00897 {
00898     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
00899     ENSURE(IS_A(pNodeCopy, NodeShadowController), "PolyCopyNodeContents given wrong dest node type");
00900 
00901     if (IS_A(pNodeCopy, NodeShadowController))
00902         CopyNodeContents((NodeShadowController*)pNodeCopy);
00903 }
00904 
00905 
00906 
00907    
00908 /********************************************************************************************
00909 
00910 >   virtual UINT32 NodeShadowController::GetNodeSize() const
00911 
00912     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00913     Created:    20/11/96
00914     Inputs:     -
00915     Outputs:    -
00916     Returns:    The size of the node in bytes
00917     Purpose:    For finding the size of the node 
00918                 
00919     SeeAlso:    Node::GetSubtreeSize
00920 
00921 ********************************************************************************************/
00922 
00923 UINT32 NodeShadowController::GetNodeSize() const 
00924 {     
00925     return (sizeof(NodeShadowController)); 
00926 }  
00927 
00928 
00929 /********************************************************************************************
00930 
00931 >   virtual BOOL NodeShadowController::CanBecomeA(BecomeA* pBecomeA) 
00932 
00933 
00934     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00935     Created:    20/11/96
00936     Inputs:     InkClass: The class of object
00937                 pNumObjects = ptr to place number of objects of type pClass that will be created (Note: can be NULL).
00938                               *pNumObects in undefined on entry
00939     Outputs:    -
00940     Returns:    TRUE if the node, or any of its children can transmogrify themselves to become 
00941                 an InkClass object
00942 
00943     Purpose:    This function is used by the convert to shapes operation. It determines if 
00944                 the node or any of its children can convert themselves into an InkClass object. 
00945 
00946                 The number you put into pNumObjects (if it's not NULL) should exactly equal the total number
00947                 of pClass objects you create.  It should NOT contain any additional objects you may produce
00948                 such as group objects for containing the pClass object, or attributes.
00949 
00950                 Also, the entry value of *pNumObjects cannot be assumed to be 0.
00951 
00952     Errors:     -
00953 
00954 ********************************************************************************************/
00955 
00956 BOOL NodeShadowController::CanBecomeA(BecomeA* pBecomeA)
00957 {
00958     BOOL bOK=FALSE;
00959 
00960     Node* Current = FindFirstChild(); 
00961     while (Current != NULL)
00962     {
00963         if (Current->CanBecomeA(pBecomeA))              // Increments count
00964         {
00965             bOK = TRUE;
00966             if (!pBecomeA->IsCounting())
00967                 return TRUE;                            // Don't want total, so return now
00968         }
00969 
00970         Current = Current->FindNext();
00971     }
00972 
00973     return bOK;
00974 }
00975 
00976 
00977 /********************************************************************************************
00978 
00979 >   virtual BOOL NodeShadowController::DoBecomeA(BecomeA* pBecomeA)
00980 
00981     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
00982     Created:    20/11/96
00983     Inputs:     pBecomeA =  ptr to a class that contains all the info needed to become a new
00984                             type of node.
00985     Outputs:    -
00986     Returns:    TRUE if the object has been transformed, FALSE if we run out of memory
00987 
00988     Purpose:    Transforms the object into another type of object. 
00989                 Note: changed 7/10/94 by MarkN to take the pBecomeA param, so that more data could be passed
00990                 to these functions in the future without causing massive rebuilds due to the editing of
00991                 node.h
00992 
00993                 14/6/95 (Markn): This now localises attributes before calling it's children, then factors them out
00994                 at the end.  This is so that common attrs trickle down to ALL leaf nodes, no matter how deep 
00995                 they may be.  This is important when a child node is a blend, because the action of DoBecomeA()
00996                 in the blend's case creates new child attrs for the path it creates, hence making any common
00997                 attrs illegal.
00998     Errors:     -
00999     SeeAlso:    Node::CanBecomeA
01000 
01001 ********************************************************************************************/
01002 
01003 BOOL NodeShadowController::DoBecomeA(BecomeA* pBecomeA)
01004 {
01005     ERROR2IF(pBecomeA == NULL,FALSE,"NULL BecomeA ptr");
01006 
01007 //  NodeGroup * pGroup = NULL;
01008     Node * pNode = NULL;
01009 //  Node * pNextNode = NULL;
01010 //  NodeHidden * pHidden = NULL;
01011 
01012     UndoableOperation* pOp = pBecomeA->GetUndoOp();
01013 
01014     // New behaviour: Leave shadow in the tree if the BecomeA claims
01015     // it will leave the result node in position in the tree
01016     if (pBecomeA->ResultsStayInPlace() && pBecomeA->IsCombineBecomeA())
01017     {
01018         return NodeEffect::DoBecomeA(pBecomeA);
01019     }
01020 
01021     switch (pBecomeA->GetReason())
01022     {
01023     case BECOMEA_REPLACE:
01024         {
01025 //          ERROR3IF(pOp == NULL, "No operation");
01026 
01027             // if any of our children are selected, then we'll have to remember to select
01028             // the group created in our place.
01029             BOOL fSelectBecomeAGroup = IsParentOfSelected();
01030 
01031             // call NodeGroup's implementation first, to convert children to paths.
01032             NodeCompound::DoBecomeA(pBecomeA);
01033 
01034             // now turn into a group.
01035             NodeGroup* pGroup = BecomeAGroup(pOp);
01036 
01037             // select the group but don't bother redrawing blobs an' suchlike.
01038             if (fSelectBecomeAGroup && pGroup != NULL)
01039                 pGroup->Select(FALSE);
01040         }
01041         break;
01042 
01043         case BECOMEA_PASSBACK:
01044         {
01045             // special behaviour for blends.
01046             if (pBecomeA->IsBlendBecomeA())
01047             {
01048                 CompoundNodeBlendBecomeA MyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath),
01049                     NULL, FALSE, this, pBecomeA);
01050                 
01051                 // now that the become a is set up call all my sub nodes with this
01052                 Node * pChild = FindFirstChild();
01053                 
01054                 while (pChild)
01055                 {
01056                     if (pChild->IsAnObject() && !pChild->NeedsParent(NULL))
01057                     {
01058                         MyBecomeA.ResetCount();
01059                         
01060                         if (pChild->CanBecomeA(&MyBecomeA))
01061                         {
01062                             // tell the become a that we're starting a block
01063                             MyBecomeA.SetNumPathNodes(MyBecomeA.GetCount());
01064                             MyBecomeA.ResetCount();
01065                             
01066                             pChild->DoBecomeA(&MyBecomeA);
01067                         }
01068                     }
01069                     
01070                     pChild = pChild->FindNext();
01071                 }
01072             }
01073 
01074             // if we're doing shadow silhouettes, then make a silhouette from our shadowed
01075             // node to pass back, then apply the shadow transform to that silhouette and
01076             // pass it back again, for the shadowed node.
01077             // OPTIMISATION: glow shadows enclose the shadowed object,
01078             // so in that case, we needn't return its path.
01079             else if (pBecomeA->DoShadowSilhouettes())
01080             {
01081                 BOOL bGlowOptimisations = (GetShadowType() == SHADOWTYPE_GLOW);
01082 
01083                 NodePath* pShadowPath   = NULL;
01084                 NodePath* pInkPath      = NULL;
01085                 NodeRenderableInk* pShadowedNode = GetShadowedNode();
01086 
01087                 Path WorkPath;
01088                 WorkPath.Initialise();
01089                 PathBecomeA baInfo(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL, FALSE,
01090                                         &WorkPath, PathBecomeA::ADD_OUTLINES);
01091 
01092                 BOOL    ok = pShadowedNode->DoBecomeA(&baInfo);
01093                 if (ok) ok = NodePath::CreateFromPath(&pShadowPath, &WorkPath, pBecomeA->GetUndoOp());
01094 
01095                 if (ok && !bGlowOptimisations)
01096                         ok = pShadowPath->NodeCopy((Node**)&pInkPath);
01097 
01098                 if (ok) ok = ApplyShadowTransform(&pShadowPath->InkPath);
01099                 if (ok) ok = pShadowPath->DoBecomeA(pBecomeA);
01100 
01101                 if (ok && !bGlowOptimisations)
01102                         ok = pInkPath->DoBecomeA(pBecomeA);
01103 
01104                 if (pShadowPath != NULL)
01105                 {
01106                     delete pShadowPath;
01107                     pShadowPath = NULL;
01108                 }
01109                 if (pInkPath != NULL)
01110                 {
01111                     delete pInkPath;
01112                     pInkPath = NULL;
01113                 }
01114 
01115                 return ok;
01116             }
01117 
01118             else
01119             {
01120                 // Sequentially ask the children of the blend to DoBecomeA
01121                 // This is all that's required because the child objects are only passing back
01122                 // the new node type, and NOT replacing themselves in the tree
01123                 // This also ensures the receipient gets the list of paths in render order
01124                 pNode = FindFirstChild();
01125                 while (pNode != NULL)
01126                 {
01127                     if (!pNode->DoBecomeA(pBecomeA))
01128                         return FALSE;
01129                     
01130                     pNode = pNode->FindNext();
01131                 }
01132             }
01133         }
01134         break;
01135         default: break;
01136     }
01137 
01138     return TRUE;
01139 
01140 }
01141 
01142 
01143 
01144 /********************************************************************************************
01145 
01146 >   BOOL NodeShadowController::ApplyShadowTransform(Path* pTargetPath)
01147 
01148     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01149     Created:    30/10/2000
01150     Inputs:     pTargetPath     the path which will be transformed.
01151 
01152     Outputs:    if successful, pTargetPath will be changed, to represent the result of
01153                 shadowing pTargetPath.
01154 
01155     Returns:    TRUE if successful, FALSE otherwise.
01156 
01157     Purpose:    Apply the shadow transformation to the given Path.
01158                 The resultant path does *not* currently take penumbra width into account.
01159 
01160     Errors:     ERROR2 if pTargetPath is NULL.
01161 
01162 ********************************************************************************************/
01163 BOOL NodeShadowController::ApplyShadowTransform(Path* pTargetPath)
01164 {
01165     ERROR2IF(pTargetPath == NULL, FALSE,
01166             "NodeShadowController::ApplyShadowTransform; NULL param!");
01167 
01168     switch (GetShadowType())
01169     {
01170     // translate pTargetPath by the wall offset.
01171     case SHADOWTYPE_WALL:
01172         pTargetPath->Translate(GetWallShadowOffset());
01173         break;
01174 
01175     // contour pTargetPath outwards by the glow width.
01176     // for some reason, the contour code expects twice the width,
01177     // as if it thinks it's applying a line width...
01178     case SHADOWTYPE_GLOW:
01179     {
01180         Path ContouredPath;
01181         ContouredPath.Initialise();
01182         pTargetPath->InitializeContourValues(2 * GetGlowWidth());
01183         if (pTargetPath->GetContourForStep(&ContouredPath) > 0)
01184         {
01185             pTargetPath->ClearPath();
01186             pTargetPath->CloneFrom(ContouredPath);
01187         }
01188 
01189         break;
01190     }
01191 
01192     // shear pTargetPath and reduce its height.
01193     case SHADOWTYPE_FLOOR:
01194     {
01195         DocRect InkRect;
01196         pTargetPath->GetTrueBoundingRect(&InkRect);
01197 
01198         DocCoord dcOffset(-InkRect.Centre().x, -InkRect.lo.y);
01199 
01200         Matrix ToOrigin = Matrix::CreateTransMatrix(dcOffset);
01201         Matrix FromOrigin = Matrix::CreateTransMatrix(-dcOffset);
01202         Matrix ScaleMat = Matrix::CreateScaleMatrix(1.0, GetFloorShadowHeight());
01203         Matrix ShearMat = Matrix::CreateShearMatrix(GetFloorShadowAngle());
01204 
01205         Matrix FloorTransform(ToOrigin * ScaleMat * ShearMat * FromOrigin);
01206 
01207         FloorTransform.transform(pTargetPath->GetCoordArray(), pTargetPath->GetNumCoords());
01208 
01209         break;
01210     }
01211 
01212     case SHADOWTYPE_FEATHER:
01213     {
01214         // No transform for feather? (Yet)
01215         break;
01216     }
01217 
01218     default:
01219         ERROR3("NodeShadowController::ApplyShadowTransform; Unrecognised shadow type!");
01220         return FALSE;
01221     }
01222 
01223     return TRUE;
01224 }
01225 
01226 
01227 
01228 /********************************************************************************************
01229 
01230 >   virtual void NodeShadowController::SelectInRect(const DocRect& Rect, SelStateAction st)
01231 
01232     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01233     Created:    14 April 2000
01234     Inputs:     Rect    const reference to the bounding rect to use.
01235                 st      the selection state, ie SET/TOGGLE/CLEAR.
01236     Outputs:    This node may have its selection status changed, depending on the inputs.
01237 
01238     Purpose:    Helper method for the static fn SelectAllInRect() used by marquee select.
01239                 This method changes the selection state of this node according to the given
01240                 bounding rectangle and sel-state action. Most nodes will want to use the
01241                 default behaviour, which is to select themselves if their bounds lie within
01242                 the given bounding rectangle. If you want to do something special with the
01243                 marquee select, then override this method.
01244 
01245     Notes:      This method is meant to be called solely from SelectAllInRect() - if you wish
01246                 to call it from elsewhere, it *may* work as you expect, but it is a good idea
01247                 that you check your assumptions on what preconditions are necessary.
01248 
01249     Errors:     ERROR3 in DEBUG if st holds an invalid state.
01250     See also:   SelectAllInRect().
01251 
01252 ********************************************************************************************/
01253 void NodeShadowController::SelectInRect(const DocRect& Rect, SelStateAction st)
01254 {
01255     // try to select each of our NodeRenderableInk children.
01256     NodeRenderableInk* pInkChild = NULL;
01257     Node* pChild = FindFirstChild();
01258     while (pChild != NULL)
01259     {
01260         if (pChild->IsAnObject())
01261         {
01262             pInkChild = (NodeRenderableInk*)pChild;
01263             if (Rect.ContainsRect(pInkChild->GetBoundingRect()))
01264             {
01265                 switch (st)
01266                 {
01267                 case CLEAR:
01268                     if (pInkChild->MarqueeSelectNode())
01269                     {
01270                         pInkChild->DeSelect(TRUE);
01271                     }
01272                     break;
01273 
01274                 case SET:
01275                     if (pInkChild->MarqueeSelectNode())
01276                     {
01277                         pInkChild->Select(TRUE);
01278                     }
01279                     break;
01280 
01281                 case TOGGLE:
01282                     if (pInkChild->MarqueeSelectNode())
01283                     {
01284                         if (pInkChild->IsSelected())
01285                             pInkChild->DeSelect(TRUE);
01286                         else
01287                             pInkChild->Select(TRUE);
01288                     }
01289                     break;
01290 
01291                 default:
01292                     ERROR3("NodeShadowController::SelectInRect; Unknown SelStateAction!\n");
01293                     return;
01294                 }
01295             }
01296         }
01297         pChild = pChild->FindNext();
01298     }
01299 }
01300 
01301 
01302 
01303 /********************************************************************************************
01304 
01305 >   BOOL NodeShadowController::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
01306                              Spread* pSpread )
01307 
01308     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
01309     Created:    20/11/96
01310     Inputs:     PointerPos - The Location of the mouse pointer at the time of the click
01311                 Click - The type of click received (single, double, drag etc)
01312                 ClickMods - The modifiers to the click (eg shift, control etc )
01313     Returns:    TRUE if the node claims the click as its own and FALSE if it is
01314                 not interested in the click
01315     Purpose:    Determines if the user has started a drag on one of the groups blobs.
01316                 If they have then it starts the groups resize operation
01317         
01318 ********************************************************************************************/
01319 
01320 BOOL NodeShadowController::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
01321                         Spread* pSpread )
01322 {
01323     // we did not use the click, so let someone else try
01324     return FALSE;
01325 }
01326 
01327 
01328 
01329 
01330 /********************************************************************************************
01331 
01332 >   virtual ChangeCode NodeShadowController::OnChildChange(ObjChangeParam* pParam)
01333 
01334     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
01335     Created:    20/11/96
01336     Inputs:     pParam  = pointer to a object change parameter class
01337     Returns:    CC_OK       if we have successfully processed the change.
01338                 CC_FAIL     if we cannot handle this particular change and must prevent the
01339                             child from continuing
01340     Purpose:    This function should be overridden in derived object classes.
01341                 Composite objects can use this function to respond to one of their children
01342                 undergoing a change. They should return CC_FAIL whenever they are unable to
01343                 cope with the change.
01344 
01345                 Groups will hide themselves if the op has left it in a state where it has no bounds,
01346                 i.e. all it's node renderable bounded children are gone.
01347 
01348     SeeAlso:    WarnParentOfChange(),AllowOp();
01349 
01350 ********************************************************************************************/
01351 ChangeCode NodeShadowController::OnChildChange(ObjChangeParam* pParam)
01352 {
01353     if (m_PerformedExtend)
01354     {
01355         // this means that we have done an extend
01356         // which added the regen action into the action list
01357         // so no need to regen the node
01358         // just reset the flag and leave (sjk 4/8/2000)
01359         m_PerformedExtend = FALSE;
01360         return CC_OK;
01361     }
01362 
01363     if (!pParam)
01364     {
01365         return NodeEffect::OnChildChange(pParam);
01366     }
01367 
01368     // get the op out of the parameter
01369     if (!pParam->GetOpPointer())
01370     {
01371         TRACEUSER( "DavidM", _T("OnChildChange - no op\n"));
01372         // force a redraw of me
01373 
01374         Document * pDoc = Document::GetCurrent();
01375 
01376         if (pParam->GetChangeFlags().RegenerateNode && pParam->GetChangeType() == OBJCHANGE_FINISHED)
01377         {
01378             RegenerateNode(NULL, FALSE, FALSE);
01379         }
01380             
01381         pDoc->ForceRedraw(FindParentSpread(), GetBoundingRect(), FALSE, this);
01382 
01383         return NodeEffect::OnChildChange(pParam);
01384     }
01385 
01386     if (pParam->GetChangeType() != OBJCHANGE_FINISHED)
01387         return NodeEffect::OnChildChange(pParam);
01388 
01389     UndoableOperation * pUndoOp = NULL;
01390 
01391     if (pParam->GetOpPointer()->IS_KIND_OF(UndoableOperation))
01392     {
01393         pUndoOp = pParam->GetOpPointer();
01394     }
01395 
01396     if (!pUndoOp)
01397     {
01398         return NodeEffect::OnChildChange(pParam);
01399     }
01400 
01401     NodeAttribute * pAttrib = NULL;
01402     NodeListItem  * pItem   = NULL;
01403 
01404     // check for the op being a transparent attribute application
01405     if (pUndoOp->IS_KIND_OF(OpApplyAttrib) &&
01406         !pUndoOp->IS_KIND_OF(OpEditFill))
01407     {
01408         CCRuntimeClass * pClass = ((OpApplyAttrib *)pUndoOp)->GetValueChangeType();
01409         if (pClass)
01410         {
01411             // regenerate me !
01412             if (pClass->IS_KIND_OF(AttrTranspChange) ||
01413                 pClass->IS_KIND_OF(AttrTranspFillGeometry) ||
01414                 pClass->IS_KIND_OF(AttrBevelIndent) ||
01415                 pClass->IS_KIND_OF(AttrBitmapTranspFill) ||
01416                 pClass->IS_KIND_OF(AttrTextureTranspFill) ||
01417                 pClass->IS_KIND_OF(AttrFillGeometry) ||
01418                 pClass->IS_KIND_OF(AttrStartCap) ||
01419                 pClass->IS_KIND_OF(AttrStartArrow) ||
01420                 pClass->IS_KIND_OF(AttrDashPattern) ||
01421                 pClass->IS_KIND_OF(AttrEndArrow) ||
01422                 pClass->IS_KIND_OF(AttrJoinType) ||
01423                 pClass->IS_KIND_OF(AttrBrushType)
01424                 )
01425             {
01426                 RegenerateNode(pUndoOp, FALSE, FALSE);
01427                 return CC_OK;
01428             }
01429         }
01430         else
01431         {
01432             TRACEUSER( "DavidM", _T("3!\n"));
01433         
01434             if (pUndoOp->IS_KIND_OF(OpEditFill))
01435             {
01436                 pAttrib = ((OpEditFill *)pUndoOp)->GetGradFill();
01437 
01438                 if (pAttrib)
01439                 {
01440                     if (pAttrib->IsATranspFill())
01441                     {
01442                         TRACEUSER( "DavidM", _T("Trans fill edit\n"));
01443                         RegenerateNode(pUndoOp, FALSE, FALSE);
01444                         return CC_OK;
01445                     }
01446                 }
01447             }
01448         }
01449     }
01450 
01451     Node * pParent = NULL;
01452     List ChildList;
01453     BOOL bRegen = FALSE;
01454 
01455     if (pUndoOp->IS_KIND_OF (OpPathNudge))
01456     {
01457         RegenerateNode(pUndoOp, FALSE, FALSE);
01458     }
01459     else if (pUndoOp->IS_KIND_OF(TransOperation) && 
01460         !pUndoOp->IS_KIND_OF(OpMovePathPoint))
01461     {
01462         if (IsSelected())
01463             return CC_OK;
01464         
01465         // a trans operation has occured - do nothing & leave it up to
01466         // the transform function
01467         // first, invalidate my region
01468 // Don't wildly invalidate all the damn time!
01469 // This can cause LiveEffects to be uncached during translation when they can
01470 // translate themeselves perfectly well...
01471 //      pUndoOp->DoInvalidateNodeRegion(this, TRUE);
01472 
01473         // find out if a select-inside drag has occurred (e.g. when a group is shadowed
01474         // and an object inside the group is selected and dragged then a regeneration 
01475         // should occur. This isn't taken care of by the transform method as this method
01476         // isn't called by the children.
01477 
01478         // first, get all the children
01479         BevelTools::GetAllNodesUnderNode(this, &ChildList, CC_RUNTIME_CLASS(NodeRenderableInk));
01480         
01481         pItem = (NodeListItem *)ChildList.GetHead();
01482 
01483         bRegen = FALSE;
01484 
01485         while (pItem)
01486         {
01487             // is it selected implying that it is being transformed
01488             if (pItem->pNode->IsSelected())
01489             {
01490                 // get & test its parent
01491                 pParent = pItem->pNode->FindParent();
01492                 
01493                 while (pParent)
01494                 {
01495                     // does the parent transform with children ?
01496                     if (pParent->ShouldITransformWithChildren())
01497                     {
01498                         // is the parent this object ?
01499                         if (pParent == this)
01500                         {
01501                             // don't regenerate
01502                             bRegen = FALSE;
01503                             
01504                             // break out of the loop
01505                             pParent = NULL;
01506                         }
01507                         else
01508                         {
01509                             // go on to the next parent 
01510                             pParent = pParent->FindParent();
01511                         }
01512                     }
01513                     else
01514                     {
01515                         // yep, regenerate ! 
01516                         bRegen = TRUE;
01517 
01518                         // break the loop
01519                         pParent = NULL;
01520                     }
01521                 }
01522             }
01523 
01524             pItem = (NodeListItem *)ChildList.GetNext(pItem);
01525         }
01526 
01527         ChildList.DeleteAll();
01528 
01529         if (bRegen)
01530         {
01531             RegenerateNode(pUndoOp, FALSE, FALSE);
01532         }
01533 
01534         return CC_OK;
01535     }
01536     else if (pUndoOp && (pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpCut)) || pUndoOp->IsKindOf(CC_RUNTIME_CLASS(OpPaste))) )
01537     {
01538         RegenerateNode(pUndoOp, FALSE, FALSE);
01539         return CC_OK;
01540     }
01541 
01542     Document * pDoc = Document::GetCurrent();
01543 
01544     if (pParam->GetChangeType() == OBJCHANGE_FINISHED)
01545     {
01546         if (pParam->GetChangeFlags().RegenerateNode ||
01547             pParam->GetChangeFlags().DeleteNode)
01548         {
01549             Spread* pSpread = FindParentSpread();
01550             if (pSpread)
01551             {
01552                 if (pDoc)
01553                     pDoc->ForceRedraw(pSpread, BoundingRectangle, FALSE, this);
01554 
01555                 ReleaseCached();
01556                 pUndoOp->DoInvalidateRegion(pSpread, GetBlobBoundingRect());
01557                 RegenerateNode(pUndoOp, FALSE, FALSE);
01558                 pUndoOp->DoInvalidateRegion(pSpread, GetBlobBoundingRect());
01559             
01560                 if (pDoc)
01561                     pDoc->ForceRedraw(pSpread, GetBoundingRect(), FALSE, this);
01562             }
01563 
01564             return NodeEffect::OnChildChange(pParam);
01565         }
01566     }
01567 
01568 // Why do we do this????
01569     if (pUndoOp)
01570     {
01571         Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
01572 
01573         if (pDoc && pSpread)
01574         {
01575             ReleaseCached(TRUE, FALSE, FALSE, TRUE);    // Parents and Derived      // Fix for 11304
01576             pDoc->ForceRedraw(pSpread, BoundingRectangle, FALSE, this, FALSE);
01577         }
01578 
01579 //      pUndoOp->DoInvalidateRegion(FindParentSpread(), BoundingRectangle);
01580     }
01581 
01582     // check for a darkness change - in which case, just invalidate and don't re-render
01583     if (pUndoOp)
01584     {
01585         if (pUndoOp->IS_KIND_OF(OpApplyAttribToNode))
01586         {
01587             // find out if the attribute it's applying is a transparency attribute
01588             if (((OpApplyAttribToNode *)pUndoOp)->GetValueChangeType() == CC_RUNTIME_CLASS(AttrTranspChange))
01589             {
01590                 // exit
01591                 TRACEUSER( "Karim", _T("Should this code be running ?\n"));
01592                 return CC_OK;
01593             }
01594         }
01595     }
01596 
01597     // return the change code
01598     return NodeEffect::OnChildChange(pParam);
01599 }
01600 
01601 
01602 
01603 /********************************************************************************************
01604   > virtual BOOL NodeShadowController::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
01605 
01606     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
01607     Created:    20/11/96
01608     Inputs:     pFilter = ptr to the filter
01609     Returns:    TRUE if record is written, FALSE if not
01610     Purpose:    Writes the group record to the filter
01611     SeeAlso:    -
01612 ********************************************************************************************/
01613 BOOL NodeShadowController::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
01614 {
01615 #ifdef DO_EXPORT
01616     ERROR2IF(pFilter == NULL,FALSE,"NULL filter param");
01617 
01618     CXaraFileRecord Rec(TAG_SHADOWCONTROLLER, TAG_SHADOWCONTROLLER_SIZE);
01619 
01620     BOOL ok = TRUE;
01621 
01622     if (ok) ok = Rec.Init();
01623     if (ok) ok = Rec.WriteBYTE(m_ShadowType);
01624     if (ok) ok = Rec.WriteINT32(m_PenumbraWidth);
01625     // Call the Transformed version as otherwise we get offset from page origin for free!
01626     // This is not something that we desire.
01627     if (ok) ok = Rec.WriteINT32(m_OffsetX);
01628     if (ok) ok = Rec.WriteINT32(m_OffsetY);
01629 
01630     // Karim 13/06/2000
01631     // Changing FloorShadowAngle to be stored in radians for precision - the down side
01632     // of this is that the file-format uses a INT32, so we must use some conversion methods
01633     // to store/retrieve the double shadow value in a INT32 without loss of much precision.
01634     if (ok) ok = Rec.WriteINT32(this->FloorAngleToINT32(m_FloorShadowAngle));
01635 
01636     INT32 Height = (INT32)(m_FloorShadowHeight * 100);
01637     
01638     if (ok) ok = Rec.WriteINT32(Height);
01639 
01640     INT32 Scale = (INT32)(m_Scale * 100.0);
01641 
01642     if (ok) ok = Rec.WriteINT32(Scale);     
01643 
01644     if (m_ShadowType==SHADOWTYPE_FEATHER)
01645     {
01646         if (ok) ok = Rec.WriteINT32(m_FeatherWidth);
01647     }
01648     else
01649     {
01650         if (ok) ok = Rec.WriteINT32(m_GlowWidth);
01651     }
01652         
01653     if (ok)
01654     {
01655         ok = ok && pFilter->Write(&Rec);
01656     }
01657 
01658     return ok;
01659 #else
01660     return FALSE;
01661 #endif
01662 }
01663 
01664 BOOL NodeShadowController::WritePreChildrenNative(BaseCamelotFilter* pFilter)
01665 {
01666 #ifdef DO_EXPORT
01667     return WritePreChildrenWeb(pFilter);
01668 #else
01669     return FALSE;
01670 #endif
01671 }
01672 
01673 
01674 
01675 /********************************************************************************************
01676 >   inline double NodeShadowController::FloorAngleFromINT32(INT32 Angle)
01677 
01678     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01679     Created:    16/06/2000
01680     Inputs:     Angle   a INT32 value in which the floor angle has been stored,
01681                         via FloorAngleToINT32().
01682 
01683     Returns:    The floor-shadow angle in radians, stored in a double.
01684 
01685     Purpose:    Unpack the floor-shadow angle, a double value, from the given INT32.
01686                 Allows us to store this value within a INT32, while maintaining some accuracy.
01687 
01688     See also:   FloorAngleToINT32().
01689 ********************************************************************************************/
01690 inline double NodeShadowController::FloorAngleFromINT32(INT32 Angle)
01691 {
01692     // divide the angle by a million, and ensure it is in the range -2*PI to 2*PI.
01693     return fmod((double)Angle / 1000000.0, 2.0 * PI);
01694 }
01695 
01696 
01697 
01698 /********************************************************************************************
01699 >   inline INT32 NodeShadowController::FloorAngleToINT32(double Angle)
01700 
01701     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01702     Created:    16/06/2000
01703     Inputs:     Angle   a double value representing the floor-shadow angle in radians.
01704 
01705     Returns:    A INT32 value into which the given Angle has been packed.
01706 
01707     Purpose:    Pack the floor-shadow angle, a double value, into a INT32.
01708                 Allows us to store this value within a INT32, while maintaining some accuracy.
01709 
01710     See also:   FloorAngleFromINT32().
01711 ********************************************************************************************/
01712 inline INT32 NodeShadowController::FloorAngleToINT32(double Angle)
01713 {
01714     // ensure the angle is in the range -2*PI to 2*PI, scale it up by a million,
01715     // so we're still well within the range of a INT32, and return the result.
01716     return (INT32)( 1000000.0 * fmod(m_FloorShadowAngle, 2.0 * PI) );
01717 }
01718 
01719 
01720 
01721 /********************************************************************************************
01722 
01723 >   void NodeShadowController::ResetShadowSettings()
01724 
01725     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01726     Created:    19/06/2000
01727     Purpose:    Resets the settings for this shadow to their default values.
01728 
01729     Notes:      Only settings which correspond to the type of this shadow are changed.
01730 
01731 ********************************************************************************************/
01732 void NodeShadowController::ResetShadowSettings()
01733 {
01734     // we won't complain too much if we can't get our shadow, as it's not vital here.
01735     NodeShadow* pShadow = GetShadow();
01736     ERROR3IF(pShadow == NULL,
01737             "NodeShadowController::ResetShadowSettings; where's my NodeShadow gone?!?");
01738 
01739     // we need to redraw the areas covered by our shadow before and after the change.
01740     DocRect drInvalidArea;
01741     if (pShadow != NULL)
01742         drInvalidArea = GetShadow()->GetBoundingRect();
01743 
01744     // reset the appropriate settings to defaults.
01745     switch (GetShadowType())
01746     {
01747     case SHADOWTYPE_WALL:
01748         SetWallShadowOffset(DocCoord(DefaultWallOffsetX, DefaultWallOffsetY), TRUE);
01749         break;
01750     case SHADOWTYPE_GLOW:
01751         SetGlowWidth(DefaultGlowWidth);
01752         break;
01753     case SHADOWTYPE_FLOOR:
01754         SetFloorShadowAngle(DefaultFloorAngle);
01755         SetFloorShadowHeight(DefaultFloorHeight);
01756         break;
01757     case SHADOWTYPE_FEATHER:
01758         SetFeatherWidth(DefaultFeatherWidth);
01759         break;
01760     default:
01761         ERROR3("NodeShadowController::ResetShadowSettings; unrecognised shadow type!");
01762         break;
01763     }
01764 
01765     if (pShadow != NULL)
01766         drInvalidArea = drInvalidArea.Union(pShadow->GetBoundingRect());
01767 
01768     // if there's any area to redraw, then do so please :o)
01769     if (!drInvalidArea.IsEmpty())
01770     {
01771         Document* pDoc = Document::GetCurrent();
01772         Spread* pSpread = FindParentSpread();
01773         if (pDoc != NULL && pSpread != NULL)
01774             pDoc->ForceRedraw(pSpread, drInvalidArea, FALSE, pShadow);
01775     }
01776 }
01777 
01778 
01779 
01780 /********************************************************************************************
01781 >   NodeRenderableInk* NodeShadowController::GetShadowedNode() const
01782 
01783     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01784     Created:    16/06/2000
01785     Returns:    The node which we are shadowing, or NULL if things go tremendously wrong.
01786     Purpose:    Obtain the node which has been given a shadow via this NodeShadowController.
01787     Errors:     ERROR3 if we cannot find a node which we have shadowed.
01788 ********************************************************************************************/
01789 NodeRenderableInk* NodeShadowController::GetShadowedNode() const
01790 {
01791     // Our shadowed node is defined as the NodeRenderableInk after our NodeShadow child.
01792     Node* pKid = GetShadow();
01793     if (pKid != NULL)
01794     {
01795         pKid = pKid->FindNext();
01796         while (pKid != NULL && !pKid->IsAnObject())
01797             pKid = pKid->FindNext();
01798     }
01799 
01800     // pKid is now either NULL, or is the node we're looking for.
01801 // This is legal in some transition cases so don't error
01802 //  ERROR3IF(pKid == NULL, "NodeShadowController::GetShadowedNode; Can't find shadowed node!");
01803 #ifdef _DEBUG
01804 if (pKid==NULL)
01805 {
01806     TRACE( _T("NodeShadowController::GetShadowedNode; Can't find shadowed node!"));
01807 }
01808 #endif
01809     return (NodeRenderableInk*)pKid;
01810 }
01811 
01812  
01813 /********************************************************************************************
01814 
01815 >   virtual NodeRenderableInk* NodeShadowController::GetInkNodeFromController() const
01816 
01817     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01818     Created:    16/10/2000
01819     Inputs:     -
01820     Outputs:    -
01821     Returns:    The ink node that is used by bevels, shadows and contours
01822     Purpose:    Ok, I know that this duplicates GetShadowedNode but there is no equivalent
01823                 for bevels and contours so I thought I would make a virtual function and 
01824                 eventually replace GetShadowedNode instead.
01825                 
01826     SeeAlso:    
01827 
01828 ********************************************************************************************/
01829 
01830 NodeRenderableInk* NodeShadowController::GetInkNodeFromController() const
01831 {
01832     return GetShadowedNode();
01833 }
01834 
01835 
01836 /********************************************************************************************
01837 
01838 >   virtual NodeRenderableInk* NodeShadowController::GetObjectToApplyTo(CCRuntimeClass* AttrType)
01839 
01840     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01841     Created:    21/01/2005
01842     Inputs:     -
01843     Outputs:    -
01844     Returns:    The ink node that the specified attribute should be applied to
01845     Purpose:    Allows Compounds/Controllers/Effects nodes to re-point attribute application
01846                 at one of their associated nodes instead of themselves...
01847     SeeAlso:    
01848 
01849 ********************************************************************************************/
01850 
01851 NodeRenderableInk* NodeShadowController::GetObjectToApplyTo(CCRuntimeClass* pAttrType)
01852 {
01853     // Yes, I know this is a mess... Wouldn't be required if we had IsATranspFill...
01854     // (See also IsValidEffectAttr)
01855     // Allow transparent attributes to be applied directly to me...
01856 /*  if (pAttrType == CC_RUNTIME_CLASS(AttrBitmapTranspFill) ||
01857         pAttrType == CC_RUNTIME_CLASS(AttrConicalTranspFill) ||
01858         pAttrType == CC_RUNTIME_CLASS(AttrFlatTranspFill) ||
01859         pAttrType == CC_RUNTIME_CLASS(AttrFourColTranspFill) ||
01860         pAttrType == CC_RUNTIME_CLASS(AttrLinearTranspFill) ||
01861         pAttrType == CC_RUNTIME_CLASS(AttrRadialTranspFill) ||
01862         pAttrType == CC_RUNTIME_CLASS(AttrSquareTranspFill) ||
01863         pAttrType == CC_RUNTIME_CLASS(AttrTextureTranspFill) ||
01864         pAttrType == CC_RUNTIME_CLASS(AttrThreeColTranspFill) ||
01865         pAttrType == CC_RUNTIME_CLASS(AttrTranspFillGeometry) ||
01866         pAttrType == CC_RUNTIME_CLASS(AttrStrokeTransp) ||
01867 
01868         pAttrType == CC_RUNTIME_CLASS(AttrTranspChange) ||
01869         pAttrType == CC_RUNTIME_CLASS(AttrStrokeTranspChange)
01870         )
01871         return NodeEffect::GetObjectToApplyTo(pAttrType);
01872 */
01873     NodeRenderableInk* pInk = GetInkNodeFromController();
01874     if (pInk)
01875     {
01876         NodeRenderableInk* pNode = pInk->GetObjectToApplyTo(pAttrType);
01877         if (pNode!=this)
01878             return pNode;
01879     }
01880 
01881     return NodeEffect::GetObjectToApplyTo(pAttrType);
01882 }
01883 
01884 
01885 
01886 
01887 /********************************************************************************************
01888 >   NodeShadow* NodeShadowController::GetShadow()
01889 
01890     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01891     Created:    4/12/96
01892     Inputs:     -
01893     Returns:    Pointer to child shadow (NULL if not found!)
01894     Purpose:    Returns a type correct pointer to child shadow
01895 ********************************************************************************************/
01896 NodeShadow* NodeShadowController::GetShadow() const
01897 {
01898     NodeShadow* pBob = (NodeShadow*)FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
01899 
01900     return pBob;
01901 }
01902 
01903 
01904 
01905 
01906 /***********************************************************************************************
01907 
01908 >   INT32 NodeShadowController::ComplexHide(UndoableOperation* pOp, Node* pNextInRange)
01909 
01910     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01911     Created:    12/8/99
01912     Inputs:     See base class
01913     Purpose:    Hides this node
01914 
01915 ***********************************************************************************************/
01916 INT32 NodeShadowController::ComplexHide(UndoableOperation* pOp, Node* pNextInRange)
01917 {
01918     NodeHidden * pHidden = NULL;
01919     BOOL ok = pOp->DoHideNode(this, TRUE, &pHidden, TRUE);
01920 
01921     if (ok)
01922         return 1;
01923 
01924     return -1;
01925 }
01926 
01927 
01928 
01929 /***********************************************************************************************
01930 
01931 >   void NodeShadow::ShowDebugTreeDetails() const
01932 
01933     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
01934     Created:    5/12/97
01935     Purpose:    Displays debugging info of the tree
01936     SeeAlso:    NodeRenderableInk::ShowDebugTreeDetails
01937 
01938 ***********************************************************************************************/
01939 void NodeShadowController::ShowDebugTreeDetails() const
01940 {                     
01941 #ifdef _DEBUG
01942     // Display a bit of debugging info
01943     // For now, we will just call the base class version
01944     TRACEALL( _T("NodeShadowController  ") );
01945     NodeRenderableInk::ShowDebugTreeDetails();  
01946 #endif
01947 }
01948 
01949 
01950 
01951 
01952 /********************************************************************************************
01953 
01954   > NodeShadow* NodeShadowController::GetDebugDetails(StringBase* Str)
01955 
01956     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
01957     Created:    5/12/97
01958     Outputs:    Str - extra debug information
01959     Purpose:    Give more information to the debug tree about this node.
01960 
01961 ********************************************************************************************/
01962 void NodeShadowController::GetDebugDetails(StringBase* Str) 
01963 {          
01964 #ifdef _DEBUG
01965     NodeRenderableInk::GetDebugDetails(Str); 
01966     
01967     if (Str == NULL)
01968         return;
01969 
01970     String_256 TempStr; 
01971 
01972     TempStr._MakeMsg(TEXT("Penumbra width = #1%d\r\n"),m_PenumbraWidth);   
01973     (*Str)+=TempStr; 
01974 
01975     /*
01976     
01977     (*Str)+= TEXT("Current render method ");
01978     switch (m_ShadowType)
01979     {
01980         case RANDOM:            TempStr = TEXT("Render n copies around a centrepoint (Fails on large penumbras)"); break;
01981         case LINEWIDTHS:        TempStr = TEXT("Render decreasing linewidths with decreasing transparency (Fails on transparent shadows)"); break;
01982         case STROKEPATHTOPATH:  TempStr = TEXT("Render StrokePathToPath linewidths (Far too slow (on complex shapes))"); break;
01983         case FLATTENEXPAND:     TempStr = TEXT("Flatten and expand paths (Fails on inverted paths)"); break;
01984         case BITMAPFILTER:      TempStr = TEXT("Bitmap shadower"); break;
01985         default:                TempStr = TEXT("Unknown"); break;
01986     }
01987     (*Str) += TempStr;
01988     */
01989 #endif
01990 }   
01991 
01992 
01993 //---------------------------------------------------------------------------------------
01994 //---------------------------------------------------------------------------------------
01995 //---------------------------------------------------------------------------------------
01996 
01997 ShadowRecordHandler::~ShadowRecordHandler()
01998 {
01999     TRACEUSER( "MarkH", _T("Killing off ShadowRecordHandler!\n"));
02000 }
02001 
02002 /********************************************************************************************
02003 
02004 >   virtual UINT32* ShadowRecordHandler::GetTagList()
02005 
02006     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
02007     Created:    5/12/97
02008     Inputs:     -
02009     Returns:    Ptr to a list of tag values, terminated by CXFRH_TAG_LIST_END
02010     Purpose:    Provides the record handler system with a list of records handled by this
02011                 handler
02012     SeeAlso:    -
02013 
02014 ********************************************************************************************/
02015 
02016 UINT32* ShadowRecordHandler::GetTagList()
02017 {
02018     static UINT32 TagList[] = { TAG_SHADOWCONTROLLER,
02019                                 TAG_SHADOW,
02020                                 CXFRH_TAG_LIST_END};
02021 
02022     return (UINT32*)&TagList;
02023 }
02024 
02025 /********************************************************************************************
02026 
02027 >   virtual BOOL ShadowRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
02028 
02029     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
02030     Created:    5/12/97
02031     Inputs:     pCXaraFileRecord = ptr to record to handle
02032     Returns:    TRUE if handled successfuly
02033                 FALSE otherwise
02034     Purpose:    Handles the given record.
02035     SeeAlso:    -
02036 
02037 ********************************************************************************************/
02038 
02039 BOOL ShadowRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
02040 {
02041     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
02042 
02043     BOOL ok = TRUE;
02044 
02045     switch (pCXaraFileRecord->GetTag())
02046     {
02047         case TAG_SHADOWCONTROLLER:
02048             ok = HandleShadowControllerRecord(pCXaraFileRecord);
02049             break;
02050 
02051         case TAG_SHADOW:
02052             ok = HandleShadowRecord(pCXaraFileRecord);
02053             break;
02054 
02055         default:
02056             ok = FALSE;
02057             ERROR3_PF(("I don't handle records with the tag (%d)\n",pCXaraFileRecord->GetTag()));
02058             break;
02059     }
02060 
02061     return ok;
02062 }
02063 
02064 
02065 /********************************************************************************************
02066 
02067 BOOL ShadowRecordHandler::HandleShadowControllerRecord(CXaraFileRecord* pCXaraFileRecord)
02068 
02069     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
02070     Created:    5/12/97
02071     Inputs:     pCXaraFileRecord = ptr to record to handle
02072     Returns:    TRUE if handled successfuly
02073                 FALSE otherwise
02074     Purpose:    Handles the given record.
02075                 The record has to be a shadow controller record
02076     SeeAlso:    -
02077 
02078 ********************************************************************************************/
02079 
02080 BOOL ShadowRecordHandler::HandleShadowControllerRecord(CXaraFileRecord* pCXaraFileRecord)
02081 {
02082     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
02083     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_SHADOWCONTROLLER,FALSE,"I don't handle this tag type");
02084 
02085     TRACEUSER( "MarkH", _T("Reading in NodeShadowController!\n"));
02086 
02087     MILLIPOINT  Width   = 0;
02088     INT32 XOffset       = 0;
02089     INT32 YOffset       = 0;
02090     INT32 INT32FloorShadowAngle = 0;
02091     INT32 FloorShadowHeight = 0;
02092     INT32 Scale         = 0;
02093     INT32 GlowWidth     = 0;
02094     ShadowType      Type    =    SHADOWTYPE_WALL;
02095 
02096     BYTE temp;
02097     BOOL    ok = pCXaraFileRecord->ReadBYTE(&temp);
02098     Type = (ShadowType)temp;
02099     if (ok) ok = pCXaraFileRecord->ReadINT32(&Width);
02100     // Call the Transformed version as otherwise we get offset from page origin for free!
02101     // This is not something that we desire.
02102     if (ok) ok = pCXaraFileRecord->ReadINT32(&XOffset);
02103     if (ok) ok = pCXaraFileRecord->ReadINT32(&YOffset);
02104 
02105     // Karim 13/06/2000
02106     // Changing FloorShadowAngle to be stored in radians for precision - the down side
02107     // of this is that the file-format uses a INT32, so we must use some conversion methods
02108     // to store/retrieve the double shadow value in a INT32 without loss of much precision.
02109     if (ok) ok = pCXaraFileRecord->ReadINT32(&INT32FloorShadowAngle);
02110 
02111     if (ok) ok = pCXaraFileRecord->ReadINT32(&FloorShadowHeight);
02112     if (ok) ok = pCXaraFileRecord->ReadINT32(&Scale);
02113     if (ok) ok = pCXaraFileRecord->ReadINT32(&GlowWidth);
02114 
02115     if (ok)
02116     {
02117         NodeShadowController* pController = new NodeShadowController;
02118         if (pController != NULL)
02119         {
02120             pController->SetShadowType(Type);
02121             pController->SetOffsetX(XOffset);
02122             pController->SetOffsetY(YOffset);
02123             pController->SetPenumbraWidth(Width);
02124             pController->SetFloorShadowAngle(pController->FloorAngleFromINT32(INT32FloorShadowAngle));
02125             pController->SetFloorShadowHeight((float)(((double)FloorShadowHeight) / 100.0));
02126             pController->SetWallShadowScale((float)(((double)Scale) / 100.0));
02127             if (Type==SHADOWTYPE_FEATHER)
02128                 pController->SetFeatherWidth(GlowWidth);
02129             else
02130                 pController->SetGlowWidth(GlowWidth);
02131             pController->SetSelected(FALSE);
02132 
02133             ok = InsertNode(pController);
02134         }
02135         else
02136             ok = FALSE;
02137     }
02138 
02139     return ok;
02140 }
02141 
02142 
02143 
02144 /********************************************************************************************
02145 
02146 >   BOOL ShadowRecordHandler::HandleShadowRecord(CXaraFileRecord* pCXaraFileRecord)
02147 
02148     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
02149     Created:    5/12/97
02150     Inputs:     pCXaraFileRecord    ptr to record to handle.
02151 
02152     Returns:    TRUE if handled successfully,
02153                 FALSE otherwise.
02154     Purpose:    Handles the given record.
02155                 The record has to be a shadow record.
02156 
02157     Notes:      Karim 06/02/2001
02158                 Added code to read extra Darkness parameter.
02159                 Because this was not part of the original file format, this needs to be
02160                 read in with no error checking for reading past end of buffer,
02161                 to remain compatible with older file formats. When reading in an older
02162                 file format, the darkness value will then not be read, and will default
02163                 to whatever it was initialised with (currently fully black).
02164 
02165 ********************************************************************************************/
02166 BOOL ShadowRecordHandler::HandleShadowRecord(CXaraFileRecord* pCXaraFileRecord)
02167 {
02168     ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL");
02169     ERROR2IF(pCXaraFileRecord->GetTag() != TAG_SHADOW,FALSE,"I don't handle this tag type");
02170 
02171     TRACEUSER( "MarkH", _T("Reading in NodeShadow!\n"));
02172     
02173     double  Bias     = 0;
02174     double  Gain     = 0;
02175     double  Darkness = 1.0;
02176 
02177     // No data to read at present
02178     BOOL ok = TRUE;
02179     if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&Bias);
02180     if (ok) ok = pCXaraFileRecord->ReadDOUBLE(&Gain);
02181     if (ok) ok = pCXaraFileRecord->ReadDOUBLEnoError(&Darkness);
02182 
02183     NodeShadow* pShadow = new NodeShadow;
02184     CProfileBiasGain Profile;
02185     Profile.SetBias((AFp)Bias);
02186     Profile.SetGain((AFp)Gain);
02187 
02188     if (pShadow != NULL)
02189     {
02190         pShadow->SetBiasGain(Profile);
02191         pShadow->SetDarkness(Darkness);
02192         ok = InsertNode(pShadow);
02193     }
02194     else
02195     {
02196         ok = FALSE;
02197     }
02198 
02199     return ok;
02200 }
02201 
02202 
02203 
02204 /********************************************************************************************
02205 
02206 >   virtual void ShadowRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
02207 
02208     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
02209     Created:    5/12/97
02210     Inputs:     pRecord = ptr to a record
02211                 pStr = ptr to string to update
02212     Returns:    -
02213     Purpose:    This provides descriptions for the shadow records.
02214     Errors:     -
02215     SeeAlso:    -
02216 
02217 ********************************************************************************************/
02218 
02219 #ifdef XAR_TREE_DIALOG
02220 void ShadowRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr)
02221 {
02222 /*
02223     if (pStr == NULL || pRecord == NULL)
02224         return;
02225 
02226     TCHAR s[256];
02227 
02228     //   Call base class first
02229     CamelotRecordHandler::GetRecordDescriptionText(pRecord,pStr);
02230 
02231     switch (pRecord->GetTag())
02232     {
02233         case TAG_SHADOWCONTROLLER:
02234         {
02235             ShadowType  Type        = NodeShadowController::RenderMethod;
02236             MILLIPOINT  Width       = 0;
02237             INT32       XOffset     = 0;
02238             INT32       YOffset     = 0;
02239             Matrix      Offset;
02240 
02241             pRecord->ReadBYTE(&Type);
02242             pRecord->ReadINT32(&Width);
02243             // Call the Transformed version as otherwise we get offset from page origin for free!
02244             // This is not something that we desire.
02245             pRecord->ReadMatrixTrans(&Offset, XOffset, YOffset);
02246 
02247             switch (Type)
02248             {
02249                 case RANDOM:            (*pStr) += TEXT("Render n copies around a centrepoint (Fails on large penumbras)"); break;
02250                 case LINEWIDTHS:        (*pStr) += TEXT("Render decreasing linewidths with decreasing transparency (Fails on transparent shadows)"); break;
02251                 case STROKEPATHTOPATH:  (*pStr) += TEXT("Render StrokePathToPath linewidths (Far too slow (on complex shapes))"); break;
02252                 case FLATTENEXPAND:     (*pStr) += TEXT("Flatten and expand paths (Fails on inverted paths)"); break;
02253                 case BITMAPFILTER:      (*pStr) += TEXT("Bitmap shadower"); break;
02254                 default:                (*pStr) += TEXT("Unknown"); break;
02255             }
02256             (*pStr) += _T("\r\n");
02257 
02258             camSprintf(s,_T("Width\t\t= %d\r\n"),Width);
02259             (*pStr) += s;
02260             (*pStr) += _T("\r\n");
02261         }
02262         break;
02263     }
02264     */
02265 }
02266 
02267 #endif
02268 
02269 
02270 /********************************************************************************************
02271 
02272 >   BOOL NodeShadowController::PostImport()
02273 
02274 
02275     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02276     Created:    16/4/99
02277     Inputs:     
02278     Returns:    True if successful
02279     Purpose:    Does post-import stuff
02280 
02281     Notes:      Karim 07/02/2001
02282                 Is this really necessary? Must try removing this fn and seeing if there
02283                 is actually any point in having it!
02284 
02285 ********************************************************************************************/
02286 BOOL NodeShadowController::PostImport()
02287 {
02288     // ok, the entire shadow tree structure has now been set up,
02289     // so force regeneration of the shadow.
02290     NodeShadow* pShadow = GetShadow();
02291 
02292     if (pShadow == NULL)
02293     {
02294         ERROR3("NodeShadowController::PostImport; Cannot find shadow child node!");
02295         return FALSE;
02296     }
02297 
02298     pShadow->GenerateShadow();
02299 
02300     return TRUE;
02301 }
02302 
02303 
02304 
02305 
02306 /********************************************************************************************
02307 
02308 >   BOOL NodeShadowController::PostDuplicate(UndoableOperation* pOp)
02309 
02310 
02311     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02312     Created:    16/4/99
02313     Inputs:     
02314     Returns:    True if successful
02315     Purpose:    Does post-import stuff
02316 
02317 ********************************************************************************************/
02318 BOOL NodeShadowController::PostDuplicate(UndoableOperation* pOp)
02319 {
02320     // force a regeneration of the shadow
02321     NodeShadow * pShadow = (NodeShadow *)FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
02322 
02323     if (!pShadow)
02324     {
02325         ERROR3("Post duplicate - cannot find shadow child node !");
02326         return FALSE;
02327     }
02328 
02329     pShadow->DeleteCache();
02330     pShadow->GenerateShadow();
02331 
02332     NodeRenderableInk::PostDuplicate(pOp);
02333 
02334     IsBoundingRectValid = FALSE;
02335     DocRect dr = GetBoundingRect(TRUE, FALSE);
02336 
02337     return TRUE;
02338 }
02339 
02340 
02341 
02342 
02343 /********************************************************************************************
02344 
02345 >   BOOL NodeShadowController::RegenerateNode(UndoableOperation * pUndoOp)
02346 
02347 
02348     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
02349     Created:    16/4/99
02350     Inputs:     
02351     Returns:    TRUE to indicate that the node was changed, FALSE if nothing changed
02352     Purpose:    Sets up a regenerate node action, in response to a 
02353 
02354 ********************************************************************************************/
02355 BOOL NodeShadowController::RegenerateNode(UndoableOperation * pUndoOp, BOOL bCacheRender,
02356                                           BOOL bInformParents)
02357 {
02358     Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
02359 
02360     if (!pSpread)
02361         pSpread = Document::GetSelectedSpread();
02362 
02363     if (!pSpread)
02364         ERROR2(FALSE, "Can't regenerate node with no spread");
02365     
02366     if (pUndoOp)
02367     {
02368         // invalidate my region
02369 //      ReleaseCached();
02370         ReleaseCached(TRUE, FALSE, TRUE, TRUE);     // Fix for 11304
02371         pUndoOp->DoInvalidateRegion(pSpread, BoundingRectangle);
02372 
02373         ChangePenumbraSizeAction * pAction = NULL;
02374 
02375         if (ChangePenumbraSizeAction::Init(pUndoOp,
02376                                        pUndoOp->GetUndoActionList(),
02377                                        this,
02378                                        m_PenumbraWidth,
02379                                        &pAction, TRUE, bCacheRender) != AC_OK)
02380         {
02381             return FALSE;
02382         }
02383         else
02384         {
02385             // invalidate the region afterwards too
02386             pUndoOp->DoInvalidateRegion(pSpread, GetBoundingRect(TRUE, FALSE));
02387 
02388             return TRUE;
02389         }
02390     }
02391     else
02392     {
02393         // just regen anyway
02394         // inform parents of before
02395         
02396         if (bInformParents)
02397         {
02398             PreInformParentsOfRegenerate();
02399         }
02400 
02401         Document * pDoc = Document::GetCurrent();
02402 
02403         if (pDoc)
02404         {
02405             pDoc->ForceRedraw(pSpread, BoundingRectangle, FALSE, this, FALSE);  // Added FALSE params for 2nd part of Job 11021
02406             pDoc->ForceRedraw(pSpread, GetShadow()->GetBoundingRect(TRUE, FALSE), FALSE, this, FALSE);
02407         }
02408 
02409         BOOL ok = this->GetShadow()->GenerateShadow();
02410 
02411         InvalidateBoundingRect(TRUE);
02412 
02413         if (pDoc)
02414         {
02415             pDoc->ForceRedraw(pSpread, GetBoundingRect(TRUE, FALSE), FALSE, this, FALSE);
02416         }
02417 
02418         // inform parents of change afterwards
02419         if (bInformParents)
02420         {
02421             PostInformParentsOfRegenerate();        
02422         }
02423 
02424         return ok;
02425     } 
02426     return TRUE;
02427 }
02428 
02429 
02430 
02431 /********************************************************************************************
02432 
02433 >   UINT32 NodeShadowController::GetShadowTransp()
02434 
02435     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02436     Created:    06/02/2001
02437 
02438     Returns:    Our NodeShadow's transparency value: 0 for opaque, 255 for transparent.
02439 
02440     Errors:     ERROR3 if we have no NodeShadow, returning 0 (opaque).
02441 
02442 ********************************************************************************************/
02443 UINT32 NodeShadowController::GetShadowTransp()
02444 {
02445     NodeShadow* pShadow = GetShadow();
02446 
02447     if (pShadow == NULL)
02448     {
02449         ERROR3("NodeShadowController::GetShadowDarkness; No NodeShadow present!");
02450         return 0;
02451     }
02452 
02453     return pShadow->GetTransp();
02454 }
02455 
02456 
02457 
02458 /********************************************************************************************
02459 
02460 >   virtual DocRect NodeShadowController::ValidateExtend(const ExtendParams& ExtParams)
02461 
02462     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02463     Created:    06/12/1999
02464     Inputs:     ExtParams       description parameters for the extension.
02465     Outputs:    
02466     Returns:    TRUE if extending this Node and its children is a reversible operation,
02467                 FALSE otherwise.
02468     Purpose:    Tests the reversibility of an Extend operation applied to this node.
02469 
02470                 In the case of a NodeShadowController, this function is identical to
02471                 Node's implementation, except that this controller's own NodeShadow
02472                 child is ignored. This allows the NodeShadow to tell its parent to
02473                 extend, without fear of infinite recursion.
02474     Errors:     
02475     See also:   IsTypeExtendible(), Extend().
02476 
02477 ********************************************************************************************/
02478 DocRect NodeShadowController::ValidateExtend(const ExtendParams& ExtParams)
02479 {
02480     Node* pBob = GetShadow();
02481     DocRect drMinExtend(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX), drThisMinExtend;
02482     for (   Node* pChildNode = FindFirstChild();
02483             pChildNode != NULL;
02484             pChildNode = pChildNode->FindNext() )
02485     {
02486         if (pChildNode == pBob)
02487             continue;
02488 
02489         drThisMinExtend = pChildNode->ValidateExtend(ExtParams);
02490         if (drMinExtend.lo.x > drThisMinExtend.lo.x) drMinExtend.lo.x = drThisMinExtend.lo.x;
02491         if (drMinExtend.lo.y > drThisMinExtend.lo.y) drMinExtend.lo.y = drThisMinExtend.lo.y;
02492         if (drMinExtend.hi.x > drThisMinExtend.hi.x) drMinExtend.hi.x = drThisMinExtend.hi.x;
02493         if (drMinExtend.hi.y > drThisMinExtend.hi.y) drMinExtend.hi.y = drThisMinExtend.hi.y;
02494     }
02495     return drMinExtend;
02496 }
02497 
02498 
02499 
02500 /********************************************************************************************
02501 
02502 >   virtual void NodeShadowController::Extend(const ExtendParams& ExtParams)
02503 
02504     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02505     Created:    06/12/1999
02506     Inputs:     ExtParams       description parameters for the extension.
02507     Outputs:    Some of this node's children may have their dimensions altered.
02508     Returns:    
02509     Purpose:    Perform an Extend operation on this Node, and its children if appropriate.
02510 
02511                 In the case of a NodeShadowController, this function is identical to
02512                 Node's implementation, except that this controller's own NodeShadow
02513                 child is ignored. This allows the NodeShadow to tell its parent to
02514                 extend, without fear of infinite recursion.
02515     Errors:     
02516     See also:   
02517 
02518 ********************************************************************************************/
02519 void NodeShadowController::Extend(const ExtendParams& ExtParams)
02520 {
02521     Node* pBob = GetShadow();
02522     for (   Node* pChildNode = FindFirstChild();
02523             pChildNode != NULL;
02524             pChildNode = pChildNode->FindNext() )
02525     {
02526         if (pChildNode == pBob)
02527             continue;
02528 
02529         pChildNode->Extend(ExtParams);
02530     }
02531     // Add an action to regen the shadow here
02532     // and instead of regening the shadow when the m_PerformedExtend occurs in the child change
02533     // cut out and let the action do the stuff
02534     m_PerformedExtend = TRUE; 
02535     RegenerateNode(ExtParams.pOp, FALSE, FALSE);
02536 }
02537 
02538 
02539 
02540 
02541 /***********************************************************************************************
02542 
02543 >   BOOL NodeShadowController::EndBlendStep(BlendNodeParam * pParam)
02544 
02545     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>/Chris Snook
02546     Created:    12/8/99
02547     Inputs:     See base class
02548     Purpose:    Starts the blending of a step
02549 
02550 ***********************************************************************************************/
02551 BOOL NodeShadowController::EndBlendStep(BlendNodeParam * pParam)
02552 {
02553     // CGS:  firstly, are we just rendering, or are we making shapes?
02554 
02555     if (!pParam->GetHandleBecomeA ())
02556     {   
02557         // were just rendering ....
02558 
02559         BOOL okToRender = TRUE;
02560         
02561         // get hold of the current path processor ....
02562         if (pParam->GetPathProcessor ())
02563         {
02564             ERROR2IF(!pParam->GetPathProcessor ()->IS_KIND_OF(SumAllPathsPathProcessor),
02565                      FALSE, "First path processor isn't a sum all paths path processor");
02566 
02567             // grab the path processor
02568             SumAllPathsPathProcessor * pProc = (SumAllPathsPathProcessor *) pParam->GetPathProcessor ();
02569 
02570             pProc->SetEnabled(FALSE);       // disable it
02571 
02572             // we can now get on and generate intermediate blend steps.  NOTE:  this node contour
02573             // controller should not be touched at all - since it is held within the tree
02574             // so we make a copy that we can play with ....
02575                 
02576             Node* copy = NULL;
02577             this->NodeCopy (&copy);
02578 
02579             NodeShadowController* ptrCopy = (NodeShadowController*) copy;
02580 
02581             if (!ptrCopy) { return (FALSE); }       // out of memory
02582 
02583             // find the contour
02584             NodeShadow * pShadowCopy = (NodeShadow *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
02585             ERROR2IF(pShadowCopy == NULL, FALSE, "Can't find the contour node");
02586 
02587 #ifdef _DEBUG       // special debug info
02588             INT32 id = ++NodeShadowController::contourLastID;
02589             ptrCopy->myContourID = id;
02590 //          char strId [100];
02591 //          wsprintf (strId, "Pushing NodeShadowController ID:  %i\n", ptrCopy->myContourID);
02592 //          TRACEUSER ("ChrisS", strId);
02593             TRACEUSER("ChrisS", _T("Pushing NodeShadowController ID:  %i\n"), ptrCopy->myContourID);
02594 #endif
02595 
02597 
02598             // now the fun begins, lest blend attributes for this particular blend step ....
02599             
02600             CCAttrMap * pMap = NULL;                        // if pEndControl is a contour as well
02601             CCAttrMap * pPrimaryShadowAttrMap = NULL;       // if pEndControl is not a contour
02602 
02603             // blend the contour's attributes
02604             NodeShadowController * pEndControl = (NodeShadowController *)FindAssociatedBlendNode(pParam);
02605 
02606             if (pEndControl)
02607             {
02608                 // we are blending two shadows YIPEE!       
02609                 
02610                 NodeShadow * pEndShadow = (NodeShadow *)pEndControl->FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
02611                 NodeRenderableInk * pOrigEndNode = pEndControl->GetBlendCreatedByNode();
02612 
02613                 // somethings a bit wrong if either of these fire ....
02614                 ERROR2IF(pOrigEndNode == NULL, FALSE, "Cant find the original contour node which this is based on");
02615                 ERROR2IF(!pOrigEndNode->IS_KIND_OF(NodeShadowController), FALSE, "Original node isn't a NodeShadowController");
02616 
02617                 pOrigEndNode = (NodeShadow *)pOrigEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
02618 
02619                 BlendPath MyStartPath;
02620                 MyStartPath.Initialise((NodeRenderableInk *)pShadowCopy->PublicCopy(), -1, pShadowCopy, 1, 0, NULL);
02621                 
02622                 BlendPath MyEndPath;
02623                 MyEndPath.Initialise((NodeRenderableInk *)pEndShadow->PublicCopy(), -1, pOrigEndNode, 1, 0, NULL);
02624                 
02625                 BlendNodeParam BNParam;
02626                 BNParam.Init(pParam, &MyStartPath, &MyEndPath);
02627                 
02628                 pMap = new CCAttrMap(30);
02629 
02630                 BlendHelpers * pHelp = new BlendHelpers;
02631                 ERRORIF(pHelp == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
02632                 
02633                 // now, perform the blend of the attributes
02634                 if (!pHelp->BlendAttributes(&BNParam, pMap))
02635                 {
02636                     pParam->GetRenderRegion()->PopPathProcessor();
02637                     okToRender = FALSE;
02638                 }
02639                 
02640                 delete pHelp;
02641 
02642                 // also blend my attributes & my node contour's attributes
02643                 ptrCopy->BlendParameters ((NodeShadowController*) m_pBlendCreatedByNode, (NodeShadowController*) pOrigEndNode->FindParent(), pParam, pMap);
02644             }
02645             else
02646             {
02647                 // were blending a shadowed node to a non-shadowed node.
02648                 // since we have not generated an attribute map, shadows that are generated
02649                 // render as white ....
02650 
02651                 // we need to make a attribute map for *this* NodeShadow, and apply it to
02652                 // the copy ....
02653 
02654                 NodeShadow * pContour = (NodeShadow *) FindFirstChild (CC_RUNTIME_CLASS (NodeShadow));
02655 
02656                 ERROR2IF (pContour == NULL, FALSE, "Can't find Contour node");
02657 
02658                 // build an attribute map for the contour
02659                 pPrimaryShadowAttrMap = CCAttrMap::MakeAppliedAttrMap(pContour);
02660 
02661                 // we don't have another node to blend to - but lets not let a little thing like that deter us! 
02662                 ptrCopy->BlendParameters ((NodeShadowController *) m_pBlendCreatedByNode, pParam);
02663             }
02664 
02665             // are we shadowing a path, or are we shadowing a compound?
02666 
02667             Node* newShadowing = GetShadowDeleteThisNode ();
02668 
02669             if (!newShadowing)          // were shadowing a path
02670             {
02671                 // scan for the path that original contour was generated with
02672                 NodePath* currentPath = (NodePath *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodePath));
02673 
02674                 // if we find one, we know get on and merge all the paths so that the contour can
02675                 // be generated correctly.
02676 
02677                 // we do this by:
02678 
02679                 // replacing currentPath by a group that contains all paths on the path processors list
02680                 // (i.e.  we replace it by the blended paths - who have their blended attribute maps
02681                 // applied to them)
02682 
02683                 if (currentPath)
02684                 {
02685                     currentPath->CascadeDelete ();
02686                     delete (currentPath);
02687                     currentPath = NULL;
02688 
02689                     NodeGroup* newGroup = new NodeGroup ();
02690 
02691                     ListItem* pItem = pProc->GetList ()->GetHead ();
02692                     
02693                     Path* blendPath = NULL;
02694                     CCAttrMap* blendMap = NULL;
02695 
02696                     while (pItem)
02697                     {
02698                         blendPath = ((SumAllPathsElem *) pItem)->GetPath ();
02699                         blendMap = ((SumAllPathsElem *) pItem)->GetAttrMap ();
02700                         
02701                         NodePath* newPath = new NodePath ();
02702                         INT32 numCoords = blendPath->GetNumCoords ();
02703                         newPath->InkPath.ClearPath ();
02704                         newPath->InkPath.Initialise (numCoords);
02705                         newPath->InkPath.CopyPathDataFrom (blendPath);
02706                         blendMap->ApplyAttributesToNode (newPath);
02707                         newPath->AttachNode(newGroup, LASTCHILD);
02708                         
02709                         pItem = pProc->GetList ()->GetNext (pItem);
02710                         delete ((SumAllPathsElem *) pProc->GetList()->RemoveHead ());
02711                     }
02712                     
02713                     newGroup->AttachNode(pShadowCopy, NEXT);
02714                 }
02715             }
02716             else        // were shadowing a compound
02717             {
02718                 // remove the old compounds subtree
02719                 
02720                 Node* pShadowing = pShadowCopy->FindNext ();
02721 
02722                 pShadowing->CascadeDelete ();
02723                 delete (pShadowing);
02724 
02725                 // and replace with the new
02726                 
02727                 newShadowing->AttachNode (pShadowCopy, NEXT);
02728             }
02729 
02730             // apply blended contour attributes ....
02731 
02732             if (pMap)
02733             {
02734                 pMap->ApplyAttributesToNode (pShadowCopy);
02735             }
02736 
02737             if (pPrimaryShadowAttrMap)
02738             {
02739                 pPrimaryShadowAttrMap->ApplyAttributesToNode (pShadowCopy);
02740             }
02741 
02742             // generate the contour (which will correctly be generated for merged paths if needed)
02743             
02744             if (!pShadowCopy->GenerateShadow())
02745             {
02746                 okToRender = FALSE;
02747             }
02748 
02749             // now render the contour controller as a complete node (just like the document renders)
02750             
02751             if (okToRender)
02752             {
02753                 RenderRegion* pRegion = pParam->GetRenderRegion();
02754 
02755                 if (pRegion)
02756                     pRegion->RenderTreeNoCache(ptrCopy);
02757             }
02758 
02759             if (pMap)
02760             {
02761                 pMap->DeleteAttributes ();
02762                 delete pMap;
02763             }
02764 
02765             if (pPrimaryShadowAttrMap)
02766             {
02767                 delete pPrimaryShadowAttrMap;
02768             }
02769 
02770             // CGS:  NodeShadowControllers can safely delete themselves (after I have made the
02771             // necessary checks to ensure that we also delete any 'linked' bevel/contour ....
02772             
02773             if (newShadowing)
02774             {
02775                 //shadowDeleteThisNode->CascadeDelete ();
02776                 //delete (shadowDeleteThisNode);
02777                 SetShadowDeleteThisNode (NULL);
02778             }
02779 
02780             ptrCopy->CascadeDelete ();
02781             delete (ptrCopy);
02782         }
02783     }
02784     else    // were doing a make shapes (i.e.  become a .....)
02785     {   
02786         // get hold of the current path processor ....
02787         if (pParam->GetPathProcessor ())
02788         {
02789             ERROR2IF(!pParam->GetPathProcessor ()->IS_KIND_OF(SumAllPathsPathProcessor),
02790                      FALSE, "First path processor isn't a sum all paths path processor");
02791 
02792             // grab the path processor
02793             SumAllPathsPathProcessor * pProc = (SumAllPathsPathProcessor *)pParam->GetPathProcessor ();
02794 
02795             pProc->SetEnabled(FALSE);       // disable it
02796 
02797             // we can now get on and generate intermediate blend steps.  NOTE:  this node contour
02798             // controller should not be touched at all - since it is held within the tree
02799             // so we make a copy that we can play with ....
02800                 
02801             Node* copy = NULL;
02802             this->NodeCopy (&copy);
02803 
02804             NodeShadowController* ptrCopy = (NodeShadowController*) copy;
02805 
02806             if (!ptrCopy) { return (FALSE); }       // out of memory
02807 
02808             // find the contour
02809             NodeShadow * pShadowCopy = (NodeShadow *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
02810             ERROR2IF(pShadowCopy == NULL, FALSE, "Can't find the contour node");
02811 
02813 
02814             // now the fun begins, lest blend attributes for this particular blend step ....
02815             
02816             CCAttrMap * pMap = NULL;                        // if pEndControl is a contour as well
02817             CCAttrMap * pPrimaryShadowAttrMap = NULL;       // if pEndControl is not a contour
02818 
02819             // blend the contour's attributes
02820             NodeShadowController * pEndControl = (NodeShadowController *)FindAssociatedBlendNode(pParam);
02821 
02822             if (pEndControl)
02823             {
02824                 // we are blending two shadows YIPEE!       
02825                 
02826                 NodeShadow * pEndShadow = (NodeShadow *)pEndControl->FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
02827                 NodeRenderableInk * pOrigEndNode = pEndControl->GetBlendCreatedByNode();
02828 
02829                 // somethings a bit wrong if either of these fire ....
02830                 ERROR2IF(pOrigEndNode == NULL, FALSE, "Cant find the original contour node which this is based on");
02831                 ERROR2IF(!pOrigEndNode->IS_KIND_OF(NodeShadowController), FALSE, "Original node isn't a NodeShadowController");
02832 
02833                 pOrigEndNode = (NodeShadow *)pOrigEndNode->FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
02834 
02835                 BlendPath MyStartPath;
02836                 MyStartPath.Initialise((NodeRenderableInk *)pShadowCopy->PublicCopy(), -1, pShadowCopy, 1, 0, NULL);
02837                 
02838                 BlendPath MyEndPath;
02839                 MyEndPath.Initialise((NodeRenderableInk *)pEndShadow->PublicCopy(), -1, pOrigEndNode, 1, 0, NULL);
02840                 
02841                 BlendNodeParam BNParam;
02842                 BNParam.Init(pParam, &MyStartPath, &MyEndPath);
02843                 
02844                 pMap = new CCAttrMap(30);
02845 
02846                 BlendHelpers * pHelp = new BlendHelpers;
02847                 ERRORIF(pHelp == NULL, _R(IDE_NOMORE_MEMORY), FALSE);
02848                 
02849                 // now, perform the blend of the attributes
02850                 pHelp->BlendAttributes(&BNParam, pMap);
02851                 
02852                 delete pHelp;
02853 
02854                 // also blend my attributes & my node contour's attributes
02855                 ptrCopy->BlendParameters ((NodeShadowController*) m_pBlendCreatedByNode, (NodeShadowController*) pOrigEndNode->FindParent(), pParam, pMap);
02856             }
02857             else
02858             {
02859                 // were blending a shadowed node to a non-shadowed node.
02860                 // since we have not generated an attribute map, shadows that are generated
02861                 // render as white ....
02862 
02863                 // we need to make a attribute map for *this* NodeShadow, and apply it to
02864                 // the copy ....
02865 
02866                 NodeShadow * pContour = (NodeShadow *) FindFirstChild (CC_RUNTIME_CLASS (NodeShadow));
02867 
02868                 ERROR2IF (pContour == NULL, FALSE, "Can't find Contour node");
02869 
02870                 // build an attribute map for the contour
02871                 pPrimaryShadowAttrMap = CCAttrMap::MakeAppliedAttrMap(pContour);
02872 
02873                 // we don't have another node to blend to - but lets not let a little thing like that deter us! 
02874                 ptrCopy->BlendParameters ((NodeShadowController *) m_pBlendCreatedByNode, pParam);
02875             }
02876 
02877             // are we shadowing a path, or are we shadowing a compound?
02878 
02879             Node* newShadowing = GetShadowThisNode ();
02880             SetShadowThisNode (NULL);                   // ensure that we always reset this
02881 
02882             std::list<NodePath*> pathPtrs;              // a list of the new paths (so that we may call normaliseattributes)
02883 
02884             BOOL normaliseAttributes = FALSE;
02885 
02886             if (!newShadowing)          // were shadowing a path
02887             {
02888                 // scan for the path that original contour was generated with
02889                 NodePath* currentPath = (NodePath *)ptrCopy->FindFirstChild(CC_RUNTIME_CLASS(NodePath));
02890 
02891                 // if we find one, we know get on and merge all the paths so that the contour can
02892                 // be generated correctly.
02893 
02894                 // we do this by:
02895 
02896                 // replacing currentPath by a group that contains all paths on the path processors list
02897                 // (i.e.  we replace it by the blended paths - who have their blended attribute maps
02898                 // applied to them)
02899 
02900                 if (currentPath)
02901                 {
02902                     currentPath->CascadeDelete ();
02903                     delete (currentPath);
02904                     currentPath = NULL;
02905 
02906                     NodeGroup* newGroup = new NodeGroup ();
02907 
02908                     ListItem* pItem = pProc->GetList ()->GetHead ();
02909                     
02910                     Path* blendPath = NULL;
02911                     CCAttrMap* blendMap = NULL;
02912 
02913                     while (pItem)
02914                     {
02915                         blendPath = ((SumAllPathsElem *) pItem)->GetPath ();
02916                         blendMap = ((SumAllPathsElem *) pItem)->GetAttrMap ();
02917                         
02918                         NodePath* newPath = new NodePath ();
02919                         INT32 numCoords = blendPath->GetNumCoords ();
02920                         newPath->InkPath.ClearPath ();
02921                         newPath->InkPath.Initialise (numCoords);
02922                         newPath->InkPath.CopyPathDataFrom (blendPath);
02923                         blendMap->ApplyAttributesToNode (newPath);
02924                         newPath->AttachNode(newGroup, LASTCHILD);
02925 
02926                         pathPtrs.push_back (newPath);
02927                         
02928                         pItem = pProc->GetList ()->GetNext (pItem);
02929                         delete ((SumAllPathsElem *) pProc->GetList()->RemoveHead ());
02930                     }
02931                     
02932                     newGroup->AttachNode(pShadowCopy, NEXT);
02933                 }
02934             }
02935             else        // were shadowing a compound
02936             {
02937                 // remove the old compounds subtree
02938                 
02939                 Node* pShadowing = pShadowCopy->FindNext ();
02940 
02941                 pShadowing->CascadeDelete ();
02942                 delete (pShadowing);
02943 
02944                 // and replace with the new
02945                 
02946                 newShadowing->AttachNode (pShadowCopy, NEXT);
02947 
02948                 normaliseAttributes = TRUE;
02949             }
02950 
02951             // apply blended contour attributes ....
02952 
02953             if (pMap)
02954             {
02955                 pMap->ApplyAttributesToNode (pShadowCopy);
02956             }
02957 
02958             if (pPrimaryShadowAttrMap)
02959             {
02960                 pPrimaryShadowAttrMap->ApplyAttributesToNode (pShadowCopy);
02961             }
02962 
02963             // generate the contour (which will correctly be generated for merged paths if needed)
02964             
02965             pShadowCopy->GenerateShadow();
02966 
02967             // now insert into the tree or passback ....
02968 
02969             BecomeA* accessPtr = pParam->GetHandleBecomeA ()->GetBecomeA ();
02970             UndoableOperation* pUndoOp = accessPtr->GetUndoOp ();
02971 
02972             // are we wanting to be inserted into the tree?
02973             if (accessPtr->GetReason () == BECOMEA_REPLACE)
02974             {
02975                 if (pUndoOp)
02976                 {
02977                     if (!pUndoOp->DoInsertNewNode(ptrCopy,pParam->GetHandleBecomeA ()->GetContextNode (),PREV,TRUE,FALSE,FALSE,TRUE))
02978                     {
02979                         return FALSE;
02980                     }
02981                 }
02982                 else
02983                 {
02984                     ptrCopy->AttachNode(pParam->GetHandleBecomeA()->GetContextNode(), PREV);
02985                 }
02986 
02987                 // now normalise all attributes ....
02988                 
02989                 pShadowCopy->NormaliseAttributes ();
02990 
02991                 std::list<NodePath*>::iterator i;
02992 
02993                 for (i = pathPtrs.begin (); i != pathPtrs.end (); i++)
02994                     (*i)->NormaliseAttributes ();
02995 
02996                 if (normaliseAttributes)
02997                 {
02998                     // since were in this piece of code, we are shadowing a bevel or a contour.
02999                     // it is our resonsibility to normalise attribute complete NodePaths that
03000                     // were created by bevels or contours (ask yourself why?  because NodePaths
03001                     // generated in this fashion have NOT been inserted into the document tree
03002                     // yet, and NormaliseAttributes does NOT work on nodes that are NOT in the tree;
03003                     // making it impossible for our child bevel/contour to do the job).
03004 
03005                     NodePath* pPath = (NodePath*) SliceHelper::FindNextOfClass(ptrCopy, ptrCopy, CC_RUNTIME_CLASS (NodePath));
03006 
03007                     while (pPath)
03008                     {
03009                         pPath->NormaliseAttributes ();
03010                         pPath = (NodePath*) SliceHelper::FindNextOfClass(pPath, ptrCopy, CC_RUNTIME_CLASS (NodePath));
03011                     }
03012 
03013                     // same for NodeContours ....
03014 
03015                     NodeContour* pContour = (NodeContour*) SliceHelper::FindNextOfClass(ptrCopy, ptrCopy, CC_RUNTIME_CLASS (NodeContour));
03016 
03017                     while (pContour)
03018                     {
03019                         pContour->NormaliseAttributes ();
03020                         pContour = (NodeContour*) SliceHelper::FindNextOfClass(pContour, ptrCopy, CC_RUNTIME_CLASS (NodeContour));
03021                     }
03022 
03023                     // same for NodeBevels ....
03024 
03025                     NodeBevel* pBevel = (NodeBevel*) SliceHelper::FindNextOfClass(ptrCopy, ptrCopy, CC_RUNTIME_CLASS (NodeBevel));
03026 
03027                     while (pBevel)
03028                     {
03029                         pBevel->NormaliseAttributes ();
03030                         pBevel = (NodeBevel*) SliceHelper::FindNextOfClass(pBevel, ptrCopy, CC_RUNTIME_CLASS (NodeBevel));
03031                     }
03032                 }
03033 
03034                 // if we are required to insert paths into the tree - THEN we MUST call DoBecomeA on
03035                 // ptrCopy.  This call now also creates the bevel bitmap for us
03036 
03037                 if (accessPtr->GetInsertComplexBlendStepsAsPaths ())
03038                 {
03039                     ptrCopy->DoBecomeA (accessPtr);
03040                 }
03041             }
03042             // or do we just want our paths?
03043             else if (accessPtr->GetReason () == BECOMEA_PASSBACK)
03044             {
03045                 ptrCopy->DoBecomeA (accessPtr);
03046                 ptrCopy->SetBlenderNode (pParam->GetNodeBlend ());
03047 
03048 #ifdef _DEBUG
03049                 INT32 id = ++NodeShadowController::shadowLastBecomeAID;
03050                 ptrCopy->myShadowBecomeAID = id;
03051 //              char strId [100];
03052 //              wsprintf (strId, "Pushing NodeShadowController PASSBACK ID:  %i\n", ptrCopy->myShadowBecomeAID);
03053 //              TRACEUSER ("ChrisS", strId);
03054                 TRACEUSER ("ChrisS", _T("Pushing NodeShadowController PASSBACK ID:  %i\n"), ptrCopy->myShadowBecomeAID);
03055 #endif
03056                     
03057                 // setup necessary stuff so that we can delete ptrCopy at the relevant time ....
03058                     
03059                 if (AllocatedBlendConsList (LT_BECOMEA_SHADOWSLIST))
03060                 {   
03061                     BlendConsListInsert (LT_BECOMEA_SHADOWSLIST, ptrCopy);
03062                 }
03063                 else
03064                 {
03065                     AllocBlendConsList (LT_BECOMEA_SHADOWSLIST);
03066                     BlendConsListInsert (LT_BECOMEA_SHADOWSLIST, ptrCopy);
03067                 }
03068             }
03069 
03070             if (pMap)
03071             {
03072                 pMap->DeleteAttributes ();
03073                 delete pMap;
03074             }
03075 
03076             if (pPrimaryShadowAttrMap)
03077             {
03078                 delete pPrimaryShadowAttrMap;
03079             }
03080         }
03081     } 
03082     return TRUE;
03083 }
03084 
03085 
03086 
03087 
03088 /********************************************************************************************
03089 
03090 >   BOOL BlendParameters(NodeShadowController * pStart, NodeShadowController * pEnd,
03091                             double Scale, CCAttrMap* pMap)
03092 
03093     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03094     Created:    12/3/2000
03095     Inputs:     pStart  -   the original start shadow controller node (the one in the tree)
03096                 pEnd    -   the original end shadow controller node (the one in the tree)
03097                 Scale   -   Scale factor to blend with
03098     Outputs:    Sets my & my shadow node child with the appropriate variables
03099     Returns:    
03100     Purpose:    Sets my & my shadow node child with the appropriate variables given the blend
03101                 step
03102     
03103 ********************************************************************************************/
03104 BOOL NodeShadowController::BlendParameters(NodeShadowController * pStart, NodeShadowController * pEnd,
03105                             BlendNodeParam * pParam, CCAttrMap* pMap)
03106 {
03107     double Scale = pParam->GetAttrBlendRatio ();
03108     double Scale2 = pParam->GetBlendRatio ();
03109     
03110     // First, lets get hold of the shadows that we need, and check ....
03111     
03112     NodeShadow * pStartShad = (NodeShadow *) pStart->GetShadow ();
03113     ERROR2IF(!pStartShad, FALSE, "Can't find shadow child node for start");
03114 
03115     NodeShadow * pEndShad = (NodeShadow *) pEnd->GetShadow ();
03116     ERROR2IF(!pEndShad, FALSE, "Can't find shadow child node for end");
03117 
03118     NodeShadow * pThisShad = (NodeShadow *) GetShadow();
03119     ERROR2IF(!pThisShad, FALSE, "Can't find shadow child node for blend node");
03120 
03121     // firstly, lets blend the shadow darkness ....
03122     double startDarkness = pStartShad->GetDarkness();
03123     double endDarkness   = pEndShad->GetDarkness();
03124     double newDarkness   = startDarkness + Scale * (endDarkness - startDarkness);
03125     newDarkness =   (newDarkness < 0.0) ? 0.0 :
03126                     (newDarkness > 1.0) ? 1.0 : newDarkness;
03127 
03128     pThisShad->SetDarkness(newDarkness);
03129 
03130     // THEN blend the shadows colour ....
03131 
03132     AttrFillGeometry* pAttrStart = NULL, *pAttrEnd = NULL;
03133     ((NodeRenderableInk*)pStartShad)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry), (NodeAttribute **)&pAttrStart);
03134     ((NodeRenderableInk*)pEndShad)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry), (NodeAttribute **)&pAttrEnd);
03135 
03136     ERROR2IF(!pAttrStart, FALSE, "Error - could NOT find start shadow fill!");
03137     ERROR2IF(!pAttrEnd, FALSE, "Error - could NOT find end shadow fill!");
03138 
03139     AttrFillGeometry* pCurrentFill = NULL;
03140 
03141     // blend the fill attribute.
03142     if (((NodeRenderableInk*)pThisShad)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry), (NodeAttribute **)&pCurrentFill))
03143     {
03144         pCurrentFill->UnlinkNodeFromTree ();
03145         delete (pCurrentFill);
03146 
03147         // now create the new one ....
03148 
03149         BlendAttrParam BlendParam;
03150         
03151         // Initialise the BlendParam with the end attr and blend ratio
03152         if (BlendParam.Init(NULL, //pParam->GetRenderRegion(),
03153                             pAttrEnd,Scale,//BlendRatio,
03154                             COLOURBLEND_FADE,//pParam->GetColourBlendType(),
03155                             NULL, NULL))
03156         {
03157             // Successfully initialised, so now try blending the attributes
03158             if (pAttrStart->Blend(&BlendParam))
03159             {
03160                 // Attrs successfully blended, now get a ptr to the new attr.
03161                 // Once we get the blended attr ptr, it belongs to us, so we have
03162                 // to delete it when it is not needed
03163                 NodeAttribute* pBlendedNodeAttr = BlendParam.GetBlendedAttr();
03164 
03165                 if (pBlendedNodeAttr)
03166                 {
03167                     if (pMap)
03168                     {
03169                         delete (pMap->ReplaceAttribute (pBlendedNodeAttr));
03170                     }
03171                 }
03172             }
03173         }
03174     }
03175 
03176     // secondly, lets blend the shadow blur ....
03177 
03178     double StartBlur = (double)pStart->GetPenumbraWidth();
03179     double EndBlur   = (double)pEnd->GetPenumbraWidth();
03180     double NewBlur = ((EndBlur - StartBlur) * Scale) + StartBlur;
03181 
03182     SetPenumbraWidth ((MILLIPOINT)NewBlur);
03183 
03184     // thirdly, lets blend the profiles ....
03185 
03186     double StartBias = pStartShad->GetBiasGain().GetBias();
03187     double EndBias   = pEndShad->GetBiasGain().GetBias();
03188     double NewBias   = ((EndBias - StartBias) * Scale) + StartBias;
03189 
03190     double StartGain = pStartShad->GetBiasGain().GetGain();
03191     double EndGain   = pEndShad->GetBiasGain().GetGain();
03192     double NewGain   = ((EndGain - StartGain) * Scale) + StartGain;
03193 
03194     CProfileBiasGain    BiasGain( NewBias, NewGain );
03195     pThisShad->SetBiasGain( BiasGain );
03196 
03197     // we should use Scale2 (i.e.  object profile) to ammend the offsets ....
03198 
03199     // let's do the context sensitive stuff
03200     if (pStart->GetShadowType() == pEnd->GetShadowType())
03201     {
03202         switch (pStart->GetShadowType())
03203         {
03204         case SHADOWTYPE_WALL:
03205             {
03206                 // blend the offsets
03207                 double StartOffsetX = (double)pStart->GetOffsetX();
03208                 double EndOffsetX = (double)pEnd->GetOffsetX();
03209                 
03210                 double NewOffsetX = ((EndOffsetX - StartOffsetX) * Scale2) + StartOffsetX;
03211                 
03212                 double StartOffsetY = (double)pStart->GetOffsetY();
03213                 double EndOffsetY = (double)pEnd->GetOffsetY();
03214                 
03215                 double NewOffsetY = ((EndOffsetY - StartOffsetY) * Scale2) + StartOffsetY;
03216                 
03217                 // set my variables
03218                 m_OffsetX = (MILLIPOINT)NewOffsetX;
03219                 m_OffsetY = (MILLIPOINT)NewOffsetY;
03220             }
03221             break;
03222         case SHADOWTYPE_FLOOR:
03223             {
03224                 double StartAngle = (double)pStart->GetFloorShadowAngle();
03225                 double EndAngle   = (double)pEnd->GetFloorShadowAngle();
03226                 
03227                 double NewAngle = ((EndAngle - StartAngle) * Scale2) + StartAngle;
03228                 
03229                 double StartHeight = (double)pStart->GetFloorShadowHeight();
03230                 double EndHeight = (double)pEnd->GetFloorShadowHeight();
03231                 
03232                 double NewHeight = ((EndHeight - StartHeight) * Scale2) + StartHeight;
03233                 
03234                 // set my variables
03235                 m_FloorShadowAngle = NewAngle;
03236                 m_FloorShadowHeight = (double)NewHeight;
03237             }
03238             break;
03239         case SHADOWTYPE_GLOW:
03240             {
03241                 double StartGlowWidth = (double)pStart->GetGlowWidth();
03242                 double EndGlowWidth = (double)pEnd->GetGlowWidth();
03243                 
03244                 double NewGlowWidth = ((EndGlowWidth - StartGlowWidth) * Scale2) + StartGlowWidth;
03245                 
03246                 m_GlowWidth = (MILLIPOINT)NewGlowWidth;
03247             }
03248             break;
03249         case SHADOWTYPE_FEATHER:
03250             {
03251                 double StartFeatherWidth = (double)pStart->GetFeatherWidth();
03252                 double EndFeatherWidth = (double)pEnd->GetFeatherWidth();
03253                 
03254                 double NewFeatherWidth = ((EndFeatherWidth - StartFeatherWidth) * Scale2) + StartFeatherWidth;
03255                 
03256                 m_FeatherWidth = (MILLIPOINT)NewFeatherWidth;
03257             }
03258             break;
03259         default: break;
03260         }
03261     }
03262 
03263     return TRUE;
03264 }
03265 
03266 
03267 
03268 
03269 /********************************************************************************************
03270 
03271 >   BOOL NodeShadowController::BlendParameters(NodeShadowController * pStart, double Scale)
03272 
03273     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
03274     Created:    7/7/2000
03275     Inputs:     pStart  -   the original start contour controller node (the one in the tree)
03276                 Scale   -   Scale factor to blend with
03277     Outputs:    Sets my & my shadow node child with the appropriate variables
03278     Returns:    
03279     Purpose:    Sets my & my shadow node child with the appropriate variables given the blend
03280                 step
03281     
03282 ********************************************************************************************/
03283 
03284 BOOL NodeShadowController::BlendParameters(NodeShadowController * pStart, BlendNodeParam * pParam)
03285 {
03286     double Scale = pParam->GetAttrBlendRatio ();
03287     
03288     // First, lets get hold of the shadows that we need, and check ....
03289     
03290     NodeShadow * pStartShad = (NodeShadow *) pStart->GetShadow ();
03291     ERROR2IF(!pStartShad, FALSE, "Can't find shadow child node for start");
03292 
03293     NodeShadow * pThisShad = (NodeShadow *) GetShadow();
03294     ERROR2IF(!pThisShad, FALSE, "Can't find shadow child node for blend node");
03295 
03296     // firstly, lets blend the shadow darkness to fully transparent....
03297     double startDarkness = pStartShad->GetDarkness();
03298     double endDarkness   = 0.0;
03299     double newDarkness   = startDarkness + (2 * Scale) * (endDarkness - startDarkness);
03300     newDarkness =   (newDarkness < 0.0) ? 0.0 :
03301                     (newDarkness > 1.0) ? 1.0 : newDarkness;
03302 
03303     pThisShad->SetDarkness(newDarkness);
03304 
03305 
03306     // secondly, lets blend the shadow blur ....
03307 
03308     double StartBlur = (double)pStart->GetPenumbraWidth();
03309     double EndBlur   = 4 * 750;             // 4 Pixels
03310     double NewBlur = ((EndBlur - StartBlur) * (2*Scale)) + StartBlur;
03311 
03312     SetPenumbraWidth ((MILLIPOINT) NewBlur);
03313 
03314     // thirdly, lets blend the profiles ....
03315 
03316     CProfileBiasGain defaultBiasGain;
03317 
03318     double StartBias = pStartShad->GetBiasGain().GetBias();
03319     double EndBias   = defaultBiasGain.GetBias();
03320     double NewBias   = ((EndBias - StartBias) * (2*Scale)) + StartBias;
03321 
03322     double StartGain = pStartShad->GetBiasGain().GetGain();
03323     double EndGain   = defaultBiasGain.GetGain();
03324     double NewGain   = ((EndGain - StartGain) * (2*Scale)) + StartGain;
03325 
03326     CProfileBiasGain    BiasGain( NewBias, NewGain );
03327     pThisShad->SetBiasGain( BiasGain );
03328 /*
03329  *  Karim 18/09/2000
03330  *  Uncommented for a trial run to gauge 'public opinion' on this blend
03331  *  behaviour. Will comment back out if people dislike it :)
03332  *
03333  *  Karim 26/07/2000
03334  *  Delete this commented code if you don't like it :o)
03335  *  What it does is blend the wall-offset, floor-vector or glow width
03336  *  down to zero for a blend from a shadowed to unshadowed node.
03337  *  The down side is that the blend code does half the blend with start
03338  *  node and half with the end node, so that blending shadow-to-non gives
03339  *  intermediate shadows for only half the distance, which doesn't look good.
03340  */
03341     switch (pStart->GetShadowType())
03342     {
03343     case SHADOWTYPE_WALL:
03344         {
03345             // blend the offsets
03346             double StartOffsetX = (double)pStart->GetOffsetX();
03347             double EndOffsetX   = 0.0;
03348 
03349             double NewOffsetX = ((EndOffsetX - StartOffsetX) * (2*Scale)) + StartOffsetX;
03350 
03351             double StartOffsetY = (double)pStart->GetOffsetY();
03352             double EndOffsetY   = 0.0;
03353 
03354             double NewOffsetY = ((EndOffsetY - StartOffsetY) * (2*Scale)) + StartOffsetY;
03355 
03356             // set my variables
03357             m_OffsetX = (MILLIPOINT)NewOffsetX;
03358             m_OffsetY = (MILLIPOINT)NewOffsetY;
03359         }
03360         break;
03361     case SHADOWTYPE_FLOOR:
03362         {
03363             double StartAngle   = (double)pStart->GetFloorShadowAngle();
03364             double EndAngle     = 0.0;
03365 
03366             double NewAngle = ((EndAngle - StartAngle) * (2*Scale)) + StartAngle;
03367 
03368             double StartHeight  = (double)pStart->GetFloorShadowHeight();
03369             double EndHeight    = 0.0;
03370 
03371             double NewHeight = ((EndHeight - StartHeight) * (2*Scale)) + StartHeight;
03372 
03373             // set my variables
03374             m_FloorShadowAngle  = NewAngle;
03375             m_FloorShadowHeight = (double)NewHeight;
03376         }
03377         break;
03378     case SHADOWTYPE_GLOW:
03379         {
03380             double StartGlowWidth   = (double)pStart->GetGlowWidth();
03381             double EndGlowWidth     = 0.0;
03382 
03383             double NewGlowWidth = ((EndGlowWidth - StartGlowWidth) * (2*Scale)) + StartGlowWidth;
03384 
03385             m_GlowWidth = (MILLIPOINT)NewGlowWidth;
03386         }
03387         break;
03388     case SHADOWTYPE_FEATHER:
03389         {
03390             double StartFeatherWidth    = (double)pStart->GetFeatherWidth();
03391             double EndFeatherWidth      = 0.0;
03392 
03393             double NewFeatherWidth = ((EndFeatherWidth - StartFeatherWidth) * (2*Scale)) + StartFeatherWidth;
03394 
03395             m_FeatherWidth = (MILLIPOINT)NewFeatherWidth;
03396         }
03397         break;
03398     default:
03399         break;
03400     }
03401 
03402     return (TRUE);
03403 }
03404 
03405 
03406 
03407 /********************************************************************************************
03408 
03409 >   virtual NodeRenderableInk * 
03410             NodeShadowController::CreateTreeFromNodeToBlend(NodeRenderableInk * pNode)
03411 
03412     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03413     Created:    20/2/2000
03414     Inputs:     The node to wrap up
03415     Outputs:    NULL for failure
03416     Returns:    The root of the new tree
03417     Purpose:    Creates a copy of my tree, wrapping up the given node
03418     
03419 ********************************************************************************************/
03420 NodeRenderableInk * 
03421             NodeShadowController::CreateTreeFromNodeToBlend(NodeRenderableInk * pNode, 
03422             CCAttrMap * pAttrMap)
03423 {
03424     // remove all attributes from the node's subtree
03425     ERROR2IF(pNode->FindParent() != NULL, NULL, "Node shouldn't have parents");
03426 
03427     if (pNode->IsNodePath())
03428     {
03429         pNode->CascadeDelete();
03430     }
03431     
03432     // apply the attribute map to this node
03433     if (pNode->IsNodePath() && pAttrMap)
03434     {
03435         pNode->ApplyAttributes(pAttrMap, FALSE);
03436     }
03437 
03438     // now, lets make a copy of myself & the bevel node
03439     NodeShadowController * pControl = NULL;
03440 
03441     pControl = (NodeShadowController *)this->PublicCopy();
03442     ERRORIF(pControl == NULL, _R(IDE_NOMORE_MEMORY), NULL);
03443 
03444     NodeShadow * pShadow = (NodeShadow *)FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
03445     ERROR2IF(pShadow == NULL, NULL, "Can't find bevel node");
03446 
03447     // apply the attribute map of the original bevel node to the bevel copy node
03448     NodeShadow * pCopyShadow = (NodeShadow *)pShadow->PublicCopy();
03449     ERRORIF(pCopyShadow == NULL, _R(IDE_NOMORE_MEMORY), NULL);
03450     
03451     CCAttrMap * pShadowMap = CCAttrMap::MakeAppliedAttrMap(pShadow);
03452     ERRORIF(pShadowMap == NULL, _R(IDE_NOMORE_MEMORY), NULL);
03453 
03454 #ifdef _DEBUG
03455     CCAttrMap::iterator pos = pShadowMap->GetStartPosition();
03456     while( pos != pShadowMap->GetEndPosition() )
03457     {
03458         CCRuntimeClass *pKey;
03459         NodeAttribute  *pVal;
03460         pShadowMap->GetNextAssoc( pos, pKey, (PVOID &)pVal );
03461 
03462         INT32 i = 0;
03463         i++;
03464     }
03465 #endif
03466 
03467     pCopyShadow->ApplyAttributes(pShadowMap, FALSE);
03468 
03469     delete pShadowMap;
03470     
03471     pNode->AttachNode(pControl, FIRSTCHILD);
03472 
03473     pCopyShadow->AttachNode(pControl, FIRSTCHILD);
03474 
03475     return pControl;
03476 }
03477 
03478 
03479 
03480 /********************************************************************************************
03481 
03482 >   void NodeShadowController::SetFloorShadowVector(const DocCoord& dcControlVector)
03483 
03484     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03485     Created:    13/06/2000
03486     Inputs:     Floor-shadow control vector, in a DocCoord (millipoint units).
03487     Purpose:    Set the floor-shadow control vector.
03488                 This is defined as a vector from the centre of the base of the floor shadow
03489                 to its top-centre point.
03490 
03491                 The height and angle which this vector represents will be clipped into range
03492                 if they lie outside an acceptable range for floor-shadow parameters.
03493 
03494     SeeAlso:    MAXSHADOWHEIGHT, MAXSHADOWANGLE.
03495 
03496 ********************************************************************************************/
03497 void NodeShadowController::SetFloorShadowVector(const DocCoord& dcControlVector)
03498 {
03499     // the 'height' of the floor shadow is the ratio of the height of this vector
03500     // to the base height.
03501     INT32 BaseHeight = GetInsideBoundingRect().Height();
03502     double HeightRatio = 0.0;
03503     if (BaseHeight > 0)
03504         HeightRatio = (double)dcControlVector.y / (double)BaseHeight;
03505     else
03506         HeightRatio = 1.0;
03507 
03508     if (HeightRatio > MAXSHADOWHEIGHT)
03509         HeightRatio = MAXSHADOWHEIGHT;
03510     else if (HeightRatio < 0.01)
03511         HeightRatio = 0.01;
03512 
03513     SetFloorShadowHeight((float)HeightRatio);
03514 
03515     // the angle of the floor shadow is the clockwise rotation of the control vector
03516     // from the vertical, in degrees.
03517     double Angle = dcControlVector.AngleFromVertical();
03518     if (Angle < -MAXSHADOWANGLE)
03519         Angle = -MAXSHADOWANGLE;
03520     else if (Angle > MAXSHADOWANGLE)
03521         Angle = MAXSHADOWANGLE;
03522 
03523     SetFloorShadowAngle(Angle);
03524 }
03525 
03526 
03527 
03528 /********************************************************************************************
03529 
03530 >   DocCoord NodeShadowController::GetFloorShadowVector()
03531 
03532     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03533     Created:    13/06/2000
03534     Returns:    Floor-shadow control vector, in a DocCoord (millipoint units).
03535     Purpose:    Get the floor-shadow control vector.
03536                 This is defined as a vector from the centre of the base of the floor shadow
03537                 to its top-centre.
03538 
03539 ********************************************************************************************/
03540 DocCoord NodeShadowController::GetFloorShadowVector()
03541 {
03542     // height of the control vector is 'height' of the floor shadow * BaseHeight.
03543     // width of the control vector is tan(floor-shadow angle) * control vector height.
03544     INT32               BaseHeight = GetInsideBoundingRect().Height();
03545     INT32               Height = INT32(GetFloorShadowHeight() * BaseHeight);
03546     return DocCoord( INT32(Height * tan( GetFloorShadowAngle() )), Height);
03547 }
03548 
03549 
03550 
03551 /********************************************************************************************
03552 
03553 >   void NodeShadowController::SetWallShadowOffset( const DocCoord& dcOffset,
03554                                                     BOOL UpdateNodeShadow )
03555     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03556     Created:    19/06/2000
03557     Inputs:     dcOffset            Offset of the wall shadow, in millipoints.
03558                 UpdateNodeShadow    Whether or not the associated NodeShadow should be
03559                                     updated - should be TRUE in mose cases.
03560 
03561     Purpose:    Set the offset of the wall shadow from the shadowed object.
03562 
03563 ********************************************************************************************/
03564 void NodeShadowController::SetWallShadowOffset( const DocCoord& dcOffset,
03565                                                 BOOL UpdateNodeShadow )
03566 {
03567     // we're a bit sunk if we can't find our related NodeShadow.
03568     NodeShadow* pShadow = GetShadow();
03569     if (pShadow == NULL)
03570     {
03571         ERROR3("NodeShadowController::SetWallShadowOffset; can't find NodeShadow!");
03572         return;
03573     }
03574 
03575     // appropriately translate any fills or moveable attrs applied to our NodeShadow.
03576     Trans2DMatrix transAttrs(   dcOffset.x - m_OffsetX,
03577                                 dcOffset.y - m_OffsetY );
03578     pShadow->TransformChildren(transAttrs);
03579 
03580     // set our wall-shadow offset directly.
03581     m_OffsetX = dcOffset.x;
03582     m_OffsetY = dcOffset.y;
03583 
03584     // if necessary, update our render information.
03585     if (UpdateNodeShadow)
03586     {
03587         // we can avoid a regeneration at this point, by doing UpdateAfterWallOffset().
03588         // unfortunately, we do have to regenerate if one of our parents is a NodeBlend,
03589         // as the blended shadows must be regenerated correctly.
03590         Node* pBlendParent = FindParent();
03591         while (pBlendParent != NULL && !pBlendParent->IsABlend())
03592             pBlendParent = pBlendParent->FindParent();
03593 
03594         if (pBlendParent != NULL)
03595             RegenerateNode(NULL);
03596 
03597         else
03598             pShadow->UpdateAfterWallOffset();
03599     }
03600 }
03601 
03602 
03603 
03604 /********************************************************************************************
03605 
03606 >   DocCoord NodeShadowController::GetWallShadowOffset()
03607 
03608     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03609     Created:    19/06/2000
03610     Returns:    Offset of the wall shadow, in a DocCoord (millipoint units).
03611     Purpose:    Get the offset of the wall shadow from the shadowed object.
03612 
03613 ********************************************************************************************/
03614 DocCoord NodeShadowController::GetWallShadowOffset()
03615 {
03616     return DocCoord(m_OffsetX, m_OffsetY);
03617 }
03618 
03619 
03620 
03621 void NodeShadowController::PreExportRender(RenderRegion* pRegion)
03622 {
03623 #ifdef DO_EXPORT
03624     if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
03625     {
03626         // Output "start group" token
03627         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
03628         pDC->OutputToken(_T("u"));
03629         pDC->OutputNewLine();
03630     }
03631 PORTNOTE("cmx", "Removed use of CMXRenderRegion")
03632 #ifndef EXCLUDE_FROM_XARALX
03633     else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
03634     {
03635         // mark start of a group...
03636         CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC();
03637         DocRect BBox = GetBoundingRect();
03638         pDC->StartGroup(&BBox);
03639     }
03640 #endif
03641 #endif
03642 }
03643 
03644 BOOL NodeShadowController::ExportRender(RenderRegion* pRegion) 
03645 {
03646 #ifdef DO_EXPORT
03647     if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
03648     {
03649         // Output "end group" token
03650         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
03651         pDC->OutputToken(_T("U"));
03652         pDC->OutputNewLine();
03653         
03654         // Tell caller we rendered ourselves ok
03655         return TRUE;
03656     }
03657 PORTNOTE("cmx", "Removed use of CMXRenderRegion")
03658 #ifndef EXCLUDE_FROM_XARALX
03659     else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
03660     {
03661         // mark start of a group...
03662         CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC();
03663         pDC->EndGroup();
03664 
03665         return TRUE;
03666     }
03667 #endif
03668 #endif
03669     // Render thid node in the normal way
03670     return FALSE;
03671 }
03672 
03673 /********************************************************************************************
03674 
03675 >   void NodeShadowController::RenderTinyBlobs(RenderRegion* pRender)
03676 
03677     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
03678     Created:    23/6/94
03679     Inputs:     pRender - The region to draw the blobs in
03680     Purpose:    Renders the tiny blobs for a group (A Single blob on the topmost child object
03681                 in the group)
03682 
03683 ********************************************************************************************/
03684 
03685 void NodeShadowController::RenderTinyBlobs(RenderRegion* pRegion)
03686 {
03687 #if !defined(EXCLUDE_FROM_RALPH)
03688     // get the topmost object in this group and tell it to render its tiny blobs.
03689     Node* pNode = FindLastChild();
03690     while (pNode != NULL && !pNode->IsAnObject())
03691         pNode = pNode->FindPrevious();
03692 
03693     // empty groups are not allowed!
03694     if (pNode == NULL)
03695     {
03696         ERROR3("NodeShadowController::RenderTinyBlobs; This group is empty! Shouldn't be!");
03697         return;
03698     }
03699     else
03700     {
03701         ((NodeRenderableInk*)pNode)->RenderTinyBlobs(pRegion);
03702     }
03703 
03704 #endif
03705 }
03706 
03707 
03708 /********************************************************************************************
03709 >   virtual BOOL NodeShadowController::CompareState(NodeEffect* pPPNode)
03710 
03711     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03712     Created:    16/11/2004
03713     Returns:    TRUE if this state matches the state (and type) of the supplied node
03714     Purpose:    -
03715     Notes:      Would be more efficient to pass in level xml instead of getting it inside
03716                 this function
03717 
03718 ********************************************************************************************/
03719 
03720 BOOL NodeShadowController::CompareState(NodeEffect* pPPNode)
03721 {
03722     if (!pPPNode->IsAShadowController())
03723         return FALSE;
03724 
03725     NodeShadowController* pTest = (NodeShadowController*)pPPNode;
03726 
03727     // ?? Switch on TYPE before doing some of these tests?
03728     BOOL bSame = (m_OffsetX==pTest->m_OffsetX && m_OffsetY==pTest->m_OffsetY);
03729     bSame = bSame && (m_PenumbraWidth==pTest->m_PenumbraWidth);
03730     bSame = bSame && (m_sShadowName==pTest->m_sShadowName && m_ShadowType==pTest->m_ShadowType);
03731     bSame = bSame && (m_FloorShadowAngle==pTest->m_FloorShadowAngle && m_FloorShadowHeight==pTest->m_FloorShadowHeight);
03732     bSame = bSame && (m_Scale==pTest->m_Scale);
03733     bSame = bSame && (m_GlowWidth==pTest->m_GlowWidth);
03734     bSame = bSame && (m_FeatherWidth==pTest->m_FeatherWidth);
03735 
03736     if (bSame)
03737     {
03738         // Do the slower comparisons too
03739         NodeShadow* pShadow = GetShadow();
03740         NodeShadow* pTestShadow = pTest->GetShadow();
03741         bSame = bSame && (pShadow->GetBiasGain() == pTestShadow->GetBiasGain());
03742         bSame = bSame && (pShadow->GetDarkness() == pTestShadow->GetDarkness());
03743     }
03744     
03745     return (bSame);
03746 }
03747 
03748 
03749 
03750 
03751 /********************************************************************************************
03752 >   virtual BOOL NodeShadowController::CompareState(NodeEffect* pPPNode, ShadowFieldMask mask)
03753 
03754     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03755     Created:    16/05/2005
03756     Returns:    TRUE if this state matches the state (and type) of the supplied node
03757     Purpose:    -
03758     Notes:      Would be more efficient to pass in level xml instead of getting it inside
03759                 this function
03760 
03761 ********************************************************************************************/
03762 
03763 BOOL NodeShadowController::CompareState(NodeEffect* pPPNode, ShadowFieldMask mask)
03764 {
03765     if (!pPPNode->IsAShadowController())
03766         return FALSE;
03767 
03768     NodeShadowController* pTest = (NodeShadowController*)pPPNode;
03769 
03770     // ?? Switch on TYPE before doing some of these tests?
03771     BOOL bSame = TRUE;
03772     bSame = bSame && (!mask.OffsetX     || (m_OffsetX==pTest->m_OffsetX));
03773     bSame = bSame && (!mask.OffsetY     || (m_OffsetY==pTest->m_OffsetY));
03774     bSame = bSame && (!mask.Penumbra    || (m_PenumbraWidth==pTest->m_PenumbraWidth));
03775     bSame = bSame && (!mask.Name        || (m_sShadowName==pTest->m_sShadowName));
03776     bSame = bSame && (!mask.Type        || (m_ShadowType==pTest->m_ShadowType));
03777     bSame = bSame && (!mask.FloorAngle  || (m_FloorShadowAngle==pTest->m_FloorShadowAngle));
03778     bSame = bSame && (!mask.FloorHeight || (m_FloorShadowHeight==pTest->m_FloorShadowHeight));
03779     bSame = bSame && (!mask.Scale       || (m_Scale==pTest->m_Scale));
03780     bSame = bSame && (!mask.GlowWidth   || (m_GlowWidth==pTest->m_GlowWidth));
03781     bSame = bSame && (!mask.FeatherWidth    || (m_FeatherWidth==pTest->m_FeatherWidth));
03782 
03783     if (bSame)
03784     {
03785         // Do the slower comparisons too
03786         NodeShadow* pShadow = GetShadow();
03787         NodeShadow* pTestShadow = pTest->GetShadow();
03788         bSame = bSame && (!mask.BiasGain    || (pShadow->GetBiasGain() == pTestShadow->GetBiasGain()));
03789         bSame = bSame && (!mask.Darkness    || (pShadow->GetDarkness() == pTestShadow->GetDarkness()));
03790     }
03791     
03792     return (bSame);
03793 }
03794 
03795 
03796 
03797 
03798 /********************************************************************************************
03799 
03800 >   virtual void NodeShadowController::InvalidateBoundingRect(BOOL InvalidateChildBounds = FALSE)
03801 
03802     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03803     Created:    19/10/2005
03804     Inputs:     InvalidateChildBounds - When TRUE all children of the node have their
03805                 bounds invalidated as well (eg. applying a line attribute to a group)
03806 
03807     Purpose:    Marks the bounding rect of this node as invalid, as well as marking this nodes
03808                 parents as invalid as well. Next time a nodes GetBoundingRect() function is
03809                 called the bounding rect will be recalculated if needed.
03810     Note:       Shadow controller overrides this because if it is being told its bounds
03811                 are out of date that MUST mean that the shadow bounds are also out of date
03812                 (Invalidation usually means the shadowed nodes have changed and the shadow
03813                 ought to have new bounds in sympathy with them)
03814 
03815 ********************************************************************************************/
03816 
03817 void NodeShadowController::InvalidateBoundingRect(BOOL InvalidateChildBounds /* = FALSE */)
03818 {
03819     // Invalid this nodes bounding rect if we need to and the shadow bounding rect
03820     if (IsBoundingRectValid)
03821     {
03822         NodeShadow* pShadow = GetShadow();
03823         if (pShadow)
03824             pShadow->InvalidateBoundingRect(InvalidateChildBounds);
03825     }
03826 
03827     NodeEffect::InvalidateBoundingRect(InvalidateChildBounds);
03828 }
03829 
03830 
03831 
03832 #ifdef NEW_SHADOW_RENDER
03833 /********************************************************************************************
03834 
03835 >   SubtreeRenderState NodeShadowController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip)
03836 
03837     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03838     Created:    11/03/2005
03839     Inputs:     pRender - The region render into (or use as context for decisions about subtree)
03840                 ppNextNode - Address of node pointer for next node to render or run to after this
03841                 bClip - flag indicating whether to clip or not
03842     Purpose:    Do clever stuff on the way into a subtree, possibly modifying rendering
03843                 behaviour.
03844 
03845 ********************************************************************************************/
03846 
03847 SubtreeRenderState NodeShadowController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip)
03848 {
03849     BOOL bRendered = FALSE;
03850 
03851     if (pRender == NULL)                            // If no render region supplied, assume we need to be rendered
03852         return SUBTREE_ROOTANDCHILDREN;
03853 
03854     // If not effect attrs, work the legacy way, so that non-mix transparencies inside the shadowed
03855     // nodes work
03856     if (!IsCapturingChildren())
03857         return SUBTREE_ROOTANDCHILDREN;
03858 
03859     // Only the top ShadowController in a stack needs to capture anything...
03860     if (!IsTopShadowController())
03861         return SUBTREE_ROOTANDCHILDREN;
03862 
03863     // Let children render themselves directly if we're not showing filled paths so the user can
03864     // see their outlines
03865     if (pRender->RRQuality.GetFillQuality() <= Quality::NoFill)
03866         return SUBTREE_ROOTANDCHILDREN;
03867 
03868     CBitmapCache* pBitmapCache = Camelot.GetBitmapCache();
03869     if (pBitmapCache==NULL)
03870         return SUBTREE_NORENDER;
03871 
03872     // Find out whether we have a cached processed bitmap for this node...
03873     /*BOOL bFoundCached =*/ FindCachedEffect(pBitmapCache);
03874 
03875     // Go find out about my bounding rectangle
03876 //  // If we don't have a cached bitmap then expand the bounding rect to ensure it will cover an expansion caused by the effect
03877     // If we do have a cached bitmap then use the bounding rect stored with the processed bitmap
03878     DocRect BoundingRect = GetBoundingRect();                                   // This should include the shadow's bounds!
03879 //  if (!bFoundCached)
03880 //      BoundingRect.Inflate((INT32)(GetPixelWidth()*150));                     // Bodge to ensure LE is rendered before it grows
03881     DocRect ClipRect = pRender->GetClipRect();
03882 
03883     if (bClip && !ClipRect.IsIntersectedWith(BoundingRect)) // If not within the clipping rect then
03884         return SUBTREE_NORENDER;                    // Don't render us or our children
03885 
03886     // No need to check BackmostChanged node here because we never capture
03887     // our background in a 24BPP bitmap we always capture at 32BPP using cfLOCKEDTRANSPARENT
03888 
03889     // Now ask our derived classes to render what they need to render
03890     bRendered = RenderCachedEffect(pBitmapCache, pRender);
03891 
03892     if (bRendered)
03893     {
03894 TRACEUSER( "Phil", _T("NSC::RenderSubtree rendered cached @ %fdpi\n"), GetPixelsPerInch());
03895         return SUBTREE_NORENDER;
03896     }
03897 
03898     // Any sort of drag prevents new cacheing (to keep drags running smoothly)
03899 // No, shadows are quick enough to be recached during dragging...
03900 //  if (Operation::GetQuickRender(this))
03901 //      return SUBTREE_ROOTANDCHILDREN;
03902 
03903     // We couldn't find or render a cached bitmap then try to cache a new one
03904     double PixelWidth = GetPixelWidth();
03905 
03906     // Work out how much of the object we propose to capture
03907     // (This may change during the Capture if we have to fall back to 24BPP opaque capture)
03908     DocRect viewrect = ClipRect;
03909     DocRect CaptureRect = GetChildBoundingRect();           // Make sure we get our child bound not result of processing!
03910 
03911     // Inflate by one pixel all round to allow for anti-aliasing effects at the edges
03912 //      CaptureRect.Inflate((INT32)PixelWidth);
03913 
03914     // Only cache if it's worth it!
03915     if (CaptureRect.Width()>=PixelWidth || CaptureRect.Height()>=PixelWidth)
03916     {
03917 TRACEUSER( "Phil", _T("NSC::RenderSubtree Capturing cached @ %fdpi\n"), GetPixelsPerInch());
03918         CaptureFlags caFlags = CaptureFlags(cfLOCKEDTRANSPARENT | cfFULLCOVERAGE);
03919         pRender->StartCapture(this, CaptureRect, CAPTUREINFO(ctNESTABLE, caFlags), TRUE, FALSE, GetPixelsPerInch());
03920     }
03921 
03922     return bRendered ? SUBTREE_NORENDER : SUBTREE_ROOTANDCHILDREN;                  // Else we must render ourselves and our children
03923 }
03924 
03925 
03926 
03927 
03928 /********************************************************************************************
03929 
03930 >   void NodeShadowController::RenderAfterSubtree(RenderRegion* pRender)
03931 
03932     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03933     Created:    07/09/2004
03934     Inputs:     pRender - The region to render into
03935     Purpose:    Capture the group as a cached bitmap
03936 
03937 ********************************************************************************************/
03938 
03939 void NodeShadowController::RenderAfterSubtree(RenderRegion* pRender)
03940 {
03941     Capture* pCapture = pRender->GetTopCapture();
03942     if (pCapture==NULL)                                         // If nothing was captured
03943         return;                                                 // Then do nothing
03944 
03945     CBitmapCache* pBitmapCache = Camelot.GetBitmapCache();
03946     if (pBitmapCache==NULL)
03947     {
03948         return;
03949     }
03950 
03951     // Only stop the capture if we started it
03952     // (means we only capture whole subtrees at the mo.)
03953     if (pCapture->GetOwner()==this)
03954     {
03955         // End this capture:
03956         // Blit capture to screen
03957         // Retain bitmap because we will release it ourselves onyl if we fail to cache it
03958         BOOL bCached = FALSE;
03959         LPBITMAPINFO lpInfo = NULL;
03960         LPBYTE lpBits = NULL;
03961         DocRect CaptureRect = GetChildBoundingRect();               // Set maximum size we allow
03962         pRender->StopCapture(this, FALSE, FALSE, &lpInfo, &lpBits, &CaptureRect);
03963 
03964         // If the capture gave us back a bitmap, try to cache it
03965         if (lpInfo && lpBits && CaptureRect.IsValid())
03966         {
03967             // Cache the ORIGINAL bitmap as Option 0
03968             // See also, SetOriginalBitmap
03969             double PixelWidth = GetPixelWidth();
03970             CBitmapCacheKey inky(this, PixelWidth, 0);
03971 
03972             CCachedBitmap cbmp;
03973 
03974             cbmp.pbmpBits = lpBits;
03975             cbmp.pbmpInfo = lpInfo;
03976             cbmp.SetCachedRect(CaptureRect);
03977             cbmp.nPriority = CACHEPRIORITY_TEMPBITMAP_HIGH;
03978 
03979             if (cbmp.IsValid())
03980             {
03981                 pBitmapCache->StoreBitmap(inky, cbmp);
03982                 bCached = TRUE;
03983             }
03984 
03985             RenderCachedEffect(pBitmapCache, pRender);
03986 
03987         }
03988 
03989         // If we failed to cache the captured bitmap then release it
03990         if (lpInfo!=NULL && lpBits!=NULL && !bCached)
03991         {
03992             FreeDIB(lpInfo, lpBits, NULL, FALSE);
03993         }
03994     }
03995 }
03996 
03997 
03998 
03999 
04000 /***********************************************************************************************
04001 
04002 >   virtual void NodeShadowController::Render(RenderRegion* pRRegion)
04003 
04004     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04005     Created:    13/12/2004
04006     Inputs:     Pointer to a render region
04007     Purpose:    Will render the liveeffect iff we have it cached - for hit detection reasons
04008 
04009 ***********************************************************************************************/
04010 
04011 // NOTE: No need for a render function because this effect does not add any pixel data
04012 // to those rendered by its children - just let the children render themselves into
04013 // the hit-detection bitmap
04014 //void NodeShadowController::Render(RenderRegion* pRender)
04015 //{
04016 //  CBitmapCache* pBitmapCache = Camelot.GetBitmapCache();
04017 //  if (pBitmapCache!=NULL && pRender->IsHitDetect() && IsCapturingChildren())
04018 //  {
04019 //      RenderCachedEffect(pBitmapCache, pRender);
04020 //  }
04021 //}
04022 
04023 
04024 
04025 
04026 /********************************************************************************************
04027 
04028 >   virtual BOOL NodeShadowController::FindCachedEffect(CBitmapCache* pBitmapCache)
04029 
04030     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04031     Created:    24/11/2004
04032     Inputs:     -
04033     Outputs:    -
04034     Purpose:    Find all cached items this node needs to call RenderCachedEffect
04035                 succesfully
04036 
04037 ********************************************************************************************/
04038 
04039 BOOL NodeShadowController::FindCachedEffect(CBitmapCache* pBitmapCache)
04040 {
04041     CBitmapCacheKey inky(this, GetPixelWidth(), 0);                     // Get cached BMP for this ORIGINAL node at our dpi
04042     CCachedBitmap cbmp;
04043     BOOL bFoundCached = pBitmapCache->Lookup(inky, cbmp);
04044 
04045     return bFoundCached;
04046 }
04047 
04048 
04049 
04050 
04051 /********************************************************************************************
04052 
04053 >   BOOL NodeShadowController::RenderCachedEffect(CBitmapCache* pBitmapCache, RenderRegion* pRender)
04054 
04055     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04056     Created:    24/11/2004
04057     Inputs:     -
04058     Outputs:    -
04059     Purpose:    Render the cached version of this node
04060     Notes:      When several shadow effects are applied on top of each other, it is desirable
04061                 to avoid showing shadows of shadows - that is unrealistic.
04062                 To do that, only the top ShadowController does any rendering, those underneath
04063                 suspend rendering and rely on the top ShadowController to build and render
04064                 their Shadow nodes for them.
04065     SeeAlso:    NodeShadowController::RenderSubtree
04066 
04067 ********************************************************************************************/
04068 
04069 BOOL NodeShadowController::RenderCachedEffect(CBitmapCache* pBitmapCache, RenderRegion* pRender)
04070 {
04071     BOOL bRendered = FALSE;
04072 
04073     CBitmapCacheKey inky(this, GetPixelWidth(), 0);                     // Get cached BMP for this ORIGINAL node at our dpi
04074     CCachedBitmap cbmp;
04075     BOOL bFoundCached = pBitmapCache->Lookup(inky, cbmp);
04076 
04077     // Render the Shadow first...
04078     NodeShadow* pShadow = GetShadow();
04079     if (pShadow && bFoundCached)
04080     {
04081         double PixelWidth = GetPixelWidth();
04082 
04083         // This is clever...
04084         // Only render shadows if this is the top-most controller in the stack.
04085         // In that case, go down the stack and render all the shadows that have not yet been
04086         // rendered.
04087         // This means that shadows do not generate shadows!
04088         if (this->IsTopShadowController())
04089         {
04090             LPBITMAPINFO lpInfo = NULL;
04091             LPBYTE lpBits = NULL;
04092             DocRect CaptureRect;
04093 
04094             pShadow = FindBottomShadow();
04095             while (pShadow)
04096             {
04097                 if (bFoundCached && pShadow->NeedsRemake())
04098                 {
04099                     // Now that we know one of our shadows needs to be regenerated
04100                     // We can decide whether we need to create a "silhouette" bitmap for it 
04101                     if (lpInfo==NULL || lpBits==NULL)
04102                     {
04103                         // Grab this bitmap and its effect attribute into a new bitmap by rendering them into a capture
04104                         // This combined new bitmap will be used as the silhouette bitmap to generate ALL shadows
04105                         CaptureFlags caFlags = CaptureFlags(cfLOCKEDTRANSPARENT | cfPIXWIDTHSCALE);
04106                         CaptureRect = cbmp.GetCachedRect();
04107                         pRender->StartCapture(this, CaptureRect, CAPTUREINFO(ctNESTABLE, caFlags), TRUE, FALSE, 72000.0/PixelWidth);
04108                         pRender->RenderBits(cbmp.pbmpInfo, cbmp.pbmpBits, &cbmp.coord0, 3, TRUE, this);
04109                         pRender->StopCapture(this, FALSE, FALSE, &lpInfo, &lpBits, &CaptureRect);
04110                     }
04111 
04112                     if (lpBits && lpInfo)
04113                         pShadow->MakeShadow(lpInfo, lpBits, CaptureRect);
04114                 }
04115     
04116                 // Render this shadow
04117                 pShadow->RenderShadow(pRender);
04118 
04119                 pShadow = FindShadowAbove(pShadow);
04120             }
04121 
04122             // If we had to make a silhouette bitmap, get rid of it now
04123             if (lpInfo && lpBits)
04124 				::FreeDIB(lpInfo, lpBits);
04125         }
04126     }
04127 
04128     // Render any effect attributes that are attached to us
04129     // along with the bitmap so that they affect its appearance
04130     if (bFoundCached)
04131     {
04132         bRendered = pRender->RenderBits(cbmp.pbmpInfo, cbmp.pbmpBits, &cbmp.coord0, 3, TRUE, this);
04133     }
04134 
04135     return bRendered;
04136 }
04137 
04138 BOOL NodeShadowController::IsTopShadowController()
04139 {
04140     Node* pParent = FindParent();
04141     return (pParent==NULL || !pParent->IsAShadowController());
04142 }
04143 
04144 BOOL NodeShadowController::IsInShadowStack()
04145 {
04146     Node* pNode = FindParent();
04147     if (pNode && pNode->IsAShadowController())
04148         return TRUE;
04149 
04150     pNode = GetInkNodeFromController();
04151     if (pNode && pNode->IsAShadowController())
04152         return TRUE;
04153 
04154     return FALSE;
04155 }
04156 
04157 NodeShadowController* NodeShadowController::FindBottomShadowController()
04158 {
04159     Node* pChild = GetInkNodeFromController();
04160     NodeShadowController* pBottomController = this;
04161 
04162     while (pChild && pChild->IsAShadowController())
04163     {
04164         pBottomController = (NodeShadowController*)pChild;
04165         pChild = pBottomController->GetInkNodeFromController();
04166     }
04167 
04168     return pBottomController;
04169 }
04170 
04171 NodeShadow* NodeShadowController::FindBottomShadow()
04172 {
04173     NodeShadowController* pBottomController = this;
04174     pBottomController = FindBottomShadowController();
04175 
04176     if (pBottomController)
04177         return pBottomController->GetShadow();
04178     else
04179         return NULL;
04180 }
04181 
04182 NodeShadow* NodeShadowController::FindShadowAbove(NodeShadow* pCurrentShadow)
04183 {
04184     Node* pController = pCurrentShadow->GetParentController();
04185     pController = pController->FindParent();
04186     if (pController==NULL || !pController->IsAShadowController())
04187         return NULL;
04188     else
04189         return ((NodeShadowController*)pController)->GetShadow();
04190 }
04191 
04192 void NodeShadowController::ReleaseStackedShadows()
04193 {
04194     NodeShadow* pShadow = FindBottomShadow();
04195     while (pShadow)
04196     {
04197         pShadow->ReleaseCached(FALSE, TRUE, TRUE, TRUE);
04198 
04199         pShadow = FindShadowAbove(pShadow);
04200     }
04201 }
04202 
04203 
04204 
04205 
04206 /********************************************************************************************
04207 
04208 >   virtual double NodeShadowController::GetPixelsPerInch()
04209 
04210     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04211     Created:    09/09/2004
04212     Inputs:     - 
04213     Outputs:    Pixel resolution for this live effect bitmap in pixels per inch (double)
04214     Retuns:     Description of the group node 
04215     Purpose:    To get the required/stored pixel res of this node
04216     Errors:     -
04217     SeeAlso:    -
04218 
04219 ********************************************************************************************/
04220 
04221 double NodeShadowController::GetPixelsPerInch()
04222 //{
04223 //  // During dragging, reduce resolution to speed up rendering
04224 //  if (IsDragged())
04225 //      return 96.0;
04226 //
04227 //  return (double)NodeShadowController::DefaultPixelsPerInch;
04228 //}
04229 {
04230 //  if (IsDragged())
04231 //      return 96.0;
04232 //
04233     double dPPI = 0.0;
04234 
04235     if (NodeBitmapEffect::ShadowPixelsPerInch==0)
04236     {
04237         // 0 means automatic
04238         // Automatic means get resolution in context
04239         // Context means either respond to a fixed resolution above us in the tree
04240         // or get resolution from current view
04241         View* pView = View::GetCurrent();
04242         double dViewPPI = 96.0;
04243         if (pView)
04244             dViewPPI = 72000.0 / pView->GetPixelWidth().MakeDouble();
04245 
04246         Node* pParent = this->FindParent();
04247         while (pParent && !pParent->IsLayer())
04248         {
04249             if (pParent->IsBitmapEffect())
04250             {
04251                 dPPI = ((NodeBitmapEffect*)pParent)->GetPixelsPerInchValue();
04252                 if (dPPI!=0)
04253                     break;
04254 
04255                 // If we found an effect at all then ensure we use default effect PPI
04256                 dPPI = dViewPPI;
04257             }
04258 
04259             pParent = pParent->FindParent();
04260         }
04261 
04262         // If we found no effects above us then try to operate at zoomed view res
04263         // for best results on screen.
04264         if (dPPI==0)
04265         {
04266             // See also OpEffectLock::DoEffectNodeOp
04267             // We should capture at at least 200dpi for printing
04268             // We should capture at a size that's at least as good as the current zoomed pixel size
04269             // (We rely on the capture being clipped to keep bitmap sizes under control at high zoom factors)
04270             // (We should reduce the resolution if the bitmap will be huge...)
04271             if (pView)
04272             {
04273                 dPPI = 72000.0 / pView->GetPixelWidth().MakeDouble();
04274                 double dScaledPPI = 72000.0 / pView->GetScaledPixelWidth().MakeDouble();
04275 
04276                 // Cap zoomed resolution at 4 * view's native resolution
04277                 if (dScaledPPI > dPPI*4.0)
04278                     dScaledPPI = dPPI*4.0;
04279 
04280                 // If zoomed resolution > native resolution then use zoomed else use native
04281                 if (dPPI < dScaledPPI)
04282                     dPPI = dScaledPPI;
04283             }
04284         }
04285     }
04286 
04287     if (dPPI==0)
04288         dPPI = NodeBitmapEffect::ShadowPixelsPerInch;
04289 
04290     if (dPPI==0)
04291     {
04292         // Shouldn't ever reach this clause but just in case...
04293         ERROR3("GetPixelsPerInch can't get sensible PPI so defaulting to 96");
04294         dPPI = 96.0;
04295     }
04296 
04297     return dPPI;
04298 };
04299 
04300 
04301 
04302 
04303 /********************************************************************************************
04304 
04305 >   virtual double NodeShadowController::GetPixelWidth()
04306 
04307     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04308     Created:    08/09/2004
04309     Inputs:     - 
04310     Outputs:    Pixel width for live effect bitmap in millipoints (double)
04311     Retuns:     Description of the group node 
04312     Purpose:    To get the required/stored pixel width of this node
04313     Errors:     -
04314     SeeAlso:    -
04315 
04316 ********************************************************************************************/
04317 
04318 double NodeShadowController::GetPixelWidth()
04319 {
04320     // During dragging, reduce resolution to speed up rendering
04321     return 72000.0 / GetPixelsPerInch();
04322 }
04323 
04324 
04325 
04326 
04327 /********************************************************************************************
04328 
04329 >   virtual BOOL NodeShadowController::ReleaseCached(BOOL bAndParents = TRUE,
04330                                                      BOOL bAndChildren = TRUE,
04331                                                      BOOL bSelf = TRUE,
04332                                                      BOOL bAndDerived = TRUE)
04333 
04334     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04335     Created:    15/03/2005
04336     Returns:    TRUE if captured data was cached
04337     Purpose:    Protected Helper function
04338                 Use the CaptureManager to capture the results of rendering, cache them
04339                 and associate them with this Ink node
04340     SeeAlso:    NodeRenderableInk::RenderCached, CaptureCached
04341 
04342 ********************************************************************************************/
04343 
04344 BOOL NodeShadowController::ReleaseCached(BOOL bAndParents, BOOL bAndChildren, BOOL bSelf, BOOL bAndDerived)
04345 {
04346     if (!IsCapturingChildren())
04347     {
04348         NodeShadow * pShadow = (NodeShadow *)FindFirstChild(CC_RUNTIME_CLASS(NodeShadow));
04349         if (pShadow && bAndDerived && !IsDragged())
04350         {
04351             pShadow->DeleteCache();
04352         }
04353 
04354         return NodeEffect::ReleaseCached(bAndParents, bAndChildren, bSelf, bAndDerived);
04355     }
04356 
04357     // Don't release cached data if the release request is the result of transformation
04358     // re-rendering because we can transform our cached data!
04359     if (!IsDragged())
04360     {
04361         CBitmapCache* pBitmapCache = Camelot.GetBitmapCache();
04362         if (pBitmapCache!=NULL && bSelf)
04363         {
04364             CBitmapCacheKey inky(this, 42);
04365             pBitmapCache->RemoveAllOwnedBitmaps(inky, FALSE, CACHEPRIORITY_PERMANENT);
04366         }
04367 
04368         // Forcibly remove my Shadow child's cached data as well
04369         // because he is really just an offshoot of me and has no children of his own
04370         // so he won't normally be released unless I do it.
04371         if (bAndDerived)
04372         {
04373             ReleaseStackedShadows();
04374 //          NodeShadow* pShadow = this->GetShadow();
04375 //          if (pShadow)
04376 //          {
04377 //              pShadow->ReleaseCached(FALSE, TRUE, TRUE, TRUE);
04378 //          }
04379         }
04380     }
04381 
04382     // If we should release our children's caches as well...
04383     if (bAndChildren)
04384     {
04385         Node* pChild = FindFirstChild();
04386         while (pChild)
04387         {
04388             if (pChild->IsBounded())
04389                 ((NodeRenderableBounded*)pChild)->ReleaseCached(FALSE, TRUE, TRUE, TRUE);
04390 
04391             pChild = pChild->FindNext();
04392         }
04393     }
04394 
04395     // If I can't be cached, neither can my parent...
04396     if (bAndParents && FindParent() && FindParent()->IsBounded())
04397         ((NodeRenderableBounded*)FindParent())->ReleaseCached(TRUE, FALSE, TRUE, TRUE);
04398 
04399     return TRUE;
04400 }
04401 
04402 
04403 
04404 
04405 /********************************************************************************************
04406 
04407 >   virtual BOOL NodeShadowController::IsCapturingChildren()
04408 
04409     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04410     Created:    15/03/2005
04411     Inputs:     - 
04412     Outputs:    -
04413     Retuns:     TRUE if this shadow works by capturing its children into a bitmap
04414                      (required if the shadow has effect attributes applied to it)
04415                 FALSE if not (typcially when the children use non-mix transparency)
04416     Purpose:    To determine the rendering mode of this shadow
04417     Errors:     -
04418     SeeAlso:    -
04419 
04420 ********************************************************************************************/
04421 
04422 BOOL NodeShadowController::IsCapturingChildren()
04423 {
04424     // TODO: Factor in non-mix transparency test in here?
04425     // Shadows should be faster & more controlled if they use the capture system
04426     return HasEffectAttrs() || IsInShadowStack();
04427 }
04428 
04429 
04430 
04431 
04432 /********************************************************************************************
04433 
04434 >   virtual DocRect NodeShadowController::GetChildBoundingRect(BOOL bIncludeAttrs = TRUE)
04435 
04436     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04437     Created:    21/03/2005
04438     Returns:    The bounding box of the children of the live effect
04439     Purpose:    Get the bounding rect of the children without updating BOundingRectangle
04440                 or IsBoundingRectValid for this node...
04441                 Ignore shadows if this is topshadow node and child is a shadowcontroller
04442 
04443 ********************************************************************************************/
04444 
04445 DocRect NodeShadowController::GetChildBoundingRect(BOOL bIncludeAttrs)
04446 {
04447     NodeShadowController* pController = FindBottomShadowController();
04448     return pController->GetShadowedNode()->GetBoundingRect(!bIncludeAttrs);
04449     
04450 /*  // just set it to be an empty rectangle
04451     DocRect BoundRect(0,0,0,0);
04452 
04453     Node* pNode = FindFirstChild();
04454     while (pNode!=NULL)
04455     {
04456         // Add in the bounding rect of this node with all the others
04457         if (pNode->IsBounded())
04458             BoundRect = BoundRect.Union(((NodeRenderableBounded*)pNode)->GetBoundingRect(!bIncludeAttrs));
04459 
04460         // And find the next node
04461         pNode = pNode->FindNext();
04462     }
04463 
04464     return BoundRect;
04465 */
04466 }
04467 
04468 
04469 
04470 
04471 #endif
04472 
04473 
04474 
04475 
04477 // class ShadowNodeTreeFactory: public CCObject
04478 // implementation
04479 /********************************************************************************************
04480 
04481 >   ShadowNodeTreeFactory::ShadowNodeTreeFactory()
04482 
04483     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04484     Created:    10/8/99
04485     Inputs:     
04486     Returns:    
04487     Purpose:    Constructor
04488 
04489 ********************************************************************************************/
04490 ShadowNodeTreeFactory::ShadowNodeTreeFactory()
04491 {
04492     m_ShadowType        =   SHADOWTYPE_NONE;
04493     m_OffsetX           =   0;
04494     m_OffsetY           =   0;
04495     m_PenumbraWidth     =   0;
04496     m_FloorShadowAngle  =   0;
04497     m_FloorShadowHeight =   0;
04498     m_Scale             =   0;
04499     m_GlowWidth         =   0;
04500     m_FeatherWidth          =   0;
04501 }
04502 
04503 /********************************************************************************************
04504 
04505 >   ShadowNodeTreeFactory::~ShadowNodeTreeFactory()
04506 
04507     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04508     Created:    10/8/99
04509     Inputs:     
04510     Returns:    
04511     Purpose:    Destructor
04512 
04513 ********************************************************************************************/
04514 ShadowNodeTreeFactory::~ShadowNodeTreeFactory()
04515 {
04516     NodeListItem * pItem = (NodeListItem *)m_AttributeList.GetHead();
04517 
04518     while (pItem)
04519     {
04520         delete pItem->pNode;
04521         pItem->pNode = NULL;
04522 
04523         pItem = (NodeListItem *)m_AttributeList.GetNext(pItem);
04524     }
04525     
04526     m_AttributeList.DeleteAll();
04527 }
04528 
04529 /********************************************************************************************
04530 
04531 >   void ShadowNodeTreeFactory::SetUpAttributeList(Node * pShadow);
04532 
04533     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04534     Created:    10/8/99
04535     Inputs:     The node to copy the attribute map from
04536     Returns:    
04537     Purpose:    Sets up the attribute map for the appropriate node
04538 
04539 ********************************************************************************************/
04540 void ShadowNodeTreeFactory::SetUpAttributeList(Node * pShadow)
04541 {
04542     NodeListItem * pItem = NULL;
04543 
04544     // first, set up the attribute map
04545     CCAttrMap *pMap = CCAttrMap::MakeAppliedAttrMap((NodeRenderableInk *)pShadow);
04546 
04547     CCAttrMap::iterator pos = pMap->GetStartPosition();
04548 
04549     NodeAttribute * pAttrNode = NULL;
04550     
04551     // run through the map, creating copies of the attributes and adding them to the list
04552     while( pos != pMap->GetEndPosition() )
04553     {
04554         CCRuntimeClass *pKey;
04555         void           *pVal;
04556         pMap->GetNextAssoc(pos, pKey, pVal);
04557 
04558         pAttrNode = ((NodeAttribute *)pVal);
04559 
04560         pItem = new NodeListItem;
04561 
04562         if (!pItem)
04563         {
04564             ERROR3("Can't create NodeListItem");
04565             return;
04566         }
04567 
04568         pAttrNode->NodeCopy(&(pItem->pNode));
04569 
04570         m_AttributeList.AddTail(pItem);
04571     }
04572 
04573     delete pMap;
04574     pMap = NULL;
04575 }
04576 
04577 /********************************************************************************************
04578 
04579 >   NodeCompound *ShadowNodeTreeFactory::CreateNode(List *pList, UndoableOperation * pOp = NULL)
04580 
04581     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04582     Created:    9/8/99
04583     Inputs:     The list of nodes to shadow, and an undoable operation (if necessary for 
04584                 undo to work)
04585     Returns:    The compound node created (NULL for failure)
04586     Purpose:    Given the list of nodes, shadows them with my parameters
04587 
04588 ********************************************************************************************/
04589 NodeCompound * ShadowNodeTreeFactory::CreateNode(List *pList, UndoableOperation * pOp /* = NULL*/)
04590 {
04591     NodeShadowController * pControl = NULL;
04592     NodeShadow           * pShadow  = NULL;
04593 
04594     NodeListItem * pItem = NULL;
04595     NodeListItem * pAttrItem = NULL;
04596 
04597 //  Node * pInsertNode = NULL;
04598 
04599     BOOL ok = TRUE;
04600     
04601     if (pOp)
04602     {
04603         // first, create the shadow controller node and the shadow node
04604         ALLOC_WITH_FAIL(pControl, (new NodeShadowController), pOp);
04605         ALLOC_WITH_FAIL(pShadow, (new NodeShadow), pOp);
04606 
04607         // move the nodes in the list to be children of the shadow controller node
04608         pItem = (NodeListItem *)pList->GetHead();
04609 
04610         // insert the controller into the tree
04611         ok = pOp->DoInsertNewNode(pControl, (Node *)pItem->pNode, NEXT, TRUE);
04612 
04613         while (pItem && ok)
04614         {
04615             ok = pOp->DoMoveNode(pItem->pNode, pControl, LASTCHILD);
04616 
04617             pItem = (NodeListItem *)pList->GetNext(pItem);
04618         }
04619 
04620         // set up the fill attributes for the shadow node
04621         pAttrItem = (NodeListItem *)m_AttributeList.GetHead();
04622 
04623         while (pAttrItem)
04624         {
04625             if (((NodeAttribute *)pAttrItem->pNode)->CanBeAppliedToObject())
04626             {
04627                 pAttrItem->pNode->CopyNode(pShadow, LASTCHILD);
04628             }
04629 
04630             pAttrItem = (NodeListItem *)m_AttributeList.GetNext(pAttrItem);
04631         }       
04632 
04633         // now all nodes are moved, insert the shadow node
04634         pOp->DoInsertNewNode(pShadow, pControl, FIRSTCHILD, TRUE);
04635 
04636         // set all the variables on the shadow node
04637         pControl->SetPenumbraWidth(this->m_PenumbraWidth);
04638         pControl->SetOffsetX(m_OffsetX);
04639         pControl->SetOffsetY(m_OffsetY);
04640         pControl->SetGlowWidth(m_GlowWidth);
04641         pControl->SetFeatherWidth(m_FeatherWidth);
04642         pControl->SetFloorShadowAngle(m_FloorShadowAngle);
04643         pControl->SetFloorShadowHeight(m_FloorShadowHeight);
04644         pControl->SetWallShadowScale(m_Scale);
04645         pControl->SetShadowType(m_ShadowType);
04646 
04647         // create shadow bitmaps & data
04648         pControl->RegenerateNode(NULL, FALSE);
04649 
04650         pOp->DoInvalidateNodeRegion(pControl, TRUE);
04651     }
04652 
04653     if (!ok)
04654     {
04655         return NULL;
04656     }
04657     
04658     return pControl;
04659 }
04660 
04661 #endif  // BUILDSHADOWS

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