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