00001 // $Id: undoop.cpp 1282 2006-06-09 09:46:49Z alex $ 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 #include "camtypes.h" 00100 //#include "undoop.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00101 00102 #include "ophist.h" 00103 00104 //#include "range.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00105 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00107 //#include "selstate.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00108 //#include "tranform.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00110 //#include "mario.h" 00111 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00113 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00114 #include "nodepath.h" 00115 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00116 #include "cpyact.h" 00117 #include "clipint.h" 00118 #include "pathedit.h" 00119 #include "ndoptmz.h" 00120 #include "sprdmsg.h" 00121 #include "layer.h" 00122 //#include "markn.h" 00123 #include "progress.h" 00124 #include "invalid.h" 00125 //#include "becomea.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00126 //#include "nodeattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00127 #include "objchge.h" 00128 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00129 #include "nodeblnd.h" 00130 #include "nodetxtl.h" 00131 #include "nodetxts.h" 00132 #include "ndbldpth.h" 00133 #include "textops.h" 00134 #include "cutop.h" 00135 #include "camdoc.h" 00136 #include "effects_stack.h" // for EffectsStack 00137 #include "attrmap.h" 00138 00139 #ifdef _DEBUG 00140 #include "layermsg.h" // layer messages for redraw layer message in debug builds 00141 #endif 00142 00143 #include "ngcore.h" // for NameGallery stretching implementation. 00144 #include "chapter.h" 00145 00146 #include "opdupbar.h" 00147 00148 CC_IMPLEMENT_DYNCREATE( UndoableOperation, Operation ) 00149 00150 // Declare smart memory handling in Debug builds 00151 #define new CAM_DEBUG_NEW 00152 00153 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00154 // ObjectSet methods 00155 00156 00157 /******************************************************************************************** 00158 00159 > BOOL ObjectSet::AddToSet(NodeRenderableInk* pObject) 00160 00161 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00162 Created: 12/5/95 00163 Returns: FALSE if we run out of memory 00164 Purpose: Adds pObject to the set 00165 SeeAlso: ObjectSet::InSet 00166 SeeAlso: ObjectSet::CopySet 00167 00168 00169 ********************************************************************************************/ 00170 00171 BOOL ObjectSet::AddToSet(NodeRenderableInk* pObject) 00172 { 00173 ERROR3IF(pObject == NULL, "AddToSet: pObject is NULL"); 00174 // Determine if the pObject is already in the set 00175 ObjectItem* pObjItem = (ObjectItem*) GetHead(); 00176 while (pObjItem != NULL) 00177 { 00178 if (pObjItem->pObject == pObject) 00179 { 00180 return TRUE; // Already in set so return 00181 } 00182 pObjItem = (ObjectItem*)GetNext(pObjItem); 00183 } 00184 00185 // The attribute type is not in the set so let's add it 00186 pObjItem = new ObjectItem; 00187 if (!pObjItem) 00188 return FALSE; // out of memory (error has been set) 00189 00190 pObjItem->pObject = pObject; 00191 00192 AddHead(pObjItem); // Add attribute to the head of the list 00193 00194 return TRUE; 00195 } 00196 00197 00198 /******************************************************************************************** 00199 00200 > BOOL ObjectSet::CopySet() 00201 00202 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00203 Created: 15/5/95 00204 Inputs: - 00205 Outputs: - 00206 Returns: A copy of the set, or NULL if we run out of memory 00207 Purpose: Makes a copy of the object set 00208 Errors: - 00209 SeeAlso: ObjectSet::AddToSet 00210 SeeAlso: ObjectSet::InSet 00211 00212 ********************************************************************************************/ 00213 00214 ObjectSet* ObjectSet::CopySet() 00215 { 00216 ObjectSet* pSetCopy; 00217 pSetCopy = new ObjectSet; 00218 if (!pSetCopy) 00219 return NULL; // Out of memory 00220 00221 // Copy each item in turn 00222 ObjectItem* pItemCopy; 00223 ObjectItem* pCurrent = (ObjectItem*)GetHead(); 00224 while (pCurrent) 00225 { 00226 pItemCopy = new ObjectItem; 00227 if (!pItemCopy) 00228 { 00229 // Tidyup 00230 pSetCopy->DeleteAll(); 00231 delete pSetCopy; 00232 return NULL; 00233 } 00234 pItemCopy->pObject = pCurrent->pObject; 00235 pSetCopy->AddTail(pItemCopy); 00236 pCurrent = (ObjectItem*)GetNext(pCurrent); 00237 } 00238 return pSetCopy; 00239 } 00240 00241 /******************************************************************************************** 00242 00243 > BOOL ObjectSet::InSet(NodeRenderableInk* pObject) 00244 00245 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00246 Created: 15/5/95 00247 Inputs: pObject: A pointer to an object 00248 Outputs: - 00249 Returns: TRUE if pObject is in the set 00250 Purpose: To determine if pObject is in the set 00251 Errors: - 00252 SeeAlso: ObjectSet::CopySet 00253 SeeAlso: ObjectSet::AddToSet 00254 00255 00256 ********************************************************************************************/ 00257 00258 BOOL ObjectSet::InSet(NodeRenderableInk* pObject) 00259 { 00260 ObjectItem* pCurrent = (ObjectItem*)GetHead(); 00261 while (pCurrent) 00262 { 00263 if (pCurrent->pObject == pObject) 00264 return TRUE; 00265 pCurrent = (ObjectItem*)GetNext(pCurrent); 00266 } 00267 return FALSE; 00268 } 00269 00270 00271 00272 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00273 // UndoableOperation methods 00274 00275 00276 00277 BOOL UndoableOperation::MovingNode = FALSE; 00278 00279 /******************************************************************************************** 00280 00281 > UndoableOperation::UndoableOperation() 00282 00283 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00284 Created: 9/3/94 00285 Inputs: - 00286 Outputs: - 00287 Returns: - 00288 Purpose: Constructs a new undoable operation object: 00289 Errors: - 00290 SeeAlso: Operation::Operation 00291 00292 ********************************************************************************************/ 00293 00294 UndoableOperation::UndoableOperation(): Operation() 00295 { 00296 // This var is a flag that ensures that this warning will only appear once for the lifetime of this op 00297 WarnInsertObjsOntoLockedLayers = TRUE; 00298 } 00299 00300 00301 /******************************************************************************************** 00302 00303 > virtual UndoableOperation::~UndoableOperation() 00304 00305 00306 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00307 Created: 9/3/94 00308 Inputs: - 00309 Outputs: - 00310 Returns: - 00311 Purpose: UndoableOperation destructor 00312 Errors: - 00313 SeeAlso: - 00314 00315 ********************************************************************************************/ 00316 00317 UndoableOperation::~UndoableOperation() 00318 { 00319 } 00320 00321 00322 00323 // --------------------------------------------------------------------------------------- 00324 // The Do functions 00325 00326 /******************************************************************************************** 00327 00328 > BOOL UndoableOperation::DoInsertNewNode(NodeRenderableBounded* NewNode, 00329 Spread *pSpread, 00330 BOOL InvalidateRgn, 00331 BOOL ClearSelection = TRUE, 00332 BOOL SelectNewObject = TRUE, 00333 BOOL NormaliseAttributes=TRUE) 00334 00335 00336 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00337 Created: 13/9/93 00338 Inputs: NewNode: The new node to be added 00339 pSpread: The spread to add the new node to 00340 *Set this to NULL if you want to insert 00341 the node onto the selected spread. 00342 InvalidateRgn: Invalidate the node's region (including blobs) 00343 ClearSelection: Clear all selected objects prior to the new object 00344 being inserted (Default TRUE) 00345 SelectNewObject: Select the new object (Default TRUE) 00346 NormaliseAttributes:When TRUE redundant attributes are removed from the 00347 node. If you know that the node being inserted has 00348 the correct set of attributes then you can set this to 00349 FALSE. 00350 00351 Note: Do not set the bounding rectangle of NewNode prior to calling 00352 this function, or the bounds will not be propagated. 00353 Returns: TRUE if successful 00354 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 00355 Purpose: Inserts the node as a last child of the active layer of the pSpread. 00356 If the NewNode is a NodeRenderableInk then the node becomes selected and 00357 all other nodes are deselected. If pSpread is NULL then the object is inserted 00358 on the selected spread. 00359 SeeAlso: UndoableOperation::DoInsertNewNode 00360 00361 ********************************************************************************************/ 00362 00363 BOOL UndoableOperation::DoInsertNewNode(NodeRenderableBounded* NewNode, 00364 Spread *pSpread, 00365 BOOL InvalidateRgn, 00366 BOOL ClearSelection, // = TRUE 00367 BOOL SelectNewObject, // = TRUE 00368 BOOL NormaliseAttributes) // = TRUE 00369 00370 { 00371 ENSURE( pOurDoc!=NULL, "No current document in DoInsertNode" ); 00372 00373 // Insert node as the active layer of the spread 00374 pOurDoc->InsertNewNode(NewNode, pSpread); 00375 NewNode->SetSelected(FALSE); 00376 00377 // This is a brand new node, so we have to make sure that all its parents 00378 // are invalidated as well. 00379 // if this nodes bounding rect was already invalid when it was added the parent 00380 // will not be invalidated as well, so we first mark its bounds as valid. 00381 // This is very important - Ask Rik if you think it looks silly. 00382 NewNode->ValidateBoundingRect(); 00383 NewNode->InvalidateBoundingRect(); 00384 00385 // If inserting a node onto a layer that is locked or invisible, warn the user before 00386 // proceeding, and ensure that the node is not selected. 00387 if (NewNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk))) 00388 { 00389 Layer* pLayer = (Layer*)NewNode->FindParent(CC_RUNTIME_CLASS(Layer)); 00390 if (pLayer != NULL) 00391 { 00392 if (!pLayer->IsVisible() || pLayer->IsLocked()) 00393 { 00394 if (WarnInsertObjsOntoLockedLayers) 00395 { 00396 #if (_OLE_VER >= 0x200) 00397 // Only warn the user if the document is visible to him/her. 00398 if (pOurDoc && pOurDoc->GetOilDoc() && pOurDoc->GetOilDoc()->IsVisible()) 00399 #endif 00400 { 00401 InformWarning(_R(IDS_WARNING_INSERTLAYER)); 00402 } 00403 // Set flag to FALSE so it won't be reported back again in this op 00404 WarnInsertObjsOntoLockedLayers = FALSE; 00405 } 00406 SelectNewObject = FALSE; 00407 } 00408 00409 // Note that this layer has been edited 00410 pLayer->SetEdited(TRUE); 00411 #ifdef _DEBUG 00412 // Tell the frame gallery to update its display of the frame 00413 BROADCAST_TO_ALL( LayerMsg( pLayer, LayerMsg::REDRAW_LAYER ) ); 00414 #endif 00415 } 00416 } 00417 00418 00419 /* 00420 if (InvalidateRgn) 00421 { 00422 if (!DoInvalidateNodeRegion((NodeRenderable*)NewNode, TRUE)) 00423 { 00424 // Remove the NewNodeFrom the tree 00425 NewNode->UnlinkNodeFromTree(); 00426 return FALSE; 00427 } 00428 } 00429 */ 00430 00431 // Create a hide node action to hide the node when we undo 00432 HideNodeAction* UndoHideNodeAction; 00433 if (HideNodeAction::Init(this, 00434 &UndoActions, 00435 NewNode, 00436 TRUE, // Include subtree size 00437 ( Action**)(&UndoHideNodeAction)) 00438 == AC_FAIL) 00439 00440 { 00441 // Remove the NewNodeFrom the tree 00442 NewNode->UnlinkNodeFromTree(); 00443 return FALSE; 00444 } 00445 00446 00447 // Make it the currently selected object if it is an Ink object 00448 if ( NewNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)) ) 00449 { 00450 if (ClearSelection) 00451 { 00452 NodeRenderableInk::DeselectAll(); 00453 GetApplication()->UpdateSelection(); 00454 } 00455 00456 00457 if (SelectNewObject) 00458 { 00459 // Select the new node, and don't redraw it yet 00460 ((NodeRenderableInk*)NewNode)->Select(FALSE); 00461 } 00462 00463 if (NormaliseAttributes) 00464 { 00465 ((NodeRenderableInk*)NewNode)->NormaliseAttributes(); // Removes redundant attributes 00466 } 00467 00468 00469 } 00470 00471 // Update the selection 00472 if (NewNode->IsSelected()) 00473 GetApplication()->UpdateSelection(); 00474 00475 if ( NewNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)) ) 00476 { 00477 if (InvalidateRgn) 00478 { 00479 if (!DoInvalidateNodeRegion((NodeRenderableBounded*)NewNode, TRUE)) 00480 { 00481 return FALSE; 00482 } 00483 } 00484 } 00485 00486 // Added by Jim: Broadcast the layerchanged message when inserting a new layer 00487 00488 if (NewNode->GetRuntimeClass() == CC_RUNTIME_CLASS(Layer)) 00489 { 00490 Spread* pSpread = (Spread*) NewNode->FindParent(CC_RUNTIME_CLASS(Spread)); 00491 BROADCAST_TO_ALL(SpreadMsg(pSpread,SpreadMsg::LAYERCHANGES)); 00492 } 00493 00494 return (TRUE); 00495 } 00496 00497 00498 00499 00500 /******************************************************************************************** 00501 00502 > BOOL UndoableOperation::DoInsertNewNode(NodeRenderableBounded* NewNode, 00503 Node* ContextNode, 00504 AttachNodeDirection Direction, 00505 BOOL InvalidateRegion, 00506 BOOL ClearSelection = TRUE, 00507 BOOL SelectNewObject = TRUE, 00508 BOOL NormaliseAttributes=TRUE) 00509 00510 00511 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00512 Created: 13/9/93 00513 Inputs: NewNode: The new node to be added 00514 ContextNode: The node to which NewNode is to be attached 00515 Direction: The direction the new node should be attached to the 00516 context node. 00517 InvalidateRgn: Invalidate the node's region (including blobs) 00518 ClearSelection: Clear all selected objects prior to the new object 00519 being inserted (Default TRUE) 00520 SelectNewObject: Select the new object (Default TRUE) 00521 NormaliseAttributes:When TRUE redundant attributes are removed from the 00522 node. If you know that the node being inserted has 00523 the correct set of attributes then you can set this to 00524 FALSE. 00525 Note: Do not set the bounding rectangle of NewNode prior to calling 00526 this function, or the bounds will not be propagated. 00527 Returns: TRUE if successful 00528 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 00529 Purpose: This high level do function attaches the node to ContextNode in the direction 00530 specified. If the NewNode is a NodeRenderableInk then the node becomes 00531 selected and all other nodes are deselected. 00532 SeeAlso: UndoableOperation::DoInsertNewNode 00533 00534 ********************************************************************************************/ 00535 00536 BOOL UndoableOperation::DoInsertNewNode(NodeRenderableBounded* NewNode, 00537 Node* ContextNode, 00538 AttachNodeDirection Direction, 00539 BOOL InvalidateRegion, 00540 BOOL ClearSelection, 00541 BOOL SelectNewObject, 00542 BOOL NormaliseAttributes) 00543 { 00544 // Insert the NewNode into the tree 00545 NewNode->AttachNode(ContextNode, Direction); 00546 NewNode->SetSelected(FALSE); 00547 00548 // This is a brand new node, so we have to make sure that all its parents 00549 // are invalidated as well. 00550 // if this nodes bounding rect was already invalid when it was added the parent 00551 // will not be invalidated as well, so we first mark its bounds as valid. 00552 // This is very important - Ask Rik if you think it looks silly. 00553 NewNode->ValidateBoundingRect(); 00554 NewNode->InvalidateBoundingRect(); 00555 00556 // If inserting a node onto a layer that is locked or invisible, warn the user before 00557 // proceeding, and ensure that the node is not selected. 00558 if (NewNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk))) 00559 { 00560 Layer* pLayer = (Layer*)NewNode->FindParent(CC_RUNTIME_CLASS(Layer)); 00561 if (pLayer != NULL) 00562 { 00563 if (!pLayer->IsVisible() || pLayer->IsLocked()) 00564 { 00565 if (WarnInsertObjsOntoLockedLayers) 00566 { 00567 #if (_OLE_VER >= 0x200) 00568 // Only warn the user if the document is visible to him/her. 00569 if (pOurDoc && pOurDoc->GetOilDoc() && pOurDoc->GetOilDoc()->IsVisible()) 00570 #endif 00571 { 00572 // ignore this warning on some ops - like if we have text syncing between layers 00573 // or other bar ops that manipulate invisible layers 00574 // or doing the right click from drag op since the msg upsets the drag 00575 PORTNOTE("text","Removed OpTextFormat reference") 00576 #ifndef EXCLUDE_FROM_XARALX 00577 if (!IS_A(this, OpTextFormat) && !IS_A(this, OpDuplicateBar) && !IS_A(this, OpCopyAndTransform)) 00578 InformWarning(_R(IDS_WARNING_INSERTLAYER)); 00579 #endif 00580 } 00581 00582 // Set flag to FALSE so it won't be reported back again in this op 00583 WarnInsertObjsOntoLockedLayers = FALSE; 00584 } 00585 SelectNewObject = FALSE; 00586 } 00587 00588 // Note that this layer has been edited 00589 pLayer->SetEdited(TRUE); 00590 #ifdef _DEBUG 00591 // Tell the frame gallery to update its display of the frame 00592 BROADCAST_TO_ALL(LayerMsg(pLayer, LayerMsg::/*LayerReason::*/REDRAW_LAYER)); 00593 #endif 00594 } 00595 } 00596 00597 // Create a hide node action to hide the node when we undo 00598 HideNodeAction* UndoHideNodeAction; 00599 if (HideNodeAction::Init(this, 00600 &UndoActions, 00601 NewNode, 00602 TRUE, // Include subtree size 00603 ( Action**)(&UndoHideNodeAction)) 00604 == AC_FAIL) 00605 00606 { 00607 return FALSE; 00608 } 00609 00610 00611 00612 // Make it the currently selected object if it is an Ink object 00613 if ( NewNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)) ) 00614 { 00615 if (ClearSelection) 00616 { 00617 NodeRenderableInk::DeselectAll(); 00618 GetApplication()->UpdateSelection(); 00619 00620 } 00621 00622 if (SelectNewObject) 00623 { 00624 00625 // Select the new node 00626 ((NodeRenderableInk*)NewNode)->Select(FALSE); 00627 } 00628 00629 00630 if (NormaliseAttributes) 00631 { 00632 ((NodeRenderableInk*)NewNode)->NormaliseAttributes(); // Removes redundant attributes 00633 } 00634 00635 00636 } 00637 00638 // Update the selection 00639 if (NewNode->IsSelected()) 00640 GetApplication()->UpdateSelection(); 00641 00642 00643 if ( NewNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)) ) 00644 { 00645 if (InvalidateRegion) 00646 { 00647 NewNode->ReleaseCached(TRUE, FALSE, TRUE, TRUE); 00648 if (!DoInvalidateNodeRegion((NodeRenderableBounded*)NewNode, TRUE, FALSE, FALSE, FALSE)) // Don't recache automatically! 00649 { 00650 return FALSE; 00651 } 00652 } 00653 } 00654 00655 // #ifdef _DEBUG 00656 // // If the node being Inserted is a layer then people need to know about it 00657 // if (NewNode->GetRuntimeClass() == CC_RUNTIME_CLASS(Layer)) 00658 // { 00659 // TRACE( _T("Layer inserted. Have you remembered to broadcast a SpreadMsg::LAYERCHANGES message?")); 00660 // //Spread* pSpread = (Spread*) NewNode->FindParent(CC_RUNTIME_CLASS(Spread)); 00661 // //BROADCAST_TO_ALL(SpreadMsg(pSpread,SpreadMsg::LAYERCHANGES)); 00662 // } 00663 // #endif 00664 00665 // Added by Jim: Broadcast the layerchanged message when inserting a new layer 00666 00667 if (NewNode->GetRuntimeClass() == CC_RUNTIME_CLASS(Layer)) 00668 { 00669 Spread* pSpread = (Spread*) NewNode->FindParent(CC_RUNTIME_CLASS(Spread)); 00670 BROADCAST_TO_ALL(SpreadMsg(pSpread,SpreadMsg::LAYERCHANGES)); 00671 } 00672 00673 return (TRUE); 00674 } 00675 00676 00677 /******************************************************************************************** 00678 00679 > BOOL UndoableOperation::DoHideNode(Node* NodeToHide, BOOL IncludeSubtreeSize, 00680 NodeHidden** nodeHidden = NULL, 00681 BOOL TellSubtree = TRUE) 00682 00683 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00684 Created: 18/10/93 00685 Inputs: Node: The Node to be hidden 00686 00687 IncludeSubtreeSize: Flag indicating if the subtree being hidden should 00688 be considered as belonging to the Operation history 00689 or not. (See the note in ops.h) 00690 00691 Outputs: nodeHidden: A pointer to the NodeHidden hiding the NodeToHide. This 00692 output is optional. 00693 00694 Returns: TRUE if successful 00695 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 00696 Purpose: This low level Do function hides the subtree with root NodeToHide 00697 Errors: - 00698 SeeAlso: UndoableOperation::DoHideNodes 00699 00700 ********************************************************************************************/ 00701 00702 BOOL UndoableOperation::DoHideNode(Node* NodeToHide, 00703 BOOL IncludeSubtreeSize, 00704 NodeHidden** nodeHidden, 00705 BOOL TellSubtree) 00706 { 00707 ERROR3IF(!OpFlags.SucceedAndDiscard && NodeToHide->IsAnAttribute() && NodeToHide->FindParent()->DiscardsAttributeChildren(), "DoHide called under Caret"); 00708 00709 if (NodeToHide->DiscardsAttributeChildren()) // If the node to hide doesn't want to produce undo (CaretNode) 00710 return TRUE; // Then bomb out now before we do anything... 00711 00712 // We shouldn't tell the node that it's being hidden if we are only 00713 // moving it. 00714 // Also if we are Hiding a Node Group, then there is no 00715 // need to tell anyone either. As the children are unaffected. 00716 if (TellSubtree) 00717 { 00718 // Tell the node and it's subtree, that they are being hidden 00719 Node* pNode = NodeToHide->FindFirstDepthFirst(); 00720 while (pNode!=NULL) 00721 { 00722 if (!pNode->HidingNode()) 00723 return FALSE; 00724 00725 // And find the next node 00726 pNode = pNode->FindNextDepthFirst(NodeToHide); 00727 } 00728 // Invalidate the region, we need to do this here because we are deleting the node 00729 if (NodeToHide->IsBounded()) 00730 ((NodeRenderableBounded*)NodeToHide)->InvalidateBoundingRect(); 00731 00732 } 00733 00734 // removed by Ed 23/5/95 'cos it caused text redraw problems 00735 // and nobody could justify why just moving an object in the tree should 00736 // cause the renderable bound associated with it to be invalidated 00737 // // Invalidate the region 00738 // if (NodeToHide->IsBounded()) 00739 // ((NodeRenderableBounded*)NodeToHide)->InvalidateBoundingRect(); 00740 00741 // Try to hide the Node 00742 NodeHidden* Hidden; 00743 ALLOC_WITH_FAIL(Hidden,(new NodeHidden(NodeToHide)),this); 00744 if (Hidden == NULL) 00745 { 00746 return (FALSE); 00747 } 00748 00749 // Try to create a ShowNodeAction which will show the node that we have just hidden. 00750 00751 ShowNodeAction* UndoShowNodeAction; 00752 00753 if (ShowNodeAction::Init(this, 00754 &UndoActions, 00755 Hidden, 00756 IncludeSubtreeSize, 00757 (Action**)(&UndoShowNodeAction), 00758 TellSubtree) == AC_FAIL) 00759 { 00760 // We must unhide the NodeToHide manually, cos no action will do it for us 00761 Hidden->ShowNode(); // Hidden is deleted don't worry 00762 return FALSE; 00763 } 00764 00765 if (NodeToHide->IsSelected()) 00766 { 00767 // Update the selection range 00768 Camelot.UpdateSelection(); 00769 } 00770 00771 // If the user provided a nodeHidden parameter then return a pointer to the NodeHidden 00772 if (nodeHidden != NULL) 00773 *nodeHidden = Hidden; 00774 00775 // Added by Jim: Broadcast the layerchanged message when inserting a new layer 00776 00777 if (NodeToHide->GetRuntimeClass() == CC_RUNTIME_CLASS(Layer)) 00778 { 00779 Spread* pSpread = (Spread*) Hidden->FindParent(CC_RUNTIME_CLASS(Spread)); 00780 BROADCAST_TO_ALL(SpreadMsg(pSpread,SpreadMsg::LAYERCHANGES)); 00781 } 00782 // Success 00783 return (TRUE); 00784 } 00785 00786 /******************************************************************************************** 00787 00788 > BOOL UndoableOperation::DoHideNodes(Range NodeRange, BOOL IncludeSubtreeSize, 00789 BOOL TellSubtree = TRUE) 00790 00791 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00792 Created: 03/03/94 00793 00794 Inputs: NodeRange: The Range of nodes to be hidden 00795 00796 IncludeSubtreeSize: Flag indicating if the subtrees being hidden should 00797 be considered as belonging to the Operation history 00798 or not. (See the note in ops.h) 00799 00800 00801 Returns: TRUE if successful 00802 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 00803 Purpose: This low level Do function hides a range of the subtrees 00804 Errors: - 00805 SeeAlso: UndoableOperation::DoHideNode 00806 00807 ********************************************************************************************/ 00808 00809 BOOL UndoableOperation::DoHideNodes(Range NodeRange, BOOL IncludeSubtreeSize, BOOL TellSubtree) 00810 { 00811 Node* Current = NodeRange.FindFirst(); 00812 while (Current != NULL) 00813 { 00814 Node* Next = NodeRange.FindNext(Current); 00815 if (!DoHideNode(Current, IncludeSubtreeSize, NULL, TellSubtree)) 00816 { 00817 return FALSE; // Failure 00818 } 00819 Current = Next; 00820 } 00821 return TRUE; // Success 00822 } 00823 00824 00825 00826 /******************************************************************************************** 00827 00828 > BOOL UndoableOperation::DoHideComplexRange(Range& RangeToHide) 00829 00830 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 00831 Created: 3/5/95 00832 Inputs: RangeToHide = the current range of nodes which need hiding 00833 Outputs: RangeToHide = the updated range of nodes which need hiding 00834 Returns: TRUE then all necessary objects were hidden correctly 00835 FALSE if failed to hide one of the objects 00836 00837 Purpose: This function is a pre process to DoHideNodes(). It calls each object in 00838 the range to hide themselves in a complex way. It may be that no objects 00839 in the selection require this facility, which means the RangeToHide object 00840 will be returned from this function unaffected. DoHideNodes() will then 00841 go through hidding all nodes in the range itself. If however a node responds 00842 to ComplexHide() and hides itself, the node will be removed from the range. 00843 This allows complex group nodes to control how they are hidden. 00844 Note, it is expected that ComplexHide() responders will hide nodes themselves 00845 This will have the automatic effect of removing them from the range. Hidden 00846 nodes do not appear in ranges. 00847 00848 ********************************************************************************************/ 00849 00850 BOOL UndoableOperation::DoHideComplexRange(Range& RangeToHide) 00851 { 00852 BOOL Failed=FALSE; 00853 Node *Next, *Current = RangeToHide.FindFirst(); 00854 00855 while (Current!=NULL) 00856 { 00857 // set the next node pointer. 00858 Next=RangeToHide.FindNext(Current); 00859 INT32 success = Current->ComplexHide(this, Next); 00860 // success=-1 then error unable to hide node 00861 // success= 0 then ignore 00862 // success= 1 then node has been hidden 00863 Failed=(success<0); 00864 if (Failed) 00865 break; 00866 00867 Current=Next; 00868 } 00869 00870 return (!Failed); 00871 } 00872 00873 00874 00875 /******************************************************************************************** 00876 00877 > BOOL UndoableOperation::DoMoveNodes(Range NodeRange, 00878 Node* Destination, 00879 AttachNodeDirection Direction) 00880 00881 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00882 Created: 13/9/93 00883 Inputs: NodeRange: The node range to be moved 00884 Destination: The context node 00885 Direction: The direction Node is to be attached to Destination 00886 00887 Outputs: - 00888 Returns: TRUE if successful 00889 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 00890 00891 Purpose: This high level function moves the range of nodes from their current position 00892 in the tree, and attaches them to the Destination node in the direction 00893 specified by Direction. 00894 00895 The nodes in the range maintain their relative z-order when they move. 00896 00897 Errors: - 00898 SeeAlso: UndoableOperation::DoMoveNode 00899 00900 ********************************************************************************************/ 00901 00902 BOOL UndoableOperation::DoMoveNodes(Range NodeRange, 00903 Node* Destination, 00904 AttachNodeDirection Direction) 00905 { 00906 Node* Current = NodeRange.FindFirst(); // Get the first node to move 00907 ENSURE(Current != NULL, "Trying to move an empty range"); 00908 00909 Node* NextNode = NodeRange.FindNext(Current); 00910 00911 // Attach the first node in the range to the Destination node in the direction specified 00912 if (!DoMoveNode(Current, Destination, Direction)) 00913 { 00914 return FALSE; // Failed to move Current 00915 } 00916 00917 Node* PrevNode = Current; 00918 00919 // Get the next node in the range 00920 Current = NextNode; 00921 00922 // Attach all other nodes in the range 00923 while (Current != NULL) 00924 { 00925 NextNode = NodeRange.FindNext(Current); 00926 if (!DoMoveNode(Current, PrevNode, NEXT)) 00927 { 00928 return FALSE; // Failed to move Current 00929 } 00930 PrevNode = Current; 00931 Current = NextNode; 00932 } 00933 return TRUE; 00934 } 00935 00936 /******************************************************************************************** 00937 00938 > BOOL UndoableOperation::DoMoveNode(Node* NodeToMove, 00939 Node* Destination, 00940 AttachNodeDirection Direction) 00941 00942 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00943 Created: 13/9/93 00944 Inputs: NodeToMove: The node to be moved, cannot be an attribute 00945 Destination: The context node 00946 Direction: The direction Node is to be attached to Destination 00947 00948 Outputs: - 00949 Returns: TRUE if successful 00950 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 00951 00952 Purpose: This high level function moves NodeToMove from its current position in the 00953 tree, and attaches it to Destination in the direction specified by Direction. 00954 00955 Note: It is illegal to move an attribute in the tree. This is because it may 00956 be deleted as a result of attribute optimisation. It is unlikely that 00957 you would want to move an attribute anyway.An ERROR2 will be generated 00958 if this occurs. 00959 Update, 22/03/2005: 00960 It is legal to move an effect attribute because they are not optimised 00961 in the same way as other attributes. 00962 00963 Errors: - 00964 SeeAlso: UndoableOperation::DoMoveNodes 00965 00966 ********************************************************************************************/ 00967 00968 BOOL UndoableOperation::DoMoveNode(Node* NodeToMove, 00969 Node* Destination, 00970 AttachNodeDirection Direction) 00971 00972 { 00973 ERROR2IF(NodeToMove->IsAnAttribute() && !((NodeAttribute*)NodeToMove)->IsEffectAttribute(), FALSE, "Trying to move an attribute, this is bad"); 00974 00975 // -------------------------------------------------------------------- 00976 // We have to move attributes by copying... 00977 if (NodeToMove->IsAnAttribute()) 00978 { 00979 // Copy the attribute to the destination location 00980 // Then hide the original 00981 00982 // Copy 00983 NodeAttribute* pCopyAttr = NULL; 00984 BOOL bOK = FALSE; 00985 CALL_WITH_FAIL(((NodeAttribute*)NodeToMove)->NodeCopy((Node**)&pCopyAttr), this, bOK); 00986 if (!bOK) return FALSE; // No room to take a copy of the node 00987 00988 // Create a hide node action to hide the node when we undo 00989 pCopyAttr->AttachNode(Destination, Direction); 00990 HideNodeAction* UndoHideNodeAction; 00991 if (HideNodeAction::Init(this, this->GetUndoActions(), pCopyAttr, TRUE, (Action**)(&UndoHideNodeAction)) 00992 == AC_FAIL) 00993 { 00994 pCopyAttr->CascadeDelete(); 00995 delete pCopyAttr; 00996 return FALSE; 00997 } 00998 00999 // Hide 01000 if (!DoHideNode(NodeToMove, TRUE, NULL, TRUE)) 01001 return FALSE; 01002 01003 return TRUE; 01004 } 01005 01006 // -------------------------------------------------------------------- 01007 // Objects are physically moved, leaving a NodeHidden in the original location 01008 // with a "hyperspace" reference to the object in its final location. 01009 // 01010 // We must flag that we are only moving the node, so we don't 01011 // try and called the 'Hiding Node' function, which should only 01012 // be called when we are 'Really' hiding the node. 01013 MovingNode = TRUE; 01014 01015 // Find out if the node is selected 01016 BOOL IsSelected = NodeToMove->IsSelected(); 01017 01018 // Before we move the object determine what the source layer is 01019 Layer* pSrcLayer = NULL; 01020 if (NodeToMove->IsAnObject()) 01021 { 01022 // Only consider ink nodes 01023 pSrcLayer = (Layer*) (NodeToMove->FindParent(CC_RUNTIME_CLASS(Layer))); 01024 } 01025 01026 // Hide it 01027 HideNodeAction* UndoHideNodeAction; 01028 // NodeRenderableBounded* pBounded = NULL; 01029 01030 // hide the node 01031 if (!DoHideNode(NodeToMove, FALSE, NULL, FALSE)) 01032 goto Failed; 01033 01034 // build the action 01035 if ( HideNodeAction::Init(this, 01036 &UndoActions, 01037 NodeToMove, 01038 FALSE, // Don't include the 01039 // subtree size 01040 (Action**)(&UndoHideNodeAction), 01041 01042 FALSE) // Don't tell subtree 01043 == AC_FAIL) 01044 { 01045 goto Failed; 01046 }; 01047 01048 // Move the node to its new location 01049 NodeToMove->MoveNode(Destination, Direction); 01050 if (IsSelected) 01051 NodeToMove->SetSelected(IsSelected); 01052 01053 MovingNode = FALSE; 01054 01055 if (NodeToMove->IsAnObject()) 01056 { 01057 // If the object has moved to a new layer then we need to invalidate both old and new 01058 // layers 01059 Layer* pDestLayer = (Layer*) (NodeToMove->FindParent(CC_RUNTIME_CLASS(Layer))); 01060 if ((pSrcLayer != pDestLayer) && pSrcLayer && pDestLayer) 01061 { 01062 pSrcLayer->InvalidateBoundingRect(); 01063 pDestLayer->InvalidateBoundingRect(); 01064 } 01065 01066 if (pSrcLayer) 01067 { 01068 // Note that this layer has been edited 01069 pSrcLayer->SetEdited(TRUE); 01070 //#ifdef _DEBUG 01071 // // Tell the frame gallery to update its display of the frame 01072 // BROADCAST_TO_ALL(LayerMsg(pSrcLayer, LayerMsg::LayerReason::REDRAW_LAYER)); 01073 //#endif 01074 } 01075 01076 if (pDestLayer && (pSrcLayer != pDestLayer)) 01077 { 01078 // Note that this layer has been edited 01079 pDestLayer->SetEdited(TRUE); 01080 //#ifdef _DEBUG 01081 // // Tell the frame gallery to update its display of the frame 01082 // BROADCAST_TO_ALL(LayerMsg(pDestLayer, LayerMsg::LayerReason::REDRAW_LAYER)); 01083 //#endif 01084 } 01085 01086 } 01087 // finish happy 01088 return (TRUE); 01089 01090 Failed: 01091 MovingNode = FALSE; // Ensure we clear this is an error occurred 01092 return FALSE; 01093 } 01094 01095 01096 01097 01098 /******************************************************************************************** 01099 01100 > BOOL UndoableOperation::DoSaveCopyOfNodes(Range NodeRange) 01101 01102 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01103 Created: 7/3/94 01104 Inputs: NodeRange: The range of nodes to save 01105 Outputs: - 01106 Returns: - 01107 Purpose: This fn takes a copy of the nodes in the range. The copies are restored when 01108 the operation is undone. 01109 01110 Errors: - 01111 SeeAlso: UndoableOperation::DoSaveCopyOfNode 01112 01113 ********************************************************************************************/ 01114 01115 BOOL UndoableOperation::DoSaveCopyOfNodes(Range NodeRange) 01116 { 01117 Node* Current = NodeRange.FindFirst(); 01118 BOOL CopiedOK; 01119 Node* TheCopy; 01120 HideNodeAction* UndoHideNodeAction; 01121 while (Current != NULL) 01122 { 01123 // Salary check 01124 if (!Current->IsBounded()) 01125 { 01126 ERROR3("Current is not a NodeRenderable node"); 01127 continue; 01128 } 01129 01130 // Make a copy of Current 01131 CALL_WITH_FAIL(Current->NodeCopy(&TheCopy), this, CopiedOK); 01132 if (!CopiedOK) 01133 { 01134 return FALSE; // No room to take a copy of the node 01135 } 01136 01137 // We need to insert the new node into the tree, but we can't use DoInsertNewNode 01138 // as this will insert an unwanted NideNode action 01139 TheCopy->AttachNode(Current, PREV); 01140 TheCopy->SetSelected(FALSE); 01141 ((NodeRenderableBounded*)TheCopy)->ValidateBoundingRect(); 01142 ((NodeRenderableBounded*)TheCopy)->InvalidateBoundingRect(); 01143 01144 // Hide the copy, creating a ShowNodeAction to show the copy when we undo 01145 if (!DoHideNode(TheCopy, 01146 TRUE // The hidden node is in the operation history 01147 ) 01148 ) 01149 { 01150 return FALSE; 01151 } 01152 01153 // Create a Hide node action to hide the current node when we undo 01154 if ( HideNodeAction::Init(this, 01155 &UndoActions, 01156 Current, 01157 FALSE, 01158 (Action**)(&UndoHideNodeAction)) 01159 == AC_FAIL) 01160 { 01161 return FALSE; 01162 }; 01163 01164 Current = NodeRange.FindNext(Current); 01165 } 01166 return TRUE; 01167 } 01168 01169 /******************************************************************************************** 01170 01171 > BOOL UndoableOperation::DoSaveCopyOfNode(NodeRenderable* Node) 01172 01173 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01174 Created: 7/3/94 01175 Inputs: Node: 01176 Outputs: - 01177 Returns: - 01178 Purpose: This fn saves a copy of the node in the operation history. When the operation 01179 is undone the copy is restored. 01180 01181 Errors: - 01182 SeeAlso: UndoableOperation::DoSaveCopyOfNodes 01183 01184 ********************************************************************************************/ 01185 01186 BOOL UndoableOperation::DoSaveCopyOfNode(NodeRenderable* Node) 01187 { 01188 ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed"); 01189 RangeControl rc( TRUE, TRUE, TRUE ); // All nodes in range 01190 Range range( Node, Node, rc ); 01191 return DoSaveCopyOfNodes( range ); 01192 } 01193 01194 /******************************************************************************************** 01195 01196 > BOOL UndoableOperation::DoTransformNodes(Range NodeRange, 01197 TransformBase* Trans) 01198 01199 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01200 Created: 13/9/93 01201 Inputs: Range: The range of nodes to transform 01202 Trans: The transform to apply to every node in the range 01203 Outputs: - 01204 Returns: TRUE if successful 01205 01206 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01207 The Trans is deleted. 01208 01209 Purpose: This high level Do function applies the transform Trans to each 01210 object in the range. 01211 01212 Don't delete the Trans object, it's deleted in TransformNodeAction slaughter 01213 method. 01214 01215 Errors: - 01216 SeeAlso: UndoableOperation::DoTransformNode 01217 01218 01219 ********************************************************************************************/ 01220 01221 BOOL UndoableOperation::DoTransformNodes(Range NodeRange, 01222 TransformBase* Trans) 01223 { 01224 // If the Trans is invertible then create a Transform node action to undo the transform 01225 // Note at this point Trans is not the inverse Transformation, but it will be soon !! 01226 // Node * pParent = NULL; 01227 // Node * pNodeToTransform = NULL; 01228 01229 // DMc 17/5/99 01230 // include the parent nodes in the range if they wish to be transformed with 01231 // their children by changing the range control flags 01232 RangeControl rg = NodeRange.GetRangeControlFlags(); 01233 rg.PromoteToParent = TRUE; 01234 NodeRange.Range::SetRangeControl(rg); 01235 01236 // DoInvalidateNodesRegions(NodeRange, TRUE); 01237 DoInvalidateNodesRegions(NodeRange, TRUE, FALSE, FALSE, FALSE); // Don't forcibly recache (see bHaveTransformedAllCached) 01238 01239 if (Trans->IsInvertable()) 01240 { 01241 TransformNodeAction* UndoTransformNodeAction; 01242 01243 if ( TransformNodeAction::Init(this, 01244 &UndoActions, 01245 NodeRange, 01246 Trans, 01247 NULL, 01248 (Action**)(&UndoTransformNodeAction)) 01249 == AC_FAIL) 01250 01251 { 01252 delete Trans; // for consistency 01253 return FALSE; // Let's get out while we can 01254 }; 01255 01256 // Scan the range and transform each node 01257 Node* Current = NodeRange.FindFirst(); 01258 Node* Next; 01259 // Node* Parent = NULL; 01260 // BOOL bTransform = FALSE; 01261 while (Current != NULL) 01262 { 01263 Next = NodeRange.FindNext(Current); 01264 01265 if (Current->IsNodeRenderableClass()) 01266 { 01267 // Prepare Control and Feedback flags 01268 Trans->bHaveTransformedCached = TRUE; 01269 Trans->bHaveTransformedChildren = TRUE; 01270 Trans->bTransformYourChildren = TRUE; 01271 01272 ((NodeRenderable *)Current)->Transform(*Trans); 01273 01274 // Compute feedback flags for caller 01275 Trans->bHaveTransformedAllCached = Trans->bHaveTransformedAllCached && Trans->bHaveTransformedCached; 01276 Trans->bHaveTransformedAllChildren = Trans->bHaveTransformedAllChildren && Trans->bHaveTransformedChildren; 01277 01278 // ---------------------------------------------- 01279 // Now do smart testing to see whether we can retain any cached info the node was holding 01280 if (Current->IsBounded()) 01281 { 01282 NodeRenderableBounded* pCurrentBound = (NodeRenderableBounded*) Current; 01283 01284 // If we have transformed all cached data successfully 01285 // And 01286 // We are translating (in which case the cached data will remain usable) 01287 // Or 01288 // The node was Direct (in which case the cached data comes from outside the tree where it can't have been transformed) 01289 // Then 01290 // Don't force the recacheing of the data for this node, just its parents 01291 if (Trans->bHaveTransformedCached && (Trans->IsTranslation() || pCurrentBound->HasCachedDirectBitmap())) 01292 // Yes, we can keep our cached info but we must tell our parents we have changed 01293 pCurrentBound->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only 01294 else 01295 // No we can't keep our cached info 01296 pCurrentBound->ReleaseCached(TRUE, TRUE); 01297 } 01298 // ---------------------------------------------- 01299 } 01300 01301 // JCF BODGE 01302 #ifdef _DEBUG 01303 else 01304 { 01305 TRACE( _T("Warning: UndoableOperation::DoTransformNodes: ") 01306 _T("Current node %s is not a NodeRenderableInk\n"), 01307 (LPCTSTR) Current->Name()); 01308 } 01309 #endif 01310 01311 Progress::Update(); 01312 Current = Next; 01313 } 01314 01315 // Invert the Trans which is in the TransformNodeAction 01316 ((TransInvertable*)Trans)->Invert(); 01317 } 01318 else // The Trans is not invertable 01319 { 01320 // We will have to go through all node in the range, making a copy and transforming it. 01321 // This used to use DoSaveCopyOfNodes, but this dosen't work as it is not completly undoable 01322 Node* Current = NodeRange.FindFirst(); 01323 Node* Next = NULL; 01324 while (Current != NULL) 01325 { 01326 Next = NodeRange.FindNext(Current); 01327 if (Current->IsNodeRenderableClass()) 01328 { 01329 // Make a copy of Current 01330 BOOL CopiedOK = TRUE; 01331 Node* TheCopy = NULL; 01332 CALL_WITH_FAIL(Current->NodeCopy(&TheCopy), this, CopiedOK); 01333 if (!CopiedOK) 01334 return FALSE; 01335 01336 // transform the copy 01337 ((NodeRenderable*)TheCopy)->Transform(*Trans); 01338 01339 // put the copy into the tree 01340 if (!DoInsertNewNode((NodeRenderableBounded*)TheCopy, Current, PREV, TRUE, FALSE)) 01341 { 01342 delete TheCopy; 01343 return FALSE; 01344 } 01345 01346 // Hide the original 01347 if (!DoHideNode(Current, FALSE)) 01348 return FALSE; 01349 } 01350 01351 // JCF BODGE 01352 #ifdef _DEBUG 01353 else 01354 { 01355 TRACE( _T("Warning: UndoableOperation::DoTransformNodes: ") 01356 _T("Current node %s is not a NodeRenderableInk\n"), 01357 (LPCTSTR) Current->Name()); 01358 } 01359 #endif 01360 01361 Progress::Update(); 01362 Current = Next; 01363 } 01364 } 01365 return (TRUE); // Success 01366 } 01367 01368 /******************************************************************************************** 01369 01370 > BOOL UndoableOperation::DoTransformNode(NodeRenderableInk* NodeToTransform, 01371 TransformBase* Trans); 01372 01373 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01374 Created: 13/9/93 01375 Inputs: NodeToTransform: The node to transform 01376 Trans: The transform to apply to the node 01377 Outputs: - 01378 Returns: TRUE if successful 01379 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01380 01381 Purpose: This high level Do function applies the transform Trans to the 01382 NodeToTransform 01383 01384 Errors: - 01385 SeeAlso: UndoableOperation::DoTransformNodes 01386 01387 ********************************************************************************************/ 01388 01389 BOOL UndoableOperation::DoTransformNode(NodeRenderableInk* NodeToTransform, 01390 TransformBase* Trans) 01391 { 01392 ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed"); 01393 // RangeControl rc = { TRUE, TRUE, TRUE }; // All nodes in range 01394 Range range( NodeToTransform, NodeToTransform, RangeControl( TRUE, TRUE, TRUE ) ); 01395 return( DoTransformNodes( range, Trans ) ); 01396 } 01397 01398 01399 /******************************************************************************************** 01400 01401 > BOOL UndoableOperation::DoSelectNode(NodeRenderableInk* NodeToSelect) 01402 01403 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01404 Created: 20/10/93 01405 Inputs: NodeToSelect: The node that is to be selected 01406 Outputs: - 01407 Returns: TRUE if successful 01408 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01409 Purpose: The function selects NodeToSelect. It also generates an action which will 01410 deselect the node when executed. 01411 Errors: - 01412 SeeAlso: UndoableOperation::DoDeselectNode 01413 01414 ********************************************************************************************/ 01415 01416 BOOL UndoableOperation::DoSelectNode(NodeRenderableInk* NodeToSelect, Spread *Parent) 01417 { 01418 SelectDeselectAction* UndoSelectDeselectAction; 01419 // Create an action to deselect the NodeToSelect when we undo. 01420 if ( SelectDeselectAction::Init(this, 01421 &UndoActions, 01422 NodeToSelect, 01423 Parent, 01424 (Action**)(&UndoSelectDeselectAction)) 01425 != AC_FAIL) 01426 { 01427 // We shall be able to deselect the object if we fail so select it. 01428 NodeToSelect->Select(FALSE); 01429 return (TRUE); 01430 } 01431 else 01432 return (FALSE); 01433 } 01434 01435 /******************************************************************************************** 01436 01437 > BOOL UndoableOperation::DoInvalidateNodesRegions(Range NodeRange, BOOL IncludeBlobs, 01438 BOOL UndoBlobs = FALSE, 01439 BOOL IfBgRedraw = FALSE, 01440 BOOL bForceRecache = TRUE) 01441 01442 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01443 Created: 28/2/94 01444 Inputs: NodeRange: The range of nodes 01445 IncludeBlobs: When TRUE we take the bounding rectangle of the objects 01446 and their blobs. 01447 UndoBlobs: TRUE, Invert the 'IncludeBlobs' for undo/redo this op. 01448 FALSE, use the same 'IncludeBlobs' for undo/redo. 01449 01450 Outputs: - 01451 Returns: TRUE if successful 01452 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01453 01454 Purpose: This low level Do function invalidates a region which is the union of 01455 the bounding rectangles of each node in the range. When the includeBlobs 01456 flag is TRUE the region which gets invalidated includes the objects blobs. 01457 01458 constraints: 01459 01460 All members of the range must be on the same spread 01461 No member of the range can be hidden 01462 01463 Errors: An ENSURE will occur if the range of nodes is empty, in a retail build 01464 the function will return TRUE but do nothing. 01465 01466 SeeAlso: UndoableOperation::DoInvalidateNodeRegion 01467 01468 ********************************************************************************************/ 01469 01470 BOOL UndoableOperation::DoInvalidateNodesRegions(Range NodeRange, BOOL IncludeBlobs, 01471 BOOL UndoBlobs, 01472 BOOL IfBgRedraw, 01473 BOOL bForceRecache) 01474 { 01475 ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed"); 01476 01477 if (UndoBlobs) 01478 { 01479 // Undo blobs = TRUE 01480 // We will use different 'IncludeBlobs' when we undo/redo this op 01481 // to the one we use initially. 01482 UndoBlobs = !IncludeBlobs; 01483 } 01484 else 01485 { 01486 // Undo blobs = FALSE 01487 // We will use the same 'IncludeBlobs' when we undo/redo this op 01488 // as the one we use initially. 01489 UndoBlobs = IncludeBlobs; 01490 } 01491 01492 BOOL bOldValue = NodeRange.SetPromoteToParent(TRUE); 01493 NodeRenderableBounded* CurrentNode = (NodeRenderableBounded*)NodeRange.FindFirst(); 01494 NodeRange.SetPromoteToParent(bOldValue); 01495 01496 if (CurrentNode == NULL) 01497 return TRUE; // The range is empty so NOP 01498 01499 // ENSURE the range of nodes is not empty 01500 // ENSURE (CurrentNode != NULL, "Calling DoInvalidateNodesRegions with an empty range"); 01501 01502 // ENSURE(CurrentNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderable)), 01503 // "Trying to invalidate a non NodeRenderable node"); 01504 01505 // Find the spread 01506 Spread* pSpread = (Spread*)CurrentNode->FindParent(CC_RUNTIME_CLASS(Spread)); 01507 01508 01509 01510 // Matt 27/11/2000 01511 // OK, here's the beef... At present, if you group a shadowed object, the tree structure correctly 01512 // reflects this as a NodeShadowController with a NodeGroup parent... Now, when we've clicked on the 01513 // 'Set New Design' button, the NodeGroup is passed OK and is hidden after being copied (as was intended) 01514 // but the NodeShadowController beneath this is then tested: When asked what it's parent spread is, it 01515 // will promptly keel over, triggering the following ENSURE as its parent (the NodeGroup) no longer has 01516 // a pointer to its parent (ie it is NULL) - to cater for this, if we reach this point and we have no 01517 // parent spread, we can assume one of two things: 01518 // 1. The node above us has been hidden, therefore we are already implicitly hidden and we shouldn't 01519 // undertake any special work 01520 // 2. Everything is stuffed so bad in our tree that it really doesn't matter what we do here - we're 01521 // about to die anyhow... 01522 // Therefore, we can just return without failing with an ensure... 01523 // 01524 // ENSURE(pSpread != NULL, "Spread is NULL"); 01525 01526 01527 01528 if (pSpread) 01529 { 01530 if (IfBgRedraw) 01531 { 01532 // ------------------------------------------------------------------------------------- 01533 // Try and create an InvalidateRegionAction 01534 InvalidateRegionIfBgRedrawAction* UndoInvalidateRgnAction; 01535 01536 // Create an InvalidateRegionAction 01537 // Note that unfortunatly the action has to store the node range, rather than the 01538 // bounding rectangle because GetBlobBoundingRect must be calculated on the fly. 01539 01540 if ( InvalidateRegionIfBgRedrawAction::Init(this, 01541 &UndoActions, 01542 NodeRange, 01543 pSpread, 01544 UndoBlobs, 01545 ( Action**)(&UndoInvalidateRgnAction)) == AC_FAIL) 01546 { 01547 return FALSE; // Failed to create action 01548 } 01549 01550 if (!GetApplication()->IsBgRendering()) 01551 { 01552 return TRUE; // Ignore if no Bg Rendering in progress 01553 } 01554 01555 // GetApplication()->DeleteRenderRegions(DocView::GetSelected()); 01556 // GetApplication()->BgRendering = FALSE; 01557 } 01558 else 01559 { 01560 // ------------------------------------------------------------------------------------- 01561 // Try and create an InvalidateRegionAction 01562 InvalidateRegionAction* UndoInvalidateRgnAction; 01563 01564 // Create an InvalidateRegionAction 01565 // Note that unfortunatly the action has to store the node range, rather than the 01566 // bounding rectangle because GetBlobBoundingRect must be calculated on the fly. 01567 01568 if ( InvalidateRegionAction::Init(this, 01569 &UndoActions, 01570 NodeRange, 01571 pSpread, 01572 UndoBlobs, 01573 ( Action**)(&UndoInvalidateRgnAction)) == AC_FAIL) 01574 { 01575 return FALSE; // Failed to create action 01576 } 01577 } 01578 01579 ENSURE(pOurDoc != 0, "There was no document in DoInvalidateNodesRegions"); 01580 01581 // Invalidate the bounds of each node in the range 01582 01583 if (pOurDoc != NULL) 01584 { 01585 DocRect TempRect; 01586 01587 BOOL bOldValue = NodeRange.SetPromoteToParent(TRUE); 01588 while (CurrentNode != NULL) 01589 { 01590 // Because we had a Range param, we could possibly be given any legal node in the tree, 01591 // e.g. the Insert node, not just renderable bounded objects, hence this 'if' statement 01592 if (CurrentNode->IsAnObject() || CurrentNode->IsPaper()) 01593 { 01594 ENSURE(CurrentNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)), 01595 "Trying to invalidate a non NodeRenderable node"); 01596 01597 TempRect = (IncludeBlobs ? 01598 (CurrentNode->GetUnionBlobBoundingRect()): 01599 (CurrentNode->GetBoundingRect()) 01600 ); 01601 TempRect = TempRect.Union(CurrentNode->GetEffectStackBounds()); 01602 01603 // Accumulate this invalid region in a pending list until the Op 01604 // finishes 01605 // Mark the region as requiring object to recache any cached 01606 // data they may be holding because the Op may have changed it 01607 // pOurDoc->ForceRedraw(pSpread, TempRect, TRUE, bForceRecache ? CurrentNode : NULL); 01608 pOurDoc->ForceRedraw(pSpread, TempRect, TRUE, CurrentNode, bForceRecache); 01609 } 01610 01611 CurrentNode = (NodeRenderableBounded*) NodeRange.FindNext(CurrentNode); 01612 } 01613 NodeRange.SetPromoteToParent(bOldValue); 01614 } 01615 } 01616 01617 // Now invalidate the region 01618 return TRUE; 01619 } 01620 01621 /******************************************************************************************** 01622 01623 > BOOL UndoableOperation::DoInvalidateNodeRegion(NodeRenderableBounded* Node, 01624 BOOL IncludeBlobs, 01625 BOOL UndoBlobs, 01626 BOOL IfBgRedraw, 01627 BOOL bForceRecache) 01628 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01629 Created: 28/2/94 01630 Inputs: Node: The node that needs to be invalidated 01631 IncludeBlobs: When TRUE we take the blob bounding rectangle of the node 01632 UndoBlobs: TRUE, Invert the 'IncludeBlobs' for undo/redo this op. 01633 FALSE, use the same 'IncludeBlobs' for undo/redo. 01634 01635 Outputs: - 01636 Returns: TRUE if successful 01637 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01638 01639 Purpose: Invalidates the nodes bounding rectangle, or blob bounding rectangle 01640 01641 Errors: - 01642 SeeAlso: UndoableOperation::DoInvalidateNodesRegions 01643 01644 ********************************************************************************************/ 01645 01646 BOOL UndoableOperation::DoInvalidateNodeRegion(NodeRenderableBounded* Node, 01647 BOOL IncludeBlobs, 01648 BOOL UndoBlobs, 01649 BOOL IfBgRedraw, 01650 BOOL bForceRecache) 01651 { 01652 ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed"); 01653 01654 // Range = Sel nodes, Unsel nodes, cross layers, Locked layers, Ignore Non-renderables, Ignore invisible layers 01655 RangeControl rc( TRUE, TRUE, TRUE, FALSE, TRUE, TRUE ); 01656 Range range( Node, Node, rc ); 01657 01658 return( DoInvalidateNodesRegions( range, IncludeBlobs, UndoBlobs, IfBgRedraw, bForceRecache ) ); 01659 } 01660 01661 01662 01663 /******************************************************************************************** 01664 01665 > BOOL UndoableOperation::DoInvalidateRegion(Spread* pSpread, DocRect& InvalidRegion) 01666 01667 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01668 Created: 25/8/94 01669 Inputs: pSpread - The spread that the region to invalidate is on 01670 InvalidRegion - The region that you want to invalidate 01671 Returns: TRUE if it all worked, FALSE if not (TIDYUP THEN CALL End()!) 01672 Purpose: Invalidates the region specified and adds the appropriate action to the 01673 action list for the current operation. If you are invalidating a region 01674 associated with a node in the tree then you should be using 01675 DoInvalidateNodeRegion() or DoInvalidateNodesRegion(). 01676 SeeAlso: UndoableOperation::DoInvalidateNodeRegion 01677 01678 ********************************************************************************************/ 01679 01680 BOOL UndoableOperation::DoInvalidateRegion( Spread *pSpread, const DocRect &InvalidRegion ) 01681 { 01682 // make sure that things are worth doing 01683 ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed"); 01684 ENSURE(pSpread!=NULL, "Attempt to invalidate a region on a null spread."); 01685 01686 // If the region is empty we could save everyone a lot of trouble 01687 if (InvalidRegion.IsEmpty()) 01688 return TRUE; 01689 01690 // Try and create an InvalidateArbitaryRegionAction and fail if it does not work 01691 InvalidateArbitaryAction* pAction; 01692 ActionCode ActCode = InvalidateArbitaryAction::Init( this, &UndoActions, pSpread, 01693 InvalidRegion, (Action**)(&pAction)); 01694 01695 // See if it worked 01696 if (ActCode==AC_FAIL) 01697 return FALSE; 01698 01699 // Now we can invalidate the region for the caller 01700 // First we need a document 01701 ENSURE(pOurDoc != NULL, "There was no document in DoInvalidateRegions"); 01702 01703 // Invalidate the bounds of each node in the range 01704 if (pOurDoc != NULL) 01705 // Accumulate this invalid region in a pending list until the Op 01706 // finishes 01707 // Mark the region as requiring object to recache any cached 01708 // data they may be holding because the Op may have changed it 01709 pOurDoc->ForceRedraw(pSpread, InvalidRegion, TRUE, pSpread); 01710 01711 // Tell them that everything worked out just fine 01712 return TRUE; 01713 } 01714 01715 /******************************************************************************************** 01716 01717 > BOOL UndoableOperation::DoDeselectNode(NodeRenderableInk* NodeToDeselect) 01718 01719 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01720 Created: 20/10/93 01721 Inputs: NodeToDeselect: The node that is to be de-selected 01722 Outputs: - 01723 Returns: TRUE if successful 01724 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01725 Purpose: The function de-selects NodeToDeselect. It also generates an action which will 01726 select the node when executed. 01727 Errors: - 01728 SeeAlso: UndoableOperation::DoDeselectNode 01729 01730 ********************************************************************************************/ 01731 01732 01733 BOOL UndoableOperation::DoDeselectNode(NodeRenderableInk* NodeToDeselect, Spread *Parent) 01734 { 01735 SelectDeselectAction* UndoSelectDeselectAction; 01736 // Create an action to deselect the NodeToSelect when we undo. 01737 if ( SelectDeselectAction::Init(this, 01738 &UndoActions, 01739 NodeToDeselect, 01740 Parent, 01741 (Action**)(&UndoSelectDeselectAction)) 01742 != AC_FAIL) 01743 { 01744 // We shall be able to select the object if we fail so deselect it. 01745 NodeToDeselect->DeSelect(FALSE); 01746 return (TRUE); 01747 } 01748 else 01749 return (FALSE); 01750 } 01751 01752 /******************************************************************************************** 01753 01754 > BOOL UndoableOperation::DoMakeShapes(Range& NodeRange) 01755 01756 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01757 Created: 03/05/94 01758 01759 Inputs: NodeRange: The Range of nodes to make into shapes 01760 01761 Returns: TRUE if successful 01762 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01763 Purpose: This low level Do function calls the DoBecomeA virtual function on all 01764 SelectedNodes. It is the node's DoBecomeA function which is responsible 01765 for generating actions. 01766 Errors: - 01767 SeeAlso: Node::DoBecomeA 01768 01769 ********************************************************************************************/ 01770 01771 BOOL UndoableOperation::DoMakeShapes(Range NodeRange) 01772 { 01773 Node* CurrentNode = NodeRange.FindFirst(); 01774 Node* Next; 01775 01776 while (CurrentNode != NULL) 01777 { 01778 // BODGE - since the group is selected and won't be replaced by anything else, no need to reselect it 01779 // this fixes a bug where make shapes on grouped text stories/molds/blends don't leave the parent group 01780 // selected but selects the objects inside! 01781 BOOL reselect = !IS_A(CurrentNode,NodeGroup) && !IS_A(CurrentNode,NodeBlend); 01782 01783 BecomeA BecomeAPath(BECOMEA_REPLACE,CC_RUNTIME_CLASS(NodePath), this, reselect); 01784 Next = NodeRange.FindNext(CurrentNode); 01785 if (!(CurrentNode->DoBecomeA(&BecomeAPath))) 01786 { 01787 // Operation failed 01788 return FALSE; 01789 } 01790 CurrentNode = Next; 01791 } 01792 return TRUE; // Success 01793 } 01794 01795 /******************************************************************************************** 01796 01797 > BOOL UndoableOperation::DoCopyNodesToClipboard(Range NodeRange) 01798 01799 01800 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01801 Created: 03/05/94 01802 01803 Inputs: NodeRange: The Range of nodes to copy 01804 01805 Returns: TRUE if successful 01806 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01807 01808 Purpose: This Do function copies all nodes in the range to the internal clipboard. 01809 The objects are made attribute complete prior to being moved. 01810 01811 Before the copy begins StartComponentCopy is called on each DocComponent in 01812 the clipboard. 01813 01814 The nodes are then copied to the clipboard 01815 01816 CopyComponentData is then called on each node which has been copied to allow 01817 component data to be copied to the clipboard. 01818 01819 In the operations End method: If the operation has not failed then 01820 EndComponentCopy is called on each doc component do give them a chance to 01821 commit changes. If the operation has failed then AbortComponentCopy is called 01822 instead. 01823 01824 Errors: - 01825 SeeAlso: NodeRenderableInk::MakeAttributeComplete 01826 01827 01828 ********************************************************************************************/ 01829 01830 BOOL UndoableOperation::DoCopyNodesToClipboard(Range NodeRange) 01831 { 01832 BOOL CopiedOK; 01833 BOOL ok; 01834 01835 InternalClipboard* pInternalClip = InternalClipboard::Instance(); 01836 01837 // After the operation ends we will need to inform all DocComponents in the clipboard 01838 // of the outcome. 01839 InformDocComponentsOfOperationsOutcome(pInternalClip); 01840 01841 01842 // Inform all DocComponents in the clipboard that a copy is about to take place 01843 CALL_WITH_FAIL(pInternalClip->StartComponentCopy(), this, ok) 01844 if (!ok) 01845 { 01846 // Start Component copy has failed so abort operation 01847 // Note that AbortComponentCopy will get called in the ops end method 01848 return FALSE; 01849 } 01850 01851 // Try to copy the selection to the clipboard 01852 CALL_WITH_FAIL(InternalClipboard::CopyObjects(NodeRange, this), this, CopiedOK) 01853 01854 if (!CopiedOK) 01855 { 01856 return FALSE; 01857 } 01858 01859 // Now try and copy accross the component data 01860 01861 CALL_WITH_FAIL(InternalClipboard::CopyComponentData(pOurDoc), this, CopiedOK) 01862 01863 if (!CopiedOK) 01864 { 01865 return FALSE; 01866 } 01867 01868 01869 // Create action, this action will do nothing for undo but it's twin redo action will 01870 // copy the selection to the clipboard. 01871 CopyObjectsToClipboardAction* UndoCopyObjectsToClipboardAction; 01872 01873 if ( CopyObjectsToClipboardAction::Init(this, 01874 &UndoActions, 01875 NodeRange, 01876 ( Action**)(&UndoCopyObjectsToClipboardAction)) == AC_FAIL) 01877 { 01878 return FALSE; // Failed to create action 01879 } 01880 return TRUE; // Success 01881 } 01882 01883 /******************************************************************************************** 01884 01885 > BOOL UndoableOperation::DoFlattenRange(Range NodeRange) 01886 01887 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01888 Created: 3/5/94 01889 Inputs: - 01890 Outputs: - 01891 Returns: - 01892 Purpose: Not implemented yet 01893 Errors: - 01894 SeeAlso: - 01895 01896 ********************************************************************************************/ 01897 01898 BOOL UndoableOperation::DoFlattenRange(Range NodeRange) 01899 { 01900 return FALSE; 01901 } 01902 01903 /******************************************************************************************** 01904 01905 BOOL UndoableOperation::DoRemoveAttrTypeFromSubtree(Node* Subtree, RuntimeClass* AttrType, Node* pExceptThis = NULL); 01906 01907 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01908 Created: 24/6/94 01909 Inputs: Subtree: Subtree to remove nodes from 01910 NodeClass: The type of attribute to remove from the subtree 01911 pExceptThis: The node that shouldn't be removed! 01912 01913 Outputs: - 01914 Returns: TRUE if successful 01915 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 01916 01917 Purpose: Searches the subtree and every attribute which has type AttrType 01918 is hidden. 01919 Errors: - 01920 Scope: private 01921 SeeAlso: - 01922 01923 ********************************************************************************************/ 01924 01925 BOOL UndoableOperation::DoRemoveAttrTypeFromSubtree(Node* Subtree, CCRuntimeClass* AttrType, Node* pExceptThis) 01926 { 01927 // Traverse the subtree depth first 01928 Node* Current = Subtree->FindFirstDepthFirst(); 01929 Node* Next; 01930 while (Current != NULL) 01931 { 01932 Next = Current->FindNextDepthFirst(Subtree); 01933 // Determine if the Current node is to be hidden 01934 if (Current!=pExceptThis && Current->IsKindOf(CC_RUNTIME_CLASS(NodeAttribute))) 01935 { 01936 if (((NodeAttribute*)Current)->GetAttributeType() == AttrType) 01937 { 01938 if (Current->FindParent()->DiscardsAttributeChildren()) 01939 { 01940 Current->CascadeDelete(); 01941 delete Current; 01942 } 01943 else 01944 { 01945 if(!DoHideNode(Current, 01946 TRUE // Include the subtree size 01947 )) 01948 { 01949 return FALSE; // Operation failed 01950 } 01951 } 01952 } 01953 } 01954 Current = Next; 01955 } 01956 return TRUE; 01957 } 01958 01959 01960 /******************************************************************************************** 01961 01962 > BOOL UndoableOperation::DoChangeSelection(NodePath* ThisNode, INT32 Index, BOOL NewState) 01963 01964 Author: Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com> 01965 Created: 19/7/94 01966 Inputs: ThisNode points at the NodePath whose state we want to change 01967 Index is the element we want to change 01968 NewState is the new state of selection we want to set 01969 Outputs: - 01970 Returns: TRUE if succeeded, FALSE otherwise 01971 Purpose: This 'Do' function changes the selected state of a particular element in a path 01972 it creates all the necessary undo information so that the selection can be undone 01973 Errors: - 01974 SeeAlso: - 01975 01976 ********************************************************************************************/ 01977 01978 BOOL UndoableOperation::DoChangeSelection(NodePath* ThisNode, INT32 Index, BOOL NewState) 01979 { 01980 #ifndef STANDALONE 01981 01982 ModifyFlagsAction* UnAction; 01983 PathFlags* Flags = ThisNode->InkPath.GetFlagArray(); 01984 if (Flags[Index].IsSelected != NewState) 01985 { 01986 ActionCode Act = ModifyFlagsAction::Init(this, 01987 &UndoActions, 01988 Flags[Index], 01989 Index, 01990 ThisNode, 01991 (Action**)&UnAction); 01992 if (Act == AC_FAIL) 01993 return FALSE; 01994 01995 Flags[Index].IsSelected = NewState; 01996 return TRUE; 01997 } 01998 01999 #endif 02000 02001 return TRUE; 02002 } 02003 02004 /******************************************************************************************** 02005 02006 > BOOL UndoableOperation::DoDeletePathSection(NodePath* ThisPath, INT32 Index, INT32 NumElements, BOOL RedrawPath = TRUE) 02007 02008 Author: Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com> 02009 Created: 19/7/94 02010 Inputs: ThisPath points at the NodePath whose state we want to change 02011 Index is the element we want to change 02012 NumElements is the number of elements to be deleted 02013 RedrawPath - TRUE if the path ares should be redrawn, FALSE if the caller will do it 02014 Outputs: - 02015 Returns: TRUE if succeeded, FALSE otherwise 02016 Purpose: This 'Do' function changes the selected state of a particular element in a path 02017 it creates all the necessary undo information so that the selection can be undone 02018 Errors: - 02019 SeeAlso: - 02020 02021 ********************************************************************************************/ 02022 02023 BOOL UndoableOperation::DoDeletePathSection(NodePath* ThisPath, INT32 Index, INT32 NumElements, BOOL RedrawPath) 02024 { 02025 #ifndef STANDALONE 02026 02027 InsertPathElementAction* UnAction = NULL; 02028 ActionCode Act; 02029 Act = InsertPathElementAction::Init(this, &UndoActions, NumElements, Index, 02030 (Action**)(&UnAction)); 02031 if (Act == AC_FAIL) 02032 return FALSE; 02033 02034 Document* pDocument = GetWorkingDoc(); 02035 ERROR2IF(pDocument == NULL, FALSE, "There was no Document when deleteing path elements"); 02036 Spread* pSpread = ThisPath->FindParentSpread(); 02037 ERROR2IF(pSpread == NULL, FALSE, "Path had no parent spread"); 02038 02039 // Force a re-draw of the place where the path used to be 02040 if (RedrawPath) 02041 { 02042 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 02043 // Mark the region as requiring object to recache any cached 02044 // data they may be holding because the Op may have changed it 02045 pDocument->ForceRedraw(pSpread, Invalid, FALSE, ThisPath); 02046 } 02047 02048 if ((Act != AC_NORECORD) && (UnAction != NULL)) 02049 { 02050 // I have to claim some memory to store the elements I'm deleting... 02051 PathVerb* ChangedVerbs = NULL; 02052 DocCoord* ChangedCoords = NULL; 02053 PathFlags* ChangedFlags = NULL; 02054 ALLOC_WITH_FAIL(ChangedVerbs,(PathVerb*) CCMalloc(NumElements * sizeof(PathVerb)),this); 02055 ALLOC_WITH_FAIL(ChangedCoords,(DocCoord*) CCMalloc(NumElements * sizeof(DocCoord)),this); 02056 ALLOC_WITH_FAIL(ChangedFlags,(PathFlags*) CCMalloc(NumElements * sizeof(PathFlags)),this); 02057 02058 if (!ChangedVerbs || !ChangedCoords || !ChangedFlags) 02059 { 02060 if (ChangedVerbs) CCFree(ChangedVerbs); 02061 if (ChangedCoords) CCFree(ChangedCoords); 02062 if (ChangedFlags) CCFree(ChangedFlags); 02063 return FALSE; 02064 } 02065 02066 // Get pointers to all the arrays of the path 02067 PathVerb* Verbs = NULL; 02068 DocCoord* Coords = NULL; 02069 PathFlags* Flags = NULL; 02070 ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags); 02071 02072 // Now copy the data from the path into the arrays 02073 for (INT32 i=0;i<NumElements;i++) 02074 { 02075 ChangedVerbs[i] = Verbs[Index+i]; 02076 ChangedCoords[i] = Coords[Index+i]; 02077 ChangedFlags[i] = Flags[Index+i]; 02078 } 02079 02080 // Now pass these arrays to the Insert action 02081 UnAction->RecordPath(ChangedVerbs, ChangedFlags, ChangedCoords, ThisPath); 02082 } 02083 02084 // Now we've recorded the data we're about to delete, let's delete it 02085 ThisPath->InkPath.DeleteSection(Index, NumElements); 02086 02087 if (RedrawPath) 02088 { 02089 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 02090 // Accumulate this invalid region in a pending list until the Op 02091 // finishes 02092 // Mark the region as requiring object to recache any cached 02093 // data they may be holding because the Op may have changed it 02094 pDocument->ForceRedraw(pSpread, Invalid, FALSE, ThisPath); 02095 } 02096 02097 #endif 02098 return TRUE; 02099 } 02100 02101 /******************************************************************************************** 02102 02103 > BOOL UndoableOperation::DoAlterPathElement( NodePath* ThisPath, 02104 INT32 Index, 02105 DocCoord NewCoord, 02106 PathFlags NewFlags, 02107 PathVerb NewVerb); 02108 02109 02110 Author: Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com> 02111 Created: 27/7/94 02112 Inputs: ThisPath points at the NodePath whose state we want to change 02113 Index is the element we want to change 02114 NewCoord is the new coordinate value 02115 NewFlags is the new flags value 02116 NewVerb is the new verb value 02117 RedrawPath - TRUE if the screen area of the path should be invalidated, FALSE if not 02118 Outputs: - 02119 Returns: TRUE if succeeded, FALSE otherwise 02120 Purpose: This 'Do' function changes the selected element, recording undo information 02121 Errors: - 02122 SeeAlso: - 02123 02124 ********************************************************************************************/ 02125 02126 BOOL UndoableOperation::DoAlterPathElement( NodePath* ThisPath, 02127 INT32 Index, 02128 DocCoord NewCoord, 02129 PathFlags NewFlags, 02130 PathVerb NewVerb, 02131 BOOL RedrawPath) 02132 { 02133 #ifndef STANDALONE 02134 02135 // Here we're changing an element, so we have to change the element in the path, 02136 // recording undo information at the same time. 02137 ModifyElementAction* ModAction = NULL; 02138 02139 // Get pointers to all the arrays of the path 02140 PathVerb* Verbs = NULL; 02141 DocCoord* Coords = NULL; 02142 PathFlags* Flags = NULL; 02143 ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags); 02144 02145 // Create an undo action for this action, which is a ModifyElementAction 02146 ActionCode Act; 02147 Act = ModifyElementAction::Init(this, 02148 &UndoActions, 02149 Verbs[Index], 02150 Flags[Index], 02151 Coords[Index], 02152 Index, 02153 ThisPath, 02154 (Action**)(&ModAction)); 02155 if (Act == AC_FAIL) 02156 return FALSE; 02157 02158 Document* pDocument = GetWorkingDoc(); 02159 ERROR2IF(pDocument == NULL, FALSE, "There was no Document when deleteing path elements"); 02160 Spread* pSpread = ThisPath->FindParentSpread(); 02161 ERROR2IF(pSpread == NULL, FALSE, "Path had no parent spread"); 02162 02163 // Force a re-draw of the place where the path used to be 02164 if (RedrawPath) 02165 { 02166 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 02167 // Mark the region as requiring object to recache any cached 02168 // data they may be holding because the Op may have changed it 02169 pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath ); 02170 } 02171 02172 // Update the coords 02173 Verbs[Index] = NewVerb; 02174 Flags[Index] = NewFlags; 02175 Coords[Index] = NewCoord; 02176 02177 // The bounding rect may have changed 02178 ThisPath->InvalidateBoundingRect(); 02179 02180 // redraw the new area 02181 if (RedrawPath) 02182 { 02183 // redraw the new area 02184 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 02185 // Mark the region as requiring object to recache any cached 02186 // data they may be holding because the Op may have changed it 02187 pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath ); 02188 } 02189 02190 #endif 02191 return TRUE; 02192 } 02193 02194 02195 /******************************************************************************************** 02196 02197 > BOOL UndoableOperation::DoSmoothNodePath(NodePath* pThisNode, double smoothacc) 02198 02199 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 02200 Created: 16/11/94 02201 Inputs: pThisNode - a pointer to a node to smooth. 02202 smoothacc - the smooth accuracy, usually in the range (256^2, 8000^2) 02203 Outputs: a new tree node will be created as a result 02204 Returns: TRUE if the operation completed 02205 FALSE if the operation failed due to lack of memory. The environment matches 02206 that on entry to the function. 02207 Purpose: This function will smooth a node path given an accuracy. It will create a 02208 new node in the tree after the node specified and hide node passed if all 02209 is succesfull. 02210 02211 ********************************************************************************************/ 02212 02213 BOOL UndoableOperation::DoSmoothNodePath(NodePath* pThisNode, double smoothacc) 02214 { 02215 #ifndef STANDALONE 02216 02217 // Copy the nodepath and all its children, without placing the copy in the tree 02218 Node* pnode; 02219 if (!pThisNode->NodeCopy(&pnode)) 02220 return FALSE; 02221 NodePath* pSmoothNode = (NodePath*)pnode; 02222 02223 // ok, smooth the nodepaths path data 02224 if (!pSmoothNode->InkPath.SmoothRegions(smoothacc, FALSE, TRUE)) 02225 goto smoothfailed; 02226 02227 // update the smoothed paths bounding rectangle 02228 pSmoothNode->InvalidateBoundingRect(); 02229 02230 // Now stick the new path into the tree 02231 if (!DoInsertNewNode(pSmoothNode, pThisNode, NEXT, TRUE)) 02232 goto smoothfailed; 02233 02234 // Now we've formed a smoothed path, let's hide the original 02235 if (!DoHideNode(pThisNode,TRUE)) 02236 goto smoothfailed; 02237 02238 #endif 02239 02240 // return all is well 02241 return TRUE; 02242 02243 #ifndef STANDALONE 02244 02245 // failed to smooth the node so time to tidy up. 02246 smoothfailed: 02247 pSmoothNode->CascadeDelete(); 02248 delete pSmoothNode; 02249 return FALSE; 02250 02251 #endif 02252 } 02253 02254 02255 /******************************************************************************************** 02256 02257 > BOOL UndoableOperation::DoMakeNodeFromPath( NodePath* pContextNode, 02258 AttachNodeDirection Direction, 02259 Path* pParentPath 02260 BOOL CopyAttributes ) 02261 02262 Author: Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 02263 Created: 22/11/94 02264 Inputs: pContextNode = pointer to a context node in the tree to attach our 02265 path to 02266 direction = direction on which to attach the new NodePath object 02267 created by this call. 02268 pParentPath = Path to place in the new NodePath object created 02269 CopyAttributes = if TRUE then take the attributes applied to the context 02270 node and apply them to the new node. 02271 Outputs: - 02272 Returns: TRUE if the node was created successfully 02273 FALSE if the node could not be created (out of memory) 02274 Purpose: This functio will create a new nodepath and attach it in the specified 02275 direction to the context node specified. It will also copy attributes 02276 applied as children of the ConextNode to the new node if required. 02277 02278 ********************************************************************************************/ 02279 02280 BOOL UndoableOperation::DoMakeNodeFromPath( NodePath* pParentNode, 02281 Path* pParentPath, 02282 AttachNodeDirection Direction, 02283 BOOL CopyAttributes) 02284 { 02285 // Create the node path object we will stick in the tree. 02286 NodePath* pChildNode; 02287 02288 ALLOC_WITH_FAIL(pChildNode, new NodePath(), this); 02289 02290 if (!pChildNode) 02291 return FALSE; 02292 02293 BOOL ok; 02294 // make room for the new path in the node path. 02295 CALL_WITH_FAIL 02296 ( 02297 pChildNode->SetUpPath(pParentPath->GetNumCoords(),12), 02298 this,ok 02299 ); 02300 02301 if (!ok) 02302 { 02303 delete pChildNode; 02304 return FALSE; 02305 } 02306 02307 // now copy the path data in there. 02308 pChildNode->InkPath.CopyPathDataFrom(pParentPath); 02309 02310 if (CopyAttributes) 02311 { 02312 CCAttrMap * AttribMap = NULL; 02313 AttribMap = CCAttrMap::MakeAppliedAttrMap(pParentNode); 02314 02315 if (AttribMap) 02316 { 02317 AttribMap->ApplyAttributesToNode (pChildNode); 02318 } 02319 } 02320 02321 // Now stick the new path into the tree 02322 CALL_WITH_FAIL 02323 ( 02324 DoInsertNewNode(pChildNode, pParentNode, Direction, TRUE, FALSE), 02325 this,ok 02326 ); 02327 02328 // if we've failed tidy up 02329 if (!ok) 02330 { 02331 pChildNode->CascadeDelete(); 02332 delete pChildNode; 02333 return FALSE; 02334 } 02335 return TRUE; 02336 } 02337 02338 02339 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 02340 // Do functions to localise/factor out attributes for attribute application 02341 02342 // Range functions first 02343 02344 /******************************************************************************************** 02345 > BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 02346 AttrTypeSet* pAffectedAttrTypes, 02347 BOOL ExcludeTextObjects = FALSE) 02348 02349 02350 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02351 Created: 17/5/95 02352 02353 Inputs: pRange: The range of objects which need their attributes localising 02354 02355 pAffectedAttrTypes: A set of the types of the attributes which are to be 02356 applied to the Range. This function will localise 02357 all attributes which have a type in this set. 02358 02359 All attriute types in this set must be localised on the 02360 same compound node (See note below). 02361 02362 ExcludeTextObjects: When TRUE atttributes will not be localised on Text 02363 compounds. This is useful for selection deletions, where 02364 TextStory localisation will be left to the Story itself. 02365 This is a bit unpleasant. 02366 02367 Outputs: - 02368 Returns: TRUE if successful, 02369 FALSE if we run out of memory. Tidyup then call End() 02370 02371 Purpose: This Do function must be called on a range prior to a set of attributes 02372 being applied to it. It globally localises those attributes which have 02373 a type in the pAffectedAttrTypes set. 02374 02375 After the attribute has been applied you should call DoFactorOutAfterAttrChange 02376 02377 If an object in the range Discards its attribute children then its attributes 02378 are not localised. 02379 02380 Note: When applying attributes, we are not always going to apply the attributes 02381 to the object itself, sometimes we need to apply them to the object's parent. 02382 Eg. when we apply a line based attribute to a text character the attributes 02383 will get applied to the parent TextLine. 02384 02385 In this situation we do not localise the objects attributes, but its parents 02386 attributes instead. 02387 02388 Warning 02389 ~~~~~~~ 02390 02391 All attribute types in the AttrTypeSet must be localised 02392 on the same compound node. This means for example that the AttrType set cannot 02393 contain a Bold and a Line space attribute. If we need to do this in future 02394 then the routine will need to be changed. 02395 02396 Errors: - 02397 SeeAlso: AttrTypeSet 02398 SeeAlso: NodeAttribute::GetAttributeType 02399 SeeAlso: UndoableOperation::DoFactorOutAfterAttrChange 02400 SeeAlso: UndoableOperation::DoLocaliseForAttrChange 02401 SeeAlso: Range::DoFactorOutAfterAttrChange 02402 02403 ********************************************************************************************/ 02404 02405 02406 BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 02407 AttrTypeSet* pAffectedAttrTypes, 02408 BOOL ExcludeTextObjects /* = FALSE */) 02409 { 02410 ERROR3IF(pRange->Count() == 0, "Range::DoLocaliseForAttrChange called on an empty range"); 02411 02412 // Iterate over the top of any liveeffects applied to the selection 02413 // Its important that this iteration matches those used in DoApplyAttribute functions 02414 // so that attributes are put in the places where mutation will find them 02415 ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE); // We DO own this range 02416 Node* pCurrent = pLevel->FindFirst(); 02417 02418 // There is no need to localise a compound more than once so we remember the last localised 02419 NodeRenderableInk* pLocalisedCompound = NULL; 02420 02421 CCRuntimeClass* AttrType; 02422 Node* pParent; 02423 Node* pObject; 02424 02425 while (pCurrent) 02426 { 02427 pObject = pCurrent; 02428 // Determine if the attribute will get applied to this object, or an alternative object 02429 // (probably its parent) 02430 if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty())) 02431 { 02432 AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType; 02433 ERROR3IF(!AttrType, "AttrType set contains NULL attribute type"); 02434 pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(AttrType); 02435 } 02436 02437 if (pObject != NULL) 02438 { 02439 02440 // We only need to localise those nodes which have a compound parent 02441 pParent = pObject->FindParent(); 02442 ERROR3IF(pParent == NULL, "Range::DoLocaliseForAttrChange, node found without a parent"); 02443 02444 // Only localise the parent if it is a compound node which has not already been localised 02445 if ((pParent->IsCompound()) && (pParent != pLocalisedCompound)) 02446 { 02447 if (!(ExcludeTextObjects && ((IS_A(pParent, TextLine)) || (IS_A(pParent, TextStory)))) ) 02448 { 02449 // Attempt to localise the compound 02450 if (!DoLocaliseForAttrChange((NodeRenderableInk*)pObject, pAffectedAttrTypes, NULL)) 02451 { 02452 delete pLevel; 02453 return FALSE; 02454 } 02455 02456 pLocalisedCompound = (NodeRenderableInk*)pParent; // Remember that it's been localised 02457 } 02458 } 02459 } 02460 02461 pCurrent = pLevel->FindNext(pCurrent); 02462 } 02463 02464 delete pLevel; 02465 return TRUE; 02466 } 02467 02468 /******************************************************************************************** 02469 02470 > BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 02471 CCRuntimeClass* pAffectedAttrType) 02472 02473 02474 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02475 Created: 17/5/95 02476 02477 Inputs: pRange: The range of objects which need their attributes localising 02478 02479 02480 pAffectedAttrType: The type of the attribute that you are about to apply 02481 to the range 02482 02483 Outputs: - 02484 Returns: TRUE if successful, 02485 FALSE if we run out of memory. Tidyup then call End() 02486 02487 Purpose: This Do function must be called on a range prior to an attribute 02488 being applied to it. If you are going to apply multiple attributes to 02489 the range then it is more efficient to call the other version of this 02490 function which takes a set of attribute types. 02491 02492 The function globally localises those attributes which have a type in the 02493 pAffectedAttrTypes set. 02494 02495 If an object in the range Discards its attribute children then its attributes 02496 are not localised. 02497 02498 Note: When applying an attribute, we are not always going to apply it 02499 to the object itself, sometimes we need to apply it to the object's parent. 02500 Eg. when we apply a line based attribute to a text character the attributes 02501 will get applied to the parent TextLine. 02502 02503 In this situation we do not localise the objects attributes, but its parents 02504 attributes instead. 02505 02506 02507 Errors: - 02508 SeeAlso: AttrTypeSet 02509 SeeAlso: NodeAttribute::GetAttributeType 02510 SeeAlso: UndoableOperation::DoFactorOutAfterAttrChange 02511 SeeAlso: UndoableOperation::DoLocaliseForAttrChange 02512 SeeAlso: Range::DoFactorOutAfterAttrChange 02513 02514 ********************************************************************************************/ 02515 02516 02517 02518 BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 02519 CCRuntimeClass* pAffectedAttrType) 02520 { 02521 ERROR3IF(pRange->Count() == 0, "Range::DoLocaliseForAttrChange called on an empty range"); 02522 02523 // Iterate over the top of any liveeffects applied to the selection 02524 // Its important that this iteration matches those used in DoApplyAttribute functions 02525 // so that attributes are put in the places where mutation will find them 02526 ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE); // We DO own this range 02527 Node* pCurrent = pLevel->FindFirst(); 02528 02529 // There is no need to localise a compound more than once so we remember the last localised 02530 NodeRenderableInk* pLocalisedCompound = NULL; 02531 02532 Node* pParent; 02533 Node* pObject; 02534 02535 while (pCurrent) 02536 { 02537 pObject = pCurrent; 02538 // Determine if the attribute will get applied to this object, or an alternative object 02539 // (probably its parent) 02540 ERROR3IF(!pAffectedAttrType, "AttrType is NULL"); 02541 pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(pAffectedAttrType); 02542 02543 { 02544 02545 // We only need to localise those nodes which have a compound parent 02546 pParent = pObject->FindParent(); 02547 ERROR3IF(pParent == NULL, "Range::DoLocaliseForAttrChange, node found without a parent"); 02548 02549 // Only localise the parent if it is a compound node which has not already been localised 02550 if ((pParent->IsCompound()) && (pParent != pLocalisedCompound)) 02551 { 02552 // Attempt to localise the compound 02553 if (!DoLocaliseForAttrChange((NodeRenderableInk*)pObject, pAffectedAttrType, NULL)) 02554 { 02555 delete pLevel; 02556 return FALSE; 02557 } 02558 02559 pLocalisedCompound = (NodeRenderableInk*)pParent; // Remember that it's been localised 02560 } 02561 } 02562 pCurrent = pLevel->FindNext(pCurrent); 02563 } 02564 02565 delete pLevel; 02566 return TRUE; 02567 } 02568 02569 /******************************************************************************************** 02570 02571 > BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 02572 AttrTypeSet* pAffectedAttrTypes) 02573 02574 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02575 Created: 18/5/95 02576 02577 Inputs: pRange: The objects that need their attributes factoring out 02578 02579 pAffectedAttrTypes: The types of the attributes that have just been 02580 applied to the range. 02581 02582 Outputs: - 02583 Returns: TRUE if successful, 02584 FALSE if we run out of memory. Tidyup then call End() 02585 02586 Purpose: This Do function must be called on the range after attributes have 02587 being applied to it. 02588 02589 The function globally factors out those attributes which have a type in the 02590 pAffectedAttrTypes set. 02591 02592 Before calling the function you should have localised attributes by calling 02593 DoLocaliseForAttrChange. 02594 02595 If an object in the range Discards its attribute children then its attributes 02596 are ignored when factoring (eg. the caret) 02597 02598 Note: When applying attributes, we are not always going to apply the attributes 02599 to the object itself, sometimes we need to apply them to the object's parent. 02600 Eg. when we apply a line based attribute to a text character the attributes 02601 will get applied to the parent TextLine. 02602 02603 In this situation we do not factor out the objects attributes, but its parents 02604 attributes instead. 02605 02606 Warning 02607 ~~~~~~~ 02608 02609 All attribute types in the AttrTypeSet must have been localised 02610 (and so need factoring out) on the same compound node. This means for example 02611 that the AttrType set cannot contain a Bold and a Line space attribute. If 02612 we need to do this in future then the routine will need to be changed. 02613 02614 02615 02616 SeeAlso: AttrTypeSet 02617 SeeAlso: NodeAttribute::GetAttributeType 02618 SeeAlso: UndoableOperation::DoFactorOutAfterAttrChange 02619 SeeAlso: Range::DoLocaliseForAttrChange 02620 02621 02622 ********************************************************************************************/ 02623 02624 02625 BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 02626 AttrTypeSet* pAffectedAttrTypes) 02627 { 02628 ERROR3IF(pRange->Count() == 0, "Range::DoFactorOutAfterAttrChange called on an empty range"); 02629 02630 // Iterate over the top of any liveeffects applied to the selection 02631 // Its important that this iteration matches those used in DoApplyAttribute functions 02632 // so that attributes are put in the places where mutation will find them 02633 ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE); // We DO own this range 02634 Node* pCurrent = pLevel->FindFirst(); 02635 02636 // There is no need to factor out the attributes on a compound more than once, so we 02637 // remember the last compound node which has had its attributes factored out 02638 NodeRenderableInk* pFactoredOutCompound = NULL; 02639 02640 Node* pParent; 02641 Node* pObject; 02642 02643 while (pCurrent) 02644 { 02645 // If the object can discard its attribute children then we should not try to factor 02646 // out its attributes 02647 CCRuntimeClass* AttrType; 02648 02649 pObject = pCurrent; 02650 if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty())) 02651 { 02652 AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType; 02653 ERROR3IF(!AttrType, "AttrType set contains NULL attribute type"); 02654 pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(AttrType); 02655 } 02656 02657 // DY added test for NULL, which can occur with bevels 02658 if ((pObject != NULL)) 02659 { 02660 // We only need to factor out attributes on nodes which have a compound parent 02661 pParent = pObject->FindParent(); 02662 ERROR3IF(pParent == NULL, "Range::DoFactorOutAfterAttrChange, node found without a parent"); 02663 02664 // Only factor out attribs if the parent has not already had its attribs factored out 02665 if ((pParent->IsCompound()) && (pParent != pFactoredOutCompound)) 02666 { 02667 // Attempt to localise the compound 02668 if (!DoFactorOutAfterAttrChange((NodeRenderableInk*)pObject, pAffectedAttrTypes)) 02669 { 02670 delete pLevel; 02671 return FALSE; 02672 } 02673 02674 pFactoredOutCompound = (NodeRenderableInk*)pParent; // Remember that it's been localised 02675 } 02676 } 02677 02678 pCurrent = pLevel->FindNext(pCurrent); 02679 } 02680 02681 delete pLevel; 02682 return TRUE; 02683 } 02684 02685 /******************************************************************************************** 02686 02687 > BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 02688 CCRuntimeClass* pAffectedAttrType) 02689 02690 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02691 Created: 18/5/95 02692 02693 Inputs: pRange: The objects that need their attributes factoring out 02694 02695 pAffectedAttrType: The type of the attribute that have just been 02696 applied to the range. 02697 02698 Outputs: - 02699 Returns: TRUE if successful, 02700 FALSE if we run out of memory. Tidyup then call End() 02701 02702 Purpose: This Do function must be called on the range after an attribute 02703 being applied to it. If you have applied multiple attributes to 02704 the range then it is more efficient to call the other version of this 02705 function which takes a set of attribute types. 02706 02707 The function globally factors out the attribute which has type pAffectedAttrType 02708 02709 Before calling the function you should have localised the attribute by calling 02710 DoLocaliseForAttrChange. 02711 02712 If an object in the range Discards its attribute children then its attributes 02713 are ignored when factoring (eg. the caret) 02714 02715 Note: When applying an attribute, we are not always going to apply it 02716 to the object itself, sometimes we need to apply it to the object's parent. 02717 Eg. when we apply a line based attribute to a text character the attributes 02718 will get applied to the parent TextLine. 02719 02720 In this situation we do not factor out the objects attributes, but its parents 02721 attributes instead. 02722 02723 Note: When applying attributes, we are not always going to apply the attributes 02724 to the object itself, sometimes we need to apply them to the object's parent. 02725 Eg. when we apply a line based attribute to a text character the attributes 02726 will get applied to the parent TextLine. 02727 02728 In this situation we do not factor out the object's attributes, but its parents 02729 attributes instead. 02730 02731 SeeAlso: AttrTypeSet 02732 SeeAlso: NodeAttribute::GetAttributeType 02733 SeeAlso: UndoableOperation::DoLocaliseForAttrChange 02734 SeeAlso: Range::DoLocaliseForAttrChange 02735 SeeAlso: Range::DoFactorOutAfterAttrChange 02736 02737 02738 ********************************************************************************************/ 02739 02740 BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 02741 CCRuntimeClass* pAffectedAttrType) 02742 { 02743 ERROR3IF(pRange->Count() == 0, "Range::DoFactorOutAfterAttrChange called on an empty range"); 02744 // Scan the range 02745 02746 // Iterate over the top of any liveeffects applied to the selection 02747 // Its important that this iteration matches those used in DoApplyAttribute functions 02748 // so that attributes are put in the places where mutation will find them 02749 ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE); // We DO own this range 02750 Node* pCurrent = pLevel->FindFirst(); 02751 02752 // There is no need to factor out the attributes on a compound more than once, so we 02753 // remember the last compound node which has had its attributes factored out 02754 NodeRenderableInk* pFactoredOutCompound = NULL; 02755 02756 Node* pParent; 02757 Node* pObject; 02758 while (pCurrent) 02759 { 02760 pObject = pCurrent; 02761 // Get the object that the attribute has been applied to 02762 ERROR3IF(!pAffectedAttrType, "AttrType is NULL"); 02763 pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(pAffectedAttrType); 02764 02765 // If the object can discard its attribute children then we should not try to factor 02766 // out its attributes 02767 02768 { 02769 02770 // We only need to factor out attributes on nodes which have a compound parent 02771 pParent = pObject->FindParent(); 02772 ERROR3IF(pParent == NULL, "Range::DoFactorOutAfterAttrChange, node found without a parent"); 02773 02774 // Only factor out attribs if the parent has not already had its attribs factored out 02775 if ((pParent->IsCompound()) && (pParent != pFactoredOutCompound)) 02776 { 02777 // Attempt to localise the compound 02778 if (!DoFactorOutAfterAttrChange((NodeRenderableInk*)pObject, pAffectedAttrType)) 02779 { 02780 delete pLevel; 02781 return FALSE; 02782 } 02783 02784 pFactoredOutCompound = (NodeRenderableInk*)pParent; // Remember that it's been localised 02785 } 02786 } 02787 pCurrent = pLevel->FindNext(pCurrent); 02788 } 02789 02790 delete pLevel; 02791 return TRUE; 02792 } 02793 02794 02795 02796 /******************************************************************************************** 02797 02798 > BOOL UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 02799 AttrTypeSet* pAffectedAttrTypes, 02800 ObjectSet* pLocalisedCompounds) 02801 02802 02803 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02804 Created: 17/5/95 02805 Inputs: Object: The object that you are about to apply attributes to 02806 02807 pAffectedAttrTypes: A set of the types of the attributes which are to be 02808 applied to the Object. This function will localise 02809 all attributes which have a type in this set. 02810 02811 pLocalisedCompounds: An optional set of compounds which have already had 02812 all attributes in the pAffectedAttrTypes localised. 02813 If Object has a parent compound which 02814 is in this set then its attributes will not need to 02815 be localised again. 02816 Outputs: - 02817 Returns: TRUE if successful, 02818 FALSE if we run out of memory. Tidyup then call End() 02819 02820 Purpose: This Do function must be called on an object prior to a set of attributes 02821 being applied to it. It globally localises those attributes which have 02822 a type in the pAffectedAttrTypes set. 02823 02824 If the function is called on a object which does not have a compound parent 02825 then there will be no attributes to localise so the function will simply 02826 return TRUE without having done anything. 02827 02828 After a compound object has localised its children it will be added to the 02829 pLocalisedCompounds set if this has been provided. 02830 02831 After the attribute has been applied you should call DoFactorOutAfterAttrChange 02832 02833 If the object discards its attribute children then the routine does nothing 02834 (eg. the caret) 02835 02836 Note: When applying attributes, we are not always going to apply the attributes 02837 to the object itself, sometimes we need to apply them to the object's parent. 02838 Eg. when we apply a line based attribute to a text character the attributes 02839 will get applied to the parent TextLine. 02840 02841 In this situation we do not localise the objects attributes, but its parents 02842 attributes instead. 02843 02844 Warning 02845 ~~~~~~~ 02846 02847 All attribute types in the AttrTypeSet must be localised 02848 on the same compound node. This means for example that the AttrType set cannot 02849 contain a Bold and a Line space attribute. If we need to do this in future 02850 then the routine will need to be changed. 02851 02852 02853 02854 Errors: - 02855 SeeAlso: AttrTypeSet 02856 SeeAlso: NodeAttribute::GetAttributeType 02857 SeeAlso: UndoableOperation::DoFactorOutAfterAttrChange 02858 SeeAlso: Range::DoLocaliseForAttrChange 02859 SeeAlso: Range::DoFactorOutAfterAttrChange 02860 02861 ********************************************************************************************/ 02862 02863 // This function must get called on a compound node before a new attribute is attached to it 02864 BOOL UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 02865 AttrTypeSet* pAffectedAttrTypes, 02866 ObjectSet* pLocalisedCompounds) 02867 { 02868 CCRuntimeClass* AttrType; 02869 // Determine if the attribute will get applied to this object, or an alternative object 02870 // (probably its parent) 02871 if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty())) 02872 { 02873 AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType; 02874 ERROR3IF(!AttrType, "AttrType set contains NULL attribute type"); 02875 Object = Object->GetObjectToApplyTo(AttrType); 02876 } 02877 02878 { 02879 02880 02881 // If the object has not got a compound parent then there will be no attributes 02882 // to localise, so we need not do anything 02883 02884 Node* pParent; 02885 pParent = Object->FindParent(); 02886 ERROR3IF(!pParent, "UndoableOperation::DoLocaliseForAttrChange called on an object which has no parent"); 02887 02888 if (pParent->IsCompound()) 02889 { 02890 // Before we attempt to localise the compound's attributes let's check that they are not 02891 // already localised. 02892 02893 if (pLocalisedCompounds) 02894 { 02895 if (pLocalisedCompounds->InSet((NodeRenderableInk*)pParent)) 02896 { 02897 return TRUE; // Attributes are already localised so there is no need to localise again ! 02898 } 02899 else 02900 { 02901 pLocalisedCompounds->AddToSet((NodeRenderableInk*)pParent); // Record that the compound has been localised 02902 } 02903 } 02904 02905 return (DoLocaliseCommonAttributes((NodeRenderableInk*)pParent, // We are localising the compound's attrs 02906 FALSE, // No need to check for duplicates 02907 TRUE, // Localise globally 02908 pAffectedAttrTypes)); // Only attributes of these types 02909 } 02910 02911 } 02912 return TRUE; 02913 } 02914 02915 /******************************************************************************************** 02916 02917 > BOOL UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 02918 CCRuntimeClass* pAffectedAttrType, 02919 ObjectSet* pLocalisedCompounds) 02920 02921 02922 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02923 Created: 18/5/95 02924 Inputs: Object: The object that you are about to apply the attribute to 02925 02926 pAffectedAttrType: The type of the attribute that you are about to apply 02927 to the object 02928 02929 pLocalisedCompounds: An optional set of compounds which have already had 02930 their pAffectedAttrType localised. 02931 If Object has a parent compound which 02932 is in this set then its attributes will not need to 02933 be localised again. 02934 02935 02936 Outputs: - 02937 Returns: TRUE if successful, 02938 FALSE if we run out of memory. Tidyup then call End() 02939 02940 Purpose: This Do function must be called on an object prior to an attribute 02941 being applied to it. If you are going to apply multiple attributes to 02942 the Object then it is more efficient to call the other version of this 02943 function which takes a set of attribute types. 02944 02945 The function globally localises those attributes which have a type in the 02946 pAffectedAttrTypes set. 02947 02948 If the function is called on a object which does not have a compound parent 02949 then there will be no attributes to localise so the function will simply 02950 return TRUE without having done anything. 02951 02952 If the object discards its attribute children then the routine does nothing 02953 (eg. the caret) 02954 02955 Note: When applying attributes, we are not always going to apply the attributes 02956 to the object itself, sometimes we need to apply them to the object's parent. 02957 Eg. when we apply a line based attribute to a text character the attributes 02958 will get applied to the parent TextLine. 02959 02960 In this situation we do not localise the objects attributes, but its parents 02961 attributes instead. 02962 02963 02964 SeeAlso: AttrTypeSet 02965 SeeAlso: NodeAttribute::GetAttributeType 02966 SeeAlso: UndoableOperation::DoFactorOutAfterAttrChange 02967 SeeAlso: Range::DoLocaliseForAttrChange 02968 SeeAlso: Range::DoFactorOutAfterAttrChange 02969 02970 02971 ********************************************************************************************/ 02972 02973 BOOL UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 02974 CCRuntimeClass* pAffectedAttrType, 02975 ObjectSet* pLocalisedCompounds) 02976 { 02977 BOOL ok = TRUE; 02978 // Determine if the attribute will get applied to this object, or an alternative object 02979 // (probably its parent) 02980 ERROR3IF(!pAffectedAttrType, "AttrType is NULL"); 02981 Object = Object->GetObjectToApplyTo(pAffectedAttrType); 02982 02983 { 02984 02985 Node* pParent; 02986 pParent = Object->FindParent(); 02987 ERROR3IF(!pParent, "UndoableOperation::DoLocaliseForAttrChange called on an object which has no parent"); 02988 02989 if (pParent->IsCompound()) 02990 { 02991 02992 // We an attribute type set 02993 AttrTypeSet Set; 02994 02995 // Add the attributes type to the set 02996 if (!Set.AddToSet(pAffectedAttrType)) 02997 { 02998 ok = FALSE; 02999 } 03000 03001 if (ok) 03002 { 03003 // Before we attempt to localise the compound's attributes let's check that they are not 03004 // already localised. 03005 03006 if (pLocalisedCompounds) 03007 { 03008 if (pLocalisedCompounds->InSet((NodeRenderableInk*)pParent)) 03009 { 03010 return TRUE; // Attributes are already localised so there is no need to localise again ! 03011 } 03012 else 03013 { 03014 pLocalisedCompounds->AddToSet((NodeRenderableInk*)pParent); // Record that the compound has been localised 03015 } 03016 } 03017 03018 03019 ok = DoLocaliseCommonAttributes((NodeRenderableInk*)pParent,// We are localising the compound's attrs 03020 FALSE, // No need to check for duplicates 03021 TRUE, // Localise globally 03022 &Set); // Only attributes of these types 03023 } 03024 03025 // The attribute type set is no longer required 03026 Set.DeleteAll(); 03027 } 03028 } 03029 return ok; 03030 } 03031 03032 03033 /******************************************************************************************** 03034 03035 > BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object, 03036 AttrTypeSet* pAffectedAttrTypes) 03037 03038 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03039 Created: 18/5/95 03040 Inputs: Object: The object that you have just applied attributes to 03041 03042 pAffectedAttrTypes: The types of the attributes that have just been 03043 applied to the object. 03044 03045 Outputs: - 03046 Returns: TRUE if successful, 03047 FALSE if we run out of memory. Tidyup then call End() 03048 03049 Purpose: This Do function must be called on an object after attributes have 03050 being applied to it. 03051 03052 The function globally factors out those attributes which have a type in the 03053 pAffectedAttrTypes set. 03054 03055 If the function is called on a object which does not have a compound parent 03056 then there will be no attributes to factor out so the function will simply 03057 return TRUE without having done anything. 03058 03059 Before calling the function you should have localised attributes by calling 03060 DoLocaliseForAttrChange. 03061 03062 If the object discards its attribute children then the routine does nothing 03063 (eg. the caret) 03064 03065 Note: When applying attributes, we are not always going to apply the attributes 03066 to the object itself, sometimes we need to apply them to the object's parent. 03067 Eg. when we apply a line based attribute to a text character the attributes 03068 will get applied to the parent TextLine. 03069 03070 In this situation we do not factor out the objects attributes, but its parents 03071 attributes instead. 03072 03073 Warning 03074 ~~~~~~~ 03075 03076 All attribute types in the AttrTypeSet must have been localised 03077 (and so need factoring out) on the same compound node. This means for example 03078 that the AttrType set cannot contain a Bold and a Line space attribute. If 03079 we need to do this in future then the routine will need to be changed. 03080 03081 03082 SeeAlso: AttrTypeSet 03083 SeeAlso: NodeAttribute::GetAttributeType 03084 SeeAlso: UndoableOperation::DoLocaliseForAttrChange 03085 SeeAlso: Range::DoLocaliseForAttrChange 03086 SeeAlso: Range::DoFactorOutAfterAttrChange 03087 03088 03089 ********************************************************************************************/ 03090 03091 BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object, 03092 AttrTypeSet* pAffectedAttrTypes) 03093 { 03094 CCRuntimeClass* AttrType; 03095 03096 // Get object which attrib was applied to 03097 if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty())) 03098 { 03099 AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType; 03100 ERROR3IF(!AttrType, "AttrType set contains NULL attribute type"); 03101 Object = Object->GetObjectToApplyTo(AttrType); 03102 } 03103 03104 { 03105 Node* pParent; 03106 pParent = Object->FindParent(); 03107 ERROR3IF(!pParent, "UndoableOperation::DoFactorOutAfterAttrChange called on an object which has no parent"); 03108 03109 if (pParent->IsCompound()) 03110 { 03111 return (DoFactorOutCommonChildAttributes((NodeRenderableInk*)pParent, 03112 TRUE, // Global 03113 pAffectedAttrTypes)); 03114 } 03115 } 03116 return TRUE; 03117 } 03118 03119 03120 /******************************************************************************************** 03121 03122 > BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object, 03123 CCRuntimeClass* pAffectedAttrType) 03124 03125 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03126 Created: 18/5/95 03127 Inputs: Object: The object that you have just applied attributes to 03128 03129 pAffectedAttrType: The types of the attribute that have just been 03130 applied to the object. 03131 03132 Outputs: - 03133 Returns: TRUE if successful, 03134 FALSE if we run out of memory. Tidyup then call End() 03135 03136 Purpose: This Do function must be called on an object after an attribute 03137 being applied to it. If you have applied multiple attributes to 03138 the Object then it is more efficient to call the other version of this 03139 function which takes a set of attribute types. 03140 03141 The function globally factors out the attribute with type pAffectedAttrType 03142 03143 If the function is called on a object which does not have a compound parent 03144 then there will be no attributes to factor out so the function will simply 03145 return TRUE without having done anything. 03146 03147 Before calling the function you should have localised attributes by calling 03148 DoLocaliseForAttrChange. 03149 03150 If the object discards its attribute children then the routine does nothing 03151 (eg. the caret) 03152 03153 Note: When applying attributes, we are not always going to apply the attributes 03154 to the object itself, sometimes we need to apply them to the object's parent. 03155 Eg. when we apply a line based attribute to a text character the attributes 03156 will get applied to the parent TextLine. 03157 03158 In this situation we do not factor out the objects attributes, but its parents 03159 attributes instead. 03160 03161 SeeAlso: AttrTypeSet 03162 SeeAlso: NodeAttribute::GetAttributeType 03163 SeeAlso: UndoableOperation::DoLocaliseForAttrChange 03164 SeeAlso: Range::DoLocaliseForAttrChange 03165 SeeAlso: Range::DoFactorOutAfterAttrChange 03166 03167 03168 ********************************************************************************************/ 03169 03170 BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object, 03171 CCRuntimeClass* pAffectedAttrType) 03172 { 03173 BOOL ok = TRUE; 03174 03175 // Get object attr was applied to 03176 ERROR3IF(!pAffectedAttrType, "AttrType is NULL"); 03177 Object = Object->GetObjectToApplyTo(pAffectedAttrType); 03178 03179 { 03180 03181 Node* pParent; 03182 pParent = Object->FindParent(); 03183 ERROR3IF(!pParent, "UndoableOperation::DoFactorOutAfterAttrChange called on an object which has no parent"); 03184 03185 if (pParent->IsCompound()) 03186 { 03187 03188 // We need an attribute type set 03189 AttrTypeSet Set; 03190 03191 // Add the attributes type to the set 03192 if (!(Set.AddToSet(pAffectedAttrType))) 03193 { 03194 ok = FALSE; 03195 } 03196 03197 if (ok) 03198 { 03199 03200 return (DoFactorOutCommonChildAttributes((NodeRenderableInk*)pParent, 03201 TRUE, // Global 03202 &Set)); 03203 } 03204 03205 Set.DeleteAll(); 03206 } 03207 } 03208 return ok; 03209 } 03210 03211 /******************************************************************************************** 03212 03213 > BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds, 03214 AttrTypeSet* pAffectedAttrTypes) 03215 03216 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03217 Created: 18/5/95 03218 03219 Inputs: pLocalisedCompounds: A set of compound nodes which have localised their 03220 pAffectedAttrTypes attributes 03221 03222 pAffectedAttrTypes: The types of the attributes that have been 03223 applied to the range. 03224 03225 Outputs: - 03226 Returns: TRUE if successful, 03227 FALSE if we run out of memory. Tidyup then call End() 03228 03229 Purpose: This Do function is designed to be called after all attributes in the 03230 pAffectedAttrTypes set have been localised on all compound nodes in 03231 the pLocalisedCompounds set. 03232 03233 The function globally factors out the attributes which have a type which is 03234 in the pAffectedAttrTypes set. 03235 03236 Before calling the function you should have localised the attribute by calling 03237 DoLocaliseForAttrChange. 03238 03239 03240 03241 SeeAlso: AttrTypeSet 03242 SeeAlso: NodeAttribute::GetAttributeType 03243 SeeAlso: UndoableOperation::DoLocaliseForAttrChange 03244 03245 03246 ********************************************************************************************/ 03247 03248 BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds, 03249 AttrTypeSet* pAffectedAttrTypes) 03250 { 03251 ERROR3IF(!pLocalisedCompounds, "DoFactorOutAfterAttrChange called with a NULL compound set"); 03252 //ERROR3IF(!pAffectedAttrTypes, "DoFactorOutAfterAttrChange called with a NULL attr type set"); 03253 03254 // Scan the range 03255 ObjectItem* pObjItem = (ObjectItem*)(pLocalisedCompounds->GetHead()); 03256 NodeRenderableInk* pCurrent; 03257 03258 while (pObjItem) 03259 { 03260 pCurrent = pObjItem->pObject; 03261 ERROR3IF(!(pCurrent->IsCompound()), "Set should only contain compound objects"); 03262 if (pCurrent->IsCompound()) 03263 { 03264 if (!DoFactorOutCommonChildAttributes((NodeRenderableInk*)pCurrent, 03265 TRUE, // Global 03266 pAffectedAttrTypes)) 03267 { 03268 return FALSE; 03269 } 03270 } 03271 pObjItem = (ObjectItem*)pLocalisedCompounds->GetNext(pObjItem); 03272 } 03273 03274 return TRUE; 03275 03276 } 03277 03278 03279 /******************************************************************************************** 03280 03281 > BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds, 03282 CCRuntimeClass* pAffectedAttrType) 03283 03284 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03285 Created: 18/5/95 03286 03287 Inputs: pLocalisedCompounds: A set of compound nodes which have localised their 03288 pAffectedAttrType attributes. 03289 03290 pAffectedAttrType: The type of the attribute that have just been 03291 applied to the range. 03292 03293 Outputs: - 03294 Returns: TRUE if successful, 03295 FALSE if we run out of memory. Tidyup then call End() 03296 03297 Purpose: This Do function is designed to be called after pAffectedAttrType has been 03298 localised on all compound nodes in the pLocalisedCompounds set. 03299 03300 The function globally factors out the attribute which has type pAffectedAttrType 03301 03302 Before calling the function you should have localised the attribute by calling 03303 DoLocaliseForAttrChange. 03304 03305 03306 03307 SeeAlso: AttrTypeSet 03308 SeeAlso: NodeAttribute::GetAttributeType 03309 SeeAlso: UndoableOperation::DoLocaliseForAttrChange 03310 03311 03312 ********************************************************************************************/ 03313 03314 03315 03316 BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds, 03317 CCRuntimeClass* pAffectedAttrType) 03318 { 03319 ERROR3IF(!pLocalisedCompounds, "DoFactorOutAfterAttrChange called with a NULL compound set"); 03320 ERROR3IF(!pAffectedAttrType, "DoFactorOutAfterAttrChange called with a NULL attr type"); 03321 03322 03323 // Scan the range 03324 ObjectItem* pObjItem = (ObjectItem*)(pLocalisedCompounds->GetHead()); 03325 NodeRenderableInk* pCurrent; 03326 03327 // We need an attribute type set 03328 AttrTypeSet Set; 03329 03330 // Add the attributes type to the set 03331 if (!(Set.AddToSet(pAffectedAttrType))) 03332 { 03333 return FALSE; 03334 } 03335 03336 while (pObjItem) 03337 { 03338 pCurrent = pObjItem->pObject; 03339 ERROR3IF(!(pCurrent->IsCompound()), "Set should only contain compound objects"); 03340 if (pCurrent->IsCompound()) 03341 { 03342 if (!DoFactorOutCommonChildAttributes((NodeRenderableInk*)pCurrent, 03343 TRUE, // Global 03344 &Set)) 03345 { 03346 Set.DeleteAll(); 03347 return FALSE; 03348 } 03349 } 03350 pObjItem = (ObjectItem*)pLocalisedCompounds->GetNext(pObjItem); 03351 } 03352 03353 Set.DeleteAll(); 03354 return TRUE; 03355 } 03356 03357 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 03358 // These functions are for attribute optimisation when deleting objects in a range 03359 03360 // They are now defunct. They were a nice idea but they could not cope with attribute children 03361 // of empty groups ! 03362 03363 03364 /******************************************************************************************** 03365 03366 > BOOL UndoableOperation::GetPreDelFactorOutInfo(Range* pRange, List* pPreDelFactorOutInfoList) 03367 03368 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03369 Created: 31/5/95 03370 03371 Inputs: pRange: The range of objects that will be deleted 03372 03373 Outputs: pPreDelFactorOutInfoList: Information to be used by the DoFactorOutAfterDeletion 03374 function. 03375 03376 Returns: TRUE if succesful 03377 FALSE if we ran out of memory. In this situation all items on the 03378 pPreDelFactorOutInfoList are deleted. 03379 03380 Purpose: This function is an information gatherer for the DoFactorOutAfterDeletion 03381 function. See this function for a full description of its purpose. 03382 Errors: - 03383 SeeAlso: UndoableOperation::DoFactorOutAfterDeletion 03384 03385 ********************************************************************************************/ 03386 /* 03387 BOOL UndoableOperation::GetPreDelFactorOutInfo(Range* pRange, List* pPreDelFactorOutInfoList) 03388 { 03389 // On entry to this function the PreDelFactorOutInfoList should be empty 03390 ERROR3IF(!(pPreDelFactorOutInfoList->IsEmpty()), "PreDelFactorOutInfoList is not empty"); 03391 // Lets make sure 03392 pPreDelFactorOutInfoList->DeleteAll(); 03393 03394 NodeRenderableInk* pCurrentCompound; // A pointer to the compound parent of the objects we are 03395 // currently processing 03396 03397 pCurrentCompound = NULL; 03398 PreDelFactorOutInfoItem* pCurrentCompoundInfo = NULL; 03399 03400 // Scan the range 03401 Node* pCurrent = pRange->FindFirst(); 03402 Node* pCurrentsParent = NULL; 03403 while (pCurrent) 03404 { 03405 pCurrentsParent = pCurrent->FindParent(); 03406 ERROR3IF(pCurrentsParent == NULL, "Current has a NULL parent"); 03407 if (pCurrentsParent->IsCompound()) 03408 { 03409 if (pCurrentsParent != pCurrentCompound) 03410 { 03411 pCurrentCompound = (NodeRenderableInk*)pCurrentsParent; 03412 03413 // We are processing a new Compound node. Create a new PreDelFactorOutInfoItem 03414 // so that we can record those attributes that must be factored out in it. 03415 03416 // Check if the last compound node we processed needs any attrs factoring out. 03417 // if it doesn't then we can safely delete its entry (every little helps) 03418 if (pCurrentCompoundInfo && pCurrentCompoundInfo->AttrTypesToFactorOut.IsEmpty()) 03419 { 03420 // bye-bye 03421 pPreDelFactorOutInfoList->RemoveItem(pCurrentCompoundInfo); 03422 delete pCurrentCompoundInfo; 03423 } 03424 03425 pCurrentCompoundInfo = new PreDelFactorOutInfoItem(); 03426 if (!pCurrentCompoundInfo) 03427 goto OutOfMemory; 03428 03429 pCurrentCompoundInfo->pCompound = pCurrentCompound; 03430 03431 // Add the item to the list 03432 pPreDelFactorOutInfoList->AddHead(pCurrentCompoundInfo); 03433 } 03434 ((NodeRenderableInk*)pCurrent)->AddChildAttrTypesToSet(&(pCurrentCompoundInfo->AttrTypesToFactorOut)); 03435 } 03436 03437 // Get next object to process 03438 pCurrent = pRange->FindNext(pCurrent); 03439 } 03440 03441 return TRUE; // success 03442 03443 OutOfMemory: 03444 // Delete all items on the pPreDelFactorOutInfoList 03445 pCurrentCompoundInfo = (PreDelFactorOutInfoItem*)(pPreDelFactorOutInfoList->GetHead()); 03446 while (pCurrentCompoundInfo) 03447 { 03448 pCurrentCompoundInfo->AttrTypesToFactorOut.DeleteAll(); 03449 pCurrentCompoundInfo = (PreDelFactorOutInfoItem*) 03450 (pPreDelFactorOutInfoList->GetNext(pCurrentCompoundInfo)); 03451 } 03452 pPreDelFactorOutInfoList->DeleteAll(); 03453 return FALSE; 03454 03455 } 03456 */ 03457 /******************************************************************************************** 03458 03459 > BOOL UndoableOperation::DoFactorOutAfterDeletion(List* pPreDelFactorOutInfoList) 03460 03461 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03462 Created: 31/5/95 03463 Inputs: pPreDelFactorOutInfoList: given to you by the GetPreDelFactorOutInfo function 03464 03465 All items on this list are helpfully deleted after 03466 this function returns. 03467 Outputs: - 03468 Returns: TRUE if successful, 03469 FALSE if we run out of memory. Tidyup then call End() 03470 03471 Purpose: Whenever we delete a range of objects, if any objects in the range have compound 03472 parents then we must adjust the common attributes on these compounds. 03473 03474 For example if a compound has three red objects, and two yellow objects. If 03475 we delete the yellow objects, then red should become a common attribute of the 03476 compound. 03477 03478 We could acheive this feat by firstly localising all attributes on the range, hiding 03479 the range, and then factoring out after. However this will result in unecessary 03480 attributes dangling off hidden nodes, so we try to be a bit cleverer than this. 03481 03482 Prior to deleting the range we call GetPreDelFactorOutInfo. This function returns 03483 us a list containing information about which attributes need to be factored out 03484 on which compounds. 03485 03486 After deleting the range this function should be called to factor 03487 out the neccessary attributes. 03488 03489 Errors: - 03490 SeeAlso: UndoableOperation::GetPreDelFactorOutInfo 03491 03492 ********************************************************************************************/ 03493 03494 // This function should get called after a range of objects have been deleted ('Hidden') 03495 /* 03496 BOOL UndoableOperation::DoFactorOutAfterDeletion(List* pPreDelFactorOutInfoList) 03497 { 03498 PreDelFactorOutInfoItem* pCurrentCompoundInfo = 03499 (PreDelFactorOutInfoItem*)(pPreDelFactorOutInfoList->GetHead()); 03500 03501 NodeRenderableInk* pCompound; 03502 03503 BOOL ok = TRUE; 03504 03505 // Process each record 03506 while (ok && pCurrentCompoundInfo) 03507 { 03508 // Are there any attributes to factor out ?? 03509 if (!(pCurrentCompoundInfo->AttrTypesToFactorOut.IsEmpty())) 03510 { 03511 pCompound = pCurrentCompoundInfo->pCompound; 03512 ERROR3IF(!(pCompound->IsCompound()), "List should only contain compound objects"); 03513 if (pCompound->IsCompound()) 03514 { 03515 if (!DoFactorOutCommonChildAttributes((NodeRenderableInk*)pCompound, 03516 TRUE, // Global 03517 &(pCurrentCompoundInfo->AttrTypesToFactorOut))) 03518 { 03519 ok = FALSE; 03520 } 03521 } 03522 } 03523 pCurrentCompoundInfo = (PreDelFactorOutInfoItem*) 03524 (pPreDelFactorOutInfoList->GetNext(pCurrentCompoundInfo)); 03525 } 03526 03527 // Delete all items on the pPreDelFactorOutInfoList 03528 pCurrentCompoundInfo = (PreDelFactorOutInfoItem*)(pPreDelFactorOutInfoList->GetHead()); 03529 while (pCurrentCompoundInfo) 03530 { 03531 pCurrentCompoundInfo->AttrTypesToFactorOut.DeleteAll(); 03532 pCurrentCompoundInfo = (PreDelFactorOutInfoItem*) 03533 (pPreDelFactorOutInfoList->GetNext(pCurrentCompoundInfo)); 03534 } 03535 pPreDelFactorOutInfoList->DeleteAll(); 03536 03537 if (!ok) 03538 FailAndExecute(); 03539 return ok; 03540 03541 } 03542 */ 03543 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 03544 03545 /******************************************************************************************** 03546 03547 > BOOL UndoableOperation::DoFactorOutCommonChildAttributes(NodeRenderableInk* CompoundObject, 03548 BOOL Global = FALSE, 03549 List* pAffectedAttrTypes = NULL) 03550 03551 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03552 Created: 7/8/94 03553 03554 Inputs: CompoundObject: The object to factor out 03555 03556 Global: When TRUE we recursively factor out attributes in all parent compounds 03557 of the CompoundObject (Bottom up) 03558 03559 pAffectedAttrTypes: An optional set of attribute types. If this is specified 03560 then we only consider factoring out those attributes 03561 which have a type which is in this set. 03562 03563 Returns: TRUE if successful 03564 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 03565 03566 Purpose: This function factors out all attributes which are common to all children 03567 of the compound object. All common attributes become first children of the 03568 compound object. 03569 03570 If the compound contains a node which can discard its attributes (like the caret) 03571 then its attributes are not considered when factoring out. 03572 03573 As with all Do functions actions are generated to undo/redo 03574 03575 Errors: - 03576 SeeAlso: UndoableOperation::DoLocaliseCommonAttributes 03577 SeeAlso: NodeRenderableInk::FactorOutCommonChildAttributes 03578 03579 ********************************************************************************************/ 03580 03581 BOOL UndoableOperation::DoFactorOutCommonChildAttributes(NodeRenderableInk* CompoundObject, 03582 BOOL Global, 03583 AttrTypeSet* pAffectedAttrTypes /* = NULL */) 03584 { 03585 LocaliseCommonAttrAct* UndoAct; 03586 03587 if (!CompoundObject->DiscardsAttributeChildren()) 03588 { 03589 // Make a copy of the attribute type set to store in the Action 03590 AttrTypeSet* pSet = NULL; 03591 if (pAffectedAttrTypes) 03592 { 03593 pSet = pAffectedAttrTypes->CopySet(); 03594 if (!pSet) 03595 return FALSE; // Failed to create set 03596 } 03597 03598 // Create an action to localise attributes which have been factored out 03599 if ( LocaliseCommonAttrAct::Init(this, 03600 &UndoActions, 03601 CompoundObject, 03602 Global, 03603 pSet, 03604 (Action**)(&UndoAct)) 03605 == AC_FAIL) 03606 return FALSE; 03607 } 03608 03609 if (!CompoundObject->FactorOutCommonChildAttributes(Global, pAffectedAttrTypes)) 03610 { 03611 FailAndExecuteAllButLast(); 03612 return FALSE; 03613 } 03614 03615 return TRUE; 03616 } 03617 03618 /******************************************************************************************** 03619 03620 > BOOL UndoableOperation::DoFactorOutCommonAttributes(NodeRenderableInk* CompoundObject, 03621 BOOL Global = FALSE, 03622 List* pAffectedAttrTypes = NULL) 03623 03624 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 03625 Created: 13/10/2004 03626 03627 Inputs: CompoundObject: The object to factor out 03628 Global: When TRUE we recursively factor out attributes in all parent compounds 03629 of the CompoundObject (Bottom up) 03630 pAffectedAttrTypes: An optional set of attribute types. If this is specified 03631 then we only consider factoring out those attributes 03632 which have a type which is in this set. 03633 Returns: TRUE if successful 03634 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 03635 Purpose: Just a wrapper to get function name consistency and do the 03636 common IsCompound test to prevent DFOCCA from barfing... 03637 03638 Errors: - 03639 SeeAlso: UndoableOperation::DoLocaliseCommonAttributes 03640 SeeAlso: NodeRenderableInk::FactorOutCommonChildAttributes 03641 03642 ********************************************************************************************/ 03643 03644 BOOL UndoableOperation::DoFactorOutCommonAttributes(NodeRenderableInk* pRootNode, 03645 BOOL bGlobal, 03646 AttrTypeSet* pAffectedAttrTypes /* = NULL */) 03647 { 03648 if (pRootNode->IsCompound()) 03649 DoFactorOutCommonChildAttributes(pRootNode, bGlobal, pAffectedAttrTypes); 03650 03651 return TRUE; 03652 } 03653 03654 /******************************************************************************************** 03655 03656 > BOOL UndoableOperation::DoLocaliseCommonAttributes(NodeRenderableInk* CompoundObject, 03657 BOOL CheckForDuplicates = FALSE, 03658 BOOL Global = FALSE, 03659 AttrTypeSet* pAffectedAttrTypes = NULL) 03660 03661 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03662 Created: 7/8/94 03663 03664 Inputs: CompoundObject: The compound object to localise 03665 03666 CheckForDuplicates: Checks for duplicate attributes being attached to 03667 children of the compound. This should never be 03668 the case as it would indicate that common attributes 03669 are incorrect. 03670 03671 Global: TRUE indicates that we should localise the attributes on all 03672 parent compound nodes (top down), before localising the attributes for 03673 this compound. 03674 03675 pAffectedAttrTypes: An optional set of attribute types. If this is specified 03676 then we only consider localising those attributes 03677 which have a type which is in this set. 03678 03679 Outputs: - 03680 Returns: TRUE if successful 03681 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 03682 03683 Purpose: This function is the opposite of DoFactorOutCommonChildAttributes 03684 it copies all attributes common to the compound object to each child object 03685 within the group which requires each attribute. The groups common attributes 03686 are deleted. 03687 03688 As with all Do functions actions are generated to undo/redo 03689 03690 Errors: - 03691 SeeAlso: UndoableOperation::DoFactorOutCommonChildAttributes 03692 SeeAlso: NodeRenderableInk::LocaliseCommonAttributes 03693 03694 ********************************************************************************************/ 03695 03696 03697 BOOL UndoableOperation::DoLocaliseCommonAttributes(NodeRenderableInk* CompoundObject, 03698 BOOL CheckForDuplicates /* FALSE */, 03699 BOOL Global /*= FALSE*/, 03700 AttrTypeSet* pAffectedAttrTypes /*= NULL*/) 03701 { 03702 03703 FactorOutCommonChildAttrAct* UndoAct; 03704 03705 03706 // Create an action to localise attributes which have been factored out 03707 if (!CompoundObject->DiscardsAttributeChildren()) 03708 { 03709 // Make a copy of the attribute type set to store in the Action 03710 AttrTypeSet* pSet = NULL; 03711 if (pAffectedAttrTypes) 03712 { 03713 pSet = pAffectedAttrTypes->CopySet(); 03714 if (!pSet) 03715 return FALSE; // Failed to create set 03716 } 03717 03718 if ( FactorOutCommonChildAttrAct::Init(this, 03719 &UndoActions, 03720 CompoundObject, 03721 Global, 03722 pSet, 03723 (Action**)(&UndoAct)) 03724 == AC_FAIL) 03725 return FALSE; 03726 } 03727 03728 if (!CompoundObject->LocaliseCommonAttributes(CheckForDuplicates, 03729 Global, 03730 pAffectedAttrTypes)) 03731 { 03732 FailAndExecuteAllButLast(); 03733 return FALSE; 03734 } 03735 03736 return TRUE; 03737 } 03738 03739 03740 03741 03742 03743 03744 /******************************************************************************************** 03745 03746 static BOOL UndoableOperation::RegisterOpDescriptor( 03747 UINT32 toolID, 03748 UINT32 txID, 03749 CCRuntimeClass* RuntimeClass, 03750 TCHAR* tok, 03751 pfnGetState gs, 03752 UINT32 helpId, 03753 UINT32 bubbleID , 03754 UINT32 resourceID, 03755 UINT32 controlID, 03756 SystemBarType GroupBarID, 03757 BOOL ReceiveMessages, 03758 BOOL Smart, 03759 BOOL Clean, 03760 UINT32 OneOpenInstID, 03761 UINT32 AutoStateFlags) 03762 03763 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 03764 Created: 22/3/94 03765 Inputs: 03766 toolID : Module Identifier 03767 txID : String Resource ID 03768 RuntimeClass : The runtime class of the Opdescriptor's operation 03769 tok, : pointer to the token string 03770 gs, : pointer to the GetState function 03771 helpId : help identifier 03772 bubbleID : string resource for bubble help 03773 bmpID = 0, : bitmap ("icon") resource ID 03774 ReceiveMessages : Does the OpDescriptor need to respond to system messages 03775 Smart ; A smart duplicate operation ? 03776 Clean : Does the operation change the document 03777 OneOpenInstID : When not 0 only one live instance 03778 of the operation is permitted, and 03779 OneOpenInstID is the string resource 03780 ID describing why.This will be useful 03781 for 1 open instance dialogs. 03782 AutoStateFlags : Flags which indicate conditions when 03783 the operation should automatically be 03784 greyed/ticked etc..(This cuts down the 03785 number of tests that need to be made in the 03786 GetState function). See Opdesc.h for a description 03787 03788 03789 03790 Outputs: - 03791 Returns: TRUE if successful, else ERROR is called and FALSE returned. 03792 03793 Purpose: This function should be called from the operations Init method. It 03794 creates an OpDescriptor for the operation 03795 03796 SeeAlso: UndoableOpDescriptor::UndoableOpDescriptor 03797 03798 ********************************************************************************************/ 03799 03800 03801 BOOL UndoableOperation::RegisterOpDescriptor( 03802 UINT32 toolID, 03803 UINT32 txID, 03804 CCRuntimeClass* RuntimeClass, 03805 TCHAR* tok, 03806 pfnGetState gs, 03807 UINT32 helpId, 03808 UINT32 bubbleID , 03809 UINT32 resourceID, 03810 UINT32 controlID, 03811 SystemBarType GroupBarID, 03812 BOOL ReceiveMessages, 03813 BOOL Smart, 03814 BOOL Clean, 03815 UINT32 OneOpenInstID, 03816 UINT32 AutoStateFlags) 03817 { 03818 // tok is going into a string which turns out to be a string 32, so do a sanity check (Neville 26/6/97) 03819 size_t len = camStrclen(tok); 03820 ERROR2IF(len > 32,FALSE,"UndoableOperation::RegisterOpDescriptor token buffer overflow!"); 03821 03822 // Try to create the OpDescriptor 03823 UndoableOpDescriptor* OpDesc = new UndoableOpDescriptor( 03824 toolID, 03825 txID, 03826 RuntimeClass, 03827 tok, 03828 gs, 03829 helpId, 03830 bubbleID, 03831 resourceID, 03832 controlID, 03833 ReceiveMessages, 03834 Smart, 03835 Clean, 03836 OneOpenInstID, 03837 AutoStateFlags 03838 ); 03839 03840 ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE); 03841 return TRUE; 03842 } 03843 03844 03845 03846 /******************************************************************************************** 03847 03848 > BOOL UndoableOperation::DoInsertPathElement(NodePath* ThisPath, 03849 INT32 Index, 03850 DocCoord NewCoord, 03851 PathFlags NewFlags, 03852 PathVerb NewVerb, 03853 BOOL RedrawPath = TRUE); 03854 03855 03856 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 03857 Created: 30/9/94 03858 Inputs: ThisPath points at the NodePath into which we want to insert an element 03859 Index - the the element will be inserted after this element 03860 NewCoord is the new element's coordinate value 03861 NewFlags is the new element's flags value 03862 NewVerb is the new element's verb value 03863 RedrawPath - TRUE if the screen area of the path should be redrawn, FALSE 03864 if the caller is responsible 03865 Outputs: - 03866 Returns: TRUE if succeeded, FALSE otherwise 03867 Purpose: This 'Do' function adds an element into a path, recording undo information 03868 Errors: - 03869 SeeAlso: - 03870 03871 ********************************************************************************************/ 03872 03873 BOOL UndoableOperation::DoInsertPathElement(NodePath* ThisPath, 03874 INT32 Index, 03875 DocCoord NewCoord, 03876 PathFlags NewFlags, 03877 PathVerb NewVerb, 03878 BOOL RedrawPath) 03879 { 03880 #ifndef STANDALONE 03881 03882 // Create an undo action for this action, which is a RemoveElementAction 03883 RemovePathElementAction* pAction = NULL; 03884 ActionCode Act; 03885 Act = RemovePathElementAction::Init(this, 03886 &UndoActions, 03887 1, 03888 Index+1, 03889 (Action**)(&pAction)); 03890 if (Act == AC_FAIL) 03891 return FALSE; 03892 03893 // Tell the operation where the path is 03894 if (pAction != NULL) 03895 pAction->RecordPath(ThisPath); 03896 03897 Document* pDocument = GetWorkingDoc(); 03898 ERROR2IF(pDocument == NULL, FALSE, "There was no Document when deleteing path elements"); 03899 Spread* pSpread = ThisPath->FindParentSpread(); 03900 ERROR2IF(pSpread == NULL, FALSE, "Path had no parent spread"); 03901 03902 // Force a re-draw of the place where the path used to be 03903 if (RedrawPath) 03904 { 03905 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 03906 // Mark the region as requiring object to recache any cached 03907 // data they may be holding because the Op may have changed it 03908 pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath ); 03909 } 03910 03911 // Insert the new element 03912 ThisPath->InkPath.InsertSection(Index,1); 03913 03914 // Get pointers to all the arrays of the path 03915 PathVerb* Verbs = NULL; 03916 DocCoord* Coords = NULL; 03917 PathFlags* Flags = NULL; 03918 ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags); 03919 03920 // Update the coords 03921 Verbs[Index+1] = NewVerb; 03922 Flags[Index+1] = NewFlags; 03923 Coords[Index+1] = NewCoord; 03924 03925 // The bounding rect may have changed 03926 ThisPath->InvalidateBoundingRect(); 03927 03928 // Redraw the new area 03929 if (RedrawPath) 03930 { 03931 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 03932 // Mark the region as requiring object to recache any cached 03933 // data they may be holding because the Op may have changed it 03934 pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath ); 03935 } 03936 03937 #endif 03938 return TRUE; 03939 } 03940 03941 03942 03943 /******************************************************************************************** 03944 03945 > BOOL UndoableOperation::DoReversePath (NodePath* ThisPath, BOOL RedrawPath = TRUE) 03946 03947 Author: Chris_Snook (Xara Group Ltd) <camelotdev@xara.com> 03948 Created: 11/5/2000 03949 Inputs: ThisPath points at the NodePath that we want to reverse 03950 RedrawPath - TRUE if the screen area of the path should be redrawn, FALSE 03951 if the caller is responsible 03952 Outputs: The reversed path in ThisPath->InkPath 03953 Returns: TRUE if succeeded, FALSE otherwise 03954 Purpose: This 'Do' function reverse a path, recording undo information 03955 Errors: - 03956 SeeAlso: - 03957 03958 ********************************************************************************************/ 03959 03960 BOOL UndoableOperation::DoReversePath (NodePath* ThisPath, BOOL RedrawPath /*= TRUE*/) 03961 { 03962 PathVerb* Verbs = NULL; 03963 PathFlags* Flags = NULL; 03964 DocCoord* Coords = NULL; 03965 ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags); 03966 INT32 NumCoords = ThisPath->InkPath.GetNumCoords(); 03967 //BOOL PrevSelected = FALSE; 03968 //INT32 PrevPos = 0; 03969 03970 Document* pDocument = GetWorkingDoc(); 03971 Spread* pSpread = ThisPath->FindParentSpread(); 03972 03973 if (RedrawPath) 03974 { 03975 // Mark the region as requiring object to recache any cached 03976 // data they may be holding because the Op may have changed it 03977 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 03978 pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath ); 03979 } 03980 03981 Path* NewEditPath = new Path (); 03982 03983 if (!NewEditPath) { return (FALSE); } 03984 03985 if (!NewEditPath->Initialise(NumCoords, 24)) 03986 { 03987 return FALSE; 03988 } 03989 03990 // for the new reversed path .... 03991 03992 PathVerb* Verbs2 = NULL; 03993 PathFlags* Flags2 = NULL; 03994 DocCoord* Coords2 = NULL; 03995 NewEditPath->GetPathArrays(&Verbs2, &Coords2, &Flags2); 03996 03997 NewEditPath->CopyPathDataFrom (&(ThisPath->InkPath)); 03998 03999 // the following loop as been optimised since this is the main part of the alg 04000 // (which just reverses the supplied path) .... 04001 04002 NewEditPath->Reverse (); 04003 04004 // now lets build an undo record .... 04005 04006 ModifyPathAction* ModAction; // since thats what were doing 04007 04008 ActionCode Act; 04009 Act = ModifyPathAction::Init(this, &UndoActions, NumCoords, (Action**)(&ModAction)); 04010 if (Act == AC_FAIL) 04011 { 04012 FailAndExecute(); 04013 End(); 04014 return FALSE; 04015 } 04016 04017 // for use with the action .... 04018 04019 PathVerb* ChangedVerbs = NULL; 04020 DocCoord* ChangedCoords = NULL; 04021 PathFlags* ChangedFlags = NULL; 04022 INT32* ChangedIndices = NULL; 04023 04024 // If the function returned AC_NO_RECORD we shouldn't record any undo information in the action 04025 // NOTE - during unwind all actions return AC_OK with a NULL ModAction pointer!! 04026 if ((Act!=AC_NORECORD) && (ModAction!=NULL)) 04027 { 04028 // This next bit is a bit hellish. Any one of these four allocations can fail, in which case 04029 // we have to tidy up afterwards. Cue a lot of nested ifs and elses. 04030 04031 ALLOC_WITH_FAIL(ChangedVerbs,(PathVerb*) CCMalloc(NumCoords * sizeof(PathVerb)),this); 04032 if (ChangedVerbs) 04033 { 04034 ALLOC_WITH_FAIL(ChangedCoords,(DocCoord*) CCMalloc(NumCoords * sizeof(DocCoord)),this); 04035 if (ChangedCoords) 04036 { 04037 ALLOC_WITH_FAIL(ChangedFlags,(PathFlags*) CCMalloc(NumCoords * sizeof(PathFlags)),this); 04038 if (ChangedFlags) 04039 { 04040 ALLOC_WITH_FAIL(ChangedIndices,(INT32*) CCMalloc(NumCoords * sizeof(INT32)),this); 04041 if (!ChangedIndices) 04042 { 04043 CCFree( ChangedFlags ); 04044 CCFree( ChangedCoords ); 04045 CCFree( ChangedVerbs); 04046 FailAndExecute(); 04047 End(); 04048 return FALSE; 04049 } 04050 } 04051 else 04052 { 04053 CCFree( ChangedCoords ); 04054 CCFree( ChangedVerbs ); 04055 FailAndExecute(); 04056 End(); 04057 return FALSE; 04058 } 04059 } 04060 else 04061 { 04062 CCFree( ChangedVerbs); 04063 FailAndExecute(); 04064 End(); 04065 return FALSE; 04066 } 04067 } 04068 04069 // Now to put the undo data into the undo action 04070 for (INT32 loop = 0; loop < NumCoords; loop++) 04071 { 04072 ChangedVerbs [loop] = Verbs [loop]; 04073 ChangedFlags [loop] = Flags [loop]; 04074 ChangedCoords [loop] = Coords [loop]; 04075 ChangedIndices [loop] = loop; 04076 } 04077 04078 // Now we've allocated the arrays, let's tell the action about 'em 04079 ModAction->StoreArrays (ChangedVerbs, ChangedFlags, ChangedCoords, ChangedIndices, ThisPath);//pOrigPath); 04080 04081 } 04082 04084 04085 // now we update the supplied path with the new path .... 04086 04087 ThisPath->InkPath.CopyPathDataFrom (NewEditPath); 04088 04089 delete (NewEditPath); // and clean up 04090 04091 if (ThisPath->IsANodeBlendPath ()) 04092 { 04093 ((NodeBlendPath*) ThisPath)->DestroyCachedInformation (); 04094 } 04095 04097 04098 ThisPath->InvalidateBoundingRect(); 04099 04100 if (RedrawPath) 04101 { 04102 DocRect Invalid = ThisPath->GetUnionBlobBoundingRect(); 04103 // Mark the region as requiring object to recache any cached 04104 // data they may be holding because the Op may have changed it 04105 pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath ); 04106 } 04107 return (TRUE); 04108 } 04109 04110 04111 04112 /******************************************************************************************** 04113 04114 > virtual void UndoableOperation::PerformMergeProcessing() 04115 04116 04117 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 04118 Created: 04/11/94 04119 Inputs: 04120 Outputs: - 04121 Returns: - 04122 Purpose: This function gets called from the Operation::EndOp method after the operation 04123 has ended successfuly and been added to the operation history. If the operation 04124 could potentially merge itself with the previous operation then perform this 04125 merging here. 04126 04127 Note that this base class function does nothing. 04128 04129 Here are some rules: 04130 04131 1. Note that this function cannot fail - doing so would really be quite scarry. 04132 If however you find yourself running out of memory in this function then 04133 simply tidyup (leaving things how they were) and then return. 04134 04135 2. After this function has been invoked the operation history size must not be 04136 greater than it was prior to the function getting called. It would be quite 04137 unimpressive if this was the case anyway. 04138 04139 3. In many cases you will want to delete 'this' operation from this function. so 04140 just be careful huh! 04141 04142 The way that you merge the operation is basically up to you, however here are a few 04143 pointers. 04144 04145 * The first thing you should do on entering this function is to check that the 04146 last Operation added to the operation history is this operation. Call 04147 OperationHistory::FindLastOp to verify this within an ERROR2 Macro. 04148 04149 * Next you will probably want to check if the previous operation performed can be 04150 merged with this op. To obtain the previous op call OperationHistory::FindPrevToLastOp. 04151 Testing the runtime class of this op is probably enough for you to determine if 04152 a merge can take place or not. 04153 04154 * Usually you will want to augment the previous operation in some way so that it 04155 includes the function which was performed by this operation. This could involve 04156 changing an action in the previous operation, adding new actions etc. 04157 04158 04159 04160 Errors: - 04161 SeeAlso: OperationHistory::FindLastOp 04162 OperationHistory::FindPrevToLastOp 04163 OperationHistory::DeleteLastOp 04164 ActionList:: 04165 04166 04167 ********************************************************************************************/ 04168 void UndoableOperation::PerformMergeProcessing() 04169 { 04170 return; // The base class function does nowt 04171 } 04172 04173 04174 04175 /******************************************************************************************** 04176 > void UndoableOperation::MergeWithPrevious() 04177 04178 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 04179 Created: 10/1/96 04180 Inputs: - 04181 Outputs: - 04182 Returns: - 04183 Purpose: Moves all the actions in this operation to the end of the previous action, then 04184 deletes this action 04185 SeeAlso: UndoableOperation::PerformMergeProcessing() 04186 ********************************************************************************************/ 04187 void UndoableOperation::MergeWithPrevious() 04188 { 04189 // Obtain a pointer to the operation history for the operation's document 04190 OperationHistory* pOpHist = &GetWorkingDoc()->GetOpHistory(); 04191 if (pOpHist == NULL) 04192 { 04193 ERROR3("There's no OpHistory!?"); 04194 return; 04195 } 04196 04197 // Ensure that we are the last operation added to the operation history 04198 if (pOpHist->FindLastOp() != this) 04199 { 04200 ERROR3("Last Op should be this op"); 04201 return; 04202 } 04203 04204 Operation* pPrevOp = pOpHist->FindPrevToLastOp(); 04205 if (pPrevOp != NULL) 04206 { 04207 // Copy all the actions from us to the end of the action list in the previous op. 04208 ActionList* pPrevActions = pPrevOp->GetUndoActionList(); 04209 ActionList* pPrevOtherActions = pPrevOp->GetRedoActionList(); 04210 if ((pPrevActions == NULL) || (pPrevOtherActions == NULL)) 04211 { 04212 ERROR3("Previous actions list pointer was NULL"); 04213 return; 04214 } 04215 04216 // Copy actions across 04217 Action* pCurrentAction = (Action*)UndoActions.RemoveHead(); 04218 while (pCurrentAction != NULL) 04219 { 04220 pCurrentAction->TransferToOtherOp(pPrevOp, pPrevActions, pPrevOtherActions); 04221 pCurrentAction = (Action*)UndoActions.RemoveHead(); 04222 } 04223 } 04224 04225 // And remove ourself 04226 pOpHist->DeleteLastOp(); 04227 } 04228 04229 /******************************************************************************************** 04230 > void UndoableOperation::IncludeParentTransformNodesInRange(Range * Rng) 04231 04232 Author: David_McClarnon (Xara Group Ltd) <camelotdev@xara.com> 04233 Created: 15/5/99 04234 Inputs: - 04235 Outputs: A new range (to be deleted outside of this function) 04236 Returns: - 04237 Purpose: Transforms the node, checking if any parents need to transform too 04238 SeeAlso: 04239 ********************************************************************************************/ 04240 Range * UndoableOperation::IncludeParentTransformNodesInRange(Range * Rng) 04241 { 04242 // do this by changing the selection, and then restoring it 04243 // first, save the current selection 04244 List * pSelList = GetApplication()->FindSelection()->MakeListOfNodes(TRUE); 04245 List * pRngList = Rng->MakeListOfNodes(FALSE); 04246 04247 NodeRenderableInk::DeselectAll(); 04248 04249 NodeListItem * pItem = (NodeListItem *)pRngList->GetHead(); 04250 Node * pParent = NULL; 04251 04252 while (pItem) 04253 { 04254 if (pItem->pNode) 04255 { 04256 if (pItem->pNode->IsRenderable()) 04257 { 04258 pItem->pNode->SetSelected(TRUE); 04259 04260 pParent = pItem->pNode->FindParent(); 04261 04262 while (pParent) 04263 { 04264 if (pParent->ShouldITransformWithChildren()) 04265 { 04266 pParent->SetSelected(TRUE); 04267 } 04268 04269 pParent = pParent->FindParent(); 04270 } 04271 } 04272 } 04273 04274 pItem = (NodeListItem *)pRngList->GetNext(pItem); 04275 } 04276 04277 pRngList->DeleteAll(); 04278 delete pRngList; 04279 04280 // update the selection range and make a copy of it 04281 GetApplication()->UpdateSelection(); 04282 04283 Range * RetnRng = new Range(*(GetApplication()->FindSelection())); 04284 04285 // restore the selection 04286 NodeRenderableInk::DeselectAll(); 04287 04288 pItem = (NodeListItem *)pSelList->GetHead(); 04289 04290 while (pItem) 04291 { 04292 if (pItem->pNode) 04293 { 04294 pItem->pNode->SetSelected(TRUE); 04295 } 04296 04297 pItem = (NodeListItem *)pSelList->GetNext(pItem); 04298 } 04299 04300 // update the selection range and make a copy of it 04301 GetApplication()->UpdateSelection(); 04302 04303 pSelList->DeleteAll(); 04304 delete pSelList; 04305 04306 // set the range control flags 04307 RangeControl rg = RetnRng->GetRangeControlFlags(); 04308 rg.PromoteToParent = TRUE; 04309 04310 RetnRng->SetRangeControl(rg); 04311 04312 return RetnRng; 04313 } 04314 04315 04316 /******************************************************************************************** 04317 04318 > virtual BOOL UndoableOperation::UpdateChangedNodes( ObjChangeParam* pParam, 04319 Spread* pSpread = NULL ) 04320 04321 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 04322 Created: 19/01/2000 04323 Inputs: pParam describes the way an op has changes the node(s) 04324 pSpread pointer to the spread containing the nodes affected by the Op 04325 NULL => use the currently selected spread. 04326 Returns: TRUE if all affected nodes were able to cope with the Op, 04327 FALSE otherwise. 04328 04329 Purpose: This function overrides its namesake in Operation, calling the base function 04330 first and then adding a call to NameGallery::PostTriggerEdit if this Op 04331 admits it may change the bounds of nodes within the tree. 04332 04333 Errors: ERROR2 if pParam is NULL. 04334 SeeAlso: Operation::UpdateChangedNodes(ObjChangeParam*, Spread*) 04335 04336 ********************************************************************************************/ 04337 04338 BOOL UndoableOperation::UpdateChangedNodes(ObjChangeParam* pParam,Spread* pSpread) 04339 { 04340 ERROR2IF(pParam == NULL, FALSE, "pParam == NULL"); 04341 BOOL ok = Operation::UpdateChangedNodes(pParam, pSpread); 04342 04344 // Karim MacDonald 19/01/2000. 04345 // If the call to the base class was successful, then we can try to call 04346 // NameGallery::PostTriggerEdit, to allow any nodes marked for stretching to 04347 // change in response to any bounds changes in the tree. 04348 if (ok) 04349 { 04350 // When querying the Op responsible for the change, I've decided to use 'this'. 04351 // Could've asked pParam for the Op's pointer, but 'this' is probably more reliable. 04352 UndoableOperation* pChangeOp = this;// pParam->GetOpPointer(); 04353 if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds() && NameGallery::Instance()) 04354 { 04355 NameGallery::Instance()->PostTriggerEdit(pChangeOp, pParam); 04356 } 04357 } 04359 04360 return ok; 04361 } 04362 04363 04364 04365 /******************************************************************************************** 04366 04367 > BOOL UndoableOperation::NoStretchUpdateChangedNodes(ObjChangeParam* pParam, 04368 Document* pDoc) 04369 04370 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 04371 Created: 19/01/2000 04372 Inputs: pParam description of the change. 04373 pDoc pointer to the document. 04374 Outputs: Causes any nodes marked for updating to update themselves. 04375 Returns: TRUE if successful, 04376 FALSE otherwise. 04377 04378 Purpose: This is *not* an override of Operation::UpdateChangedNodes(..., Document*), 04379 although it performs *almost* exactly the same job. It is provided solely 04380 for NameGallery::PostTriggerEdit to call in order to get all affected nodes 04381 to update themselves correctly after a stretch. PostTriggerEdit is itself 04382 called in response to normal UpdateChangedNodes calls, so this avoids 04383 recursion problems. 04384 04385 Errors: We ERROR2 if either of the parameters is NULL. 04386 See also: Operation::UpdateChangedNodes(ObjChangeParam*, Document*). 04387 04388 ********************************************************************************************/ 04389 BOOL UndoableOperation::NoStretchUpdateChangedNodes(ObjChangeParam* pParam, Document* pDoc) 04390 { 04391 ERROR2IF(pParam == NULL, FALSE, "pParam == NULL"); 04392 ERROR2IF(pDoc == NULL, FALSE, "pDoc == NULL"); 04393 04394 BOOL ok = TRUE; 04395 Chapter* pChapter = Node::FindFirstChapter(pDoc); 04396 while (pChapter != NULL && ok) 04397 { 04398 // Now find the first child of the Chapter, because this is the level at which Spreads hang out 04399 Node* pNode = pChapter->FindFirstChild(); 04400 04401 // Update changed nodes in each of the chapter's spreads. We *don't* call 04402 // Operation::UpdateChangedNodes(..., Spread*), as that would lead to recursion... 04403 while (pNode != NULL && ok) 04404 { 04405 if (pNode->IsSpread()) 04406 { 04407 Spread* pSpread = (Spread*)pNode; 04408 ok = UpdateChangedNode(pParam, pSpread->FindFirstChild()); 04409 } 04410 04411 pNode = pNode->FindNext(); 04412 } 04413 04414 pChapter = pChapter->FindNextChapter(); 04415 } 04416 04417 return ok; 04418 } 04419