ndclpcnt.cpp

Go to the documentation of this file.
00001 // $Id: ndclpcnt.cpp 1361 2006-06-25 16:43:38Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 //
00099 // Implementation of NodeClipViewController.
00100 //
00101 
00102 #include "camtypes.h"   // pre-compiled header
00103 #include "ndclpcnt.h"   // header file
00104 //#include "clipres.h"  // ClipView resources
00105 #include "nodeclip.h"   // so we can use NodeClipView and PathBecomeA
00106 
00107 //#include "cxfrec.h"       // save/load - in camtypes.h [AUTOMATICALLY REMOVED]
00108 #include "cxftags.h"    //
00109 //#include "cxfdefs.h"  // - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "camfiltr.h" // - in camtypes.h [AUTOMATICALLY REMOVED]
00111 
00112 //#include "becomea.h"  // make shapes stuff - in camtypes.h [AUTOMATICALLY REMOVED]
00113 
00114 #include "extender.h"   // extend stuff
00115 //#include "ngcore.h"
00116 
00117 #include "nodepath.h"   // NodePath reference
00118 //#include "node.h"     // for PreRenderChildren stuff - in camtypes.h [AUTOMATICALLY REMOVED]
00119 #include "gdraw.h"      // for GDraw calls
00120 //#include "mario.h"        // for _R(IDE_NOMORE_MEMORY)
00121 
00122 #include "ppbevel.h"    // for BevelHelpers functions
00123 #include "attrmap.h"    // AttrMap reference
00124 
00125 #include "blobs.h"      // for blob rendering.
00126 
00127 #include "objchge.h"    // for ObjChangeType.
00128 
00129 #include "lineattr.h"   // for AttrStrokeColour.
00130 
00131 //#include "docview.h"  // for DocView. - in camtypes.h [AUTOMATICALLY REMOVED]
00132 #include "osrndrgn.h"   // for OSRenderRegion.
00133 
00134 #include "selector.h"   // for the SelectorTool.
00135 //#include "spread.h"       // for class Spread - in camtypes.h [AUTOMATICALLY REMOVED]
00136 
00137 #include "pbecomea.h"   // for PathBecomeA.
00138 
00139 #include "saveeps.h"    // for EPSRenderRegion and EPSExportDC
00140 #include "cmxrendr.h"   // for CMXRenderRegion
00141 #include "ophist.h"
00142 //#include "cmxexdc.h"  // for CMXExportDC
00143 
00144 DECLARE_SOURCE("$Revision: 1361 $");
00145 
00146 // dynamic class creation stuff.
00147 CC_IMPLEMENT_DYNCREATE(NodeClipViewController, NodeGroup)
00148 CC_IMPLEMENT_DYNCREATE(ClipViewRecordHandler, CamelotRecordHandler)
00149 CC_IMPLEMENT_DYNCREATE(UpdateCachedKeyholePathAction, Action)
00150 
00151 // Declare smart memory handling in Debug builds
00152 #define new CAM_DEBUG_NEW
00153 
00154 // some constants we require for half-decent path clipping.
00155 const INT32 NodeClipViewController::CLIPVIEW_TOLERANCE      = 1;
00156 const INT32 NodeClipViewController::CLIPVIEW_SOURCEFLATNESS = 50;
00157 const INT32 NodeClipViewController::CLIPVIEW_CLIPFLATNESS   = 50;
00158 
00159 /********************************************************************************************
00160 
00161 >   NodeClipViewController::NodeClipViewController()
00162 
00163     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00164     Created:    28 January 2000
00165     Purpose:    Default constructor.
00166     Errors: 
00167     See also:   
00168 
00169 ********************************************************************************************/
00170 NodeClipViewController::NodeClipViewController() : NodeGroup()
00171 {
00172     // state initialisation.
00173     m_KeyholePath.Initialise();
00174     MarkKeyholeInvalid();
00175 }
00176 
00177 
00178 
00179 /********************************************************************************************
00180 
00181 >   NodeClipViewController::NodeClipViewController( Node* pContextNode,
00182                                                     AttachNodeDirection Direction,
00183                                                     BOOL Locked     = FALSE,
00184                                                     BOOL Mangled    = FALSE,
00185                                                     BOOL Marked     = FALSE,
00186                                                     BOOL Selected   = FALSE )
00187     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00188     Created:    28 January 2000
00189     Inputs:     pContextNode    points to the node to which to attach this node.
00190                 Direction       the direction in which this node is to be attached to 
00191                                 pContextNode. Possible values:
00192 
00193                 PREV        Attach node as a previous sibling of the context node.
00194                 NEXT        Attach node as a next sibling of the context node.
00195                 FIRSTCHILD  Attach node as the first child of the context node.
00196                 LASTCHILD   Attach node as a last child of the context node.
00197 
00198                 Locked      is node locked?
00199                 Mangled     is node mangled?
00200                 Marked      is node marked?
00201                 Selected    is node selected?
00202 
00203     Purpose:    This method initialises the node and links it to pContextNode in the
00204                 direction specified by Direction. All necessary tree links are updated.
00205                 Most of the work is carried out by base constructors.
00206 
00207     Errors:     An ENSURE error will occur if pContextNode is NULL.
00208     See also:   
00209 
00210 ********************************************************************************************/
00211 NodeClipViewController::NodeClipViewController( Node* pContextNode,  
00212                                                 AttachNodeDirection Direction,  
00213                                                 BOOL Locked, 
00214                                                 BOOL Mangled,  
00215                                                 BOOL Marked, 
00216                                                 BOOL Selected )
00217                         : NodeGroup(pContextNode, Direction,
00218                                     Locked, Mangled, Marked, Selected) 
00219 {
00220     // state initialisation.
00221     m_KeyholePath.Initialise();
00222     MarkKeyholeInvalid();
00223 }
00224 
00225 
00226 
00227 /********************************************************************************************
00228 
00229 >   NodeClipViewController::~NodeClipViewController()
00230 
00231     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00232     Created:    28 January 2000
00233     Purpose:    Destructor.
00234     Errors: 
00235     See also:   
00236 
00237 ********************************************************************************************/
00238 NodeClipViewController::~NodeClipViewController()
00239 {
00240     m_KeyholePath.ClearPath();
00241 }
00242 
00243 
00244 
00245 /********************************************************************************************
00246 
00247 >   void NodeClipViewController::GetDebugDetails(StringBase* pStr)
00248 
00249     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00250     Created:    5/25/00
00251     Inputs:     pStr    ptr to the string to hold the debug output.
00252     Outputs:    pStr gets our debug output put in it.
00253     Purpose:    Get Debug-tree details for this node.
00254                 In the case of NCVC, we output our current clipping-path data.
00255     Notes:      Only _implemented_ in DEBUG builds.
00256 
00257 ********************************************************************************************/
00258 void NodeClipViewController::GetDebugDetails(StringBase* pStr)
00259 {
00260 #ifdef _DEBUG
00261     // Get base class debug info.
00262     NodeGroup::GetDebugDetails(pStr);
00263 
00264     // output the data for our keyhole path.
00265     String_256 TempStr;
00266     (*pStr) += TEXT( "\r\nClipping Path Data\r\n" );
00267     m_KeyholePath.GetDebugDetails(pStr);
00268 #endif
00269 }
00270 
00271 
00272 
00273 /********************************************************************************************
00274 
00275 >   NodeClipView* NodeClipViewController::GetClipView(BOOL ReportErrors = TRUE)
00276 
00277     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00278     Created:    18 February 2000
00279     Inputs:     ReportErrors    if set to FALSE then this method will not ERROR3 if it
00280                                 cannot find the clipview node.
00281     Returns:    The ClipView node which is associated with this NodeClipViewController, or
00282                 NULL if unsuccessful.
00283     Purpose:    Get this controller's clipview node.
00284     Errors:     ERROR3 if no relevant NodeClipView is found.
00285     See also:   
00286 
00287 ********************************************************************************************/
00288 NodeClipView* NodeClipViewController::GetClipView(BOOL ReportErrors)
00289 {
00290     Node* pClipView = FindFirstChild();
00291     while (pClipView != NULL && !pClipView->IsANodeClipView())
00292         pClipView = pClipView->FindNext();
00293 
00294     ERROR3IF(ReportErrors && pClipView == NULL, "NCVC has no ClipView node!");
00295 
00296     return (NodeClipView*)pClipView;
00297 }
00298 
00299 
00300 
00301 /********************************************************************************************
00302 
00303 >   BOOL NodeClipViewController::OwnsAsKeyholeNode(const NodeRenderableInk* pTestNode)
00304 
00305     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00306     Created:    09/05/2000
00307     Inputs:     pTestNode   the possible keyhole node.
00308     Returns:    TRUE if pTestNode is a keyhole node for us,
00309                 FALSE otherwise.
00310     Purpose:    Test whether the given node is a keyhole node for this NCVC.
00311     Errors:     In debug, ERROR3 if pTestNode is NULL.
00312                 In release, just return with FALSE.
00313     See also:   
00314 
00315 ********************************************************************************************/
00316 BOOL NodeClipViewController::OwnsAsKeyholeNode(const Node* pTestNode)
00317 {
00318     // input validation.
00319     if (pTestNode == NULL)
00320     {
00321         ERROR3("NCVC::OwnsAsKeyholeNode; NULL entry parameter!");
00322         return FALSE;
00323     }
00324 
00325     // Ensure keyhole path is valid (helps hit-detection)
00326     UpdateKeyholePath();
00327 
00328     // ok, any ink-node to the left of our NodeClipView is a keyhole node.
00329     Node* pKeyhole  = NULL;
00330     Node* pClipView = GetClipView();
00331     BOOL bFoundKeyhole = FALSE;
00332     if (pClipView != NULL)
00333     {
00334         pKeyhole = pClipView->FindPrevious();
00335         while (!bFoundKeyhole && pKeyhole != NULL)
00336         {
00337             bFoundKeyhole = (pKeyhole->IsAnObject() && pTestNode == pKeyhole);
00338             pKeyhole = pKeyhole->FindPrevious();
00339         }
00340     }
00341 
00342     return bFoundKeyhole;
00343 }
00344 
00345 
00346 
00347 /********************************************************************************************
00348 
00349 >   virtual DocRect NodeClipViewController::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
00350 
00351     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00352     Created:    28 January 2000
00353     Inputs:     DontUseAttrs    TRUE of you want to ignore all the node's attributes.
00354                 HitTest         TRUE if being called during a hit-test.
00355     Returns:    The bounding rectangle of this node.
00356 
00357     Purpose:    Find this node's bounding rectangle. If the rectangle is known to be valid 
00358                 then it is simply returned. If IsBoundingRectValid() is FALSE then the rect 
00359                 is recalculated before it is returned and the validity flag reset.
00360 
00361                 The bounding rect of a NCVC is the union of the bounds of all its keyhole
00362                 nodes.
00363 
00364     See also:   GetBlobBoundingRect().
00365 
00366 ********************************************************************************************/
00367 DocRect NodeClipViewController::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest)
00368 {
00369 // DEBUG:
00370 //  TRACEUSER( "Karim", _T("NCVC::GetBoundingRect\n"));
00371 
00372     // ensure our cached keyhole path is up to date.
00373 //  UpdateKeyholePath();
00374 
00375     // start off with a zeroed bounding rect, and union it with the bounds of all keyhole
00376     // nodes, ie ink-nodes to the left of our NodeClipView.
00377     BoundingRectangle = DocRect(0, 0, 0, 0);
00378     NodeClipView* pClipView = GetClipView();
00379     if (pClipView != NULL)
00380     {
00381         Node* pKeyhole = pClipView->FindPrevious();
00382         while (pKeyhole != NULL)
00383         {
00384             if (pKeyhole->IsAnObject())
00385             {
00386                 IsBoundingRectValid = TRUE;
00387                 BoundingRectangle = BoundingRectangle.Union(((NodeRenderableInk*)pKeyhole)->GetBoundingRect(DontUseAttrs, HitTest));
00388             }
00389             pKeyhole = pKeyhole->FindPrevious();
00390         }
00391     }
00392 
00393     return BoundingRectangle;
00394 }
00395 
00396 
00397 
00398 /********************************************************************************************
00399 
00400 >   virtual DocRect NodeClipViewController::GetBlobBoundingRect()
00401 
00402     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00403     Created:    28 January 2000
00404     Returns:    The bounding rectangle of this node with its blobs drawn.
00405     Purpose:    Get this NCVC's bounding rectangle when its blobs are drawn.
00406     See also:   GetBoundingRect();
00407 
00408 ********************************************************************************************/
00409 DocRect NodeClipViewController::GetBlobBoundingRect()
00410 {
00411     // we return our bounding rectangle, inflated to account for whichever blobs we're
00412     // currently showing.
00413     DocRect drBounds = GetBoundingRect();
00414     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
00415     if (pBlobMgr != NULL)
00416     {
00417         DocCoord Low    = drBounds.LowCorner();
00418         DocCoord High   = drBounds.HighCorner();
00419 
00420         BlobStyle bsBlobs = pBlobMgr->GetCurrentInterest(TRUE);
00421         if (bsBlobs.Object)
00422         {
00423             DocRect drBlobSize;
00424             pBlobMgr->GetBlobRect(Low, &drBlobSize);
00425             drBounds = drBounds.Union(drBlobSize);
00426             pBlobMgr->GetBlobRect(High, &drBlobSize);
00427             drBounds = drBounds.Union(drBlobSize);
00428             pBlobMgr->GetBlobRect(DocCoord(Low.x, High.y), &drBlobSize);
00429             drBounds = drBounds.Union(drBlobSize);
00430             pBlobMgr->GetBlobRect(DocCoord(High.x, Low.y), &drBlobSize);
00431             drBounds = drBounds.Union(drBlobSize);
00432         }
00433 
00434         if (bsBlobs.Tiny)
00435         {
00436             DocRect drBlobSize;
00437             pBlobMgr->GetBlobRect(DocCoord(Low.x, High.y), &drBlobSize);
00438             drBounds = drBounds.Union(drBlobSize);
00439         }
00440 
00441         if (bsBlobs.ToolObject)
00442         {
00443             DocRect drBlobSize;
00444             pBlobMgr->GetBlobRect(DocCoord(0, 0), &drBlobSize);
00445             drBounds.Inflate(drBlobSize.Width(), drBlobSize.Height());
00446         }
00447     }
00448 
00449     // this well-named method includes the blob-bounds of any child-attrs,
00450     // but only if we're showing fill blobs (!!?!?).
00451     IncludeChildrensBoundingRects(&drBounds);
00452 
00453     return drBounds;
00454 }
00455 
00456 
00457 
00458 /********************************************************************************************
00459 
00460 >   virtual DocRect NodeClipViewController::GetEorDragBoundingRect()
00461 
00462     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00463     Created:    03 April 2000
00464     Returns:    The bounding rect of this ClipView group to draw when dragging it around,
00465                 or a zeroed DocRect if we have no keyhole nodes (a *bad* thing).
00466     Purpose:    The rectangle calculated is the union of EorDragBoundingRect()'s for each
00467                 of our keyhole nodes.
00468     Errors:     
00469     See also:   
00470 
00471 ********************************************************************************************/
00472 DocRect NodeClipViewController::GetEorDragBoundingRect()
00473 {
00474     // start off with a zeroed bounding rect, and union it with the bounds of all keyhole
00475     // nodes, ie ink-nodes to the left of our NodeClipView.
00476     DocRect drBounds(0, 0, 0, 0);
00477     NodeClipView* pClipView = GetClipView();
00478     if (pClipView != NULL)
00479     {
00480         Node* pKeyhole = pClipView->FindPrevious();
00481         while (pKeyhole != NULL)
00482         {
00483             if (pKeyhole->IsAnObject())
00484                 drBounds = drBounds.Union(((NodeRenderableInk*)pKeyhole)->GetEorDragBoundingRect());
00485 
00486             pKeyhole = pKeyhole->FindPrevious();
00487         }
00488     }
00489 
00490     return drBounds;
00491 }
00492 
00493 
00494 
00495 /********************************************************************************************
00496 
00497 >   virtual SubtreeRenderState NodeClipViewController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode = NULL, BOOL bClip = TRUE)
00498 
00499     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00500     Created:    15 February 2000
00501     Inputs:     
00502     Outputs:    
00503     Returns:    PRE_RENDER_CHILDREN.
00504     Purpose:    Called by the main rendering loop before this node's children are rendered, 
00505                 usually to allow us to muck around with how we want our children to render.
00506 
00507                 It is used here to clear up any debris left over from an unfinished hit-test 
00508                 render-loop.
00509     Errors:     
00510     See also:   
00511 
00512 ********************************************************************************************/
00513 SubtreeRenderState NodeClipViewController::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip)
00514 {
00515     if (pRender && !pRender->IsPrinting())
00516     {
00517         // ensure our cached keyhole path is up to date.
00518         UpdateKeyholePath();
00519 
00520 //      return SUBTREE_ROOTANDCHILDREN;
00521         return NodeGroup::RenderSubtree(pRender, ppNextNode, bClip);
00522     }
00523     else
00524 //      return SUBTREE_ROOTANDCHILDREN;
00525         return NodeGroup::RenderSubtree(pRender, ppNextNode, bClip);
00526 
00527 //  return SUBTREE_ROOTANDCHILDREN;
00528 }
00529 
00530 
00531 
00532 /********************************************************************************************
00533 
00534 >   virtual void NodeClipViewController::RenderEorDrag(RenderRegion* pRender)
00535 
00536     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00537     Created:    28 January 2000
00538     Inputs:     pRender     pointer to the render region to render into.
00539     Purpose:    Render this node as eor-blobs into the given render region.
00540 
00541                 What we probably want to do is clip the outlines of all our children, and
00542                 render those outlines into the given render region, however for now we'll
00543                 just render our keyhole node's outline.
00544     Errors: 
00545     See also:   See Render() for info.
00546 
00547 ********************************************************************************************/
00548 void NodeClipViewController::RenderEorDrag(RenderRegion* pRender)
00549 {
00550     // <RANT>
00551     // unfortunately, if we opt to do eor-drag-rendering for our kids (and we do),
00552     // then as well as getting to render for them when we're dragged around, we also
00553     // get lumbered with rendering for them when *they* get dragged around.
00554     // this is annoying and the solution should *not* be to do their rendering
00555     // for them if we're not selected, but that's what we're gonna do.
00556     // </RANT>
00557 
00558     // determine whether myself or one of my ancestors is selected.
00559     BOOL fMeOrMyAncestorIsSelected = IsSelected();
00560     Node* pParent = FindParent();
00561     while (!fMeOrMyAncestorIsSelected && pParent != NULL)
00562     {
00563         fMeOrMyAncestorIsSelected = pParent->IsSelected();
00564         pParent = pParent->FindParent();
00565     }
00566 
00567     // do normal eor-drag-rendering for myself (eor-rendering our keyhole path.)
00568     if (fMeOrMyAncestorIsSelected)
00569     {
00570         UpdateKeyholePath();
00571         pRender->DrawPath(&m_KeyholePath);
00572     }
00573 
00574     // do eor-drag-rendering for those of my lazy children which are selected.
00575     RenderEorDragSelectedChildren(this, pRender);
00576 }
00577 
00578 /********************************************************************************************
00579 
00580 >   virtual void NodeClipViewController::RenderEorDragSelectedChildren(Node* pParent,
00581                                                                        RenderRegion* pRender)
00582     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00583     Created:    13/04/2000
00584     Inputs:     
00585     Outputs:    
00586     Returns:    
00587     Purpose:    Recursively render EOR outlines for those of my children who are selected.
00588                 Note that if we meet a selected child which claims to render its children
00589                 then we let it do so, otherwise we use RenderEorDragChildren() to forcibly
00590                 render its kids.
00591 
00592     Notes:      I get the feeling this vfn should really live in NodeRenderableInk, so if
00593                 things work out then I'll put it there.
00594 
00595     Errors:     
00596     See also:   RenderEorDragChildren() for something similar...
00597 
00598 ********************************************************************************************/
00599 void NodeClipViewController::RenderEorDragSelectedChildren( Node* pParent,
00600                                                             RenderRegion* pRender )
00601 {
00602     NodeRenderableInk* pInkKid = NULL;
00603     Node* pKid = pParent->FindFirstChild();
00604     while (pKid != NULL)
00605     {
00606         if (pKid->IsAnObject())
00607         {
00608             pInkKid = (NodeRenderableInk*)pKid;
00609 
00610             // ok, is it selected? if so, render its drag blobs, taking account of bossy
00611             // controller nodes who want to render their children themselves.
00612             if (pInkKid->IsSelected())
00613             {
00614                 pInkKid->RenderEorDrag(pRender);
00615                 if (!pInkKid->ChildrenAreEorDragRenderedByMe())
00616                     pInkKid->RenderEorDragChildren(pRender);
00617             }
00618 
00619             // nope, not selected - recurse down into it if possible.
00620             else
00621             {
00622                 RenderEorDragSelectedChildren(pInkKid, pRender);
00623             }
00624         }
00625         pKid = pKid->FindNext();
00626     }
00627 }
00628 
00629 
00630 
00631 /********************************************************************************************
00632 
00633 >   virtual BOOL NodeClipViewController::ChildrenAreEorDragRenderedByMe()
00634 
00635     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00636     Created:    04 April 2000
00637     Inputs:     
00638     Outputs:    
00639     Returns:    TRUE.
00640     Purpose:    Tells everyone else that I want to be responsible for rendering the drag
00641                 blobs for my children.
00642     Errors:     
00643     See also:   
00644 
00645 ********************************************************************************************/
00646 BOOL NodeClipViewController::ChildrenAreEorDragRenderedByMe()
00647 {
00648     return TRUE;
00649 }
00650 
00651 
00652 
00653 /********************************************************************************************
00654 
00655 >   virtual void NodeClipViewController::RenderTinyBlobs(RenderRegion* pRender)
00656 
00657     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00658     Created:    06 April 2000
00659     Inputs:     pRender     the render-region to render into.
00660     Purpose:    Render our tiny-blob.
00661                 This consists of a solitary blob at the top-left of our bounding rect.
00662 
00663 ********************************************************************************************/
00664 void NodeClipViewController::RenderTinyBlobs(RenderRegion* pRender)
00665 {
00666     // Get our bounding rect, and from it the position of our tiny blob.
00667     DocRect drBounds = GetBoundingRect();
00668     DocCoord dcTinyPos = DocCoord(drBounds.lo.x, drBounds.hi.y);
00669 
00670     // Set our blob's colours and render it.
00671     pRender->SetFillColour(COLOUR_UNSELECTEDBLOB);
00672     pRender->SetLineColour(COLOUR_NONE);
00673     pRender->DrawBlob(dcTinyPos, BT_UNSELECTED);
00674 }
00675 
00676 /********************************************************************************************
00677 
00678 >   virtual void NodeClipViewController::RenderToolObjectBlobs(RenderRegion* pRender)
00679 
00680     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00681     Created:    23 March 2000
00682     Inputs:     pRender     ptr to the region to render into.
00683     Purpose:    Render our tool-object blobs.
00684 
00685 ********************************************************************************************/
00686 void NodeClipViewController::RenderToolObjectBlobs(RenderRegion* pRender)
00687 {
00688     RenderClipViewBlobs(pRender);
00689 }
00690 
00691 
00692 
00693 /********************************************************************************************
00694 
00695 >   virtual void NodeClipViewController::RenderClipViewBlobs(RenderRegion* pRender)
00696 
00697     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00698     Created:    23 March 2000
00699     Inputs:     pRender     the region to render to.
00700     Purpose:    Render the blobs specifically used by ClipView groups.
00701                 These are currently as follows:
00702                 1.  A keyhole-selection blob, which is rendered at the top-left of our
00703                     bounding rectangle.
00704                 2.  A select-contained-objects blob, which is rendered in the centre of the
00705                     group's bounding rectangle.
00706     Errors:     
00707     See also:   
00708 
00709 ********************************************************************************************/
00710 void NodeClipViewController::RenderClipViewBlobs(RenderRegion* pRender)
00711 {
00712     // quit immediately unless the current tool is the selector tool.
00713     Tool* pTool = Tool::GetCurrent();
00714     if (pTool == NULL || pTool->GetID() != TOOLID_SELECTOR)
00715         return;
00716 
00717     // get the blob manager and set the colours we'll be using.
00718 //  BlobManager* pBlobManager = GetApplication()->GetBlobManager();
00719     pRender->SetLineColour(COLOUR_NONE);
00720     pRender->SetFillColour(COLOUR_UNSELECTEDBLOB);
00721 
00722     // plot the keyhole blob at the top-left of our bounding rectangle.
00723     DocRect drBounds = GetBoundingRect();
00724     DocCoord dcKeyhole(drBounds.lo.x, drBounds.hi.y);
00725     pRender->DrawBitmapBlob(dcKeyhole, _R(IDBMP_KEYHOLE));
00726 
00727     // plot the select-inside blob at the centre of our bounding rectangle.
00728     pRender->DrawBitmapBlob(drBounds.Centre(), _R(IDBMP_CLIPVIEW_SELECT_CONTENTS));
00729 
00730     // BODGE CODE!
00731     // some of the ClipView blobs are rendered in the same position as our tiny blobs.
00732     // we don't want the tiny blobs to interfere with these blobs, so we double-render
00733     // them off the picture (double XOR = 0) whenever these blobs are rendered.
00734     // this relies on Tiny blobs always being rendered whenever these blobs
00735     // are rendered (which they have always been up till 06/04/2000)
00736     RenderTinyBlobs(pRender);
00737 }
00738 
00739 /********************************************************************************************
00740 
00741 >   virtual BOOL NodeClipViewController::OnClick(   DocCoord dcClickPos, ClickType cType,
00742                                                     ClickModifiers cMods, Spread* pSpread )
00743     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00744     Created:    23 March 2000
00745     Inputs:     dcClickPos      where the click occurred.
00746                 cType           what sort of click it was.
00747                 cMods           what modifier keys were pressed at the time.
00748                 pSpread         the spread the click occurred on.
00749     Returns:    TRUE if we could handle,
00750                 FALSE if it was just too much for us.
00751 
00752     Purpose:    Handle any click-messages passed to us from a tool, eg if we want to handle
00753                 clicks on our object or tool-object blobs.
00754                 Currently, you can click on an NCVC tool-object blob and either select its
00755                 keyhole node, or select everything *except* its keyhole node. At some point
00756                 in future, you will also be able to directly drag everything except the
00757                 keyhole node (instead of clicking once to select, _then_ dragging).
00758 
00759 ********************************************************************************************/
00760 BOOL NodeClipViewController::OnClick(   DocCoord dcClickPos, ClickType cType,
00761                                         ClickModifiers cMods, Spread* pSpread )
00762 {
00763 // DEBUG
00764 //  TRACEUSER( "Karim", _T("NCVC::OnClick\n"), (DWORD)cType);
00765 
00766     // determine what to do with the click.
00767     // note that we can't do anything unless we have valid pointers to view and blob manager.
00768     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
00769     DocView* pDocView = DocView::GetSelected();
00770     if (pBlobMgr == NULL || pDocView == NULL)
00771         return FALSE;
00772 
00773     // currently we only specifically handle ToolObject blobs.
00774     BlobStyle bsCurrentInterest = pBlobMgr->GetCurrentInterest();
00775     if (bsCurrentInterest.ToolObject)
00776     {
00777         // get bounds rects for the appropriate blobs.
00778         DocRect drKeyholeBlobRect;
00779         DocRect drContentsBlobRect;
00780         DocRect drBounds = GetBoundingRect();
00781         pBlobMgr->GetBlobRect(DocCoord(drBounds.lo.x, drBounds.hi.y), &drKeyholeBlobRect,
00782                                                                         FALSE, BT_CLIPVIEW);
00783         pBlobMgr->GetBlobRect(drBounds.Centre(), &drContentsBlobRect, FALSE, BT_CLIPVIEW);
00784 
00785         // inflate the rects, so the user doesn't need to be pin-point accurate.
00786         MILLIPOINT nNearRadius = OSRenderRegion::GetHitTestRadius(pDocView);
00787         drKeyholeBlobRect.Inflate(nNearRadius);
00788         drContentsBlobRect.Inflate(nNearRadius);
00789 
00790         // ok, lets test!
00791         if (drKeyholeBlobRect.ContainsCoord(dcClickPos))
00792         {
00793             if (HandleKeyholeBlobClick(cType))
00794                 return TRUE;
00795         }
00796         else if (drContentsBlobRect.ContainsCoord(dcClickPos))
00797         {
00798             if (HandleContentsBlobClick(cType))
00799                 return TRUE;
00800         }
00801     }
00802 
00803     // if we haven't handled the click and we're displaying object blobs,
00804     // hand over to our base implementation.
00805     if (bsCurrentInterest.Object)
00806         return NodeGroup::OnClick(dcClickPos, cType, cMods, pSpread);
00807 
00808     // we couldn't handle the click.
00809     return FALSE;
00810 }
00811 
00812 
00813 
00814 /********************************************************************************************
00815 
00816 >   virtual BOOL NodeClipViewController::HandleKeyholeBlobClick(ClickType cType)
00817 
00818     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00819     Created:    23 March 2000
00820     Inputs:     
00821     Outputs:    
00822     Returns:    TRUE if we handled the click,
00823                 FALSE if we passed.
00824     Purpose:    Handler for what happens when the user clicks on our keyhole-select blob.
00825 
00826                 All we do is try to select our keyhole nodes.
00827     Errors:     
00828     See also:   
00829 
00830 ********************************************************************************************/
00831 BOOL NodeClipViewController::HandleKeyholeBlobClick(ClickType cType)
00832 {
00833 // DEBUG
00834 //  TRACEUSER( "Karim", _T("NCVC::HandleKeyholeBlobClick!\n"));
00835 
00836     // quit immediately unless it's a button-up click with the selector tool.
00837     Tool* pTool = Tool::GetCurrent();
00838     if (cType != CLICKTYPE_UP || pTool == NULL || pTool->GetID() != TOOLID_SELECTOR)
00839         return FALSE;
00840 
00841     // select all our keyhole nodes, which are those nodes to the left of our NodeClipView.
00842     // if for some reason we can't select any nodes, we'll return FALSE.
00843     BOOL bChangedSelection  = FALSE;
00844     Node* pFirstKeyhole     = NULL;
00845     NodeClipView* pClipView = GetClipView();
00846     if (pClipView != NULL)
00847     {
00848         Node* pKeyhole      = pClipView->FindPrevious();
00849         while (pKeyhole != NULL)
00850         {
00851             if (pKeyhole->IsAnObject())
00852             {
00853                 if (!bChangedSelection)
00854                 {
00855                     DeselectAll();
00856                     pFirstKeyhole       = pKeyhole;
00857                     bChangedSelection   = TRUE;
00858                 }
00859 
00860                 ((NodeRenderableInk*)pKeyhole)->Select(TRUE);
00861             }
00862             pKeyhole = pKeyhole->FindPrevious();
00863         }
00864     }
00865 
00866     if (bChangedSelection)
00867     {
00868         SelRange* pSel = GetApplication()->FindSelection();
00869         if (pSel != NULL)
00870             pSel->Update(TRUE, pFirstKeyhole);
00871     }
00872 
00873     return bChangedSelection;
00874 }
00875 
00876 
00877 
00878 /********************************************************************************************
00879 
00880 >   virtual BOOL NodeClipViewController::HandleContentsBlobClick(ClickType cType)
00881 
00882     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00883     Created:    23 March 2000
00884     Inputs:     
00885     Outputs:    
00886     Returns:    TRUE if we handled the click,
00887                 FALSE if we passed.
00888     Purpose:    Handler for what happens when the user clicks on our contents-select blob.
00889 
00890                 We select all our NodeRenderableInk children *except* for our keyhole node.
00891     Errors:     
00892     See also:   
00893 
00894 ********************************************************************************************/
00895 BOOL NodeClipViewController::HandleContentsBlobClick(ClickType cType)
00896 {
00897 
00898 // DEBUG
00899 //  TRACEUSER( "Karim", _T("NCVC::HandleContentsBlobClick; cType %s\n"),
00900 //                                                  (cType == CLICKTYPE_DRAG)   ? "DRAG" :
00901 //                                                  (cType == CLICKTYPE_UP)     ? "UP" : "");
00902 
00903     // quit immediately unless it's the selector tool.
00904     Tool* pTool = Tool::GetCurrent();
00905     if (pTool == NULL || pTool->GetID() != TOOLID_SELECTOR)
00906         return FALSE;
00907 
00908     // also quit if we don't like the click type.
00909     if (cType != CLICKTYPE_DRAG && cType != CLICKTYPE_UP)
00910         return FALSE;
00911 
00912     // deselect everything, then select all non-keyhole nodes,
00913     // ie all nodes which are right-siblings of our clipview node.
00914     BOOL bChangedSelection  = FALSE;
00915     Node* pFirstKid         = NULL;
00916     NodeClipView* pClipView = GetClipView();
00917     if (pClipView != NULL)
00918     {
00919         Node* pKid = pClipView->FindNext();
00920         while (pKid != NULL)
00921         {
00922             if (pKid->IsAnObject())
00923             {
00924                 if (!bChangedSelection)
00925                 {
00926                     DeselectAll();
00927                     pFirstKid           = pKid;
00928                     bChangedSelection   = TRUE;
00929                 }
00930 
00931                 ((NodeRenderableInk*)pKid)->Select(TRUE);
00932             }
00933             pKid = pKid->FindNext();
00934         }
00935     }
00936 
00937     if (bChangedSelection)
00938     {
00939         SelRange* pSel = GetApplication()->FindSelection();
00940         if (pSel != NULL)
00941             pSel->Update(TRUE, pFirstKid);
00942     }
00943 
00944     // ok, if we successfully changed the selection, we
00945     // may need to start a drag with the selector tool.
00946     if (bChangedSelection && cType == CLICKTYPE_DRAG)
00947         ((SelectorTool*)pTool)->PublicDoTranslate();
00948 
00949     return bChangedSelection;
00950 }
00951 
00952 /********************************************************************************************
00953 
00954 >   virtual BOOL NodeClipViewController::CanBecomeA(BecomeA* pBecomeA)
00955 
00956     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00957     Created:    28 January 2000
00958     Inputs:     pClass          pointer to the runtime class of the node type to convert to.
00959                 pNumObjects     pointer in which to return the number of objects of type 
00960                                 pClass which would be created under a DoBecomeA(). Can be
00961                                 NULL, in which case we only return success or failure.
00962     Returns:    TRUE if we can change ourself or our children into a pClass type of node,
00963                 FALSE otherwise.
00964 
00965     Purpose:    This method is used by the convert to shapes operation. It determines whether 
00966                 this node or any of its children can convert themselves into a pClass Node.
00967 
00968                 The number returned in pNumObjects (if it's not NULL) should exactly equal
00969                 the total number of pClass objects which would be created by the appropriate 
00970                 call to DoBecomeA on this node. It should NOT contain any additional objects
00971                 produced, such as group objects or attributes created by the conversion.
00972 
00973                 Unfortunately, in the case of NodeClipViewController this is impossible
00974                 without changing the CanBecomeA mechanism or the rules by which NCVC converts
00975                 its children to shapes. The function for number of shapes is recursive:
00976 
00977                 SetOfKidPaths   = {all paths which our children return under DoBecomeA}
00978                 NumObjects      =   #SetOfKidPaths +
00979                                     #(members of SetOfKidPaths with non-zero line width)
00980 
00981                 , which would require CanBecomeA to return numbers of objects returned with
00982                 and without zero-width outlines.
00983 
00984                 We _can_ set upper and lower bounds on the number of objects we'll return -
00985                 either all of our children will have line widths, or none of them will, so
00986                 NodeGroup::CanBecomeA will return a lower bound and twice that number will
00987                 give an upper bound. We choose to return the upper bound, for ease of use
00988                 with DMc's code.
00989     Errors:     
00990     See also:   
00991 
00992 ********************************************************************************************/
00993 BOOL NodeClipViewController::CanBecomeA(BecomeA* pBecomeA)
00994 {
00995     // to convert to paths we'll need to convert each of our children.
00996 
00997     // we cannot return an accurate number of created objects unless we DoBecomeA on our
00998     // children. we _can_ however return a minimum or maximum number of objects.
00999     // David Mc's contour/bevel code does not like being passed a minimum, so we'll
01000     // give him a maximum instead.
01001 
01002     // minimum = number of paths our children will return.
01003     // maximum = twice the number of paths our children will return.
01004     UINT32 GroupNumObjects = pBecomeA->GetCount();
01005     BOOL bCanDo = NodeGroup::CanBecomeA(pBecomeA);
01006     pBecomeA->AddCount(pBecomeA->GetCount()-GroupNumObjects);   // Add the same count again to double it
01007 
01008     return bCanDo;
01009 }
01010 
01011 
01012 
01013 /********************************************************************************************
01014 
01015 >   virtual BOOL NodeClipViewController::DoBecomeA(BecomeA* pBecomeA)
01016 
01017     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01018     Created:    28 January 2000
01019     Inputs:     pBecomeA    pointer to a BecomeA structure containing information about the 
01020                 required transformation.
01021     Outputs:    This node and its children may be hidden and replaced with other node types 
01022                 representing a similar scene, eg a group of paths.
01023     Returns:    TRUE if successful,
01024                 FALSE otherwise.
01025 
01026     Purpose:    Turn this node into another node of a particular type.
01027                 This method implements make-shapes functionality, which basically means 
01028                 turning itself and its children into paths. Don't hold your breath if you ask 
01029                 it to turn into anything else...
01030 
01031                 Defined behaviour for ClipView DoBecomeA is that the keyhole nodes will have
01032                 the DoBecomeA call passed directly on to them. Any nodes which are clipped
01033                 behave differently. If these nodes have an outline, then it is first
01034                 split off from them, into a separate, new shape. Both outline shape and
01035                 original shape are then clipped to the keyhole path and left in the tree or
01036                 passed back. These nodes will also all have their line width set to zero and
01037                 their line colour set to transparent.
01038 
01039     Notes:      If this node cannot locate a full set of attributes (eg it is hidden), you
01040                 may get strange results from this method.
01041 
01042                 Karim 14/06/2000
01043                 Just added Silhouette functionality to BecomeA. This means that for PASSBACK,
01044                 the BecomeA object can flag that a silhouette is all that's required, which
01045                 should hopefully lead to better contouring/bevelling with ClipView.
01046                 If we're silhouetting, then we pass the BecomeA on to our our keyhole nodes.
01047 
01048     Errors:     ERROR2 if pBecomeA is NULL.
01049     See also:   
01050 
01051 ********************************************************************************************/
01052 BOOL NodeClipViewController::DoBecomeA(BecomeA* pBecomeA)
01053 {
01054     // validate input parameter.
01055     ERROR2IF(pBecomeA == NULL, FALSE, "NULL pBecomeA");
01056 
01057     // quit unless we're converting to paths.
01058     ERROR2IF(!pBecomeA->BAPath(), FALSE, "Attempt to convert to other than a path");
01059 
01060     // ensure our keyhole path is up to date.
01061     if (!UpdateKeyholePath())
01062         return FALSE;
01063 
01064     // ok, we do different things depending on what type of BecomeA we are.
01065     switch (pBecomeA->GetReason())
01066     {
01067     case BECOMEA_REPLACE:
01068 // DEBUG
01069 //      TRACEUSER( "Karim", _T("NCVC::DoBecomeA; BECOMEA_REPLACE\n"));
01070         {
01071             // if we're doing a BECOMEA_REPLACE, we need an op.
01072             UndoableOperation* pUndoOp = pBecomeA->GetUndoOp();
01073 //          ERROR2IF(pUndoOp == NULL, FALSE, "Need an UndoOp with BECOMEA_REPLACE");
01074 
01075             // local variables.
01076             Node*               pKid        = NULL;
01077             Node*               pNextKid    = NULL;
01078 //          Node*               pAfterKey   = NULL;
01079 //          NodeHidden*         pHiddenKey  = NULL;
01080             Node*               pClipView   = GetClipView();
01081             BOOL ok = (pClipView != NULL);
01082 
01083             // localise attributes.
01084             if (ok)
01085             {
01086                 if (pUndoOp)
01087                     ok = pUndoOp->DoLocaliseCommonAttributes(this);
01088                 else
01089                     LocaliseCommonAttributes();
01090             }
01091 
01092             // first, DoBecomeA on our children.
01093             if (ok)
01094             {
01095                 // if we only have to get a silhouette, then DoBecomeA on our keyhole nodes,
01096                 // and hide the rest of the children.
01097                 if (pBecomeA->DoSilhouette())
01098                 {
01099                     pKid = FindFirstChild();
01100                     while (ok && pKid != pClipView)
01101                     {
01102                         if (pKid->IsAnObject())
01103                             ok = pKid->DoBecomeA(pBecomeA);
01104                         pKid = pKid->FindNext();
01105                     }
01106                     pKid = pClipView->FindNext();
01107                     while (ok && pKid != NULL)
01108                     {
01109                         pNextKid = pKid->FindNext();
01110                         if (pUndoOp)
01111                             ok = pUndoOp->DoHideNode(pKid, TRUE);
01112                         else
01113                         {
01114                             pKid->CascadeDelete();
01115                             delete pKid;
01116                         }
01117                         pKid = pNextKid;
01118                     }
01119                 }
01120 
01121                 // otherwise, DoBecomeA on all of our children.
01122                 else
01123                 {
01124                     pKid = FindFirstChild();
01125                     while (ok && pKid != NULL)
01126                     {
01127                         pNextKid = pKid->FindNext();
01128                         ok = pKid->DoBecomeA(pBecomeA);
01129                         pKid = pNextKid;
01130                     }
01131                 }
01132             }
01133 
01134             // now separate the outline and fill of each of our non-keyhole children out into
01135             // new ink-nodes, clipped to our keyhole path. non-keyhole means all nodes to the 
01136             // right of our NodeClipView, and we do this depth-first, so we don't miss paths
01137             // hidden underneath group hierarchies.
01138 
01139             // unfortunately, paths can sometimes be completely clipped out of the tree. if
01140             // this happens then we could end up with empty groups (not ideal behaviour), so
01141             // we need to check for these and if we find any then we hide them.
01142             if (ok && !pBecomeA->DoSilhouette())
01143             {
01144                 BOOL bFinishedAGroup = FALSE;
01145                 pKid = pClipView->FindNextDepthFirst(this);
01146                 while (ok && pKid != NULL && pKid != this)
01147                 {
01148                     pNextKid = pKid->FindNextDepthFirst(this);
01149 
01150                     // if we're about to do the last kid in a group, remember this for later.
01151                     if (IS_A(pNextKid, NodeGroup)/*->IsAGroup()*/ && pNextKid == pKid->FindParent())
01152                         bFinishedAGroup = TRUE;
01153                     else
01154                         bFinishedAGroup = FALSE;
01155 
01156                     // if this one's a NodePath, process it.
01157                     if (pKid->IsNodePath())
01158                         ok = MakeShapeAndLine((NodePath*)pKid, pBecomeA, TRUE);
01159 
01160                     // if we've just finished a group, we need to check whether we left it
01161                     // containing any ink-children - if not, we need to hide it.
01162                     if (ok && bFinishedAGroup)
01163                     {
01164                         BOOL bFoundInkKids = FALSE;
01165                         Node* pInkKid = pNextKid->FindFirstChild();
01166                         while (!bFoundInkKids && pInkKid != NULL)
01167                         {
01168                             bFoundInkKids = pInkKid->IsAnObject();
01169                             pInkKid = pInkKid->FindNext();
01170                         }
01171                         if (!bFoundInkKids)
01172                         {
01173                             // move to the next depth-first node and hide the group.
01174                             pKid = pNextKid;
01175                             pNextKid = pKid->FindNextDepthFirst(this);
01176                             if (pUndoOp)
01177                                 ok = pUndoOp->DoHideNode(pKid, TRUE);
01178                             else
01179                             {
01180                                 pKid->CascadeDelete();
01181                                 delete pKid;
01182                             }
01183                         }
01184                     }
01185 
01186                     pKid = pNextKid;
01187                 }
01188             }
01189 
01190             // make sure that all remaining common attributes are factored back out.
01191             if (ok)
01192             {
01193                 if (pUndoOp)
01194                     ok = pUndoOp->DoFactorOutCommonChildAttributes(this);
01195                 else
01196                     FactorOutCommonChildAttributes();
01197             }
01198 
01199             // mighty-morph into a group (ClipView nodes hidden and replaced by a group node).
01200             if (ok) ok = (BecomeAGroup(pUndoOp) != NULL);
01201 
01202             // return the result of our deliberations.
01203             return ok;
01204         }
01205         break;
01206 
01207     case BECOMEA_PASSBACK:
01208 // DEBUG
01209 //      TRACEUSER( "Karim", _T("NCVC::DoBecomeA; BECOMEA_PASSBACK\n"));
01210         {
01211             // local variables.
01212             Node *          pKid        = NULL;
01213             NodeGroup *     pContainer  = NULL;
01214 //          NodeHidden *    pHiddenKey  = NULL;
01215             NodeClipView *  pClipView   = GetClipView();
01216             BOOL ok = (pClipView != NULL);
01217 
01218             // passback all our keyhole nodes directly.
01219             if (ok)
01220             {
01221                 pKid = FindFirstChild();
01222                 while (ok && pKid != pClipView)
01223                 {
01224                     if (pKid->IsAnObject())
01225                         ok = pKid->DoBecomeA(pBecomeA);
01226                     pKid = pKid->FindNext();
01227                 }
01228             }
01229 
01230             // quit now if all that is required is our silhouette,
01231             // as that is fully defined by our keyhole nodes.
01232             if (pBecomeA->DoSilhouette())
01233                 return ok;
01234 
01235             // make shape copies of all our non-keyhole kids (which are right-siblings
01236             // of our clipview node) and put the result into a container node.
01237             if (ok)
01238             {
01239                 pContainer = new NodeGroup();
01240                 ok = (pContainer != NULL);
01241             }
01242             if (ok)
01243             {
01244                 CopyBecomeA refCopyBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL);
01245                 refCopyBecomeA.SetContextNode(pContainer);
01246                 pKid = pClipView->FindNext();
01247                 while (ok && pKid != NULL)
01248                 {
01249                     if (pKid->CanBecomeA(&refCopyBecomeA))
01250                         ok = pKid->DoBecomeA(&refCopyBecomeA);
01251 
01252                     pKid = pKid->FindNext();
01253                 }
01254             }
01255 
01256             // convert the outlines of these shapes into independent shapes,
01257             // clip the output to our keyhole path and pass it all back.
01258             if (ok)
01259             {
01260                 pKid = pContainer->FindFirstDepthFirst();
01261                 while (ok && pKid != NULL)
01262                 {
01263                     if (pKid->IsNodePath())
01264                         ok = MakeShapeAndLine((NodePath*)pKid, pBecomeA, TRUE);
01265 
01266                     pKid = pKid->FindNextDepthFirst(pContainer);
01267                 }
01268             }
01269 
01270             // dispose of our child-shapes when we've finished with them.
01271             if (pContainer != NULL)
01272             {
01273                 pContainer->CascadeDelete();
01274                 delete pContainer;
01275                 pContainer = NULL;
01276             }
01277 
01278             return ok;
01279         }
01280         break;
01281 
01282     default:
01283         ERROR2(FALSE, "Unrecognised reason in convert-to-shapes");
01284         break;
01285     }
01286 
01287     return FALSE;
01288 }
01289 
01290 
01291 
01292 /********************************************************************************************
01293 
01294 >   BOOL NodeClipViewController::MakeShapeAndLine(  NodePath* pNodePath,
01295                                                     BecomeA* pBecomeA,
01296                                                     BOOL bClipToKeyhole )
01297     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01298     Created:    21/03/2000
01299     Inputs:     pNodePath       the node whose shape and line we will extract.
01300                 pBecomeA        ptr to a BecomeA - determines how to return the output.
01301                 bClipToKeyhole  whether we should clip the output to our keyhole path.
01302     Outputs:    
01303     Returns:    TRUE if successful, FALSE otherwise.
01304     Purpose:    Given a NodePath, copy it and remove the line width, and if it had any line
01305                 width then create a path from its applied line. If required, clip the copies
01306                 against the keyhole path, and depending on whether doing a BECOMEA_REPLACE or
01307                 a BECOMEA_PASSBACK, either undoably replace the node with the copies or pass
01308                 them back.
01309     Errors:     ERROR3 returning FALSE if invalid parameters.
01310     See also:   
01311 
01312 ********************************************************************************************/
01313 BOOL NodeClipViewController::MakeShapeAndLine(  NodePath* pNodePath,
01314                                                 BecomeA* pBecomeA,
01315                                                 BOOL bClipToKeyhole )
01316 {
01317 // DEBUG
01318 //  TRACEUSER( "Karim", _T("NCVC::MakeShapeAndLine\n"));
01319 
01320     // validate inputs.
01321     if (pNodePath == NULL || pBecomeA == NULL)
01322     {
01323         ERROR3("NCVC::MakeShapeAndLine; NULL parameters");
01324         return FALSE;
01325     }
01326     BecomeAReason Reason = pBecomeA->GetReason();
01327     if (Reason != BECOMEA_REPLACE && Reason != BECOMEA_PASSBACK)
01328     {
01329         ERROR3("NCVC::MakeShapeAndLine; Invalid parameters");
01330         return FALSE;
01331     }
01332     UndoableOperation* pUndoOp = pBecomeA->GetUndoOp();
01333 //  if (Reason == BECOMEA_REPLACE && pUndoOp == NULL)
01334 //  {
01335 //      ERROR3("NCVC::MakeShapeAndLine; Invalid parameters");
01336 //      return FALSE;
01337 //  }
01338 
01339     // local variables.
01340     BOOL        ok              = TRUE;
01341     NodePath*   pNewNode        = NULL;
01342     CCAttrMap*  pAttrMap        = NULL;
01343 
01344     // if the path is filled then insert/passback a doctored copy of it,
01345     // complete with attached copies of all the node's attributes.
01346     // note that we don't want to find indirectly applied GLAs here (eg feathers).
01347     if (pNodePath->InkPath.IsFilled)
01348     {
01349         if (ok)
01350         {
01351             pNewNode = (NodePath*)pNodePath->SimpleCopy();
01352             ok = (pNewNode != NULL);
01353         }
01354         if (ok)
01355         {
01356             pAttrMap = NULL;
01357             ALLOC_WITH_FAIL(pAttrMap, new CCAttrMap, pUndoOp);
01358             ok = (pAttrMap != NULL);
01359         }
01360         if (ok) ok = pNodePath->FindAppliedAttributes(pAttrMap, 5000, NULL, TRUE);
01361         if (ok) ok = pNewNode->ApplyAttributes(pAttrMap);
01362         if (ok)
01363         {
01364             if (Reason == BECOMEA_REPLACE)
01365             {
01366                 ok = InsertClippedNode(pNewNode, pNodePath, PREV, pUndoOp, bClipToKeyhole);
01367                 if (ok) pBecomeA->PassBack(pNewNode, this, pAttrMap->Copy());
01368             }
01369             else
01370                 ok = PassBackClippedNode(pNewNode, pBecomeA, bClipToKeyhole);
01371         }
01372         if (pAttrMap != NULL)
01373         {
01374             delete pAttrMap;
01375             pAttrMap = NULL;
01376         }
01377     }
01378 
01379     // if the path has an outline, then stroke its outline and pass it back/insert it,
01380     // complete with copies of the node's attributes.
01381     if (ok && !InkHasClearLineColour(pNodePath))
01382     {
01383         if (ok)
01384         {
01385             INT32 Flatness = EstimatePathFlatness();
01386             pNewNode = pNodePath->MakeNodePathFromAttributes(Flatness, NULL, TRUE);
01387             ok = (pNewNode != NULL);
01388         }
01389         if (ok)
01390         {
01391             pAttrMap = NULL;
01392             ALLOC_WITH_FAIL(pAttrMap, new CCAttrMap, pUndoOp);
01393             ok = (pAttrMap != NULL);
01394         }
01395         if (ok) ok = pNodePath->FindAppliedAttributes(pAttrMap, 5000, NULL, TRUE);
01396         if (ok) ok = pNewNode->ApplyAttributes(pAttrMap);
01397         if (ok) ok = CopyInkFillColourFromLine(pNewNode);
01398         if (ok) ok = CopyInkTransparencyFromLine(pNewNode);
01399         if (ok)
01400         {
01401             if (Reason == BECOMEA_REPLACE)
01402             {
01403                 ok = InsertClippedNode(pNewNode, pNodePath, PREV, pUndoOp, bClipToKeyhole);
01404                 if (ok) pBecomeA->PassBack(pNewNode, this, pAttrMap->Copy());
01405             }
01406             else
01407                 ok = PassBackClippedNode(pNewNode, pBecomeA, bClipToKeyhole);
01408         }
01409         if (pAttrMap != NULL)
01410         {
01411             delete pAttrMap;
01412             pAttrMap = NULL;
01413         }
01414     }
01415 
01416     // if we're replacing, and things are ok, then hide the NodePath.
01417     if (ok && Reason == BECOMEA_REPLACE)
01418     {
01419         if (pUndoOp)
01420             ok = pUndoOp->DoHideNode(pNodePath, FALSE);
01421         else
01422         {
01423             pNodePath->CascadeDelete();
01424             delete pNodePath;
01425         }
01426     }
01427 
01428     return ok;
01429 }
01430 
01431 /********************************************************************************************
01432 
01433 >   BOOL NodeClipViewController::InsertClippedNode( NodePath* pNewNode,
01434                                                     NodePath* pDestNode,
01435                                                     AttachNodeDirection Direction,
01436                                                     UndoableOperation* pUndoOp,
01437                                                     BOOL bClipToKeyhole )
01438     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01439     Created:    21 March 2000
01440     Inputs:     pNewNode        the new node to insert.
01441                 pDestNode       where to insert it.
01442                 Direction       before, after, under or over ;o)
01443                 pUndoOp         we need an UndoOp to do all this undoably.
01444                 bClipToKeyhole  should its path be clipped to the keyhole's path first?
01445     Outputs:    pNewNode is inserted into the tree, with its attributes normalised.
01446     Returns:    TRUE if success, FALSE otherwise.
01447     Purpose:    Insert the given node into the tree, having first removed any line width it
01448                 has applied to it, and also optionally clipped its path to our cached keyhole
01449                 path. Note that if we are unsuccessful then pNewNode will be deleted, along
01450                 with any children it may have had.
01451     Errors:     ERROR3 returning FALSE if invalid parameters.
01452     See also:   
01453 
01454 ********************************************************************************************/
01455 BOOL NodeClipViewController::InsertClippedNode( NodePath* pNewNode,
01456                                                 NodePath* pDestNode,
01457                                                 AttachNodeDirection Direction,
01458                                                 UndoableOperation* pUndoOp,
01459                                                 BOOL bClipToKeyhole )
01460 {
01461     // input validation.
01462     if (pNewNode == NULL /*|| pUndoOp == NULL*/)
01463     {
01464         ERROR3("NCVC::InsertClippedNode; NULL parameters");
01465         return FALSE;
01466     }
01467 
01468     // make the node's line transparent, if necessary clip to the keyhole node,
01469     // and then undoably insert it into the tree.
01470     // note that we *don't* abort the whole operation if clipping the
01471     // path went badly, we just don't insert it into the tree.
01472     BOOL clipok = TRUE;
01473     BOOL ok     = RemoveInkLineWidth(pNewNode);
01474     if (ok)
01475     {
01476         if (bClipToKeyhole)
01477         {
01478             Path* pWorkPath = &(pNewNode->InkPath);
01479             m_KeyholePath.ClipPathToPath(*pWorkPath, pWorkPath, 2,  CLIPVIEW_TOLERANCE,
01480                                                                     CLIPVIEW_SOURCEFLATNESS,
01481                                                                     CLIPVIEW_CLIPFLATNESS);
01482             if (pWorkPath->GetNumCoords() < 2)
01483                 clipok = FALSE;
01484             else
01485                 pWorkPath->InitialiseFlags();
01486         }
01487         if (clipok)
01488         {
01489             if (pUndoOp)
01490                 ok = pUndoOp->DoInsertNewNode(  pNewNode, pDestNode, Direction, FALSE,
01491                                                 FALSE, pDestNode->IsSelected(), TRUE );
01492             else
01493                 pNewNode->AttachNode(pDestNode, Direction);
01494         }
01495     }
01496 
01497     // tidy up if necessary.
01498     if ((!clipok || !ok) && pNewNode != NULL)
01499     {
01500         pNewNode->CascadeDelete();
01501         delete pNewNode;
01502     }
01503     pNewNode = NULL;
01504 
01505     return ok;
01506 }
01507 
01508 
01509 
01510 /********************************************************************************************
01511 
01512 >   BOOL NodeClipViewController::PassBackClippedNode(   NodePath* pNewNode,
01513                                                         BecomeA* pBecomeA,
01514                                                         BOOL bClipToKeyhole )
01515     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01516     Created:    21 March 2000
01517     Inputs:     pNewNode        the node to pass back.
01518                 pBecomeA        contains the passback method we'll use.
01519                 bClipToKeyhole  whether we should clip the node to our keyhole path.
01520     Outputs:    see Purpose.
01521     Returns:    TRUE if successful, FALSE otherwise.
01522     Purpose:    Calls pBecomeA->PassBack() with pNewNode and an appropriate attribute map.
01523                 Before passing back, pNewNode's line width is removed and it is optionally
01524                 clipped to our keyhole path. The attribute map copies all the attributes
01525                 currently applied to pNewNode.
01526                 Any children of pNewNode are deleted before it is passed back.
01527                 If anything goes wrong then pNewNode and its children are deleted.
01528 
01529     Errors:     ERROR3 returning FALSE if invalid parameters.
01530     See also:   
01531 
01532 ********************************************************************************************/
01533 BOOL NodeClipViewController::PassBackClippedNode(   NodePath* pNewNode,
01534                                                     BecomeA* pBecomeA,
01535                                                     BOOL bClipToKeyhole )
01536 {
01537     // validate inputs.
01538     if (pNewNode == NULL || pBecomeA == NULL)
01539     {
01540         ERROR3("NCVC::PassBackClippedNode; NULL parameters");
01541         return FALSE;
01542     }
01543 
01544     // local variables.
01545     BOOL        clipok          = TRUE;
01546     BOOL        ok              = TRUE;
01547     CCAttrMap*  pAttrMap        = NULL;
01548     CCAttrMap*  pCopiedAttrs    = NULL;
01549 
01550     // remove line width, copy its attribute map, try to clip to the keyhole
01551     // node and then pass back the path.
01552     // NB   although we usually abort if anything goes wrong, we do *not* abort
01553     //      if we failed to clip to the keyhole path - we just don't pass it back.
01554     if (ok) ok = RemoveInkLineWidth(pNewNode);
01555     if (ok)
01556     {
01557         pAttrMap = new CCAttrMap();
01558         ok = (pAttrMap != NULL);
01559     }
01560     if (ok) ok = pNewNode->FindAppliedAttributes(pAttrMap);
01561     if (ok)
01562     {
01563         pCopiedAttrs = pAttrMap->Copy();
01564         ok = (pCopiedAttrs != NULL);
01565     }
01566     if (ok)
01567     {
01568         pNewNode->CascadeDelete();
01569         if (bClipToKeyhole)
01570         {
01571             Path* pWorkPath = &(pNewNode->InkPath);
01572             m_KeyholePath.ClipPathToPath(*pWorkPath, pWorkPath, 2,  CLIPVIEW_TOLERANCE,
01573                                                                     CLIPVIEW_SOURCEFLATNESS,
01574                                                                     CLIPVIEW_CLIPFLATNESS);
01575             if (pWorkPath->GetNumCoords() < 2)
01576                 clipok = FALSE;
01577             else
01578                 pWorkPath->InitialiseFlags();
01579         }
01580         if (clipok)
01581             ok = pBecomeA->PassBack(pNewNode, this, pCopiedAttrs);
01582     }
01583 
01584     // tidy up if everything didn't go according to plan.
01585     if (!ok || !clipok)
01586     {
01587         if (pNewNode != NULL)
01588         {
01589             pNewNode->CascadeDelete();
01590             delete pNewNode;
01591         }
01592         if (pCopiedAttrs != NULL)
01593         {
01594             pCopiedAttrs->DeleteAttributes();
01595             delete pCopiedAttrs;
01596         }
01597     }
01598     if (pAttrMap != NULL)
01599         delete pAttrMap;
01600 
01601     return ok;
01602 }
01603 
01604 
01605 
01606 /********************************************************************************************
01607 
01608 >   BOOL NodeClipViewController::InkHasClearLineColour(NodeRenderableInk* pInkNode)
01609 
01610     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01611     Created:    08 March 2000
01612     Inputs:     InkNode     const reference to a NodeRenderableInk.
01613 
01614     Returns:    TRUE if we can definitely confirm the node has a transparent line width,
01615                 FALSE for any other result.
01616 
01617     Purpose:    Test whether the line colour of the given NodeRenderableInk is transparent.
01618     Errors:     
01619     See also:   
01620 
01621 ********************************************************************************************/
01622 BOOL NodeClipViewController::InkHasClearLineColour(NodeRenderableInk* pInkNode)
01623 {
01624     NodeAttribute *pAppliedAttr = NULL;
01625     pInkNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), &pAppliedAttr);
01626     if (pAppliedAttr != NULL)
01627     {
01628         DocColour* pLineColour = ((AttrStrokeColour*)pAppliedAttr)->GetStartColour();
01629         if (pLineColour != NULL)
01630             return pLineColour->IsTransparent();
01631     }
01632     return FALSE;
01633 }
01634 
01635 
01636 
01637 /********************************************************************************************
01638 
01639 >   BOOL NodeClipViewController::RemoveInkLineWidth(NodeRenderableInk* pInk)
01640 
01641     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01642     Created:    09 March 2000
01643     Inputs:     pInk    pointer to the NodeRenderableInk to work on.
01644     Outputs:    pInk's line colour should become transparent.
01645     Returns:    TRUE if successful,
01646                 FALSE otherwise.
01647     Purpose:    Make the outline of the given node transparent and set its line width to 0.
01648 
01649     NOTE:       This method does *not* record undo information, and it expects attributes
01650                 to have been localised beforehand.
01651     Errors:     ERROR3 and returns FALSE if pInk is NULL.
01652     See also:   
01653 
01654 ********************************************************************************************/
01655 BOOL NodeClipViewController::RemoveInkLineWidth(NodeRenderableInk* pInk)
01656 {
01657     if (pInk == NULL)
01658     {
01659         ERROR3("NCVC::RemoveInkLineWidth; NULL parameter(s)");
01660         return FALSE;
01661     }
01662 
01663     NodeAttribute*      pAppliedAttr = NULL;
01664     DocColour*          pLineColour  = NULL;
01665     LineWidthAttribute* pLineWidth   = NULL;
01666 
01667     pInk->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), &pAppliedAttr);
01668     if (pAppliedAttr != NULL)
01669         pLineColour = ((AttrStrokeColour*)pAppliedAttr)->GetStartColour();
01670 
01671     pAppliedAttr = NULL;
01672     pInk->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth), &pAppliedAttr);
01673     if (pAppliedAttr != NULL)
01674         pLineWidth = (LineWidthAttribute*)pAppliedAttr->GetAttributeValue();
01675 
01676     if (pLineColour != NULL && pLineWidth != NULL)
01677     {
01678         *pLineColour = DocColour(COLOUR_TRANS);
01679         pLineWidth->LineWidth = 0;
01680         return TRUE;
01681     }
01682     
01683     return FALSE;
01684 }
01685 
01686 
01687 
01688 /********************************************************************************************
01689 
01690 >   BOOL NodeClipViewController::CopyInkFillColourFromLine(NodeRenderableInk* pInk)
01691 
01692     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01693     Created:    09 March 2000
01694     Inputs:     pInk    pointer to the NodeRenderableInk to work on.
01695     Outputs:    pInk's fill colour should match its line colour.
01696     Returns:    TRUE if successful,
01697                 FALSE otherwise.
01698     Purpose:    Obtain the line colour of the given NodeRenderableInk and re-apply it as its
01699                 new fill colour.
01700 
01701     NOTE:       This method is *not* undoable!
01702 
01703     Errors:     ERROR2 returning FALSE if pInk is NULL.
01704     See also:   
01705 
01706 ********************************************************************************************/
01707 BOOL NodeClipViewController::CopyInkFillColourFromLine(NodeRenderableInk* pInk)
01708 {
01709     ERROR2IF(pInk == NULL, FALSE, "NULL parameter(s)");
01710 
01711     NodeAttribute *pLineColourAttr = NULL;
01712     DocColour* pLineColour = NULL;
01713 //  DocColour* pFillColour = NULL;
01714     Node* pOldFill = NULL;
01715     AttrFlatColourFill* pNewFlatFill = NULL;
01716 
01717     // obtain the line-colour attribute and its actual colour value.
01718     pInk->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), &pLineColourAttr);
01719     // DY: bodge to solve problem of creating brushes from clipview objects.  The problem was 
01720     // that we are using an inknode that is not located in a document so it sometimes does
01721     // not have an applied stroke colour, so just return true
01722     
01723     BOOL ok = (pLineColourAttr != NULL);
01724     if (ok)
01725     {
01726         pLineColour = ((AttrStrokeColour*)pLineColourAttr)->GetStartColour();
01727         ok = (pLineColour != NULL);
01728     }
01729     else
01730         return TRUE; // safe return 
01731 
01732     // search the children of the object node for its current colour fill attribute.
01733     if (ok)
01734     {
01735         BOOL bFoundFill = FALSE;
01736         pOldFill = pInk->FindFirstChild();
01737         while (!bFoundFill && pOldFill != NULL)
01738         {
01739             if ( pOldFill->IsAFillAttr() && ((NodeAttribute*)pOldFill)->IsAColourFill() )
01740                 bFoundFill = TRUE;
01741             else
01742                 pOldFill = pOldFill->FindNext();
01743         }
01744     }
01745 
01746     // create a new fill attribute.
01747     if (ok)
01748     {
01749         pNewFlatFill = new AttrFlatColourFill();
01750         ok = (pNewFlatFill != NULL);
01751     }
01752 
01753     // if we were successful, then put it all into the tree and remove any
01754     // previous fill applied.
01755     if (ok)
01756     {
01757         pNewFlatFill->SetStartColour(pLineColour);
01758         pNewFlatFill->AttachNode(pInk, LASTCHILD);
01759         if (pOldFill != NULL)
01760         {
01761             pOldFill->UnlinkNodeFromTree();
01762             delete pOldFill;
01763         }
01764     }
01765 
01766     // ensure we free allocated memory if unsuccessful.
01767     else
01768     {
01769         if (pNewFlatFill != NULL)
01770         {
01771             delete pNewFlatFill;
01772             pNewFlatFill = NULL;
01773         }
01774     }
01775 
01776     return ok;
01777 }
01778 
01779 
01780 
01781 /********************************************************************************************
01782 
01783 >   BOOL NodeClipViewController::CopyInkTransparencyFromLine(NodeRenderableInk* pInk)
01784 
01785     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01786     Created:    20 March 2000
01787     Inputs:     pInk    pointer to the NodeRenderableInk to work on.
01788     Outputs:    pInk should be given the same transparency as its outline.
01789     Returns:    TRUE if successful,
01790                 FALSE otherwise.
01791     Purpose:    Obtain the line transparency of the given NodeRenderableInk and re-apply it
01792                 as a new flat transparency.
01793 
01794     NOTE:       This method is *not* undoable!
01795 
01796     Errors:     ERROR2 returning FALSE if pInk is NULL.
01797     See also:   
01798 
01799 ********************************************************************************************/
01800 BOOL NodeClipViewController::CopyInkTransparencyFromLine(NodeRenderableInk* pInk)
01801 {
01802     ERROR2IF(pInk == NULL, FALSE, "NULL parameter(s)");
01803 
01804     NodeAttribute *pLineTranspAttr = NULL;
01805     UINT32* pLineTransp = NULL;
01806 //  UINT32* pFillTransp = NULL;
01807     Node* pOldFill = NULL;
01808     AttrFlatTranspFill* pNewFlatFill = NULL;
01809 
01810     // obtain the line-transparency attribute and its actual transparency value.
01811     pInk->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeTransp), &pLineTranspAttr);
01812     // DY: bodge to solve problem of creating brushes from clipview objects.  The problem was 
01813     // that we are using an inknode that is not located in a document so it sometimes does
01814     // not have an applied stroke transparency, so just return true
01815     
01816     BOOL ok = (pLineTranspAttr != NULL);
01817     if (ok)
01818     {
01819         pLineTransp = ((AttrStrokeTransp*)pLineTranspAttr)->GetStartTransp();
01820         ok = (pLineTransp != NULL);
01821     }
01822     else
01823         return TRUE;
01824 
01825     // search the children of the object node for its current transparency fill attribute.
01826     if (ok)
01827     {
01828         BOOL bFoundFill = FALSE;
01829         pOldFill = pInk->FindFirstChild();
01830         while (!bFoundFill && pOldFill != NULL)
01831         {
01832             if ( pOldFill->IsAFillAttr() && ((NodeAttribute*)pOldFill)->IsATranspFill() )
01833                 bFoundFill = TRUE;
01834             else
01835                 pOldFill = pOldFill->FindNext();
01836         }
01837     }
01838 
01839     // create a new transparency fill attribute.
01840     if (ok)
01841     {
01842         pNewFlatFill = new AttrFlatTranspFill();
01843         ok = (pNewFlatFill != NULL);
01844     }
01845 
01846     // if we were successful, then put it all into the tree and remove any
01847     // previous fill applied.
01848     if (ok)
01849     {
01850         pNewFlatFill->SetStartTransp(pLineTransp);
01851         pNewFlatFill->AttachNode(pInk, LASTCHILD);
01852         if (pOldFill != NULL)
01853         {
01854             pOldFill->UnlinkNodeFromTree();
01855             delete pOldFill;
01856         }
01857     }
01858 
01859     // ensure we free allocated memory if unsuccessful.
01860     else
01861     {
01862         if (pNewFlatFill != NULL)
01863         {
01864             delete pNewFlatFill;
01865             pNewFlatFill = NULL;
01866         }
01867     }
01868 
01869     return ok;
01870 }
01871 
01872 
01873 
01874 /********************************************************************************************
01875 
01876 >   virtual NodeGroup* NodeClipViewController::BecomeAGroup(UndoableOperation* pUndoOp)
01877 
01878     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01879     Created:    07 March 2000
01880     Inputs:     pUndoOp     pointer to an UndoableOperation
01881     Outputs:    
01882     Returns:    TRUE if successful,
01883                 FALSE otherwise.
01884 
01885     Purpose:    We are sometimes required to turn ourself into a normal group node, and that
01886                 is exactly what this method does. If an UndoOp is supplied then it is done 
01887                 in an undoable fashion but it must also work non-undoably if a NULL op pointer 
01888                 is passed in.
01889 
01890 ********************************************************************************************/
01891 NodeGroup* NodeClipViewController::BecomeAGroup(UndoableOperation* pUndoOp)
01892 {
01893     // ok!
01894     BOOL ok = TRUE;
01895 
01896     NodeGroup*      pGroup      = NULL;
01897     NodeClipView*   pClipView   = GetClipView(FALSE);
01898 
01899     // hide our clipview node if it exists.
01900     if (ok) ok = (pClipView != NULL);
01901     if (ok)
01902     {
01903         if (pUndoOp)
01904             ok = pUndoOp->DoHideNode(pClipView, TRUE);
01905         else
01906         {
01907             pClipView->CascadeDelete();
01908             delete pClipView;       // Scary!
01909         }
01910     }
01911 
01912     // call our base class to turn into a group.
01913     if (ok) pGroup = NodeGroup::BecomeAGroup(pUndoOp);
01914 
01915     return pGroup;
01916 }
01917 
01918 
01919 
01920 /********************************************************************************************
01921 
01922 >   BOOL NodeClipViewController::UpdateKeyholePath()
01923 
01924     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01925     Created:    13 March 2000
01926     Inputs:     
01927     Outputs:    
01928     Returns:    
01929     Purpose:    Update our cached outline for our keyhole node.
01930                 Note that this method only tries to update the keyhole path if it is marked
01931                 as invalid. If it is already valid, you just get TRUE back.
01932                 It will also only re-mark the keyhole as valid if it successfully updates
01933                 the keyhole cache.
01934     Errors:     
01935     See also:   InvalidateKeyhole().
01936 
01937 ********************************************************************************************/
01938 BOOL NodeClipViewController::UpdateKeyholePath()
01939 {
01940 // DEBUG
01941 //  TRACEUSER( "Karim", _T("NCVC::UpdateKeyholePath; %s.\n"),
01942 //              IsKeyholeValid() ? "valid" : "invalid");
01943 
01944     if (IsKeyholeValid())
01945         return TRUE;
01946 
01947     NodeClipView* pClipView = GetClipView();
01948     if (pClipView == NULL)
01949         return FALSE;
01950 
01951     // DoBecomeA on all our keyhole nodes (nodes to the left of our clipview node), and
01952     // add the resulting paths up to get one big keyhole-path.
01953     // in the interests of accuracy, and because most of the time we expect only to have
01954     // one keyhole path, we're going to do as little clip-path-to-path'ing as possible.
01955     BOOL bAddedFirstPath = FALSE;
01956     Node* pKeyhole = pClipView->FindPrevious();
01957     while (!bAddedFirstPath && pKeyhole != NULL)
01958     {
01959         if (pKeyhole->IsAnObject())
01960         {
01961             m_KeyholePath.ClearPath();
01962             PathBecomeA baInfo(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL, FALSE,
01963                                 &m_KeyholePath, PathBecomeA::STRIP_OUTLINES );
01964             baInfo.SetPathsOnly();
01965             if (pKeyhole->DoBecomeA(&baInfo))
01966                 bAddedFirstPath = TRUE;
01967         }
01968         pKeyhole = pKeyhole->FindPrevious();
01969     }
01970 
01971     // ok, now add any other keyholes which are kicking around.
01972     BOOL ok = TRUE;
01973     if (pKeyhole != NULL)
01974     {
01975         INT32 Flatness = EstimatePathFlatness();
01976         Path m_WorkPath;
01977         m_WorkPath.Initialise();
01978         while (ok && pKeyhole != NULL)
01979         {
01980             if (pKeyhole->IsAnObject())
01981             {
01982                 m_WorkPath.ClearPath();
01983                 PathBecomeA baInfo(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL, FALSE,
01984                                     &m_WorkPath, PathBecomeA::STRIP_OUTLINES);
01985                 baInfo.SetPathsOnly();
01986                 if (ok && pKeyhole->DoBecomeA(&baInfo))
01987                     ok = (m_KeyholePath.ClipPathToPath( m_WorkPath, &m_KeyholePath, 7,
01988                                                         CLIPVIEW_TOLERANCE,
01989                                                         Flatness,
01990                                                         Flatness) != -1);
01991             }
01992             pKeyhole = pKeyhole->FindPrevious();
01993         }
01994     }
01995 
01996     // if our keyhole path is empty, then we need to hide everything which we're clipping.
01997     // we'll do this by building an empty keyhole path out of our bounding rect.
01998     if (m_KeyholePath.GetNumCoords() == 0)
01999     {
02000         DocRect drBounds = GetBoundingRect();
02001         DocCoord drLeft(drBounds.lo.x, (drBounds.lo.y + drBounds.hi.y) / 2);
02002         DocCoord drRight(drBounds.hi.x, (drBounds.lo.y + drBounds.hi.y) / 2);
02003 
02004         m_KeyholePath.Initialise(3);
02005         m_KeyholePath.AddMoveTo(drLeft);
02006         m_KeyholePath.AddLineTo(drRight);
02007         m_KeyholePath.AddLineTo(drLeft);
02008         m_KeyholePath.IsFilled = TRUE;
02009     }
02010 
02011     // ok, setup with the new keyhole path.
02012     pClipView->SetClipPath(&m_KeyholePath);
02013     if (ok)
02014         MarkKeyholeValid();
02015 
02016     return ok;
02017 }
02018 
02019 
02020 
02021 /********************************************************************************************
02022 
02023 >   static INT32 NodeClipViewController::EstimatePathFlatness()
02024 
02025     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02026     Created:    09 March 2000
02027     Returns:    Flatness required for paths used by ClipView.
02028     Purpose:    Return an estimation of how flat paths need to be for ClipView groups, based
02029                 on the current document's zoom factor. A default value will be returned if
02030                 there is no current document.
02031 
02032                 According to DMc, flatness is the max straight line length when a curve is
02033                 approximated - smaller is smoother.
02034                 We should aim for a flatness of ~0.1 pixels.
02035     Errors:     
02036     See also:   
02037 
02038 ********************************************************************************************/
02039 INT32 NodeClipViewController::EstimatePathFlatness()
02040 {
02041     // we use a default flatness of 75 millipoints, as this is 0.1 pixels at 100% zoom.
02042     // (100% zoom = 96 dpi @ 72000 mp per inch ==> 750 mp per dot)
02043     static const double DefaultFlatness = 75.0;
02044     DocView* pView      = DocView::GetCurrent();
02045     double ZoomFactor   = (pView == NULL) ? 1 : pView->GetZoomFactor();
02046     INT32 Flatness      = (INT32)(ZoomFactor * DefaultFlatness);
02047     return (Flatness == 0) ? 1 : Flatness;
02048 }
02049 
02050 
02051 
02052 /********************************************************************************************
02053 
02054 >   virtual BOOL NodeClipViewController::AllowOp(   ObjChangeParam *pParam,
02055                                                     BOOL SetOpPermissionState,
02056                                                     BOOL DoPreTriggerEdit )
02057     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02058     Created:    28 January 2000
02059     Inputs:     pParam                  describes the way an op wants to change the node
02060                 SetOpPermissionState    if TRUE the OpPermission of nodes should be set
02061                 DoPreTriggerEdit        if TRUE then NameGallery::PreTriggerEdit is called.
02062                                         *Must* be TRUE if the calling Op may make any nodes
02063                                         change their bounds, eg move, line width, cut.
02064                                         Use TRUE if unsure.
02065     Outputs:    See input param descriptions.
02066     Returns:    TRUE if the Op may continue,
02067                 FALSE to stop the Op in its tracks.
02068     Purpose:    See Node::AllowOp() for a lengthy explanation.
02069     Errors:     ERROR2 if pParam is NULL.
02070     See also:   
02071 
02072 ********************************************************************************************/
02073 BOOL NodeClipViewController::AllowOp(ObjChangeParam *pParam, BOOL SetOpPermissionState,
02074                                                              BOOL DoPreTriggerEdit)
02075 {
02076     // input validation.
02077     ERROR2IF(pParam == NULL, FALSE, "NULL pParam");
02078 
02079     // call our base class' AllowOp().
02080     BOOL allowed = NodeGroup::AllowOp(pParam, SetOpPermissionState, DoPreTriggerEdit);
02081 
02082     // Slight misuse of the AllowOp/OnChildChange system, as that says that only our
02083     // children are meant to set our permission to PERMISSION_ALLOWED.
02084     // This will ensure that our OnChildChange() method is called after the op has finished,
02085     // allowing us to update our keyhole path after any changes.
02086     if (allowed && SetOpPermissionState)
02087         SetOpPermission(PERMISSION_ALLOWED, TRUE);
02088 
02089     return allowed;
02090 }
02091 
02092 
02093 
02094 /********************************************************************************************
02095 
02096 >   ChangeCode NodeClipViewController::OnChildChange(ObjChangeParam* pParam)
02097 
02098     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02099     Created:    28 January 2000
02100     Inputs:     pParam  describes the change which occurred.
02101     Outputs:    This node may regenerate itself to accomodate the change.
02102     Returns:    CC_OK       if we have successfully processed the change.
02103                 CC_FAIL     if we cannot handle this particular change and must prevent the
02104                             child from continuing.
02105     Purpose:    Used to notify this node of changes to its children, so that if required it
02106                 can update itself. This forms part of the AllowOp / UpdateChangedNodes
02107                 mechanism.
02108     Errors:     ERROR2 if pParam is NULL.
02109     See also:   Node::WarnParentOfChange(), Node::AllowOp()
02110 
02111 ********************************************************************************************/
02112 ChangeCode NodeClipViewController::OnChildChange(ObjChangeParam* pParam)
02113 {
02114     // validate input.
02115     ERROR2IF(pParam == NULL, CC_FAIL, "NULL pParam");
02116 
02117     // we'll need pointers to our ClipView and Keyhole nodes later on.
02118     NodeClipView* pClipView = GetClipView(FALSE);
02119 
02120     // we need to deal with circumstances which may put us into an inconsistent state.
02121     // these are:
02122     //
02123     //  * No ink-children, or only a ClipView node  ==> hide ourself.
02124     //  * No keyhole nodes      =>
02125     //  * No clipped nodes      ==>     turn ourself into a group.
02126     //  * No ClipView node      =>
02127     //
02128     UndoableOperation* pUndoOp = pParam->GetOpPointer();
02129     ObjChangeType cType = pParam->GetChangeType();
02130     if (cType == OBJCHANGE_FINISHED)
02131     {
02132         // count our kids.
02133         UINT32 NumKeyhole   = 0;
02134         UINT32 NumClipped   = 0;
02135         UINT32 NumKids  = 0;
02136         BOOL fCountingKeyholes = TRUE;
02137         Node* pKid = FindFirstChild();
02138         while (pKid != NULL)
02139         {
02140             if (pKid->IsAnObject())
02141             {
02142                 if (pKid == pClipView)
02143                 {
02144                     fCountingKeyholes = FALSE;
02145                 }
02146                 else
02147                 {
02148                     if (fCountingKeyholes)
02149                         NumKeyhole ++;
02150                     else
02151                         NumClipped ++;
02152                 }
02153             }
02154             pKid = pKid->FindNext();
02155         }
02156         NumKids = NumKeyhole + NumClipped;
02157 
02158         // ok, we hide ourself if we have no ink-kids (ignoring the clipview node).
02159         if (NumKids == 0)
02160         {
02161 
02162             // we need an Op in order to do tree manipulations.
02163             BOOL ok = (pUndoOp != NULL);
02164 
02165             // hide ourself - find our parent before we do though.
02166             Node* pParent = FindParent();
02167             if (ok)
02168                 ok = pUndoOp->DoHideNode(this,TRUE);
02169 
02170             // now we're hidden, factor out all attributes on our parent.
02171             if (ok)
02172                 if (pParent != NULL && pParent->IsCompound())
02173                     ok = pUndoOp->DoFactorOutCommonChildAttributes(((NodeRenderableInk*)pParent),TRUE);
02174 
02175             // TODO: rewind any actions here?
02176             if (!ok)
02177                 return CC_FAIL;
02178         }
02179 
02180         // if we're missing any one of our three magic ingredients, we mighty-morph into a group.
02181         if (pClipView == NULL || NumKeyhole == 0 || NumClipped == 0)
02182         {
02183             // we need a valid UndoOp ptr with which to perform the conversion.
02184             if (pUndoOp != NULL)
02185                 if (BecomeAGroup(pUndoOp))
02186                     return CC_OK;
02187 
02188             return CC_FAIL;
02189         }
02190 
02191         // the last case is where we have all three ingredients, in which case we
02192         // invalidate our keyhole path and make a 'mental' note that note that it
02193         // will need invalidating again whenever the user performs an undo or redo.
02194         else if (pUndoOp != NULL)
02195         {
02196 // DEBUG
02197 /*          TRACEUSER( "Karim", _T("NCVC::OnCC;"));
02198 
02199             // direction of change.
02200             ObjChangeDirection cDirection = pParam->GetDirection();
02201             switch (cDirection)
02202             {
02203             case OBJCHANGE_CALLEDBYCHILD:
02204                 TRACEUSER( "Karim", _T(" OBJCHANGE_CALLEDBYCHILD"));
02205                 break;
02206 
02207             case OBJCHANGE_CALLEDBYPARENT:
02208                 TRACEUSER( "Karim", _T(" OBJCHANGE_CALLEDBYPARENT"));
02209                 break;
02210 
02211             case OBJCHANGE_CALLEDBYOP:
02212                 TRACEUSER( "Karim", _T(" OBJCHANGE_CALLEDBYOP"));
02213                 break;
02214 
02215             default:
02216                 break;
02217             }
02218 
02219             // type of change.
02220             TRACEUSER( "Karim", _T(" ;"));
02221             switch (cType)
02222             {
02223             case OBJCHANGE_UNDEFINED:
02224                 TRACEUSER( "Karim", _T(" OBJCHANGE_UNDEFINED"));
02225                 break;
02226 
02227             case OBJCHANGE_STARTING:
02228                 TRACEUSER( "Karim", _T(" OBJCHANGE_STARTING"));
02229                 break;
02230 
02231             case OBJCHANGE_RENDERCURRENTBLOBS:
02232                 TRACEUSER( "Karim", _T(" OBJCHANGE_RENDERCURRENTBLOBS"));
02233                 break;
02234 
02235             case OBJCHANGE_RENDERCHANGEDBLOBS:
02236                 TRACEUSER( "Karim", _T(" OBJCHANGE_RENDERCHANGEDBLOBS"));
02237                 break;
02238 
02239             case OBJCHANGE_FINISHED:
02240                 TRACEUSER( "Karim", _T(" OBJCHANGE_FINISHED"));
02241                 break;
02242 
02243             case OBJCHANGE_IGNORE:
02244                 TRACEUSER( "Karim", _T(" OBJCHANGE_IGNORE"));
02245                 break;
02246 
02247             case OBJCHANGE_FAILED:
02248                 TRACEUSER( "Karim", _T(" OBJCHANGE_FAILED"));
02249                 break;
02250 
02251             default:
02252                 break;
02253             }
02254 
02255             // set change-flags.
02256             TRACEUSER( "Karim", _T("; Flags {"));
02257             ObjChangeFlags cFlags = pParam->GetChangeFlags();
02258             if (cFlags.Attribute)
02259                 TRACEUSER( "Karim", _T(" Attribute"));
02260             if (cFlags.CopyNode)
02261                 TRACEUSER( "Karim", _T(" CopyNode"));
02262             if (cFlags.DeleteNode)
02263                 TRACEUSER( "Karim", _T(" DeleteNode"));
02264             if (cFlags.MoveNode)
02265                 TRACEUSER( "Karim", _T(" MoveNode"));
02266             if (cFlags.MultiReplaceNode)
02267                 TRACEUSER( "Karim", _T(" MultiReplaceNode"));
02268             if (cFlags.RegenerateNode)
02269                 TRACEUSER( "Karim", _T(" RegenerateNode"));
02270             if (cFlags.ReplaceNode)
02271                 TRACEUSER( "Karim", _T(" ReplaceNode"));
02272             if (cFlags.TransformNode)
02273                 TRACEUSER( "Karim", _T(" TransformNode"));
02274             TRACEUSER( "Karim", _T(" }\n"));
02275 */
02276             // mark the cached keyhole path for update.
02277             UpdateCachedKeyholePathAction::Init( pUndoOp,
02278                                                  pUndoOp->GetUndoActions(),
02279                                                  this );
02280         }
02281         else if (!pUndoOp)
02282         {
02283             Document * pDoc = Document::GetCurrent();
02284 
02285             if (pDoc)
02286             {
02287                 if (pParam->GetChangeFlags ().RegenerateNode)
02288                 {
02289                     MarkKeyholeInvalid ();      // CGS:  it is now legitimate for us to do this
02290                 }
02291                 
02292                 Spread * pSpread = (Spread *)FindParent(CC_RUNTIME_CLASS(Spread));
02293                 pDoc->ForceRedraw(pSpread, GetBoundingRect(FALSE, FALSE), FALSE, this);
02294             }
02295         }
02296     }
02297 
02298     // call the base class version for the normal group checks, which will also deal with the
02299     // case where we have no kids at all.
02300     return NodeGroup::OnChildChange(pParam);
02301 }
02302 
02303 
02304 
02305 /********************************************************************************************
02306 
02307 >   virtual String NodeClipViewController::Describe(BOOL Plural, BOOL Verbose)
02308 
02309     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02310     Created:    28 January 2000
02311     Inputs:     Plural      whether to pluralise the description.
02312                 Verbose     short or long version.
02313     Returns:    A string description of this node.
02314     Purpose:    Get a string description, for use in menus and infobar etc.
02315     Errors:     
02316     See also:   
02317 
02318 ********************************************************************************************/
02319 String NodeClipViewController::Describe(BOOL Plural, BOOL Verbose)
02320 {
02321     if (Plural)
02322         return(String(_R(IDS_CLIPVIEW_CONTROLLER_DESCRP)));
02323     else
02324         return(String(_R(IDS_CLIPVIEW_CONTROLLER_DESCRS)));
02325 }
02326 
02327 
02328 
02329 /********************************************************************************************
02330 
02331 >   virtual UINT32 NodeClipViewController::GetNodeSize() const 
02332 
02333     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02334     Created:    02 February 2000
02335     Returns:    The size of the node in bytes.
02336     Purpose:    Obtain the size of a NodeClipViewController object.
02337 
02338     See also:   Node::GetSubtreeSize
02339 
02340 ********************************************************************************************/
02341 UINT32 NodeClipViewController::GetNodeSize() const 
02342 {
02343     return sizeof(NodeClipViewController);
02344 }
02345 
02346 
02347 
02348 /********************************************************************************************
02349 
02350 >   void NodeClipViewController::Transform(TransformBase &Trans)
02351 
02352     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02353     Created:    31 January 2000
02354     Inputs:     Trans   non-const reference to a description of the transformation.
02355     Outputs:    This node will be transformed appropriately.
02356     Purpose:    Perform a transformation on this node.
02357     Errors:     
02358     See also:   
02359 
02360 ********************************************************************************************/
02361 void NodeClipViewController::Transform(TransformBase &Trans)
02362 {
02363     // required because certain processes which transform nodes do not fire off my
02364     // OnChildChange method (haven't investigated why)
02365     MarkKeyholeInvalid();
02366 
02367     // ClipViews can't just transform their cached data - they have to
02368     // also transform their children because clipview's GetBoundingRect
02369     // always calculates - never returns cached rectangle.
02370     Trans.bTransformYourChildren = TRUE;
02371 
02372     // ClipViews can't just transform their cached data - they have to
02373     // also transform their children because clipview's GetBoundingRect
02374     // always calculates - never returns cached rectangle.
02375     Trans.bTransformYourChildren = TRUE;
02376 
02377     // ClipViews can't just transform their cached data - they have to
02378     // also transform their children because clipview's GetBoundingRect
02379     // always calculates - never returns cached rectangle.
02380     Trans.bTransformYourChildren = TRUE;
02381 
02382     // transform our children etc, in the same way a normal group would.
02383     NodeGroup::Transform(Trans);
02384 }
02385 
02386 
02387 
02388 /********************************************************************************************
02389 
02390 >   BOOL NodeClipViewController::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
02391 
02392     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02393     Created:    31 January 2000
02394     Inputs:     pFilter     pointer to a camelot file filter.
02395 
02396     Returns:    TRUE if successful,
02397                 FALSE otherwise.
02398     Purpose:    Writes this node out to a camelot document.
02399     Errors:     ERROR2 if pFilter is NULL.
02400     See also:   
02401 
02402 ********************************************************************************************/
02403 BOOL NodeClipViewController::WritePreChildrenWeb(BaseCamelotFilter* pFilter)
02404 {
02405     // validate input.
02406     ERROR2IF(pFilter == NULL, FALSE, "NULL parameter");
02407     
02408     CXaraFileRecord Rec(TAG_CLIPVIEWCONTROLLER, TAG_CLIPVIEW_CONTROLLER_SIZE);
02409 
02410     BOOL    ok = Rec.Init();
02411     if (ok) ok = (pFilter->Write(&Rec) != 0);
02412 
02413     return ok;
02414 }
02415 
02416 
02417 
02418 /********************************************************************************************
02419 
02420 >   BOOL NodeClipViewController::WritePreChildrenNative(BaseCamelotFilter* pFilter)
02421 
02422     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02423     Created:    31 January 2000
02424     Inputs:     pFilter     pointer to a camelot file filter.
02425 
02426     Returns:    TRUE if successful,
02427                 FALSE otherwise.
02428     Purpose:    Writes this node out to a camelot document.
02429     Errors:     
02430     See also:   
02431 
02432 ********************************************************************************************/
02433 BOOL NodeClipViewController::WritePreChildrenNative(BaseCamelotFilter* pFilter)
02434 {
02435     return WritePreChildrenWeb(pFilter);
02436 }
02437 
02438 
02439 
02440 /********************************************************************************************
02441 
02442 >   BOOL NodeClipViewController::PostImport()
02443 
02444     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02445     Created:    31 January 2000
02446     Returns:    TRUE if success,
02447                 FALSE if unsuccessful.
02448     Purpose:    Performs any necessary post-processing once the object has been read in
02449                 from a file.
02450     Errors:     
02451     See also:   
02452 
02453 ********************************************************************************************/
02454 BOOL NodeClipViewController::PostImport()
02455 {
02456     return UpdateKeyholePath();
02457 }
02458 
02459 
02460 
02461 /********************************************************************************************
02462 
02463 >   virtual Node* NodeClipViewController::SimpleCopy()
02464 
02465     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02466     Created:    09 February 2000
02467     Returns:    A copy of this node, or
02468                 NULL if unsuccessful.
02469     Purpose:    Copy this node.
02470     Errors:     ERROR1 returning NULL if we couldn't allocate memory for the new node.
02471     See also:   Node::SimpleCopy()
02472 
02473 ********************************************************************************************/
02474 Node* NodeClipViewController::SimpleCopy()
02475 {
02476     NodeClipViewController* pNodeCopy = new NodeClipViewController;;
02477     ERROR1IF(pNodeCopy == NULL, NULL, _R(IDE_NOMORE_MEMORY));
02478     CopyNodeContents(pNodeCopy);
02479 
02480     return pNodeCopy;
02481 }
02482 
02483 
02484 
02485 /********************************************************************************************
02486 
02487 >   virtual void NodeClipViewController::CopyNodeContents(NodeClipViewController* pNodeCopy)
02488 
02489     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02490     Created:    09 February 2000
02491     Inputs:     pNodeCopy   the node to copy our contents into.
02492     Purpose:    Copy this node's contents into pNodeCopy.
02493     Errors:     
02494     See also:   Node::CopyNodeContents()
02495 
02496 ********************************************************************************************/
02497 void NodeClipViewController::CopyNodeContents(NodeClipViewController* pNodeCopy)
02498 {
02499     // call base-class implementation; this will also perform necessary validation for us.
02500     NodeGroup::CopyNodeContents(pNodeCopy);
02501 }
02502 
02503 
02504 
02505 /***********************************************************************************************
02506 >   void NodeClipViewController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
02507 
02508     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02509     Created:    18/12/2003
02510     Outputs:    -
02511     Purpose:    Polymorphically copies the contents of this node to another
02512     Errors:     An assertion failure will occur if NodeCopy is NULL
02513     Scope:      protected
02514                                      
02515 ***********************************************************************************************/
02516 
02517 void NodeClipViewController::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
02518 {
02519     ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
02520     ENSURE(IS_A(pNodeCopy, NodeClipViewController), "PolyCopyNodeContents given wrong dest node type");
02521 
02522     if (IS_A(pNodeCopy, NodeClipViewController))
02523         CopyNodeContents((NodeClipViewController*)pNodeCopy);
02524 }
02525 
02526 
02527 
02528 
02529 /********************************************************************************************
02530 
02531 >   virtual void NodeClipViewController::PreExportRender(RenderRegion* pRegion)
02532 
02533     Author:     Chris_Gallimore (Xara Group Ltd) <camelotdev@xara.com>
02534     Created:    8th November 2000
02535     Inputs:     pRegion - the RenderRegion we're exporting to.
02536     Purpose:    Starts exporting a clipview group - basically writes a 'q' token out
02537     Errors:     
02538     See also:   Node::PreExportRender()
02539 
02540 ********************************************************************************************/
02541 void NodeClipViewController::PreExportRender(RenderRegion* pRegion)
02542 {
02543 #ifdef DO_EXPORT
02544     if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
02545     {
02546         // Output "start group" token
02547         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
02548         pDC->OutputToken(_T("q"));
02549         pDC->OutputNewLine();
02550     }
02551 PORTNOTE("cmx", "Removed use of CMXRenderRegion")
02552 #ifndef EXCLUDE_FROM_XARALX
02553     else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
02554     {
02555         // mark start of a group...
02556         CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC();
02557         DocRect BBox = GetBoundingRect();
02558         pDC->StartGroup(&BBox);
02559     }
02560 #endif
02561 #endif
02562 }
02563 
02564 /********************************************************************************************
02565 
02566 >   virtual void NodeClipViewController::ExportRender(RenderRegion* pRegion)
02567 
02568     Author:     Chris_Gallimore (Xara Group Ltd) <camelotdev@xara.com>
02569     Created:    8th November 2000
02570     Inputs:     pRegion - the RenderRegion we're exporting to.
02571     Purpose:    Finishes exporting a clipview group - basically write a 'Q' token out.
02572     Errors:     
02573     See also:   Node::ExportRender()
02574 
02575 ********************************************************************************************/
02576 BOOL NodeClipViewController::ExportRender(RenderRegion* pRegion) 
02577 {
02578 #ifdef DO_EXPORT
02579     if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion)))
02580     {
02581         // Output "end group" token
02582         EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC();
02583         pDC->OutputToken(_T("Q"));
02584         pDC->OutputNewLine();
02585         
02586         // Tell caller we rendered ourselves ok
02587         return TRUE;
02588     }
02589 PORTNOTE("epsfilter", "Removed use of CMXRenderRegion")
02590 #ifndef EXCLUDE_FROM_XARALX
02591     else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion)))
02592     {
02593         // mark start of a group...
02594         CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC();
02595         pDC->EndGroup();
02596 
02597         return TRUE;
02598     }
02599 #endif
02600 #endif
02601     // Render this node in the normal way
02602     return FALSE;
02603 }
02604 
02605 
02606 //-------------------------------------------------------------------------------------------
02607 //-------------------------------------------------------------------------------------------
02608 //-------------------------------------------------------------------------------------------
02609 //-------------------------------------------------------------------------------------------
02610 
02611 
02612 
02613 /********************************************************************************************
02614 
02615 >   ClipViewRecordHandler::ClipViewRecordHandler()
02616 
02617     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02618     Created:    09 February 2000
02619     Purpose:    Constructor
02620 
02621 ********************************************************************************************/
02622 ClipViewRecordHandler::ClipViewRecordHandler()
02623 {
02624     // empty.
02625 }
02626 
02627 
02628 
02629 /********************************************************************************************
02630 
02631 >   ClipViewRecordHandler::~ClipViewRecordHandler()
02632 
02633     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02634     Created:    09 February 2000
02635     Purpose:    Destructor
02636 
02637 ********************************************************************************************/
02638 ClipViewRecordHandler::~ClipViewRecordHandler()
02639 {
02640     // empty.
02641 }
02642 
02643 
02644 
02645 /********************************************************************************************
02646 
02647 >   virtual UINT32* ClipViewRecordHandler::GetTagList()
02648 
02649     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02650     Created:    09 February 2000
02651     Returns:    An array of the tags which we can handle, terminated by CXFRH_TAG_LIST_END.
02652     Purpose:    Gets the tag list for this handler to handle.
02653 
02654 ********************************************************************************************/
02655 UINT32* ClipViewRecordHandler::GetTagList()
02656 {
02657     static UINT32 TagList[] = { TAG_CLIPVIEWCONTROLLER, TAG_CLIPVIEW, CXFRH_TAG_LIST_END };
02658 
02659     return (UINT32*)&TagList;
02660 }
02661 
02662 
02663 
02664 /********************************************************************************************
02665 
02666 >   virtual BOOL ClipViewRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
02667 
02668     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02669     Created:    09 February 2000
02670     Inputs:     pCXaraFileRecord    pointer to the file object.
02671     Returns:    TRUE if successful,
02672                 FALSE otherwise.
02673 
02674     Purpose:    Creates a NodeClipViewController when the appropriate tag
02675                 is read in from the file.
02676     Errors:     ERROR1 if a new node cannot be created,
02677                 ERROR2 if pCXaraFileRecord is NULL,
02678                 ERROR3 if we are passed a tag we do not recognise.
02679     See also:   
02680 
02681 ********************************************************************************************/
02682 BOOL ClipViewRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord)
02683 {
02684     // validate inputs.
02685     ERROR2IF(pCXaraFileRecord == NULL, FALSE, "NULL parameter passed");
02686 
02687     // at the moment, we only handle the one tag.
02688     BOOL ok = TRUE;
02689     UINT32 Tag = pCXaraFileRecord->GetTag();
02690     switch (Tag)
02691     {
02692         case TAG_CLIPVIEWCONTROLLER:
02693         {
02694             NodeClipViewController* pNCVC = new NodeClipViewController;
02695             ERROR1IF(pNCVC == NULL, FALSE, _R(IDE_NOMORE_MEMORY));
02696             ok = InsertNode(pNCVC);
02697             break;
02698         }
02699 
02700         case TAG_CLIPVIEW:
02701         {
02702             NodeClipView* pNCV = new NodeClipView;
02703             ERROR1IF(pNCV == NULL, FALSE, _R(IDE_NOMORE_MEMORY));
02704             ok = InsertNode(pNCV);
02705             break;
02706         }
02707 
02708         default:
02709             ok = FALSE;
02710             ERROR3_PF(("ClipView file record handler - unrecognised tag: %d", Tag));
02711             break;
02712     }
02713 
02714     return ok;
02715 }
02716 
02717 
02718 
02719 //-------------------------------------------------------------------------------------------
02720 //-------------------------------------------------------------------------------------------
02721 //-------------------------------------------------------------------------------------------
02722 //-------------------------------------------------------------------------------------------
02723 
02724 
02725 
02726 /********************************************************************************************
02727 
02728 >   UpdateCachedKeyholePathAction::UpdateCachedKeyholePathAction()
02729 
02730     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02731     Created:    20 March 2000
02732     Purpose:    Constructor.
02733 
02734 ********************************************************************************************/
02735 UpdateCachedKeyholePathAction::UpdateCachedKeyholePathAction()
02736 {
02737     m_pNCVC = NULL;
02738 }
02739 
02740 
02741 
02742 /********************************************************************************************
02743 
02744 >   UpdateCachedKeyholePathAction::~UpdateCachedKeyholePathAction()
02745 
02746     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02747     Created:    20 March 2000
02748     Purpose:    Destructor.
02749 
02750 ********************************************************************************************/
02751 UpdateCachedKeyholePathAction::~UpdateCachedKeyholePathAction()
02752 {
02753     m_pNCVC = NULL;
02754 }
02755 
02756 
02757 
02758 /********************************************************************************************
02759 
02760 >   ActionCode UpdateCachedKeyholePathAction::Init( UndoableOperation* pUndoOp,
02761                                                     ActionList* pActionList,
02762                                                     NodeClipViewController* pUpdateNCVC )
02763     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02764     Created:    20 March 2000
02765     Inputs:     pUndoOp         we must either be invoked from within an undoable operation,
02766                                 or must at least have a valid pointer to one.
02767                 pActionList     the ActionList which the new Action will be put on. This will
02768                                 be either the undo or the redo action list.
02769                 pUpdateNCVC     pointer to the NodeClipViewController which this Action will
02770                                 update.
02771 
02772     Outputs:    Puts a copy of this action at the head of the given action list. The upshot
02773                 of this is that when that list, eg undo list, is executed, this action's
02774                 Execute method will be called last (the lists go backwards(??))
02775 
02776     Returns:    AC_FAIL, AC_NORECORD or AC_OK. See Action::Init() in ops.cpp for more detail.
02777 
02778     Purpose:    Action Init function.
02779                 This is the public interface through which the Action is invoked. It should
02780                 call Action::Init to actually construct a new Action and put it on the given
02781                 action list. It should usually also perform the action (we don't actually
02782                 in this case, as this action is a BODGE so we update NCVC's on undo/redo!)
02783     Errors:     
02784     See also:   
02785 
02786 ********************************************************************************************/
02787 ActionCode UpdateCachedKeyholePathAction::Init( UndoableOperation* pUndoOp,
02788                                                 ActionList* pActionList,
02789                                                 NodeClipViewController* pUpdateNCVC )
02790 {
02791     // call the base class to create and put the action onto the action list.
02792     ActionCode ac = AC_FAIL;
02793     UINT32 ActSize = sizeof(UpdateCachedKeyholePathAction);
02794     UpdateCachedKeyholePathAction* pNewAction = NULL;
02795     ac = Action::Init(  pUndoOp,
02796                         pActionList,
02797                         ActSize,
02798                         CC_RUNTIME_CLASS(UpdateCachedKeyholePathAction),
02799                         (Action**)&pNewAction  );
02800 
02801     // ok, the action now exists. initialise it with the NCVC to be updated,
02802     // and mark the NCVC as needing to update itself.
02803     if (ac != AC_FAIL && pNewAction != NULL)
02804     {
02805         pUpdateNCVC->MarkKeyholeInvalid();
02806         pNewAction->SetNCVCToUpdate(pUpdateNCVC);
02807     }
02808 
02809     // we want the update action to be called last whenever an action is undone or redone,
02810     // so it actually needs to go at the other end of the list.
02811     pActionList->RemoveItem(pNewAction);
02812     pActionList->AddHead(pNewAction);
02813 
02814     return ac;
02815 }
02816 
02817 
02818 
02819 /********************************************************************************************
02820 
02821 >   ActionCode UpdateCachedKeyholePathAction::Execute()
02822 
02823     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02824     Created:    20 March 2000
02825     Inputs:     
02826     Outputs:    
02827     Returns:    
02828     Purpose:    Called by Camelot when it meets this Action on an undo/redo ActionList.
02829                 You should undo/redo the action here and record an opposite action in
02830                 the opposite ActionList. If the action is not too complex or you can't be
02831                 bothered with two Actions, then you can use the same Action to both undo and
02832                 redo, passing it appropriate information via Init(), or other means.
02833 
02834                 This Action simply causes a specific NodeClipViewController to update its
02835                 cached keyhole path.
02836     Errors:     
02837     See also:   
02838 
02839 ********************************************************************************************/
02840 ActionCode UpdateCachedKeyholePathAction::Execute()
02841 {
02842     // complain loudly if our state is invalid - however, we can survive without it,
02843     // so we'll still return a thumbs up anyway.
02844     ERROR3IF(m_pNCVC == NULL, "UpdateCachedKeyholePathAction::Execute; NULL NCVC pointer!");
02845     
02846     // record our 'inverse' action, and as a side effect of this, our NCVC
02847     // will be told that it needs to update it cached keyhole path.
02848     ActionCode ac = AC_OK;
02849     if (m_pNCVC != NULL)
02850     {
02851         ac = UpdateCachedKeyholePathAction::Init(   (UndoableOperation*)pOperation,
02852                                                     pOppositeActLst,
02853                                                     m_pNCVC );
02854     }
02855     return ac;
02856 }
02857 
02858 
02859 
02860 /********************************************************************************************
02861 
02862 >   void UpdateCachedKeyholePathAction::SetNCVCToUpdate(NodeClipViewController* pNCVC)
02863 
02864     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
02865     Created:    20 March 2000
02866     Inputs:     pNCVC   ptr to the NCVC to update.
02867     Purpose:    Tell us which NCVC we will be updating.
02868 
02869 ********************************************************************************************/
02870 void UpdateCachedKeyholePathAction::SetNCVCToUpdate(NodeClipViewController* pNCVC)
02871 {
02872     ERROR3IF(pNCVC == NULL, "UpdateCachedKeyholePathAction::SetNCVCToUpdate; NULL param!");
02873     m_pNCVC = pNCVC;
02874 }
02875 

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