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 ( TransformNo