group.cpp

Go to the documentation of this file.
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 

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