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