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,