00001 // $Id: group.cpp 1688 2006-08-10 12:05:20Z gerry $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 00099 /* 00100 //*/ 00101 00102 #include "camtypes.h" 00103 #include "saveeps.h" 00104 #include "ccdc.h" 00105 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 #include "osrndrgn.h" 00107 //#include "simon.h" 00108 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 //#include "mario.h" 00110 //#include "opscale.h" 00111 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 #include "objchge.h" 00113 #include "blobs.h" 00114 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00115 //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00116 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00117 #include "cxftags.h" 00118 //#include "camfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00119 #include "rechgrp.h" 00120 #include "cmxrendr.h" 00121 //#include "cmxexdc.h" 00122 #include "extender.h" 00123 #include "nodeblnd.h" 00124 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00125 #include "nodebldr.h" 00126 //#include "capturemanager.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00127 //#include "phil.h" // for _R(IDB_CACHE_MARK) 00128 #include "lineattr.h" 00129 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00130 //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00131 #include "nodeliveeffect.h" 00132 #include "scanrr.h" 00133 #include "pmaskrgn.h" 00134 #include "blndhelp.h" 00135 //#include "mrhbits.h" 00136 #include "transop.h" 00137 #include "opscale.h" 00138 00139 CC_IMPLEMENT_DYNAMIC(NodeGroup, NodeCompound) 00140 CC_IMPLEMENT_DYNAMIC(NodeListItemWithDocPtr, NodeListItem) 00141 CC_IMPLEMENT_DYNAMIC(GroupRecordHandler, CamelotRecordHandler) 00142 00143 // Give this file in memory dumps 00144 // Declare smart memory handling in Debug builds 00145 #define new CAM_DEBUG_NEW 00146 00147 /********************************************************************************************* 00148 00149 > NodeGroup::NodeGroup() 00150 00151 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00152 Created: 13/5/93 00153 Inputs: - 00154 Outputs: 00155 Returns: - 00156 00157 Purpose: This constructor creates a NodeGroup linked to no other, with all status 00158 flags false and an uninitialised bounding rectangle. 00159 00160 Errors: 00161 00162 **********************************************************************************************/ 00163 00164 NodeGroup::NodeGroup(): NodeCompound() 00165 { 00166 blenderNode = NULL; 00167 } 00168 00169 /*********************************************************************************************** 00170 00171 > void NodeGroup::NodeGroup 00172 ( 00173 Node* ContextNode, 00174 AttachNodeDirection Direction, 00175 BOOL Locked = FALSE, 00176 BOOL Mangled = FALSE, 00177 BOOL Marked = FALSE, 00178 BOOL Selected = FALSE, 00179 ) 00180 00181 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00182 Created: 19/10/93 00183 00184 Inputs: ContextNode: Pointer to a node which this node is to be attached to. 00185 00186 Direction: 00187 00188 Specifies the direction in which this node is to be attached to the 00189 ContextNode. The values this variable can take are as follows: 00190 00191 PREV : Attach node as a previous sibling of the context node 00192 NEXT : Attach node as a next sibling of the context node 00193 FIRSTCHILD: Attach node as the first child of the context node 00194 LASTCHILD : Attach node as a last child of the context node 00195 00196 The remaining inputs specify the status of the node: 00197 00198 Locked: Is node locked ? 00199 Mangled: Is node mangled ? 00200 Marked: Is node marked ? 00201 Selected: Is node selected ? 00202 00203 Outputs: - 00204 Returns: - 00205 Purpose: This method initialises the node and links it to ContextNode in the 00206 direction specified by Direction. All necessary tree links are 00207 updated. 00208 00209 Errors: An assertion error will occur if ContextNode is NULL 00210 00211 00212 ***********************************************************************************************/ 00213 00214 NodeGroup::NodeGroup(Node* ContextNode, 00215 AttachNodeDirection Direction, 00216 BOOL Locked, 00217 BOOL Mangled, 00218 BOOL Marked, 00219 BOOL Selected 00220 ):NodeCompound(ContextNode, Direction, Locked, Mangled, Marked, 00221 Selected) 00222 { 00223 blenderNode = NULL; 00224 } 00225 00226 00227 00228 void NodeGroup::PreExportRender(RenderRegion* pRegion) 00229 { 00230 #ifdef DO_EXPORT 00231 if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion))) 00232 { 00233 // Output "start group" token 00234 EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC(); 00235 pDC->OutputToken(_T("u")); 00236 pDC->OutputNewLine(); 00237 } 00238 PORTNOTE("cmx", "Removed use of CMXRenderRegion") 00239 #ifndef EXCLUDE_FROM_XARALX 00240 else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion))) 00241 { 00242 // mark start of a group... 00243 CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC(); 00244 DocRect BBox = GetBoundingRect(); 00245 pDC->StartGroup(&BBox); 00246 } 00247 #endif 00248 #endif 00249 } 00250 00251 BOOL NodeGroup::ExportRender(RenderRegion* pRegion) 00252 { 00253 #ifdef DO_EXPORT 00254 if (pRegion->IsKindOf(CC_RUNTIME_CLASS(EPSRenderRegion))) 00255 { 00256 // Output "end group" token 00257 EPSExportDC *pDC = (EPSExportDC *) pRegion->GetRenderDC(); 00258 pDC->OutputToken(_T("U")); 00259 pDC->OutputNewLine(); 00260 00261 // Tell caller we rendered ourselves ok 00262 return TRUE; 00263 } 00264 PORTNOTE("cmx", "Removed use of CMXRenderRegion") 00265 #ifndef EXCLUDE_FROM_XARALX 00266 else if(pRegion->IsKindOf(CC_RUNTIME_CLASS(CMXRenderRegion))) 00267 { 00268 // mark start of a group... 00269 CMXExportDC *pDC = (CMXExportDC *) pRegion->GetRenderDC(); 00270 pDC->EndGroup(); 00271 00272 return TRUE; 00273 } 00274 #endif 00275 #endif 00276 // Render thid node in the normal way 00277 return FALSE; 00278 } 00279 00280 /********************************************************************************************* 00281 00282 > virtual BOOL NodeGroup::IsCompound(void) const 00283 00284 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00285 Created: 19/4/93 00286 Inputs: - 00287 Outputs: - 00288 Returns: TRUE if the node is a compound object, will return TRUE 00289 Purpose: For determining quickly if the node is a compound object 00290 Errors: 00291 00292 **********************************************************************************************/ 00293 00294 // Phil, 22/10/2004. Implemented in Group.h where it will be automatically inlined 00295 //BOOL NodeGroup::IsCompound() const 00296 //{ 00297 // return TRUE; // cos it is 00298 //} 00299 00300 00301 00302 /******************************************************************************************** 00303 00304 > void NodeGroup::RenderAfterSubtree(RenderRegion* pRender) 00305 00306 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00307 Created: 17/06/2004 00308 Inputs: pRender - The region to render into 00309 Purpose: Capture the group as a cached bitmap 00310 00311 ********************************************************************************************/ 00312 00313 void NodeGroup::RenderAfterSubtree(RenderRegion* pRegion) 00314 { 00315 // Deal with group transparency capture 00316 // Call Helper function to run all my cacheing functionality for me... 00317 // If we have Effect attrs applied then the user wants "tight group transparency" 00318 if (HasEffectAttrs()) 00319 CaptureTight(pRegion); 00320 else 00321 CaptureCached(pRegion); 00322 } 00323 00324 00325 00326 /******************************************************************************************** 00327 00328 > SubtreeRenderState NodeGroup::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip) 00329 00330 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00331 Created: 29/06/2004 00332 Inputs: pRender - The region render into (or use as context for decisions about subtree) 00333 ppNextNode - Address of node pointer for next node to render or run to after this 00334 bClip - flag indicating whether to clip or not 00335 Purpose: Do clever stuff on the way into a subtree, possibly modifying rendering 00336 behaviour. 00337 00338 ********************************************************************************************/ 00339 00340 SubtreeRenderState NodeGroup::RenderSubtree(RenderRegion* pRender, Node** ppNextNode, BOOL bClip) 00341 { 00342 if (pRender == NULL) // If no render region supplied, assume we need to be rendered 00343 return SUBTREE_ROOTANDCHILDREN; 00344 00345 // Go find out about my bounding rectangle 00346 DocRect BoundingRect = GetBoundingRect(); 00347 DocRect ClipRect = pRender->GetClipRect(); 00348 00349 if (bClip && !ClipRect.IsIntersectedWith(BoundingRect)) // If not within the clipping rect then 00350 return SUBTREE_NORENDER; // Don't render us or our children 00351 00352 // Ask Helper function to set up cacheing for me... 00353 // If we have Effect attrs applied then the user wants "tight group transparency" 00354 if (HasEffectAttrs()) 00355 { 00356 if (RenderTight(pRender)) 00357 return SUBTREE_NORENDER; 00358 } 00359 else 00360 { 00361 if (RenderCached(pRender)) // If we can find a cached bitmap for this node and render it 00362 { 00363 //TRACEUSER("Phil", _T("Skipping cached group %x %s\n"), this, GetRuntimeClass()->m_lpszClassName); 00364 return SUBTREE_NORENDER; // Then tell the renderer to move on without doing any more... 00365 } 00366 //TRACEUSER("Phil", _T("Re-rendering cached group %x %s\n"), this, GetRuntimeClass()->m_lpszClassName); 00367 } 00368 00369 return SUBTREE_ROOTANDCHILDREN; // Else we must render ourselves and our children 00370 } 00371 00372 00373 00374 /******************************************************************************************** 00375 00376 > virtual BOOL NodeGroup::RenderTight(RenderRegion* pRender) 00377 00378 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00379 Created: 11/01/2005 00380 Returns: TRUE if the node managed to render a cached version of itself 00381 then pNextNode contains the pointer to the node to continue rendering from 00382 FALSE if the node did not render a cached version of itself and thus 00383 normal rendering is required 00384 Purpose: Protected Helper function 00385 Allow this node to render itself using cached data if it has it and for that 00386 cached data to represent an arbitrary section of the document (usually a subtree 00387 but can be more) 00388 00389 For this implementation we assume that our cached info will represent just 00390 this subtree. 00391 00392 Also start to capture the rendered info into a cached bitmap if possible 00393 SeeAlso: NodeRenderableBounded::CaptureCached 00394 00395 ********************************************************************************************/ 00396 00397 BOOL NodeGroup::RenderTight(RenderRegion* pRender) 00398 { 00399 // If the caller wants his bitmap to respond to Effect Attributes 00400 // Then we have to behave quite differently than a normal cache/capture sequence 00401 // We must grab the bitmap regardless of the cache-enabling flag 00402 // We must grab at a controlled resolution 00403 // We must grab the whole thing 00404 // We must grab at 32BPP RGBT 00405 // 00406 // If the rendering quality is outline - no point showing cached bitmaps... 00407 if (pRender->RRQuality.GetFillQuality() <= Quality::Solid) 00408 return FALSE; 00409 00410 // If the rendering system is doing black and white only then we can't sensibly 00411 // produce a monochrome version of the cached bitmap... 00412 if (pRender->IsVeryMono() || pRender->IsHitDetect()) 00413 return FALSE; 00414 00415 // Tight groups need to be rendered in the complex bitmap stages of printing 00416 if (pRender->IsKindOf(CC_RUNTIME_CLASS(ScanningRenderRegion))) 00417 { 00418 ScanningRenderRegion* pScanner = (ScanningRenderRegion*)pRender; 00419 pScanner->ComplexShapeFoundWrapper(); 00420 00421 return TRUE; // Return TRUE so that rendering will NOT continue into the group normally 00422 } 00423 00424 CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); 00425 if (pBitmapCache==NULL) 00426 return FALSE; 00427 00428 // If this node is definitely not cached, skip the cache rendering stuff... 00429 DocRect cliprect = pRender->GetClipRect(); 00430 DocRect bounds = GetBoundingRect(); // Assure new bounds are recalced if they were invalid 00431 BOOL bRendered = FALSE; 00432 00433 CBitmapCacheKey inky(this, GetTightGroupPixelWidth(pRender)); 00434 00435 // No need to check BackmostChanged node here because we never capture 00436 // our background in a 24BPP bitmap we always capture at 32BPP using cfLOCKEDTRANSPARENT 00437 00438 // Look for a cached bitmap at the appropriate pixel size... 00439 CCachedBitmap cbmp; 00440 BOOL bFound = pBitmapCache->Lookup(inky, cbmp); 00441 if (bFound) 00442 { 00443 // If cached bitmap covers the current clipped object rect, we can use it... 00444 DocRect clippedboundrect = cliprect.Intersection(bounds); 00445 if (cbmp.GetCachedRect().ContainsRect(clippedboundrect)) 00446 { 00447 //TRACEUSER("Phil", _T("Rendering cached bitmap for %x %s\n"), this, GetRuntimeClass()->GetClassName() ); 00448 bRendered = pRender->RenderBits(cbmp.pbmpInfo, cbmp.pbmpBits, &cbmp.coord0, 3, TRUE, this); 00449 } 00450 } 00451 00452 if (bRendered) 00453 return bRendered; 00454 00455 // If we couldn't find or render a cached bitmap then try to cache a new one 00456 // double ScaledPixelWidth = pRender->GetScaledPixelWidth(); 00457 00458 // Work out how much of the object we propose to capture 00459 DocRect viewrect = cliprect; 00460 DocRect CaptureRect = bounds; 00461 00462 if (!pRender->IsPrinting()) 00463 { 00464 View* pView = pRender->GetRenderView(); 00465 Spread* pSpread = pRender->GetRenderSpread(); 00466 if (pView && pSpread) 00467 { 00468 viewrect = pView->GetDocViewRect(pSpread); 00469 pSpread->DocCoordToSpreadCoord(&viewrect); 00470 if (!viewrect.IsValid() || !viewrect.ContainsRect(cliprect)) 00471 viewrect = cliprect; 00472 } 00473 } 00474 00475 CaptureRect = CaptureRect.Intersection(viewrect); // Proposed capture area is obj x viewport 00476 00477 // If our capture rect is invalid (no intersection) then don't do any rendering 00478 if (!CaptureRect.IsValid()) 00479 return(TRUE); 00480 00481 if (!pRender->IsPrinting()) 00482 { 00483 // If we're nearly going to capture the whole object 00484 // (Compare area of bounding rect with area of view rect so that bitmap sizes don't get out of control) 00485 XLONG areaBounds = (XLONG)bounds.Width()*(XLONG)bounds.Height(); 00486 XLONG area2View = (XLONG)2*(XLONG)viewrect.Width()*(XLONG)viewrect.Height(); 00487 if (areaBounds < area2View) 00488 { 00489 // Then grab the whole thing 00490 // (Bounding Rect is small enough for us to capture the whole object ignoring clipping rect) 00491 CaptureRect = bounds; 00492 } 00493 } 00494 00495 // Inflate by one pixel all round to allow for anti-aliasing effects at the edges 00496 CaptureRect.Inflate((INT32)GetTightGroupPixelWidth(pRender)); 00497 00498 BOOL bFullCoverage = CaptureRect.ContainsRect(bounds); 00499 00500 // Finally start the Capture @ 32BPP 00501 CaptureFlags caFlags = CaptureFlags(cfLOCKEDTRANSPARENT | cfUNCLIP | (bFullCoverage ? cfFULLCOVERAGE : cfNONE)); 00502 //TRACEUSER("Phil", _T("Starting TightGroup Capture %x, %f\n"), this, GetTightGroupPixelsPerInch(pRender)); 00503 pRender->StartCapture(this, CaptureRect, CAPTUREINFO(ctNESTABLE, caFlags), TRUE, FALSE, GetTightGroupPixelsPerInch(pRender)); 00504 00505 return bRendered; 00506 } 00507 00508 00509 00510 00511 /******************************************************************************************** 00512 00513 > virtual BOOL NodeGroup::CaptureTight(RenderRegion* pRender) 00514 00515 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00516 Created: 11/01/2005 00517 Returns: TRUE if captured data was cached 00518 Purpose: Protected Helper function 00519 Use the CaptureManager to capture the results of rendering, cache them 00520 and associate them with this Ink node 00521 SeeAlso: NodeRenderableBounded::RenderCached 00522 00523 ********************************************************************************************/ 00524 00525 BOOL NodeGroup::CaptureTight(RenderRegion* pRender) 00526 { 00527 // If the caller wants his bitmap to respond to Effect Attributes 00528 // Then we have to behave quite differently than a normal cache/capture sequence 00529 // We must grab the bitmap regardless of the cache-enabling flag 00530 // We must grab at a controlled resolution 00531 // We must grab the whole thing 00532 // We must grab at 32BPP RGBT 00533 Capture* pCapture = pRender->GetTopCapture(); 00534 if (pCapture==NULL) // If nothing was captured 00535 return FALSE; // Then do nothing 00536 00537 CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); 00538 if (pBitmapCache==NULL) 00539 { 00540 return FALSE; 00541 } 00542 00543 // ERROR3IF(IsDragged() || m_bCachedBitmapShared, "CaptureTight asked to capture when its dragged"); 00544 // ERROR3IF(IsDragged(), "CaptureTight asked to capture when its dragged"); 00545 00546 // Only stop the capture if we started it 00547 // (means we only capture whole subtrees at the mo.) 00548 BOOL bCached = FALSE; 00549 if (pCapture->GetOwner()==this) 00550 { 00551 // End this capture: 00552 // Blit capture to screen 00553 // Retain bitmap because we will release it ourselves onyl if we fail to cache it 00554 LPBITMAPINFO lpInfo = NULL; 00555 LPBYTE lpBits = NULL; 00556 DocRect CaptureRect = GetBoundingRect(); // Set maximum size we allow 00557 BOOL bFullCoverage = pCapture->HasFullCoverage(); 00558 pRender->StopCapture(this, FALSE, FALSE, &lpInfo, &lpBits, &CaptureRect); 00559 00560 // If the capture gave us back a bitmap, try to cache it 00561 if (lpInfo && lpBits && CaptureRect.IsValid()) 00562 { 00563 //TRACEUSER("Phil", _T("Ending TightGroup Capture SUCCEEDED %x, %f\n"), this, GetTightGroupPixelWidth(pRender)); 00564 // Cache the ORIGINAL bitmap as Option 0 00565 // See also, SetOriginalBitmap 00566 double PixelWidth = GetTightGroupPixelWidth(pRender); 00567 CBitmapCacheKey inky(this, PixelWidth, 0); 00568 00569 CCachedBitmap cbmp; 00570 00571 cbmp.pbmpBits = lpBits; 00572 cbmp.pbmpInfo = lpInfo; 00573 cbmp.SetCachedRect(CaptureRect); 00574 cbmp.nPriority = CACHEPRIORITY_TEMPBITMAP_HIGH; 00575 cbmp.bFullCoverage = bFullCoverage; 00576 00577 if ( cbmp.IsValid() 00578 && !pRender->IsKindOf(CC_RUNTIME_CLASS(PrintingMaskedRenderRegion)) 00579 ) 00580 { 00581 pBitmapCache->StoreBitmap(inky, cbmp); 00582 MayBeCached = TRUE; 00583 bCached = TRUE; 00584 } 00585 00586 // Render the bitmap taking effect attrs into account... 00587 if (lpInfo && lpBits) 00588 pRender->RenderBits(lpInfo, lpBits, CaptureRect, TRUE, this); 00589 } 00590 00591 // If we failed to cache the captured bitmap then release it 00592 if (lpInfo!=NULL && lpBits!=NULL && !bCached) 00593 { 00594 //TRACEUSER("Phil", _T("Ending TightGroup Capture FAILED %x, %f\n"), this, GetTightGroupPixelWidth(pRender)); 00595 FreeDIB(lpInfo, lpBits, NULL, FALSE); 00596 } 00597 } 00598 00599 return bCached; 00600 } 00601 00602 /******************************************************************************************** 00603 00604 > virtual double NodeGroup::GetTightGroupPixelsPerInch(RenderRegion* pRender) 00605 00606 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00607 Created: 20/01/2005 00608 Inputs: - 00609 Returns: pixel width for capturing 00610 Purpose: Get width of pixels for use in capturing this group as a tight group bitmap 00611 00612 ********************************************************************************************/ 00613 00614 double NodeGroup::GetTightGroupPixelsPerInch(RenderRegion* pRender) const 00615 { 00616 double dPPI = 0.0; 00617 00618 if (NodeBitmapEffect::GroupPixelsPerInch==0) 00619 { 00620 // 0 means automatic 00621 // Automatic means get resolution in context 00622 // Context means either respond to a fixed resolution above us in the tree 00623 // or get resolution from current view 00624 double dViewPPI = 96.0; 00625 double dViewScaledPPI = 96.0; 00626 if (pRender==NULL) 00627 { 00628 View* pView = View::GetCurrent(); 00629 if (pView) 00630 dViewPPI = 72000.0 / pView->GetPixelWidth().MakeDouble(); 00631 dViewScaledPPI = 72000.0 / pView->GetScaledPixelWidth().MakeDouble(); 00632 } 00633 else 00634 { 00635 dViewPPI = pRender->GetPixelsPerInch(); 00636 dViewScaledPPI = 72000.0 / pRender->GetScaledPixelWidth(); 00637 } 00638 00639 Node* pParent = this->FindParent(); 00640 while (pParent && !pParent->IsLayer()) 00641 { 00642 if (pParent->IsBitmapEffect()) 00643 { 00644 dPPI = ((NodeBitmapEffect*)pParent)->GetPixelsPerInchValue(); 00645 if (dPPI!=0) 00646 break; 00647 00648 // If we found an effect at all then ensure we use default effect PPI 00649 dPPI = dViewPPI; 00650 } 00651 00652 pParent = pParent->FindParent(); 00653 } 00654 00655 // If we found no effects above us then try to operate at zoomed view res 00656 // for best results on screen. 00657 if (dPPI==0) 00658 { 00659 // See also OpEffectLock::DoEffectNodeOp 00660 // We should capture at at least 200dpi for printing 00661 // We should capture at a size that's at least as good as the current zoomed pixel size 00662 // (We rely on the capture being clipped to keep bitmap sizes under control at high zoom factors) 00663 // (We should reduce the resolution if the bitmap will be huge...) 00664 dPPI = dViewScaledPPI; 00665 } 00666 } 00667 00668 // Try to limit bitmap size if dpi is getting large inside a wrapped 00669 // RenderRegion. 00670 // Without this restriction, tight groups inside shadows become a problem 00671 // because this function does not pick up the reduced resolution of the 00672 // offscreen render region used by the shadow to create its silhouette. 00673 if (pRender && pRender->IsWrappedRender() && dPPI>480) 00674 { 00675 dPPI = 480; 00676 } 00677 00678 if (dPPI==0) 00679 dPPI = NodeBitmapEffect::GroupPixelsPerInch; 00680 00681 if (dPPI==0) 00682 { 00683 // Shouldn't ever reach this clause but just in case... 00684 ERROR3("GetPixelsPerInch can't get sensible PPI so defaulting to 96"); 00685 dPPI = 96.0; 00686 } 00687 00688 TRACEUSER("Phil", _T("GTGPPI %f\n"), dPPI); 00689 return dPPI; 00690 } 00691 00692 /******************************************************************************************** 00693 00694 > double NodeGroup::GetTightGroupPixelWidth(RenderRegion* pRender) const 00695 00696 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00697 Created: 20/01/2005 00698 Inputs: - 00699 Returns: pixel width for capturing 00700 Purpose: Get width of pixels for use in capturing this group as a tight group bitmap 00701 00702 ********************************************************************************************/ 00703 00704 /*double NodeGroup::GetTightGroupPixelWidth(RenderRegion* pRender) const 00705 { 00706 return 72000.0/GetTightGroupPixelsPerInch(pRender); 00707 }*/ 00708 00709 00710 /*double NodeGroup::GetTightGroupPixelWidth(RenderRegion* pRender) const 00711 { 00712 // We should capture at at least 200dpi for printing 00713 // We should capture at a size that's at least as good as the current zoomed pixel size 00714 // (We rely on the capture being clipped to keep bitmap sizes under control at high zoom factors) 00715 // (We should reduce the resolution if the bitmap will be huge...) 00716 double pixwidth = 72000.0/200.0; 00717 double scaledwidth = 72000.0/200.0; 00718 00719 View* pView = View::GetCurrent(); 00720 if (pView!=NULL) 00721 scaledwidth = pView->GetScaledPixelWidth().MakeDouble(); 00722 00723 // If our proposed pixels are bigger than screen pixels we'll use screen pixel width instead 00724 if (pixwidth > scaledwidth) 00725 pixwidth = scaledwidth; 00726 00727 return pixwidth; 00728 }*/ 00729 00730 /******************************************************************************************** 00731 00732 > void NodeGroup::IsValidEffectAttr(CCRuntimeClass* pAttrClass) const 00733 00734 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00735 Created: 18/01/2005 00736 Inputs: Attribute class 00737 Returns: TRUE if the attr class is a valid effect attr 00738 Purpose: Determine whether this type of attribute can be an effect attribute 00739 On this node at this time. 00740 00741 ********************************************************************************************/ 00742 00743 /*BOOL NodeGroup::IsValidEffectAttr(CCRuntimeClass* pAttrClass) const 00744 { 00745 return (AttrFillGeometry::s_bGroupTransparency && 00746 (pAttr->IsATranspFill() || 00747 pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrTranspChange)) || 00748 pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeTranspChange) 00749 ) 00750 ); 00751 }*/ 00752 00753 00754 00755 00756 /******************************************************************************************** 00757 00758 > void NodeGroup::IsValidEffectAttr(NodeAttribute* pAttr) const 00759 00760 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00761 Created: 18/01/2005 00762 Inputs: Attribute 00763 Returns: TRUE if the attr is a valid effect attr 00764 Purpose: Determine whether this attribute instance can be an effect attribute 00765 On this node at this time. 00766 00767 ********************************************************************************************/ 00768 00769 BOOL NodeGroup::IsValidEffectAttr(NodeAttribute* pAttr) const 00770 { 00771 // return (AttrFillGeometry::s_bGroupTransparency && // If pref allows group transparency 00772 // (pAttr->IsATranspFill() || // And the attribute is a transparency type 00773 // pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrTranspChange)) || // Return TRUE to apply the attr as an effect 00774 // pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeTranspChange)) 00775 // ) 00776 // ); 00777 CCRuntimeClass* pAttrType = pAttr->GetAttributeType(); 00778 00779 return (AttrFillGeometry::s_bGroupTransparency && // If pref allows group transparency 00780 (pAttr->IsATranspFill() || 00781 pAttrType == CC_RUNTIME_CLASS(AttrTranspFillGeometry) || //->IsKindOf(CC_RUNTIME_CLASS(AttrTranspChange)) || 00782 pAttrType == CC_RUNTIME_CLASS(AttrStrokeTransp) || //pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeTranspChange)) || 00783 pAttrType == CC_RUNTIME_CLASS(AttrTranspFillMapping) 00784 ) 00785 ); 00786 } 00787 00788 00789 00790 00791 /*********************************************************************************************** 00792 00793 > virtual void NodeGroup::Render(RenderRegion* pRRegion) 00794 00795 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00796 Created: 19/01/2005 00797 Inputs: Pointer to a render region 00798 Purpose: Will render the group iff we have it cached - for hit detection reasons 00799 00800 ***********************************************************************************************/ 00801 00802 // NOTE: No need for a render function because this effect does not add any pixel data 00803 // to those rendered by its children - just let the children render themselves into 00804 // the hit-detection bitmap 00805 /* 00806 void NodeGroup::Render(RenderRegion* pRender) 00807 { 00808 // Tight groups need to be rendered in the complex bitmap stages of printing 00809 if (HasEffectAttrs() && pRender->IsKindOf(CC_RUNTIME_CLASS(ScanningRenderRegion))) 00810 { 00811 ScanningRenderRegion* pScanner = (ScanningRenderRegion*)pRender; 00812 pScanner->ComplexShapeFoundWrapper(); 00813 } 00814 }*/ 00815 /*void NodeGroup::Render(RenderRegion* pRender) 00816 { 00817 CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); 00818 if (pBitmapCache!=NULL && HasEffectAttrs() && pRender->IsHitDetect()) 00819 { 00820 CBitmapCacheKey inky(this, GetTightGroupPixelWidth(pRender)); 00821 CCachedBitmap cbmp; 00822 BOOL bFound = pBitmapCache->Lookup(inky, cbmp); 00823 if (bFound) 00824 pRender->RenderBits(cbmp.pbmpInfo, cbmp.pbmpBits, &cbmp.coord0, 3, TRUE, this); 00825 } 00826 }*/ 00827 00828 00829 00830 00831 /******************************************************************************************** 00832 00833 > void NodeGroup::RenderObjectBlobs(RenderRegion* pRender) 00834 00835 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00836 Created: 23/6/94 00837 Inputs: pRender - The region to draw the blobs in 00838 Purpose: Renders the object blobs 00839 00840 ********************************************************************************************/ 00841 00842 void NodeGroup::RenderObjectBlobs(RenderRegion* pRegion) 00843 { 00844 NodeCompound::RenderObjectBlobs(pRegion); 00845 00846 #if !defined(EXCLUDE_FROM_RALPH) 00847 // Find out about the groups bounding rect 00848 DocRect BoundingRect = GetBoundingRect(); 00849 00850 // Find out where to draw the blobs 00851 DocCoord Low = BoundingRect.LowCorner(); 00852 DocCoord High = BoundingRect.HighCorner(); 00853 00854 // Set the colours of the blobs 00855 pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB); 00856 pRegion->SetLineColour(COLOUR_NONE); 00857 00858 // Draw all the blobs 00859 pRegion->DrawBlob(Low, BT_UNSELECTED); 00860 pRegion->DrawBlob(High, BT_UNSELECTED); 00861 pRegion->DrawBlob(DocCoord(Low.x, High.y), BT_UNSELECTED); 00862 pRegion->DrawBlob(DocCoord(High.x, Low.y), BT_UNSELECTED); 00863 00864 #endif 00865 } 00866 00867 00868 00869 /******************************************************************************************** 00870 00871 > void NodeGroup::RenderTinyBlobs(RenderRegion* pRender) 00872 00873 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00874 Created: 23/6/94 00875 Inputs: pRender - The region to draw the blobs in 00876 Purpose: Renders the tiny blobs for a group (A Single blob on the topmost child object 00877 in the group) 00878 00879 ********************************************************************************************/ 00880 00881 void NodeGroup::RenderTinyBlobs(RenderRegion* pRegion) 00882 { 00883 #if !defined(EXCLUDE_FROM_RALPH) 00884 if (NodeRenderableBounded::bShowCacheBlobs) 00885 { 00886 // To help see what the cacheing system is doing 00887 // If this node has a cached bitmap associated with it 00888 // Render a special blob to show that... 00889 CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); 00890 if (pBitmapCache && MayBeCached && !HasEffectAttrs()) 00891 { 00892 CBitmapCacheKey inky(this, pRegion->GetScaledPixelWidth()); //GetScaledPixelWidthDouble? 00893 CCachedBitmap cbmp; 00894 BOOL bFound = pBitmapCache->Lookup(inky, cbmp); 00895 if (bFound) 00896 { 00897 // Find out about the groups bounding rect 00898 DocRect BoundingRect = GetBoundingRect(); 00899 00900 // Draw a blob 00901 if (cbmp.IsTransparent()) 00902 pRegion->DrawBitmapBlob(DocCoord(BoundingRect.hi.x - pRegion->GetScaledPixelWidth(), BoundingRect.lo.y - pRegion->GetScaledPixelWidth()), _R(IDB_CACHE_MARK_32)); 00903 else 00904 pRegion->DrawBitmapBlob(DocCoord(BoundingRect.hi.x - pRegion->GetScaledPixelWidth(), BoundingRect.lo.y - pRegion->GetScaledPixelWidth()), _R(IDB_CACHE_MARK_24)); 00905 } 00906 } 00907 } 00908 00909 // get the topmost object in this group and tell it to render its tiny blobs. 00910 Node* pNode = FindLastChild(); 00911 while (pNode != NULL && !pNode->IsAnObject()) 00912 pNode = pNode->FindPrevious(); 00913 00914 // empty groups are not allowed! 00915 if (pNode == NULL) 00916 { 00917 ERROR3("NodeGroup::RenderTinyBlobs; This group is empty! Shouldn't be!"); 00918 return; 00919 } 00920 else 00921 { 00922 ((NodeRenderableInk*)pNode)->RenderTinyBlobs(pRegion); 00923 } 00924 00925 #endif 00926 } 00927 00928 00929 /******************************************************************************************** 00930 00931 > void NodeGroup::Transform( TransformBase& Trans ) 00932 00933 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00934 Created: 23/6/94 00935 Inputs: Trans - A transformation object 00936 Purpose: Transforms the group object and all its children. 00937 00938 ********************************************************************************************/ 00939 00940 void NodeGroup::Transform( TransformBase& Trans ) 00941 { 00942 View* pView = View::GetCurrent(); 00943 if (GroupCanTransformCached(Trans) && pView) 00944 // if (GroupCanTransformCached() && pView) 00945 { 00946 // Note! View::GetScaledPixelWidth returns an unrounded fractional value where 00947 // RenderRegion::GetScaledPixelWidth return a rounded integer!!! 00948 // 00949 // Note! It would be better not to call HasEffectAttrs() here because it can be slow in large groups 00950 if (HasEffectAttrs()) 00951 TransformTight(Trans, GetTightGroupPixelsPerInch()); 00952 else 00953 TransformCached(Trans, (MILLIPOINT)(pView->GetScaledPixelWidth().MakeDouble()+0.5)); 00954 } 00955 else 00956 { 00957 // Our cached data can't be transformed so we must remove it because 00958 // it will now be out of date 00959 // We must do it directly because ReleaseCached does conditional 00960 // stuff based on IsDragged 00961 //TRACEUSER("Phil", _T("NodeGroup::Transform releasing cache\n")); 00962 CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); 00963 if (pBitmapCache!=NULL) 00964 { 00965 CBitmapCacheKey inky(this, 42); 00966 pBitmapCache->RemoveAllOwnedBitmaps(inky); 00967 MayBeCached = FALSE; 00968 Trans.bHaveTransformedCached = FALSE; 00969 Trans.bTransformYourChildren = TRUE; 00970 } 00971 } 00972 00973 // Optimisation: 00974 // If we have transformed our cached data 00975 // And we're dragging 00976 // And we were holding cached data (sanity check) 00977 // (And /assuming/ that the cached data fully represents the child objects!!!) 00978 // 00979 // Then we can avoid transforming our children to save time 00980 // 00981 // This is safe because we know that the dragged objects will not remain permanently in the 00982 // document so it doesn't matter if they're not up to date... 00983 // 00984 BOOL bTransCache = Trans.bHaveTransformedCached; 00985 if (Trans.bTransformYourChildren) 00986 { 00987 // Transform all the children 00988 NodeRenderableInk::TransformChildren(Trans); 00989 } 00990 else 00991 { 00992 // We can get away with just transforming our effect attrs 00993 TransformEffectAttrs(Trans); 00994 Trans.bHaveTransformedChildren = FALSE; 00995 } 00996 Trans.bHaveTransformedCached = bTransCache; 00997 00998 // If we have successfully transformed the cached data for this 00999 // contour then we can simply transform the bounds to suit... 01000 // (Some derived classes might not be able to compute bounds 01001 // if their children have not been transformed.) 01002 // (Bounds have to be correct for RenderCached to allow the cached 01003 // bitmap to be used during dragging.) 01004 // 01005 // We can only do this for translations at the moment 01006 // 01007 if (Trans.bHaveTransformedCached && Trans.IsTranslation()) 01008 { 01009 Trans.Transform(&BoundingRectangle.lo, 1); 01010 Trans.Transform(&BoundingRectangle.hi, 1); 01011 /* The following code doesn't work because it keeps on transforming 01012 the results of previous transformed bounding rects, iteratively getting 01013 bigger and bigger. 01014 It needs to be able to work on the original bounding rect all the time 01015 DocCoord temp[4]; 01016 temp[0] = BoundingRectangle.lo; 01017 temp[1] = BoundingRectangle.hi; 01018 temp[2] = DocCoord(BoundingRectangle.lox, BoundingRectangle.hiy); 01019 temp[3] = DocCoord(BoundingRectangle.hix, BoundingRectangle.loy); 01020 01021 Trans.Transform(temp, 4); 01022 01023 BoundingRectangle.lo = temp[0]; 01024 BoundingRectangle.hi = temp[0]; 01025 BoundingRectangle.IncludePoint(temp[1]); 01026 BoundingRectangle.IncludePoint(temp[2]); 01027 BoundingRectangle.IncludePoint(temp[3]); 01028 */ 01029 } 01030 else 01031 { 01032 // The groups bounding rect is no longer valid 01033 // ERROR3IF(!IS_A(this, NodeGroup) && !Trans.bHaveTransformedChildren, "Compound children not transformed but compound bounding rect depends on them\n"); 01034 InvalidateBoundingRect(); 01035 } 01036 01037 // -------------------------------------------------------------------- 01038 // CGS: We need to check for a blend on a path here .... 01039 if (IS_A (this, NodeBlend)) 01040 { 01041 NodeBlend* ptrBlend = (NodeBlend*) this; 01042 NodeBlender* ptrBlender = (NodeBlender*) ptrBlend->FindFirstBlender (); 01043 01044 if (ptrBlender) 01045 { 01046 ptrBlender->UpdateBlendStartAngles ((Trans2DMatrix&) Trans); 01047 01048 BOOL done = FALSE; 01049 01050 while (!done) 01051 { 01052 ptrBlender = ptrBlend->FindNextBlender (ptrBlender); 01053 01054 if (!ptrBlender) 01055 { 01056 done = TRUE; 01057 } 01058 else 01059 { 01060 ptrBlender->UpdateBlendStartAngles ((Trans2DMatrix&) Trans); 01061 } 01062 } 01063 } 01064 } 01065 } 01066 01067 01068 01069 01070 /******************************************************************************************** 01071 01072 > virtual void NodeGroup::TransformTight(TransformBase& Trans, double dTestPixelWidth) 01073 01074 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01075 Created: 22/10/2004 01076 Inputs: Trans - Transformation 01077 dPixelWidth - PixelWidth to be tested specifically to control Trans.bHaveTransformedCached 01078 Returns: - 01079 Purpose: Transform all the cached bitmaps associated with this node 01080 SeeAlso: NodeRenderableInk::RenderCached, CaptureCached 01081 01082 ********************************************************************************************/ 01083 01084 void NodeGroup::TransformTight(TransformBase& Trans, double dTestPixelWidth) 01085 { 01086 CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); 01087 01088 CBitmapCacheKey inky(this, 42); 01089 CCacheKeyMap::iterator pos = pBitmapCache->GetStartPosition(); 01090 CCachedBitmap abitmap = pBitmapCache->FindNextOwnedBitmap(pos, inky); 01091 MayBeCached = abitmap.IsValid(); // Update MayBeCached here because we can 01092 BOOL bTransformedTested = FALSE; 01093 while (abitmap.IsValid()) 01094 { 01095 abitmap.Transform(Trans); 01096 pBitmapCache->StoreBitmap(inky, abitmap); 01097 01098 if (inky.GetPixelWidth() == dTestPixelWidth && abitmap.IsTransparent()) 01099 bTransformedTested = TRUE; 01100 01101 abitmap = pBitmapCache->FindNextOwnedBitmap(pos, inky); 01102 } 01103 01104 // We can only continue to transform cached things if all our bitmaps are transparent (32BPP) 01105 // And if we actually had some cached data to transform 01106 if (!bTransformedTested) 01107 { 01108 Trans.bHaveTransformedCached = FALSE; 01109 Trans.bTransformYourChildren = TRUE; 01110 } 01111 } 01112 01113 01114 01115 01116 /******************************************************************************************** 01117 01118 > virtual DocRect NodeGroup::GetEorDragBoundingRect() 01119 01120 Author: David_McClarnon (Xara Group Ltd) <camelotdev@xara.com> 01121 Created: 20/1/2000 01122 Inputs: None 01123 Returns: The eor drag bounding rect for all of my children 01124 Purpose: Gets the eor drag bounding rect for all my children 01125 SeeAlso: - 01126 01127 ********************************************************************************************/ 01128 DocRect NodeGroup::GetEorDragBoundingRect() 01129 { 01130 Node * pChild = FindFirstChild(); 01131 01132 DocRect dr(0,0,0,0); 01133 01134 while (pChild) 01135 { 01136 if (pChild->IsBounded()) 01137 { 01138 dr = dr.Union(((NodeRenderableBounded *)pChild)->GetEorDragBoundingRect()); 01139 } 01140 01141 pChild = pChild->FindNext(); 01142 } 01143 01144 return dr; 01145 } 01146 01147 /******************************************************************************************** 01148 01149 > DocRect NodeGroup::GetBoundingRect(BOOL DontUseAttrs=FALSE, BOOL HitTest=FALSE) 01150 01151 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01152 Created: 23/6/94 01153 Inputs: DontUseAttrs - TRUE if the attrs associated with the node should be 01154 ignored. defaults to FALSE 01155 HitTest - TRUE if being called during HitTest 01156 Returns: The Groups bounding rect 01157 Purpose: If the groups bounding rect is known then it is returned. If not, it is 01158 re-calculated. 01159 01160 ********************************************************************************************/ 01161 01162 DocRect NodeGroup::GetBoundingRect(BOOL DontUseAttrs, BOOL HitTest) 01163 { 01164 if (!IsBoundingRectValid || DontUseAttrs) 01165 { 01166 // Optimisation: 01167 // If we're holding cached data that is a full representation of our children 01168 // And we're being dragged 01169 // And the bounding box should include attrs (the cached data includes attrs) 01170 // 01171 // Then we can avoid calling our children recursively to get their bounding 01172 // rectangles because the info is all contained in our cached data's bounding 01173 // rectangle. 01174 // 01175 if (MayBeCached && IsDragged() && !DontUseAttrs) 01176 { 01177 CBitmapCache* pBitmapCache = Camelot.GetBitmapCache(); 01178 CCachedBitmap cbmp; 01179 BOOL bFound = FALSE; 01180 if (HasEffectAttrs()) 01181 { 01182 CBitmapCacheKey inky(this, GetTightGroupPixelWidth()); 01183 bFound = pBitmapCache->Lookup(inky, cbmp); 01184 bFound = bFound && cbmp.bFullCoverage; 01185 } 01186 else 01187 { 01188 View* pCurrentView = View::GetCurrent(); 01189 if (pCurrentView) 01190 { 01191 // Note! View::GetScaledPixelWidth returns an unrounded fractional value where 01192 // RenderRegion::GetScaledPixelWidth return a rounded integer!!! 01193 CBitmapCacheKey inky(this, (MILLIPOINT)(pCurrentView->GetScaledPixelWidth().MakeDouble()+0.5)); 01194 bFound = pBitmapCache->Lookup(inky, cbmp); 01195 bFound = bFound && cbmp.bFullCoverage; 01196 } 01197 } 01198 if (bFound) 01199 { 01200 BoundingRectangle = cbmp.GetCachedRect(); 01201 IsBoundingRectValid = TRUE; 01202 return BoundingRectangle; 01203 } 01204 } 01205 01206 // Find the groups first child 01207 DocRect NewRect(0,0,0,0); 01208 Node* pNode = FindFirstChild(); 01209 01210 while (pNode!=NULL) 01211 { 01212 // if this node is bounded, union its bounds with the others 01213 if (pNode->IsBounded()) 01214 NewRect = NewRect.Union(((NodeRenderableBounded*)pNode)->GetBoundingRect(DontUseAttrs)); 01215 01216 // Get the next node in the list 01217 pNode = pNode->FindNext(); 01218 } 01219 01220 if (DontUseAttrs) 01221 { 01222 // just return this rectangle as it is not the nodes true bounding rect 01223 return NewRect; 01224 } 01225 01226 // copy the new docrect into the node bounding rect 01227 BoundingRectangle = NewRect; 01228 01229 // Mark the bounding rect as valid 01230 IsBoundingRectValid = TRUE; 01231 } 01232 01233 // Return the resulting bounding rect 01234 return BoundingRectangle; 01235 } 01236 01237 01238 /******************************************************************************************** 01239 > DocRect NodeGroup::GetBlobBoundingRect() 01240 01241 Author: Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com> (re-written) 01242 Created: 29/6/95 01243 Returns: DocRect - The bounding rect of the group and its blobs 01244 Purpose: Finds the bounding rect of the group along with its blobs. Its main use is 01245 to determine which areas of the document to invalidate 01246 ********************************************************************************************/ 01247 01248 DocRect NodeGroup::GetBlobBoundingRect() 01249 { 01250 DocRect NewRect = DocRect(0,0,0,0); 01251 01252 #if !defined(EXCLUDE_FROM_RALPH) 01253 // Find the Blob manager 01254 BlobManager* pBlobMgr = GetApplication()->GetBlobManager(); 01255 if (pBlobMgr!= NULL) 01256 { 01257 BlobStyle VisibleBlobs = pBlobMgr->GetCurrentInterest(TRUE); 01258 if (VisibleBlobs.Object) 01259 { 01260 DocRect BoundingRect = GetBoundingRect(); 01261 01262 // Object blobs are visible, so include blob in each 01263 // corner of the group bounds 01264 DocRect BlobSize; 01265 01266 // Find out where the blobs will be drawn 01267 DocCoord Low = BoundingRect.LowCorner(); 01268 DocCoord High = BoundingRect.HighCorner(); 01269 01270 // Include the object blob in each corner 01271 pBlobMgr->GetBlobRect(Low, &BlobSize); 01272 NewRect = NewRect.Union(BlobSize); 01273 pBlobMgr->GetBlobRect(High, &BlobSize); 01274 NewRect = NewRect.Union(BlobSize); 01275 pBlobMgr->GetBlobRect(DocCoord(Low.x, High.y), &BlobSize); 01276 NewRect = NewRect.Union(BlobSize); 01277 pBlobMgr->GetBlobRect(DocCoord(High.x, Low.y), &BlobSize); 01278 NewRect = NewRect.Union(BlobSize); 01279 } 01280 } 01281 01282 Node* pNode=FindFirstChild(); 01283 while (pNode!=NULL) 01284 { 01285 if (pNode->IsBounded()) 01286 NewRect = NewRect.Union(((NodeRenderableBounded*)pNode)->GetBlobBoundingRect()); 01287 pNode = pNode->FindNext(); 01288 } 01289 01290 IncludeChildrensBoundingRects(&NewRect); 01291 #endif 01292 return NewRect; 01293 } 01294 01295 01296 /******************************************************************************************** 01297 01298 > virtual String NodeGroup::Describe(BOOL Plural, BOOL Verbose = TRUE) 01299 01300 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01301 Created: 18/6/93 01302 Inputs: Plural: Flag indicating if the string description should be plural or 01303 singular. 01304 Outputs: - 01305 Retuns: Description of the group node 01306 Purpose: To return a description of the Group object in either the singular or the 01307 plural. This method is called by the DescribeRange method. 01308 01309 The description will always begin with a lower case letter. 01310 01311 Errors: (Need to do this) 01312 SeeAlso: - 01313 01314 ********************************************************************************************/ 01315 01316 String NodeGroup::Describe(BOOL Plural, BOOL Verbose) 01317 { 01318 if (Plural) 01319 return(String(_R(IDS_GROUP_DESCRP))); 01320 else 01321 return(String(_R(IDS_GROUP_DESCRS))); 01322 }; 01323 01324 01325 /*********************************************************************************************** 01326 > Node* NodeGroup::SimpleCopy() 01327 01328 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01329 Created: 28/4/93 01330 Inputs: - 01331 Outputs: 01332 Returns: A copy of the node, or NULL if memory has run out 01333 01334 Purpose: This method returns a shallow copy of the node with all Node pointers NULL. 01335 The function is virtual, and must be defined for all derived classes. 01336 01337 Errors: If memory runs out when trying to copy, then ERROR is called with an out of memory 01338 error and the function returns NULL. 01339 01340 **********************************************************************************************/ 01341 01342 Node* NodeGroup::SimpleCopy() 01343 { 01344 NodeGroup* NodeCopy; 01345 NodeCopy = new NodeGroup(); 01346 ERRORIF(NodeCopy == NULL, _R(IDE_NOMORE_MEMORY), NULL); 01347 CopyNodeContents(NodeCopy); 01348 return (NodeCopy); 01349 } 01350 01351 01352 /*********************************************************************************************** 01353 01354 > void NodeGroup::CopyNodeContents(NodeGroup* pCopyOfNode) 01355 01356 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01357 Created: 22/10/2004 01358 Inputs: pCopyOfNode - The node to copy data to 01359 Outputs: - 01360 Returns: - 01361 Purpose: Copies the data from this node to pCopyOfNode by first calling the base class to get it to 01362 copy its stuff, and then copying its own stuff 01363 Scope: protected 01364 SeeAlso: NodeGroup::CopyNodeContents 01365 01366 ***********************************************************************************************/ 01367 01368 void NodeGroup::CopyNodeContents(NodeGroup* pCopyOfNode) 01369 { 01370 NodeRenderableInk::CopyNodeContents(pCopyOfNode); 01371 pCopyOfNode->m_bBlendStartNode = m_bBlendStartNode; // NodeController? 01372 pCopyOfNode->m_bBlendEndNode = m_bBlendEndNode; // NodeController? 01373 pCopyOfNode->m_pBlendCreatedByNode = m_pBlendCreatedByNode; // NodeController? 01374 CopyCached(pCopyOfNode); // Copy cached bitmaps 01375 } 01376 01377 01378 /*********************************************************************************************** 01379 > void NodeGroup::PolyCopyNodeContents(NodeRenderable* pNodeCopy) 01380 01381 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01382 Created: 22/10/2004 01383 Outputs: - 01384 Purpose: Polymorphically copies the contents of this node to another 01385 Errors: An assertion failure will occur if NodeCopy is NULL 01386 Scope: protected 01387 01388 ***********************************************************************************************/ 01389 01390 void NodeGroup::PolyCopyNodeContents(NodeRenderable* pNodeCopy) 01391 { 01392 ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node"); 01393 ENSURE(IS_A(pNodeCopy, NodeGroup), "PolyCopyNodeContents given wrong dest node type"); 01394 01395 if (IS_A(pNodeCopy, NodeGroup)) 01396 CopyNodeContents((NodeGroup*)pNodeCopy); 01397 } 01398 01399 01400 /******************************************************************************************** 01401 01402 > virtual BOOL NodeGroup::CloneNode(Node** ppNodeCopy, BOOL bLightweight) 01403 01404 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01405 Created: 23/07/2005 01406 Inputs: bLightweight - Only copy what you really need, 01407 Make references to original large data items instead of copying them 01408 if possible 01409 Outputs: NodeCopy: A deep copy of the node if Successful 01410 Returns: - 01411 Purpose: This method outputs a 'deep' copy of the node. It is the same as CopyNode 01412 except that the copy is not linked into the tree. 01413 Override if your class can interpret the lightweight flag 01414 01415 Errors: - 01416 SeeAlso: Node::CopyNode 01417 01418 ********************************************************************************************/ 01419 01420 /*BOOL NodeGroup::CloneNode(Node** ppNodeCopy, BOOL bLightweight) 01421 { 01422 // Don't let derived classes be copied as NodeGroups 01423 // (There must be a better way to do this!) 01424 if (!IS_A(this, NodeGroup)) 01425 return NodeCompound::CloneNode(ppNodeCopy, bLightweight); 01426 01427 // TODO: Do clever stuff with bLightWeight!!!! 01428 NodeGroup* pNewGroup = new NodeGroup(); 01429 ERRORIF(pNewGroup == NULL, _R(IDE_NOMORE_MEMORY), FALSE); 01430 01431 NodeRenderableInk::CopyNodeContents(pNewGroup); 01432 // CopyCached(pNewGroup, bLightweight); // Copy cached bitmaps 01433 CopyCached(pNewGroup); // Copy cached bitmaps 01434 01435 // Possible optimisation: 01436 // If a lightweight copy is requested and GroupCanTransformCached==TRUE 01437 // and we find we have a cached bitmap that will be used during dragging 01438 // then we could possibly avoid copying the children! 01439 // 01440 if (!CopyChildren(Child, pNewGroup, ccALL)) // Out of memory 01441 { 01442 DeleteChildren(pNewGroup->FindFirstChild()); 01443 delete pNewGroup; 01444 return FALSE; 01445 } 01446 01447 *ppNodeCopy = pNewGroup; 01448 return TRUE; 01449 } 01450 */ 01451 01452 01453 01454 01455 /******************************************************************************************** 01456 01457 > virtual UINT32 NodeGroup::GetNodeSize() const 01458 01459 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01460 Created: 6/10/93 01461 Inputs: - 01462 Outputs: - 01463 Returns: The size of the node in bytes 01464 Purpose: For finding the size of the node 01465 01466 SeeAlso: Node::GetSubtreeSize 01467 01468 ********************************************************************************************/ 01469 01470 UINT32 NodeGroup::GetNodeSize() const 01471 { 01472 return (sizeof(NodeGroup)); 01473 } 01474 01475 01476 /******************************************************************************************** 01477 01478 > BOOL NodeGroup::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, 01479 Spread* pSpread ) 01480 01481 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01482 Created: 27/09/94 01483 Inputs: PointerPos - The Location of the mouse pointer at the time of the click 01484 Click - The type of click received (single, double, drag etc) 01485 ClickMods - The modifiers to the click (eg shift, control etc ) 01486 Returns: TRUE if the node claims the click as its own and FALSE if it is 01487 not interested in the click 01488 Purpose: Determines if the user has started a drag on one of the groups blobs. 01489 If they have then it starts the groups resize operation 01490 01491 ********************************************************************************************/ 01492 01493 BOOL NodeGroup::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, 01494 Spread* pSpread ) 01495 { 01496 #if !defined(EXCLUDE_FROM_RALPH) 01497 // Find a view and if we failed to find a view, then just don't claim the click 01498 DocView *pDocView = DocView::GetSelected(); 01499 if (pDocView==NULL) 01500 return FALSE; 01501 01502 // we only handle the click if we can confirm that object blobs are being displayed. 01503 BlobManager* pBlobMgr = GetApplication()->GetBlobManager(); 01504 if (pBlobMgr == NULL) 01505 return FALSE; 01506 if (!pBlobMgr->GetCurrentInterest().Object) 01507 return FALSE; 01508 01509 // Find the DocRect of blob size around the point that was clicked on 01510 DocRect MouseBlob; 01511 OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), PointerPos, BT_SELECTEDLARGEST, &MouseBlob); 01512 01513 INT32 StartBlob = 0; 01514 01515 // If the user has clicked on a blob we will need to set the centre of rotation to the 01516 // oposite corner 01517 DocCoord OpCorner; 01518 01519 // Has the user clicked on one of the group's blobs ? 01520 if (MouseBlob.ContainsCoord(BoundingRectangle.lo)) // Bottom left 01521 { 01522 StartBlob = 6; 01523 OpCorner = BoundingRectangle.hi; 01524 } 01525 else if (MouseBlob.ContainsCoord(BoundingRectangle.hi)) // Top right 01526 { 01527 StartBlob = 3; 01528 OpCorner = BoundingRectangle.lo; 01529 01530 } 01531 else if (MouseBlob.ContainsCoord(DocCoord(BoundingRectangle.lo.x,BoundingRectangle.hi.y))) // Top left 01532 { 01533 StartBlob = 1; 01534 OpCorner = DocCoord(BoundingRectangle.hi.x,BoundingRectangle.lo.y); 01535 } 01536 else if (MouseBlob.ContainsCoord(DocCoord(BoundingRectangle.hi.x,BoundingRectangle.lo.y))) // Bottom right 01537 { 01538 StartBlob = 8; 01539 OpCorner = DocCoord(BoundingRectangle.lo.x,BoundingRectangle.hi.y); 01540 01541 } 01542 if (StartBlob != 0) 01543 { 01544 // User has clicked on a blob 01545 if (Click==CLICKTYPE_DRAG) 01546 { 01547 // Try to create the operation to scale the group 01548 OpScaleTrans* pOp = new OpScaleTrans(); 01549 if (pOp == NULL) 01550 { 01551 InformError(); 01552 return TRUE; // Claim the click though 01553 } 01554 01555 // Create a range containing only this object 01556 // RangeControl RngCtl = { TRUE, FALSE, FALSE }; 01557 01558 static TransformData TransData; 01559 01560 // Setup the transform info 01561 TransData.CentreOfTrans = OpCorner; 01562 TransData.StartBlob = StartBlob; 01563 TransData.LockAspect = FALSE; 01564 TransData.LeaveCopy = FALSE; 01565 TransData.ScaleLines = TRUE; 01566 TransData.TransFills = TRUE; 01567 TransData.pRange = 0; 01568 01569 TransformBoundingData BoundingData; 01570 BoundingData.x = BoundingRectangle.lo.x; 01571 BoundingData.y = BoundingRectangle.lo.y; 01572 BoundingData.Width = BoundingRectangle.Width(); 01573 BoundingData.Height = BoundingRectangle.Height(); 01574 BoundingData.XScale = (FIXED16) 1; 01575 BoundingData.YScale = (FIXED16) 1; 01576 BoundingData.Rotation = (ANGLE) 0; 01577 BoundingData.Shear = (ANGLE) 0; 01578 01579 01580 // Start the drag 01581 pOp->DragStarted(&TransData, 01582 NULL, // Don't report to any tool 01583 &BoundingData, // Must give bounds data or call fails 01584 PointerPos, 01585 pSpread, 01586 ClickMods, 01587 DocCoord(0,0), 01588 this 01589 ); 01590 01591 01592 } 01593 // we have used that click up 01594 return TRUE; 01595 } 01596 #endif 01597 // we did not use the click, so let someone else try 01598 return FALSE; 01599 } 01600 01601 /******************************************************************************************** 01602 01603 > virtual ChangeCode NodeGroup::OnChildChange(ObjChangeParam* pParam) 01604 01605 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01606 Created: 07/02/95 01607 Inputs: pParam = pointer to a object change parameter class 01608 Returns: CC_OK if we have successfully processed the change. 01609 CC_FAIL if we cannot handle this particular change and must prevent the 01610 child from continuing 01611 Purpose: This function should be overridden in derived object classes. 01612 Composite objects can use this function to respond to one of their children 01613 undergoing a change. They should return CC_FAIL whenever they are unable to 01614 cope with the change. 01615 01616 Groups will hide themselves if the op has left it in a state where it has no bounds, 01617 i.e. all it's node renderable bounded children are gone. 01618 01619 SeeAlso: WarnParentOfChange(),AllowOp(); 01620 01621 ********************************************************************************************/ 01622 01623 01624 ChangeCode NodeGroup::OnChildChange(ObjChangeParam* pParam) 01625 { 01626 ERROR2IF(pParam == NULL,CC_FAIL,"pParam == NULL"); 01627 01628 ObjChangeType cType = pParam->GetChangeType(); 01629 01630 if (cType == OBJCHANGE_FINISHED) 01631 { 01632 InvalidateBoundingRect(); 01633 // The groups 01634 DocRect Rect = GetBoundingRect(); 01635 01636 if (Rect.IsEmpty()) 01637 { 01638 UndoableOperation* pOp = pParam->GetOpPointer(); 01639 if (pOp != NULL) 01640 { 01641 Node* pParent = FindParent(); 01642 if (!pOp->DoHideNode(this,TRUE)) 01643 { 01644 return CC_FAIL; 01645 } 01646 // This bit is a bit scary, but should work ! 01647 // After a deletion we factor out all attributes on the parent of this group 01648 if (pParent && pParent->IsCompound()) 01649 { 01650 if (!pOp->DoFactorOutCommonChildAttributes(((NodeRenderableInk*)pParent),TRUE)) 01651 { 01652 return CC_FAIL; 01653 } 01654 } 01655 } 01656 } 01657 } 01658 01659 return NodeCompound::OnChildChange(pParam); 01660 } 01661 01662 //--------------------------------------------------------------------------------------- 01663 //--------------------------------------------------------------------------------------- 01664 //--------------------------------------------------------------------------------------- 01665 01666 /******************************************************************************************** 01667 01668 > virtual BOOL NodeGroup::WritePreChildrenWeb(BaseCamelotFilter* pFilter) 01669 01670 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01671 Created: 17/6/96 01672 Inputs: pFilter = ptr to the filter 01673 Returns: TRUE if record is written, FALSE if not 01674 Purpose: Writes the group record to the filter 01675 SeeAlso: - 01676 01677 ********************************************************************************************/ 01678 01679 BOOL NodeGroup::WritePreChildrenWeb(BaseCamelotFilter* pFilter) 01680 { 01681 #ifdef DO_EXPORT 01682 ERROR2IF(pFilter == NULL,FALSE,"NULL filter param"); 01683 01684 CXaraFileRecord Rec(TAG_GROUP,0); 01685 01686 BOOL ok = pFilter->Write(&Rec); 01687 01688 return ok; 01689 #else 01690 return FALSE; 01691 #endif 01692 } 01693 01694 //-------------------------------------------------------------- 01695 // See NodeGroup::WritePreChildrenWeb(BaseCamelotFilter* pFilter) 01696 // 01697 BOOL NodeGroup::WritePreChildrenNative(BaseCamelotFilter* pFilter) 01698 { 01699 #ifdef DO_EXPORT 01700 return WritePreChildrenWeb(pFilter); 01701 #else 01702 return FALSE; 01703 #endif 01704 } 01705 01706 01707 /******************************************************************************************** 01708 01709 > virtual BOOL NodeGroup::WriteBoundsRecord(BaseCamelotFilter* pFilter) 01710 01711 Author: Gerry_Iles (Xara Group Ltd) <camelotdev@xara.com> 01712 Created: 25/10/05 01713 Inputs: pFilter = ptr to filter to write to 01714 Returns: TRUE if ok, FALSE otherwise 01715 Purpose: Write out a record containing the bounds of this object 01716 01717 ********************************************************************************************/ 01718 01719 BOOL NodeGroup::WriteBoundsRecord(BaseCamelotFilter* pFilter) 01720 { 01721 BOOL ok = TRUE; 01722 // Write out the record 01723 DocRect Rect = GetBoundingRect(); 01724 01725 if (HasEffectAttrs()) 01726 { 01727 CamelotFileRecord Rec(pFilter, TAG_COMPOUNDRENDER, TAG_COMPOUNDRENDER_SIZE); 01728 ok = Rec.Init(); 01729 if (ok) ok = Rec.WriteUINT32(0); // Reserved 01730 if (ok) ok = Rec.WriteCoord(Rect.lo); 01731 if (ok) ok = Rec.WriteCoord(Rect.hi); 01732 if (ok) ok = pFilter->Write(&Rec); 01733 } 01734 else 01735 { 01736 CamelotFileRecord Rec(pFilter, TAG_OBJECTBOUNDS, TAG_OBJECTBOUNDS_SIZE); 01737 ok = Rec.Init(); 01738 if (ok) ok = Rec.WriteCoord(Rect.lo); 01739 if (ok) ok = Rec.WriteCoord(Rect.hi); 01740 if (ok) ok = pFilter->Write(&Rec); 01741 } 01742 01743 return(ok); 01744 } 01745 01746 01747 /******************************************************************************************** 01748 01749 > virtual BOOL NodeGroup::WillWriteBounds(BaseCamelotFilter* pFilter) 01750 01751 Author: Gerry_Iles (Xara Group Ltd) <camelotdev@xara.com> 01752 Created: 25/10/05 01753 Inputs: pFilter = ptr to filter to write to 01754 Returns: TRUE if this node will write out a bounds record 01755 Purpose: Determines if the down/up pair need to be written 01756 01757 ********************************************************************************************/ 01758 01759 BOOL NodeGroup::WillWriteBounds(BaseCamelotFilter* pFilter) 01760 { 01761 return(HasEffectAttrs() || pFilter->GetBoundsWriteLevel() >= BWL_COMPOUND); 01762 } 01763 01764 01765 01766 /******************************************************************************************** 01767 01768 > virtual BOOL NodeGroup::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter) 01769 01770 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01771 Created: 30/09/2005 01772 Inputs: pFilter = ptr to filter to write to 01773 Returns: TRUE if ok, FALSE otherwise 01774 Purpose: Begin to write out your child records, in the native format 01775 01776 The base class will write out a TAG_DOWN record, but only if it has a child ptr 01777 01778 SeeAlso: WritePostChildrenWeb(), WritePreChildrenWeb() 01779 01780 ********************************************************************************************/ 01781 /* 01782 BOOL NodeGroup::WriteBeginChildRecordsNative(BaseCamelotFilter* pFilter) 01783 { 01784 BOOL ok = TRUE; 01785 Node* pChild = FindFirstChild(); 01786 if (pChild != NULL) 01787 { 01788 // The group is considered to have children ONLY if at least one child is not a hidden node. 01789 if (!pChild->IsNodeHidden() || (pChild->FindNextNonHidden() != NULL)) 01790 { 01791 ok = pFilter->WriteZeroSizedRecord(TAG_DOWN); 01792 01793 if (ok) 01794 ok = WriteBoundsRecord(pFilter); 01795 } 01796 } 01797 01798 return ok; 01799 } 01800 */ 01801 01802 01803 01804 /******************************************************************************************** 01805 01806 > virtual BOOL NodeGroup::WriteBeginChildRecordsWeb(BaseCamelotFilter* pFilter) 01807 01808 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01809 Created: 30/09/2005 01810 Inputs: pFilter = ptr to filter to write to 01811 Returns: TRUE if ok, FALSE otherwise 01812 Purpose: Begin to write out you child records, in the native format 01813 01814 The base class will write out a TAG_DOWN record, but only if it has a child ptr 01815 01816 SeeAlso: WritePostChildrenWeb(), WritePreChildrenWeb() 01817 01818 ********************************************************************************************/ 01819 /* 01820 BOOL NodeGroup::WriteBeginChildRecordsWeb(BaseCamelotFilter* pFilter) 01821 { 01822 return WriteBeginChildRecordsNative(pFilter); 01823 } 01824 */ 01825 01826 01827 01828 /******************************************************************************************** 01829 01830 > virtual BOOL NodeGroup::AreYouSafeToRender() 01831 01832 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01833 Created: 18/9/96 01834 Inputs: - 01835 Returns: TRUE if safe to render, FALSE otherwise 01836 Purpose: Group nodes are always safe to render, but derived group nodes (such as blends and 01837 text, etc) are not safe until their post import functions have been called 01838 SeeAlso: - 01839 01840 ********************************************************************************************/ 01841 01842 BOOL NodeGroup::AreYouSafeToRender() 01843 { 01844 if (IS_A(this,NodeGroup)) 01845 return TRUE; 01846 else 01847 return NodeRenderableInk::AreYouSafeToRender(); 01848 } 01849 01850 01851 01852 /******************************************************************************************** 01853 01854 > DocRect NodeGroup::ValidateExtend(const ExtendParams& ExtParams) 01855 01856 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 01857 Created: 5/25/00 01858 Inputs: ExtParams parameters describing the extension. 01859 Outputs: 01860 Returns: TRUE if this group can be validly extended, 01861 FALSE otherwise. 01862 Purpose: Tests to see whether this group's extend-centre is positioned so as to make 01863 an extend operation irreversible. 01864 01865 Note that we do not pass the ValidateExtend() call on to our kids, as our 01866 extend behaviour means that our children don't need extending or validating. 01867 Errors: 01868 See also: 01869 01870 ********************************************************************************************/ 01871 //DocRect NodeGroup::ValidateExtend(const ExtendParams& ExtParams) 01872 //{ 01873 // DocCoord doccArray[1] = { FindExtendCentre() }; 01874 // DocRect drMinExtend = Extender::ValidateControlPoints(1, doccArray, ExtParams); 01875 // 01876 // return drMinExtend; 01877 //} 01878 01879 01880 01881 /******************************************************************************************** 01882 01883 > void NodeGroup::Extend(const ExtendParams& ExtParams) 01884 01885 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 01886 Created: 5/25/00 01887 Inputs: ExtParams parameters describing the extension. 01888 Outputs: this NodeGroup will be extended in accordance with ExtParams. 01889 Returns: 01890 Purpose: Perform an Extend operation on this group. Behaviour is as follows: 01891 01892 * the group extends separately along each axis. 01893 * if the group is asked to stretch, it translates itself so that its centre 01894 is where it would be if it had stretched. 01895 * if the group is asked to extend, it is translated as a whole, as described 01896 by ExtParams. 01897 01898 Note that we do not pass the Extend() call on to our children, as our extend 01899 behaviours result in our children being transformed appropriately anyway. 01900 Errors: 01901 See also: class Extender 01902 01903 ********************************************************************************************/ 01904 //void NodeGroup::Extend(const ExtendParams& ExtParams) 01905 //{ 01906 // // do the extension operations on ourself. 01907 // TransformTranslateNoStretchObject(ExtParams); 01908 // TransformTranslateObject(ExtParams); 01909 //} 01910 01911 01912 01913 01915 // Implementation of NodeListItem 01917 01918 /******************************************************************************************** 01919 01920 > NodeListItemWithDocPtr::NodeListItemWithDocPtr() 01921 01922 Author: Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com> 01923 Created: 22/7/94 01924 Inputs: - 01925 Outputs: - 01926 Returns: - 01927 Purpose: default constructor. Initialises pNode = NULL 01928 Errors: - 01929 SeeAlso: - 01930 01931 ********************************************************************************************/ 01932 01933 NodeListItemWithDocPtr::NodeListItemWithDocPtr() 01934 { 01935 pNode = NULL; 01936 } 01937 01938 /******************************************************************************************** 01939 01940 > NodeListItemWithDocPtr::NodeListItemWithDocPtr(Node* WhichNode) 01941 01942 Author: Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com> 01943 Created: 22/7/94 01944 Inputs: - 01945 Outputs: - 01946 Returns: - 01947 Purpose: Constructor for NodeListItem which sets the pNode variable to WhichNode 01948 Errors: - 01949 SeeAlso: - 01950 01951 ********************************************************************************************/ 01952 01953 NodeListItemWithDocPtr::NodeListItemWithDocPtr(Node* WhichNode) 01954 { 01955 pNode = WhichNode; 01956 } 01957 01958 01959 01960 //--------------------------------------------------------------------------------------- 01961 //--------------------------------------------------------------------------------------- 01962 //--------------------------------------------------------------------------------------- 01963 01964 /******************************************************************************************** 01965 01966 > virtual UINT32* GroupRecordHandler::GetTagList() 01967 01968 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01969 Created: 17/6/96 01970 Inputs: - 01971 Returns: Ptr to a list of tag values, terminated by CXFRH_TAG_LIST_END 01972 Purpose: Provides the record handler system with a list of records handled by this 01973 handler 01974 SeeAlso: - 01975 01976 ********************************************************************************************/ 01977 01978 UINT32* GroupRecordHandler::GetTagList() 01979 { 01980 static UINT32 TagList[] = {TAG_GROUP, TAG_COMPOUNDRENDER, TAG_OBJECTBOUNDS, CXFRH_TAG_LIST_END}; 01981 01982 return (UINT32*)&TagList; 01983 } 01984 01985 /******************************************************************************************** 01986 01987 > virtual BOOL GroupRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord) 01988 01989 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01990 Created: 17/6/96 01991 Inputs: pCXaraFileRecord = ptr to record to handle 01992 Returns: TRUE if handled successfuly 01993 FALSE otherwise 01994 Purpose: Handles the given record. 01995 SeeAlso: - 01996 01997 ********************************************************************************************/ 01998 01999 BOOL GroupRecordHandler::HandleRecord(CXaraFileRecord* pCXaraFileRecord) 02000 { 02001 ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); 02002 02003 BOOL ok = TRUE; 02004 02005 switch (pCXaraFileRecord->GetTag()) 02006 { 02007 case TAG_GROUP: 02008 ok = HandleGroupRecord(pCXaraFileRecord); 02009 break; 02010 02011 case TAG_COMPOUNDRENDER: 02012 case TAG_OBJECTBOUNDS: 02013 // Do nothing 02014 // We ignore TAG_COMPOUNDRENDER records because they are really just a hint to 02015 // external XAR stream handlers 02016 ok = TRUE; 02017 break; 02018 02019 default: 02020 ok = FALSE; 02021 ERROR3_PF(("I don't handle records with the tag (%d)\n",pCXaraFileRecord->GetTag())); 02022 break; 02023 } 02024 02025 return ok; 02026 } 02027 02028 02029 /******************************************************************************************** 02030 02031 BOOL GroupRecordHandler::HandleGroupRecord(CXaraFileRecord* pCXaraFileRecord) 02032 02033 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 02034 Created: 17/6/96 02035 Inputs: pCXaraFileRecord = ptr to record to handle 02036 Returns: TRUE if handled successfuly 02037 FALSE otherwise 02038 Purpose: Handles the given record. 02039 The record has to be a Line colour record 02040 SeeAlso: - 02041 02042 ********************************************************************************************/ 02043 02044 BOOL GroupRecordHandler::HandleGroupRecord(CXaraFileRecord* pCXaraFileRecord) 02045 { 02046 ERROR2IF(pCXaraFileRecord == NULL,FALSE,"pCXaraFileRecord is NULL"); 02047 ERROR2IF(pCXaraFileRecord->GetTag() != TAG_GROUP,FALSE,"I don't handle this tag type"); 02048 02049 NodeGroup* pGroup = new NodeGroup; 02050 BOOL ok = FALSE; 02051 02052 if (pGroup != NULL) 02053 ok = InsertNode(pGroup); 02054 02055 return ok; 02056 } 02057 02058 02059 02060 02061 /******************************************************************************************** 02062 02063 > virtual void GroupRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr) 02064 02065 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02066 Created: 30/09/2005 02067 Inputs: pRecord = ptr to a record 02068 pStr = ptr to string to update 02069 Returns: - 02070 Purpose: Produce a textual description of the specified record 02071 Errors: - 02072 SeeAlso: - 02073 02074 ********************************************************************************************/ 02075 02076 #ifdef XAR_TREE_DIALOG 02077 void GroupRecordHandler::GetRecordDescriptionText(CXaraFileRecord* pRecord,StringBase* pStr) 02078 { 02079 if (pStr == NULL || pRecord == NULL) 02080 return; 02081 02082 TCHAR s[256]; 02083 02084 // Call base class first 02085 CamelotRecordHandler::GetRecordDescriptionText(pRecord,pStr); 02086 02087 switch (pRecord->GetTag()) 02088 { 02089 case TAG_GROUP: 02090 break; 02091 02092 case TAG_COMPOUNDRENDER: 02093 { 02094 INT32 reserved = 0; 02095 pRecord->ReadINT32(&reserved); 02096 camSprintf(s,_T("Reserved\t\t= %d\r\n"), reserved); 02097 (*pStr) += s; 02098 } 02099 nobreak; // fall through 02100 02101 case TAG_OBJECTBOUNDS: 02102 DocRect rbounds; 02103 pRecord->ReadCoord(&rbounds.lo); 02104 pRecord->ReadCoord(&rbounds.hi); 02105 camSprintf(s, _T("Bounds\t\t= %d, %d, %d, %d\r\n"), rbounds.lo.x, rbounds.lo.y, rbounds.hi.x, rbounds.hi.y); 02106 (*pStr) += s; 02107 break; 02108 } 02109 } 02110 #endif 02111 02112