range.cpp

Go to the documentation of this file.
00001 // $Id: range.cpp 1744 2006-09-06 15:58:54Z luke $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098     
00099 /* Implementation of class Range */     
00100     
00101 /*
00102 */ 
00103  
00104 #include "camtypes.h" 
00105 
00106 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 //#include "docmsgs.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "ensure.h"  - in camtypes.h [AUTOMATICALLY REMOVED]
00111 //#include "node.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "range.h"    - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 //#include "simon.h"
00115 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 //#include "dlgbar.h"
00118 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00119 #include "sprdmsg.h"
00120 #include "layer.h"
00121 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00122 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00123 //#include "peter.h"
00124 #include "nodetext.h"
00125 //#include "undoop.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 #include "nodetxtl.h"
00127 #include "nodetxts.h"
00128 #include "nodecont.h"
00129 #include "nodeshad.h"
00130 #include "objreg.h"
00131 #include "attrmap.h"
00132 #include "toolmsg.h"
00133 #include "comattrmsg.h"
00134 #include "qualattr.h"
00135 #include "ngcore.h"     // NameGallery, for stretching functionality
00136 #include "objchge.h"    // for ues of ObjChangeParam
00137 //#include "scrvw.h"
00138 //#include "will2.h"
00139 //#include "justin2.h"
00140 //#include "resimmap.h" //For _R(IDS_SINGLELAYER_INSIDE)
00141 //#include "bmpsdlgr.h" // _R(IDS_ON_FRAME_OUTSIDE_NOREF)
00142 #include "effects_stack.h"
00143 //#include "bars.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00144 #include "fillattr2.h"
00145 
00146 DECLARE_SOURCE( "$Revision: 1744 $" );    
00147 
00148 CC_IMPLEMENT_DYNAMIC(Range, CCObject)   
00149 CC_IMPLEMENT_DYNAMIC(NodeListItem, ListItem)
00150 
00151 CC_IMPLEMENT_DYNAMIC(SelRange, Range)   
00152 CC_IMPLEMENT_DYNAMIC(SelChangingMsg, Msg)
00153 
00154 CC_IMPLEMENT_DYNAMIC(ListRange, Range)   
00155 
00156 CC_IMPLEMENT_DYNAMIC(SelRangeMessageHandler, MessageHandler)
00157 
00158 CC_IMPLEMENT_DYNAMIC(CommonAttrsChangedMsg, Msg);
00159 
00160 
00161 // This line MUST occur after all CC_IMPLEMENT lines
00162 // Declare smart memory handling in Debug builds
00163 #define new CAM_DEBUG_NEW
00164 
00165 
00166 // Enable Fast XOR drag blob rendering - see below
00167 // Set this value to 0 (FALSE) to disable, or 1 (TRUE) to enable fast XORing
00168 #define ENABLE_FAST_XOR 1
00169 
00170 
00171 // Define _DEBUG_LISTCHECKS to enable list checking in ListRange
00172 #ifdef _DEBUG
00173 //  #define _DEBUG_LISTCHECKS
00174 #endif
00175 
00176 
00177 /*****************************************************************************************
00178 
00179 >   RangeControl::RangeControl( BOOL Sel                = FALSE,
00180                                 BOOL Unsel              = FALSE,
00181                                 BOOL Cross              = FALSE,
00182                                 BOOL IgLock             = FALSE,
00183                                 BOOL Rendr              = FALSE,
00184                                 BOOL IgInvisible        = FALSE,
00185                                 BOOL SibsOnly           = FALSE,
00186                                 BOOL Promot             = FALSE )
00187     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00188     Created:    26/01/95
00189 
00190     Inputs:     -
00191     Outputs:    -
00192     Returns:    -
00193     Purpose:    RangeControl constructor. Sets the flags to safe defaults!
00194                 See RangeControl definition for details of flags.
00195 
00196     Errors:     -
00197 
00198 ******************************************************************************************/
00199 
00200 RangeControl::RangeControl( BOOL Sel, BOOL Unsel, BOOL Cross,
00201                             BOOL IgLock, BOOL Rendr, BOOL IgInvisible, BOOL SibsOnly,
00202                             BOOL Promot )
00203 {
00204     Selected = Sel;
00205     Unselected = Unsel;
00206     CrossLayer = Cross;
00207     IgnoreLockedLayers = IgLock;
00208     IgnoreNoneRenderable = Rendr;
00209     IgnoreInvisibleLayers = IgInvisible;
00210     SiblingsOnly = SibsOnly;
00211     PromoteToParent = Promot;
00212 }
00213 
00214 
00215 
00216 
00217 /********************************************************************************************
00218 
00219 >   Range::Range(Node* First, Node* Last, RangeControl RangeControlFlgs)
00220 
00221     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00222     Created:    28/6/93
00223     Inputs:     RangeControlFlags: Specifies the selected status of the nodes to be included
00224                                    in the range.
00225                                    
00226                                    If RangeControlFlgs.Selected = TRUE
00227                                         All nodes with a selected status are included as 
00228                                         members of the range. 
00229                                         
00230                                    If RangeControlFlgs.Unselected = TRUE
00231                                         All nodes with an unselected status are included as
00232                                         members in the range. 
00233                                         
00234                                    If both these flags are TRUE then all nodes from First to Last
00235                                    are included in the range. 
00236 
00237                                    If RangeControlFlgs.CrossLayer = TRUE
00238                                         The range may cross several layers; when the end of a layer
00239                                         is reached, the search will continue at the start of the
00240                                         following layer.
00241 
00242                                    If RangeControlFlgs.IgnoreLockedLayers = TRUE
00243                                         locked layers are included in the search.
00244                                         Defaults to FALSE so locked layers ignored.
00245                                    
00246                                    If RangeControlFlgs.IgnoreNoneRenderable = TRUE
00247                                         non-renderable nodes are included in the search.
00248                                         Defaults to FALSE so non-redenderable nodes ignored.
00249                                    
00250                                    If RangeControlFlgs.IgnoreInvisibleLayers = TRUE
00251                                         Invisable layers are included in the search.
00252                                         Defaults to FALSE so invisible layers ignored.
00253                                    
00254                 First:  The node from which to commence searching for members of a range.  
00255                 Last:   The final node to search (must be a right sibling of First, or NULL). 
00256                         NULL specifies that all nodes from First to the end of First's sibling 
00257                         list (or end of the tree, if CrossLayer is TRUE) are to be searched for
00258                         members of the range. 
00259                             
00260                                     
00261     Outputs:    -
00262     Returns:    -
00263     Purpose:    The purpose of this function is to create a node range. 
00264     Errors:     -
00265 
00266 ********************************************************************************************/
00267   
00268 Range::Range(Node* First, Node* Last, RangeControl RangeControlFlgs)
00269 {
00270     // When the First node in a range is NULL the range is assumed to lie directly under
00271     // the surface of layers...
00272 /*
00273     if (First!=NULL)
00274     {
00275         ERROR3IF( RangeControlFlgs.Unselected
00276                     && !First->FindParent()->IsLayer()
00277                     && First != Last,
00278                     "Attempt to create an illegal range!"
00279                  );
00280     }
00281 */
00282 
00283     RangeControlFlags = RangeControlFlgs; 
00284     FirstNode = First; 
00285     LastNode = Last;
00286     pCommonAttribCache = NULL;             // The common attribute cache only comes into
00287                                            // existance if the FindCommonAttribute functions
00288                                            // are called on the range.
00289  
00290     // Initialise the XOR rendering stuff
00291     NodeToRender = NULL;
00292     ResetXOROutlineRenderer(TRUE);
00293 } 
00294 
00295 
00296 
00297 
00298 /********************************************************************************************
00299 
00300 >   Range::Range()
00301 
00302 
00303     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00304     Created:    2/3/94
00305     Inputs:     -
00306     Outputs:    -
00307     Returns:    -
00308     Purpose:    Creates an uninitialised range 
00309     Errors:     -
00310     SeeAlso:    -
00311 
00312 ********************************************************************************************/
00313 
00314 Range::Range()
00315 {
00316     FirstNode = LastNode = NULL;
00317     pCommonAttribCache = NULL;             // The common attribute cache only comes into
00318                                            // existance if the FindCommonAttribute functions
00319 }
00320 
00321 
00322 
00323 
00324 Range::~Range()
00325 {
00326     // Destroy the CommonAttribSet if one exists
00327     if (pCommonAttribCache)
00328     {
00329         delete pCommonAttribCache; 
00330     }
00331 }
00332 
00333 
00334 
00335 
00336 /********************************************************************************************
00337 
00338 >   Range::Range(const Range& Range);   
00339 
00340     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00341     Created:    2/3/94
00342     Inputs:     Rng:    The Range to copy 
00343     Outputs:    -
00344     Returns:    -
00345     Purpose:    Range copy constructor 
00346     Errors:     -
00347     SeeAlso:    -
00348 
00349 ********************************************************************************************/
00350 
00351 Range::Range(Range& Rng)
00352 {
00353     // If the range is a sel range we must be sure that the range has been cached 
00354     Rng.FreshenCache();
00355 
00356     RangeControlFlags = Rng.RangeControlFlags;  
00357     FirstNode = Rng.FirstNode; 
00358     LastNode = Rng.LastNode;
00359     pCommonAttribCache = NULL;      
00360 }      
00361 
00362 
00363 
00364 
00365 /********************************************************************************************
00366 
00367 >   Range& Range::operator=(Range& Range)              
00368 
00369     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00370     Created:    2/3/94
00371     Inputs:     Range: The range to copy 
00372     Outputs:    
00373     Returns:    The Range
00374     Purpose:    Range = operator
00375     Errors:     -
00376     SeeAlso:    -
00377 
00378 ********************************************************************************************/
00379 
00380 Range& Range::operator=(Range& Rng)
00381 {
00382     // If the range is a sel range we must be sure that the range has been cached 
00383     Rng.FreshenCache(); 
00384 
00385     RangeControlFlags = Rng.RangeControlFlags;  
00386     FirstNode = Rng.FirstNode; 
00387     LastNode = Rng.LastNode; 
00388     pCommonAttribCache = NULL;      
00389     return *this;       
00390 }
00391 
00392 
00393 
00394 
00395 /********************************************************************************************
00396 
00397 >   void Range::SetRangeControl(RangeControl RangeControlFlgs)
00398 
00399     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00400     Created:    29/6/93 
00401     
00402     Inputs:     RangeControlFlags: Specifies the selected status of the nodes to be included
00403                                    in the range.
00404     Outputs:    -
00405     Returns:    -
00406     Purpose:    To set the range control for the range.  
00407     Errors:     -
00408     SeeAlso:    -
00409 
00410 ********************************************************************************************/
00411                             
00412 void Range::SetRangeControl(RangeControl RangeControlFlgs)
00413 {
00414     RangeControlFlags = RangeControlFlgs;
00415 }   
00416 
00417 
00418 
00419 
00420 /*********************************************************************************************
00421 
00422 >    Node* Range::FindFirst(BOOL AndChildren = FALSE)
00423 
00424      Author:    Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> (Changed by Phil, 30/11/94)
00425      Created:   25/6/93
00426      
00427      Returns:   If the range contains any members then
00428                     A pointer to the first node in the range is returned
00429                 Else
00430                     NULL is returned 
00431             
00432      Purpose:   The purpose of this function is to find the first node in a range. 
00433 
00434      SeeAlso:   Range::FindNext    
00435      
00436      Errors:    An assertion failure will occur if:
00437 
00438                 There are no more nodes to search and the last node to search was not found.
00439 
00440 **********************************************************************************************/
00441                                     
00442 Node* Range::FindFirst(BOOL AndChildren)
00443 {
00444     // Preconditions...
00445     ERROR2IF(this==NULL,NULL,"FindFirst called on NULL range");
00446 
00447     // Locals...
00448     Node* pNode = FirstNode;
00449 
00450 
00451 // **** !!!! BODGE - Traps bug in message broadcast from document.cpp to try to
00452 // keep the program stable.
00453     if (Document::GetSelectedSpread() != NULL)
00454     {
00455         if (!Document::SpreadBelongsToDoc(Document::GetSelected(),
00456                                             Document::GetSelectedSpread()))
00457         {
00458             TRACE( _T("SelRange: Selected Spread is NOT in the Selected Document!\n"));
00459             return(NULL);
00460         }
00461     }
00462 // **** !!!! BODGE
00463 
00464     // Implementation...
00465     // If First is NULL then the caller expects to get a range of nodes
00466     // in the child lists of layers. So find the first layer on the
00467     // current spread.
00468     if (pNode == NULL)
00469     {
00470         Spread* pSelectedSpread = Document::GetSelectedSpread();
00471         if (pSelectedSpread==NULL)
00472         {
00473             // The range does not have a FirstNode. Normally it would find one
00474             // automatically based on the SelectedSpread but there is no SelectedSpread
00475             // so we have no choice but to return an empty range to the caller...
00476             TRACEUSER( "Phil", _T("Warning! Range is NULL because there's no selected spread\n"));
00477             return NULL;
00478         }
00479         
00480         // Oh well, we will use the first layer instead
00481         pNode = pSelectedSpread->FindFirstLayer();
00482 
00483         // If there is no layer, then give up and return NULL
00484         if (pNode==NULL)
00485             return NULL;
00486     }
00487 
00488     // The first node might be a valid member of the range so test for that...
00489     // (Set the special FirstNode flag TRUE so that the logic treats this node is
00490     //  if it hasn't yet been met in the scan.)
00491     pNode = SmartFindNext(pNode, AndChildren, TRUE);
00492     return pNode;
00493 }                       
00494 
00495 
00496      
00497 
00498 /*********************************************************************************************
00499 
00500 >    Node* Range::FindNext(Node* Prev, BOOL AndChildren = FALSE)
00501      Author:    Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>  (Changed by Phil, 30/11/94)
00502      Created:   25/6/93
00503      Inputs:    Prev: The previous node in the range (usually returned from a 
00504                        Range::FindFirst, or a previous Range::FindNext call). 
00505      Outputs:    - 
00506      Returns:   If the range contains any more members then
00507                     A pointer to the next node in the range is returned
00508                 Else
00509                     NULL is returned 
00510      Purpose:   The purpose of this function is to find the next node in the range after Prev 
00511      SeeAlso:   Range::FindFirst    
00512      Errors:    An assertion failure will occur if:
00513                 There are no more nodes to search and the last node to search was not found.
00514 
00515 **********************************************************************************************/
00516 
00517 Node* Range::FindNext(Node* pPrevious, BOOL AndChildren)
00518 {
00519     // Preconditions
00520     // No need to check that "this" is NULL because that's already been done
00521     // in FindFirst.
00522     ERROR2IF(pPrevious == NULL, NULL, "NULL pointer passed to Range::FindNext");
00523 
00524     Node* pNode = SmartFindNext(pPrevious, AndChildren);
00525 
00526     // DMc
00527     // if the output is the same as the input, and promote to parent is set,
00528     // the this should never happen !
00529     if (pNode == pPrevious && RangeControlFlags.PromoteToParent)
00530     {
00531         return NULL;
00532     }
00533         
00534     return pNode;
00535 }
00536 
00537 
00538 
00539 /*********************************************************************************************
00540 
00541 >    Node* Range::FindPrev(Node* pNode, BOOL AndChildren = FALSE)
00542      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00543      Created:   19/01/95
00544      Inputs:    pNode: The node which you want the previous one in the range to... 
00545      Outputs:    - 
00546      Returns:   If the range contains any members before the input node then
00547                     A pointer to the prev node in the range is returned
00548                 Else
00549                     NULL is returned (Note NULL returned if input node was not in the range)
00550      Purpose:   The purpose of this function is to find the previous node in the range before
00551                 the input node.
00552                 NOTE! THIS ROUTINE IS SLOW! It scans through the range in forwards order
00553                 until it meets the input node, then returns the previous node to that!!!
00554      SeeAlso:   Range::FindFirst; Range::FindNext    
00555      Errors:    -
00556 
00557 **********************************************************************************************/
00558 
00559 Node* Range::FindPrev(Node* pNode, BOOL AndChildren)
00560 {
00561     // Preconditions
00562     // No need to check that "this" is NULL because that's already been done
00563     // in FindFirst.
00564     ERROR2IF(pNode == NULL, NULL, "NULL pointer passed to Range::FindNext");
00565 
00566     Node* pPrev = NULL;
00567     Node* pCurrent = FindFirst(AndChildren);
00568     while (pCurrent)                                // While there's a node to check
00569     {
00570         if (pCurrent == pNode)                      // See if it's the one we're looking for
00571             return pPrev;                           // If it is return the previous node!
00572 
00573         pPrev = pCurrent;                           // Else, remember this node
00574         pCurrent = FindNext(pCurrent, AndChildren); // and find the next one in the range
00575     }
00576 
00577     return NULL;
00578 }
00579 
00580 
00581 
00582 /*********************************************************************************************
00583 
00584 >    Node* Range::FindLast()
00585 
00586      Author:    Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00587      Created:   25/6/93
00588      
00589      Returns:   If the range contains any members then
00590                     A pointer to the last node in the range is returned
00591                 Else
00592                     NULL is returned 
00593             
00594      Purpose:   The purpose of this function is to find the last node in a range. 
00595                 If the range was constructed with a NULL last node specifier then
00596                 the range is scanned until the last node is found. If a non NULL last node
00597                 was specified however the value of last is simply returned. It's existance
00598                 is not verified !. 
00599 
00600      SeeAlso:   Range::FindFirst
00601                 Range::FindNext    
00602      
00603      Errors:    An assertion failure will occur if:
00604 
00605                 There are no more nodes to search and the last node to search was not found.
00606 
00607 **********************************************************************************************/
00608 
00609 Node* Range::FindLast()
00610 {
00611     if (LastNode == NULL)
00612     {
00613         // We need to search for the last node in the range
00614         Node* Scout = FindFirst();
00615         Node* n = NULL; 
00616         while (Scout != NULL)
00617         {
00618             n = Scout;
00619             Scout = FindNext(Scout); // Get the next value in the range
00620         }
00621         return n; // If the range is empty NULL will be returned
00622     }
00623     else
00624     {
00625         // get the parent of the last node (if necessary to promote)
00626         Node * pContext = LastNode;
00627 
00628         if (pContext != NULL)
00629         {
00630             Node *pParent = pContext->FindParent();
00631         
00632             if (RangeControlFlags.PromoteToParent)
00633             {
00634                 while (pParent)
00635                 {
00636                     if (pParent->ShouldITransformWithChildren())
00637                     {
00638                         pContext = pParent;
00639                     }
00640                 
00641                     pParent = pParent->FindParent();
00642                 }
00643             }
00644         }
00645 
00646         return pContext; 
00647     }
00648 }
00649 
00650 
00651 
00652 
00653 /******************************************************************************************
00654 
00655 >   void Range::Update(BOOL TellWorld = FALSE)
00656 
00657      Author:    Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00658      Created:   03/10/94    (Was an inline function prior to this)
00659 
00660      Inputs:    BOOL TellWorld: When TRUE the function will inform the world that the 
00661                 selection has changed. Normally the selection is changed in an operation
00662                 and it is unneccesary to broadcast a SelChangingMsg at this point because
00663                 this will occur at the end of the operation. However If the selection is
00664                 changed outside of an operation then the flag should be set to TRUE.  
00665      Outputs:   -
00666      Returns:   -
00667 
00668      Purpose:   To inform the SelRange that the selection has changed
00669                 This invalidates the SelRange's selection-info cache so it will be
00670                 recached when info is next requested.
00671 
00672 ******************************************************************************************/
00673 
00674 void  Range::Update(BOOL TellWorld)
00675 {
00676     Camelot.FindSelection()->Update(TellWorld);     
00677 }   
00678 
00679 
00680 
00681 
00682 /*********************************************************************************************
00683 
00684 >    UINT32 Range::Count()
00685      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00686      Created:   15/2/94
00687      Inputs:    -
00688      Outputs:   -
00689      Returns:   The number of nodes described by the Range.
00690      Purpose:   To count the number of nodes in a range. Currently this routine always scans
00691                 the range to count the number of nodes. In some future incarnation it may 
00692                 keep the count cached for quicker access.
00693      SeeAlso:   Range::FindFirst
00694      SeeAlso:   Node::FindNextInRange
00695      Errors:    -
00696 
00697 **********************************************************************************************/
00698 
00699 UINT32 Range::Count()
00700 {
00701     UINT32 count = 0;
00702     Node* pNode;
00703 
00704     pNode = FindFirst();
00705     while (pNode)
00706     {
00707         count++;
00708         pNode = FindNext(pNode);
00709     }
00710 
00711     return(count); 
00712 }
00713 
00714 
00715 /*********************************************************************************************
00716 
00717 >    BOOL Range::Contains(Node* pLookFor, BOOL bAndChildren)
00718 
00719      Author:    Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00720      Created:   3/5/95
00721      Inputs:    pLookFor = a pointer to a node to look for.
00722                 bAndChildren - Test for node being in subtree of member of range
00723      Outputs:   -
00724      Returns:   TRUE if the range contains this particular node
00725                 FALSE if the node is not held within this range
00726      Purpose:   To determin whether a particular node is actually in the range list.
00727      Errors:    -
00728 
00729 **********************************************************************************************/
00730 
00731 BOOL Range::Contains(Node* pLookFor, BOOL bAndChildren)
00732 {
00733     Node* pNode=FindFirst();
00734     while (pNode)
00735     {
00736         if (pNode==pLookFor)
00737             return TRUE;
00738 
00739         if (bAndChildren && pNode->IsNodeInSubtree(pLookFor))
00740             return TRUE;
00741 
00742         pNode=FindNext(pNode);
00743     }
00744     return FALSE;
00745 }
00746 
00747 
00748 
00749 /*********************************************************************************************
00750 
00751 >   BOOL Range::ContainsSelectInside()
00752     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00753     Created:    28/11/94
00754     Inputs:     -
00755     Outputs:    -
00756     Returns:    TRUE if Selection inside occurs in this range one or more times.
00757                 FALSE otherwise.
00758                 (Also returns FALSE when the Range is empty)
00759     Purpose:    To find out whether Select-inside is going on in this range. I.e. are any
00760                 members of this range children of nodes which would normally be selected.
00761     Errors:     -
00762 
00763 **********************************************************************************************/
00764 
00765 BOOL Range::ContainsSelectInside()
00766 {
00767 #if !defined(EXCLUDE_FROM_RALPH)
00768     // Preconditions
00769     ERROR2IF(this==NULL,FALSE,"ContainsSelectInside called on NULL pointer");
00770 
00771     // Implementation
00772     Node* pNodeInRange = FindFirst();
00773     Node* pParent;
00774     while (pNodeInRange)                                // While there's a node in the range
00775     {
00776         pParent = pNodeInRange->FindParent();           // Look at it's parent node
00777         
00778         // DMc - added promotehittestonchildrentome test to eliminate the 'inside' setting
00779         // for nodes should as shadows, contours, bevels etc
00780         while (pParent)
00781         {
00782             if (pParent->IsAnObject() && pParent->IsParentOfSelected() &&
00783                 pParent->PromoteHitTestOnChildrenToMe())    // If that's marked parent of sel
00784                 return TRUE;                                // Then this node is "inside" so return TRUE
00785 
00786             pParent = pParent->FindParent();
00787         }
00788 
00789         pNodeInRange = FindNext(pNodeInRange);          // Else, keep scanning members of range
00790     }
00791 #endif
00792     return FALSE;                                       // If no more in range, none were "inside"
00793 }
00794 
00795 
00796 
00797 
00798 /*********************************************************************************************
00799 
00800 >    Node* Range::SmartFindNext(Node* pContext, BOOL AndChildren = FALSE, BOOL FirstCall = FALSE) const
00801      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00802      Created:   24/11/94
00803      Inputs:    pContext    Pointer to node in range to find next range member from
00804                             NOTE! pContext MUST be in the range!!!
00805                 AndChildren Flag specifying whether to return children of nodes in
00806                             the range.
00807                 Last        A pointer to the final node to search or NULL if all possible
00808                             members of the range are to be searched.   
00809                 Flags       Define the nodes which are to be members of the range 
00810                      Flags.Selected   : Selected nodes in the range
00811                      Flags.Unselected : Unselected nodes in the range
00812                      Flags.CrossLayer : Crossing into the next layer is allowed
00813                 If Flags.Selected and Flags.Unselected are both set, then all nodes from
00814                 this node to the Last node (or end of the layer, whichever comes first,
00815                 if CrossLayer is clear) are included in the range.
00816      Returns:   If the range contains any members following pContext then
00817                     A pointer to the next node in the range is returned
00818                 Else
00819                     A NULL is returned 
00820                 If no flags are set then there can be no members in the range
00821                 and NULL is returned.
00822      Purpose:   This function looks for the next node in a range following this node
00823                 and ending at the node specified by Last (or the last node in this layer
00824                 if the CrossLayer flag is clear).
00825                 Last must be a right sibling of this node, or NULL if all siblings are
00826                 to be searched.                 
00827      SeeAlso:   Node::FindFirstInRange; Node::FindNextInRange    
00828      Errors:    There are no more nodes to search and Last was not found
00829      Scope:     Protected
00830 
00831 **********************************************************************************************/
00832 /*  Technical Notes:
00833 
00834     This routine is just a souped up depth-first traversal of the whole tree with subtrees
00835     carefully pruned off to make the routine efficient.
00836 
00837 **********************************************************************************************/
00838 Node* Range::SmartFindNext(Node* pContext, BOOL AndChildren, BOOL FirstCall) const
00839 {
00840     // Preconditions
00841     ERROR2IF(this==NULL,NULL,"SmartFindNext called on NULL range");
00842     ERROR2IF(pContext==NULL,NULL,"SmartFindNext not given a context node to start from");
00843     ERROR2IF(RangeControlFlags.Unselected && AndChildren,NULL,"Range can't cope with unsel and children");
00844     ERROR2IF(pContext->IsPaper() && !pContext->IsLayer(),NULL,"SmartFindNext called above layers surface");
00845 
00846     // If the PromoteToParent flag is set then the context node might be a "promoted parent"
00847     if (RangeControlFlags.PromoteToParent && !FirstCall && !AndChildren)
00848     {
00849         // Using PromoteToParent, it is possible for the context node to jump *above* the
00850         // last node in the Range, causing us to iterate outside of it. We must therefore
00851         // check whether the context node is a parent of the last node, in which case
00852         // we have finished iteration.
00853         if (pContext->IsNodeInSubtree(LastNode))
00854             return NULL;
00855 
00856         // If the context node's parents wish to do so, then switch them in instead of it.
00857         // Go up the tree until this is no longer the case.
00858         Node* pParent = pContext->FindParent();
00859         while (pParent != NULL && pParent->ShouldITransformWithChildren())
00860         {
00861             pContext = pParent;
00862             pParent = pContext->FindParent();
00863         }
00864     }
00865 
00866     // Implementation
00867     // Loop, looking for suitable nodes and testing whether they're in the range or not.
00868     // Traverses the tree in basically depth-first order...
00869     do
00870     {
00871         // If context node from previous findnext was last node then there can be no more...
00872         if (!FirstCall && LastNode!=NULL && pContext==LastNode) return NULL;
00873 
00874         // Do the normal depth-first thing; assume subtree we're given has already been
00875         // met and so try to find lowest leaf of next subtree...
00876         // (If this is the first call, from FindFirst, then treat the node we've got
00877         //  as if it hasn't been scanned yet. 'Cos it hasn't!)
00878         if (pContext->FindNext() || FirstCall)
00879         {
00880             // Here, either have a sibling or FirstCall is TRUE
00881             // So, if firstcall is NOT TRUE, must be here due to sibling...
00882             if (!FirstCall)
00883                 pContext = pContext->FindNext();                    // Find top of next subtree
00884             FirstCall = FALSE;
00885 
00886             // In a normal depth-first traversal of the tree we would find the deepest first child
00887             // in the subtree. However, by choosing not to go down certain child links
00888             // we can omit whole subtrees from the scan.
00889             // The following tests optimise away much of the work of a true depth-first
00890             // search by pruning subtrees that don't need to be traversed.
00891 
00892             //---------------------------------------------------------------------------//
00893             // If above layers, skip on down towards one...
00894             while (pContext->FindFirstChild() && !pContext->IsLayer() && pContext->IsPaper())
00895                 pContext = pContext->FindFirstChild();
00896             // If we've not made it to a layer and we're still in paper then prune whatever
00897             // subtree we have because this routine is interested in children of layers and
00898             // their subtrees.
00899             if (pContext->IsPaper() && !pContext->IsLayer())
00900                 goto PruneSubtree;
00901 
00902             //---------------------------------------------------------------------------//
00903             // We know now we're either at a layer node or underneath one...
00904             // If this layer has children go down to that level because that surface has
00905             // a special meaning to this routine. Most ranges are found in the child lists
00906             // of Layers.
00907             if (pContext->IsLayer())
00908             {
00909                 // Don't allow Ranges into invisible layers at all!
00910                 // New range control flag allows this to be overriden, defaults to FALSE
00911                 if ( !RangeControlFlags.IgnoreInvisibleLayers && !((Layer*)pContext)->IsVisible() )
00912                     goto PruneSubtree;
00913 
00914                 // Don't allow SelRanges into locked layers at all!
00915                 // New range control flag allows this to be overriden, defaults to FALSE
00916                 if ( RangeControlFlags.IgnoreLockedLayers && ((Layer*)pContext)->IsLocked() )
00917                     goto PruneSubtree;
00918 
00919                 // If layer has children go find first child and continue pruning tests
00920                 if (pContext->FindFirstChild())
00921                     pContext = pContext->FindFirstChild();
00922                 // Otherwise don't try to process empty layers at all!
00923                 else
00924                     goto PruneSubtree;
00925             }
00926 
00927             //---------------------------------------------------------------------------//
00928             // Descend subtree further if we come across select-inside (flagged by ParentOfSelected)
00929             // If looking for selected nodes (and not unselected nodes - Ed 1/5/95)
00930             // AND this node is the parent of selected nodes
00931             // Then go down this subtree as far as the parent of selected
00932             // flags last out...
00933             if (pContext->IsParentOfSelected() && RangeControlFlags.Selected && !RangeControlFlags.Unselected)
00934             {
00935                 while (pContext->IsParentOfSelected() && pContext->FindFirstChild())
00936                 {
00937                     pContext = pContext->FindFirstChild();
00938                 }
00939                 ERROR3IF(pContext->IsParentOfSelected(),"Parent of selected has no children!");
00940             }
00941 
00942 // Logically, if promotetoparent is not set we shouldn't return any node which would
00943 // be the target of promotetoparent activities...
00944 if (!RangeControlFlags.PromoteToParent && RangeControlFlags.Selected && RangeControlFlags.Unselected)
00945 {
00946     while (pContext->ShouldITransformWithChildren() && pContext->FindFirstChild())
00947     {
00948         pContext = pContext->FindFirstChild();
00949     }
00950     ERROR3IF(pContext->ShouldITransformWithChildren(), "Parent of selected has no children!");
00951 }
00952 
00953             //---------------------------------------------------------------------------//
00954             // Finally, there's no choice any more, we've got to descend into the subtree IF
00955             // the AndChildren flag is set and the node is a Primary member of the range
00956             // Then we are either already scanning a subtree and so we must continue
00957             // or else we're starting to scan a subtree because it meets the range
00958             // criteria and the range specifies that all child nodes of primary nodes
00959             // should be returned.
00960             // So, look for lowest leaf of subtree...
00961             if (!RangeControlFlags.SiblingsOnly && AndChildren)
00962             {
00963                 Node* pChild = pContext->FindFirstChild();
00964                 while (pChild)
00965                 {
00966                     if (InRange(pChild, AndChildren))
00967                     {
00968                         pContext=pChild;
00969                         pChild=pChild->FindFirstChild();
00970                     }
00971                     else
00972                         pChild=pChild->FindNext();
00973                 }
00974             }
00975 
00976             //---------------------------------------------------------------------------//
00977             // If control gets here then the root of the "next" subtree is being returned for
00978             // consideration because we have judged that its subtree doesn't need scanning!
00979 PruneSubtree:;
00980 
00981         }
00982         else
00983         {
00984             // No next subtree, so go up to parent...
00985             // If no parent then we must have scanned the entire tree (by definition of
00986             // depth-first traversal) so return NULL to caller.
00987             pContext = pContext->FindParent();
00988 
00989             // If we're not promoting to parent and we find our parent is the type of object we would normally
00990             // promote to, DON'T return it, keep going up!
00991             // (It doesn't make sense to return a node and it's parent controller)
00992             // This change made so that a shadowed node on the clipboard will return a set of sensible
00993             // common attributes when FindCommonAttributes is called on the clipboard range.
00994             // Test by using Paste Attributes
00995 // NOPE, THIS DOESN'T WORK!
00996 //          while (pContext && !AndChildren && !RangeControlFlags.PromoteToParent && pContext->ShouldITransformWithChildren())
00997 //          {
00998 //              pContext = pContext->FindParent();
00999 //          }
01000 
01001             if (pContext==NULL)
01002                 return NULL;
01003 
01004             // Ranges cannot exist across spreads so if we reach a Spread node we can stop...
01005             if (pContext->IsSpread())
01006                 return NULL;
01007 
01008             if (pContext->IsLayer() && !RangeControlFlags.CrossLayer)
01009                 // Can't cross layers so signal end of Range...
01010                 return NULL;
01011         }
01012     }
01013     while (pContext!=NULL && (pContext->IsLayer() || pContext->IsPaper() || !InRange(pContext, AndChildren)));
01014 
01015     // Finally, check for parents wanting to be included instead of the children
01016     if (pContext != NULL && RangeControlFlags.PromoteToParent && !AndChildren)
01017     {
01018         Node *pParent = pContext->FindParent();
01019         while (pParent != NULL && pParent->ShouldITransformWithChildren())
01020         {
01021             pContext = pParent;
01022             pParent = pContext->FindParent();
01023         }
01024     }
01025 
01026     return pContext;
01027 }
01028 
01029 
01030 
01031 
01032 /******************************************************************************************
01033 
01034 >   BOOL Range::InRange(Node* pNode, BOOL AndChildren) const
01035 
01036      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01037      Created:   24/11/94
01038      Rewritten: 08/12/2004
01039      Inputs:    pNode           Pointer to node to test for inclusion in the range
01040                 AndChildren     Children of the primary range are allowed to be returned
01041      Outputs:   -
01042      Returns:   TRUE if node is in this range (ignoring CrossLayer flag)
01043                 FALSE if not
01044      Purpose:   Private helper function for SmartFindNext.
01045                 Tests whether the presented node is in the range according to the
01046                 RangeControlFlags and two other locally presented flags.
01047 
01048                 Version 2, 08/12/2004
01049                 Simplified and adjusted to deal with selection inside Compound Parents
01050                 Controllers and PostProcessors
01051 
01052                 This function should ONLY be called on nodes that /could/ be members of Range
01053                 Not Layers or Paper nodes!
01054 
01055      Scope:     Protected
01056      SeeAlso:   Range::SmartFindNext
01057      Errors:    When attempting to return children and finds no parent link.
01058 
01059 ******************************************************************************************/
01060 
01061 BOOL Range::InRange(Node* pNode, BOOL AndChildren) const
01062 {
01063     // Preconditions
01064     ERROR2IF(this==NULL, FALSE, "InRange called on NULL range");
01065     ERROR2IF(pNode==NULL, FALSE, "InRange given a NULL node");
01066     ERROR2IF(pNode->IsPaper(), FALSE, "InRange asked to decide on Paper");
01067     ERROR2IF(pNode->IsLayer(), FALSE, "InRange asked to decide on Layer");
01068     ERROR2IF(RangeControlFlags.Unselected && !RangeControlFlags.Selected && FirstNode==NULL, FALSE, "InRange can't detect unselected nodes without a FirstNode");
01069 
01070     //------------------------------------------------------------------------------------
01071     // Test for Primary members of the range...
01072     //
01073     // If an op has asked this node for permission to apply the op to it, and the node said no, return.
01074     if (pNode->GetOpPermission() == PERMISSION_DENIED)
01075         return FALSE;
01076 
01077     // if trying to ignore none renderable nodes then check the node
01078     SubtreeRenderState state = pNode->RenderSubtree(NULL);
01079     if (RangeControlFlags.IgnoreNoneRenderable && !(state==SUBTREE_ROOTONLY || state==SUBTREE_ROOTANDCHILDREN))
01080         return FALSE;
01081 
01082     // If looking for selected nodes and node is selected, OK
01083     if (RangeControlFlags.Selected && pNode->IsSelected())
01084         return TRUE;
01085 
01086 // If we're not promoting to parents but we seem to have found one such parent
01087 // Then deny this node
01088 if (!AndChildren && !RangeControlFlags.PromoteToParent && pNode->ShouldITransformWithChildren())
01089     return FALSE;
01090 
01091     // If looking for all nodes but not going into child subtrees, OK whether node is selected or not
01092     if (!AndChildren && RangeControlFlags.Selected && RangeControlFlags.Unselected)
01093         return TRUE;
01094 
01095     // If node isn't paper then it ought to have a parent node which we will need later...
01096     Node* pParent = pNode->FindParent();
01097     ERROR2IF_PF(pParent==NULL, FALSE, ("OOER! Range found an ink node %lx without any parents!",pNode));
01098 
01099     Node* pFirstParent = NULL;
01100     if (FirstNode)
01101         pFirstParent = FirstNode->FindParent();
01102 
01103     // If looking for immediate siblings only then check that this ndoe has the same parent as
01104     // the first node (we've already checked pParent to be non-NULL)
01105     if (RangeControlFlags.SiblingsOnly)
01106     {
01107         if (pFirstParent!=pParent)
01108             return FALSE;
01109     }
01110     
01111     // If looking for unselected nodes then we need to check whether this node is unselected
01112     // and in the same level of the tree as the first node...
01113     while (!pNode->IsSelected() && RangeControlFlags.Unselected && pFirstParent && pParent)
01114     {
01115         if (pFirstParent == pParent)
01116             return TRUE;
01117 
01118         pFirstParent = pFirstParent->FindParent();
01119         pParent = pParent->FindParent();
01120     }
01121 
01122     //------------------------------------------------------------------------------------
01123     // Test for Secondary members of the range by recursion...
01124     //
01125     // If looking for children of nodes in the primary range specification
01126     // or looking for selected nodes (which may be "selected-inside")
01127     // Then we must check that our parent is in the range. If he is then we are!
01128     if (AndChildren && !pParent->IsLayer() && !pParent->IsPaper())
01129         return InRange(pParent, AndChildren);
01130 
01131     return FALSE;
01132 }
01133 
01134 
01135 
01136 
01137 
01138 
01139 
01140 
01141 /*****************************************************************************************/
01142 
01143 /********************************************************************************************
01144 
01145 >   List* Range::MakeListOfNodes(BOOL AndChildren = FALSE)
01146 
01147     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01148     Created:    22/7/94
01149     Inputs:     -
01150     Outputs:    -
01151     Returns:    Pointer to a list of NodeListItems representing the current range
01152     Purpose:    This function will create and return a pointer to a list representing
01153                 the objects in the range. It will return NULL if it couldn't create the
01154                 list due to lack of memory.
01155                 WARNING! This function creates objects which must be deleted by the calling
01156                 routine. The last thing we need right now is another bunch of listitems
01157                 appearing on shutdown.
01158     Errors:     -
01159     SeeAlso:    -
01160 
01161 ********************************************************************************************/
01162 
01163 List* Range::MakeListOfNodes(BOOL AndChildren)
01164 {
01165     // Create the list object first
01166     List* RangeList = new List;
01167     
01168     // only build the list if we found enough memory
01169     if (RangeList)
01170     {
01171         // Get the first object in the range to start the loop
01172         Node* pNode = FindFirst(AndChildren);
01173         BOOL Failed = FALSE;            // Will be TRUE if we ran out of memory
01174 
01175         // loop round all the objects in the range
01176         while (pNode)
01177         {
01178             if (!pNode->IsNodeHidden()) // DON'T record hidden nodes because they might be deleted!
01179             {
01180                 // Mark Howitt 13/12/00 - This is a bodge fix for the release version. As the ClipViewAttribute is
01181                 // only used by the new bevel code, it`s safe (Bodge) to make it not includable in any selections.
01182                 // This will need to be fix for future versions if we decide to start using the attribute correctly!
01183                 if(!pNode->IsAClipViewAttr())
01184                 {
01185                     NodeListItem* pItem;
01186                     if ((pItem = new NodeListItem(pNode))!=NULL)
01187                     {
01188                         // Creation successful, so add it to the list
01189                         RangeList->AddTail(pItem);
01190                     }
01191                     else
01192                     {
01193                         // Creation failed, so flag an error and break the loop
01194                         Failed = TRUE;
01195                         break;
01196                     }
01197                 }
01198             }
01199 
01200             // Go to the next node in the range
01201             pNode = FindNext(pNode, AndChildren);
01202         }
01203         // Handle an error if it occurred
01204         if (Failed)
01205         {
01206             while (!RangeList->IsEmpty())
01207             {
01208                 NodeListItem* pItem = (NodeListItem*)(RangeList->RemoveHead());
01209                 delete pItem;
01210             }
01211             delete RangeList;
01212             return NULL;
01213         }
01214 
01215     }
01216     return RangeList;
01217 }
01218 
01219 
01220 /********************************************************************************************
01221 
01222 >   void Range::UpdateParentBoundsOfSelection(BOOL AndChildren = FALSE)
01223 
01224     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01225     Created:    15/7/94
01226     Purpose:    This function updates the parent bounds of all selected objects. It is a 
01227                 lot more efficient than calling ChangeBounds on every node in the selection
01228                 (cos it uses an algorithm!)
01229     SeeAlso:    NodeRenderableBounded::ChangeBounds
01230 
01231 ********************************************************************************************/
01232 
01233 void Range::UpdateParentBoundsOfSelection(BOOL AndChildren)
01234 {
01235     // Find the first node in the selection
01236     Node* pNode = FindFirst(AndChildren);
01237     
01238     // loop through the selection, invalidating all the bounding rects
01239     while (pNode!=NULL)
01240     {
01241         // If the node was bounded, then invalidate its bounding rect
01242         if (pNode->IsBounded())
01243             ((NodeRenderableBounded*)pNode)->InvalidateBoundingRect();
01244 
01245         // Get the next node in the selection
01246         pNode = FindNext(pNode, AndChildren);
01247     }
01248 
01249 // Below is the old version of this function, that used to use change bounds
01250 // Since this no longer exists, a new bit of code has been created
01251 #if 0
01252     Node* Current = FindFirst(); 
01253     Node* UpdatedDaddy = NULL;
01254     Node* CurrentDaddy;
01255 
01256     NodeRenderableBounded* CurrentBound; 
01257 
01258     // For each selected node
01259     while(Current != NULL)
01260     {
01261         if (Current->IsAnObject())
01262         {
01263             // Find out Current's dad
01264             CurrentDaddy = Current->FindParent(); 
01265         
01266             CurrentBound = (NodeRenderableBounded*)Current;
01267 
01268             if (UpdatedDaddy != CurrentDaddy)
01269             {
01270                 // We need to update CurrentDaddy
01271                 CurrentBound->InvalidateBoundingRect();
01272 
01273                 // Remember that CurrentDaddy has been updated
01274                 UpdatedDaddy = CurrentDaddy;    
01275             }
01276         }
01277         Current = FindNext(Current);        
01278     }
01279 #endif
01280 } 
01281 
01282 
01283 
01284 
01285 /********************************************************************************************
01286 
01287 >   BOOL Range::MakeAttributeComplete(BOOL CheckForDuplicates = TRUE,
01288                                       BOOL AndChildren = FALSE,
01289                                       BOOL IgnoreComplex = FALSE)
01290 
01291     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01292     Created:    7/2/94
01293 
01294     Inputs:     CheckForDuplicates: When TRUE a check is made to ensure that duplicate
01295                                     attribiutes are not added to the subtree
01296                 AndChildren:
01297                 IgnoreComplex:      When TRUE do not make attributes complete on complexcopy
01298                                     nodes.
01299                     
01300     Outputs:    -
01301     Returns:    TRUE if successful
01302                 FALSE if we run out of memory, the children of the node will remain unchanged
01303 
01304     Purpose:    This function calls NodeRenderableInk::MakeAttributeComplete on every Object
01305                 in the selection. If we run out of memory then all changes are undone. 
01306                   
01307     SeeAlso:    NodeRenderableInk::MakeAttributeComplete
01308     SeeAlso:    Range::NormaliseAttributes
01309 
01310 ********************************************************************************************/
01311 
01312 BOOL Range::MakeAttributeComplete(BOOL CheckForDuplicates, 
01313                                   BOOL AndChildren, 
01314                                   BOOL IgnoreComplex)
01315 {
01316     BOOL MakeComplete;
01317     Node* Current = FindFirst(AndChildren); // First item in range
01318     while (Current != NULL)
01319     {
01320         if (Current->IsAnObject())
01321         {
01322             NodeRenderableInk* pInkObj = (NodeRenderableInk*)Current;
01323             MakeComplete = TRUE;
01324 
01325             if (IgnoreComplex)
01326                 if ((pInkObj->GetCopyType()) == COMPLEXCOPY)
01327                     MakeComplete=FALSE;
01328 
01329             // complete the attributes on this node if necessary
01330             if (MakeComplete)
01331             {
01332                 if (!(pInkObj->MakeAttributeComplete(NULL, CheckForDuplicates)))
01333                 {
01334                     // Note that at this point Current's children have not been changed
01335                     goto Abort; 
01336                 }
01337             }
01338         }
01339         Current = FindNext(Current, AndChildren); 
01340     }
01341     return TRUE; // Success
01342     
01343     Abort:
01344     // We need to tidyup any changes that have been made
01345     BOOL Normalise;
01346     Node* Sentinal = Current; 
01347     Current = FindFirst(AndChildren); 
01348     while (Current != Sentinal )
01349     {
01350         ERROR3IF(Current == NULL, "Could not find sentinal"); 
01351         if (Current->IsAnObject())
01352         {
01353             NodeRenderableInk* pInkObj = (NodeRenderableInk*)Current;
01354             Normalise = TRUE;
01355 
01356             if (IgnoreComplex)
01357                 if ((pInkObj->GetCopyType()) == COMPLEXCOPY)
01358                     Normalise=FALSE;
01359 
01360             // complete the attributes on this node if necessary
01361             if (Normalise)
01362                 pInkObj->NormaliseAttributes();         
01363         }
01364 
01365         Current = FindNext(Current, AndChildren);
01366     }
01367     return FALSE; 
01368 }
01369 
01370 
01371 
01372 
01373 /********************************************************************************************
01374 
01375 >   void Range::NormaliseAttributes(BOOL AndChildren = FALSE)
01376 
01377     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01378     Created:    7/2/94
01379     Inputs      AndChildren:    Normalise the attributes on children as well
01380                 IgnoreComplex:  When TRUE do not make attributes complete on complexcopy
01381                                 nodes.
01382 
01383     Outputs:    -
01384     Returns:    -
01385 
01386     Purpose:    This function calls NodeRenderableInk::NormaliseAttributes on every Object
01387                 in the selection.  
01388                   
01389     SeeAlso:    NodeRenderableInk::NormaliseAttributes
01390     SeeAlso:    Range::MakeAttributeComplete
01391 
01392 ********************************************************************************************/
01393 
01394 void Range::NormaliseAttributes(BOOL AndChildren,
01395                                 BOOL IgnoreComplex)
01396 {
01397     BOOL Normalise;
01398     Node* Current = FindFirst(AndChildren); // First item in range
01399     while (Current != NULL)
01400     {
01401         if (Current->IsAnObject())
01402         {
01403             NodeRenderableInk* pInkObj = (NodeRenderableInk*)Current;
01404             Normalise = TRUE;
01405 
01406             if (IgnoreComplex)
01407                 if ((pInkObj->GetCopyType()) == COMPLEXCOPY)
01408                     Normalise=FALSE;
01409 
01410             // complete the attributes on this node if necessary
01411             if (Normalise)
01412                 pInkObj->NormaliseAttributes();         
01413 
01414         }
01415         Current = FindNext(Current, AndChildren);
01416     }
01417 }
01418 
01419 
01420 
01421 
01422 
01423 /********************************************************************************************
01424 
01425 >   BOOL Range::CopyComponentDataToDoc(BaseDocument* pSrcDoc,
01426                                        BaseDocument* pDestDoc,
01427                                        BOOL AndChildren = FALSE)
01428 
01429     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01430     Created:    9/9/94
01431     Inputs:     pSrcDoc: the document from where all nodes in the range were copied
01432                 pDestDoc - where to copy all the nodes to.
01433     Returns:    FALSE if we are unable to copy the data accross. In this situation 
01434                 AbortComponentCopy is called on the CurrentDocument. 
01435     Purpose:    Asks all objects in the range (including their children) to copy across their
01436                 component data to the specified destination document.
01437                 The range must be in the destination doc.
01438     Errors:     -
01439     SeeAlso:    -
01440 
01441 ********************************************************************************************/
01442 
01443 BOOL Range::CopyComponentDataToDoc(BaseDocument* pSrcDoc,
01444                                    BaseDocument* pDestDoc,
01445                                    BOOL AndChildren)
01446 {
01447     // Scan all nodes in the range
01448     Node* Scan = FindFirst(AndChildren);
01449     while (Scan != NULL)
01450     {
01451         Node* Root = Scan;
01452         Node* Current = Root->FindFirstDepthFirst();
01453         // This depth first search was written BEFORE the AndChildren flag was passed in.
01454         // It could probably be removed now...
01455         while (Current != NULL)
01456         {
01457             // Ask the current node if it would copy it's data to the relevant DocComponents
01458             if (Current->IsKindOf(CC_RUNTIME_CLASS(NodeRenderable)))
01459             {
01460                 if (!((NodeRenderable*)Current)->CopyComponentData(pSrcDoc, pDestDoc))
01461                 {
01462                     // No luck
01463                     pDestDoc->AbortComponentCopy(); // Cancel all data which has been copied
01464                     return FALSE; 
01465                 }
01466             }
01467 
01468             Current = Current->FindNextDepthFirst(Root); 
01469         }
01470         Scan = FindNext(Scan, AndChildren);
01471     }
01472     return TRUE; // Success         
01473 } 
01474 
01475 
01476 
01477 
01478 /********************************************************************************************
01479 
01480 >   virtual DocRect Range::GetBoundingRect()
01481 
01482     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01483     Created:    29/9/94
01484     Inputs:     -
01485     Outputs:    -
01486     Returns:    The bounding rectangle of all objects in the range
01487     Purpose:    To determine the bounds of all objects in the range 
01488     Errors:     -
01489     SeeAlso:    -
01490 
01491 ********************************************************************************************/
01492 
01493 DocRect Range::GetBoundingRect()
01494 {
01495     DocRect Bounds; 
01496     // Calculate the union of all bounded nodes within the range 
01497     Node* pNode = FindFirst(FALSE);
01498     while (pNode != NULL)
01499     {
01500         if (pNode->IsBounded())
01501             Bounds = Bounds.Union(((NodeRenderableBounded*)pNode)->GetBoundingRect());
01502 
01503         pNode = FindNext(pNode, FALSE);
01504     } 
01505     return Bounds; 
01506 
01507 }
01508 
01509 /********************************************************************************************
01510 
01511 >   virtual DocRect Range::GetBoundingRectForEorDragging()
01512 
01513     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
01514     Created:    20/1/2000
01515     Inputs:     -
01516     Outputs:    -
01517     Returns:    The bounding rectangle of all objects in the range
01518     Purpose:    Same as for GetBoundingRect() but calls GetDragEorBoundingRect for all
01519                 nodes           
01520     Errors:     -
01521     SeeAlso:    -
01522 
01523 ********************************************************************************************/
01524 
01525 DocRect Range::GetBoundingRectForEorDragging()
01526 {
01527     DocRect Bounds; 
01528     // Calculate the union of all nodes within the range 
01529     NodeRenderableBounded* pNode = (NodeRenderableBounded*) FindFirst(FALSE);
01530     while (pNode != NULL)
01531     {
01532         Bounds = Bounds.Union(pNode->GetEorDragBoundingRect()); 
01533         pNode =(NodeRenderableBounded*)FindNext(pNode, FALSE);
01534     } 
01535     return Bounds; 
01536 
01537 }
01538 
01539 
01540 
01542 // Implementation of NodeListItem
01544 
01545 /********************************************************************************************
01546 
01547 >   NodeListItem::NodeListItem()
01548 
01549     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01550     Created:    22/7/94
01551     Inputs:     -
01552     Outputs:    -
01553     Returns:    -
01554     Purpose:    default constructor. Initialises pNode = NULL
01555     Errors:     -
01556     SeeAlso:    -
01557 
01558 ********************************************************************************************/
01559 
01560 NodeListItem::NodeListItem()
01561 {
01562     pNode = NULL;
01563 }
01564 
01565 /********************************************************************************************
01566 
01567 >   NodeListItem::NodeListItem(Node* WhichNode)
01568 
01569     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01570     Created:    22/7/94
01571     Inputs:     -
01572     Outputs:    -
01573     Returns:    -
01574     Purpose:    Constructor for NodeListItem which sets the pNode variable to WhichNode
01575     Errors:     -
01576     SeeAlso:    -
01577 
01578 ********************************************************************************************/
01579 
01580 NodeListItem::NodeListItem(Node* WhichNode)
01581 {
01582     pNode = WhichNode;
01583 }
01584 
01585 
01586 
01587 
01588 
01589 
01590 
01591 
01592 
01593 
01594 
01595 
01596 
01597 //------------------------------------------------------------------------------------------
01598 // Shared XOR-redraw-of-selection methods (Used by Scale, rotate, select tools)
01599 
01600 
01601 /********************************************************************************************
01602 
01603 >   void Range::ResetXOROutlineRenderer(BOOL FlushTheCache = TRUE);
01604 
01605     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01606     Created:    22/2/94
01607     Inputs:     FlushTheCache - This should ALWAYS be TRUE in external calls
01608 
01609     Purpose:    Shared code used by the rotate, select, and scale tools to XOR the object(s)
01610                 being dragged on/off screen. This function is called before starting to XOR
01611                 the objects onto screen to reset the count of objects which have been rendered
01612                 (for interruptible blob redraw) and other pertinent information.
01613                 This should be called once before starting an outline drag, and under normal
01614                 circumstances need never be called again, as it is called internally by
01615                 RenderXOROutlinesOff at appropriate times.
01616 
01617     Notes:      For example client code, see the selector and rotate tools.
01618                 If you fail to call this before starting outline dragging, the cached drag
01619                 information used by related calls will be wrong, and it'll break.
01620 
01621     SeeAlso:    SelRange::RenderXOROutlinesOn; SelRange::RenderXOROutlinesOff;
01622                 SelRange::RenderXOROutlinesToCatchUp; OpSelectorMove
01623 
01624 ********************************************************************************************/
01625 
01626 typedef enum            // Rendering pass order (a simple state machine mechanism)
01627 {
01628     RENDER_START,       // About to start rendering passes from the beginning
01629     RENDER_HIGH,        // Render all high-priority objects
01630     RENDER_MEDIUM,      // Render all medium-priority objects
01631     RENDER_LOW,         // Render all low-priority objects (anything that's left!)
01632     RENDER_END          // Have rendered all available objects
01633 } XORRenderPasses;
01634 
01635 
01636 void Range::ResetXOROutlineRenderer(BOOL FlushTheCache)
01637 {
01638     NumObjectsDrawn = 0;
01639     RenderingPass   = RENDER_START;
01640 
01641     if (FlushTheCache)          // Ensure info cache is cleared     
01642     {
01643         XORInfo.PrimeObject = NULL;
01644         XORInfo.SigRegion.MakeEmpty();
01645         XORInfo.SigRegionIsScreen = TRUE;
01646         XORInfo.Cached = FALSE;
01647     }
01648 
01649     
01650 }
01651 
01652 
01653 
01654 /********************************************************************************************
01655 
01656 >   BOOL Range::CacheXOROutlineInfo(Spread *pSpread, Node *ClickedNode);
01657 
01658     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01659     Created:    23/2/94
01660 
01661     Inputs:     Spread - The spread in which the drag is occurring
01662                 ClickedNode - The renderable node which the user clicked on to start the drag.
01663                 (This object will always be drawn first. You may pass in NULL, but this will
01664                 reduce the interactiveness of the dragging system)
01665 
01666     Purpose:    This function is called before starting to XOR the objects onto screen to
01667                 precalculate information pertaining to object significance (the order in which
01668                 objects are drawn)
01669 
01670     Notes:      See the selector and rotate tools for example code using this system.
01671 
01672     Scope:      private
01673     SeeAlso:    SelRange::RenderXOROutlinesOn; SelRange::RenderXOROutlinesOff;
01674                 SelRange::RenderXOROutlinesToCatchUp
01675 
01676 ********************************************************************************************/
01677 
01678 BOOL Range::CacheXOROutlineInfo(Spread *pSpread, Node *ClickedObject)
01679 // Caches information which is used to determine object significance (drawing
01680 // priority) in the DragBlob XORing routines. If called again, will return
01681 // directly, as it has cached the information. Call ResetRenderingDragBlobs
01682 // to flush the cache ready for re-caching.
01683 {
01684 
01685 #if !defined(EXCLUDE_FROM_RALPH)
01686 
01687     if (XORInfo.Cached)     // Have already cached the info
01688         return TRUE;
01689 
01690     BOOL bPromote = RangeControlFlags.PromoteToParent;
01691     RangeControlFlags.PromoteToParent = TRUE;
01692 
01693     ERROR2IF(pSpread == NULL,FALSE, "NULL Spread pointer passed to SelRange::CacheXOROutlineInfo");
01694     ERROR2IF(DocView::GetSelected() == NULL,FALSE, "DocView::GetSelected is NULL in SelRange::CacheXOROutlineInfo");
01695 
01696     DocView *pDocView = DocView::GetSelected();
01697     if (pDocView == NULL)
01698     {
01699         RangeControlFlags.PromoteToParent = bPromote;
01700         return FALSE;
01701     }
01702 
01703     XORInfo.SigRegion = pDocView->GetDocViewRect(pSpread);
01704     
01705     // Take screenrect or source bounding rect, whichever is smaller
01706     // (if the source rect passed in was NULL, just use screen coords)
01707     DocRect SourceRegion = GetBoundingRectForEorDragging();
01708     if (Count() > 0 &&
01709         XORInfo.SigRegion.Width() + XORInfo.SigRegion.Height() >
01710         SourceRegion.Width() + SourceRegion.Height())
01711     {
01712         XORInfo.SigRegion = SourceRegion;
01713         XORInfo.SigRegionIsScreen = FALSE;
01714     }
01715     else
01716     {
01717         // Convert SigRgn from Document to Spread coords
01718         pSpread->DocCoordToSpreadCoord(&XORInfo.SigRegion);
01719         XORInfo.SigRegionIsScreen = TRUE;
01720     }
01721 
01722 
01723     XORInfo.PrimeObject = NULL;
01724     if (ClickedObject != NULL)
01725     {
01726         // And remember which object must always be drawn first (the prime object)
01727         //  --but only if it's a renderable object that is part of the selection!
01728         BOOL Selected = ClickedObject->IsSelected();
01729 
01730         if (ClickedObject->ShouldITransformWithChildren())
01731         {
01732             Selected = TRUE;
01733         }
01734 
01735         if (!Selected)
01736         {
01737             // The object isn't selected, but perhaps it is in a selected group.
01738             // Search up through all parent nodes until we have determined if it
01739             // really is selected.
01740 
01741             Node *TheNode = (Node *) ClickedObject;
01742             do
01743             {
01744                 Selected = (TheNode->IsSelected());
01745                 TheNode = TheNode->FindParent();
01746             }
01747             while (!Selected && TheNode != NULL);
01748         }
01749     
01750         if (Selected && ClickedObject->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)))
01751             XORInfo.PrimeObject = ClickedObject;
01752     }
01753     else
01754     {
01755         
01756     }
01757 
01758     XORInfo.Cached = TRUE;
01759 
01760     RangeControlFlags.PromoteToParent = bPromote;
01761 
01762     return TRUE;
01763 #else
01764     return FALSE;
01765 #endif
01766 }
01767 
01768 
01769 /********************************************************************************************
01770 
01771 >   BOOL Range::FindNextXOROutlineNode(Node **OriginalNode, DocRect *pClipRect);
01772 
01773     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01774     Created:    22/2/94
01775     Inputs:     OriginalNode - A handle on the current Node. Will be filled in with NULL or a
01776                 pointer to the next node to be drawn, or NULL if there is nothing more to draw
01777 
01778                 pClipRect - The current rendering clip rectangle - nodes outside this area
01779                 will not be considered for redrawing.
01780 
01781     Purpose:    Shared code used by the rotate, select, and scale tools to XOR the object(s)
01782                 being dragged on/off screen. This function is used to calculate the next
01783                 object to be drawn; it attempts to draw the most significant objects first,
01784                 so that minute detail is only drawn if there is enough time spare. This
01785                 significantly enhances interactiveness of dragging.
01786 
01787     Notes:      See the selector and rotate tools for example code using this system.
01788 
01789     Scope:      private
01790 
01791     SeeAlso:    SelRange::RenderXOROutlinesOn; SelRange::RenderXOROutlinesOff;
01792                 SelRange::RenderXOROutlinesToCatchUp
01793 
01794 ********************************************************************************************/
01795 
01796 BOOL Range::FindNextXOROutlineNode(Node **OriginalNode, DocRect *pClipRect)
01797 // Finds the next selected NodeRedrawable for the XOR dragblobs code to draw.
01798 // Now returns objects in the following order, based upon their significance:
01799 //  1) Prime object (the object on whic the user clicked to start the drag)
01800 //  2) High priority object(s) - any:   screen/2 < object_size < screen
01801 //  3) Medium priority -                screen/6 < object_size < screen/2
01802 //  4) Low priority - anything left over (<screen/6 or >screen)
01803 //
01804 // If the selection was larger than the screen, the significance of each object
01805 // which was initially visible on screen is promoted (by doubling it) to increase
01806 // the chance of visible things being drawn before offscreen things.
01807 {
01808 #if !defined(EXCLUDE_FROM_RALPH)
01809     BOOL bPromote = RangeControlFlags.PromoteToParent;
01810     RangeControlFlags.PromoteToParent = FALSE;
01811 
01812     Node * pParent = NULL;
01813 
01814     BOOL DoRender = FALSE;
01815     const INT32 MaxSignificance = XORInfo.SigRegion.Width() + XORInfo.SigRegion.Height();
01816     const INT32 MediumSignificance  = MaxSignificance / 6;
01817     const INT32 HighSignificance        = MaxSignificance / 2;
01818 
01819     NodeRenderable *PrimeObject = (NodeRenderableInk *)XORInfo.PrimeObject;
01820 
01821     ERROR2IF(OriginalNode == NULL,FALSE, "Range::FindNextXOROutlineNode - NULL parameter is illegal");
01822     NodeRenderableBounded *CurrentNode = (NodeRenderableBounded *) *OriginalNode;
01823 
01824     if (CurrentNode == NULL)            // Can't continue from here, so start fresh
01825         RenderingPass = RENDER_START;
01826 
01827     if (CurrentNode == PrimeObject)
01828         CurrentNode = NULL;             // Start at beginning of selectn for next pass
01829     else
01830     {
01831         if (RenderingPass == RENDER_START && PrimeObject != NULL)
01832         {
01833             SubtreeRenderState state = PrimeObject->RenderSubtree(NULL);
01834             if (state==SUBTREE_ROOTONLY || state==SUBTREE_ROOTANDCHILDREN)
01835             {
01836                 *OriginalNode = (Node *) PrimeObject;   // Just starting, so
01837                 RangeControlFlags.PromoteToParent = bPromote;
01838                 return TRUE;                            // render prime object now
01839             }
01840         }
01841     }
01842 
01843     do
01844     {
01845         do      // Search for next renderable object
01846         {
01847             if (CurrentNode == NULL)
01848             {
01849                 // Have run out of objects to draw - Start again on the next pass
01850                 if (++RenderingPass >= RENDER_END)
01851                 {
01852                     *OriginalNode = NULL;
01853                     return TRUE;            // Everything drawn (all passes done)
01854                 }
01855 
01856                 CurrentNode = (NodeRenderableBounded *) FindFirst(TRUE);
01857 
01858                 // now, see if this node has a PromoteToParent node
01859                 if (CurrentNode)
01860                 {
01861                     pParent = CurrentNode->FindParent();
01862                 }
01863                 else
01864                 {
01865                     pParent = NULL;
01866                 }
01867                 
01868                 // DMc insertion - to cope with compound nodes
01869                 // in the cases they take care of their own blob rendering
01870                 if (pParent && pParent->IsAnObject())
01871                 {
01872                     if (((NodeRenderableInk *)pParent)->ChildrenAreEorDragRenderedByMe())
01873                     {
01874                         CurrentNode = (NodeRenderableBounded *)pParent;
01875                     }
01876 
01877                     pParent = pParent->FindParent();
01878                 }
01879             }
01880             else
01881             {
01882                 // DMc insertion - to cope with compound nodes
01883                 // in the cases they take care of their own blob rendering
01884                 CurrentNode = (NodeRenderableBounded *) FindNext(CurrentNode, TRUE);
01885 
01886                 // now, see if this node has a PromoteToParent node
01887                 if (CurrentNode)
01888                 {
01889                     pParent = CurrentNode->FindParent();
01890                 }
01891                 else
01892                 {
01893                     pParent = NULL;
01894                 }
01895                 
01896                 while (pParent && pParent->IsBounded())
01897                 {
01898                     if (pParent->IsAnObject())
01899                     {
01900                         if (((NodeRenderableInk *)pParent)->ChildrenAreEorDragRenderedByMe())
01901                         {
01902                             CurrentNode = (NodeRenderableBounded *)pParent;
01903                         }
01904                     }
01905 
01906                     pParent = pParent->FindParent();
01907                 }               
01908             }
01909                 
01910 //              CurrentNode = (NodeRenderableBounded *) FindNextAndChild(CurrentNode);
01911 
01912         } while (CurrentNode == NULL            ||
01913                     CurrentNode == PrimeObject  ||
01914                     !CurrentNode->IsBounded() );
01915                     
01916         // Now, determine priority, and return this object if priority is right
01917         DocRect BBox = CurrentNode->GetBoundingRect();
01918         INT32 Significance = BBox.Width() + BBox.Height();
01919 
01920         if (XORInfo.SigRegionIsScreen &&
01921             Significance < HighSignificance &&
01922             XORInfo.SigRegion.ContainsRect(BBox))
01923         {
01924             // If selection is larger than the screen, and this is not
01925             // large enough to be a high-significance object, but it was 
01926             // visible on-screen at the start of the drag, promote its
01927             // significance so it has a greater chance of being drawn soon.
01928             Significance *= 2;
01929         }
01930 
01931         switch(RenderingPass)
01932         {
01933             case RENDER_HIGH:           // High significance - draw ASAP
01934                 DoRender = Significance >= HighSignificance &&
01935                             Significance < MaxSignificance;
01936                 break;
01937 
01938             case RENDER_MEDIUM:         // Medium Significance - draw second
01939                 DoRender = Significance >= MediumSignificance &&
01940                             Significance < HighSignificance;
01941                 break;
01942     
01943             case RENDER_LOW:            // Low significance - draw last
01944                 DoRender = (Significance >= MaxSignificance) ||
01945                             (Significance < MediumSignificance);
01946                 break;
01947 
01948             default:
01949                 ERROR3_PF( ("** FindNextDragBlobNode - Illegal rendering pass %d. Tell Jason", RenderingPass) );
01950                 break;
01951         }
01952     } while (!DoRender);    
01953 
01954     *OriginalNode = (Node *) CurrentNode;
01955 
01956     RangeControlFlags.PromoteToParent = bPromote;
01957 
01958     return TRUE;
01959 #else
01960     return FALSE;
01961 #endif
01962 }
01963 
01964 
01965 
01966 /********************************************************************************************
01967 
01968 >   static void DrawXORBoundingRect(RenderRegion *pRender, DocRect &TheRect)
01969 
01970     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01971     Created:    28/4/95
01972 
01973     Inputs:     pRender - a render region to render into
01974                 TheRect - the current bounding rectanglew of the XOR outline objects
01975 
01976     Purpose:    Shared code which renders the bounding rectangle of the XOR outline objects
01977                 in the current colours. It is rendered as a path so that the rect
01978 
01979                 Basically just a veneer for DrawDragBounds
01980 
01981     SeeAlso:    OSRenderRegion::DrawDragBounds
01982 
01983 ********************************************************************************************/
01984 
01985 static inline void DrawXORBoundingRect(RenderRegion *pRender, DocRect &TheRect)
01986 {
01987     if (!TheRect.IsValid() || TheRect.IsEmpty() || !DocView::OutlineShowBounds)
01988         return;
01989     
01990 #if !defined(EXCLUDE_FROM_RALPH)
01991     pRender->SaveContext();
01992 
01993     pRender->SetLineColour(COLOUR_XORSELECT);
01994     pRender->DrawDragBounds(&TheRect);
01995 
01996     pRender->RestoreContext();
01997 #endif
01998 }
01999 
02000 
02001 
02002 /********************************************************************************************
02003 
02004 >   void Range::RenderXOROutlinesToCatchUp(DocRect* ClipRect, Spread *Spread,
02005                                                 Matrix *Transform);
02006 
02007     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> (moved here by Jason) (recoded and renamed by Jason)
02008     Created:    27/10/93 (new lease of life 04/03/94)
02009 
02010     Inputs:     ClipRect - NULL to just clip to the spread/window bounds, or a pointer to
02011                 a clipping rectangle into which the xored stuff will be clipped.
02012                 Spread - The spread in which the drag is occurring
02013                 Transform - A transformation matrix indicating how the blobs should
02014                 be rendered - this allows use of this one routine for translation, scaling,
02015                 and rotation operations.
02016 
02017     Purpose:    Shared code used by the rotate, select, and scale tools to XOR the object(s)
02018                 being dragged onto screen. This function is used when an area is redrawn during
02019                 a drag (e.g. scrolling the window forces a redraw) to 'catch up' to the XOR
02020                 outlines already present in other areas of the screen (i.e. it redraws
02021                 everything that should already be on screen in unchanged areas into the
02022                 invalidated area, so that XOR dragging can continue without leaving mess behind.
02023 
02024     Notes:      See the selector and rotate tools for example code using this system.
02025                 This function is identical to RenderXOROutlinesOff, only it preserves the
02026                 current state of background redraw, so that XOR state will not be corrupted.
02027 
02028     SeeAlso:    SelRange::RenderXOROutlinesOn; SelRange::RenderXOROutlinesOff
02029 
02030 ********************************************************************************************/
02031 
02032 void Range::RenderXOROutlinesToCatchUp(DocRect *ClipRect, Spread *pSpread, Matrix *Transform)
02033 {
02034 #if !defined(EXCLUDE_FROM_RALPH)
02035     BOOL bPromote = RangeControlFlags.PromoteToParent;
02036     RangeControlFlags.PromoteToParent = TRUE;
02037 
02038     INT32 OldRenderingPass = RenderingPass; // Preserve previous rendering pass
02039 
02040     // Start up a bit of a RenderOnTop loop type of thing to do all the rendering
02041     RenderRegion* pRegion = DocView::RenderOnTop( ClipRect, pSpread, ClippedEOR );
02042     while ( pRegion )
02043     {
02044         // Need to tell the render region to transform the objects a bit first.
02045         Matrix OldRenderMatrix = pRegion->ConcatenateMatrix( *Transform );
02046 
02047         DocRect Bounds = GetBoundingRectForEorDragging();
02048         DrawXORBoundingRect(pRegion, Bounds);
02049 
02050         // Set the line colour and fill colour
02051         pRegion -> SetFillColour( COLOUR_TRANS );
02052         pRegion -> SetLineColour( COLOUR_XORDRAG );
02053 
02054         // Render the first NumObjectsDrawn objects
02055         Node *CurrentNode = NULL;
02056         RenderingPass = RENDER_START;
02057 
02058         for (INT32 i=0; i<NumObjectsDrawn; i++)
02059         {
02060             // Find and draw the next object
02061             FindNextXOROutlineNode(&CurrentNode, ClipRect);
02062             if (CurrentNode == NULL)
02063                 break;              // Shouldn't happen, but best to be safe
02064 
02065             if (CurrentNode->IsRenderable() || ForceRenderEORAll()) ((NodeRenderableInk*)CurrentNode)->RenderEorDrag(pRegion);
02066         }
02067 
02068         // OK, we have finished with this render region, so set the matrix
02069         // back to how we found it and get the next one
02070         pRegion -> SetMatrix( OldRenderMatrix );
02071         pRegion = DocView::GetNextOnTop( ClipRect );
02072     }
02073 
02074     RenderingPass = OldRenderingPass;   // Restore previous rendering pass info
02075 
02076     RangeControlFlags.PromoteToParent = bPromote;
02077 
02078 #endif
02079 }
02080 
02081 
02082 /********************************************************************************************
02083 
02084 >   void Range::RenderXOROutlinesOff(DocRect* ClipRect, Spread *Spread,
02085                                                 Matrix *Transform);
02086 
02087     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02088     Created:    04/03/94
02089 
02090     Inputs:     ClipRect - NULL to just clip to the spread/window bounds, or a pointer to
02091                 a clipping rectangle into which the xored stuff will be clipped.
02092                 Spread - The spread in which the drag is occurring
02093                 Transform - A transformation matrix indicating how the blobs should
02094                 be rendered - this allows use of this one routine for translation, scaling,
02095                 and rotation operations.
02096 
02097     Purpose:    Shared code used by the rotate, select, and scale tools to XOR the object(s)
02098                 being dragged onto screen. This function is used to remove XORed object outline
02099                 blobs from the screen (obviously they will only be truly removed if you call it
02100                 with the screen in the correct state). After the outlines have been rendered,
02101                 the XOROutline renderer state is reset, so it is ready to begin a fresh pass 
02102                 of rendering blobs back on to the screen.
02103 
02104     Notes:      See the selector and rotate tools for example code using this system.
02105 
02106     SeeAlso:    SelRange::RenderXOROutlinesOn; SelRange::RenderXOROutlinesToCatcvhUp
02107 
02108 ********************************************************************************************/
02109 
02110 void Range::RenderXOROutlinesOff(DocRect *ClipRect, Spread *pSpread, Matrix *Transform)
02111 {
02112 #if !defined(EXCLUDE_FROM_RALPH)
02113     // If called in the correct state, the following will un-XOR the existing drag blobs
02114     // and then reset the rendering state (as we should now have a 'blank' screen, so 
02115     // there is no way we should try to un-XOR this stuff again)
02116     RenderXOROutlinesToCatchUp(ClipRect, pSpread, Transform);
02117     ResetXOROutlineRenderer(FALSE);     // Reset, but don't discard cached info
02118 #endif
02119 }
02120 
02121 
02122 /********************************************************************************************
02123 
02124 >   void Range::RenderXOROutlinesOn(DocRect* ClipRect, Spread *Spread,
02125                                         Matrix *Transform, Node *ClickedObject);
02126 
02127     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> (moved here by Jason) (recoded by Jason)
02128     Created:    27/10/93
02129 
02130     Inputs:     ClipRect - NULL to just clip to the spread/window bounds, or a pointer to
02131                 a clipping rectangle into which the xored stuff will be clipped.
02132                 Spread - The spread in which the drag is occurring
02133                 Transform - A transformation matrix indicating how the blobs should
02134                 be rendered - this allows use of this one routine for translation, scaling,
02135                 and rotation operations.
02136                 ClickedObject - NULL, or a pointer to the node object which needs to be redrawn
02137                 first (the prime object). This allows super-interactive dragging, by always
02138                 redrawing the object which the user clicked upon to start the drag first, so
02139                 a specific thing can be easily positioned without waiting hours for redraw.
02140                 Note that the 'ClickedObject' is cached during the first call, so it need only
02141                 be passed in the first time you call this function.
02142 
02143     Purpose:    Shared code used by the rotate, select, and scale tools to XOR the object(s)
02144                 being dragged on/off screen. This function will do background rendering to 
02145                 XOR outlines onto screen (Successive calls will render more of the selection
02146                 until it has all been rendered - it must therefore be called during mouse idle
02147                 events to complete redraws)
02148 
02149     Notes:      See the selector and rotate tools for example code using this system.
02150 
02151     SeeAlso:    SelRange::RenderXOROutlinesOff; SelRange::RenderXOROutlinesToCatcvhUp
02152 
02153 ********************************************************************************************/
02154 
02155 void Range::RenderXOROutlinesOn(DocRect *ClipRect, Spread *pSpread,
02156                                     Matrix *Transform, Node *ClickedObject)
02157 {
02158     // DMc
02159     // promote the clicked object to any parents which take care of their own eor drag
02160     // rendering
02161     if (ClickedObject)
02162     {
02163         Node * pLastParent = ClickedObject;
02164         Node * pParent = ClickedObject->FindParent();
02165         
02166         while (pParent)
02167         {
02168             if (pParent->IsAnObject())
02169             {
02170                 if (((NodeRenderableInk *)pParent)->ChildrenAreEorDragRenderedByMe())
02171                 {
02172                     pLastParent = pParent;
02173                 }
02174             }
02175             
02176             pParent = pParent->FindParent();
02177         }
02178         
02179         ClickedObject = pLastParent;
02180     }
02181 
02182     BOOL bPromote = RangeControlFlags.PromoteToParent;
02183     RangeControlFlags.PromoteToParent = TRUE;
02184 
02185 #if !defined(EXCLUDE_FROM_RALPH)
02186     CacheXOROutlineInfo(pSpread, ClickedObject);
02187 
02188         // If we have drawn nothing so far, find the first object
02189     if (NumObjectsDrawn == 0)
02190         NodeToRender = NULL;
02191     else
02192     {
02193         if (RenderingPass == RENDER_END)
02194         {
02195             RangeControlFlags.PromoteToParent = bPromote;
02196             return; // No more to be drawn
02197         }
02198     
02199         if (NodeToRender == NULL)       // If starting fresh redraw, ensure counter zeroed
02200             NumObjectsDrawn = 0;
02201     }
02202 
02203 #if ENABLE_FAST_XOR
02204     // The following significantly speeds up XOR redrawing
02205     // The problem is that we want to multitask while drawing blobs on, so
02206     // we must draw each object into ALL render regions. The code below works,
02207     // but has to create a new render region for each invalid region for each
02208     // node, so is *very* slow.
02209     // However, in the case where there is only one render region (often the case
02210     // when dragging in a topped window, and apparently also the case under Windows,
02211     // which gives you an all-encompassing rectangle and does the clipping to the
02212     // arbitrary invalid region itself), we can just select that region, draw
02213     // all the objects we can, and then deselect the region. Much faster
02214 
02215     // First, count the number of render regions with a dummy rendering loop...
02216     INT32 RegionCount = 0;
02217     RenderRegion* pRegion = DocView::RenderOnTop( ClipRect, pSpread, ClippedEOR );
02218     while ( pRegion )
02219     {
02220         RegionCount++;
02221         pRegion = DocView::GetNextOnTop( ClipRect );
02222     }
02223 
02224     if (RegionCount == 1)       // There is ony one render region, so we can draw much faster
02225     {
02226         MonotonicTime RenderClock;
02227         RenderRegion* pRegion = DocView::RenderOnTop( ClipRect, pSpread, ClippedEOR );
02228 
02229         // Need to tell the render region to transform the objects a bit first.
02230         Matrix OldRenderMatrix = pRegion->ConcatenateMatrix( *Transform );
02231 
02232         // Draw the original bounding box in it's rotated/translated position, but only
02233         // on the first pass - we don't want the box flashing on and off!
02234         if (NumObjectsDrawn == 0)
02235         {
02236             DocRect Bounds = GetBoundingRectForEorDragging();
02237 
02238             DrawXORBoundingRect(pRegion, Bounds);
02239         }
02240         
02241         // Set the line colour and fill colour
02242         pRegion -> SetFillColour( COLOUR_TRANS );
02243         pRegion -> SetLineColour( COLOUR_XORDRAG );
02244 
02245         while (!RenderClock.Elapsed( 90 ))
02246         {
02247             FindNextXOROutlineNode(&NodeToRender, ClipRect);
02248 
02249             if (NodeToRender == NULL)
02250                 break;      // Nothing more to render - exit
02251 
02252             // Render the next node that needs rendering
02253             if (NodeToRender->IsRenderable() || ForceRenderEORAll()) ((NodeRenderableInk*)NodeToRender) -> RenderEorDrag( pRegion );
02254 
02255             // Keep the count of what we have drawn
02256             NumObjectsDrawn++;
02257         }
02258 
02259         // OK, we have finished with this render region, so set the matrix
02260         // back to how we found it and get the next one
02261         pRegion -> SetMatrix( OldRenderMatrix );
02262         pRegion = DocView::GetNextOnTop( ClipRect );
02263 
02264         ERROR3IF(pRegion != NULL, "FastXor drawing failed - There is another render region!");
02265 
02266         RangeControlFlags.PromoteToParent = bPromote;
02267         return;
02268     }
02269     
02270     //... else drop through to the normal slow rendering loop
02271 
02272 #endif
02273 
02274 
02275     // Start a clock to tell us how long we have been rendering
02276     MonotonicTime RenderClock;
02277     while ( !RenderClock.Elapsed( 90 ) )
02278     {
02279         FindNextXOROutlineNode(&NodeToRender, ClipRect);
02280         if (NodeToRender == NULL)
02281         {
02282             RangeControlFlags.PromoteToParent = bPromote;
02283             return;             // Nothing else to draw, may as well stop
02284         }
02285 
02286         // Start up a bit of a RenderOnTop loop type of thing to do all the rendering
02287         RenderRegion* pRegion = DocView::RenderOnTop( ClipRect, pSpread, ClippedEOR );
02288         while ( pRegion )
02289         {
02290             // Need to tell the render region to transform the objects a bit first.
02291             Matrix OldRenderMatrix = pRegion->ConcatenateMatrix( *Transform );
02292 
02293             // Draw the original bounding box in it's rotated/translated position, but only
02294             // on the first pass - we don't want the box flashing on and off!
02295             if (NumObjectsDrawn == 0)
02296             {
02297                 DocRect Bounds = GetBoundingRectForEorDragging();
02298                 DrawXORBoundingRect(pRegion, Bounds);
02299             }
02300             
02301             // Set the line colour and fill colour
02302             pRegion -> SetFillColour( COLOUR_TRANS );
02303             pRegion -> SetLineColour( COLOUR_XORDRAG );
02304 
02305             // Render the next node that needs rendering
02306             if (NodeToRender->IsRenderable() || ForceRenderEORAll()) ((NodeRenderableInk*)NodeToRender) -> RenderEorDrag( pRegion );
02307 
02308             // OK, we have finished with this render region, so set the matrix
02309             // back to how we found it and get the next one
02310             pRegion -> SetMatrix( OldRenderMatrix );
02311             pRegion = DocView::GetNextOnTop( ClipRect );
02312         }
02313         
02314         // Keep the count of what we have drawn
02315         NumObjectsDrawn++;
02316     }
02317 #endif
02318 
02319     RangeControlFlags.PromoteToParent = bPromote;
02320 
02321 }
02322 
02323 
02324 
02325 /********************************************************************************************
02326 
02327 >   virtual BOOL Range::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState = TRUE,
02328                                                         BOOL DoPreTriggerEdit = TRUE)
02329 
02330     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 19/01/2000
02331     Created:    6/02/95
02332     Inputs:     pParam               =  describes the way an op wants to change the node
02333                 SetOpPermissionState =  if TRUE the Op permission state of this node (and its parents) will be set 
02334                                         according to the outcome of the call
02335                 DoPreTriggerEdit     =  if TRUE then calls NameGallery::PreTriggerEdit.
02336                                         *Must* be TRUE if the calling Op may make any nodes
02337                                         change their bounds, eg move, line width, cut.
02338                                         Use TRUE if unsure.
02339     Outputs:    -
02340     Returns:    TRUE if one or more of the nodes in the range will allow the op to happen, FALSE otherwise
02341     Purpose:    Calls AllowOp() on all nodes in the range.
02342                 If SetOpPermissionState is TRUE, the op permission state of all nodes in the range, plus all parents
02343                 of these nodes, will have their op permission state changed.
02344 
02345     SeeAlso:    Node::AllowOp(),Node::GetOpPermission(),Node::SetOpPermission()
02346 
02347 ********************************************************************************************/
02348 
02349 BOOL Range::AllowOp(ObjChangeParam* pParam, BOOL SetOpPermissionState, BOOL DoPreTriggerEdit)
02350 {
02351     Node* pNode = FindFirst();
02352     BOOL result = FALSE,allowed;
02353 
02354     // we call AllowOp on all Nodes in the range, but never ask for a PreTriggerEdit on
02355     // each one, as if it is necessary then we deal with them en masse next.
02356     while (pNode != NULL)
02357     {
02358         Node* pNextNode = FindNext(pNode);
02359         allowed = pNode->AllowOp(pParam, SetOpPermissionState, FALSE);
02360         result  = result || allowed;
02361         pNode = pNextNode;
02362     }
02363 
02364     // if we're ok so far and were asked to do a PreTriggerEdit, then
02365     // determine whether the Op may change the bounds of some nodes.
02366     // If it may, then call NameGallery::PreTriggerEdit.
02367     if (result && DoPreTriggerEdit)
02368     {
02369         // if the Op is non-NULL then query its MayChangeNodeBounds() method.
02370         UndoableOperation* pChangeOp = pParam->GetOpPointer();
02371         if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds() && NameGallery::Instance())
02372         {
02373             result = NameGallery::Instance()->PreTriggerEdit(pChangeOp, pParam, this);
02374         }
02375     }
02376 
02377     return result;
02378 }
02379 
02380 /********************************************************************************************
02381 
02382 >   BOOL Range::GetCompoundObjectSet(ObjectSet* pCompoundObjectSet, 
02383                                      BOOL ExcludeTextObjects = FALSE); 
02384 
02385     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02386     Created:    5/6/95
02387     Inputs:     -
02388     Outputs:    pCompoundObjectSet: A set of all the parent compound nodes of the items in the
02389                                     range. This set can already contain items on entry to this
02390                                     function, and these items will not be deleted. 
02391 
02392                 ExcludeTextObjects: When TRUE TextStory compound objects will be excluded from 
02393                                     the set.
02394 
02395 
02396     Returns:    TRUE if successful
02397                 FALSE if we run out of memory. In this situation we do NOT delete any items
02398                 already added to the set. 
02399 
02400     Purpose:    This function traverses the range, and whenever it finds an object which is
02401                 the child of a compound node it adds the compound node to the set.
02402 
02403                 This is useful if you want to process the compound parents of the range 
02404                 after they have been hidden, eg. when we need to factor out 
02405                 attributes after a complex deletion.
02406 
02407     Errors:     -
02408     SeeAlso:    -
02409 
02410 ********************************************************************************************/
02411 
02412 BOOL Range::GetCompoundObjectSet(ObjectSet* pCompoundObjectSet, BOOL ExcludeTextObjects /* = FALSE*/) 
02413 {
02414     Node *pParent;
02415 
02416     // Scan the range
02417     Node* pCurrent = FindFirst();
02418     while (pCurrent)
02419     {
02420         pParent = pCurrent->FindParent();
02421         ERROR3IF(!pParent, "Object in range has no parent !"); 
02422         if (pParent && (pParent->IsCompound()))
02423         {
02424             if (!(ExcludeTextObjects && ((IS_A(pParent, TextLine)) || (IS_A(pParent, TextStory)))) )
02425             {
02426 
02427                 // A compound parent has been found, add it to the set
02428                 if (!(pCompoundObjectSet->AddToSet(((NodeRenderableInk*)pParent))))
02429                 {
02430                     return FALSE;
02431                 }
02432             }
02433         }
02434 
02435         pCurrent = pCurrent->FindNext(); 
02436     }
02437     return TRUE;
02438 }
02439 
02440 
02441  /********************************************************************************************
02442 
02443 >   BOOL Range::FindCommonAttributes(CommonAttrSet* CommonAttributeSet, 
02444                                      BOOL CompoundAccuracy = FALSE )
02445 
02446     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02447     Created:    3/8/94
02448 
02449     Inputs:     CommonAttributeSet:An OPTIONAL subset of attribute types to find common attributes 
02450                 for. Use the CommonAttrSet::AddTypeToSet fn to build up a subset of attribute types. 
02451                 If this set is empty on entry then the function will find common 
02452                 attributes for ALL attribute types.
02453 
02454                 Handy hint: If you repeatedly need to find common attribute values for a specific
02455                 set of attribute types eg. for a tool's InfoBar,  then you only need to create 
02456                 the CommmonAttributeSet once (during initialisation). 
02457 
02458                 CompoundAccuracy: TRUE => Accurate results with compound objects. This requires
02459                                   more work. This is important in some cases eg. PasteAttributes.                
02460 
02461     Outputs:    The CommonAttributeSet containing a CommonAttributeItem for each attribute Type 
02462                 (or a subset of attribute types - see above). These define the status of each 
02463                 attribute type (see below). Use CommonAttrSet::FindAttrItem to locate a particular 
02464                 attribute type in this set; the order of the items in the set will have changed.
02465 
02466     Returns:    TRUE if successful, FALSE otherwise (out of memory) 
02467     Purpose:    Finds all attributes which are common to the objects in the selection. There is a
02468                 CommonAttributeItem for every attribute type (common or not). The Status field
02469                 in this structure specifies if the attribute is common. 
02470 
02471                 Status field values
02472 
02473                 ATTR_COMMON:There is a common attribute 
02474                             pAttr will point to an instance of it 
02475 
02476                 ATTR_NONE:  There is no selection, or none of the selected objects require the 
02477                             attribute. 
02478                             If there is a document
02479                                 pAttr will point to the current attribute for the 
02480                                 selected tool, this will be the value which is usually 
02481                                 displayed in this situation.
02482                             else
02483                                 pAttr will be NULL
02484                                 No value will usually be displayed in this situation
02485                          
02486                 ATTR_MANY:  There is no common attribute. In this situation 'Many' will usually
02487                             be displayed in this situation.
02488 
02489 
02490     Errors:     -
02491     Scope:      private
02492     SeeAlso:    Range::FindCommonAttribute
02493     SeeAlso:    InternalClipboard::GetCommonAttributes
02494     SeeAlso:    CommonAttrSet
02495     SeeAlso:    CommonAttrSet::FindAttrItem
02496 
02497 ********************************************************************************************/
02498 
02499 BOOL Range::FindCommonAttributes(CommonAttrSet* CommonAttributeSet, 
02500                                  BOOL CompoundAccuracy /* = FALSE */)
02501 {
02502 #if !defined(EXCLUDE_FROM_RALPH)
02503 
02504     ERROR3IF(!CommonAttributeSet, "Common Attribute Set is NULL"); 
02505     
02506     BOOL DelCommonAttrSet = FALSE; // If we run out of memory whilst processing then 
02507                                    // we want to leave the CommonAttributeSet with 
02508                                    // the same number of items it entered with.
02509               
02510     // Has the user specified a subset of attributes ?
02511     if (CommonAttributeSet->IsEmpty())
02512     {
02513         // Place all attributes in the CommonAttributeSet
02514         if (!(CommonAttributeSet->AddAllTypesToSet()))
02515         {
02516             return FALSE; // ERROR already called 
02517         }
02518         DelCommonAttrSet = TRUE; // Delete all items in set if we fail
02519     }
02520 
02521     // Initialise all CommonAttributeItems. Remember this function has been written so that 
02522     // the CommonAttributeSet can be reused. So the Status and pAttr fields may contain
02523     // garbage on entry to the function.
02524     CommonAttributeSet->ClearResults();
02525     
02526     INT32 NumValuesToFind; // The number of attribute types we need to find values for.
02527                          // This value will decrease as more values are found.
02528     INT32 NumNewValues;
02529      
02530     // One, Many, or all common attribute values may already be cached
02531     CommonAttrSet* pCommonAttrCache = GetCommonAttrCache(); 
02532 
02533     if (pCommonAttrCache)
02534     {
02535         // We only need to consider the first NumValuesToFind attributes in the list
02536         NumValuesToFind = CommonAttributeSet->UpdateKnownValues(pCommonAttrCache);
02537     }
02538     else
02539     {
02540         // We need to find all values
02541         NumValuesToFind = CommonAttributeSet->GetCount();
02542     }
02543 
02544     NumNewValues = NumValuesToFind;
02545 
02546     if (!NumValuesToFind)
02547         return TRUE; 
02548 
02549     // This is a recent optimisation. We create a map of Attribute types to applied attrs. This 
02550     // allows us to scan up the tree looking for attributes applied to each object once only.
02551     CCAttrMap* pAttrMap = new CCAttrMap;  // Create it dynamically as it could be quite big
02552     if (!pAttrMap)
02553     {
02554         if (DelCommonAttrSet) CommonAttributeSet->DeleteAll(); 
02555         return FALSE; // out of memory
02556     }
02557 
02558     // A special type of attribute map which is used for Compound objects only. It only gets 
02559     // built if and when we need it. When we find a default attribute applied to a compound
02560     // object, we only know that this attribute is a common attribute if at least one object
02561     // in the compound requires the attribute AND the attribute does not make an appearance 
02562     // anywhere as a child of the compound. The attrmap maps an attribute type which is known
02563     // to be a default onto an ObjectAttrUsage value.
02564     BOOL CompoundAttrMapBuilt;  
02565     CMapPtrToWord* pCompoundAttrMap = new CMapPtrToWord;
02566     if (!pCompoundAttrMap)
02567     {
02568         delete pAttrMap;
02569         if (DelCommonAttrSet) CommonAttributeSet->DeleteAll(); 
02570         return FALSE;   // out of memory 
02571     }
02572     
02573     // Scan all objects in the range,
02574     Node* pNode = NULL;
02575     NodeRenderableInk* CurrentObject;
02576 
02577     pNode = FindFirst(); 
02578     // Stop when we have found all attribute values (all ATTR_MANY), or there are no more objects
02579     // to process.
02580     while ((pNode != NULL) && (NumValuesToFind))
02581     {
02582         if (pNode->IsAnObject())
02583         {
02584             CurrentObject = (NodeRenderableInk*)pNode;
02585 
02586             pAttrMap->RemoveAll();          // Remove any items that may have been in the map
02587             pCompoundAttrMap->clear();  // Ditto
02588             CompoundAttrMapBuilt = FALSE;   // Cos we only build this when it becomes required
02589 
02590             // Find all attributes applied to CurrentObject, let's worry about compounds a bit later 
02591             CurrentObject->FindAppliedAttributes(pAttrMap); 
02592 
02593             // Scan attributes in the CommonAttributeSet
02594             CommonAttributeItem* CommonAttrItem;
02595             CommonAttributeItem* NxtCommonAttrItem;
02596 
02597             INT32 NumValuesToProcess = NumValuesToFind;
02598             
02599             CommonAttrItem = (CommonAttributeItem*)CommonAttributeSet->GetHead();
02600             while (NumValuesToProcess)
02601             {
02602                 ERROR3IF(CommonAttrItem==NULL || CommonAttrItem->CheckMemory(FALSE)==FALSE, "CommonAttrItem should not be NULL"); 
02603                 // Hand over hand, as the item will be moved to the tail of the list if 
02604                 // it's value becomes known
02605                 NxtCommonAttrItem = (CommonAttributeItem*)CommonAttributeSet->GetNext(CommonAttrItem); 
02606                 if (CurrentObject->RequiresAttrib(CommonAttrItem->AttrType, CompoundAccuracy)) 
02607                 {
02608                     // Find the attribute which is applied to the CurrentObject
02609                     void* vpAttr=NULL;
02610                     NodeAttribute* pAttrNode;
02611                     pAttrMap->Lookup(CommonAttrItem->AttrType, vpAttr);
02612                     pAttrNode = (NodeAttribute*)vpAttr;
02613                     ERROR3IF(pAttrNode == NULL, "Could not find attribute of type AttrType"); 
02614                     if (pAttrNode != NULL)  // For safety
02615                     {
02616                         BOOL AttrCommonInCompound = TRUE;
02617                         BOOL AttrReqd = TRUE;
02618                         // Special Compound object processing
02619                         if (CurrentObject->IsCompound())
02620                         {
02621                             // In this situation pAttrNode will be a common attribute if it is 
02622                             // not a default attribute. i.e. if it's parent is not a NodeDocument
02623                             if (pAttrNode->IsADefaultAttr())
02624                             {
02625                                 // ok we are messing with a default attribute here.
02626                                 if (!CompoundAttrMapBuilt)
02627                                 {
02628                                     // If we haven't already got ourselves a CompoundAttrMap
02629                                     // construct one, adding all attribute types that are
02630                                     // known to be defaults.
02631                                     if (!CommonAttributeSet->BuildAttrDetailsMap(CurrentObject, 
02632                                                                             NumValuesToFind,
02633                                                                             pAttrMap, 
02634                                                                             pCompoundAttrMap))
02635                                     {
02636                                         if (DelCommonAttrSet) CommonAttributeSet->DeleteAll();
02637                                         delete pAttrMap; delete pCompoundAttrMap; 
02638                                         return FALSE; 
02639                                     }
02640                                     CompoundAttrMapBuilt = TRUE;
02641                                 }
02642 //                              WORD wCompoundAttrUsage;
02643 //                              pCompoundAttrMap->Lookup(CommonAttrItem->AttrType, wCompoundAttrUsage);
02644                                 CMapPtrToWord::iterator iter = pCompoundAttrMap->find(CommonAttrItem->AttrType);
02645                                 if (pCompoundAttrMap->end() != iter)
02646                                 {
02647                                     CommonAttrSet::ObjectAttrUsage CompoundAttrUsage = 
02648                                         (CommonAttrSet::ObjectAttrUsage)iter->second;
02649                                     // if the attribute type has been found as a child of the compound then we know
02650                                     // it's not a common attribute of the compound, and so the Selection.
02651                                     if (CompoundAttrUsage == CommonAttrSet::FOUND)
02652                                         AttrCommonInCompound = FALSE;
02653                                     else if (CompoundAttrUsage == CommonAttrSet::NOT_REQUIRED)
02654                                         AttrReqd = FALSE;
02655                                     // else CompoundAttrUsage == REQUIRED (attr is common so continue)
02656                                 }
02657                             }
02658                         }
02659                         if (AttrReqd)  
02660                         {
02661                             // Ok it has an attribute of this type
02662                             if (CommonAttrItem->pAttr == NULL)
02663                             {
02664                                 // The attribute becomes a common attribute
02665                                 CommonAttrItem->Status = Range::ATTR_COMMON;  // up to this point anyway
02666                                 CommonAttrItem->pAttr = pAttrNode;
02667                             }                       
02668                             // not an else (need to catch Compound case
02669                             if (!AttrCommonInCompound || (!((*pAttrNode)==(*(CommonAttrItem->pAttr)))) )
02670                             {
02671                                 // Attributes have different values. 
02672                                 CommonAttrItem->Status = Range::ATTR_MANY;
02673                                 CommonAttrItem->pAttr = NULL;
02674                                 // This item's value has been found it will not cahnge so move it to the
02675                                 // end of the list. 
02676                                 CommonAttributeSet->RemoveItem(CommonAttrItem); 
02677                                 CommonAttributeSet->AddTail(CommonAttrItem);
02678                                 NumValuesToFind--; // One less to find for the next object
02679                             }
02680                         }
02681                     } // if
02682                 } // if
02683                 NumValuesToProcess--;
02684                 CommonAttrItem = NxtCommonAttrItem;
02685             } // Attribute scan
02686         }
02687         // Get the next object
02688         pNode = FindNext(pNode);
02689     } // Range scan
02690 
02691 
02692     // Post processing: For every new value found 
02693     //      Each ATTR_NONE item should point at the current attribute associated with the selected tool
02694     //      The new value should be added to the cache
02695     Document* SelectedDoc = Document::GetSelected();
02696 
02697     CommonAttributeItem* CommonAttrItem;
02698 
02699     AttributeManager& pAttrMgr = SelectedDoc->GetAttributeMgr();
02700     INT32 Count;
02701     for (CommonAttrItem = (CommonAttributeItem*)CommonAttributeSet->GetHead(), Count = 0;
02702          Count != NumNewValues;
02703          CommonAttrItem = (CommonAttributeItem*)CommonAttributeSet->GetNext(CommonAttrItem), Count++)
02704     {
02705         ERROR3IF(!CommonAttrItem, "CommonAttrItem should not be NULL"); 
02706         if (SelectedDoc && CommonAttrItem->Status == Range::ATTR_NONE)
02707         {
02708             CommonAttrItem->pAttr = pAttrMgr.GetSelToolCurrentAttribute(CommonAttrItem->AttrType);
02709         }
02710         // Add the item to the cache.
02711         if (pCommonAttrCache)
02712         {
02713             CommonAttributeItem* pNewAttrItem = new CommonAttributeItem(CommonAttrItem->AttrType);
02714             if (pNewAttrItem)
02715             {
02716                 // Add the attribute type results to the cache, taking a copy of the attribute
02717                 if (!(pNewAttrItem->InitSafeAttrItem(CommonAttrItem->AttrType, 
02718                                                      CommonAttrItem->pAttr, 
02719                                                      CommonAttrItem->Status)))
02720                 {
02721                     delete pNewAttrItem;
02722                 }
02723                 else
02724                 {
02725                     pCommonAttrCache->AddTail(pNewAttrItem); // Add to the cache    
02726                 }
02727             } // else we failed to allocate memory for the item, we can live with this.
02728         }
02729     }
02730 
02731     // else pAttr will be NULL for all items
02732 
02733     delete pAttrMap;
02734     delete pCompoundAttrMap; 
02735 
02736     return TRUE; // Job done
02737 #else
02738     return FALSE;
02739 #endif
02740 }
02741 
02742 
02743 
02744 /********************************************************************************************
02745 
02746 >   CommonAttribResult Range::FindCommonAttribute(RuntimeClass* AttribType, 
02747                                                      NodeAttribute** pAttribute,
02748                                                      BOOL bRangeIsEffectsLevel = FALSE)
02749 
02750     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02751     Created:    29/6/94
02752     
02753     Inputs:     AttribType: The Type of attribute to find - defined in the virtual 
02754                             GetAttributeType method of NodeAttribute
02755 
02756     Outputs:    pAttribute: Depends on the return value, see below
02757                                              
02758 
02759     Returns:    ATTR_COMMON:There is a common attribute 
02760                             pAttribute will point to an instance of it 
02761 
02762                 ATTR_NONE:  There is no selection, or none of the selected objects require the 
02763                             attribute. 
02764                             If there is a document
02765                                 pAttribute will point to the current attribute for the 
02766                                 selected tool, this will be the value which is usually 
02767                                 displayed in this situation.
02768                             else
02769                                 pAttribute will be NULL
02770                                 No value will usually be displayed in this situation
02771                          
02772                 ATTR_MANY:  There is no common attribute, 
02773                             pAttribute will be NULL
02774                          
02775         
02776     Purpose:    To determine if all objects in the range share an attribute with the 
02777                 same value. You should only use this function if you need to find a 
02778                 common attribute value for a single attribute type. If you need to find
02779                 common values for many types then FindCommonAttributes is much more efficient.
02780 
02781     Errors:     -
02782     Scope:      private
02783     SeeAlso:    Range::FindCommonAttributes
02784 
02785 ********************************************************************************************/
02786 
02787 Range::CommonAttribResult Range::FindCommonAttribute(CCRuntimeClass* AttribType, 
02788                                                      NodeAttribute** pAttribute,
02789                                                      BOOL bRangeIsEffectsLevel)
02790 {
02791 #if !defined(EXCLUDE_FROM_RALPH)
02792     CommonAttribResult Result;
02793     *pAttribute = NULL;
02794 
02795     // Before we go away and do any hard work, let's see if the value that the user
02796     // is after is already cached.
02797     CommonAttrSet* pCommonAttrCache = GetCommonAttrCache(); 
02798     if (pCommonAttrCache)
02799     {
02800         // are the AttrTypes common details cached 
02801         CommonAttributeItem* pAttrItem = pCommonAttrCache->FindAttrItem(AttribType);
02802         if (pAttrItem)
02803         {
02804             // Aren't we lucky
02805             *pAttribute = pAttrItem->pAttr;
02806             return (pAttrItem->Status);
02807         }   
02808     }
02809     // else the cache could not be created. We shall just have to work without one 
02810 
02811     // Scan each selected node until we get a result
02812     NodeRenderableInk* n = NULL;
02813     NodeRenderableInk* pn = (NodeRenderableInk*)FindFirst(); 
02814     
02815     NodeAttribute* AppliedAttr; // The attribute applied and used by n.
02816 
02817     while (pn != NULL)
02818     {
02819         n = pn;
02820         if (bRangeIsEffectsLevel)
02821             n = pn->GetObjectToApplyTo(AttribType);
02822 
02823         AppliedAttr = NULL;
02824         if (n->IsCompound())
02825         {
02826             // Determine if all objects within the  compound object share a common attribute
02827             
02828             ERROR3IF(!n->IsAnObject(), "Item in range is not a NodeRenderableInk"); 
02829 
02830             // We may sometime later decide that this attribute is not applied to the 
02831             // compound.
02832             AppliedAttr = ((NodeRenderableInk*)n)->FindAppliedAttribute(AttribType);
02833 
02834             if (AppliedAttr->IsADefaultAttr() && !((NodeRenderableInk*)n)->IsValidEffectAttr(AppliedAttr))   // else AppliedAttr will be correct
02835             {
02836                 // This can mean one of three things
02837                 // a. All nodes in the compound which require the attribute share the default 
02838                 //    attribute
02839                 // b. Nodes requiring attribute have more than one attribute value. i.e. 
02840                 //    the default attribute is not a common attribute.
02841                 // c. No nodes in the group require the attribute
02842 
02843                 // Flag which is set to TRUE when we know that AttribType is required by the 
02844                 // the compound
02845                 BOOL AttribRequiredInCompound = FALSE; 
02846 
02847                 // Scan the compound depth first
02848                 Node* Current = n->FindFirstDepthFirst();
02849                                     
02850                 while(Current != n)  // Stop when we hit the compound (used to be NULL, this was a bug)
02851                 {
02852                     if (!AttribRequiredInCompound)
02853                     {
02854                         // Determine if the attribute is required by the current node
02855                         if (Current->IsAnObject())
02856                         {
02857                             AttribRequiredInCompound = ((NodeRenderableInk*)Current)
02858                                 ->RequiresAttrib(AttribType);   
02859                         }
02860                     }
02861                     if (Current->IsAnAttribute())
02862                     {
02863                         if (((NodeAttribute*)Current)->GetAttributeType() == AttribType)
02864                         {
02865                             // This means that the compound must have more than one
02866                             // attribute value, because if this attribute was common to
02867                             // all objects requiring the attribute in the compound then
02868                             // it would have been found in the child attribute block !
02869                             (*pAttribute) = NULL; // Nothing useful to return here
02870                             Result = ATTR_MANY;
02871                             goto End;        
02872                         }
02873                     }
02874                     Current = Current->FindNextDepthFirst(n);       
02875                 }
02876                 if (!AttribRequiredInCompound)
02877                 {
02878                     AppliedAttr = NULL; // Compound has no applied attribute !
02879                 }
02880                 // else attribute not required in compound so ignore it. 
02881             }
02882         }
02883         else
02884         {
02885             // Does n require attribute AttribType to render itself ?
02886             if (n->RequiresAttrib(AttribType))
02887             {
02888                 // n needs to have attribute AttribType defined so that it can render properly
02889                 // scan from the last child of n, up the tree in reverse render order until we 
02890                 // find an instance of an AttribType attribute.
02891                 AppliedAttr = n->FindAppliedAttribute(AttribType);
02892                 
02893             }
02894             // else n does not require the attribute so it is ignored
02895         }
02896         
02897         // Determine if the attribute is common
02898 
02899         if (AppliedAttr != NULL)
02900         {
02901 
02902             if ((*pAttribute) == NULL) // This is the first instance of the attribute found
02903             {
02904                 (*pAttribute) = AppliedAttr;    
02905             }
02906             else
02907             {
02908                 // If the attribute found is not the same as all others found then 
02909                 // we know that there is no common attribute, so there is no point in
02910                 // searching any further
02911             
02912                 if (!((**pAttribute) == (*AppliedAttr)))
02913                 {
02914                     (*pAttribute) = NULL; // Nothing useful to return here
02915                     Result = ATTR_MANY;
02916                     goto End;
02917                 }
02918                 // else the Applied attribute is the same as all others found so carry
02919                 // on searching.
02920             }
02921         }
02922          
02923         pn = (NodeRenderableInk*)FindNext(pn); // Obtain the next selected object
02924     }
02925 
02926     // Ok weve finished the scan, so either no objects require the attribute, or there
02927     // is a common attribute value shared by all objects that do require the attribute
02928     if ((*pAttribute) != NULL)
02929     {
02930         Result = ATTR_COMMON;
02931         // pAttribute will point to an instance of the common attribute
02932     }
02933     else
02934     {
02935         Result = ATTR_NONE;
02936 
02937         Document* SelectedDoc = Document::GetSelected();
02938     
02939         // If there is a selected document then pAttribute is set to the current attribute
02940         // for the selected tool.
02941         if (SelectedDoc != NULL)
02942         {
02943             (*pAttribute) = SelectedDoc->GetAttributeMgr().GetSelToolCurrentAttribute(AttribType);
02944         }
02945         // else pAttribute will be NULL 
02946     }
02947 
02948     End:
02949 
02950     // Add the result to the Common attribute cache
02951     if (pCommonAttrCache)
02952     {
02953         CommonAttributeItem* pNewAttrItem = new CommonAttributeItem(AttribType);
02954         if (pNewAttrItem)
02955         {
02956             // Add the attribute type results to the cache, taking a copy of the attribute
02957             if (!(pNewAttrItem->InitSafeAttrItem(AttribType, *pAttribute, Result)))
02958                 delete pNewAttrItem;
02959             else
02960                 pCommonAttrCache->AddTail(pNewAttrItem); // Add to the cache    
02961         } // else we failed to allocate memory for the item, we can live with this.
02962     }
02963     // If we have a Common
02964     return Result;
02965 #else
02966     return ATTR_NONE;
02967 #endif
02968 }
02969 
02970 
02971 
02972 
02973 /********************************************************************************************
02974 
02975 >   Range::CommonAttribResult Range::FindCommonAttributeType(CCRuntimeClass* AttribType, 
02976                                                                CCRuntimeClass** pCommonType,
02977                                                                BOOL ForceAttribType,
02978                                                                BOOL bRangeIsEffectsLevel = FALSE)
02979 
02980     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02981     Created:    15/8/94
02982     
02983     Inputs:     AttribType: The Type of attribute to find - defined in the virtual 
02984                             GetAttributeType method of NodeAttribute
02985 
02986     Outputs:    pCommonType: Depends on the return value, see below
02987 
02988     Returns:    ATTR_COMMON:There is a common attribute type
02989                             pCommonType will point to its runtime class 
02990 
02991                 ATTR_NONE:  There is no selection, or none of the selected objects require the 
02992                             attribute. 
02993                             If there is a document
02994                                 pCommonType will point to the type of thecurrent attribute 
02995                                 for the selected tool, this will be the value which is usually 
02996                                 displayed in this situation.
02997                             else
02998                                 pCommonType will be NULL
02999                                 No value will usually be displayed in this situation
03000                          
03001                 ATTR_MANY:  There is no common attribute type, 
03002                             pCommonType will be NULL
03003                          
03004         
03005     Purpose:    To determine if all objects in the selection share attributes with the 
03006                 same type. 
03007 
03008     Errors:     -
03009     Scope:      private
03010     SeeAlso:    -
03011 
03012 ********************************************************************************************/
03013 
03014 Range::CommonAttribResult Range::FindCommonAttributeType(CCRuntimeClass* AttribType, 
03015                                                          CCRuntimeClass** pCommonType,
03016                                                          BOOL ForceAttribType,
03017                                                          BOOL bRangeIsEffectsLevel)
03018 {
03019 #if !defined(EXCLUDE_FROM_RALPH)
03020     CommonAttribResult Result;
03021     NodeAttribute *pAttribute = NULL;
03022     *pCommonType = NULL;
03023 
03024     // Scan each selected node until we get a result
03025     NodeRenderableInk* n = NULL;
03026     NodeRenderableInk* pn = (NodeRenderableInk*)FindFirst(); 
03027     
03028     NodeAttribute* AppliedAttr; // The attribute applied to n
03029 
03030     while (pn != NULL)
03031     {
03032         n = pn;
03033         if (bRangeIsEffectsLevel)
03034             n = pn->GetObjectToApplyTo(AttribType);
03035 
03036         AppliedAttr = NULL;
03037         if (n->IsCompound())        // TODO: Don't do this clause if n would allow effect attrs of the given type???
03038         {
03039             // Determine if all objects within the  compound object share a common attribute
03040             
03041             // (Check the child attribute block)
03042             // If we find the attribute defined as a left child of the compound object (before 
03043             // other, non attribute nodes) then we can be sure that this attribute is 
03044             // shared by all objects requiring the attribute within the compound.
03045 
03046             ERROR3IF(!n->IsAnObject(), "Item in range is not a NodeRenderableInk"); 
03047 
03048             AppliedAttr = ((NodeRenderableInk*)n)->GetChildAttrOfType(AttribType);
03049             
03050             if (!AppliedAttr)
03051             {
03052                 // This can mean one of three things
03053                 // a. All nodes in the compound which require the attribute share the default 
03054                 //    attribute
03055                 // b. Nodes requiring attribute have more than one attribute value
03056                 // c. No nodes in the group require the attribute
03057 
03058                 // Flag which is set to TRUE when we know that AttribType is required by the 
03059                 // the compound
03060                 BOOL AttribRequiredInCompound = FALSE; 
03061 
03062                 // Scan the compound depth first
03063                 Node* Current = n->FindFirstDepthFirst();
03064                 
03065                 while (Current != NULL)
03066                 {
03067                     if (!AttribRequiredInCompound)
03068                     {
03069                         // Determine if the attribute is required by the current node
03070                         if (Current->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)))
03071                         {
03072                             AttribRequiredInCompound = ((NodeRenderableInk*)Current)
03073                                 ->RequiresAttrib(AttribType);   
03074                         }
03075                     }
03076                     if (Current->IsAnAttribute())
03077                     {
03078                         if (((NodeAttribute*)Current)->GetAttributeType() == AttribType)
03079                         {
03080                             // This means that the compound must have more than one
03081                             // attribute value, because if this attribute was common to
03082                             // all objects requiring the attribute in the compound then
03083                             // it would have been found in the child attribute block !
03084                             
03085                             if (!ForceAttribType)
03086                             {
03087                                 (*pCommonType) = NULL; // Nothing useful to return here
03088                                 return ATTR_MANY;         //                        *RETURN
03089                             }
03090                             else        // we want to try and force a common attribute ....
03091                                         // e.g.  if we have two shapes with a different linear
03092                                         // fill each and then group these, it is desirable to
03093                                         // still show these to the user as linear, and NOT change
03094                                         // (by default) to showing many ....
03095                             {
03096                                 AppliedAttr = (NodeAttribute*)Current;
03097                                 
03098                                 if ((pAttribute) == NULL) // This is the first instance of the attribute found
03099                                 {
03100                                     (pAttribute) = AppliedAttr; 
03101                                 }
03102                                 else
03103                                 {
03104                                     // If the attribute found is not the same as all others found then 
03105                                     // we know that there is no common attribute, so there is no point in
03106                                     // searching any further
03107             
03108                                     if (! ((pAttribute->GetRuntimeClass()) == (AppliedAttr->GetRuntimeClass())) )
03109                                     {
03110                                         (*pCommonType) = NULL; // Nothing useful to return here
03111                                         return ATTR_MANY;         //                                    *RETURN
03112                                     }
03113     
03114                                     // Complicated by this special case.
03115                                     // If it's a Radial Fill Geometry, then we need to see if it's
03116                                     // circular or elliptical
03117 
03118                                     if ( pAttribute->IsKindOf(CC_RUNTIME_CLASS(AttrRadialFill)) )
03119                                     {
03120                                         if (! (((AttrRadialFill*)pAttribute)->IsElliptical()) == 
03121                                             (((AttrRadialFill*)AppliedAttr)->IsElliptical()) )
03122                                         {
03123                                             (*pCommonType) = NULL; // Nothing useful to return here
03124                                             return ATTR_MANY;      //                                   *RETURN
03125                                         }
03126                                     }
03127 
03128                                     // else the Applied attribute is the same as all others found so carry
03129                                     // on searching.
03130                                 }
03131                             }
03132                         }
03133                     }
03134                     Current = Current->FindNextDepthFirst(n);       
03135                 }
03136                 if ((AttribRequiredInCompound) && (!ForceAttribType))
03137                 {
03138                     // The group uses an attribute further up the tree
03139                     // scan left then up the tree in reverse render order until we 
03140                     // find an instance of an AttribClass attribute.
03141                     AppliedAttr = n->FindAppliedAttribute(AttribType, TRUE);    // bExcludeChildAttrs
03142                 }
03143                 // else attribute not required in compound so ignore it. 
03144             }
03145         }
03146         else
03147         {
03148             // Does n require attribute AttribType to render itself ?
03149             if (n->RequiresAttrib(AttribType))
03150             {
03151                 // n needs to have attribute AttribType defined so that it can render properly
03152                 // scan from the last child of n, up the tree in reverse render order until we 
03153                 // find an instance of an AttribType attribute.
03154                 AppliedAttr = n->FindAppliedAttribute(AttribType);
03155                 
03156             }
03157             // else n does not require the attribute so it is ignored
03158         }
03159         
03160         // Determine if the attribute is common
03161 
03162         if (AppliedAttr != NULL)
03163         {
03164 
03165             if ((pAttribute) == NULL) // This is the first instance of the attribute found
03166             {
03167                 (pAttribute) = AppliedAttr; 
03168             }
03169             else
03170             {
03171                 // If the attribute found is not the same as all others found then 
03172                 // we know that there is no common attribute, so there is no point in
03173                 // searching any further
03174             
03175                 if (! ((pAttribute->GetRuntimeClass()) == (AppliedAttr->GetRuntimeClass())) )
03176                 {
03177                     (*pCommonType) = NULL; // Nothing useful to return here
03178                     return ATTR_MANY;         //                                    *RETURN
03179                 }
03180     
03181                 // Complicated by this special case.
03182                 // If it's a Radial Fill Geometry, then we need to see if it's
03183                 // circular or elliptical
03184 
03185                 if ( pAttribute->IsKindOf(CC_RUNTIME_CLASS(AttrRadialFill)) )
03186                 {
03187                     if (! (((AttrRadialFill*)pAttribute)->IsElliptical()) == 
03188                             (((AttrRadialFill*)AppliedAttr)->IsElliptical()) )
03189                     {
03190                         (*pCommonType) = NULL; // Nothing useful to return here
03191                         return ATTR_MANY;      //                                   *RETURN
03192                     }
03193                 }
03194 
03195                 // else the Applied attribute is the same as all others found so carry
03196                 // on searching.
03197             }
03198         }
03199          
03200         pn = (NodeRenderableInk*)FindNext(pn); // Obtain the next selected object
03201     }
03202 
03203     // Ok weve finished the scan, so either no objects require the attribute, or there
03204     // is a common attribute value shared by all objects that do require the attribute
03205     if ((pAttribute) != NULL)
03206     {
03207         *pCommonType = pAttribute->GetRuntimeClass();
03208 
03209         // If it's one of these silly circle things then
03210         // we'll have to return this 'virtual' class type.
03211         if ( pAttribute->IsKindOf(CC_RUNTIME_CLASS(AttrRadialColourFill)) )
03212         {
03213             if ( ((AttrRadialColourFill*)pAttribute)->IsCircular() )
03214                 *pCommonType = CC_RUNTIME_CLASS(AttrCircularColourFill);
03215         }
03216         else if ( pAttribute->IsKindOf(CC_RUNTIME_CLASS(AttrRadialTranspFill)) )
03217         {
03218             if ( ((AttrRadialTranspFill*)pAttribute)->IsCircular() )
03219                 *pCommonType = CC_RUNTIME_CLASS(AttrCircularTranspFill);
03220         }
03221 
03222         Result = ATTR_COMMON;
03223         // pAttribute will point to an instance of the common attribute
03224     }
03225     else
03226     {
03227         Result = ATTR_NONE;
03228 
03229         Document* SelectedDoc = Document::GetSelected();
03230     
03231         // If there is a selected document then pAttribute is set to the current attribute
03232         // for the selected tool.
03233         if (SelectedDoc != NULL)
03234         {
03235             *pCommonType = (SelectedDoc->GetAttributeMgr().GetSelToolCurrentAttribute(AttribType))->GetRuntimeClass();
03236         }
03237         // else pAttribute will be NULL 
03238     }
03239     return Result;
03240 #else
03241     return ATTR_NONE;
03242 #endif
03243 }
03244 
03245 /********************************************************************************************
03246 
03247 >   CommonAttrSet* Range::GetCommonAttrCache()
03248 
03249     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03250     Created:    13/9/95
03251     Returns:    A pointer to the common attribute cache. NULL is returned if one could not
03252                 be created because we ran out of memory.
03253     Purpose:    This fn should be called to get a pointer to the range's common attribute cache.
03254                 If the cache does not exist in the range then one is first created.
03255     Scope:      private
03256 
03257 ********************************************************************************************/
03258 
03259 CommonAttrSet* Range::GetCommonAttrCache()
03260 {
03261     FreshenCache();            // Cache may not be valid
03262     if (!pCommonAttribCache)
03263     {
03264         // Let's try and create one
03265         pCommonAttribCache = new CommonAttrSet(); 
03266     }
03267     FreshenCache();            // Could delete the pCommonAttribCache if not cached
03268     return pCommonAttribCache; // Will be NULL if we ran out of memory
03269 } 
03270 
03271 
03272 
03273 /********************************************************************************************
03274 
03275 >   Range::CommonAttribResult Range::FindCommonNonOptimisingAttr(CCRuntimeClass* pAttribClass,
03276                                                                  NodeAttribute** ppAttr )
03277     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
03278     Created:    22/08/2000
03279     Inputs:     pAttribClass
03280                 ppAttr
03281 
03282     Outputs:    If returning ATTR_COMMON, then ppAttr is filled with a ptr to one of the
03283                 common attributes, so that its values may be read by whoever called us.
03284 
03285     Returns:    ATTR_MANY   if we find more than one attribute of the given class, and they
03286                             are different.
03287                 ATTR_COMMON if we find one or more attributes, and they are the same.
03288                 ATTR_NONE   if we find no attributes of the given class.
03289 
03290     Purpose:    Do a search on the range, for non-optimising attrs.
03291                 Because they are not optimised, we don't need to search up and down the tree
03292                 for these attributes - we just look at the children of each node in the range.
03293 
03294     Notes:      This method *does* search all children of a node, so that it will detect
03295                 multiple different attributes of the same type attached to one node.
03296 
03297     Errors:     ERROR2 with ATTR_NONE if either parameter is NULL.
03298     See also:   FindCommonAttribute().
03299 
03300 ********************************************************************************************/
03301 Range::CommonAttribResult Range::FindCommonNonOptimisingAttr(   CCRuntimeClass* pAttribClass,
03302                                                                 NodeAttribute** ppAttr )
03303 {
03304     ERROR2IF(ppAttr == NULL || pAttribClass == NULL, ATTR_NONE,
03305             "Range::FindCommonNonOptimisingAttr; NULL input parameter!");
03306 
03307     NodeAttribute* pAttr    = NULL;
03308     NodeAttribute* pFirstAttr   = NULL;
03309 
03310     Node* pNode = FindFirst();
03311     while (pNode != NULL)
03312     {
03313         if (pNode->IsAnObject())    // sanity check.
03314         {
03315             pAttr = (NodeAttribute*)pNode->FindFirstChild(pAttribClass);
03316             while (pAttr != NULL)
03317             {
03318                 // remember the first match we have, so we can check for differences.
03319                 if (pFirstAttr == NULL)
03320                     pFirstAttr = pAttr;
03321 
03322                 // if we have multiple matches, then check each one for differences.
03323                 else if (pAttr->IsDifferent(pFirstAttr))
03324                     return ATTR_MANY;
03325 
03326                 pAttr = (NodeAttribute*)pAttr->FindNext(pAttribClass);
03327             }
03328         }
03329 
03330         pNode = FindNext(pNode);
03331     }
03332 
03333     if (pFirstAttr != NULL)
03334     {
03335         *ppAttr = pFirstAttr;
03336         return ATTR_COMMON;
03337     }
03338     else
03339     {
03340         return ATTR_NONE;
03341     }
03342 }
03343 
03344 
03345 
03346 /********************************************************************************************
03347 
03348 >   BOOL Range::AddTextStoryCompoundsForDel(ObjectSet* pCompoundObjectSet)
03349 
03350     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03351     Created:    6/6/95
03352     Inputs:     -
03353     Outputs:    -
03354     Returns:    -
03355     Purpose:    This function is really a BODGE. When we are deleting a range which contains 
03356                 text chars, the deletion code may/may-not hide unselected TextLines as well. If 
03357                 this occurs then we will not be able to globally localise/factor out attributes 
03358                 on these TextLines. 
03359 
03360                 So what this function does is to add all parent TextStory compounds of 
03361                 any selected TextChars to the set, so that we can factor out on these. 
03362 
03363                 Really any deletion of this sort should occur after the operation ends
03364                 similar to group deletion in the no children case.But we have got to ship
03365                 this product some time.
03366     Errors:     -
03367     SeeAlso:    -
03368 
03369 ********************************************************************************************/
03370 
03371 BOOL Range::AddTextStoryCompoundsForDel(ObjectSet* pCompoundObjectSet)
03372 {
03373     Node* pCurrent = FindFirst();
03374     Node* pParent;
03375     while (pCurrent)
03376     {
03377         pParent = pCurrent->FindParent();
03378         ERROR3IF(!pParent, "object in range has no parent"); 
03379         if (pParent)
03380         { 
03381             if (IS_A(pParent, TextLine))
03382             {
03383                 pParent = pParent->FindParent(); 
03384                 ERROR3IF(!pParent, "parent of object in range has no parent"); 
03385                 ERROR3IF(!(IS_A(pParent, TextStory)), "Parent of TextLine is not a TextStory");
03386                 if (IS_A(pParent, TextStory))
03387                 {
03388                     // Add the story to the set
03389                     if (!(pCompoundObjectSet->AddToSet((NodeRenderableInk*)pParent)))
03390                         return FALSE;  
03391                 }
03392             }
03393         }
03394         pCurrent = pCurrent->FindNext(); 
03395     }
03396     return TRUE; 
03397 } 
03398 
03399 /********************************************************************************************
03400 
03401     void Range::AttrsHaveChanged()
03402 
03403     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03404     Created:    12/12/94
03405     Purpose:    This function gets called to inform the range that attributes applied to its 
03406                 objects have, or may have changed. It deletes the Common attribute cache if 
03407                 one exists.
03408     SeeAlso:    SelRange::AttrsHaveChanged
03409 
03410 ********************************************************************************************/
03411 
03412 void Range::AttrsHaveChanged()
03413 {
03414     // if we are currently caching current attrs delete them 
03415     if (pCommonAttribCache)
03416     {
03417         delete pCommonAttribCache;
03418         pCommonAttribCache = NULL;
03419     }
03420 }
03421 
03422 /********************************************************************************************
03423 
03424     BOOL Range::ConsistsOf(CCRuntimeClass* pccrtSearch, BOOL fIncludeDerivedClasses=TRUE)
03425 
03426     Author:     Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com>
03427     Created:    24/3/97
03428     Inputs:     pccrtSearch     The type of object to search for
03429                 fIncludeDerivedClasses
03430                                 If FALSE, the region must consist only of objects of
03431                                 class pccrtSearch.
03432 
03433                                 If TRUE, the region must consist only of objects
03434                                 of pccrtSearch and derived classes.
03435                 
03436     Purpose:    Determines whether the range consists entirely of the given
03437                 class of object - or of derived classes of the given sort of object.
03438 
03439                 This is used by the WebAddressDlg, which needs to know whether
03440                 the selection consists entirely of text.
03441 
03442 
03443     SeeAlso:    Range::FindFirst, Range::FindNext, SelRange::FindFirst,
03444                 Range::FindNext
03445 
03446 ********************************************************************************************/
03447 
03448 BOOL Range::ConsistsOf(CCRuntimeClass* pccrtSearch, BOOL fIncludeDerivedClasses)
03449 {
03450     //Find the first node in the range, not including children
03451     Node* pThisNode=FindFirst(FALSE);
03452 
03453     //Now scan until we get to the end of the range
03454     while (pThisNode!=NULL)
03455     {
03456         BOOL ok=TRUE;
03457 
03458         //Is the object allowed to be a derived class of pccrtSearch?
03459         if (fIncludeDerivedClasses)
03460             //Yes. So test to see whether the node is a derived class of pccrtSearch
03461             ok=pThisNode->IsKindOf(pccrtSearch);
03462         else
03463             //No. So test whether the node is exactly the same class as pccrtSearch
03464             ok=(pThisNode->GetRuntimeClass()==pccrtSearch);
03465 
03466         //If the node wasn't of the appropriate class, return FALSE now.
03467         if (!ok)
03468             return FALSE;
03469 
03470         //Get the next object to test
03471         pThisNode=FindNext(pThisNode, FALSE);
03472     }
03473 
03474     //We've reached the end of the range and all the nodes were of the class we wanted.
03475     //So return TRUE
03476     return TRUE;
03477 
03478 }
03479 
03480 /********************************************************************************************
03481 
03482     BOOL Range::IsEmpty()
03483 
03484     Author:     Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com>
03485     Created:    24/3/97
03486     Inputs:     -
03487     Returns:    TRUE if the range is empty
03488                 
03489     Purpose:    Determines whether the range is empty
03490 
03491 
03492     SeeAlso:    -
03493 
03494 ********************************************************************************************/
03495 
03496 BOOL Range::IsEmpty()
03497 {
03498     return (FindFirst()==NULL);
03499 }
03500 
03501 
03502 
03503 
03504 /*********************************************************************************************
03505 
03506 >    void Range::SetRenderable(BOOL bNewVis = FALSE)
03507      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03508      Created:   11/12/2003
03509      Inputs:    bNewVis - New state of visibility flag on the nodes in this range
03510      Outputs:   -
03511      Returns:   -
03512      Purpose:   To mark the range as visible or invisible to the screen renderer
03513      SeeAlso:   Range::FindFirst
03514      SeeAlso:   Node::FindNextInRange
03515      Errors:    -
03516 
03517 **********************************************************************************************/
03518 
03519 void Range::SetRenderable(BOOL bNewVis)
03520 {
03521     Node* pNode;
03522 
03523     pNode = FindFirst();
03524     while (pNode)
03525     {
03526         pNode->SetRender(bNewVis, TRUE);
03527 //      if (pNode->IsAnObject()) ((NodeRenderableInk*)pNode)->ReleaseCached();
03528 
03529         pNode = FindNext(pNode);
03530     }
03531 }
03532 
03533 
03534 
03535 
03536 /*********************************************************************************************
03537 
03538 >    Range* Range::CloneNodes(UINT32 timeLimit, BOOL bCloneOnTop, BOOL bLightweight, Layer* pDestLayer = NULL)
03539      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03540      Created:   11/12/2003
03541      Inputs:    timelimit       - if the clone doesn't complete within timelimit, back out
03542                 bCloneOnTop     - Make the clones at the current insertpoint rather than next
03543                                   to the originals
03544                 bLightweight    - Don't do FULL copies of the nodes, assuming they will be
03545                                   deleted rather than retained in the tree
03546      Outputs:   -
03547      Returns:   -
03548      Purpose:   Make a simple copy of the range, without making any undo actions for use
03549                 during solid dragging.
03550      SeeAlso:   Range::FindFirst
03551      SeeAlso:   Node::FindNextInRange
03552      Errors:    -
03553 
03554 **********************************************************************************************/
03555 
03556 Range* Range::CloneNodes(UINT32 timeLimit, BOOL bCloneOnTop, BOOL bLightweight, Layer* pDestLayer)
03557 {
03558     Node* pNode;
03559     ListRange* pCloneRange = new ListRange();
03560     MonotonicTime timeClone;
03561 
03562     pNode = FindFirst();
03563     while (pNode && !timeClone.Elapsed(timeLimit))
03564     {
03565         // Make a copy of the current node
03566         Node* pCopy = NULL;
03567         BOOL bOK = FALSE;
03568 
03569 //      bOK = pNode->CloneNode(&pCopy, bLightweight);
03570         bOK = pNode->NodeCopy(&pCopy);
03571         if (!bOK)
03572             goto CloneError;
03573 
03574         // make sure that it is bounded
03575         ERROR2IF(!pCopy->IsBounded(), FALSE, "Object being pasted is not a NodeRenderableBounded"); 
03576 //      NodeRenderableBounded* BoundCopy = (NodeRenderableBounded*)pCopy;
03577 
03578         // Make sure the copy is not selected
03579         pCopy->SetSelected(FALSE);
03580 
03581         if (pDestLayer==NULL)
03582         {
03583             if (!bCloneOnTop)
03584             {
03585                 // Insert the copied node right alongside the original
03586                 pCopy->AttachNode(pNode, NEXT, FALSE, FALSE);
03587             }
03588             else
03589             {
03590                 Node* pTail = pNode->FindParent(CC_RUNTIME_CLASS(Layer))->FindLastChild(TRUE);
03591                 pCopy->AttachNode(pTail, NEXT, FALSE, FALSE);
03592             }
03593         }
03594         else
03595         {
03596             // Copy the node into a new target spread...
03597             Node* pTail = pDestLayer->FindLastChild(TRUE);
03598             if (pTail)
03599                 pCopy->AttachNode(pTail, NEXT, FALSE, FALSE);
03600             else
03601                 pCopy->AttachNode(pDestLayer, FIRSTCHILD, FALSE, FALSE);
03602         }
03603 
03604         // Add the copied node to the output ListRange
03605         pCloneRange->AddNode(pCopy);
03606 
03607         pNode = FindNext(pNode);
03608     }
03609 
03610 //  If we ran through all the nodes we needed to then pNode will be NULL and we can exit normally
03611     if (pNode==NULL)
03612         return pCloneRange;
03613 //  Else we aborted early and we must fall through to the tidy up routine and return NULL
03614 
03615 CloneError:
03616     pNode = pCloneRange->FindLast();
03617     while (pNode)
03618     {
03619         pNode->CascadeDelete();
03620         delete pNode;
03621 
03622         pNode = pCloneRange->FindPrev(pNode);
03623     }
03624 
03625     delete pCloneRange;
03626 
03627     return NULL;
03628 }
03629 
03630 
03631 
03632 /*********************************************************************************************
03633 
03634 >    void Range::DeleteNodes()
03635      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03636      Created:   12/12/2003
03637      Inputs:    -
03638      Outputs:   -
03639      Returns:   -
03640      Purpose:   Delete the nodes in the range, without making any undo actions for use
03641                 during solid dragging.
03642      SeeAlso:   Range::FindFirst
03643      SeeAlso:   Node::FindNextInRange
03644      Errors:    -
03645 
03646 **********************************************************************************************/
03647 
03648 void Range::DeleteNodes()
03649 {
03650     Node* pNode;
03651     Node* pNext;
03652 
03653     pNode = FindFirst();
03654     while (pNode)
03655     {
03656         pNext = FindNext(pNode);
03657 
03658         pNode->CascadeDelete();
03659         delete pNode;
03660 
03661         pNode = pNext;
03662     }
03663 }
03664 
03665 
03666 
03667 
03668 /*********************************************************************************************
03669 
03670 >    void Range::TransformNodes()
03671      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03672      Created:   12/12/2003
03673      Inputs:    -
03674      Outputs:   -
03675      Returns:   -
03676      Purpose:   Transform the nodes in the range, without making any undo actions for use
03677                 during solid dragging.
03678      SeeAlso:   Range::FindFirst
03679      SeeAlso:   Node::FindNextInRange
03680      Errors:    -
03681 
03682 **********************************************************************************************/
03683 
03684 BOOL Range::TransformNodes(TransformBase* Trans)
03685 {
03686     // If the Trans is invertible then create a Transform node action to undo the transform
03687     // Note at this point Trans is not the inverse Transformation, but it will be soon !!
03688 //  Node* pParent = NULL;
03689 //  Node* pNodeToTransform = NULL;
03690 
03691     // Assume that the transformation is invertible
03692     // If we test for it below, the test explodes when the matrix scale factor is zero.
03693 //  if (Trans->IsInvertable()) 
03694     {
03695         // Scan the range and transform each node 
03696         Node* Current = FindFirst();
03697         Node* Next; 
03698 //      Node* Parent = NULL;
03699 //      BOOL bTransform = FALSE;
03700         while (Current != NULL)
03701         {
03702             Next = FindNext(Current); 
03703 
03704             if (Current->IsNodeRenderableClass())
03705             {
03706                 Trans->bHaveTransformedCached = TRUE;
03707                 Trans->bHaveTransformedChildren = TRUE;
03708 
03709                 ((NodeRenderable*)Current)->Transform(*Trans);
03710 
03711                 ERROR3IF(!Trans->bHaveTransformedCached && !Trans->bHaveTransformedChildren,
03712                          "Node::Transform has failed to either transform its children or transform its cached data"
03713                         );
03714 
03715                 ERROR3IF(Trans->bTransformYourChildren && !Trans->bHaveTransformedChildren,
03716                          "Node::Transform was told to transform its children but says it hasn't done so"
03717                         );
03718 
03719                 Trans->bHaveTransformedAllCached = Trans->bHaveTransformedAllCached && Trans->bHaveTransformedCached;
03720                 Trans->bHaveTransformedAllChildren = Trans->bHaveTransformedAllChildren && Trans->bHaveTransformedChildren;
03721 
03722             }
03723             
03724             Current = Next;  
03725         }
03726 
03727     }
03728 //  else // The Trans is not invertable
03729 //  {
03730 //      ERROR2(FALSE, "Range::TransformNodes can't do non-invertible transforms yet");
03731 //  }
03732 
03733     return (TRUE); // Success 
03734 }   
03735 
03736 
03737 
03738 
03739 /*********************************************************************************************
03740 
03741 >    void Range::SetDraggedNodes(BOOL bNewState)
03742      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03743      Created:   12/12/2003
03744      Inputs:    bNewState - TRUE to mark nodes as currently being dragged
03745      Outputs:   -
03746      Returns:   -
03747      Purpose:   Set the Dragged flag on all the Bounded nodes in the range so that
03748                 they can be treated differently than normal objects by routines which
03749                 need to know.
03750      SeeAlso:   Range::FindFirst
03751      SeeAlso:   Node::FindNextInRange; CSnap::TryToSnapToObject
03752      Errors:    -
03753 
03754 **********************************************************************************************/
03755 
03756 void Range::SetDraggedNodes(BOOL bNewState)
03757 {
03758     Node* pNode;
03759 
03760     pNode = FindFirst();
03761     while (pNode)
03762     {
03763 //      if (pNode->IsBounded())
03764 //          ((NodeRenderableBounded*)pNode)->SetDraggedState(bNewState, TRUE);
03765         if (pNode->IsNodeRenderableClass())
03766             ((NodeRenderable*)pNode)->SetDraggedState(bNewState, TRUE);
03767 
03768         pNode = FindNext(pNode);
03769     }
03770 }
03771 
03772 
03773 
03774 
03775 /*********************************************************************************************
03776 
03777 >   void Range::CopyNodesContents(Range* pDestRange, BOOL bSetDragged = FALSE, BOOL bSetRenderable = FALSE)
03778     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03779     Created:    15/12/2003
03780     Inputs:     pDestRange - The destination range whose nodes must match those in this range
03781                 EXACTLY!
03782     Outputs:    -
03783     Returns:    -
03784     Purpose:    Copy node contents from one range to another.
03785     SeeAlso:    Range::FindFirst
03786     SeeAlso:    Node::FindNextInRange
03787     Errors:     -
03788 
03789 **********************************************************************************************/
03790 void Range::CopyNodesContents(Range* pDestRange, BOOL bSetDragged, BOOL bSetRenderable)
03791 {
03792     Node* pSrcNode;
03793     Node* pDestNode;
03794     NodeRenderable* pSrcRendNode;
03795     NodeRenderable* pDestRendNode;
03796 
03797 //  ENSURE(GetCount()==pDestRange->GetCount(), "Ranges differ too much");
03798 
03799     pSrcNode = FindFirst(FALSE);
03800     pDestNode = pDestRange->FindFirst(FALSE);
03801     while (pSrcNode && pDestNode)
03802     {
03803         ENSURE(pSrcNode->GetRuntimeClass() == pDestNode->GetRuntimeClass(), "Ranges differ unexpectedly");
03804         if (pSrcNode->IsBounded() && pSrcNode->GetRuntimeClass() == pDestNode->GetRuntimeClass())
03805         {
03806             pSrcRendNode = (NodeRenderable*)pSrcNode;
03807             pDestRendNode = (NodeRenderable*)pDestNode;
03808 
03809             // Do the children because PolyCopyNodeContents can't sensibly be recursive
03810             Node* pSrcSubNode = pSrcRendNode->FindFirstDepthFirst();
03811             while (pSrcSubNode && (pSrcSubNode->IsNodeHidden() || pSrcSubNode->IsDragged()))                    // URGH
03812                 pSrcSubNode = pSrcSubNode->FindNextDepthFirst(pSrcRendNode);    // URGH
03813 
03814             Node* pDestSubNode = pDestRendNode->FindFirstDepthFirst();
03815             while (pDestSubNode && (pDestSubNode->IsNodeHidden() || !pDestSubNode->IsDragged()))                // URGH
03816                 pDestSubNode = pDestSubNode->FindNextDepthFirst(pDestRendNode); // URGH
03817 
03818             while (pSrcSubNode && pDestSubNode)
03819             {
03820                 if (pSrcSubNode->IsNodeRenderableClass()
03821                     && pDestSubNode->IsNodeRenderableClass())
03822                 {
03823                     ENSURE(pSrcSubNode->GetRuntimeClass() == pDestSubNode->GetRuntimeClass(), "Ranges differ unexpectedly");
03824                     ((NodeRenderable*)pSrcSubNode)->PolyCopyNodeContents((NodeRenderable*)pDestSubNode);
03825                     pDestSubNode->SetSelected(FALSE);
03826                     pDestSubNode->SetParentOfSelected(FALSE);
03827                 }
03828 
03829                 pSrcSubNode = pSrcSubNode->FindNextDepthFirst(pSrcRendNode);
03830                 while (pSrcSubNode && (pSrcSubNode->IsNodeHidden() || pDestSubNode->IsDragged()))                   // URGH
03831                     pSrcSubNode = pSrcSubNode->FindNextDepthFirst(pSrcRendNode);    // URGH
03832 
03833                 pDestSubNode = pDestSubNode->FindNextDepthFirst(pDestRendNode);
03834                 while (pDestSubNode && (pDestSubNode->IsNodeHidden() || !pDestSubNode->IsDragged()))                // URGH
03835                     pDestSubNode = pDestSubNode->FindNextDepthFirst(pDestRendNode); // URGH
03836             }
03837 
03838 //          if (bSetDragged && pDestRendNode->IsBounded())
03839 //              ((NodeRenderableBounded*)pDestRendNode)->SetDraggedState(TRUE, TRUE);
03840             if (bSetDragged)
03841                 pDestRendNode->SetDraggedState(TRUE, TRUE);
03842             if (bSetRenderable)
03843                 pDestRendNode->SetRender(TRUE, TRUE);
03844 
03845         }
03846 
03847         pSrcNode = FindNext(pSrcNode, FALSE);
03848         pDestNode = pDestRange->FindNext(pDestNode, FALSE);
03849     }
03850 }
03851 
03852 
03853 
03854 
03855 /*****************************************************************************************
03856 //
03857 //  SelRange class implementation...
03858 */
03859 
03860 
03861 /********************************************************************************************
03862 
03863 >   static BOOL SelRange::DeclarePrefs()
03864 
03865     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03866     Created:    18/08/95
03867     Returns:    TRUE if it worked, FALSE if it failed
03868     Purpose:    Declares any preferences that the SelRange class needs to declare
03869 
03870 ********************************************************************************************/
03871 
03872 BOOL SelRange::DeclarePrefs()
03873 {
03874     BOOL ok = TRUE;
03875 
03876     // No preferences today, thank you!
03877 
03878     return ok;
03879 }
03880 
03881 
03882 
03883 
03884 /*****************************************************************************************
03885 
03886 >   SelRange::SelRange()
03887 
03888     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03889     Created:    16/2/94
03890 
03891     Inputs:     -
03892     Outputs:    -
03893     Returns:    -
03894 
03895     Purpose:    SelRange constructor. Defines a SelRange to be a range containing selected 
03896                 objects and that range is allowed to cross layers.
03897 
03898     Errors:     -
03899 
03900 ******************************************************************************************/
03901 
03902 SelRange::SelRange() :  Range(NULL, NULL, RangeControl(TRUE,FALSE,TRUE,TRUE))   // Call Range constructor
03903 {
03904     ScopeDocument = NULL;
03905     Cached = FALSE;
03906     IsGagged = FALSE;
03907 
03908     CachedBounds = FALSE;       // Bounds not cached
03909     CachedBoundsNoAttrs = FALSE;    // Nor are bounds-without-attributes
03910 
03911     InformedSelChanged = FALSE; 
03912     CachedCount = 0;            // Number of objects in range is unknown
03913     LastFindNextNode = NULL;
03914 //  SelectedSpread = NULL; 
03915     // There is no need to initialise CachedBBox, CachedBlobBBox, or CachedBBoxNoBounds
03916 
03917     pLastSelectedNode = NULL;   // We don't know of any last selected node yet
03918     pLastSelectedDoc  = NULL;
03919 
03920     MessageHandler = new SelRangeMessageHandler;
03921 
03922     m_pEffectsStack     = NULL;
03923     m_pEffectClassRange = NULL;
03924     m_bEffectClassNodesOnly = FALSE;
03925     m_pAreaNode         = NULL;
03926 }
03927 
03928 /*****************************************************************************************
03929 
03930 >   SelRange::SelRange()
03931 
03932     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03933     Created:    16/2/94
03934 
03935     Inputs:     -
03936     Outputs:    -
03937     Returns:    -
03938 
03939     Purpose:    SelRange constructor. Defines a SelRange to be a range containing selected 
03940                 objects and that range is allowed to cross layers.
03941 
03942     Errors:     -
03943 
03944 ******************************************************************************************/
03945 SelRange::SelRange(SelRange &Copy) : Range(Copy)
03946 {
03947     ScopeDocument = Copy.ScopeDocument;
03948     Cached = Copy.Cached;
03949     IsGagged = Copy.IsGagged;
03950 
03951     CachedBounds = Copy.CachedBounds;       // Bounds not cached
03952     CachedBoundsNoAttrs = Copy.CachedBoundsNoAttrs; // Nor are bounds-without-attributes
03953 
03954     InformedSelChanged = Copy.InformedSelChanged; 
03955     CachedCount = Copy.CachedCount;         // Number of objects in range is unknown
03956     LastFindNextNode = Copy.LastFindNextNode;
03957 //  SelectedSpread = NULL; 
03958     // There is no need to initialise CachedBBox, CachedBlobBBox, or CachedBBoxNoBounds
03959 
03960     pLastSelectedNode = Copy.pLastSelectedNode; // We don't know of any last selected node yet
03961     pLastSelectedDoc  = Copy.pLastSelectedDoc;
03962 
03963     MessageHandler = new SelRangeMessageHandler;
03964 
03965     m_pEffectsStack         = NULL;
03966     m_pEffectClassRange     = NULL;
03967     m_bEffectClassNodesOnly = FALSE;
03968     m_pAreaNode             = NULL;
03969 }
03970 
03971 
03972 
03973 
03974 
03975 SelRange::~SelRange()
03976 {
03977     if (MessageHandler)
03978     {
03979         delete MessageHandler;
03980     }
03981 }
03982 
03983 
03984 
03985 
03986 /******************************************************************************************
03987 
03988 >   void SelRange::SetRangeControl(RangeControl RangeControlFlgs)
03989 
03990     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03991     Created:    18/02/94
03992     
03993     Inputs:     RangeControlFlags: Specifies the properties of the nodes to be included
03994                                    in the range. Or it would if we didn't ignore it.
03995     Outputs:    -
03996     Returns:    -
03997     Purpose:    To prevent the range controls of the SelRange subclass being altered.
03998                 A call to this function has no effect (other than a TRACE warning msg)
03999     Errors:     -
04000     SeeAlso:    -
04001 
04002 ******************************************************************************************/
04003 
04004 void SelRange::SetRangeControl(RangeControl RangeControlFlgs)
04005 {
04006     TRACE( _T("Warning: Calls to SelRange::SetRangeControl have no effect"));
04007 }
04008 
04009 
04010 
04011 
04012 /******************************************************************************************
04013 
04014 >    Node* SelRange::FindFirst(BOOL AndChildren = FALSE)
04015 
04016      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> (& Jason) (Changed by Phil, 30/11/94)
04017      Created:   18/02/94
04018      
04019      Returns:   If the range contains any members then
04020                     A pointer to the first node in the range is returned
04021                 Else
04022                     NULL is returned 
04023             
04024      Purpose:   The purpose of this function is to find the first node in a range. 
04025 
04026      Notes:     This function, in combination with SelRange::FindNext, will attempt
04027                 to cache useful information on the fly. This will occur if you scan the
04028                 range in sequential order from start to finish.
04029 
04030      SeeAlso:   SelRange::FindNext; SelRange::FindLast
04031 
04032 ******************************************************************************************/
04033 
04034 Node* SelRange::FindFirst(BOOL AndChildren)
04035 {
04036     if (Document::GetSelected() == NULL)
04037     {
04038         Cached = FALSE;
04039         CachedBounds = FALSE;
04040         CachedBoundsNoAttrs = FALSE;
04041         if (pCommonAttribCache)
04042         {
04043             delete pCommonAttribCache;
04044             pCommonAttribCache = NULL; 
04045         }
04046         return(NULL);                           // No document so no selection
04047     }
04048 
04049     // If we're in a Document SelChanged message broadcast, somebody might ask us
04050     // for info before we get the msg: In this case, we flush the cache, so that
04051     // we don't return any out of date info
04052     if (Cached && Document::GetSelected() != ScopeDocument)
04053     {
04054         Update(FALSE, NULL);
04055         pLastSelectedNode = NULL;
04056     }
04057 
04058     // Call base class FindFirst function instead of more specialised and less flexible
04059     // one in Node. (When FirstNode is already known, calling this func instead of just
04060     // returning FirstNode allows children of it to be returned.)
04061     Node* pNode = Range::FindFirst(AndChildren);
04062     // Don't set FirstNode or LastNode yet, we'll set FirstNode below only if it's
04063     // not already been cached. LastNode will only be set when SelRange::FindNext
04064     // reaches the end of the range.
04065 
04066     if (!Cached || CachedCount < 0)         // If cached count is invalid/incomplete
04067     {
04068         Cached = FALSE;
04069         if (pCommonAttribCache)
04070         {
04071             delete pCommonAttribCache;
04072             pCommonAttribCache = NULL; 
04073         }
04074 
04075         CachedBounds = FALSE; 
04076         if (pNode == NULL)
04077         {
04078             // No objects in range. Note that in this case                  
04079             // the BBoxes will be recalculated whenever                                     
04080             // requested, so we don't need to touch them
04081             CachedCount = 0;
04082             FirstNode = NULL;
04083             LastNode = NULL;
04084         }
04085         else
04086         {
04087             // Range contains at least some members!
04088             //
04089             // Only allow primary members of the range to be counted and have their bounds
04090             // accumulated by setting the AndChildren flag FALSE on the InRange test...
04091             if ( InRange(pNode,FALSE) )
04092             {
04093                 FirstNode = pNode;
04094                 CachedBBox = ((NodeRenderableBounded*)FirstNode)->GetBoundingRect();
04095                 CachedCount = -1;
04096             }
04097             else
04098                 CachedCount = 0;
04099         }
04100     }
04101 
04102     return(LastFindNextNode = pNode); 
04103 }
04104 
04105 
04106 
04107 
04108 /******************************************************************************************
04109 
04110 >    Node* SelRange::FindNext(Node* Prev, BOOL AndChildren = FALSE)
04111 
04112      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> (& Jason) (Changed by Phil, 30/11/94)
04113      Created:   18/02/94
04114     
04115      Inputs:    Prev: The previous node in the range (usually returned from a 
04116                         Range::FindFirst, or a previous Range::FindNext call).
04117      Outputs:    - 
04118     
04119      Returns:   If the range contains any more members then
04120                     A pointer to the next node in the range is returned
04121                     (NOTE this does not return members of a selected group object)
04122                 Else
04123                     NULL is returned 
04124 
04125      Purpose:   The purpose of this function is to find the next node in the range after
04126                 Prev.
04127 
04128      Notes:     This function, in combination with SelRange::FindFirst, will attempt
04129                 to cache useful information on the fly. This will occur if you scan the
04130                 range in sequential order from start to finish.
04131 
04132      SeeAlso:   SelRange::FindFirst; Range::findNext
04133      
04134      Errors:    An assertion failure will occur if:
04135                     There are no more nodes to search and the last node to search was not found.
04136                     The pointer passed in is NULL
04137 
04138      Technical Notes:
04139                 This function attempts to cache as much useful information as possible.
04140                 After reaching the last node of a range, it has cached the first and last
04141                 pointers. If the client progressed through the range in a sequential fashion
04142                 (each 'Previous' pointer passed in was returned by the previous FindNext call)
04143                 then a cached count of the number of objects is also recorded. If this is not
04144                 the case, then the count will be set to zero, which forces the Count() function
04145                 to re-scan the entire range to determine the count. Note that during this process
04146                 CachedCount is zero if the count is invalid, or a NEGATIVE accumulated count of
04147                 number of objects counted so far. If the value is positive, it is already cached.
04148                 While the CachedCount is valid, an overall Bounding Box will also be cached.
04149 
04150 ******************************************************************************************/
04151 
04152 Node *SelRange::FindNext(Node *Previous, BOOL AndChildren)
04153 {
04154     // Preconditions
04155 //  ERROR2IF(Previous==NULL,NULL,"NULL pointer passed to SelRange::FindNext");
04156 
04157     NodeRenderableBounded* pNext;
04158 
04159     if (Previous == NULL)
04160         return(LastFindNextNode = NULL);
04161 
04162     if (CachedCount < 0 && Previous != LastFindNextNode)    // If accumulating a count, and 
04163         CachedCount = 0;                                    //  not progressing sequentially through
04164                                                             //  the range, abandon the cached count.
04165 
04166     // Call base class FindNext...
04167     pNext = (NodeRenderableBounded*) Range::FindNext(Previous, AndChildren);
04168 
04169     if (!Cached)
04170     {
04171         if (pNext != NULL)                      //  If there is another node
04172         {
04173             if (CachedCount < 0 && InRange(pNext,FALSE))
04174             {
04175                 CachedCount--;                  //  and if count still valid, accumulate it
04176                 CachedBBox = CachedBBox.Union(pNext->GetBoundingRect());
04177             }
04178         }
04179         else
04180         {
04181             if (CachedCount < 0)
04182             {                                   // If there were no more selected nodes
04183                 Cached = TRUE;                  // then Last is now true last object ptr
04184                 CachedCount = -CachedCount;     // and count (if any) is now valid
04185                 LastNode = Previous;            // Know that Previous was last, so record that info
04186             }
04187         }
04188     }
04189 
04190     return ( LastFindNextNode = (Node *) pNext );   // Return, remembering the return value
04191 }
04192 
04193 
04194 /******************************************************************************************
04195 
04196 >    Node* SelRange::FindLast()
04197 
04198      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04199      Created:   02/03/94
04200      
04201      Returns:   If the range contains any members then
04202                     A pointer to the last node in the range is returned
04203                 Else
04204                     NULL is returned 
04205             
04206      Purpose:   The purpose of this function is to find the last node in a SelRange. 
04207 
04208      Notes:     If the LastNode value is not cached, then this will necessitate a scan
04209                 of the entire selction range to determine (and cache) the LastNode.
04210 
04211      SeeAlso:   SelRange::FindFirst;
04212      
04213 ******************************************************************************************/
04214 
04215 Node* SelRange::FindLast()
04216 {
04217     FreshenCache();                     // Ensure the cache is valid
04218     return(LastNode);                   // Return cached LastNode value
04219 }
04220 
04221 
04222 
04223 /******************************************************************************************
04224 
04225 >    Node* SelRange::FreshenCache()
04226 
04227      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> (with tattered remnants of Phil's code in there somewhere)
04228      Created:   02/03/94
04229     
04230      Inputs:    -
04231      Outputs:   - 
04232      Returns:   - 
04233 
04234      Purpose:   Internal call for caching useful information
04235                 Determines if the SelRange information cache is valid, and if not, scans
04236                 the entire range to accumulate that information, and freshens the cache.
04237                                             
04238      Notes:     After a call to this function, all possible SelRange information will have
04239                 been cached. The cache is invalidated when the selection changes.
04240                 This routine just makes repeated calls to SelRange::FindFirst and FindNext
04241                 because they will cache any missing information.
04242 
04243      SeeAlso:   SelRange::FindFirst; SelRange::FindNext
04244      
04245      Errors:    -
04246 
04247 ******************************************************************************************/
04248 
04249 void SelRange::FreshenCache()
04250 {
04251     BOOL NoDocument = (Document::GetSelected() == NULL);
04252     Node* pNode = NULL;
04253 
04254     if (NoDocument || (Cached && Document::GetSelected() != ScopeDocument))
04255     {
04256         Cached = FALSE;
04257         
04258         if (pCommonAttribCache)
04259         {
04260             delete pCommonAttribCache;
04261             pCommonAttribCache = NULL; 
04262         }
04263 
04264         CachedBounds = FALSE; 
04265         FirstNode = LastNode = NULL;
04266         CachedCount = 0;
04267         CachedBBox.MakeEmpty();
04268         CachedBlobBBox.MakeEmpty();
04269         pLastSelectedNode = NULL;
04270         pLastSelectedDoc  = NULL;
04271     }
04272 
04273     if (!Cached || CachedCount <= 0 || FirstNode==NULL)     // If count not cached, or is invalid
04274     {
04275         BOOL bOldValue = RangeControlFlags.PromoteToParent;
04276         RangeControlFlags.PromoteToParent = TRUE;
04277 
04278         if ((!Cached && !NoDocument) || FirstNode==NULL)    // If FirstNode not cached, find it
04279             pNode = FindFirst();            // Call own find first in SelRange function
04280 
04281         if (FirstNode != NULL)
04282         {
04283             while (pNode)                       // Scan the range...
04284             {
04285                 pNode = FindNext(pNode);        // by calling own find next in SelRange function
04286             }
04287         }
04288         else
04289         {
04290             // No objects - Ensure that the bounding boxes are set to 'empty'
04291             CachedBBox.MakeEmpty();
04292             CachedBlobBBox.MakeEmpty();
04293 
04294             CachedBoundsNoAttrs = FALSE;
04295             CachedBBoxNoAttrs.MakeEmpty();
04296 
04297             LastNode = NULL;
04298         }
04299 
04300         RangeControlFlags.PromoteToParent = bOldValue;
04301 
04302         if (!NoDocument && FirstNode!=NULL)
04303         {
04304             Cached = TRUE;          // All useful info possible has been cached
04305             CachedBounds = TRUE; 
04306         }
04307     }
04308 
04309 
04310 // This seems to serve no useful purpose other than bringing up an Ensure box
04311 // in the middle of some perfectly valid situations.
04312 //#ifdef _DEBUG
04313 //  if (NoDocument)
04314 //      ERROR3("SelRange called when there is no SelectedDocument!");
04315 //#endif
04316 }
04317 
04318 
04319 
04320 
04321 /******************************************************************************************
04322 
04323 >    UINT32 SelRange::Count()
04324 
04325      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04326      Created:   18/02/94
04327      
04328      Inputs:    -
04329      Outputs:   -
04330      
04331      Returns:   The number of nodes described by the SelRange (may be zero)
04332 
04333      Purpose:   To count the number of nodes in a range.
04334 
04335      Notes:     After a call to this function, all possible SelRange information will have
04336                 been cached. The cache is invalidated when the selection changes.
04337 
04338      Errors:    -
04339 
04340 ******************************************************************************************/
04341 
04342 UINT32 SelRange::Count()
04343 {
04344     // DMc 18/5/99
04345     // if PromoteToParent is set, then I think we need to recalc this
04346     Node * pNode = FindFirst();
04347     
04348     INT32 count = 0;
04349     
04350     while (pNode)
04351     {
04352         count ++;
04353         pNode = FindNext(pNode);
04354     }
04355     
04356     CachedCount = count;
04357     
04358     return(CachedCount);        // Return the cached count
04359 }
04360 
04361 
04362 
04363 /********************************************************************************************
04364 
04365 >   void SelRange::FreshenBoundsCache()
04366 
04367     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04368     Created:    13/12/94
04369     Inputs:     -
04370     Outputs:    -
04371     Returns:    -
04372     Purpose:    This function ensures that the  CachedBBox and CachedBlobBBox are up-to-date
04373                 
04374     Scope:      private
04375     Errors:     -
04376     SeeAlso:    SelRange::UpdateBounds
04377 
04378 ********************************************************************************************/
04379 
04380 void SelRange::FreshenBoundsCache()
04381 {
04382     FreshenCache();     // Ensure the cache is valid
04383     if (!CachedBounds)
04384     {
04385         // Scan the range and calculate the bounds
04386         CachedBBox.MakeEmpty();
04387         CachedBlobBBox.MakeEmpty();
04388 
04389         BOOL bOldValue = RangeControlFlags.PromoteToParent;
04390         RangeControlFlags.PromoteToParent = TRUE;
04391         Node* Current = FindFirst();
04392         while (Current != NULL)
04393         {
04394             CachedBBox = CachedBBox.Union(((NodeRenderableBounded*)Current)->GetBoundingRect());
04395             Current = FindNext(Current);
04396         }
04397         RangeControlFlags.PromoteToParent = bOldValue;
04398         CachedBounds = TRUE;
04399     }
04400 }
04401 
04402 
04403 
04404 /******************************************************************************************
04405 
04406 >    DocRect SelRange::GetBoundingRect(BOOL bPromoteToControllers = FALSE);
04407 
04408      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04409      Created:   02/03/94
04410          
04411      Inputs:    -
04412      Outputs:   -
04413      
04414      Returns:   The bounding rectangle of the current selection. If nothing is selected this
04415                 will be an empty rectangle.
04416                 Note that rather than using DocRect::IsEmpty, it may be slightly more efficient
04417                 to check the empty case with  if (SelRange::Count() == 0),  as the SelRange
04418                 caching mechanism results in this being an efficient call. This may also make
04419                 your code more readable.
04420 
04421      Purpose:   To find the bounding box of all objects in a selection range.
04422 
04423      Notes:     After a call to this function, all possible SelRange information will have
04424                 been cached. The cache is invalidated when the selection changes.
04425 
04426      SeeAlso:   SelRange::GetBlobBoundingRect
04427 
04428      Errors:    -
04429 
04430 ******************************************************************************************/
04431 
04432 DocRect SelRange::GetBoundingRect(BOOL bPromoteToControllers)
04433 {
04434     FreshenBoundsCache();       // Ensure the cache is valid
04435 
04436 // This strange stuff is needed because FreshenBoundsCache doesn't necessarilly
04437 // loop through the node in this range
04438 // Phil 27/10/2004
04439 //
04440     BOOL bOldValue = SetPromoteToParent(bPromoteToControllers);
04441 
04442     // Calculate the union of all nodes within the range 
04443     DocRect Bounds; 
04444     NodeRenderableBounded* pNode = (NodeRenderableBounded*) FindFirst(FALSE);
04445     while (pNode != NULL)
04446     {
04447         Bounds = Bounds.Union(pNode->GetBoundingRect());
04448         pNode =(NodeRenderableBounded*)FindNext(pNode, FALSE);
04449     } 
04450 
04451     CachedBBox = Bounds;
04452     
04453     SetPromoteToParent(bOldValue);
04454 
04455     return(CachedBBox);         // Return cached bounding box
04456 }
04457 
04458 
04459 
04460 
04461 /******************************************************************************************
04462 
04463 >   DocRect SelRange::GetBoundsWithoutAttrs(void)
04464 
04465      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04466      Created:   23/02/95
04467          
04468      Returns:   The bounds of the selection, not including the effects of attributes
04469                 (e.g. line widths, etc)
04470 
04471      Purpose:   To find the bounding box of all objects in a selection range, not
04472                 including the effects of any attributes applied thereto.
04473 
04474                 This is used (mainly in the selector tool) to provide things like
04475                 the width and height of objects, exclusive of line width.
04476 
04477     Notes:      The returned bounding box will be "Empty" if there is no selection.
04478 
04479 ******************************************************************************************/
04480 
04481 DocRect SelRange::GetBoundsWithoutAttrs(void)
04482 {
04483     // If we happen to have already got this information, then it'll be cached,
04484     // but otherwise, we'll have to go and find out.
04485     if (!CachedBoundsNoAttrs)
04486     {
04487         // Set a reasonable default state in case of failure
04488         CachedBBoxNoAttrs.MakeEmpty();
04489 
04490         // Scan the range, accumulating the bounds. GetBoundingRect is called with a TRUE
04491         // parameter in order to get the bounds exclusive of attributes.
04492         Node *pNode = FindFirst();
04493 
04494         if (pNode != NULL)
04495         {
04496             // Set the bounds to the bounds of the first object - who can tell what we'll
04497             // get if we union an "empty" rectangle with another.
04498             CachedBBoxNoAttrs = ((NodeRenderableBounded*)pNode)->GetBoundingRect(TRUE);
04499             pNode = FindNext(pNode);
04500 
04501             while (pNode != NULL)
04502             {
04503                 CachedBBoxNoAttrs = CachedBBoxNoAttrs.Union( ((NodeRenderableBounded*)pNode)->GetBoundingRect(TRUE) );
04504                 pNode = FindNext(pNode);
04505             }
04506         }
04507 
04508         // And remember that we now have a cached set of bounds
04509         CachedBoundsNoAttrs = TRUE;
04510     }
04511 
04512     return(CachedBBoxNoAttrs);
04513 }
04514 
04515 
04516 
04517 /******************************************************************************************
04518 
04519 >    DocRect SelRange::GetBlobBoundingRect(void);
04520 
04521      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04522      Created:   03/03/94
04523          
04524      Inputs:    -
04525      Outputs:   -
04526      
04527      Returns:   The bounding rectangle of the current selection, including the selection blobs
04528                 If nothing is selected this will be an empty rectangle. 
04529                 Note that rather than using DocRect::IsEmpty, it may be slightly more efficient
04530                 to check the empty case with  if (SelRange::Count() == 0), as the SelRange
04531                 caching mechanism results in this being an efficient call. This may also make
04532                 your code more readable.
04533 
04534      Purpose:   To find the bounding box of all objects in a selection range, including their
04535                 selection blobs.
04536 
04537      Notes:     After a call to this function, all possible SelRange information will have
04538                 been cached. The cache is invalidated when the selection changes.
04539 
04540      SeeAlso:   SelRange::GetBoundingRect
04541 
04542      Errors:    -
04543 
04544 ******************************************************************************************/
04545 
04546 DocRect SelRange::GetBlobBoundingRect(void)
04547 {
04548     // Get some rectangles
04549     DocRect BlobRect;
04550     DocRect RealRect;
04551 
04552     // Make sure they are empty
04553     BlobRect.MakeEmpty();
04554     RealRect.MakeEmpty();
04555 
04556     // Loop through the selection
04557     BOOL bOldValue = RangeControlFlags.PromoteToParent;
04558     RangeControlFlags.PromoteToParent = TRUE;
04559 
04560     Node* Current = FindFirst();
04561     while (Current != NULL)
04562     {
04563         ERROR3IF(!Current->IsBounded(), "Non-bounded node in selection in GetBlobBoundingRect\n");
04564         if (Current->IsBounded())
04565         {
04566             NodeRenderableBounded* pBounded = (NodeRenderableBounded*)Current;
04567 
04568             // Get the real bounding rect, since this is worked out anyway
04569             RealRect = RealRect.Union(pBounded->GetBoundingRect());
04570 
04571             // Get the blob bounding rect
04572             BlobRect = BlobRect.Union(pBounded->GetBlobBoundingRect());
04573         }
04574 
04575         // Get the next node in the list
04576         Current = FindNext(Current);
04577     }
04578 
04579     // Make sure the basic selection's blob bounds are included (without doing
04580     // fully recursive scan of the selection)
04581     RangeControlFlags.PromoteToParent = FALSE;
04582 
04583     Current = FindFirst();
04584     while (Current != NULL)
04585     {
04586         ERROR3IF(!Current->IsBounded(), "Non-bounded node in selection in GetBlobBoundingRect\n");
04587         if (Current->IsBounded())
04588         {
04589             NodeRenderableBounded* pBounded = (NodeRenderableBounded*)Current;
04590 
04591             // Get the blob bounding rect
04592             BlobRect = BlobRect.Union(pBounded->GetBlobBoundingRect());
04593         }
04594 
04595         // Get the next node in the list
04596         Current = FindNext(Current);
04597     }
04598 
04599     RangeControlFlags.PromoteToParent = bOldValue;
04600 
04601     // It the cached bounds was out of date, we can use the one we have just built
04602     if (!CachedBounds)
04603     {
04604         // Update the cached rectangle and make it as valid
04605         CachedBBox = RealRect;
04606         CachedBounds = TRUE;
04607     }
04608 
04609     // return the blob bounding rect
04610     return BlobRect;
04611 }
04612 
04613 
04614 
04615 #ifdef SELECTION_AREA_FEATURE
04616 /******************************************************************************************
04617 
04618 >    BOOL SelRange::GetAreaDetails(XLONG* pxlArea, XLONG* pxlPerim)
04619 
04620      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04621      Created:   12/03/2005
04622      Inputs:    -
04623      Outputs:   -
04624      Returns:   The area and perimiter length of the current selection. If nothing is selected these
04625                 will be zero.
04626      Purpose:   To find the area details of all objects in a selection range.
04627      SeeAlso:   SelRange::GetBoundingRect
04628      Errors:    -
04629 
04630 ******************************************************************************************/
04631 
04632 BOOL SelRange::GetAreaDetails(XLONG* pxlArea, XLONG* pxlPerim)
04633 {
04634     FreshenBoundsCache();       // Ensure the cache is valid
04635 
04636 // This strange stuff is needed because FreshenBoundsCache doesn't necessarilly
04637 // loop through the node in this range
04638 // Phil 27/10/2004
04639 //
04640     BOOL bOK = TRUE;
04641     XLONG xlArea = 0; 
04642     XLONG xlPerim = 0;
04643     BOOL bOldValue = SetPromoteToParent(FALSE);
04644 
04645     // Calculate the union of all nodes within the range 
04646     Node* pNode = FindFirst(FALSE);
04647     while (pNode)
04648     {
04649         if (pNode->IsAnObject())
04650         {
04651             NodeRenderableInk* pInkNode = (NodeRenderableInk*)pNode;
04652             bOK = pInkNode->GetAreaDetails(&xlArea, &xlPerim);
04653             *pxlArea += xlArea;
04654             *pxlPerim += xlPerim;
04655         }
04656 
04657         pNode = FindNext(pNode, FALSE);
04658     } 
04659 
04660     SetPromoteToParent(bOldValue);
04661 
04662     return TRUE;
04663 }
04664 
04665 
04666 
04667 
04668 /******************************************************************************************
04669 
04670 >    BOOL SelRange::GetAreaDetailsWhileIdle(XLONG* pxlArea, XLONG* pxlPerim)
04671 
04672      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04673      Created:   28/03/2005
04674      Inputs:    -
04675      Outputs:   -
04676      Returns:   The area and perimiter length of the current selection. If nothing is selected these
04677                 will be zero.
04678      Purpose:   To find the area details of all objects in a selection range.
04679      SeeAlso:   SelRange::GetBoundingRect
04680      Errors:    -
04681 
04682 ******************************************************************************************/
04683 
04684 BOOL SelRange::GetAreaDetailsWhileIdle(XLONG* pxlArea, XLONG* pxlPerim)
04685 {
04686     FreshenBoundsCache();       // Ensure the cache is valid
04687 
04688 // This strange stuff is needed because FreshenBoundsCache doesn't necessarilly
04689 // loop through the node in this range
04690 // Phil 27/10/2004
04691 //
04692     BOOL bOK = TRUE;
04693     XLONG xlArea = 0; 
04694     XLONG xlPerim = 0;
04695     BOOL bOldValue = SetPromoteToParent(FALSE);
04696     MonotonicTime Slice;                            // Sample time now
04697 
04698     // Calculate the union of all nodes within the range
04699     Node* pNode = m_pAreaNode;
04700     if (pNode==NULL)
04701     {
04702         pNode = FindFirst(FALSE);
04703         *pxlArea = 0;
04704         *pxlPerim = 0;
04705     }
04706 
04707     while (pNode && !Slice.Elapsed(50))
04708     {
04709         if (pNode->IsAnObject())
04710         {
04711             NodeRenderableInk* pInkNode = (NodeRenderableInk*)pNode;
04712             bOK = pInkNode->GetAreaDetails(&xlArea, &xlPerim);
04713             *pxlArea += xlArea;
04714             *pxlPerim += xlPerim;
04715         }
04716 
04717         pNode = FindNext(pNode, FALSE);
04718     }
04719 
04720     m_pAreaNode = pNode;
04721 
04722     SetPromoteToParent(bOldValue);
04723 
04724     return (pNode!=NULL);
04725 }
04726 #endif
04727 
04728 
04729 
04730 
04731 /******************************************************************************************
04732 
04733 >   Node *SelRange::GetLastSelectedNode(void)
04734 
04735      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04736      Created:   04/01/95
04737          
04738      Inputs:    -
04739      Outputs:   -
04740      
04741      Returns:   NULL, or a pointer to the last node that was selected
04742 
04743      Purpose:   To find the last node that was selected. This may be only a short-term
04744                 memory (i.e. any update of the selection cache will force us to forget
04745                 the node, so the info may only be available for a short time after the
04746                 node is selected).
04747 
04748                 This information is used by entities such as the colour editor (which
04749                 needs to know the last object selected when it recieves a SelChanged
04750                 message, so it can make an intelligent guess at a good colour to edit)
04751 
04752      SeeAlso:   SelRange::Update
04753 
04754      Errors:    -
04755 
04756 ******************************************************************************************/
04757 
04758 Node *SelRange::GetLastSelectedNode(void)
04759 {
04760     if (pLastSelectedNode != NULL)                          // If we know a last-selected node
04761     {
04762         if (!pLastSelectedNode->IsSelected() ||             // ...& is no longer selected...
04763             pLastSelectedDoc != Document::GetSelected())    // ...or was in a different doc...
04764         {           
04765             pLastSelectedNode = NULL;                       // ...then forget about it
04766             pLastSelectedDoc  = NULL;
04767         }
04768     }
04769 
04770     return(pLastSelectedNode);
04771 }
04772 
04773 
04774 
04775 /******************************************************************************************
04776 
04777 >   void SelRange::Update(BOOL TellWorld = FALSE, Node *LastSelectedNode)
04778 
04779      Author:    Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> (Changed by Phil, 30/11/94)
04780      Created:   18/5/94 (Was an inline function prior to this)
04781 
04782      Inputs:    BOOL TellWorld: When TRUE the function will inform the world that the 
04783                 selection has changed. Normally the selection is changed in an operation
04784                 and it is unneccesary to broadcast a SelChangingMsg at this point because
04785                 this will occur at the end of the operation. However If the selection is
04786                 changed outside of an operation then the flag should be set to TRUE.  
04787 
04788                 LastSelectedNode - NULL (the default), or a pointer to the node being
04789                 selected. This is remembered for return by GetLastSelectedNode, which
04790                 is used by entities such as the colour editor to try to show a sensible
04791                 colour for editing.
04792      Outputs:   -
04793      Returns:   -
04794 
04795      Purpose:   To inform the SelRange that the selection has changed
04796                 This invalidates the SelRange's selection-info cache so it will be
04797                 recached when info is next requested.
04798 
04799      SeeAlso:   SelRange::GetLastSelectedNode; SelRange::UpdateBounds
04800 
04801 ******************************************************************************************/
04802 
04803 void SelRange::Update(BOOL TellWorld, Node *LastSelectedNode)
04804 {
04805     Cached              = FALSE;    
04806     CachedCount         = 0;
04807     InformedSelChanged  = FALSE; 
04808     FirstNode           = NULL;
04809     LastNode            = NULL;
04810     CachedBounds        = FALSE;
04811     CachedBoundsNoAttrs = FALSE;
04812 
04813     LastFindNextNode    = NULL;     // Ensure all on-the-fly FindNext caching is wiped
04814 
04815     // destroy the CommonAttribute cache, a new one will be created the next time
04816     // a user calls the 
04817     if (pCommonAttribCache)
04818     {
04819         delete (pCommonAttribCache); 
04820         pCommonAttribCache = NULL;
04821     }
04822 
04823     // Get rid of any postprocessor stack that has been created
04824     // Note that this means that people using the PostPro stack at times when Update is likely to be called
04825     // must make local copies and NOT rely on this cached copy!
04826     // And there's no such thing as a "Fog Chicken"
04827     if (m_pEffectsStack)
04828     {
04829         delete m_pEffectsStack;
04830         m_pEffectsStack = NULL;
04831     }
04832     if (m_pEffectClassRange)
04833     {
04834         delete m_pEffectClassRange;
04835         m_pEffectClassRange = NULL;
04836     }
04837 
04838     m_pAreaNode = NULL;
04839 
04840     // Remember the last node which was selected (or if it's NULL, forget the last
04841     // node, because we cannot any longer guarantee (a) that it is still selected, or
04842     // (far more importantly) (b) if it still exists
04843     if (LastSelectedNode != NULL)
04844     {
04845         pLastSelectedNode = LastSelectedNode;
04846         pLastSelectedDoc  = Document::GetSelected();
04847     }
04848 
04849     // Inform the DialogBars that the system state has changed so that they can refresh
04850     // themselves
04851     DialogBarOp::SetSystemStateChanged(); 
04852 
04853     if (TellWorld)
04854     {
04855         // Broadcast message/ update status bar etc.
04856         SelChangedCommit();         
04857     }
04858 }
04859 
04860 /********************************************************************************************
04861 
04862 >   EffectsStack* SelRange::GetEffectsStack(BOOL bGetCopy = FALSE, BOOL bEscapeDerived = TRUE)
04863 
04864     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04865     Created:    16/11/2004
04866     Inputs:     -
04867     Outputs:    -
04868     Returns:    -
04869     Purpose:    -
04870     Errors:     -
04871     SeeAlso:    -
04872 
04873 ********************************************************************************************/
04874 
04875 EffectsStack* SelRange::GetEffectsStack(BOOL bGetCopy, BOOL bEscapeDerived)
04876 {
04877     if (bGetCopy)
04878         return EffectsStack::GetEffectsStackFromSelection(this, bEscapeDerived);
04879 
04880     if (m_pEffectsStack==NULL)
04881         m_pEffectsStack = EffectsStack::GetEffectsStackFromSelection(this, bEscapeDerived);
04882 
04883     return m_pEffectsStack;
04884 }
04885 
04886 
04887 /********************************************************************************************
04888 
04889 >   ListRange* SelRange::GetTopClassRange(CCRuntimeClass* pClass, BOOL bClassOnly, BOOL bIgnorePassThroughEffects, Node** ppMasterNode, INT32* piStackPos)
04890 
04891     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04892     Created:    13/05/2005
04893     Inputs:     pClass - pointer to class descriptor for class we're looking for
04894     Outputs:    pMasterNode - pointer to master node (sample node from consistent range) or NULL
04895                 iStackPos - index into effect stack or STACKPOS_INVALID if range not valid in stack
04896     Returns:    -
04897     Purpose:    -
04898     Errors:     -
04899     SeeAlso:    -
04900 
04901 ********************************************************************************************/
04902 
04903 ListRange* SelRange::GetTopClassRange(CCRuntimeClass* pClass, BOOL bClassOnly, BOOL bIgnorePassThroughEffects, Node** ppMasterNode, INT32* piStackPos)
04904 {
04905     CCRuntimeClass* pEffectClass = NULL;
04906     Node* pFirstEffectNode = NULL;
04907 
04908     // First get the type of the nodes in the cached range
04909     if (m_pEffectClassRange)
04910         pFirstEffectNode = m_pEffectClassRange->FindFirst();
04911     if (pFirstEffectNode)
04912         pEffectClass = pFirstEffectNode->GetRuntimeClass();
04913 
04914     if (m_pEffectClassRange==NULL || pClass!=pEffectClass || m_bEffectClassNodesOnly!=bClassOnly)
04915     {
04916         if (m_pEffectClassRange)
04917             delete m_pEffectClassRange;
04918 
04919         m_pEffectClassRange = EffectsStack::GetTopClassRange(pClass, bClassOnly, bIgnorePassThroughEffects, ppMasterNode, piStackPos, this, !bIgnorePassThroughEffects);
04920         m_bEffectClassNodesOnly = bClassOnly;
04921     }
04922     else
04923     {
04924         if (ppMasterNode)
04925             *ppMasterNode = pFirstEffectNode;
04926         if (piStackPos)
04927             *piStackPos = STACKPOS_INVALID;
04928     }
04929 
04930     return m_pEffectClassRange;
04931 }
04932 
04933 
04934 /********************************************************************************************
04935 
04936 >   void SelRange::UpdateBounds()
04937 
04938     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04939     Created:    12/12/94
04940     Inputs:     -
04941     Outputs:    -
04942     Returns:    -
04943     Purpose:    To inform the SelRange that its bounds have changed
04944     Errors:     -
04945     SeeAlso:    -
04946 
04947 ********************************************************************************************/
04948 
04949 void SelRange::UpdateBounds()
04950 {
04951     CachedBounds        = FALSE;
04952     CachedBoundsNoAttrs = FALSE;
04953 
04954 // Removed by Jason, 23/10/95
04955 // After discussion with Simon, this has been removed, as we reckon it's completely
04956 // rampant. The selection has not been changed, but the bounds of the selected item(s)
04957 // have changed. Perhaps there should be a notification broadcast, but it should NOT
04958 // be the SelChangingMsg::SELECTIONCHANGED message that this flag triggers!
04959 //  InformedSelChanged  = FALSE; 
04960 
04961 
04962 #if !defined(EXCLUDE_FROM_RALPH)
04963     // Inform the DialogBars that the system state has changed so that they can refresh
04964     // themselves
04965 
04966     DialogBarOp::SetSystemStateChanged(); 
04967 #endif
04968 }
04969 
04970 
04971 
04972 /********************************************************************************************
04973 
04974 >   void SelRange::BroadcastAnyPendingMessages(void)
04975 
04976     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04977     Created:    21/10/95
04978 
04979     Purpose:    Ensures that any pending SelChangingMsg broadcasts are sent immediately.
04980                 This should be called by any code which calls SelRange::Update with the
04981                 default FALSE (don't broadcast yet) flag parameter, to ensure that soon
04982                 after the change the delayed message broadcast occurs.
04983 
04984     Note:       Any pending message is automatically sent when an Op ends. Thus, you
04985                 only need to call this code directly when you update the selection
04986                 outside an Op. (e.g. when the selected document changes, etc)
04987 
04988     SeeAlso:    SelRange::Update()
04989 
04990 ********************************************************************************************/
04991 
04992 void SelRange::BroadcastAnyPendingMessages(void)
04993 {
04994     // If we're not gagged, and we have any pending message broadcast, do it
04995     if (!IsGagged && !InformedSelChanged)
04996         SelChangedCommit();
04997 }
04998 
04999 
05000 
05001 /********************************************************************************************
05002 
05003 >   void SelRange::SelChangedCommit(); 
05004 
05005 
05006     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
05007     Created:    18/7/94
05008     Inputs:     -
05009     Outputs:    -
05010     Returns:    -
05011     Purpose:    This private fn gets called to inform the world that the selection has changed
05012                 / update the status bar etc.
05013     Errors:     -
05014     Scope:      private
05015     SeeAlso:    -
05016 
05017 ********************************************************************************************/
05018 
05019 void SelRange::SelChangedCommit() 
05020 {
05021     BOOL DifferentDoc = (ScopeDocument != Document::GetSelected());
05022 
05023     // Tell the world that the selection has changed
05024     if (!IsGagged)
05025     {
05026         BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::SELECTIONCHANGED, DifferentDoc));
05027         InformedSelChanged = TRUE;  
05028 
05029         // Update the status bar description of the selection
05030         DescribeSelectionInStatusBar(); 
05031 
05032     }
05033 
05034     // Update our idea of the current scope document
05035     ScopeDocument = Document::GetSelected();
05036 
05037     Node* pNode = FindFirst();
05038     if (pNode != NULL)
05039     {
05040         // Set the selected spread to be the parent spread of the first node in the selection.
05041         pNode = pNode->FindParent(CC_RUNTIME_CLASS(Spread));
05042         ERROR3IF(pNode == NULL,"first node in selection doesn't have a parent spread!");
05043 
05044         // Ensure selected Doc, DocView, and Spread are legal
05045         // (If pNode is NULL, it will try to sort out our life for us)
05046         Document::SetSelectedViewAndSpread(ScopeDocument, NULL, (Spread *)pNode);
05047     }
05048 }
05049 
05050 
05051 
05052 /********************************************************************************************
05053 >   BOOL SafeAppendString(StringBase* pstrAppendum, const StringBase& strAppendee,
05054                           BOOL fAddSep = TRUE)
05055 
05056     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
05057     Created:    21/7/95
05058     Inputs:     pstrAppendum        pointer to the string to append to
05059                 strAppendee         the string to append
05060                 fAddSep             if TRUE (the default) will append a space after
05061                                     strAppendee string.
05062     Outputs:    pstrAppendum        the appended string
05063     Purpose:    Safe way to append one string to another without risking overflow.  If
05064                 an overflow would occur the appending isn't done.
05065     Returns:    TRUE if it was trivial to append, FALSE if the string to be appended had
05066                 to be truncated.
05067     SeeAlso:    SelRange::LayerDescription
05068 ********************************************************************************************/
05069 
05070 BOOL SafeAppendString(StringBase* pstrAppendum, const StringBase& strAppendee, BOOL fAddSep = TRUE)
05071 {
05072     INT32 nTotal = pstrAppendum->Length() + strAppendee.Length() + (fAddSep != 0);
05073     INT32 nMax = pstrAppendum->MaxLength();
05074     BOOL fNoTrunc= (nTotal < nMax);
05075     if (fNoTrunc)
05076     {
05077         // We can append without fear of overflow . . .
05078         *pstrAppendum += strAppendee;
05079         if (fAddSep) *pstrAppendum += TEXT(" ");
05080     }
05081     else
05082     {
05083         // We have to truncate the string to append, to avoid overflow . . .
05084         INT32 nTruncCount = strAppendee.Length() - (nTotal - nMax);
05085         if (nTruncCount > 0)
05086         {
05087             // We have some room for more text, so append the truncated string.
05088             String_256 strTrunc;
05089             strAppendee.Left(&strTrunc, nTruncCount);
05090             *pstrAppendum += strTrunc;
05091             if (fAddSep) *pstrAppendum += TEXT(" ");
05092         }
05093     }
05094 
05095     // Return FALSE if we had to truncate.
05096     return fNoTrunc;
05097 }
05098 
05099 
05100 
05101 
05102 /***********************************************************************************************
05103 
05104 >   String_256 SelRange::Describe(DescriptionFormat Format) const
05105 
05106     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
05107     Created:    25/6/93     
05108         
05109     Inputs:     DescriptFormat: The format of the description generated. Currently there are 
05110                 two types of description which can be generated. 
05111                 
05112                 When DescriptFormat = MENU 
05113                 
05114                     A description suitable for displaying on a menu is generated. The 
05115                         description will have the form:
05116                     
05117                     "rounded rectangle"  // For a single rounded rectangle object 
05118                                          // in the range. 
05119                     "rounded rectangles" // For multiple rounded rectangles in the range. 
05120                     "objects"            // For multiple objects of different types 
05121                                          // in the range. 
05122 
05123                     If the range conatains only group nodes which all have the same name
05124                     then the Group name is used for the description i.e
05125 
05126                     "bannana"            // For a single group with the name bananna
05127                     "bannanas"           // For multiple groups all with the name bananna
05128                     
05129                 When DescriptionFormat = INFOBAR
05130                 
05131                     A description suitable for the infobar is generated. The 
05132                     description will be the same as that generated by the MENU 
05133                     DescriptFormat, except that the descriptions will be prefixed by 
05134                     the number of objects in the range, and will contain a layer description
05135 
05136                     "1 rounded rectangle on layer Blobby"
05137                     "5 rounded rectangles on layer Blobby 
05138                     "5 bannanas on 4 layers"
05139 
05140                     if one item is selected, and is inside, then the description will look like
05141                     this
05142 
05143                     "1 bannana 'inside' on layer Blobby"
05144 
05145                     If there is more than one item selected and at least one item is inside
05146                     then the description will have the word 'inside' appended to it, like this
05147 
05148                     "5 bannanas on 4 layers ('inside')"
05149 
05150     Outputs:    -
05151     Returns:    If there are any members in the range then a description of the members
05152                   in the range is returned, else an empty string is returned. 
05153                   
05154     Purpose:    This routine returns a description of a range of objects.             
05155     
05156     Errors:     A resource exception will be thrown if a problem occurs when loading a
05157                 string resource. 
05158     
05159     SeeAlso:    -
05160 
05161 ***********************************************************************************************/
05162 
05163 String_256 SelRange::Describe(DescriptionFormat Format) 
05164 {
05165     String_256 str = ObjectDescription(Format);
05166     SafeAppendString(&str, LayerDescription(Format), FALSE);
05167     return str;
05168 }
05169 
05170 
05171 
05172 
05173 /********************************************************************************************
05174 
05175 >   String_64 SelRange::ObjectDescription(DescriptionFormat Format)
05176 
05177     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
05178     Created:    30/6/94
05179     Inputs:     -
05180     Outputs:    -
05181     Returns:    -
05182     Purpose:    Returns a string describing the objects in the selection, see 
05183                 SelRange::Describe for a description of the format
05184     Errors:     -
05185     Scope:      private
05186     SeeAlso:    SelRange::Describe
05187     SeeAlso:    SelRange::LayerDescription
05188 
05189 ********************************************************************************************/
05190 
05191 String_64 SelRange::ObjectDescription(DescriptionFormat Format)
05192 {
05193     String_256 Description;
05194     Node* Current = FindFirst();
05195 
05196     // Implementation
05197     if (Current != NULL)                                      // There are objects in range
05198     {                                            
05199         // Use shortended description for Menus
05200         BOOL Verbose = (Format != MENU);
05201 
05202         Node* FirstInRange = Current; 
05203 
05204         // Simon - 17/08/95. Pre Quickshapes the NodeType was the runtime class of the object
05205         // we now use description strings. This should still be quick cos most will be 
05206         // identifiable by the first char.
05207         String NodeType = Current->Describe(FALSE, Verbose); // Use singular  
05208         
05209         // --------------------------------------------------------------------------------
05210         // Determine if there is more than one type of object selected
05211 
05212         BOOL OneTypeOfObject = TRUE;                          // TRUE if range contains only
05213                                                               // one type of object. 
05214 
05215         while ((Current = FindNext(Current)) != NULL)
05216         {   
05217             if (Current->Describe(FALSE, Verbose) != NodeType)        // Range contains more than one
05218                                                               // type of object
05219             {
05220                 // Current object is not the same type as the first so we know there is more
05221                 // than one type of object.
05222                 OneTypeOfObject = FALSE;
05223                 break;
05224             }
05225         }     
05226         
05227         UINT32 NumObjectsInRange = Count();                      // Count is cached
05228         ERROR3IF(NumObjectsInRange <= 0, "No objects selected"); 
05229 
05230         if (OneTypeOfObject)
05231         {
05232             // All objects in the selection are of the same type
05233 
05234             String_32 Name;
05235             BOOL UseClassToDescribe = TRUE;
05236 
05237             NodeGroup* FirstGroup = NULL; 
05238 
05239             if (FirstInRange->GetRuntimeClass() == CC_RUNTIME_CLASS(NodeGroup))
05240             {
05241                 UseClassToDescribe = FALSE; 
05242                 // If all selected groups have the same name then the name is used in the 
05243                 // description
05244                 FirstGroup = (NodeGroup*)FirstInRange;
05245                 NodeGroup* CurrentGroup = FirstGroup;
05246                 Name = CurrentGroup->GetName(); 
05247                 if (!(Name.IsEmpty()))
05248                 {
05249                     do
05250                     {
05251                         CurrentGroup=(NodeGroup*)FindNext(CurrentGroup); 
05252                 
05253                         if (CurrentGroup != NULL)
05254                         {
05255                             if (CurrentGroup->GetName() != Name)
05256                             {
05257                                 // A group has been found with a different name. Stop the search and
05258                                 // use the class to describe the selection.
05259                                 UseClassToDescribe = TRUE; 
05260                                 break;                  
05261                             }
05262                         }
05263 
05264                     } while (CurrentGroup != NULL); 
05265                 }
05266                 else
05267                 {
05268                     UseClassToDescribe = TRUE;
05269                 }
05270             }
05271             if (UseClassToDescribe)
05272             {
05273                 Description = (FirstInRange->Describe(NumObjectsInRange>1, Verbose));                                   
05274             }
05275             else 
05276             {
05277                 // Use the group name to describe the selection
05278                 ERROR3IF(FirstGroup == NULL, "FirstGroup should be the first selected group"); 
05279                 if (NumObjectsInRange > 1)
05280                 {
05281                     Description = Pluralise(FirstGroup->GetName()); 
05282                 }
05283                 else
05284                 {
05285                     Description = FirstGroup->GetName();
05286                 }
05287             }
05288         }
05289         else
05290         {
05291             // There must be more than one type of selected objects
05292             // ERROR3IF(NumObjectsInRange <= 1, "Only a single object is in range"); 
05293             // Cos there are multiple objects of different types we describe them as 'objects'
05294             Description = String(_R(IDS_OBJECTS));  
05295         }
05296 
05297         if (Format == STATUS_BAR)
05298         {      
05299             // If the description is to be displayed on the status bar, then it needs to be prefixed 
05300             // with the number of objects in the range
05301             Description._MakeMsg( _T("#1%lu #2%S"),NumObjectsInRange, &Description); 
05302         }
05303     }
05304     else // No objects are selected
05305     {
05306         // Only show "Nothing selected:" if there are documents open.
05307 PORTNOTE("other","Removed ScreenView usage")
05308 #ifndef EXCLUDE_FROM_XARALX
05309         if (ScreenView::HowMany() > 0) Description = String_32(_R(IDS_NO_OBJECTS_SEL));
05310 #endif
05311     }                                     
05312     
05313     return (Description);
05314 }
05315 
05316 
05317 
05318 /********************************************************************************************
05319 >   String_256 SelRange::LayerDescription(DescriptionFormat nFormat)
05320 
05321     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
05322     Created:    3/1/96
05323     Inputs:     nFormat         an enum describing how this description will be used,
05324                                 eg. STATUS_BAR (anything else returns empty string).
05325     Returns:    A string containing a text description of what is selected, if anything,
05326                 on the selected layer(s).
05327     Purpose:    Builds the text displayed by the status-bar code.  Rewritten to be
05328                 internationally portable.
05329 ********************************************************************************************/
05330 
05331 String_256 SelRange::LayerDescription(DescriptionFormat nFormat)
05332 {
05333     // Check if there's nothing to do.
05334     String_256 strDescription;
05335     if (nFormat != STATUS_BAR || FindFirst() == NULL ||
05336         FindLast() == NULL) return strDescription;
05337 
05338     // Do we need to describe the selection as "inside"?
05339     BOOL fIsInside = ContainsSelectInside() && !(Count() == 1 && IS_A(FindFirst(), CaretNode));
05340 
05341     // DMc change
05342     Layer* pLastLayer = NULL;
05343 
05344     pLastLayer = (Layer*) (FindLast()->FindParent(CC_RUNTIME_CLASS(Layer)));
05345     
05346     //Graham 8/7/97: Now, get the document
05347     Document* pDocument = pLastLayer->FindDocument();
05348     if (pDocument == NULL)
05349     {
05350         // No document yet. So return an empty string
05351         return String_256("");
05352     }
05353 
05354     //Is there only one layer in the document?
05355     if (pDocument->GetNumLayers()==1)
05356     {
05357         //Yes. So we needn't put the layer name into the description string.
05358         //So, is the selection "inside"?
05359         if(fIsInside)
05360         {
05361             //Yes. So return "(inside)" as our description string
05362             return String_256(_R(IDS_SINGLELAYER_INSIDE));
05363         }
05364         else
05365         {
05366             //No. So return an empty string
05367             return String_256("");
05368         }
05369     }
05370     
05371 
05372     // Count how many layers contain select objects if more than one object is selected.
05373 //  INT32 nLayers=0;
05374     INT32 nLayersWithSelected = 1;
05375     if (Count() > 1)
05376     {
05377         // Loop over all the layers in the doc (?)
05378         Layer* pLayer = (Layer*) (FindFirst()->FindParent(CC_RUNTIME_CLASS(Layer)));
05379         while (pLayer != pLastLayer)
05380         {
05381             // Over-run?
05382             ERROR3IF(pLayer == NULL, "Couldn't find the last layer in SelRange::LayerDescription");
05383 
05384             // Determine if pLayer has any selected objects.
05385             Node* pObj = pLayer->FindFirstChild();
05386             while (pObj != NULL)
05387             {
05388                 // Is selected or has selected children?
05389                 if (pObj->IsSelected() || pObj->IsParentOfSelected())
05390                 {
05391                     // Yes, go on to next layer.
05392                     nLayersWithSelected++;
05393                     break;
05394                 }
05395 
05396                 // No, consider the next sibling.
05397                 pObj = pObj->FindNext();
05398             }
05399 
05400             // Go on to the next sibling layer.
05401             pLayer = (Layer*) (pLayer->FindNext(CC_RUNTIME_CLASS(Layer)));
05402         }
05403     }
05404 
05405     // Is there more than one layer with selected objects?
05406     if (nLayersWithSelected > 1)
05407     {
05408         // Yes, so we want a string with one of these formats:-
05409         // 'on <no. of layers> layers' or 'on <no. of layers> layers (inside)'
05410         UINT32 nID = (fIsInside) ? _R(IDS_ON_MANY_LAYERS_INSIDE)            // " on #1%d layers (inside)"
05411                                : _R(IDS_ON_MANY_LAYERS);                // " on #1%d layers"
05412         strDescription.MakeMsg(nID, (INT32) nLayersWithSelected);
05413     }
05414     else
05415     {
05416         // Only one layer with a selection.  Decide whether to use 'inside' as a prefix or suffix.
05417         BOOL fShouldSuffix = (Count() != 1);
05418 
05419         // Does the name of the layer contain the word 'layer'/'Frame' in whatever language?)
05420         BOOL fLayerReferredTo = FALSE;
05421 
05422 // RanbirR - 28/10/97 - Changed for Camelot v2, Frame/Layer integration. 
05423 
05424 #ifdef WEBSTER
05425 
05426         // Choose which format string to use.  This is the proper way to construct portable strings!
05427         static UINT32 nFormat[8] =
05428         {
05429             _R(IDS_ON_FRAME_OUTSIDE_NOREF),             // " on frame #1%S"
05430             _R(IDS_ON_FRAME_OUTSIDE_NOREF),
05431             _R(IDS_ON_FRAME_OUTSIDE_REF),               // " on #1%S"
05432             _R(IDS_ON_FRAME_OUTSIDE_REF),
05433             _R(IDS_ON_FRAME_INSIDE_NOREF_PREFIX),       // " inside on frame #1%S"
05434             _R(IDS_ON_FRAME_INSIDE_NOREF_SUFFIX),       // " on frame #1%S (inside)"
05435             _R(IDS_ON_FRAME_INSIDE_REF_PREFIX),         // " inside on #1%S"
05436             _R(IDS_ON_FRAME_INSIDE_REF_SUFFIX)          // " on #1%S (inside)"
05437         };
05438 
05439         String_256 strLayerID = pLastLayer->GetLayerID();
05440         strLayerID.toUpper();
05441         fLayerReferredTo = (strLayerID.Sub(String_64(_R(IDS_LOCAL_LAYER_NOUN))) != -1);
05442 
05443 #else
05444         static UINT32 nFormat[8];
05445 
05446         // Determine the document mode.
05447         BOOL IsFrame = pLastLayer->IsFrame();
05448 
05449         // Frame Mode.
05450         if(IsFrame)
05451         {
05452             nFormat[0] =    _R(IDS_ON_FRAME_OUTSIDE_NOREF);             // " on frame #1%S"
05453             nFormat[1] =    _R(IDS_ON_FRAME_OUTSIDE_NOREF);
05454             nFormat[2] =    _R(IDS_ON_FRAME_OUTSIDE_REF);               // " on #1%S"
05455             nFormat[3] =    _R(IDS_ON_FRAME_OUTSIDE_REF);
05456             nFormat[4] =    _R(IDS_ON_FRAME_INSIDE_NOREF_PREFIX);       // " inside on frame #1%S"
05457             nFormat[5] =    _R(IDS_ON_FRAME_INSIDE_NOREF_SUFFIX);       // " on frame #1%S (inside)"
05458             nFormat[6] =    _R(IDS_ON_FRAME_INSIDE_REF_PREFIX);         // " inside on #1%S"
05459             nFormat[7] =    _R(IDS_ON_FRAME_INSIDE_REF_SUFFIX);         // " on #1%S (inside)"
05460 
05461             // Is the Word 'frame' already included in the text name.
05462             String_256 strLayerID = pLastLayer->GetLayerID();
05463             strLayerID.toUpper();
05464             fLayerReferredTo = (strLayerID.Sub(String_64(_R(IDS_LOCAL_FRAME_NOUN))) != -1);
05465         }
05466         // Layer Mode.
05467         else
05468 
05469         {
05470             nFormat[0] =    _R(IDS_ON_LAYER_OUTSIDE_NOREF);             // " on layer #1%S"
05471             nFormat[1] =    _R(IDS_ON_LAYER_OUTSIDE_NOREF);
05472             nFormat[2] =    _R(IDS_ON_LAYER_OUTSIDE_REF);               // " on #1%S"
05473             nFormat[3] =    _R(IDS_ON_LAYER_OUTSIDE_REF);
05474             nFormat[4] =    _R(IDS_ON_LAYER_INSIDE_NOREF_PREFIX);       // " inside on layer #1%S"
05475             nFormat[5] =    _R(IDS_ON_LAYER_INSIDE_NOREF_SUFFIX);       // " on layer #1%S (inside)"
05476             nFormat[6] =    _R(IDS_ON_LAYER_INSIDE_REF_PREFIX);         // " inside on #1%S"
05477             nFormat[7] =    _R(IDS_ON_LAYER_INSIDE_REF_SUFFIX);         // " on #1%S (inside)"
05478         
05479             // Is the Word 'layer' already included in the text name.
05480             String_256 strLayerID = pLastLayer->GetLayerID();
05481             strLayerID.toUpper();
05482             fLayerReferredTo = (strLayerID.Sub(String_64(_R(IDS_LOCAL_LAYER_NOUN))) != -1);
05483         }
05484 #endif
05485         
05486         UINT32 nID = nFormat[(fIsInside << 2) | (fLayerReferredTo << 1) | fShouldSuffix];
05487 
05488         // Substitute the layer's name into the correct format string.
05489         strDescription.MakeMsg(nID, &(pLastLayer->GetLayerID()));
05490     }
05491 
05492     // OK, return the resulting description.
05493     return strDescription;
05494 }
05495 
05496 
05497 
05498 /********************************************************************************************
05499 
05500 >   static String_256 SelRange::Pluralise(String_256 Noun)
05501 
05502     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
05503     Created:    30/6/94
05504     Inputs:     Noun: The word to pluralise
05505     Outputs:    -
05506     Returns:    The plural of Noun
05507     Purpose:    Converts the Noun string to a plural
05508     Errors:     -
05509     SeeAlso:    -
05510 
05511 ********************************************************************************************/
05512 
05513 String_256 SelRange::Pluralise(String_256 Noun)
05514 {
05515     // A bit simple for now
05516     return (Noun += String_8(_R(IDS_K_RANGE_PLURAL))); 
05517 }
05518 
05519 
05520 
05521 
05522 /********************************************************************************************
05523 
05524 >   void SelRange::DescribeSelectionInStatusBar()
05525 
05526     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
05527     Created:    28/6/94
05528     Inputs:     -
05529     Outputs:    -
05530     Returns:    -
05531     Purpose:    This function should get called whenever the selection description needs 
05532                 to be displayed. The description is cached
05533 
05534     Errors:     -
05535     SeeAlso:    -
05536 
05537 ********************************************************************************************/
05538 
05539 void SelRange::DescribeSelectionInStatusBar()
05540 {
05541     String_256          strNull("");
05542     GetApplication()->UpdateStatusBarText( &strNull ); 
05543 }
05544 
05545 
05546 
05547 
05548 
05549 /********************************************************************************************
05550 
05551 >   SelRangeMessageHandler::SelRangeMessageHandler()
05552 
05553     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05554     Created:    07/04/94
05555     Inputs:     Message: The message
05556     Outputs:    -
05557     Returns:    -
05558     Purpose:    Constructs a SelRangeMessageHandler, an object which, surprisingly,
05559                 handles messages for its parent SelRange.
05560     Errors:     -
05561     SeeAlso:    MessageHandler
05562 
05563 ********************************************************************************************/
05564 
05565 SelRangeMessageHandler::SelRangeMessageHandler()
05566   : MessageHandler(CC_RUNTIME_CLASS(SelRangeMessageHandler), TRUE)
05567 {
05568 }
05569 
05570 
05571 
05572 
05573 /********************************************************************************************
05574 
05575 >   virtual MsgResult SelRangeMessageHandler::Message(Msg* Message)
05576 
05577     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
05578     Created:    08/04/94
05579     Inputs:     Message: The message
05580     Outputs:    -
05581     Returns:    -
05582     Purpose:    Process messages sent to the SelRange. 
05583     
05584                 Note that a SelChangingMsg is only broadcast at the end of an operation which 
05585                 has changed the selection.
05586 
05587     Errors:     -
05588     SeeAlso:    MessageHandler
05589 
05590 ********************************************************************************************/
05591 
05592 MsgResult SelRangeMessageHandler::Message(Msg* Message)
05593 {
05594     SelRange* Sel = GetApplication()->FindSelection();
05595 
05596     if (MESSAGE_IS_A(Message, DocChangingMsg))
05597     {
05598         DocChangingMsg *Msg = (DocChangingMsg *) Message;
05599 
05600         // ***** Fix for Alpha release 15/8/94 Markn
05601         // Put a check in for ABOUTTODIE and KILLED doc messages because we only want to update
05602         // the selection once when the document dies (i.e. when ~Document() is called)
05603         // This solution makes hugh asumptions on the messages that are generated on document
05604         // destruction, and could go wrong in the future.
05605         // *****
05606         // Fixed again by Jason, 5/1/95
05607         // This ABSOLUTELY UNEQUIVOCALLY MUST flush the cache!
05608         // So if we don't want the msg broadcast, we just pass FALSE to Update() 
05609         // *****
05610         if (Sel != NULL)
05611         {
05612             if (Msg->State != DocChangingMsg::ABOUTTODIE &&
05613                 Msg->State != DocChangingMsg::KILLED )
05614             {
05615                 // Ensure selection cache is flushed.
05616                 // The TRUE param. makes this broadcast a SelChangingMsg to inform the world
05617                 
05618                 // But don't bother doing a flush if it's only a title-changed notification
05619                 if (Msg->State != DocChangingMsg::TITLECHANGED)
05620                     Sel->Update(TRUE);
05621             }
05622             else
05623             {
05624                 // Ensure the selection cache is flushed, but don't broadcast a msg!
05625                 Sel->Update(FALSE);
05626             }
05627         }
05628     }
05629     else if (MESSAGE_IS_A(Message, OpMsg))
05630     {
05631         if (((OpMsg*)Message)->MsgType == OpMsg::END)
05632         {
05633             // Make sure that any delayed message broadcast is now sent out.
05634             // This is used to allow Ops to make multiple changes to the selection,
05635             // yet only cause one SelChangingMsg broadcast (when they End).
05636             GetApplication()->FindSelection()->BroadcastAnyPendingMessages();
05637         }
05638     }
05639     else if (MESSAGE_IS_A(Message, ToolsCurrentAttrGroupChangedMsg) ||
05640              MESSAGE_IS_A(Message, CurrentAttrChangedMsg) ||
05641              MESSAGE_IS_A(Message, SelChangingMsg)
05642             )
05643     {
05644         // This convenient message gets sent out whenever a user needs to 
05645         // reflect a change in the common attributes. Usually the user will
05646         // call Range::FindCommonAttribute\s is receipt of this message.
05647         // The FindCommonAttribute fns return Current Attributes in certain
05648         // cases which is why the message is broadcast when there is a 
05649         // change in these as well.
05650         
05651         Sel->AttrsHaveChanged(); // The CommonAttributeCache is no longer valid. 
05652         BROADCAST_TO_ALL(CommonAttrsChangedMsg); 
05653     }
05654     /**********
05655         Taken out because when the selected spread changes, NodeRenderableInk::DeselectAll() is called
05656         which will generate a selection changed message if it needs to.
05657            Markn reporting on behalf of the Alpha release bug fixing team, 16/8/94
05658            And now back to the studio
05659 
05660     else if (MESSAGE_IS_A(Message, SpreadMsg))
05661     {
05662         SpreadMsg*  pSpreadMsg = (SpreadMsg*) Message;
05663 
05664         switch (pSpreadMsg->Reason)
05665         {
05666             case (SpreadMsg::SELCHANGED) :
05667                 GetApplication()->FindSelection()->Update(TRUE);
05668                 break;
05669         }
05670     }
05671     */
05672 
05673 
05674 
05675     return OK; 
05676 }      
05677 
05678 
05679 
05680 /********************************************************************************************
05681 
05682 >   BOOL SelRange::FindObject(CCRuntimeClass* pClass, Node** pOutput = NULL)
05683 
05684     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
05685     Created:    19/01/95
05686 
05687     Inputs:     pClass  = A runtime class
05688                 pOutput = A pointer to a pointer to a node (NULL if none)
05689 
05690     Outputs:    pOutput (if non-NULL) is updated to point to the first node of type
05691                 pClass in the selection. If there is no object of this type, this
05692                 is returned containing NULL.
05693 
05694     Returns:    TRUE if there is an object of this type in the selection
05695                 FALSE if not
05696 
05697     Purpose:    Scans the selection hunting for a selected object of type pClass. If an
05698                 object is found, the function will return true, and set pOutput to point
05699                 at the object (if it was not NULL on entry).
05700 
05701 ********************************************************************************************/
05702 
05703 BOOL SelRange::FindObject(CCRuntimeClass* p, Node** pOutput)
05704 {
05705     BOOL Sel = FALSE;
05706 
05707     if (pOutput != NULL)        // Set a reasonable return value if we fail
05708         *pOutput = NULL;
05709 
05710     Node *pNode = FindFirst();
05711     while ((pNode!=NULL) && (!Sel))
05712     {
05713         Sel = (pNode->GetRuntimeClass() == p);
05714         if (!Sel)
05715             pNode = FindNext(pNode);
05716     }
05717 
05718     if (Sel && pOutput != NULL)
05719         *pOutput = pNode;
05720 
05721     return Sel;
05722 }
05723 
05724 
05725 
05726 /********************************************************************************************
05727 >   void SelRange::SetGag(BOOL NewState)
05728 
05729     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
05730     Created:    20/05/95
05731     Inputs:     NewState - TRUE to gag the SelRange - FALSE to ungag it 
05732     Outputs:    -
05733     Returns:    -
05734     Purpose:    Allows operations to gag the selrange in order to stop it sending SelChanged
05735                 messages.  THIS IS VERY SCARY!  The only op that does this it the moment is
05736                 when the text caret is moving left/right.  Whilst the cursor key is down the
05737                 SelRange is gagged so the cursor movement is not slowed by constant SelChanged
05738                 messages.
05739 ********************************************************************************************/
05740 void SelRange::SetGag(BOOL NewState)
05741 {
05742     IsGagged = NewState;                                                                      
05743 }
05744 
05745 
05746 
05747 /********************************************************************************************
05748 >   BOOL SelRange::IsSelRangeGagged()
05749 
05750     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
05751     Created:    20/05/95
05752     Inputs:     -
05753     Outputs:    -
05754     Returns:    TRUE if the SelRange is gagged, FALSE if it isn't
05755     Purpose:    For getting the current gag state of the SelRange.
05756 ********************************************************************************************/
05757 BOOL SelRange::IsSelRangeGagged()
05758 {
05759     return IsGagged;                                                                      
05760 }
05761 
05762 
05763 
05764 /********************************************************************************************
05765 >   Node* SelRange::FindFirstObject(CCRuntimeClass* pClass)
05766 
05767     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
05768     Created:    20/05/95
05769     Inputs:     pClass  = A runtime class
05770     Outputs:    -
05771     Returns:    A pointer to the first selected object that is the class specified.
05772                 NULL if none found
05773     Purpose:    Finds the first selected object of the required class.  For finding derived
05774                 classes use FindFirstDerivedObject
05775     SeeAlso:    SelRange::FindObject, SelRange::FindFirstDerivedObject
05776 ********************************************************************************************/
05777 Node* SelRange::FindFirstObject(CCRuntimeClass* pClass)
05778 {
05779     Node* pNode = FindFirst();
05780 
05781     while ((pNode != NULL) && (pNode->GetRuntimeClass() != pClass))  
05782     {
05783         pNode = FindNext(pNode);
05784     }
05785 
05786     return pNode;
05787 }
05788 
05789 
05790 
05791 /********************************************************************************************
05792 >   Node* SelRange::FindNextObject(CCRuntimeClass* pClass, Node* Previous)
05793 
05794     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
05795     Created:    20/05/95
05796     Inputs:     pClass - A runtime class
05797                 Previous - the node to scan from
05798     Outputs:    -
05799     Returns:    A pointer to the next selected object that is the class specified.
05800                 NULL if none found
05801     Purpose:    Finds the next selected object of the required class.  For finding derived
05802                 classes use FindNextDerivedObject
05803     SeeAlso:    SelRange::FindNext, SelRange::FindNextDerivedObject
05804 ********************************************************************************************/
05805 Node* SelRange::FindNextObject(CCRuntimeClass* pClass, Node* Previous)
05806 {
05807     ERROR3IF(Previous == NULL, "Previous node pointer was NULL");
05808 
05809     if (Previous != NULL)
05810         Previous = FindNext(Previous);
05811 
05812     while ((Previous != NULL) && (Previous->GetRuntimeClass() != pClass))
05813     {
05814         Previous = FindNext(Previous);
05815     }
05816 
05817     return Previous;
05818 }
05819 
05820 
05821 
05822 /********************************************************************************************
05823 >   Node* SelRange::FindFirstDerivedObject(CCRuntimeClass* pClass)
05824 
05825     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
05826     Created:    20/05/95
05827     Inputs:     pClass  = A runtime class
05828     Outputs:    -
05829     Returns:    A pointer to the first selected object derived from the class specified.
05830                 NULL if none found
05831     Purpose:    Finds the first selected object of the required derived class.  For finding
05832                 extact class matches uses FindFirstObject
05833     SeeAlso:    SelRange::FindObject, SelRange::FindFirstObject
05834 ********************************************************************************************/
05835 Node* SelRange::FindFirstDerivedObject(CCRuntimeClass* pClass)
05836 {
05837     Node* pNode = FindFirst();
05838 
05839     while ((pNode != NULL) && !pNode->IsKindOf(pClass))  
05840     {
05841         pNode = FindNext(pNode);
05842     }
05843 
05844     return pNode;
05845 }
05846 
05847 
05848 
05849 /********************************************************************************************
05850 >   Node* SelRange::FindNextDerivedObject(CCRuntimeClass* pClass, Node* Previous)
05851 
05852     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
05853     Created:    20/05/95
05854     Inputs:     pClass - A runtime class
05855                 Previous - the node to scan from
05856     Outputs:    -
05857     Returns:    A pointer to the next selected object that is derived from the class specified.
05858                 NULL if none found
05859     Purpose:    Finds the next selected object of the required derived class.  For finding
05860                 exact class matches use FindNextObject
05861     SeeAlso:    SelRange::FindNext, SelRange::FindNextObject 
05862 ********************************************************************************************/
05863 
05864 Node* SelRange::FindNextDerivedObject(CCRuntimeClass* pClass, Node* Previous)
05865 {
05866     ERROR3IF(Previous == NULL, "Previous node pointer was NULL");
05867 
05868     if (Previous != NULL)
05869         Previous = FindNext(Previous);
05870 
05871     while ((Previous != NULL) && !Previous->IsKindOf(pClass))
05872     {
05873         Previous = FindNext(Previous);
05874     }
05875 
05876     return Previous;
05877 }
05878 
05879 
05880 
05881 /********************************************************************************************
05882 
05883     void SelRange::AttrsHaveChanged()
05884 
05885     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
05886     Created:    12/12/94
05887     Purpose:    This function gets called to inform the range that attributes applied to its 
05888                 objects have, or may have changed. It deletes the Common attribute cache if 
05889                 one exists.
05890     SeeAlso:    Range::AttrsHaveChanged
05891 
05892 ********************************************************************************************/
05893 
05894 void SelRange::AttrsHaveChanged()
05895 {
05896 // Removed by Jason, 23/10/95
05897 // After discussion with Simon, this has been removed, as we reckon it's completely
05898 // rampant. The selection has not been changed, but the attrs of the selected item(s)
05899 // have changed. Perhaps there should be a notification broadcast, but it should NOT
05900 // be the SelChangingMsg::SELECTIONCHANGED message that this flag triggers!
05901 // In fact, all the current callers (in AttrMgr.cpp) send relevant notification
05902 // broadcasts around, so setting us up for another broadcast is silly.
05903 //  InformedSelChanged  = FALSE; 
05904 
05905 #if !defined(EXCLUDE_FROM_RALPH)
05906     // Inform the DialogBars that the system state has changed so that they can refresh
05907     // themselves
05908     DialogBarOp::SetSystemStateChanged(); 
05909 #endif
05910 
05911     if (m_pEffectsStack)
05912     {
05913         m_pEffectsStack->AttrsHaveChanged();
05914     }
05915 
05916     if (m_pEffectClassRange)
05917         m_pEffectClassRange->AttrsHaveChanged();
05918 
05919     Range::AttrsHaveChanged(); // Call base class fn
05920 }
05921 
05922 
05923 
05924 /********************************************************************************************
05925 
05926 >   BOOL SelRange::MakePartialSelectionWhole(BOOL TellWorld = TRUE, BOOL bTextOnly = FALSE, BOOL bPathInText = FALSE)
05927 
05928     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> (from Karim MacDonald)
05929     Created:    21/04/2005 (25 August 2000)
05930 
05931     Inputs:     TellWorld   Whether or not to tell everyone if the selection changes.
05932                             Note that if this function is called from within an Op, this flag
05933                             can be set to FALSE as the selection will be updated on 
05934                             completion of the Op.
05935 
05936     Outputs:    If any text characters are selected, then their parent text stories will be
05937                 selected instead.
05938 
05939     Returns:    TRUE if successful, FALSE otherwise.
05940 
05941     Purpose:    Run through the selection and make sure that no children of TextStories
05942                 are selected - only whole TextStories themselves.
05943 
05944 ********************************************************************************************/
05945 BOOL SelRange::MakePartialSelectionWhole(BOOL TellWorld, BOOL bTextOnly, BOOL bPathInText)
05946 {
05947     BOOL bChangedSelection = FALSE;
05948     Node* pNode = FindFirst();
05949 
05950     while (pNode)
05951     {
05952         // Push any text sub-selection up to the selected story
05953         if (pNode->IsAnAbstractTextChar())
05954         {
05955             ((NodeRenderableInk*)pNode)->DeSelect(TRUE);
05956             TextStory* pStory = ((AbstractTextChar*)pNode)->FindParentStory();
05957             if (pStory != NULL)
05958             {
05959                 pStory->Select(TRUE);
05960                 bChangedSelection = TRUE;
05961             }
05962         }
05963 
05964         // Push subselection of fit-text-to-curve node up to the selected story
05965         if (bPathInText && pNode->IsNodePath() && pNode->FindParent()->IsKindOf(CC_RUNTIME_CLASS(TextStory)))
05966         {
05967             ((TextStory*)pNode->FindParent())->Select(TRUE);
05968             bChangedSelection = TRUE;
05969         }
05970 
05971         // Push subselection of fit-text-to-curve node up to the selected story
05972         if (bPathInText && pNode->IsNodePath() && pNode->FindParent()->IsKindOf(CC_RUNTIME_CLASS(TextStory)))
05973         {
05974             ((TextStory*)pNode->FindParent())->Select(TRUE);
05975             bChangedSelection = TRUE;
05976         }
05977 
05978         // Push subselection of fit-text-to-curve node up to the selected story
05979         if (bPathInText && pNode->IsNodePath() && pNode->FindParent()->IsKindOf(CC_RUNTIME_CLASS(TextStory)))
05980         {
05981             ((TextStory*)pNode->FindParent())->Select(TRUE);
05982             bChangedSelection = TRUE;
05983         }
05984 
05985         // Push subselection of fit-text-to-curve node up to the selected story
05986         if (bPathInText && pNode->IsNodePath() && pNode->FindParent()->IsKindOf(CC_RUNTIME_CLASS(TextStory)))
05987         {
05988             ((TextStory*)pNode->FindParent())->Select(TRUE);
05989             bChangedSelection = TRUE;
05990         }
05991 
05992         if (!bTextOnly)
05993         {
05994             // Do the same thing for sub-selection inside controller nodes
05995             NodeCompound* pController = pNode->GetParentController();
05996             if (pController && pNode->NeedsParent(pController))
05997             {
05998                 ((NodeRenderableInk*)pNode)->DeSelect(TRUE);
05999                 NodeRenderableInk* pInkNode = pController->GetInkNodeFromController();
06000                 if (pInkNode)
06001                     pInkNode->Select(TRUE);
06002                 bChangedSelection = TRUE;
06003             }
06004         }
06005 
06006         pNode = FindNext(pNode);
06007     }
06008 
06009     if (bChangedSelection)
06010         Update(TellWorld);
06011 
06012     return TRUE;
06013 }
06014 
06015 
06016 
06017 // -------------------------------------------------------------------------------------------
06018 // CommonAttrSet methods
06019 
06020 /********************************************************************************************
06021 
06022 >   BOOL CommonAttrSet::AddTypeToSet(CCRuntimeClass* AttrType)
06023 
06024     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06025     Created:    13/9/95
06026     Inputs:     AttrType: The type to add to the set
06027     Returns:    FALSE if we run out of memory
06028     Purpose:    Call this function to build up a subset of attribute types to find common
06029                 attribute values for. It creates an CommonAttributeItem and adds it to the set.
06030 
06031     Errors:     In a debug build a check is made to ensure that AttrType does not initially exist
06032                 in the set. 
06033 
06034     SeeAlso:    Range::FindCommonAttributes
06035 
06036 ********************************************************************************************/
06037    
06038 BOOL CommonAttrSet::AddTypeToSet(CCRuntimeClass* AttrType)
06039 {
06040     ERROR3IF(FindAttrItem(AttrType), "Trying to add a duplicate item to a CommonAttrSet"); 
06041     CommonAttributeItem* CommonAttrItem;
06042     CommonAttrItem = new CommonAttributeItem(AttrType);
06043     if (CommonAttrItem == NULL)
06044     {
06045         // Oh no we've run out of memory
06046         return FALSE; 
06047     }
06048     AddTail(CommonAttrItem); 
06049     return TRUE;
06050 }
06051 
06052 /********************************************************************************************
06053 
06054 >   BOOL CommonAttrSet::AddAllTypesToSet()
06055 
06056     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06057     Created:    13/9/95
06058     Returns:    FALSE if we run out of memory. In this situation the CommonAttrSet is left empty
06059     Purpose:    This function adds all attribute types to the CommonAttrSet. You will probably
06060                 never need to call this function because Range::FindCommonAttributes will
06061                 call it for you if you pass in an empty CommonAttrSet.
06062     Errors:     On entry to this function the CommonAttrSet must be empty
06063     SeeAlso:    CommonAttrSet::AddTypeToSet
06064     SeeAlso:    Range::FindCommonAttributes
06065 
06066 ********************************************************************************************/
06067 
06068 BOOL CommonAttrSet::AddAllTypesToSet()
06069 {
06070     // If the CommonAttributeSet is not empty on entry then something is probably not right !
06071     ERROR3IF(!IsEmpty(), 
06072              "CommonAttributeSet not empty on entry to AddAllTypesToSet");
06073 
06074     // Obtain a list of all required attributes
06075     List* ReqdAttribList = ObjectRegistry::GetRequiredAttribs(NULL);
06076     
06077     // Now create a CommonAttributeItem for each item in the list
06078     NodeAttributeClassItem* ReqdAttrib = (NodeAttributeClassItem*)ReqdAttribList->GetHead();
06079  
06080     while(ReqdAttrib != NULL)
06081     {
06082         if (!(ReqdAttrib->AttributeClass == CC_RUNTIME_CLASS(AttrQuality)))
06083         {
06084             if (!AddTypeToSet(ReqdAttrib->AttributeClass))
06085             {
06086                 DeleteAll(); 
06087                 return FALSE;
06088             }
06089         }
06090         ReqdAttrib = (NodeAttributeClassItem*)ReqdAttribList->GetNext(ReqdAttrib);
06091     }
06092     return TRUE; // Success
06093 }
06094 
06095 /********************************************************************************************
06096 
06097 >   CommonAttributeItem* CommonAttrSet::FindAttrItem(CCRuntimeClass* AttrType)
06098 
06099     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06100     Created:    13/9/95
06101     Inputs:     AttrType: The attribute type to search for
06102     Returns:    A pointer to the CommonAttributeItem with type AttrType.
06103                 NULL if the item does not exist.
06104     Purpose:    To find an item in the CommonAttrSet
06105 
06106 ********************************************************************************************/
06107 
06108 CommonAttributeItem* CommonAttrSet::FindAttrItem(CCRuntimeClass* AttrType)
06109 {
06110     CommonAttributeItem* pAttrItem;
06111     pAttrItem = (CommonAttributeItem*)(GetHead()); 
06112     while (pAttrItem)
06113     {
06114         if (pAttrItem->AttrType == AttrType)
06115             break;
06116 
06117         pAttrItem = (CommonAttributeItem*)(GetNext(pAttrItem));     
06118     }
06119     return pAttrItem;    
06120 }
06121 
06122 /********************************************************************************************
06123 
06124 >   BOOL CommonAttrSet::FindAttrDetails(CCRuntimeClass* AttrType, 
06125                                      NodeAttribute** pAttr, 
06126                                      Range::CommonAttribResult* Status)
06127 
06128     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06129     Created:    13/9/95
06130     Inputs:     AttrType: The attribute type to search for
06131     
06132     Outputs:    See Range::FindCommonAttributes for the semantics of these output values
06133     
06134                 pAttr:  Attribute pointer (or NULL)
06135                 Status: The attribute's status
06136 
06137     Returns:    FALSE if the attribute does not exist
06138     Purpose:    To find an item in the CommonAttrSet
06139     SeeAlso:    Range::FindCommonAttributes
06140 
06141 ********************************************************************************************/
06142 
06143 BOOL CommonAttrSet::FindAttrDetails(CCRuntimeClass* AttrType, 
06144                                     NodeAttribute** pAttr, 
06145                                     Range::CommonAttribResult* Status)
06146 {
06147     CommonAttributeItem* pAttrItem;
06148     pAttrItem = (CommonAttributeItem*)(GetHead()); 
06149     while (pAttrItem)
06150     {
06151         if (pAttrItem->AttrType == AttrType)
06152         {
06153             *pAttr = pAttrItem->pAttr;
06154             *Status = pAttrItem->Status;
06155             return TRUE; 
06156         }
06157         pAttrItem = (CommonAttributeItem*)(GetNext(pAttrItem));     
06158     }
06159     return FALSE;    
06160 }
06161 
06162 
06163 /********************************************************************************************
06164 
06165 >   BOOL CommonAttrSet::IsAnyItemCommon()
06166 
06167     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06168     Created:    15/9/95
06169     Purpose:    Returns TRUE if any item in the CommonAttrSet is a common attribute. 
06170     SeeAlso:    Range::FindCommonAttributes
06171 
06172 ********************************************************************************************/
06173 
06174 
06175 BOOL CommonAttrSet::IsAnyItemCommon()
06176 {
06177     // Scan the set
06178     CommonAttributeItem* pAttrItem;
06179     pAttrItem = (CommonAttributeItem*)(GetHead());
06180     while (pAttrItem)
06181     {
06182         if (pAttrItem->Status == Range::ATTR_COMMON)
06183             return TRUE;
06184         pAttrItem = (CommonAttributeItem*)(GetNext(pAttrItem));     
06185     }
06186     return FALSE;
06187 } 
06188 
06189 
06190 /********************************************************************************************
06191 
06192 >   void CommonAttrSet::ClearResults();  
06193 
06194     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06195     Created:    14/9/95
06196     Purpose:    This function initialises the CommonAttrSet ready for finding common attributes
06197                 Each CommonAttributeItem in the set has its Status set to ATTR_NONE and its 
06198                 pAttr set to NULL. You will probably never need to call this function as
06199                 Range::FindCommonAttributes makes a call to it prior to looking for
06200                 common attributes. 
06201 
06202     Errors:     The CommonAttrSet must not be empty
06203                 The AttrType field in each CommonAttributeItem must be a valid attribute type
06204     SeeAlso:    Range::FindCommonAttributes
06205 
06206 ********************************************************************************************/
06207 
06208 void CommonAttrSet::ClearResults()
06209 {
06210     ERROR3IF(IsEmpty(), "CommonAttrSet::ClearResults called on an empty CommonAttrSet");
06211     CommonAttributeItem* pAttrItem;
06212     pAttrItem = (CommonAttributeItem*)(GetHead()); 
06213     while (pAttrItem)
06214     {
06215         ERROR3IF(!pAttrItem->AttrType, "CommonAttrSet contains invalid attr type"); 
06216         pAttrItem->ClearResults(); 
06217         pAttrItem = (CommonAttributeItem*)(GetNext(pAttrItem));     
06218     }
06219 }
06220 
06221 
06222 /********************************************************************************************
06223 
06224 >   INT32 CommonAttrSet::UpdateKnownValues(CommonAttrSet* pCache); 
06225 
06226     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06227     Created:    14/9/95
06228     Inputs:     pCache: A pointer to CommonAttrSet which contains known Status and pAttr values.
06229     Outputs:    -
06230     Returns:    The number of items which have not been updated (n). If this number is 0 then 
06231                 all items in the set have values. This number is  useful because all
06232                 updated items get moved to the end of the list, and so the first n items in 
06233                 the list are those that you need to find values for. This makes the list 
06234                 scanning much more efficient.
06235 
06236     Purpose:    This function is provided so that a CommonAttrSet can be updated from a cache
06237                 of known values. Each AttrType in this set which has values in the pCache is
06238                 updated. 
06239 
06240                 It is called by Range::FindCommonAttributes to reduce the number of attribute
06241                 types to find common attributes for. 
06242 
06243     SeeAlso:    Range::FindCommonAttributes
06244 
06245 ********************************************************************************************/
06246 
06247 INT32 CommonAttrSet::UpdateKnownValues(CommonAttrSet* pCache)
06248 {
06249     CommonAttributeItem* pMyItem;
06250     CommonAttributeItem* pCachedItem;
06251     CommonAttributeItem* pFirstKnownItem = NULL;
06252     INT32 UncachedItems = GetCount();
06253 
06254     // Scan thru the cache
06255     pCachedItem = (CommonAttributeItem*)(pCache->GetHead());
06256 
06257     while (pCachedItem && UncachedItems)
06258     {
06259         pMyItem = (CommonAttributeItem*)GetHead(); 
06260         while (pMyItem != pFirstKnownItem)
06261         {
06262             if (pMyItem->AttrType == pCachedItem->AttrType)
06263             {
06264                 // Item is cached 
06265                 pMyItem->pAttr = pCachedItem->pAttr;
06266                 pMyItem->Status = pCachedItem->Status;
06267                 UncachedItems--;
06268                 // Move the item to the tail of the list, so that we can stop when we
06269                 // hit the first known item.
06270                 RemoveItem(pMyItem); 
06271                 AddTail(pMyItem);
06272                 if (!pFirstKnownItem)
06273                     pFirstKnownItem = pMyItem;
06274                 break;
06275             }
06276             pMyItem = (CommonAttributeItem*)(GetNext(pMyItem));     
06277         }
06278         pCachedItem = (CommonAttributeItem*)(pCache->GetNext(pCachedItem));     
06279     }
06280     return UncachedItems;
06281 }  
06282 
06283 /********************************************************************************************
06284 
06285     BOOL CommonAttrSet::BuildAttrDetailsMap(NodeRenderableInk* CompoundObject, 
06286                                             INT32 FirstN, 
06287                                             CCAttrMap* pAppliedAttrMap, 
06288                                             CMapPtrToWord* pCompoundAttrMap)
06289 
06290     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06291     Created:    27/9/95
06292     Inputs:     CompoundObject: The compound we wish to generate the map for
06293 
06294                 FirstN:         Specifies how many attribute types to add to the map.
06295                                 Useful because as we find CommonAttributeItem values we
06296                                 move them to the end of the list.
06297 
06298                 pAppliedAttrMap: A map from an attribute type to the attribute value which is
06299                                  'applied' to the CompoundObject. Only AttrTypes which have
06300                                  default attribute values are added to the AttrDetailsMap
06301 
06302     Outputs:    A map from an AttrType to an ObjectAttrUsage value
06303     Returns:    FALSE if we run out of memory. All items in the map are deleted.
06304     Purpose:    This method is used by FindCommonAttributes to determine if a default attribute
06305                 is common to a CompoundObject.
06306 
06307                 The routine creates a map from the FirstN items in the CommonAttrSet,  to 
06308                 ObjectAttrUsage values. 
06309                 
06310                 For Child read Child, or Child of Child etc.
06311 
06312                 If an AttrType maps to a REQUIRED value, this means that at least
06313                 one Child of the compound requires the attribute, but an attribute of this
06314                 type does not appear as a Child of the compound.
06315 
06316                 If it maps to a FOUND value then this means that the attribute 
06317                 is required by at least one child object and has been found as a Child
06318 
06319                 if it maps to a NOT_REQUIRED value then this means that the attribute
06320                 is not required by any child of the compound.
06321     Errors:     -
06322     SeeAlso:    -
06323 
06324 ********************************************************************************************/
06325 
06326 BOOL CommonAttrSet::BuildAttrDetailsMap(NodeRenderableInk* Object, 
06327                                    INT32 FirstN,
06328                                    CCAttrMap* pAppliedAttrMap, 
06329                                    CMapPtrToWord* pCompoundAttrMap)
06330 {
06331   
06332     // This will probably indicate a bug
06333     ERROR3IF(FirstN == 0, "Called CommonAttrSet::BuildAttrDetailsMap with nothing to do");
06334      
06335     // I can't imagine why anyone would want to call this fn on a non-compound object
06336     ERROR3IF(!(Object->IsCompound()), "BuildAttrDetailsMap called on a non-compound"); 
06337 
06338     if (FirstN == 0)
06339         return TRUE;  // Nothing to do
06340 
06341     // We want to scan this list fast, and we don't want to change the order of items 
06342     // within it. So create a ListIndex
06343     List IdxIntoAttrSet;
06344     if (!CreateIndex(&IdxIntoAttrSet))
06345     {
06346         return FALSE; 
06347     }
06348     
06349     // Place required attribute types into the map, and remove index items we are not interested
06350     // in.
06351     ListItemIdx* ppAttrItem = (ListItemIdx*)(IdxIntoAttrSet.GetHead());
06352     CommonAttributeItem* pAttrItem = (CommonAttributeItem*)GetHead();
06353     ListItemIdx* ppNxtAttrItem; // Hand over hand
06354     INT32 Count = 0; 
06355     while(pAttrItem && ppAttrItem)
06356     {
06357         ppNxtAttrItem = (ListItemIdx*)(IdxIntoAttrSet.GetNext(ppAttrItem)); 
06358 
06359         if  (Count != FirstN) // We are interested in this item, add it to the map
06360         {
06361             // Is the applied AttrType attribute a default ?
06362             void* pvAttrValue = NULL;
06363             NodeAttribute* pAttrValue;
06364             pAppliedAttrMap->Lookup(pAttrItem->AttrType, pvAttrValue);
06365             pAttrValue = (NodeAttribute*)pvAttrValue;
06366             if (pAttrValue->IsADefaultAttr()) 
06367             {
06368                 (*pCompoundAttrMap)[pAttrItem->AttrType] = (WORD)CommonAttrSet::NOT_REQUIRED; 
06369             }
06370             else
06371             {
06372                 // Remove item from index - we are not interested in non-defaults
06373                 delete(IdxIntoAttrSet.RemoveItem(ppAttrItem));              
06374             }
06375             pAttrItem = (CommonAttributeItem*)GetNext(pAttrItem);
06376             Count++; 
06377         }
06378         else  // We are not interested in this item remove it from our index
06379         {
06380             delete(IdxIntoAttrSet.RemoveItem(ppAttrItem));              
06381         }
06382         ppAttrItem = ppNxtAttrItem;
06383     }
06384 
06385     // Scan the compound depth first
06386     Node* Current = Object->FindFirstDepthFirst();
06387     
06388     INT32 UnknownValues = IdxIntoAttrSet.GetCount();  // When this equals 0 we can stop
06389     
06390     while((Current != Object) && UnknownValues) // stop when we hit the compound
06391     {
06392         if (Current->IsAnObject())
06393         {
06394             ppAttrItem = (ListItemIdx*)(IdxIntoAttrSet.GetHead());
06395             while (ppAttrItem)
06396             {
06397                 ppNxtAttrItem = (ListItemIdx*)(IdxIntoAttrSet.GetNext(ppAttrItem)); 
06398     
06399                 CCRuntimeClass* AttrType = ((CommonAttributeItem*)(ppAttrItem->pItem))->AttrType; 
06400                 if (((NodeRenderableInk*)Current)->
06401                      RequiresAttrib(AttrType))
06402                 {
06403                     CommonAttrSet::ObjectAttrUsage AttrUsage;
06404                     CMapPtrToWord::iterator iter = pCompoundAttrMap->find( AttrType );
06405                     if( pCompoundAttrMap->end() != iter )
06406                     {
06407                         AttrUsage = (CommonAttrSet::ObjectAttrUsage)iter->second;
06408                         if( AttrUsage != CommonAttrSet::FOUND ) 
06409                         {
06410                             (*pCompoundAttrMap)[AttrType] = (WORD)CommonAttrSet::REQUIRED;
06411                         }
06412                     }
06413 
06414                     // We don't need to consider this item again, so remove it from the
06415                     // index. 
06416                     delete(IdxIntoAttrSet.RemoveItem(ppAttrItem)); 
06417                 }
06418                 ppAttrItem = ppNxtAttrItem;
06419             }
06420 
06421         }
06422         else if (Current->IsAnAttribute())
06423         {
06424             CCRuntimeClass* AttrType = ((NodeAttribute*)Current)->GetAttributeType(); 
06425 
06426             CommonAttrSet::ObjectAttrUsage AttrUsage;
06427             CMapPtrToWord::iterator iter = pCompoundAttrMap->find( AttrType );
06428             if( pCompoundAttrMap->end() != iter )
06429             {
06430                 AttrUsage = (CommonAttrSet::ObjectAttrUsage)iter->second;
06431             
06432                 if (AttrUsage != CommonAttrSet::FOUND)  // Has not been found before !
06433                 {
06434                     (*pCompoundAttrMap)[AttrType] = (WORD)CommonAttrSet::FOUND;
06435                     UnknownValues--; // This AttrUsage value is not going to change
06436                 }
06437             } // else it's an attribute we're not interested in !   
06438         }
06439         Current = Current->FindNextDepthFirst(Object);      
06440     }
06441 
06442     IdxIntoAttrSet.DeleteAll(); // Tidyup
06443 
06444     return TRUE;
06445 } 
06446 
06447 
06448 
06449 #if 0
06450 
06451 /*
06452     JCF says: this has been rewritten so that the strings it constructs can be
06453               easily trasnslated.
06454 */
06455 
06456 /********************************************************************************************
06457 
06458 >   String_64 SelRange::LayerDescription(DescriptionFormat Format)
06459 
06460     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06461     Created:    30/6/94
06462     Inputs:     Format: The format of the description
06463     Outputs:    -
06464     Returns:    -
06465     Purpose:    Returns a string describing the layer part of the selection Description.
06466     Errors:     -
06467     Scope:      private
06468     SeeAlso:    SelRange::Describe
06469     SeeAlso:    SelRange::ObjectDescription
06470 
06471 ********************************************************************************************/
06472 
06473 String_64 SelRange::LayerDescription(DescriptionFormat Format)
06474 {
06475     String_64 Description;
06476 #if !defined(EXCLUDE_FROM_RALPH) && !defined(EXCLUDE_FROM_XARALX)
06477 
06478     if ((Format == STATUS_BAR) && (FindFirst() != NULL))
06479     {
06480         // Determine if the selection contains any 'inside' objects
06481         BOOL SelInside = ContainsSelectInside();
06482 
06483         // If the selected object is the caret, then we don't want to display the 'inside' part of
06484         // the description.
06485         BOOL CaretSelected = ((Count()==1) && (IS_A((FindFirst()), CaretNode)));
06486 
06487         // InsideDesc will be set to an empty string or the word 'inside'
06488         String_16 InsideDesc;
06489         String_16 InsideBracketDesc; // Same as InsideDesc except enclosed in backets - "('inside')"
06490         if (SelInside && !CaretSelected)
06491         {
06492             InsideDesc._MakeMsg(" #1%S",&String_16(_R(IDS_INSIDE)));
06493             InsideBracketDesc._MakeMsg("(#1%S)",&String_16(_R(IDS_INSIDE))); 
06494         }
06495     
06496         UINT32 NumLayersWithSel = 1;                          // Number of layers with selected
06497                                                               // objects.
06498 
06499         Layer* LastLayer = (Layer*) FindLast()->FindParent(CC_RUNTIME_CLASS(Layer)); 
06500 
06501         // ---------------------------------------------------------------------------------
06502         // Determine the number of layers containing selected objects
06503     
06504         if (Count() > 1)
06505         {
06506             Layer* CurrentLayer = (Layer*)(FindFirst()->FindParent(CC_RUNTIME_CLASS(Layer)));
06507             Node* CurrentObj;
06508             while (CurrentLayer != LastLayer) 
06509             {
06510                 ERROR3IF(CurrentLayer == NULL, "Could not find last layer"); 
06511                 // Determine if the current layer has any selected objects IsParentOfSelected cannot
06512                 // be called on a layer at the moment.
06513                 CurrentObj = CurrentLayer->FindFirstChild();
06514                 while(CurrentObj != NULL)
06515                 {
06516                     if (CurrentObj->IsSelected() || CurrentObj->IsParentOfSelected())
06517                     {
06518                         NumLayersWithSel++;     
06519                         break;
06520                     }
06521                     CurrentObj = CurrentObj->FindNext();
06522                 }
06523 
06524                 CurrentLayer =  (Layer*)(CurrentLayer->FindNext(CC_RUNTIME_CLASS(Layer)));
06525             } 
06526         }
06527         if (NumLayersWithSel > 1)
06528         {
06529             // The format is 'On <NumLayers> Layers' with an optional ('inside') string if there is at 
06530             // least one object selected inside.
06531             Description._MakeMsg(" #1%S #2%lu #3%S #4%S",
06532                       &String_8(_R(IDS_ON)),NumLayersWithSel,&String_8(_R(IDS_LAYERS)),&InsideBracketDesc); 
06533 
06534         }
06535         else 
06536         {
06537             // Inside handling
06538             String_16 Prefix;
06539             String_16 Suffix; 
06540  
06541             if (Count() == 1)
06542             {
06543                 Prefix._MakeMsg("#1%S", &InsideDesc);      // ' inside' prefix
06544             }
06545             else
06546             {
06547                 Suffix._MakeMsg("#1%S", &InsideBracketDesc);       // ' inside' prefix
06548             }
06549 
06550 
06551             // The format is 'On Layer <LayerName>'
06552             // if LayerName contains the word layer however then the format is
06553             // On <LayerName>
06554 
06555             String_256 TempLayerID = LastLayer->GetLayerID(); 
06556             TempLayerID.toUpper();
06557             if (TempLayerID.Sub(String_8(_R(IDS_LAYER_MASK))) != -1)   // does TempLayer contain the string 'LAYER" ?
06558             {
06559                 // 'LAYER' found in layer id
06560 /*              Description._MakeMsg("#1%S #2%S #3%S #4%S",
06561                                       &Prefix, &String_8(_R(IDS_ON)), &(LastLayer->GetLayerID()), &Suffix);
06562 */
06563                 SafeAppendString(&Description, Prefix);
06564                 SafeAppendString(&Description, String_8(_R(IDS_ON)));
06565                 SafeAppendString(&Description, LastLayer->GetLayerID());
06566                 SafeAppendString(&Description, Suffix, FALSE);
06567             } 
06568             else
06569             { 
06570 /*              Description._MakeMsg("#1%S #2%S #3%S #4%S",
06571                                       &Prefix, &String_8(_R(IDS_ON_LAYER)), &(LastLayer->GetLayerID()), &Suffix);
06572 */
06573                 SafeAppendString(&Description, Prefix);
06574                 SafeAppendString(&Description, String_8(_R(IDS_ON_LAYER)));
06575                 SafeAppendString(&Description, LastLayer->GetLayerID());
06576                 SafeAppendString(&Description, Suffix, FALSE);
06577             }
06578         }
06579     }   
06580 #endif
06581     return Description;
06582 }
06583 
06584 #endif
06585 
06586 
06587 
06588 /*********************************************************************************************
06589 >   BOOL Range::operator==(const Range& R) const
06590 
06591     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
06592     Created:    23/1/96
06593     Inputs:     R - the range to compare against.
06594     Outputs:    - 
06595     Returns:    TRUE if R describes the same range as this range.
06596     Purpose:    Test for equality of two ranges.  Ranges are considered equal if they have
06597                 the same flags and the same first & last nodes.
06598     SeeAlso     Range::operator!=
06599     Errors:     None.
06600 **********************************************************************************************/  
06601 BOOL Range::operator==(const Range& R) const
06602 {
06603     return (R.RangeControlFlags.Selected == RangeControlFlags.Selected &&
06604             R.RangeControlFlags.Unselected == RangeControlFlags.Unselected &&
06605             R.RangeControlFlags.CrossLayer == RangeControlFlags.CrossLayer &&
06606             R.RangeControlFlags.IgnoreLockedLayers == RangeControlFlags.IgnoreLockedLayers &&
06607             R.RangeControlFlags.IgnoreNoneRenderable == RangeControlFlags.IgnoreNoneRenderable &&
06608             R.RangeControlFlags.IgnoreInvisibleLayers == RangeControlFlags.IgnoreInvisibleLayers &&
06609             R.FirstNode == FirstNode &&
06610             R.LastNode == LastNode);
06611 }
06612 
06613 
06614 
06615 /*********************************************************************************************
06616 >   BOOL Range::operator!=(const Range& R) const
06617 
06618     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
06619     Created:    23/1/96
06620     Inputs:     R - the range to compare against.
06621     Outputs:    - 
06622     Returns:    TRUE if R dosen't describe the same range as this range.
06623     Purpose:    Test for non-equality of two ranges.  Ranges are considered equal if they have
06624                 the same flags and the same first & last nodes.
06625     SeeAlso     Range::operator==
06626     Errors:     None.
06627 **********************************************************************************************/  
06628 BOOL Range::operator!=(const Range& R) const
06629 {
06630     return !(*this==R);
06631 }
06632 
06633 
06634 
06635 
06636 /*****************************************************************************************
06637     ListRange class
06638 */
06639 /*****************************************************************************************
06640 
06641 >   ListRange::ListRange()
06642 
06643     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
06644     Created:    11/12/2003
06645 
06646     Inputs:     -
06647     Outputs:    -
06648     Returns:    -
06649 
06650     Purpose:    ListRange constructor.
06651 
06652     Errors:     -
06653 
06654 ******************************************************************************************/
06655 
06656 ListRange::ListRange() :    Range(NULL, NULL, RangeControl(TRUE,FALSE,TRUE,TRUE))   // Call Range constructor
06657 {
06658     pLastReturnedItem = NULL;
06659 }
06660 
06661 
06662 
06663 
06664 /*****************************************************************************************
06665 
06666 >   ListRange::ListRange(const List& srcList)
06667 
06668     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
06669     Created:    11/12/2003
06670 
06671     Inputs:     -
06672     Outputs:    -
06673     Returns:    -
06674 
06675     Purpose:    SelRange constructor.
06676 
06677     Errors:     -
06678 
06679 ******************************************************************************************/
06680 ListRange::ListRange(const List& srcList) : Range()
06681 {
06682     ERROR2RAW("Unimplemented!");
06683 }
06684 
06685 
06686 
06687 
06688 
06689 /*****************************************************************************************
06690 
06691 >   ListRange::ListRange(const ListRange& srcRange)
06692 
06693     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
06694     Created:    14/02/2005
06695 
06696     Inputs:     -
06697     Outputs:    -
06698     Returns:    -
06699 
06700     Purpose:    SelRange constructor.
06701 
06702     Errors:     -
06703 
06704 ******************************************************************************************/
06705 ListRange::ListRange(const ListRange& srcRange) : Range()
06706 {
06707     BOOL bOK = TRUE;
06708     NodeListItem* pNodeItem = (NodeListItem*)srcRange.nodelist.GetHead();
06709     while (pNodeItem)
06710     {
06711         NodeListItem* pNewNodeItem = new NodeListItem(pNodeItem->pNode);
06712         if (pNewNodeItem==NULL)
06713         {
06714             bOK = FALSE;
06715             break;
06716         }
06717         nodelist.AddTail(pNewNodeItem);
06718 
06719         pNodeItem = (NodeListItem*)srcRange.nodelist.GetNext(pNodeItem);
06720     }
06721 
06722     if (!bOK)
06723     {
06724         // Get rid of all NodeListItems in the nodelist
06725         while (nodelist.GetHead())
06726             delete nodelist.RemoveHead();
06727     }
06728     pLastReturnedItem = NULL;
06729 }
06730 
06731 
06732 
06733 
06734 /*****************************************************************************************
06735 
06736 >   ListRange::ListRange(const ListRange* pSrcRange)
06737 
06738     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
06739     Created:    14/02/2005
06740 
06741     Inputs:     -
06742     Outputs:    -
06743     Returns:    -
06744 
06745     Purpose:    SelRange constructor.
06746 
06747     Errors:     -
06748 
06749 ******************************************************************************************/
06750 ListRange::ListRange(const ListRange* pSrcRange) : Range()
06751 {
06752     if (pSrcRange==NULL)
06753         return;
06754 
06755     BOOL bOK = TRUE;
06756     NodeListItem* pNodeItem = (NodeListItem*)pSrcRange->nodelist.GetHead();
06757     while (pNodeItem)
06758     {
06759         NodeListItem* pNewNodeItem = new NodeListItem(pNodeItem->pNode);
06760         if (pNewNodeItem==NULL)
06761         {
06762             bOK = FALSE;
06763             break;
06764         }
06765         nodelist.AddTail(pNewNodeItem);
06766 
06767         pNodeItem = (NodeListItem*)pSrcRange->nodelist.GetNext(pNodeItem);
06768     }
06769 
06770     if (!bOK)
06771     {
06772         // Get rid of all NodeListItems in the nodelist
06773         while (nodelist.GetHead())
06774             delete nodelist.RemoveHead();
06775     }
06776     pLastReturnedItem = NULL;
06777 }
06778 
06779 
06780 
06781 
06782 /*****************************************************************************************
06783 
06784 >   ListRange::~ListRange()
06785 
06786     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
06787     Created:    11/12/2003
06788 
06789     Inputs:     -
06790     Outputs:    -
06791     Returns:    -
06792 
06793     Purpose:    SelRange destructor.
06794 
06795     Errors:     -
06796 
06797 ******************************************************************************************/
06798 
06799 ListRange::~ListRange()
06800 {
06801     // Get rid of all NodeListItems in the nodelist
06802     while (nodelist.GetHead())
06803         delete nodelist.RemoveHead();
06804 
06805     pLastReturnedItem = NULL;
06806 }
06807 
06808 
06809 
06810 
06811 /*********************************************************************************************
06812 
06813 >    Node* ListRange::FindFirst(BOOL AndChildren = FALSE)
06814 
06815      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 11/12/2003
06816      Created:   25/6/93
06817      
06818      Returns:   If the range contains any members then
06819                     A pointer to the first node in the range is returned
06820                 Else
06821                     NULL is returned 
06822             
06823      Purpose:   The purpose of this function is to find the first node in a range. 
06824 
06825      SeeAlso:   Range::FindNext    
06826      
06827      Errors:    An assertion failure will occur if:
06828 
06829                 There are no more nodes to search and the last node to search was not found.
06830 
06831 **********************************************************************************************/
06832                                     
06833 Node* ListRange::FindFirst(BOOL AndChildren)
06834 {
06835     // Preconditions...
06836     ERROR2IF(this==NULL,NULL,"FindFirst called on NULL range");
06837 //  ERROR2IF(AndChildren, NULL, "ListRange can't honour AndChildren requests yet");
06838 
06839     NodeListItem* pNodeItem = (NodeListItem*)nodelist.GetHead();
06840     pLastReturnedItem = pNodeItem;
06841     if (pNodeItem)
06842     {
06843         if (AndChildren)
06844             return pNodeItem->pNode->FindFirstDepthFirst();
06845         else
06846             return pNodeItem->pNode;
06847     }
06848 
06849     return NULL;
06850 }
06851 
06852 
06853      
06854 
06855 /*********************************************************************************************
06856 
06857 >    Node* ListRange::FindNext(Node* Prev, BOOL AndChildren = FALSE)
06858      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 11/12/2003
06859      Created:   25/6/93
06860      Inputs:    Prev: The previous node in the range (usually returned from a 
06861                        Range::FindFirst, or a previous Range::FindNext call). 
06862      Outputs:    - 
06863      Returns:   If the range contains any more members then
06864                     A pointer to the next node in the range is returned
06865                 Else
06866                     NULL is returned 
06867      Purpose:   The purpose of this function is to find the next node in the range after Prev 
06868      SeeAlso:   Range::FindFirst    
06869      Errors:    An assertion failure will occur if:
06870                 There are no more nodes to search and the last node to search was not found.
06871 
06872 **********************************************************************************************/
06873 
06874 Node* ListRange::FindNext(Node* pPrevious, BOOL AndChildren)
06875 {
06876     // Preconditions
06877     // No need to check that "this" is NULL because that's already been done
06878     // in FindFirst.
06879     ERROR2IF(pPrevious == NULL, NULL, "NULL pointer passed to Range::FindNext");
06880 //  ERROR2IF(AndChildren, NULL, "ListRange can't honour AndChildren requests yet");
06881 
06882     BOOL found = FALSE;
06883     NodeListItem* pNodeItem = (NodeListItem*)nodelist.GetHead();
06884 
06885     // ------------------
06886     // Optimisation
06887     if (pLastReturnedItem)
06888     {
06889         if (pLastReturnedItem->pNode == pPrevious)
06890             pNodeItem = pLastReturnedItem;
06891         else if (AndChildren && pLastReturnedItem->pNode->IsNodeInSubtree(pPrevious))
06892             pNodeItem = pLastReturnedItem;
06893     }
06894     // ------------------
06895 
06896     while (pNodeItem && !found)
06897     {
06898         found = (pNodeItem->pNode == pPrevious);
06899         if (!found && AndChildren)
06900         {
06901             // If this list item contains the previous context node
06902             // Then we are still traversing the subtree of that list item
06903             // So get the next item in the subtree
06904             if (pNodeItem->pNode->IsNodeInSubtree(pPrevious))
06905             {
06906                 pLastReturnedItem = pNodeItem;
06907                 return pPrevious->FindNextDepthFirst(pNodeItem->pNode);
06908             }
06909         }
06910 
06911         pNodeItem = (NodeListItem*)nodelist.GetNext(pNodeItem);
06912     }
06913 
06914     pLastReturnedItem = pNodeItem;
06915 
06916     if (pNodeItem && found)
06917         if (AndChildren)
06918             return pNodeItem->pNode->FindFirstDepthFirst();
06919         else
06920             return pNodeItem->pNode;
06921 
06922     return NULL;
06923 }
06924 
06925 
06926 
06927 
06928 /*********************************************************************************************
06929 
06930 >    Node* ListRange::FindPrev(Node* pNode, BOOL AndChildren = FALSE)
06931      Author:    Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 11/12/2003
06932      Created:   19/01/95
06933      Inputs:    pNode: The node which you want the previous one in the range to... 
06934      Outputs:    - 
06935      Returns:   If the range contains any members before the input node then
06936                     A pointer to the prev node in the range is returned
06937                 Else
06938                     NULL is returned (Note NULL returned if input node was not in the range)
06939      Purpose:   The purpose of this function is to find the previous node in the range before
06940                 the input node.
06941                 until it meets the input node, then returns the previous node to that!!!
06942      SeeAlso:   Range::FindFirst; Range::FindNext    
06943      Errors:    -
06944 
06945 **********************************************************************************************/
06946 
06947 Node* ListRange::FindPrev(Node* pNode, BOOL AndChildren)
06948 {
06949     // Preconditions
06950     // No need to check that "this" is NULL because that's already been done
06951     // in FindFirst.
06952     ERROR2IF(pNode == NULL, NULL, "NULL pointer passed to Range::FindNext");
06953     ERROR2IF(AndChildren, NULL, "ListRange::FindPrev can't honour AndChildren requests yet");
06954 
06955     BOOL found = FALSE;
06956     NodeListItem* pNodeItem = (NodeListItem*)nodelist.GetTail();
06957 
06958     // ------------------
06959     // Optimisation
06960     if (pLastReturnedItem && pLastReturnedItem->pNode == pNode)
06961         pNodeItem = pLastReturnedItem;
06962     // ------------------
06963 
06964     while (pNodeItem && !found)
06965     {
06966         found = (pNodeItem->pNode == pNode);
06967 
06968         pNodeItem = (NodeListItem*)nodelist.GetPrev(pNodeItem);
06969     }
06970 
06971     pLastReturnedItem = pNodeItem;
06972 
06973     if (pNodeItem && found)
06974         return pNodeItem->pNode;
06975 
06976     return NULL;
06977 }
06978 
06979 
06980 
06981 
06982 /*********************************************************************************************
06983 
06984 >    Node* ListRange::FindLast()
06985 
06986      Author:    Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
06987      Created:   25/6/93
06988      
06989      Returns:   If the range contains any members then
06990                     A pointer to the last node in the range is returned
06991                 Else
06992                     NULL is returned 
06993             
06994      Purpose:   The purpose of this function is to find the last node in a range. 
06995                 If the range was constructed with a NULL last node specifier then
06996                 the range is scanned until the last node is found. If a non NULL last node
06997                 was specified however the value of last is simply returned. It's existance
06998                 is not verified !. 
06999 
07000      SeeAlso:   Range::FindFirst
07001                 Range::FindNext    
07002      
07003      Errors:    An assertion failure will occur if:
07004 
07005                 There are no more nodes to search and the last node to search was not found.
07006 
07007 **********************************************************************************************/
07008 
07009 Node* ListRange::FindLast()
07010 {
07011     ERROR2IF(this==NULL,NULL,"FindFirst called on NULL range");
07012 
07013 //  BOOL found = FALSE;
07014     NodeListItem* pNodeItem = (NodeListItem*)nodelist.GetTail();
07015     pLastReturnedItem = pNodeItem;
07016     if (pNodeItem)
07017         return pNodeItem->pNode;
07018 
07019     return NULL;
07020 }
07021 
07022 
07023 
07024 
07025 /**********************************************************************************************
07026 **********************************************************************************************/
07027 void ListRange::AddNode(Node* pNode)
07028 {
07029 #ifdef _DEBUG_LISTCHECKS
07030     ERROR3IF(InRange(pNode, FALSE), "Attempt to add duplicate node to ListRange!\n");
07031 #endif
07032 
07033     NodeListItem* pNodeItem = new NodeListItem(pNode);
07034     nodelist.AddTail(pNodeItem);
07035 }
07036 
07037 
07038 
07039 
07040 /**********************************************************************************************
07041 **********************************************************************************************/
07042 Node* ListRange::RemoveNode(Node* pNode)
07043 {
07044     BOOL found = FALSE;
07045     Node* pFoundNode = NULL;
07046     NodeListItem* pNodeItem = (NodeListItem*)nodelist.GetHead();
07047     while (pNodeItem && pFoundNode==NULL)
07048     {
07049         found = (pNodeItem->pNode == pNode);
07050 
07051         if (found)
07052             pFoundNode = pNode;
07053         else
07054             pNodeItem = (NodeListItem*)nodelist.GetNext(pNodeItem);
07055     }
07056 
07057     if (pNodeItem && pFoundNode)
07058         nodelist.RemoveItem(pNodeItem);
07059 
07060     pLastReturnedItem = NULL;
07061 
07062     return pFoundNode;
07063 }
07064 
07065 
07066 
07067 
07068 /**********************************************************************************************
07069 **********************************************************************************************/
07070 BOOL ListRange::InRange(Node* pNode, BOOL AndChildren) const
07071 {
07072     ERROR2IF(AndChildren, FALSE, "ListRange can't honour AndChildren requests yet");
07073 
07074     BOOL found = FALSE;
07075     NodeListItem* pNodeItem = (NodeListItem*)nodelist.GetHead();
07076     while (pNodeItem && !found)
07077     {
07078         found = (pNodeItem->pNode == pNode);
07079 
07080         pNodeItem = (NodeListItem*)nodelist.GetNext(pNodeItem);
07081     }
07082 
07083 //  pLastReturnedItem = pNodeItem;
07084 
07085     return found;
07086 }
07087 
07088 
07089 
07090 
07091 /**********************************************************************************************
07092 **********************************************************************************************/
07093 void ListRange::Clear()
07094 {
07095     // Get rid of all NodeListItems in the nodelist
07096     while (nodelist.GetHead())
07097         delete nodelist.RemoveHead();
07098 
07099     pLastReturnedItem = NULL;
07100 }
07101 
07102 
07103 
07104 
07105 /********************************************************************************************
07106 
07107 >   BOOL ListRange::MatchesSelectionEffectLevel(INT32 iStackPos)
07108 
07109     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
07110     Created:    13/05/2005
07111     Inputs:     iStackPos - index into effects stack
07112     Outputs:    -
07113     Returns:    TRUE if this range matches the specifed level in the Effects stack
07114                 FALSE otherwise
07115     Purpose:    Get the current edit context for the feather UI controls
07116     Notes:      VERY IMPORTANT!
07117                 The node pointers held in this range could be out of date - the nodes
07118                 could have been deleted. So it's important that the pointers are
07119                 only used AFTER we have verified that they actually point to something!
07120 
07121     More:       The implementation assumes that the nodes will be stored in the lists
07122                 in the same order. This is not a good idea!
07123                 A better algorithm, avoiding O(n^2) problems, would be to check the two
07124                 lists are the same length, then go through them both clearing a marker flag
07125                 on every node, then scan one list setting the marker flag, finally scan
07126                 the other list checking whether all nodes are marked.
07127                 Er, but what about the possible dead pointers in one of the lists?
07128 
07129 ********************************************************************************************/
07130 BOOL ListRange::MatchesSelectionEffectLevel(INT32 iStackPos)
07131 {
07132     if (iStackPos==STACKPOS_INVALID)
07133         return FALSE;
07134 
07135     SelRange* pSelRange = GetApplication()->FindSelection();
07136     EffectsStack* pStack = pSelRange->GetEffectsStack();        // From cache
07137     if (pStack==NULL || pStack->IsEmpty())
07138         return FALSE;
07139 
07140     INT32 iPos = iStackPos;
07141     Range* pLevelRange = pStack->GetLevelRange(&iPos);
07142     if (pLevelRange==NULL)
07143         return FALSE;
07144 
07145     Node* pNode = pLevelRange->FindFirst();
07146     Node* pEditNode = FindFirst();
07147     while (pNode && pEditNode)
07148     {
07149         // If the pointers are the same then we can reasonably assume
07150         // the the pointer stored in s_pEditRange is still valid...
07151         if (pNode!=pEditNode)
07152             return FALSE;
07153 
07154         // Now we know the pointer at least points to SOME kind of node
07155         // Check whether Another node has been allocated into the same
07156         // address as a deleted node...
07157         if (pNode->GetTag() != pEditNode->GetTag())
07158             return FALSE;
07159 
07160         pNode = pLevelRange->FindNext(pNode);
07161         pEditNode = FindNext(pEditNode);
07162     }
07163 
07164     // If either pointer is non-null then we stopped without matching every case
07165     // So must return NULL
07166     if (pNode || pEditNode)
07167         return FALSE;
07168 
07169     return TRUE;
07170 }
07171 
07172 
07173 /**********************************************************************************************
07174 **********************************************************************************************/
07175 void Range::ForceRedrawView(DocView* pDocView, BOOL bReleaseCache, BOOL bUseBlobRects, BOOL bReleaseParentsOnly)
07176 {
07177     BOOL bOldPromote = SetPromoteToParent(TRUE);
07178 
07179     NodeRenderableBounded* CurrentNode = (NodeRenderableBounded*)FindFirst(); 
07180 
07181     if (CurrentNode != NULL && pDocView!=NULL)
07182     {
07183         Spread* pSpread = (Spread*)CurrentNode->FindParent(CC_RUNTIME_CLASS(Spread));
07184         Node* pBackmost = CurrentNode;
07185         DocRect Bounds(0,0,0,0);
07186 
07187         while (CurrentNode)
07188         {
07189             if (bReleaseCache && CurrentNode->IsAnObject()) ((NodeRenderableInk*)CurrentNode)->ReleaseCached(TRUE, !bReleaseParentsOnly, !bReleaseParentsOnly, !bReleaseParentsOnly);
07190             if (CurrentNode->IsBounded()) Bounds = Bounds.Union( ((NodeRenderableBounded*)CurrentNode)->GetBoundingRect() );
07191             if (CurrentNode->IsBounded() && bUseBlobRects) Bounds = Bounds.Union( ((NodeRenderableBounded*)CurrentNode)->GetBlobBoundingRect() );
07192             if (CurrentNode->IsUnder(pBackmost)) pBackmost = CurrentNode;
07193 
07194             CurrentNode = (NodeRenderableBounded*)FindNext(CurrentNode);
07195         }
07196 
07197         pDocView->ForceRedraw(pSpread, Bounds, TRUE, pBackmost);
07198     }
07199 
07200     SetPromoteToParent(bOldPromote);
07201 }
07202 
07203 
07204 
07205 
07206 /*******************************************************************************************
07207 
07208 >   BOOL Range::FindBitmap( KernelBitmap * pFoundBitmap = NULL,
07209                             KernelBitmapRef ** ppFoundBitmapRef = NULL,
07210                             NodeBitmap ** ppFoundNode = NULL,
07211                             AttrFillGeometry ** ppFoundFillAttribute = NULL)
07212 
07213     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
07214     Created:    21/1/97
07215     Changed:    Phil, 20/01/2004: Moved from PlugInUndoOp static to be member function of range
07216                                     !!!!!!!!!
07217     Inputs:     None
07218     Outputs:    if supplied, it will return a pointer to the bitmap that is in the selection
07219                 if supplied, it will return a pointer to the bitmap ref that is in the selection
07220                 if supplied, it will return a pointer to the node bitmap that is in the selection
07221                 if supplied, it will return a pointer to the fill geometry that is in the selection
07222     Returns:    TRUE if succeeded in finding a bitmap, FALSE if not
07223     Purpose:    Find out if there is a valid bitmap in the selection and return useful information
07224                 to the caller.
07225     SeeAlso:    -
07226 
07227 *******************************************************************************************/
07228 
07229 BOOL Range::FindBitmap( KernelBitmap** ppFoundBitmap,
07230                         KernelBitmapRef** ppFoundBitmapRef,
07231                         NodeBitmap** ppFoundNode,
07232                         AttrFillGeometry** ppFoundFillAttribute)
07233 {
07234     KernelBitmap * pBitmap = NULL;
07235     KernelBitmapRef* pBitmapRef = NULL;
07236 
07237     // Assume nothing found
07238     if (ppFoundBitmap)          *ppFoundBitmap = NULL;
07239     if (ppFoundBitmapRef)       *ppFoundBitmapRef = NULL;
07240     if (ppFoundNode)            *ppFoundNode = NULL;
07241     if (ppFoundFillAttribute)   *ppFoundFillAttribute = NULL;
07242 
07243     Node* pCurrentNode = FindFirst(); 
07244 
07245     if (pCurrentNode != NULL) // No nodes selected so End
07246     {
07247         // Do all bitmaps. OK this should pick up the fill as well. Never mind
07248         while (pCurrentNode != NULL)
07249         {
07250             if  (pCurrentNode->IsABitmap())
07251             {
07252                 NodeBitmap* pNodeBmp = (NodeBitmap*)pCurrentNode;
07253                 pBitmap = pNodeBmp->GetBitmap();
07254                 pBitmapRef = pNodeBmp->GetBitmapRef();
07255                 // If the caller wanted it, return the found bitmap to them
07256                 if (ppFoundBitmap)
07257                     *ppFoundBitmap = pBitmap;
07258                 // ditto found bitmap ref
07259                 if (ppFoundBitmapRef)
07260                     *ppFoundBitmapRef = pBitmapRef;
07261                 // ditto found node
07262                 if (ppFoundNode)
07263                     *ppFoundNode = pNodeBmp;
07264                 // ditto found fill attribute
07265                 if (ppFoundFillAttribute)
07266                     *ppFoundFillAttribute = NULL;
07267 
07268                 return TRUE;
07269             }
07270 
07271             pCurrentNode = FindNext(pCurrentNode);
07272         }
07273 
07274     } 
07275 
07276     if (ppFoundFillAttribute)
07277     {
07278         // Find the first Fill Attribute in the selection
07279         AttrFillGeometry* pAttrNode = AttrFillGeometry::FindFirstSelectedAttr();
07280 
07281         while (pAttrNode != NULL)
07282         {
07283             if (pAttrNode->IsKindOf(CC_RUNTIME_CLASS(AttrBitmapColourFill)))
07284             {
07285                 pBitmap = pAttrNode->GetBitmap();
07286                 pBitmapRef = pAttrNode->GetBitmapRef();
07287                 // If the caller wanted it, return the found bitmap to them
07288                 if (ppFoundBitmap)
07289                     *ppFoundBitmap = pBitmap;
07290                 // ditto found bitmap ref
07291                 if (ppFoundBitmapRef)
07292                     *ppFoundBitmapRef = pBitmapRef;
07293                 // ditto found node
07294                 if (ppFoundNode)
07295                     *ppFoundNode = NULL;
07296                 // ditto found fill attribute
07297                 if (ppFoundFillAttribute)
07298                     *ppFoundFillAttribute = pAttrNode;
07299 
07300                 return TRUE;
07301             }
07302 
07303             // Check the next fill
07304             pAttrNode = AttrFillGeometry::FindNextSelectedAttr();
07305         }
07306     }
07307 
07308     return FALSE;
07309 }
07310 
07311 
07312 
07313 

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