undoop.cpp

Go to the documentation of this file.
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