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 ( TransformNodeAction::Init(this, 
01244                                        &UndoActions, 
01245                                        NodeRange, 
01246                                        Trans,
01247                                        NULL,
01248                                        (Action**)(&UndoTransformNodeAction))
01249                                        == AC_FAIL) 
01250          
01251         {   
01252             delete Trans; // for consistency 
01253             return FALSE; // Let's get out while we can        
01254         }; 
01255         
01256         // Scan the range and transform each node 
01257         Node* Current = NodeRange.FindFirst();
01258         Node* Next; 
01259 //      Node* Parent = NULL;
01260 //      BOOL bTransform = FALSE;
01261         while (Current != NULL)
01262         {
01263             Next = NodeRange.FindNext(Current); 
01264 
01265             if (Current->IsNodeRenderableClass())
01266             {
01267                 // Prepare Control and Feedback flags
01268                 Trans->bHaveTransformedCached = TRUE;
01269                 Trans->bHaveTransformedChildren = TRUE;
01270                 Trans->bTransformYourChildren = TRUE;
01271 
01272                 ((NodeRenderable *)Current)->Transform(*Trans);
01273 
01274                 // Compute feedback flags for caller
01275                 Trans->bHaveTransformedAllCached = Trans->bHaveTransformedAllCached && Trans->bHaveTransformedCached;
01276                 Trans->bHaveTransformedAllChildren = Trans->bHaveTransformedAllChildren && Trans->bHaveTransformedChildren;
01277 
01278                 // ----------------------------------------------
01279                 // Now do smart testing to see whether we can retain any cached info the node was holding
01280                 if (Current->IsBounded())
01281                 {
01282                     NodeRenderableBounded* pCurrentBound = (NodeRenderableBounded*) Current;
01283 
01284                     // If we have transformed all cached data successfully
01285                     // And 
01286                     //      We are translating (in which case the cached data will remain usable)
01287                     //      Or
01288                     //      The node was Direct (in which case the cached data comes from outside the tree where it can't have been transformed)
01289                     // Then
01290                     //      Don't force the recacheing of the data for this node, just its parents
01291                     if (Trans->bHaveTransformedCached && (Trans->IsTranslation() || pCurrentBound->HasCachedDirectBitmap()))
01292                         // Yes, we can keep our cached info but we must tell our parents we have changed
01293                         pCurrentBound->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only
01294                     else
01295                         // No we can't keep our cached info
01296                         pCurrentBound->ReleaseCached(TRUE, TRUE);
01297                 }
01298                 // ----------------------------------------------
01299             }
01300             
01301             // JCF BODGE
01302         #ifdef _DEBUG
01303             else
01304             {
01305                 TRACE( _T("Warning: UndoableOperation::DoTransformNodes: ")
01306                       _T("Current node %s is not a NodeRenderableInk\n"),
01307                         (LPCTSTR) Current->Name());
01308             }
01309         #endif
01310 
01311             Progress::Update();
01312             Current = Next;  
01313         }
01314 
01315         // Invert the Trans which is in the TransformNodeAction
01316         ((TransInvertable*)Trans)->Invert(); 
01317     }
01318     else // The Trans is not invertable
01319     {
01320         // We will have to go through all node in the range, making a copy and transforming it.
01321         // This used to use DoSaveCopyOfNodes, but this dosen't work as it is not completly undoable
01322         Node* Current = NodeRange.FindFirst();
01323         Node* Next = NULL; 
01324         while (Current != NULL)
01325         {
01326             Next = NodeRange.FindNext(Current);
01327             if (Current->IsNodeRenderableClass())
01328             {
01329                 // Make a copy of Current 
01330                 BOOL CopiedOK = TRUE;
01331                 Node* TheCopy = NULL;
01332                 CALL_WITH_FAIL(Current->NodeCopy(&TheCopy), this, CopiedOK);  
01333                 if (!CopiedOK)
01334                     return FALSE;
01335 
01336                 // transform the copy
01337                 ((NodeRenderable*)TheCopy)->Transform(*Trans);
01338 
01339                 // put the copy into the tree
01340                 if (!DoInsertNewNode((NodeRenderableBounded*)TheCopy, Current, PREV, TRUE, FALSE))
01341                 {
01342                     delete TheCopy;
01343                     return FALSE;
01344                 }
01345 
01346                 // Hide the original
01347                 if (!DoHideNode(Current, FALSE))
01348                     return FALSE;
01349             }
01350 
01351             // JCF BODGE
01352         #ifdef _DEBUG
01353             else
01354             {
01355                 TRACE( _T("Warning: UndoableOperation::DoTransformNodes: ")
01356                       _T("Current node %s is not a NodeRenderableInk\n"),
01357                         (LPCTSTR) Current->Name());
01358             }
01359         #endif
01360 
01361             Progress::Update();
01362             Current = Next;  
01363         }
01364     }
01365     return (TRUE); // Success 
01366 }   
01367 
01368 /********************************************************************************************
01369                           
01370 >   BOOL UndoableOperation::DoTransformNode(NodeRenderableInk* NodeToTransform, 
01371                                             TransformBase* Trans);   
01372 
01373     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01374     Created:    13/9/93
01375     Inputs:     NodeToTransform: The node to transform 
01376                 Trans: The transform to apply to the node 
01377     Outputs:    -
01378     Returns:    TRUE if successful 
01379                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01380                 
01381     Purpose:    This high level Do function applies the transform Trans to the 
01382                 NodeToTransform
01383                
01384     Errors:     -
01385     SeeAlso:    UndoableOperation::DoTransformNodes
01386 
01387 ********************************************************************************************/
01388 
01389 BOOL UndoableOperation::DoTransformNode(NodeRenderableInk* NodeToTransform, 
01390                                 TransformBase* Trans)
01391 {
01392     ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed");
01393 //  RangeControl rc = { TRUE, TRUE, TRUE }; // All nodes in range
01394     Range               range( NodeToTransform, NodeToTransform, RangeControl( TRUE, TRUE, TRUE ) );
01395     return( DoTransformNodes( range, Trans ) ); 
01396 }   
01397 
01398 
01399 /********************************************************************************************
01400 
01401 >   BOOL UndoableOperation::DoSelectNode(NodeRenderableInk* NodeToSelect)
01402 
01403     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01404     Created:    20/10/93
01405     Inputs:     NodeToSelect: The node that is to be selected 
01406     Outputs:    -
01407     Returns:    TRUE if successful 
01408                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01409     Purpose:    The function selects NodeToSelect. It also generates an action which will 
01410                 deselect the node when executed.  
01411     Errors:     -
01412     SeeAlso:    UndoableOperation::DoDeselectNode
01413 
01414 ********************************************************************************************/
01415 
01416 BOOL UndoableOperation::DoSelectNode(NodeRenderableInk* NodeToSelect, Spread *Parent)
01417 {   
01418     SelectDeselectAction* UndoSelectDeselectAction; 
01419     // Create an action to deselect the NodeToSelect when we undo. 
01420     if ( SelectDeselectAction::Init(this, 
01421                                     &UndoActions, 
01422                                     NodeToSelect,
01423                                     Parent,
01424                                     (Action**)(&UndoSelectDeselectAction))
01425                                     != AC_FAIL)  
01426     {   
01427         // We shall be able to deselect the object if we fail so select it.       
01428         NodeToSelect->Select(FALSE);  
01429         return (TRUE);      
01430     }
01431     else 
01432         return (FALSE); 
01433 } 
01434 
01435 /********************************************************************************************
01436 
01437 >   BOOL UndoableOperation::DoInvalidateNodesRegions(Range NodeRange, BOOL IncludeBlobs, 
01438                                                      BOOL UndoBlobs = FALSE,
01439                                                      BOOL IfBgRedraw = FALSE,
01440                                                      BOOL bForceRecache = TRUE)
01441 
01442     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01443     Created:    28/2/94
01444     Inputs:     NodeRange:      The range of nodes  
01445                 IncludeBlobs:   When TRUE we take the bounding rectangle of the objects 
01446                                 and their blobs. 
01447                 UndoBlobs:      TRUE, Invert the 'IncludeBlobs' for undo/redo this op.
01448                                 FALSE, use the same 'IncludeBlobs' for undo/redo.
01449                                 
01450     Outputs:    -
01451     Returns:    TRUE if successful 
01452                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01453 
01454     Purpose:    This low level Do function invalidates a region which is the union of
01455                 the bounding rectangles of each node in the range. When the includeBlobs
01456                 flag is TRUE the region which gets invalidated includes the objects blobs.  
01457                 
01458                 constraints: 
01459 
01460                 All members of the range must be on the same spread
01461                 No member of the range can be hidden 
01462                 
01463     Errors:     An ENSURE will occur if the range of nodes is empty, in a retail build 
01464                 the function will return TRUE but do nothing. 
01465 
01466     SeeAlso:    UndoableOperation::DoInvalidateNodeRegion
01467 
01468 ********************************************************************************************/
01469 
01470 BOOL UndoableOperation::DoInvalidateNodesRegions(Range NodeRange, BOOL IncludeBlobs, 
01471                                                  BOOL UndoBlobs,
01472                                                  BOOL IfBgRedraw,
01473                                                  BOOL bForceRecache)
01474 {
01475     ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed"); 
01476 
01477     if (UndoBlobs)
01478     {
01479         // Undo blobs = TRUE
01480         // We will use different 'IncludeBlobs' when we undo/redo this op
01481         // to the one we use initially.
01482         UndoBlobs = !IncludeBlobs;
01483     }
01484     else
01485     {
01486         // Undo blobs = FALSE
01487         // We will use the same 'IncludeBlobs' when we undo/redo this op
01488         // as the one we use initially.
01489         UndoBlobs = IncludeBlobs;
01490     }
01491 
01492     BOOL bOldValue = NodeRange.SetPromoteToParent(TRUE);
01493     NodeRenderableBounded* CurrentNode = (NodeRenderableBounded*)NodeRange.FindFirst(); 
01494     NodeRange.SetPromoteToParent(bOldValue);
01495 
01496     if (CurrentNode == NULL)
01497         return TRUE; // The range is empty so NOP
01498 
01499     // ENSURE the range of nodes is not empty
01500 //  ENSURE (CurrentNode != NULL, "Calling DoInvalidateNodesRegions with an empty range"); 
01501 
01502 //  ENSURE(CurrentNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderable)), 
01503 //          "Trying to invalidate a non NodeRenderable node");
01504 
01505     // Find the spread 
01506     Spread* pSpread = (Spread*)CurrentNode->FindParent(CC_RUNTIME_CLASS(Spread));
01507 
01508 
01509 
01510     // Matt 27/11/2000
01511     // OK, here's the beef... At present, if you group a shadowed object, the tree structure correctly
01512     // reflects this as a NodeShadowController with a NodeGroup parent... Now, when we've clicked on the
01513     // 'Set New Design' button, the NodeGroup is passed OK and is hidden after being copied (as was intended)
01514     // but the NodeShadowController beneath this is then tested: When asked what it's parent spread is, it
01515     // will promptly keel over, triggering the following ENSURE as its parent (the NodeGroup) no longer has
01516     // a pointer to its parent (ie it is NULL) - to cater for this, if we reach this point and we have no
01517     // parent spread, we can assume one of two things:
01518     // 1.   The node above us has been hidden, therefore we are already implicitly hidden and we shouldn't
01519     //      undertake any special work
01520     // 2.   Everything is stuffed so bad in our tree that it really doesn't matter what we do here - we're
01521     //      about to die anyhow...
01522     // Therefore, we can just return without failing with an ensure...
01523     //
01524     // ENSURE(pSpread != NULL, "Spread is NULL"); 
01525 
01526 
01527 
01528     if (pSpread)
01529     {
01530         if (IfBgRedraw)
01531         {
01532             // -------------------------------------------------------------------------------------
01533             // Try and create an InvalidateRegionAction 
01534             InvalidateRegionIfBgRedrawAction* UndoInvalidateRgnAction;                          
01535    
01536             // Create an InvalidateRegionAction  
01537             // Note that unfortunatly the action has to store the node range, rather than the 
01538             // bounding rectangle because GetBlobBoundingRect must be calculated on the fly. 
01539 
01540             if ( InvalidateRegionIfBgRedrawAction::Init(this,                    
01541                                               &UndoActions,
01542                                               NodeRange, 
01543                                               pSpread,      
01544                                               UndoBlobs,
01545                                               ( Action**)(&UndoInvalidateRgnAction)) == AC_FAIL)  
01546             {
01547                 return FALSE;  // Failed to create action 
01548             }  
01549         
01550             if (!GetApplication()->IsBgRendering())
01551             {
01552                 return TRUE;    // Ignore if no Bg Rendering in progress
01553             }
01554 
01555     //      GetApplication()->DeleteRenderRegions(DocView::GetSelected());
01556     //      GetApplication()->BgRendering = FALSE;
01557         }
01558         else
01559         {
01560             // -------------------------------------------------------------------------------------
01561             // Try and create an InvalidateRegionAction 
01562             InvalidateRegionAction* UndoInvalidateRgnAction;                          
01563    
01564             // Create an InvalidateRegionAction  
01565             // Note that unfortunatly the action has to store the node range, rather than the 
01566             // bounding rectangle because GetBlobBoundingRect must be calculated on the fly. 
01567 
01568             if ( InvalidateRegionAction::Init(this,                    
01569                                               &UndoActions,
01570                                               NodeRange, 
01571                                               pSpread,      
01572                                               UndoBlobs,
01573                                               ( Action**)(&UndoInvalidateRgnAction)) == AC_FAIL)  
01574             {
01575                 return FALSE;  // Failed to create action 
01576             }  
01577         }
01578 
01579         ENSURE(pOurDoc != 0, "There was no document in DoInvalidateNodesRegions");
01580 
01581         // Invalidate the bounds of each node in the range
01582 
01583         if (pOurDoc != NULL)
01584         {
01585             DocRect TempRect; 
01586 
01587             BOOL bOldValue = NodeRange.SetPromoteToParent(TRUE);
01588             while (CurrentNode != NULL)
01589             {
01590                 // Because we had a Range param, we could possibly be given any legal node in the tree,
01591                 // e.g. the Insert node, not just renderable bounded objects, hence this 'if' statement
01592                 if (CurrentNode->IsAnObject() || CurrentNode->IsPaper())
01593                 {
01594                     ENSURE(CurrentNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)), 
01595                         "Trying to invalidate a non NodeRenderable node");
01596 
01597                     TempRect = (IncludeBlobs ? 
01598                                     (CurrentNode->GetUnionBlobBoundingRect()):
01599                                     (CurrentNode->GetBoundingRect())
01600                                  );
01601 TempRect = TempRect.Union(CurrentNode->GetEffectStackBounds());
01602 
01603                     // Accumulate this invalid region in a pending list until the Op
01604                     // finishes
01605                     // Mark the region as requiring object to recache any cached
01606                     // data they may be holding because the Op may have changed it
01607 //                  pOurDoc->ForceRedraw(pSpread, TempRect, TRUE, bForceRecache ? CurrentNode : NULL);
01608                     pOurDoc->ForceRedraw(pSpread, TempRect, TRUE, CurrentNode, bForceRecache);
01609                 }
01610 
01611                 CurrentNode = (NodeRenderableBounded*) NodeRange.FindNext(CurrentNode); 
01612             }
01613             NodeRange.SetPromoteToParent(bOldValue);
01614         }
01615     }
01616 
01617     // Now invalidate the region
01618     return TRUE;  
01619 }
01620 
01621 /********************************************************************************************
01622 
01623 >   BOOL UndoableOperation::DoInvalidateNodeRegion(NodeRenderableBounded* Node, 
01624                                                    BOOL IncludeBlobs,
01625                                                    BOOL UndoBlobs,
01626                                                    BOOL IfBgRedraw,
01627                                                    BOOL bForceRecache)
01628     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01629     Created:    28/2/94
01630     Inputs:     Node:           The node that needs to be invalidated    
01631                 IncludeBlobs:   When TRUE we take the blob bounding rectangle of the node 
01632                 UndoBlobs:      TRUE, Invert the 'IncludeBlobs' for undo/redo this op.
01633                                 FALSE, use the same 'IncludeBlobs' for undo/redo.
01634                                 
01635     Outputs:    -
01636     Returns:    TRUE if successful 
01637                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01638 
01639     Purpose:    Invalidates the nodes bounding rectangle,  or blob bounding rectangle 
01640                 
01641     Errors:     -
01642     SeeAlso:    UndoableOperation::DoInvalidateNodesRegions
01643 
01644 ********************************************************************************************/
01645 
01646 BOOL UndoableOperation::DoInvalidateNodeRegion(NodeRenderableBounded* Node, 
01647                                                BOOL IncludeBlobs,
01648                                                BOOL UndoBlobs,
01649                                                BOOL IfBgRedraw,
01650                                                BOOL bForceRecache)
01651 {   
01652     ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed");
01653 
01654     // Range = Sel nodes, Unsel nodes, cross layers, Locked layers, Ignore Non-renderables, Ignore invisible layers
01655     RangeControl        rc( TRUE, TRUE, TRUE, FALSE, TRUE, TRUE );
01656     Range               range( Node, Node, rc );
01657 
01658     return( DoInvalidateNodesRegions( range, IncludeBlobs, UndoBlobs, IfBgRedraw, bForceRecache ) ); 
01659 }
01660 
01661 
01662 
01663 /********************************************************************************************
01664 
01665 >   BOOL UndoableOperation::DoInvalidateRegion(Spread* pSpread, DocRect& InvalidRegion)
01666 
01667     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01668     Created:    25/8/94
01669     Inputs:     pSpread - The spread that the region to invalidate is on
01670                 InvalidRegion - The region that you want to invalidate
01671     Returns:    TRUE if it all worked, FALSE if not (TIDYUP THEN CALL End()!) 
01672     Purpose:    Invalidates the region specified and adds the appropriate action to the
01673                 action list for the current operation. If you are invalidating a region
01674                 associated with a node in the tree then you should be using
01675                 DoInvalidateNodeRegion() or DoInvalidateNodesRegion().
01676     SeeAlso:    UndoableOperation::DoInvalidateNodeRegion
01677 
01678 ********************************************************************************************/
01679 
01680 BOOL UndoableOperation::DoInvalidateRegion( Spread *pSpread, const DocRect &InvalidRegion )
01681 {
01682     // make sure that things are worth doing
01683     ENSURE((!OpFlags.Failed), "Calling a DO function after operation has failed");
01684     ENSURE(pSpread!=NULL, "Attempt to invalidate a region on a null spread.");
01685 
01686     // If the region is empty we could save everyone a lot of trouble
01687     if (InvalidRegion.IsEmpty())
01688         return TRUE;
01689 
01690     // Try and create an InvalidateArbitaryRegionAction and fail if it does not work
01691     InvalidateArbitaryAction* pAction;
01692     ActionCode ActCode = InvalidateArbitaryAction::Init( this, &UndoActions, pSpread,
01693                          InvalidRegion, (Action**)(&pAction));
01694     
01695     // See if it worked
01696     if (ActCode==AC_FAIL)
01697         return FALSE;
01698 
01699     // Now we can invalidate the region for the caller
01700     // First we need a document
01701     ENSURE(pOurDoc != NULL, "There was no document in DoInvalidateRegions");
01702 
01703     // Invalidate the bounds of each node in the range
01704     if (pOurDoc != NULL)
01705         // Accumulate this invalid region in a pending list until the Op
01706         // finishes
01707         // Mark the region as requiring object to recache any cached
01708         // data they may be holding because the Op may have changed it
01709         pOurDoc->ForceRedraw(pSpread, InvalidRegion, TRUE, pSpread);
01710 
01711     // Tell them that everything worked out just fine
01712     return TRUE;
01713 }
01714 
01715 /********************************************************************************************
01716 
01717 >   BOOL UndoableOperation::DoDeselectNode(NodeRenderableInk* NodeToDeselect)
01718 
01719     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01720     Created:    20/10/93
01721     Inputs:     NodeToDeselect: The node that is to be de-selected 
01722     Outputs:    -
01723     Returns:    TRUE if successful 
01724                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01725     Purpose:    The function de-selects NodeToDeselect. It also generates an action which will 
01726                 select the node when executed.  
01727     Errors:     -
01728     SeeAlso:    UndoableOperation::DoDeselectNode
01729 
01730 ********************************************************************************************/
01731 
01732   
01733 BOOL UndoableOperation::DoDeselectNode(NodeRenderableInk* NodeToDeselect, Spread *Parent)
01734 {   
01735     SelectDeselectAction* UndoSelectDeselectAction; 
01736     // Create an action to deselect the NodeToSelect when we undo. 
01737     if ( SelectDeselectAction::Init(this, 
01738                                     &UndoActions, 
01739                                     NodeToDeselect,
01740                                     Parent,
01741                                     (Action**)(&UndoSelectDeselectAction))
01742                                     != AC_FAIL)  
01743     {                   
01744         // We shall be able to select the object if we fail so deselect it. 
01745         NodeToDeselect->DeSelect(FALSE);  
01746         return (TRUE);      
01747     }               
01748     else
01749         return (FALSE); 
01750 } 
01751 
01752 /********************************************************************************************
01753 
01754 >   BOOL UndoableOperation::DoMakeShapes(Range& NodeRange)
01755 
01756     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01757     Created:    03/05/94
01758 
01759     Inputs:     NodeRange: The Range of nodes to make into shapes 
01760       
01761     Returns:    TRUE if successful 
01762                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01763     Purpose:    This low level Do function calls the DoBecomeA virtual function on all 
01764                 SelectedNodes. It is the node's DoBecomeA function which is responsible
01765                 for generating actions. 
01766     Errors:     -
01767     SeeAlso:    Node::DoBecomeA
01768 
01769 ********************************************************************************************/
01770 
01771 BOOL UndoableOperation::DoMakeShapes(Range NodeRange)
01772 {
01773     Node* CurrentNode = NodeRange.FindFirst();
01774     Node* Next; 
01775 
01776     while (CurrentNode != NULL)
01777     {
01778 // BODGE - since the group is selected and won't be replaced by anything else, no need to reselect it
01779 // this fixes a bug where make shapes on grouped text stories/molds/blends don't leave the parent group
01780 // selected but selects the objects inside!
01781         BOOL reselect = !IS_A(CurrentNode,NodeGroup) && !IS_A(CurrentNode,NodeBlend);
01782 
01783         BecomeA BecomeAPath(BECOMEA_REPLACE,CC_RUNTIME_CLASS(NodePath), this, reselect);
01784         Next = NodeRange.FindNext(CurrentNode);
01785         if (!(CurrentNode->DoBecomeA(&BecomeAPath)))
01786         {
01787             // Operation failed
01788             return FALSE; 
01789         } 
01790         CurrentNode = Next; 
01791     }
01792     return TRUE; // Success
01793 }
01794 
01795 /********************************************************************************************
01796 
01797 >   BOOL UndoableOperation::DoCopyNodesToClipboard(Range NodeRange)
01798 
01799     
01800     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01801     Created:    03/05/94
01802 
01803     Inputs:     NodeRange: The Range of nodes to copy
01804       
01805     Returns:    TRUE if successful 
01806                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01807 
01808     Purpose:    This Do function copies all nodes in the range to the internal clipboard.
01809                 The objects are made attribute complete prior to being moved.
01810 
01811                 Before the copy begins StartComponentCopy is called on each DocComponent in
01812                 the clipboard.
01813                 
01814                 The nodes are then copied to the clipboard
01815                 
01816                 CopyComponentData is then called on each node which has been copied to allow
01817                 component data to be copied to the clipboard.
01818 
01819                 In the operations End method: If the operation has not failed then 
01820                 EndComponentCopy is called on each doc component do give them a chance to 
01821                 commit changes. If the operation has failed then AbortComponentCopy is called
01822                 instead.
01823 
01824     Errors:     -
01825     SeeAlso:    NodeRenderableInk::MakeAttributeComplete
01826 
01827 
01828 ********************************************************************************************/
01829 
01830 BOOL UndoableOperation::DoCopyNodesToClipboard(Range NodeRange)
01831 {
01832     BOOL CopiedOK;
01833     BOOL ok;
01834 
01835     InternalClipboard* pInternalClip = InternalClipboard::Instance();
01836 
01837     // After the operation ends we will need to inform all DocComponents in the clipboard
01838     // of the outcome.
01839     InformDocComponentsOfOperationsOutcome(pInternalClip); 
01840 
01841 
01842     // Inform all DocComponents in the clipboard that a copy is about to take place
01843     CALL_WITH_FAIL(pInternalClip->StartComponentCopy(), this, ok)
01844     if (!ok)
01845     {
01846         // Start Component copy has failed so abort operation
01847         // Note that AbortComponentCopy will get called in the ops end method
01848         return FALSE;
01849     } 
01850 
01851     // Try to copy the selection to the clipboard
01852     CALL_WITH_FAIL(InternalClipboard::CopyObjects(NodeRange, this), this, CopiedOK)
01853 
01854     if (!CopiedOK)
01855     {
01856         return FALSE; 
01857     }
01858 
01859     // Now try and copy accross the component data
01860 
01861     CALL_WITH_FAIL(InternalClipboard::CopyComponentData(pOurDoc), this, CopiedOK)
01862 
01863     if (!CopiedOK)
01864     {
01865         return FALSE; 
01866     }
01867 
01868 
01869     // Create action, this action will do nothing for undo but it's twin redo action will
01870     // copy the selection to the clipboard.
01871     CopyObjectsToClipboardAction* UndoCopyObjectsToClipboardAction;                          
01872 
01873     if ( CopyObjectsToClipboardAction::Init(this, 
01874                                        &UndoActions, 
01875                                        NodeRange,  
01876                                       ( Action**)(&UndoCopyObjectsToClipboardAction)) == AC_FAIL)  
01877     {
01878         return FALSE;  // Failed to create action 
01879     } 
01880     return TRUE; // Success
01881 }
01882 
01883 /********************************************************************************************
01884 
01885 >   BOOL UndoableOperation::DoFlattenRange(Range NodeRange) 
01886 
01887     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01888     Created:    3/5/94
01889     Inputs:     -
01890     Outputs:    -
01891     Returns:    -
01892     Purpose:    Not implemented yet
01893     Errors:     -
01894     SeeAlso:    -
01895 
01896 ********************************************************************************************/
01897 
01898 BOOL UndoableOperation::DoFlattenRange(Range NodeRange) 
01899 {
01900     return FALSE;
01901 }
01902 
01903 /********************************************************************************************
01904 
01905     BOOL UndoableOperation::DoRemoveAttrTypeFromSubtree(Node* Subtree, RuntimeClass* AttrType, Node* pExceptThis = NULL);
01906 
01907     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01908     Created:    24/6/94
01909     Inputs:     Subtree:        Subtree to remove nodes from
01910                 NodeClass:      The type of attribute to remove from the subtree
01911                 pExceptThis:    The node that shouldn't be removed!
01912 
01913     Outputs:    -
01914     Returns:    TRUE if successful 
01915                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01916 
01917     Purpose:    Searches the subtree and every attribute which has type AttrType
01918                 is hidden.
01919     Errors:     -
01920     Scope:      private
01921     SeeAlso:    -
01922 
01923 ********************************************************************************************/
01924 
01925 BOOL UndoableOperation::DoRemoveAttrTypeFromSubtree(Node* Subtree, CCRuntimeClass* AttrType, Node* pExceptThis)
01926 {
01927     // Traverse the subtree depth first
01928     Node* Current = Subtree->FindFirstDepthFirst();
01929     Node* Next;
01930     while (Current != NULL)
01931     {
01932         Next =  Current->FindNextDepthFirst(Subtree); 
01933         // Determine if the Current node is to be hidden
01934         if (Current!=pExceptThis && Current->IsKindOf(CC_RUNTIME_CLASS(NodeAttribute)))
01935         {
01936             if (((NodeAttribute*)Current)->GetAttributeType() == AttrType)
01937             {
01938                 if (Current->FindParent()->DiscardsAttributeChildren())
01939                 {
01940                     Current->CascadeDelete();
01941                     delete Current;
01942                 }
01943                 else
01944                 { 
01945                     if(!DoHideNode(Current, 
01946                                    TRUE         // Include the subtree size 
01947                                    ))
01948                     {
01949                         return FALSE;           // Operation failed
01950                     } 
01951                 }
01952             }
01953         }
01954         Current = Next;
01955     }
01956     return TRUE; 
01957 }
01958 
01959 
01960 /********************************************************************************************
01961 
01962 >   BOOL UndoableOperation::DoChangeSelection(NodePath* ThisNode, INT32 Index, BOOL NewState)
01963 
01964     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
01965     Created:    19/7/94
01966     Inputs:     ThisNode points at the NodePath whose state we want to change
01967                 Index is the element we want to change
01968                 NewState is the new state of selection we want to set
01969     Outputs:    -
01970     Returns:    TRUE if succeeded, FALSE otherwise
01971     Purpose:    This 'Do' function changes the selected state of a particular element in a path
01972                 it creates all the necessary undo information so that the selection can be undone
01973     Errors:     -
01974     SeeAlso:    -
01975 
01976 ********************************************************************************************/
01977 
01978 BOOL UndoableOperation::DoChangeSelection(NodePath* ThisNode, INT32 Index, BOOL NewState)
01979 {
01980 #ifndef STANDALONE
01981 
01982     ModifyFlagsAction* UnAction;
01983     PathFlags* Flags = ThisNode->InkPath.GetFlagArray();
01984     if (Flags[Index].IsSelected != NewState)
01985     {
01986         ActionCode Act = ModifyFlagsAction::Init(this, 
01987                                                     &UndoActions,
01988                                                     Flags[Index],
01989                                                     Index,
01990                                                     ThisNode,
01991                                                     (Action**)&UnAction);
01992         if (Act == AC_FAIL)
01993             return FALSE;
01994         
01995         Flags[Index].IsSelected = NewState;
01996         return TRUE;
01997     }
01998 
01999 #endif
02000 
02001     return TRUE;
02002 }
02003 
02004 /********************************************************************************************
02005 
02006 >   BOOL UndoableOperation::DoDeletePathSection(NodePath* ThisPath, INT32 Index, INT32 NumElements, BOOL RedrawPath = TRUE)
02007 
02008     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
02009     Created:    19/7/94
02010     Inputs:     ThisPath points at the NodePath whose state we want to change
02011                 Index is the element we want to change
02012                 NumElements is the number of elements to be deleted
02013                 RedrawPath - TRUE if the path ares should be redrawn, FALSE if the caller will do it
02014     Outputs:    -
02015     Returns:    TRUE if succeeded, FALSE otherwise
02016     Purpose:    This 'Do' function changes the selected state of a particular element in a path
02017                 it creates all the necessary undo information so that the selection can be undone
02018     Errors:     -
02019     SeeAlso:    -
02020 
02021 ********************************************************************************************/
02022 
02023 BOOL UndoableOperation::DoDeletePathSection(NodePath* ThisPath, INT32 Index, INT32 NumElements, BOOL RedrawPath)
02024 {
02025 #ifndef STANDALONE
02026 
02027     InsertPathElementAction* UnAction = NULL;
02028     ActionCode Act;
02029     Act = InsertPathElementAction::Init(this, &UndoActions, NumElements, Index,
02030                                             (Action**)(&UnAction));
02031     if (Act == AC_FAIL)
02032         return FALSE;
02033 
02034     Document* pDocument = GetWorkingDoc();
02035     ERROR2IF(pDocument == NULL, FALSE, "There was no Document when deleteing path elements");
02036     Spread* pSpread = ThisPath->FindParentSpread();
02037     ERROR2IF(pSpread == NULL, FALSE, "Path had no parent spread");
02038 
02039     // Force a re-draw of the place where the path used to be
02040     if (RedrawPath)
02041     {
02042         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
02043         // Mark the region as requiring object to recache any cached
02044         // data they may be holding because the Op may have changed it
02045         pDocument->ForceRedraw(pSpread, Invalid, FALSE, ThisPath);
02046     }
02047 
02048     if ((Act != AC_NORECORD) && (UnAction != NULL))
02049     {
02050         // I have to claim some memory to store the elements I'm deleting...
02051         PathVerb* ChangedVerbs = NULL;
02052         DocCoord* ChangedCoords = NULL;
02053         PathFlags* ChangedFlags = NULL;
02054         ALLOC_WITH_FAIL(ChangedVerbs,(PathVerb*) CCMalloc(NumElements * sizeof(PathVerb)),this);
02055         ALLOC_WITH_FAIL(ChangedCoords,(DocCoord*) CCMalloc(NumElements * sizeof(DocCoord)),this);
02056         ALLOC_WITH_FAIL(ChangedFlags,(PathFlags*) CCMalloc(NumElements * sizeof(PathFlags)),this);
02057 
02058         if (!ChangedVerbs || !ChangedCoords || !ChangedFlags)
02059         {
02060             if (ChangedVerbs) CCFree(ChangedVerbs);
02061             if (ChangedCoords) CCFree(ChangedCoords);
02062             if (ChangedFlags) CCFree(ChangedFlags);
02063             return FALSE;
02064         }
02065 
02066         // Get pointers to all the arrays of the path
02067         PathVerb* Verbs = NULL;
02068         DocCoord* Coords = NULL;
02069         PathFlags* Flags = NULL;
02070         ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags);
02071 
02072         // Now copy the data from the path into the arrays
02073         for (INT32 i=0;i<NumElements;i++)
02074         {
02075             ChangedVerbs[i] = Verbs[Index+i];
02076             ChangedCoords[i] = Coords[Index+i];
02077             ChangedFlags[i] = Flags[Index+i];
02078         }
02079 
02080         // Now pass these arrays to the Insert action
02081         UnAction->RecordPath(ChangedVerbs, ChangedFlags, ChangedCoords, ThisPath);
02082     }
02083 
02084     // Now we've recorded the data we're about to delete, let's delete it
02085     ThisPath->InkPath.DeleteSection(Index, NumElements);
02086 
02087     if (RedrawPath)
02088     {
02089         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
02090         // Accumulate this invalid region in a pending list until the Op
02091         // finishes
02092         // Mark the region as requiring object to recache any cached
02093         // data they may be holding because the Op may have changed it
02094         pDocument->ForceRedraw(pSpread, Invalid, FALSE, ThisPath);
02095     }
02096 
02097 #endif
02098     return TRUE;
02099 }
02100 
02101 /********************************************************************************************
02102 
02103 >   BOOL UndoableOperation::DoAlterPathElement( NodePath* ThisPath, 
02104                                                 INT32 Index, 
02105                                                 DocCoord NewCoord,
02106                                                 PathFlags NewFlags,
02107                                                 PathVerb NewVerb);
02108 
02109 
02110     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
02111     Created:    27/7/94
02112     Inputs:     ThisPath points at the NodePath whose state we want to change
02113                 Index is the element we want to change
02114                 NewCoord is the new coordinate value
02115                 NewFlags is the new flags value
02116                 NewVerb is the new verb value
02117                 RedrawPath - TRUE if the screen area of the path should be invalidated, FALSE if not
02118     Outputs:    -
02119     Returns:    TRUE if succeeded, FALSE otherwise
02120     Purpose:    This 'Do' function changes the selected element, recording undo information
02121     Errors:     -
02122     SeeAlso:    -
02123 
02124 ********************************************************************************************/
02125 
02126 BOOL UndoableOperation::DoAlterPathElement( NodePath* ThisPath, 
02127                                             INT32 Index, 
02128                                             DocCoord NewCoord,
02129                                             PathFlags NewFlags,
02130                                             PathVerb NewVerb,
02131                                             BOOL RedrawPath)
02132 {
02133 #ifndef STANDALONE
02134 
02135     // Here we're changing an element, so we have to change the element in the path,
02136     // recording undo information at the same time.
02137     ModifyElementAction* ModAction = NULL;
02138     
02139     // Get pointers to all the arrays of the path
02140     PathVerb* Verbs = NULL;
02141     DocCoord* Coords = NULL;
02142     PathFlags* Flags = NULL;
02143     ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags);
02144 
02145     // Create an undo action for this action, which is a ModifyElementAction
02146     ActionCode Act;
02147     Act = ModifyElementAction::Init(this, 
02148                                     &UndoActions, 
02149                                     Verbs[Index],
02150                                     Flags[Index],
02151                                     Coords[Index],
02152                                     Index,
02153                                     ThisPath,
02154                                     (Action**)(&ModAction));
02155     if (Act == AC_FAIL)
02156         return FALSE;
02157 
02158     Document* pDocument = GetWorkingDoc();
02159     ERROR2IF(pDocument == NULL, FALSE, "There was no Document when deleteing path elements");
02160     Spread* pSpread = ThisPath->FindParentSpread();
02161     ERROR2IF(pSpread == NULL, FALSE, "Path had no parent spread");
02162 
02163     // Force a re-draw of the place where the path used to be
02164     if (RedrawPath)
02165     {
02166         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
02167         // Mark the region as requiring object to recache any cached
02168         // data they may be holding because the Op may have changed it
02169         pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath );
02170     }
02171 
02172     // Update the coords
02173     Verbs[Index] = NewVerb;
02174     Flags[Index] = NewFlags;
02175     Coords[Index] = NewCoord;
02176 
02177     // The bounding rect may have changed
02178     ThisPath->InvalidateBoundingRect();
02179 
02180     // redraw the new area
02181     if (RedrawPath)
02182     {
02183         // redraw the new area
02184         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
02185         // Mark the region as requiring object to recache any cached
02186         // data they may be holding because the Op may have changed it
02187         pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath );
02188     }
02189 
02190 #endif
02191     return TRUE;
02192 }
02193 
02194 
02195 /********************************************************************************************
02196 
02197 >   BOOL UndoableOperation::DoSmoothNodePath(NodePath* pThisNode, double smoothacc)
02198 
02199     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02200     Created:    16/11/94
02201     Inputs:     pThisNode - a pointer to a node to smooth.
02202                 smoothacc - the smooth accuracy, usually in the range (256^2, 8000^2)
02203     Outputs:    a new tree node will be created as a result
02204     Returns:    TRUE if the operation completed
02205                 FALSE if the operation failed due to lack of memory. The environment matches
02206                 that on entry to the function.
02207     Purpose:    This function will smooth a node path given an accuracy. It will create a
02208                 new node in the tree after the node specified and hide node passed if all 
02209                 is succesfull.
02210                 
02211 ********************************************************************************************/
02212 
02213 BOOL UndoableOperation::DoSmoothNodePath(NodePath* pThisNode, double smoothacc)
02214 {
02215 #ifndef STANDALONE
02216 
02217     // Copy the nodepath and all its children, without placing the copy in the tree
02218     Node* pnode;
02219     if (!pThisNode->NodeCopy(&pnode))
02220         return FALSE;
02221     NodePath* pSmoothNode = (NodePath*)pnode;
02222 
02223     // ok, smooth the nodepaths path data
02224     if (!pSmoothNode->InkPath.SmoothRegions(smoothacc, FALSE, TRUE))
02225         goto smoothfailed;
02226 
02227     // update the smoothed paths bounding rectangle
02228     pSmoothNode->InvalidateBoundingRect();
02229 
02230     // Now stick the new path into the tree
02231     if (!DoInsertNewNode(pSmoothNode, pThisNode, NEXT, TRUE))
02232         goto smoothfailed;
02233     
02234     // Now we've formed a smoothed path, let's hide the original
02235     if (!DoHideNode(pThisNode,TRUE))
02236         goto smoothfailed;
02237 
02238 #endif
02239 
02240     // return all is well
02241     return TRUE;
02242 
02243 #ifndef STANDALONE
02244 
02245 // failed to smooth the node so time to tidy up.
02246 smoothfailed:
02247     pSmoothNode->CascadeDelete();
02248     delete pSmoothNode;
02249     return FALSE;
02250 
02251 #endif
02252 }
02253 
02254 
02255 /********************************************************************************************
02256 
02257 >   BOOL UndoableOperation::DoMakeNodeFromPath( NodePath* pContextNode,
02258                                                 AttachNodeDirection Direction,
02259                                                 Path* pParentPath
02260                                                 BOOL CopyAttributes )
02261 
02262     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02263     Created:    22/11/94
02264     Inputs:     pContextNode    = pointer to a context node in the tree to attach our
02265                                   path to
02266                 direction       = direction on which to attach the new NodePath object
02267                                   created by this call.
02268                 pParentPath     = Path to place in the new NodePath object created
02269                 CopyAttributes  = if TRUE then take the attributes applied to the context
02270                                   node and apply them to the new node.       
02271     Outputs:    -
02272     Returns:    TRUE if the node was created successfully
02273                 FALSE if the node could not be created (out of memory)
02274     Purpose:    This functio will create a new nodepath and attach it in the specified
02275                 direction to the context node specified. It will also copy attributes
02276                 applied as children of the ConextNode to the new node if required.
02277 
02278 ********************************************************************************************/
02279 
02280 BOOL UndoableOperation::DoMakeNodeFromPath( NodePath* pParentNode,
02281                                             Path* pParentPath,
02282                                             AttachNodeDirection Direction,
02283                                             BOOL CopyAttributes)
02284 {
02285     // Create the node path object we will stick in the tree.
02286     NodePath* pChildNode;
02287 
02288     ALLOC_WITH_FAIL(pChildNode, new NodePath(), this);
02289 
02290     if (!pChildNode)
02291         return FALSE;
02292 
02293     BOOL ok;
02294     // make room for the new path in the node path.
02295     CALL_WITH_FAIL
02296     (
02297         pChildNode->SetUpPath(pParentPath->GetNumCoords(),12),
02298         this,ok
02299     );
02300 
02301     if (!ok)
02302     {
02303         delete pChildNode;
02304         return FALSE;
02305     }
02306 
02307     // now copy the path data in there.
02308     pChildNode->InkPath.CopyPathDataFrom(pParentPath);
02309 
02310     if (CopyAttributes)
02311     {
02312         CCAttrMap * AttribMap = NULL;
02313         AttribMap = CCAttrMap::MakeAppliedAttrMap(pParentNode);
02314 
02315         if (AttribMap)
02316         {
02317             AttribMap->ApplyAttributesToNode (pChildNode);
02318         }
02319     }
02320 
02321     // Now stick the new path into the tree
02322     CALL_WITH_FAIL
02323     (
02324         DoInsertNewNode(pChildNode, pParentNode, Direction, TRUE, FALSE),
02325         this,ok
02326     );
02327 
02328     // if we've failed tidy up
02329     if (!ok)
02330     {
02331         pChildNode->CascadeDelete();
02332         delete pChildNode;
02333         return FALSE;
02334     }
02335     return TRUE;
02336 }
02337 
02338 
02339 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
02340 // Do functions to localise/factor out attributes for attribute application
02341 
02342 // Range functions first
02343 
02344 /********************************************************************************************
02345 >   BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 
02346                                                     AttrTypeSet* pAffectedAttrTypes, 
02347                                                     BOOL ExcludeTextObjects = FALSE)
02348 
02349 
02350     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02351     Created:    17/5/95
02352 
02353     Inputs:     pRange: The range of objects which need their attributes localising
02354     
02355                 pAffectedAttrTypes: A set of the types of the attributes which are to be 
02356                                     applied to the Range. This function will localise 
02357                                     all attributes which have a type in this set. 
02358                                     
02359                                     All attriute types in this set must be localised on the 
02360                                     same compound node (See note below).
02361 
02362                 ExcludeTextObjects: When TRUE atttributes will not be localised on Text
02363                                     compounds. This is useful for selection deletions, where
02364                                     TextStory localisation will be left to the Story itself.
02365                                     This is a bit unpleasant.
02366 
02367     Outputs:    -
02368     Returns:    TRUE if successful, 
02369                 FALSE if we run out of memory. Tidyup then call End()
02370 
02371     Purpose:    This Do function must be called on a range prior to a set of attributes 
02372                 being applied to it. It globally localises those attributes which have
02373                 a type in the pAffectedAttrTypes set.
02374                 
02375                 After the attribute has been applied you should call DoFactorOutAfterAttrChange
02376                                                                 
02377                 If an object in the range Discards its attribute children then its attributes
02378                 are not localised.
02379 
02380                 Note: When applying attributes, we are not always going to apply the attributes
02381                       to the object itself, sometimes we need to apply them to the object's parent. 
02382                       Eg. when we apply a line based attribute to a text character the attributes
02383                       will get applied to the parent TextLine.
02384 
02385                       In this situation we do not localise the objects attributes, but its parents
02386                       attributes instead. 
02387                       
02388                       Warning
02389                       ~~~~~~~
02390 
02391                       All attribute types in the AttrTypeSet must be localised
02392                       on the same compound node. This means for example that the AttrType set cannot
02393                       contain a Bold and a Line space attribute. If we need to do this in future
02394                       then the routine will need to be changed.
02395 
02396     Errors:     -
02397     SeeAlso:    AttrTypeSet
02398     SeeAlso:    NodeAttribute::GetAttributeType
02399     SeeAlso:    UndoableOperation::DoFactorOutAfterAttrChange
02400     SeeAlso:    UndoableOperation::DoLocaliseForAttrChange
02401     SeeAlso:    Range::DoFactorOutAfterAttrChange
02402 
02403 ********************************************************************************************/
02404 
02405 
02406 BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 
02407                                                 AttrTypeSet* pAffectedAttrTypes, 
02408                                                 BOOL ExcludeTextObjects /* = FALSE */)
02409 {
02410     ERROR3IF(pRange->Count() == 0, "Range::DoLocaliseForAttrChange called on an empty range");  
02411 
02412     // Iterate over the top of any liveeffects applied to the selection
02413     // Its important that this iteration matches those used in DoApplyAttribute functions
02414     // so that attributes are put in the places where mutation will find them
02415     ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE);  // We DO own this range
02416     Node* pCurrent = pLevel->FindFirst();
02417 
02418     // There is no need to localise a compound more than once so we remember the last localised 
02419     NodeRenderableInk* pLocalisedCompound = NULL;
02420 
02421     CCRuntimeClass* AttrType;
02422     Node* pParent;
02423     Node* pObject;
02424                         
02425     while (pCurrent)
02426     {
02427         pObject = pCurrent;
02428         // Determine if the attribute will get applied to this object, or an alternative object
02429         // (probably its parent)
02430         if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty()))
02431         {
02432             AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType;
02433             ERROR3IF(!AttrType, "AttrType set contains NULL attribute type");  
02434             pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(AttrType);
02435         }
02436 
02437         if (pObject != NULL)
02438         {
02439 
02440             // We only need to localise those nodes which have a compound parent
02441             pParent = pObject->FindParent(); 
02442             ERROR3IF(pParent == NULL, "Range::DoLocaliseForAttrChange, node found without a parent"); 
02443         
02444             // Only localise the parent if it is a compound node which has not already been localised
02445             if ((pParent->IsCompound()) && (pParent != pLocalisedCompound))
02446             {
02447                 if (!(ExcludeTextObjects && ((IS_A(pParent, TextLine)) || (IS_A(pParent, TextStory)))) )
02448                 {
02449                     // Attempt to localise the compound
02450                     if (!DoLocaliseForAttrChange((NodeRenderableInk*)pObject, pAffectedAttrTypes, NULL))
02451                     {
02452                         delete pLevel;
02453                         return FALSE;
02454                     }
02455                  
02456                     pLocalisedCompound = (NodeRenderableInk*)pParent;  // Remember that it's been localised
02457                 }
02458             }
02459         }
02460         
02461         pCurrent = pLevel->FindNext(pCurrent);
02462     }
02463 
02464     delete pLevel;
02465     return TRUE;
02466 }
02467 
02468 /********************************************************************************************
02469 
02470 >   BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 
02471                                                     CCRuntimeClass* pAffectedAttrType)
02472 
02473 
02474     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02475     Created:    17/5/95
02476 
02477     Inputs:     pRange: The range of objects which need their attributes localising
02478 
02479     
02480                 pAffectedAttrType: The type of the attribute that you are about to apply
02481                                    to the range
02482 
02483     Outputs:    -
02484     Returns:    TRUE if successful, 
02485                 FALSE if we run out of memory. Tidyup then call End()
02486 
02487     Purpose:    This Do function must be called on a range prior to an attribute 
02488                 being applied to it. If you are going to apply multiple attributes to 
02489                 the range then it is more efficient to call the other version of this 
02490                 function which takes a set of attribute types. 
02491                 
02492                 The function globally localises those attributes which have a type in the 
02493                 pAffectedAttrTypes set.
02494 
02495                 If an object in the range Discards its attribute children then its attributes
02496                 are not localised.
02497                                 
02498                 Note: When applying an attribute, we are not always going to apply it
02499                 to the object itself, sometimes we need to apply it to the object's parent. 
02500                 Eg. when we apply a line based attribute to a text character the attributes
02501                 will get applied to the parent TextLine.
02502 
02503                 In this situation we do not localise the objects attributes, but its parents
02504                 attributes instead. 
02505               
02506 
02507     Errors:     -
02508     SeeAlso:    AttrTypeSet
02509     SeeAlso:    NodeAttribute::GetAttributeType
02510     SeeAlso:    UndoableOperation::DoFactorOutAfterAttrChange
02511     SeeAlso:    UndoableOperation::DoLocaliseForAttrChange
02512     SeeAlso:    Range::DoFactorOutAfterAttrChange
02513 
02514 ********************************************************************************************/
02515 
02516 
02517                              
02518 BOOL UndoableOperation::DoLocaliseForAttrChange(Range* pRange, 
02519                                                 CCRuntimeClass* pAffectedAttrType)
02520 {
02521     ERROR3IF(pRange->Count() == 0, "Range::DoLocaliseForAttrChange called on an empty range");  
02522 
02523     // Iterate over the top of any liveeffects applied to the selection
02524     // Its important that this iteration matches those used in DoApplyAttribute functions
02525     // so that attributes are put in the places where mutation will find them
02526     ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE);  // We DO own this range
02527     Node* pCurrent = pLevel->FindFirst();
02528 
02529     // There is no need to localise a compound more than once so we remember the last localised 
02530     NodeRenderableInk* pLocalisedCompound = NULL;
02531 
02532     Node* pParent;
02533     Node* pObject;
02534                         
02535     while (pCurrent)
02536     {
02537         pObject = pCurrent;
02538         // Determine if the attribute will get applied to this object, or an alternative object
02539         // (probably its parent)
02540         ERROR3IF(!pAffectedAttrType, "AttrType is NULL");  
02541         pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(pAffectedAttrType);
02542 
02543         {
02544 
02545             // We only need to localise those nodes which have a compound parent
02546             pParent = pObject->FindParent(); 
02547             ERROR3IF(pParent == NULL, "Range::DoLocaliseForAttrChange, node found without a parent"); 
02548         
02549             // Only localise the parent if it is a compound node which has not already been localised
02550             if ((pParent->IsCompound()) && (pParent != pLocalisedCompound))
02551             {
02552                 // Attempt to localise the compound
02553                 if (!DoLocaliseForAttrChange((NodeRenderableInk*)pObject, pAffectedAttrType, NULL))
02554                 {
02555                     delete pLevel;
02556                     return FALSE;
02557                 }
02558                  
02559                 pLocalisedCompound = (NodeRenderableInk*)pParent;  // Remember that it's been localised
02560             }
02561         }
02562         pCurrent = pLevel->FindNext(pCurrent);
02563     }
02564 
02565     delete pLevel;
02566     return TRUE;
02567 }
02568 
02569 /********************************************************************************************
02570 
02571 >   BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 
02572                                                        AttrTypeSet* pAffectedAttrTypes)
02573 
02574     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02575     Created:    18/5/95
02576 
02577     Inputs:     pRange: The objects that need their attributes factoring out
02578     
02579                 pAffectedAttrTypes: The types of the attributes that have just been
02580                                     applied to the range.
02581 
02582     Outputs:    -
02583     Returns:    TRUE if successful, 
02584                 FALSE if we run out of memory. Tidyup then call End()
02585 
02586     Purpose:    This Do function must be called on the range after attributes have
02587                 being applied to it. 
02588                 
02589                 The function globally factors out those attributes which have a type in the 
02590                 pAffectedAttrTypes set.
02591                 
02592                 Before calling the function you should have localised attributes by calling
02593                 DoLocaliseForAttrChange.
02594                 
02595                 If an object in the range Discards its attribute children then its attributes
02596                 are ignored when factoring (eg. the caret)
02597 
02598                 Note: When applying attributes, we are not always going to apply the attributes
02599                       to the object itself, sometimes we need to apply them to the object's parent. 
02600                       Eg. when we apply a line based attribute to a text character the attributes
02601                       will get applied to the parent TextLine.
02602 
02603                       In this situation we do not factor out the objects attributes, but its parents
02604                       attributes instead. 
02605                       
02606                       Warning
02607                       ~~~~~~~
02608 
02609                       All attribute types in the AttrTypeSet must have been localised 
02610                       (and so need factoring out) on the same compound node. This means for example 
02611                       that the AttrType set cannot contain a Bold and a Line space attribute. If 
02612                       we need to do this in future then the routine will need to be changed.
02613 
02614  
02615 
02616     SeeAlso:    AttrTypeSet
02617     SeeAlso:    NodeAttribute::GetAttributeType
02618     SeeAlso:    UndoableOperation::DoFactorOutAfterAttrChange
02619     SeeAlso:    Range::DoLocaliseForAttrChange
02620 
02621 
02622 ********************************************************************************************/
02623 
02624 
02625 BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 
02626                                                    AttrTypeSet* pAffectedAttrTypes)
02627 {
02628     ERROR3IF(pRange->Count() == 0, "Range::DoFactorOutAfterAttrChange called on an empty range");  
02629 
02630     // Iterate over the top of any liveeffects applied to the selection
02631     // Its important that this iteration matches those used in DoApplyAttribute functions
02632     // so that attributes are put in the places where mutation will find them
02633     ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE);  // We DO own this range
02634     Node* pCurrent = pLevel->FindFirst();
02635 
02636     // There is no need to factor out the attributes on a compound more than once, so we 
02637     // remember the last compound node which has had its attributes factored out 
02638     NodeRenderableInk* pFactoredOutCompound = NULL;
02639 
02640     Node* pParent;
02641     Node* pObject;
02642                         
02643     while (pCurrent)
02644     {
02645         // If the object can discard its attribute children then we should not try to factor
02646         // out its attributes
02647         CCRuntimeClass* AttrType;
02648         
02649         pObject = pCurrent;
02650         if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty()))
02651         {
02652             AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType;
02653             ERROR3IF(!AttrType, "AttrType set contains NULL attribute type");  
02654             pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(AttrType);
02655         }
02656 
02657         // DY added test for NULL, which can occur with bevels
02658         if ((pObject != NULL))
02659         {
02660             // We only need to factor out attributes on nodes which have a compound parent
02661             pParent = pObject->FindParent(); 
02662             ERROR3IF(pParent == NULL, "Range::DoFactorOutAfterAttrChange, node found without a parent"); 
02663         
02664             // Only factor out attribs if  the parent has not already had its attribs factored out
02665             if ((pParent->IsCompound()) && (pParent != pFactoredOutCompound))
02666             {
02667                 // Attempt to localise the compound
02668                 if (!DoFactorOutAfterAttrChange((NodeRenderableInk*)pObject, pAffectedAttrTypes))
02669                 {
02670                     delete pLevel;
02671                     return FALSE;
02672                 }
02673                  
02674                 pFactoredOutCompound = (NodeRenderableInk*)pParent;  // Remember that it's been localised
02675             }
02676         }
02677         
02678         pCurrent = pLevel->FindNext(pCurrent);
02679     }
02680 
02681     delete pLevel;
02682     return TRUE;
02683 }
02684 
02685 /********************************************************************************************
02686 
02687 >   BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 
02688                                                        CCRuntimeClass* pAffectedAttrType)
02689 
02690     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02691     Created:    18/5/95
02692 
02693     Inputs:     pRange: The objects that need their attributes factoring out
02694 
02695                 pAffectedAttrType: The type of the attribute that have just been
02696                                     applied to the range.
02697 
02698     Outputs:    -
02699     Returns:    TRUE if successful, 
02700                 FALSE if we run out of memory. Tidyup then call End()
02701 
02702     Purpose:    This Do function must be called on the range after an attribute 
02703                 being applied to it. If you have applied multiple attributes to 
02704                 the range then it is more efficient to call the other version of this 
02705                 function which takes a set of attribute types. 
02706                 
02707                 The function globally factors out the attribute which has type pAffectedAttrType
02708 
02709                 Before calling the function you should have localised the attribute by calling
02710                 DoLocaliseForAttrChange.
02711 
02712                 If an object in the range Discards its attribute children then its attributes
02713                 are ignored when factoring (eg. the caret)
02714 
02715                 Note: When applying an attribute, we are not always going to apply it
02716                 to the object itself, sometimes we need to apply it to the object's parent. 
02717                 Eg. when we apply a line based attribute to a text character the attributes
02718                 will get applied to the parent TextLine.
02719 
02720                 In this situation we do not factor out the objects attributes, but its parents
02721                 attributes instead.
02722                 
02723                 Note: When applying attributes, we are not always going to apply the attributes
02724                       to the object itself, sometimes we need to apply them to the object's parent. 
02725                       Eg. when we apply a line based attribute to a text character the attributes
02726                       will get applied to the parent TextLine.
02727 
02728                       In this situation we do not factor out the object's attributes, but its parents
02729                       attributes instead. 
02730                                         
02731     SeeAlso:    AttrTypeSet
02732     SeeAlso:    NodeAttribute::GetAttributeType
02733     SeeAlso:    UndoableOperation::DoLocaliseForAttrChange
02734     SeeAlso:    Range::DoLocaliseForAttrChange
02735     SeeAlso:    Range::DoFactorOutAfterAttrChange
02736 
02737 
02738 ********************************************************************************************/
02739 
02740 BOOL UndoableOperation::DoFactorOutAfterAttrChange(Range* pRange, 
02741                                                    CCRuntimeClass* pAffectedAttrType)
02742 {
02743     ERROR3IF(pRange->Count() == 0, "Range::DoFactorOutAfterAttrChange called on an empty range");  
02744     // Scan the range
02745 
02746     // Iterate over the top of any liveeffects applied to the selection
02747     // Its important that this iteration matches those used in DoApplyAttribute functions
02748     // so that attributes are put in the places where mutation will find them
02749     ListRange* pLevel = EffectsStack::GetNewLevelRange(pRange, FALSE);  // We DO own this range
02750     Node* pCurrent = pLevel->FindFirst();
02751 
02752     // There is no need to factor out the attributes on a compound more than once, so we 
02753     // remember the last compound node which has had its attributes factored out 
02754     NodeRenderableInk* pFactoredOutCompound = NULL;
02755 
02756     Node* pParent;
02757     Node* pObject;                  
02758     while (pCurrent)
02759     {
02760         pObject = pCurrent;
02761         // Get the object that the attribute has been applied to
02762         ERROR3IF(!pAffectedAttrType, "AttrType is NULL");  
02763         pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(pAffectedAttrType);
02764 
02765         // If the object can discard its attribute children then we should not try to factor
02766         // out its attributes
02767 
02768         {
02769 
02770             // We only need to factor out attributes on nodes which have a compound parent
02771             pParent = pObject->FindParent(); 
02772             ERROR3IF(pParent == NULL, "Range::DoFactorOutAfterAttrChange, node found without a parent"); 
02773         
02774             // Only factor out attribs if  the parent has not already had its attribs factored out
02775             if ((pParent->IsCompound()) && (pParent != pFactoredOutCompound))
02776             {
02777                 // Attempt to localise the compound
02778                 if (!DoFactorOutAfterAttrChange((NodeRenderableInk*)pObject, pAffectedAttrType))
02779                 {
02780                     delete pLevel;
02781                     return FALSE;
02782                 }
02783                  
02784                 pFactoredOutCompound = (NodeRenderableInk*)pParent;  // Remember that it's been localised
02785             }
02786         }
02787         pCurrent = pLevel->FindNext(pCurrent);
02788     }
02789 
02790     delete pLevel;
02791     return TRUE;
02792 }
02793 
02794 
02795 
02796 /********************************************************************************************
02797 
02798 >   BOOL UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 
02799                                                     AttrTypeSet* pAffectedAttrTypes, 
02800                                                     ObjectSet* pLocalisedCompounds)
02801 
02802 
02803     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02804     Created:    17/5/95
02805     Inputs:     Object: The object that you are about to apply attributes to
02806                 
02807                 pAffectedAttrTypes: A set of the types of the attributes which are to be 
02808                                     applied to the Object. This function will localise 
02809                                     all attributes which have a type in this set.
02810 
02811                 pLocalisedCompounds: An optional set of compounds which have already had
02812                                      all attributes in the pAffectedAttrTypes localised. 
02813                                      If Object has a parent compound which 
02814                                      is in this set then its attributes will not need to
02815                                      be localised again. 
02816     Outputs:    -
02817     Returns:    TRUE if successful, 
02818                 FALSE if we run out of memory. Tidyup then call End()
02819 
02820     Purpose:    This Do function must be called on an object prior to a set of attributes 
02821                 being applied to it. It globally localises those attributes which have
02822                 a type in the pAffectedAttrTypes set.
02823                 
02824                 If the function is called on a  object which does not have a compound parent 
02825                 then there will be no attributes to localise so the function will simply
02826                 return TRUE without having done anything.
02827 
02828                 After a compound object has localised its children it will be added to the 
02829                 pLocalisedCompounds set if this has been provided. 
02830 
02831                 After the attribute has been applied you should call DoFactorOutAfterAttrChange
02832 
02833                 If the object discards its attribute children then the routine does nothing 
02834                 (eg. the caret)
02835 
02836                 Note: When applying attributes, we are not always going to apply the attributes
02837                 to the object itself, sometimes we need to apply them to the object's parent. 
02838                 Eg. when we apply a line based attribute to a text character the attributes
02839                 will get applied to the parent TextLine.
02840 
02841                 In this situation we do not localise the objects attributes, but its parents
02842                 attributes instead. 
02843               
02844                 Warning
02845                 ~~~~~~~
02846 
02847                 All attribute types in the AttrTypeSet must be localised
02848                 on the same compound node. This means for example that the AttrType set cannot
02849                 contain a Bold and a Line space attribute. If we need to do this in future
02850                 then the routine will need to be changed.
02851 
02852 
02853 
02854     Errors:     -
02855     SeeAlso:    AttrTypeSet
02856     SeeAlso:    NodeAttribute::GetAttributeType
02857     SeeAlso:    UndoableOperation::DoFactorOutAfterAttrChange
02858     SeeAlso:    Range::DoLocaliseForAttrChange
02859     SeeAlso:    Range::DoFactorOutAfterAttrChange
02860 
02861 ********************************************************************************************/
02862 
02863 // This function must get called on a compound node before a new attribute is attached to it
02864 BOOL UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 
02865                                                 AttrTypeSet* pAffectedAttrTypes, 
02866                                                 ObjectSet* pLocalisedCompounds)
02867 {
02868     CCRuntimeClass* AttrType;
02869     // Determine if the attribute will get applied to this object, or an alternative object
02870     // (probably its parent)
02871     if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty()))
02872     {
02873         AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType;
02874         ERROR3IF(!AttrType, "AttrType set contains NULL attribute type");  
02875         Object = Object->GetObjectToApplyTo(AttrType);
02876     }
02877 
02878     {   
02879 
02880 
02881         // If the object has not got a compound parent then there will be no attributes 
02882         // to localise, so we need not do anything
02883     
02884         Node* pParent;
02885         pParent = Object->FindParent(); 
02886         ERROR3IF(!pParent, "UndoableOperation::DoLocaliseForAttrChange called on an object which has no parent"); 
02887 
02888         if (pParent->IsCompound())
02889         {
02890             // Before we attempt to localise the compound's attributes let's check that they are not 
02891             // already localised.
02892             
02893             if (pLocalisedCompounds)
02894             {
02895                 if (pLocalisedCompounds->InSet((NodeRenderableInk*)pParent))
02896                 {
02897                     return TRUE; // Attributes are already localised so there is no need to localise again !
02898                 }
02899                 else
02900                 {
02901                     pLocalisedCompounds->AddToSet((NodeRenderableInk*)pParent); // Record that the compound has been localised
02902                 }
02903             }
02904 
02905             return (DoLocaliseCommonAttributes((NodeRenderableInk*)pParent,  // We are localising the compound's attrs
02906                                                FALSE,    // No need to check for duplicates
02907                                                TRUE,     // Localise globally
02908                                                pAffectedAttrTypes)); // Only attributes of these types
02909         }
02910         
02911     }
02912     return TRUE; 
02913 }
02914 
02915 /********************************************************************************************
02916 
02917 >   BOOL  UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 
02918                                                      CCRuntimeClass* pAffectedAttrType, 
02919                                                      ObjectSet* pLocalisedCompounds)
02920 
02921 
02922     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02923     Created:    18/5/95
02924     Inputs:     Object: The object that you are about to apply the attribute to
02925                 
02926                 pAffectedAttrType: The type of the attribute that you are about to apply
02927                                    to the object
02928                                                                 
02929                 pLocalisedCompounds: An optional set of compounds which have already had
02930                                      their  pAffectedAttrType localised. 
02931                                      If Object has a parent compound which 
02932                                      is in this set then its attributes will not need to
02933                                      be localised again. 
02934 
02935 
02936     Outputs:    -
02937     Returns:    TRUE if successful, 
02938                 FALSE if we run out of memory. Tidyup then call End()
02939 
02940     Purpose:    This Do function must be called on an object prior to an attribute 
02941                 being applied to it. If you are going to apply multiple attributes to 
02942                 the Object then it is more efficient to call the other version of this 
02943                 function which takes a set of attribute types. 
02944                 
02945                 The function globally localises those attributes which have a type in the 
02946                 pAffectedAttrTypes set.
02947                 
02948                 If the function is called on a  object which does not have a compound parent 
02949                 then there will be no attributes to localise so the function will simply
02950                 return TRUE without having done anything.
02951 
02952                 If the object discards its attribute children then the routine does nothing 
02953                 (eg. the caret)
02954 
02955                 Note: When applying attributes, we are not always going to apply the attributes
02956                 to the object itself, sometimes we need to apply them to the object's parent. 
02957                 Eg. when we apply a line based attribute to a text character the attributes
02958                 will get applied to the parent TextLine.
02959 
02960                 In this situation we do not localise the objects attributes, but its parents
02961                 attributes instead. 
02962 
02963 
02964     SeeAlso:    AttrTypeSet
02965     SeeAlso:    NodeAttribute::GetAttributeType
02966     SeeAlso:    UndoableOperation::DoFactorOutAfterAttrChange
02967     SeeAlso:    Range::DoLocaliseForAttrChange
02968     SeeAlso:    Range::DoFactorOutAfterAttrChange
02969 
02970 
02971 ********************************************************************************************/
02972                              
02973 BOOL  UndoableOperation::DoLocaliseForAttrChange(NodeRenderableInk* Object, 
02974                                                  CCRuntimeClass* pAffectedAttrType,
02975                                                  ObjectSet* pLocalisedCompounds)
02976 {
02977     BOOL ok = TRUE;
02978     // Determine if the attribute will get applied to this object, or an alternative object
02979     // (probably its parent)
02980     ERROR3IF(!pAffectedAttrType, "AttrType is NULL");  
02981     Object = Object->GetObjectToApplyTo(pAffectedAttrType);
02982 
02983     {   
02984 
02985         Node* pParent;
02986         pParent = Object->FindParent(); 
02987         ERROR3IF(!pParent, "UndoableOperation::DoLocaliseForAttrChange called on an object which has no parent"); 
02988 
02989         if (pParent->IsCompound())
02990         {
02991     
02992             // We an attribute type set
02993             AttrTypeSet Set; 
02994         
02995             // Add the attributes type to the set
02996             if (!Set.AddToSet(pAffectedAttrType))
02997             {
02998                 ok = FALSE; 
02999             }
03000 
03001             if (ok)
03002             {
03003                 // Before we attempt to localise the compound's attributes let's check that they are not 
03004                 // already localised.
03005             
03006                 if (pLocalisedCompounds)
03007                 {
03008                     if (pLocalisedCompounds->InSet((NodeRenderableInk*)pParent))
03009                     {
03010                         return TRUE; // Attributes are already localised so there is no need to localise again !
03011                     }
03012                     else
03013                     {
03014                         pLocalisedCompounds->AddToSet((NodeRenderableInk*)pParent); // Record that the compound has been localised
03015                     }
03016                 }
03017 
03018 
03019                 ok = DoLocaliseCommonAttributes((NodeRenderableInk*)pParent,// We are localising the compound's attrs
03020                                                    FALSE,                   // No need to check for duplicates
03021                                                    TRUE,                    // Localise globally
03022                                                    &Set);                   // Only attributes of these types
03023             }
03024     
03025             // The attribute type set is no longer required
03026             Set.DeleteAll(); 
03027         }
03028     }
03029     return ok; 
03030 }
03031 
03032 
03033 /********************************************************************************************
03034 
03035 >   BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object,
03036                                                        AttrTypeSet* pAffectedAttrTypes)
03037 
03038     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03039     Created:    18/5/95
03040     Inputs:     Object: The object that you have just applied attributes to
03041                 
03042                 pAffectedAttrTypes: The types of the attributes that have just been
03043                                     applied to the object.
03044 
03045     Outputs:    -
03046     Returns:    TRUE if successful, 
03047                 FALSE if we run out of memory. Tidyup then call End()
03048 
03049     Purpose:    This Do function must be called on an object after attributes have
03050                 being applied to it. 
03051                 
03052                 The function globally factors out those attributes which have a type in the 
03053                 pAffectedAttrTypes set.
03054                 
03055                 If the function is called on a  object which does not have a compound parent 
03056                 then there will be no attributes to factor out so the function will simply
03057                 return TRUE without having done anything.
03058 
03059                 Before calling the function you should have localised attributes by calling
03060                 DoLocaliseForAttrChange. 
03061 
03062                 If the object discards its attribute children then the routine does nothing 
03063                 (eg. the caret)
03064 
03065                 Note: When applying attributes, we are not always going to apply the attributes
03066                       to the object itself, sometimes we need to apply them to the object's parent. 
03067                       Eg. when we apply a line based attribute to a text character the attributes
03068                       will get applied to the parent TextLine.
03069 
03070                       In this situation we do not factor out the objects attributes, but its parents
03071                       attributes instead. 
03072                       
03073                       Warning
03074                       ~~~~~~~
03075 
03076                       All attribute types in the AttrTypeSet must have been localised 
03077                       (and so need factoring out) on the same compound node. This means for example 
03078                       that the AttrType set cannot contain a Bold and a Line space attribute. If 
03079                       we need to do this in future then the routine will need to be changed.
03080 
03081 
03082     SeeAlso:    AttrTypeSet
03083     SeeAlso:    NodeAttribute::GetAttributeType
03084     SeeAlso:    UndoableOperation::DoLocaliseForAttrChange
03085     SeeAlso:    Range::DoLocaliseForAttrChange
03086     SeeAlso:    Range::DoFactorOutAfterAttrChange
03087 
03088 
03089 ********************************************************************************************/
03090 
03091 BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object,
03092                                                    AttrTypeSet* pAffectedAttrTypes)
03093 {
03094     CCRuntimeClass* AttrType;
03095 
03096     // Get object which attrib was applied to
03097     if (pAffectedAttrTypes && (!pAffectedAttrTypes->IsEmpty()))
03098     {
03099         AttrType = ((AttrTypeItem*)pAffectedAttrTypes->GetHead())->AttributeType;
03100         ERROR3IF(!AttrType, "AttrType set contains NULL attribute type");  
03101         Object = Object->GetObjectToApplyTo(AttrType);
03102     }
03103 
03104     {   
03105         Node* pParent;
03106         pParent = Object->FindParent(); 
03107         ERROR3IF(!pParent, "UndoableOperation::DoFactorOutAfterAttrChange called on an object which has no parent"); 
03108 
03109         if (pParent->IsCompound())
03110         {
03111             return (DoFactorOutCommonChildAttributes((NodeRenderableInk*)pParent, 
03112                                                      TRUE,     // Global
03113                                                      pAffectedAttrTypes));
03114         }
03115     }
03116     return TRUE;
03117 }
03118 
03119 
03120 /********************************************************************************************
03121 
03122 >   BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object,
03123                                                        CCRuntimeClass* pAffectedAttrType)
03124 
03125     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03126     Created:    18/5/95
03127     Inputs:     Object: The object that you have just applied attributes to
03128                 
03129                 pAffectedAttrType: The types of the attribute that have just been
03130                                     applied to the object.
03131 
03132     Outputs:    -
03133     Returns:    TRUE if successful, 
03134                 FALSE if we run out of memory. Tidyup then call End()
03135 
03136     Purpose:    This Do function must be called on an object after an attribute 
03137                 being applied to it. If you have applied multiple attributes to 
03138                 the Object then it is more efficient to call the other version of this 
03139                 function which takes a set of attribute types. 
03140                 
03141                 The function globally factors out the attribute with type pAffectedAttrType
03142                 
03143                 If the function is called on a  object which does not have a compound parent 
03144                 then there will be no attributes to factor out so the function will simply
03145                 return TRUE without having done anything.
03146 
03147                 Before calling the function you should have localised attributes by calling
03148                 DoLocaliseForAttrChange. 
03149 
03150                 If the object discards its attribute children then the routine does nothing 
03151                 (eg. the caret)
03152 
03153                 Note: When applying attributes, we are not always going to apply the attributes
03154                       to the object itself, sometimes we need to apply them to the object's parent. 
03155                       Eg. when we apply a line based attribute to a text character the attributes
03156                       will get applied to the parent TextLine.
03157 
03158                       In this situation we do not factor out the objects attributes, but its parents
03159                       attributes instead. 
03160 
03161     SeeAlso:    AttrTypeSet
03162     SeeAlso:    NodeAttribute::GetAttributeType
03163     SeeAlso:    UndoableOperation::DoLocaliseForAttrChange
03164     SeeAlso:    Range::DoLocaliseForAttrChange
03165     SeeAlso:    Range::DoFactorOutAfterAttrChange
03166 
03167 
03168 ********************************************************************************************/
03169 
03170 BOOL UndoableOperation::DoFactorOutAfterAttrChange(NodeRenderableInk* Object,
03171                                                    CCRuntimeClass* pAffectedAttrType)
03172 {
03173     BOOL ok = TRUE;
03174 
03175     // Get object attr was applied to
03176     ERROR3IF(!pAffectedAttrType, "AttrType is NULL");  
03177     Object = Object->GetObjectToApplyTo(pAffectedAttrType);
03178 
03179     {   
03180 
03181         Node* pParent;
03182         pParent = Object->FindParent(); 
03183         ERROR3IF(!pParent, "UndoableOperation::DoFactorOutAfterAttrChange called on an object which has no parent"); 
03184 
03185         if (pParent->IsCompound())
03186         {
03187     
03188             // We need an attribute type set
03189             AttrTypeSet Set; 
03190 
03191             // Add the attributes type to the set
03192             if (!(Set.AddToSet(pAffectedAttrType)))
03193             {
03194                 ok = FALSE; 
03195             }
03196 
03197             if (ok)
03198             {
03199 
03200                 return (DoFactorOutCommonChildAttributes((NodeRenderableInk*)pParent, 
03201                                                          TRUE,     // Global
03202                                                          &Set));
03203             }
03204     
03205             Set.DeleteAll(); 
03206         }
03207     }
03208     return ok; 
03209 }
03210 
03211 /********************************************************************************************
03212 
03213 >   BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds,
03214                                                        AttrTypeSet* pAffectedAttrTypes)
03215 
03216     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03217     Created:    18/5/95
03218 
03219     Inputs:     pLocalisedCompounds: A set of compound nodes which have localised their
03220                                      pAffectedAttrTypes attributes
03221 
03222                 pAffectedAttrTypes: The types of the attributes that have been
03223                                     applied to the range.
03224 
03225     Outputs:    -
03226     Returns:    TRUE if successful, 
03227                 FALSE if we run out of memory. Tidyup then call End()
03228 
03229     Purpose:    This Do function is designed to be called after all attributes in the 
03230                 pAffectedAttrTypes set  have been localised on all compound nodes in 
03231                 the pLocalisedCompounds set.    
03232                 
03233                 The function globally factors out the attributes which have a type which is
03234                 in the pAffectedAttrTypes set.
03235 
03236                 Before calling the function you should have localised the attribute by calling
03237                 DoLocaliseForAttrChange.
03238 
03239 
03240 
03241     SeeAlso:    AttrTypeSet
03242     SeeAlso:    NodeAttribute::GetAttributeType
03243     SeeAlso:    UndoableOperation::DoLocaliseForAttrChange
03244 
03245 
03246 ********************************************************************************************/
03247 
03248 BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds, 
03249                                                    AttrTypeSet* pAffectedAttrTypes)
03250 {
03251     ERROR3IF(!pLocalisedCompounds, "DoFactorOutAfterAttrChange called with a NULL compound set");  
03252     //ERROR3IF(!pAffectedAttrTypes, "DoFactorOutAfterAttrChange called with a NULL attr type set"); 
03253 
03254     // Scan the range
03255     ObjectItem* pObjItem = (ObjectItem*)(pLocalisedCompounds->GetHead());
03256     NodeRenderableInk* pCurrent;
03257 
03258     while (pObjItem)
03259     {
03260         pCurrent = pObjItem->pObject;
03261         ERROR3IF(!(pCurrent->IsCompound()), "Set should only contain compound objects");
03262         if (pCurrent->IsCompound())
03263         {   
03264             if (!DoFactorOutCommonChildAttributes((NodeRenderableInk*)pCurrent, 
03265                                                      TRUE,     // Global
03266                                                      pAffectedAttrTypes))
03267             {
03268                 return FALSE; 
03269             }
03270         }
03271         pObjItem = (ObjectItem*)pLocalisedCompounds->GetNext(pObjItem);
03272     }
03273 
03274     return TRUE; 
03275 
03276 }
03277 
03278 
03279 /********************************************************************************************
03280 
03281 >   BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds,
03282                                                        CCRuntimeClass* pAffectedAttrType)
03283 
03284     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03285     Created:    18/5/95
03286 
03287     Inputs:     pLocalisedCompounds: A set of compound nodes which have localised their
03288                                      pAffectedAttrType attributes.
03289 
03290                 pAffectedAttrType: The type of the attribute that have just been
03291                                     applied to the range.
03292 
03293     Outputs:    -
03294     Returns:    TRUE if successful, 
03295                 FALSE if we run out of memory. Tidyup then call End()
03296 
03297     Purpose:    This Do function is designed to be called after pAffectedAttrType has been 
03298                 localised on all compound nodes in the pLocalisedCompounds set. 
03299                 
03300                 The function globally factors out the attribute which has type pAffectedAttrType
03301 
03302                 Before calling the function you should have localised the attribute by calling
03303                 DoLocaliseForAttrChange.
03304                       
03305 
03306 
03307     SeeAlso:    AttrTypeSet
03308     SeeAlso:    NodeAttribute::GetAttributeType
03309     SeeAlso:    UndoableOperation::DoLocaliseForAttrChange
03310 
03311 
03312 ********************************************************************************************/
03313 
03314 
03315 
03316 BOOL UndoableOperation::DoFactorOutAfterAttrChange(ObjectSet* pLocalisedCompounds,
03317                                                   CCRuntimeClass* pAffectedAttrType)
03318 {
03319     ERROR3IF(!pLocalisedCompounds, "DoFactorOutAfterAttrChange called with a NULL compound set");  
03320     ERROR3IF(!pAffectedAttrType, "DoFactorOutAfterAttrChange called with a NULL attr type"); 
03321 
03322 
03323     // Scan the range
03324     ObjectItem* pObjItem = (ObjectItem*)(pLocalisedCompounds->GetHead());
03325     NodeRenderableInk* pCurrent;
03326 
03327     // We need an attribute type set
03328     AttrTypeSet Set; 
03329 
03330     // Add the attributes type to the set
03331     if (!(Set.AddToSet(pAffectedAttrType)))
03332     {
03333         return FALSE; 
03334     }
03335 
03336     while (pObjItem)
03337     {
03338         pCurrent = pObjItem->pObject;
03339         ERROR3IF(!(pCurrent->IsCompound()), "Set should only contain compound objects");
03340         if (pCurrent->IsCompound())
03341         {   
03342             if (!DoFactorOutCommonChildAttributes((NodeRenderableInk*)pCurrent, 
03343                                                      TRUE,     // Global
03344                                                      &Set))
03345             {
03346                 Set.DeleteAll();
03347                 return FALSE; 
03348             }
03349         }
03350         pObjItem = (ObjectItem*)pLocalisedCompounds->GetNext(pObjItem);
03351     }
03352 
03353     Set.DeleteAll(); 
03354     return TRUE; 
03355 }
03356 
03357 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
03358 // These functions are for attribute optimisation when deleting objects in a range
03359 
03360 // They are now defunct. They were a nice idea but they could not cope with attribute children
03361 // of empty groups !
03362 
03363 
03364 /********************************************************************************************
03365 
03366 >   BOOL UndoableOperation::GetPreDelFactorOutInfo(Range* pRange, List* pPreDelFactorOutInfoList)
03367 
03368     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03369     Created:    31/5/95
03370 
03371     Inputs:     pRange: The range of objects that will be deleted
03372     
03373     Outputs:    pPreDelFactorOutInfoList: Information to be used by the DoFactorOutAfterDeletion
03374                                           function.
03375 
03376     Returns:    TRUE if succesful
03377                 FALSE if we ran out of memory. In this situation all items on the 
03378                 pPreDelFactorOutInfoList are deleted. 
03379 
03380     Purpose:    This function is an information gatherer for the DoFactorOutAfterDeletion
03381                 function. See this function for a full description of its purpose. 
03382     Errors:     -
03383     SeeAlso:    UndoableOperation::DoFactorOutAfterDeletion
03384 
03385 ********************************************************************************************/
03386 /*
03387 BOOL UndoableOperation::GetPreDelFactorOutInfo(Range* pRange, List* pPreDelFactorOutInfoList)
03388 {
03389     // On entry to this function the PreDelFactorOutInfoList should be empty
03390     ERROR3IF(!(pPreDelFactorOutInfoList->IsEmpty()), "PreDelFactorOutInfoList is not empty"); 
03391     // Lets make sure
03392     pPreDelFactorOutInfoList->DeleteAll();
03393     
03394     NodeRenderableInk* pCurrentCompound; // A pointer to the compound parent of the objects we are
03395                                          // currently processing 
03396     
03397     pCurrentCompound = NULL;
03398     PreDelFactorOutInfoItem* pCurrentCompoundInfo = NULL;
03399      
03400     // Scan the range
03401     Node* pCurrent = pRange->FindFirst();
03402     Node* pCurrentsParent = NULL;
03403     while (pCurrent)
03404     {
03405         pCurrentsParent = pCurrent->FindParent();
03406         ERROR3IF(pCurrentsParent == NULL, "Current has a NULL parent"); 
03407         if (pCurrentsParent->IsCompound())
03408         {
03409             if (pCurrentsParent != pCurrentCompound)
03410             {
03411                 pCurrentCompound = (NodeRenderableInk*)pCurrentsParent; 
03412 
03413                 // We are processing a new Compound node. Create a new PreDelFactorOutInfoItem
03414                 // so that we can record those attributes that must be factored out in it. 
03415 
03416                 // Check if the last compound node we processed needs any attrs factoring out. 
03417                 // if it doesn't then we can safely delete its entry (every little helps)
03418                 if (pCurrentCompoundInfo && pCurrentCompoundInfo->AttrTypesToFactorOut.IsEmpty())
03419                 {
03420                     // bye-bye
03421                     pPreDelFactorOutInfoList->RemoveItem(pCurrentCompoundInfo);
03422                     delete pCurrentCompoundInfo; 
03423                 }
03424 
03425                 pCurrentCompoundInfo = new PreDelFactorOutInfoItem(); 
03426                 if (!pCurrentCompoundInfo)
03427                     goto OutOfMemory;
03428 
03429                 pCurrentCompoundInfo->pCompound = pCurrentCompound;
03430 
03431                 // Add the item to the list
03432                 pPreDelFactorOutInfoList->AddHead(pCurrentCompoundInfo);
03433             }
03434             ((NodeRenderableInk*)pCurrent)->AddChildAttrTypesToSet(&(pCurrentCompoundInfo->AttrTypesToFactorOut)); 
03435         }
03436 
03437         // Get next object to process
03438         pCurrent = pRange->FindNext(pCurrent); 
03439     }
03440     
03441     return TRUE; // success
03442 
03443     OutOfMemory:
03444     // Delete all items on the pPreDelFactorOutInfoList
03445     pCurrentCompoundInfo = (PreDelFactorOutInfoItem*)(pPreDelFactorOutInfoList->GetHead());
03446     while (pCurrentCompoundInfo)
03447     {
03448         pCurrentCompoundInfo->AttrTypesToFactorOut.DeleteAll();
03449         pCurrentCompoundInfo = (PreDelFactorOutInfoItem*)
03450             (pPreDelFactorOutInfoList->GetNext(pCurrentCompoundInfo));
03451     }
03452     pPreDelFactorOutInfoList->DeleteAll(); 
03453     return FALSE; 
03454         
03455 } 
03456 */
03457 /********************************************************************************************
03458 
03459 >   BOOL UndoableOperation::DoFactorOutAfterDeletion(List* pPreDelFactorOutInfoList)
03460 
03461     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03462     Created:    31/5/95
03463     Inputs:     pPreDelFactorOutInfoList: given to you by the GetPreDelFactorOutInfo function
03464 
03465                                           All items on this list are helpfully deleted after
03466                                           this function returns.
03467     Outputs:    -
03468     Returns:    TRUE if successful, 
03469                 FALSE if we run out of memory. Tidyup then call End()
03470 
03471     Purpose:    Whenever we delete a range of objects, if any objects in the range have compound
03472                 parents then we must adjust the common attributes on these compounds. 
03473 
03474                 For example if a compound has three red objects, and two yellow objects. If
03475                 we delete the yellow objects, then red should become a common attribute of the
03476                 compound.
03477 
03478                 We could acheive this feat by firstly localising all attributes on the range, hiding
03479                 the range, and then factoring out after. However this will result in unecessary
03480                 attributes dangling off hidden nodes, so we try to be a bit cleverer than this.
03481 
03482                 Prior to deleting the range we call GetPreDelFactorOutInfo. This function returns
03483                 us a list containing information about which attributes need to be factored out 
03484                 on which compounds. 
03485 
03486                 After deleting the range this function should be called to  factor 
03487                 out the neccessary attributes.
03488 
03489     Errors:     -
03490     SeeAlso:    UndoableOperation::GetPreDelFactorOutInfo
03491 
03492 ********************************************************************************************/
03493             
03494 // This function should get called after a range of objects have been deleted ('Hidden')
03495 /*
03496 BOOL UndoableOperation::DoFactorOutAfterDeletion(List* pPreDelFactorOutInfoList)
03497 {
03498     PreDelFactorOutInfoItem* pCurrentCompoundInfo = 
03499         (PreDelFactorOutInfoItem*)(pPreDelFactorOutInfoList->GetHead());
03500 
03501     NodeRenderableInk* pCompound;
03502 
03503     BOOL ok = TRUE;
03504 
03505     // Process each record
03506     while (ok && pCurrentCompoundInfo)
03507     {   
03508         // Are there any attributes to factor out ??
03509         if (!(pCurrentCompoundInfo->AttrTypesToFactorOut.IsEmpty()))
03510         {
03511             pCompound = pCurrentCompoundInfo->pCompound; 
03512             ERROR3IF(!(pCompound->IsCompound()), "List should only contain compound objects");
03513             if (pCompound->IsCompound())
03514             {   
03515                 if (!DoFactorOutCommonChildAttributes((NodeRenderableInk*)pCompound, 
03516                                                          TRUE,     // Global
03517                                                          &(pCurrentCompoundInfo->AttrTypesToFactorOut)))
03518                 {
03519                     ok = FALSE; 
03520                 }
03521             }
03522         }
03523         pCurrentCompoundInfo = (PreDelFactorOutInfoItem*)
03524             (pPreDelFactorOutInfoList->GetNext(pCurrentCompoundInfo));
03525     }
03526 
03527     // Delete all items on the pPreDelFactorOutInfoList
03528     pCurrentCompoundInfo = (PreDelFactorOutInfoItem*)(pPreDelFactorOutInfoList->GetHead());
03529     while (pCurrentCompoundInfo)
03530     {
03531         pCurrentCompoundInfo->AttrTypesToFactorOut.DeleteAll();
03532         pCurrentCompoundInfo = (PreDelFactorOutInfoItem*)
03533             (pPreDelFactorOutInfoList->GetNext(pCurrentCompoundInfo));
03534     }
03535     pPreDelFactorOutInfoList->DeleteAll(); 
03536 
03537     if (!ok)
03538         FailAndExecute();
03539     return ok; 
03540 
03541 } 
03542 */
03543 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
03544 
03545 /********************************************************************************************
03546 
03547 >   BOOL UndoableOperation::DoFactorOutCommonChildAttributes(NodeRenderableInk* CompoundObject, 
03548                                                              BOOL Global = FALSE,
03549                                                              List* pAffectedAttrTypes = NULL)
03550 
03551     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03552     Created:    7/8/94
03553 
03554     Inputs:     CompoundObject: The object to factor out
03555     
03556                 Global: When TRUE we recursively factor out attributes in all parent compounds 
03557                         of the CompoundObject (Bottom up)
03558     
03559                 pAffectedAttrTypes: An optional set of attribute types. If this is specified
03560                                     then we only consider factoring out those attributes
03561                                     which have a type which is in this set. 
03562 
03563     Returns:    TRUE if successful 
03564                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
03565 
03566     Purpose:    This function factors out all attributes which are common to all children
03567                 of the compound object. All common attributes become first children of the
03568                 compound object.
03569 
03570                 If the compound contains a node which can discard its attributes (like the caret)
03571                 then its attributes are not considered when factoring out.
03572 
03573                 As with all Do functions actions are generated to undo/redo 
03574 
03575     Errors:     -
03576     SeeAlso:    UndoableOperation::DoLocaliseCommonAttributes
03577     SeeAlso:    NodeRenderableInk::FactorOutCommonChildAttributes
03578 
03579 ********************************************************************************************/
03580 
03581 BOOL UndoableOperation::DoFactorOutCommonChildAttributes(NodeRenderableInk* CompoundObject, 
03582                                                          BOOL Global, 
03583                                                          AttrTypeSet* pAffectedAttrTypes /* = NULL */)
03584 {
03585     LocaliseCommonAttrAct* UndoAct;
03586                  
03587     if (!CompoundObject->DiscardsAttributeChildren())
03588     {
03589         // Make a copy of the attribute type set to store in the Action
03590         AttrTypeSet* pSet = NULL;
03591         if (pAffectedAttrTypes)
03592         {
03593             pSet = pAffectedAttrTypes->CopySet(); 
03594             if (!pSet)
03595                 return FALSE; // Failed to create set
03596         }
03597 
03598         // Create an action to localise attributes which have been factored out
03599         if ( LocaliseCommonAttrAct::Init(this,
03600                                          &UndoActions,
03601                                          CompoundObject,
03602                                          Global,
03603                                          pSet,
03604                                          (Action**)(&UndoAct))
03605                                           == AC_FAIL)
03606             return FALSE;
03607     }
03608 
03609     if (!CompoundObject->FactorOutCommonChildAttributes(Global, pAffectedAttrTypes))
03610     {
03611         FailAndExecuteAllButLast();
03612         return FALSE; 
03613     }
03614 
03615     return TRUE;
03616 }
03617 
03618 /********************************************************************************************
03619 
03620 >   BOOL UndoableOperation::DoFactorOutCommonAttributes(NodeRenderableInk* CompoundObject, 
03621                                                          BOOL Global = FALSE,
03622                                                          List* pAffectedAttrTypes = NULL)
03623 
03624     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03625     Created:    13/10/2004
03626 
03627     Inputs:     CompoundObject: The object to factor out
03628                 Global: When TRUE we recursively factor out attributes in all parent compounds 
03629                         of the CompoundObject (Bottom up)
03630                 pAffectedAttrTypes: An optional set of attribute types. If this is specified
03631                                     then we only consider factoring out those attributes
03632                                     which have a type which is in this set. 
03633     Returns:    TRUE if successful 
03634                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
03635     Purpose:    Just a wrapper to get function name consistency and do the
03636                 common IsCompound test to prevent DFOCCA from barfing...
03637 
03638     Errors:     -
03639     SeeAlso:    UndoableOperation::DoLocaliseCommonAttributes
03640     SeeAlso:    NodeRenderableInk::FactorOutCommonChildAttributes
03641 
03642 ********************************************************************************************/
03643 
03644 BOOL UndoableOperation::DoFactorOutCommonAttributes(NodeRenderableInk* pRootNode, 
03645                                                      BOOL bGlobal, 
03646                                                      AttrTypeSet* pAffectedAttrTypes /* = NULL */)
03647 {
03648     if (pRootNode->IsCompound())
03649         DoFactorOutCommonChildAttributes(pRootNode, bGlobal, pAffectedAttrTypes);
03650         
03651     return TRUE;
03652 }
03653 
03654 /********************************************************************************************
03655 
03656 >   BOOL  UndoableOperation::DoLocaliseCommonAttributes(NodeRenderableInk* CompoundObject, 
03657                                                         BOOL CheckForDuplicates = FALSE,
03658                                                         BOOL Global = FALSE,  
03659                                                         AttrTypeSet* pAffectedAttrTypes = NULL)
03660 
03661     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03662     Created:    7/8/94
03663     
03664     Inputs:     CompoundObject: The compound object to localise
03665     
03666                 CheckForDuplicates: Checks for duplicate attributes being attached to 
03667                                     children of the compound. This should never be 
03668                                     the case as it would indicate that common attributes
03669                                     are incorrect.
03670 
03671                 Global: TRUE indicates that we should localise the attributes on all
03672                         parent compound nodes (top down), before localising the attributes for
03673                         this compound.
03674 
03675                 pAffectedAttrTypes: An optional set of attribute types. If this is specified
03676                                     then we only consider localising  those attributes
03677                                     which have a type which is in this set. 
03678 
03679     Outputs:    -
03680     Returns:    TRUE if successful 
03681                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
03682 
03683     Purpose:    This function is the opposite of DoFactorOutCommonChildAttributes
03684                 it copies all attributes common to the compound object to each child object 
03685                 within the group which requires each attribute. The groups common attributes 
03686                 are deleted.
03687 
03688                 As with all Do functions actions are generated to undo/redo 
03689 
03690     Errors:     -
03691     SeeAlso:    UndoableOperation::DoFactorOutCommonChildAttributes
03692     SeeAlso:    NodeRenderableInk::LocaliseCommonAttributes
03693 
03694 ********************************************************************************************/
03695 
03696 
03697 BOOL UndoableOperation::DoLocaliseCommonAttributes(NodeRenderableInk* CompoundObject, 
03698                                                    BOOL CheckForDuplicates /* FALSE */, 
03699                                                    BOOL Global /*= FALSE*/,  
03700                                                    AttrTypeSet* pAffectedAttrTypes /*= NULL*/)
03701 {   
03702 
03703     FactorOutCommonChildAttrAct* UndoAct;
03704 
03705     
03706     // Create an action to localise attributes which have been factored out
03707     if (!CompoundObject->DiscardsAttributeChildren())
03708     {
03709         // Make a copy of the attribute type set to store in the Action
03710         AttrTypeSet* pSet = NULL;
03711         if (pAffectedAttrTypes)
03712         {
03713             pSet = pAffectedAttrTypes->CopySet(); 
03714             if (!pSet)
03715                 return FALSE; // Failed to create set
03716         }
03717 
03718         if ( FactorOutCommonChildAttrAct::Init(this, 
03719                                                &UndoActions, 
03720                                                CompoundObject,
03721                                                Global, 
03722                                                pSet,
03723                                                (Action**)(&UndoAct))
03724                                                == AC_FAIL)
03725             return FALSE;
03726     }
03727 
03728     if (!CompoundObject->LocaliseCommonAttributes(CheckForDuplicates, 
03729                                                       Global, 
03730                                                       pAffectedAttrTypes))
03731     {
03732         FailAndExecuteAllButLast(); 
03733         return FALSE; 
03734     }
03735 
03736     return TRUE;
03737 }
03738 
03739 
03740 
03741 
03742 
03743 
03744 /********************************************************************************************
03745 
03746 static BOOL UndoableOperation::RegisterOpDescriptor(
03747                                      UINT32 toolID,       
03748                                      UINT32 txID, 
03749                                      CCRuntimeClass* RuntimeClass,        
03750                                      TCHAR* tok,    
03751                                      pfnGetState gs,     
03752                                      UINT32 helpId, 
03753                                      UINT32 bubbleID ,
03754                                      UINT32 resourceID,  
03755                                      UINT32 controlID,
03756                                      SystemBarType GroupBarID,   
03757                                      BOOL ReceiveMessages, 
03758                                      BOOL Smart,
03759                                      BOOL Clean,
03760                                      UINT32 OneOpenInstID,      
03761                                      UINT32 AutoStateFlags) 
03762 
03763     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03764     Created:    22/3/94
03765     Inputs: 
03766                 toolID          : Module Identifier
03767                 txID            : String Resource ID
03768                 RuntimeClass    : The runtime class of the Opdescriptor's operation
03769                 tok,            : pointer to the token string
03770                 gs,             : pointer to the GetState function
03771                 helpId          : help identifier 
03772                 bubbleID        : string resource for bubble help
03773                 bmpID = 0,      : bitmap ("icon") resource ID
03774                 ReceiveMessages : Does the OpDescriptor need to respond to system messages
03775                 Smart           ; A smart duplicate operation ?
03776                 Clean           : Does the operation change the document
03777                 OneOpenInstID   : When not 0 only one live instance
03778                                   of the operation is permitted, and
03779                                   OneOpenInstID is the string resource
03780                                   ID describing why.This will be useful
03781                                   for 1 open instance dialogs.
03782                 AutoStateFlags  : Flags which indicate conditions when
03783                                   the operation should automatically be
03784                                   greyed/ticked etc..(This cuts down the 
03785                                   number of tests that need to be made in the 
03786                                   GetState function). See Opdesc.h for a description
03787     
03788                                   
03789                   
03790     Outputs:    -
03791     Returns:    TRUE if successful, else ERROR is called and FALSE returned. 
03792 
03793     Purpose:    This function should be called from the operations Init method. It 
03794                 creates an OpDescriptor for the operation
03795 
03796     SeeAlso:    UndoableOpDescriptor::UndoableOpDescriptor
03797 
03798 ********************************************************************************************/
03799 
03800 
03801 BOOL UndoableOperation::RegisterOpDescriptor(
03802                                      UINT32 toolID,       
03803                                      UINT32 txID, 
03804                                      CCRuntimeClass* RuntimeClass,        
03805                                      TCHAR* tok,    
03806                                      pfnGetState gs,     
03807                                      UINT32 helpId, 
03808                                      UINT32 bubbleID ,
03809                                      UINT32 resourceID,  
03810                                      UINT32 controlID,
03811                                      SystemBarType GroupBarID,   
03812                                      BOOL ReceiveMessages, 
03813                                      BOOL Smart,
03814                                      BOOL Clean,
03815                                      UINT32 OneOpenInstID,      
03816                                      UINT32 AutoStateFlags)
03817 {
03818     // tok is going into a string which turns out to be a string 32, so do a sanity check (Neville 26/6/97)
03819     size_t len = camStrclen(tok);
03820     ERROR2IF(len > 32,FALSE,"UndoableOperation::RegisterOpDescriptor token buffer overflow!");
03821 
03822     // Try to create the OpDescriptor
03823     UndoableOpDescriptor* OpDesc = new UndoableOpDescriptor(
03824                                                             toolID,
03825                                                             txID,
03826                                                             RuntimeClass, 
03827                                                             tok,
03828                                                             gs,
03829                                                             helpId,
03830                                                             bubbleID,
03831                                                             resourceID,
03832                                                             controlID,
03833                                                             ReceiveMessages, 
03834                                                             Smart,
03835                                                             Clean,
03836                                                             OneOpenInstID,
03837                                                             AutoStateFlags  
03838                                                            );
03839 
03840     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
03841     return TRUE; 
03842 }  
03843 
03844 
03845 
03846 /********************************************************************************************
03847 
03848 >   BOOL UndoableOperation::DoInsertPathElement(NodePath* ThisPath, 
03849                                                 INT32 Index, 
03850                                                 DocCoord NewCoord,
03851                                                 PathFlags NewFlags,
03852                                                 PathVerb NewVerb,
03853                                                 BOOL RedrawPath = TRUE);
03854 
03855 
03856     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
03857     Created:    30/9/94
03858     Inputs:     ThisPath points at the NodePath into which we want to insert an element
03859                 Index - the the element will be inserted after this element
03860                 NewCoord is the new element's coordinate value
03861                 NewFlags is the new element's flags value
03862                 NewVerb is the new element's verb value
03863                 RedrawPath - TRUE if the screen area of the path should be redrawn, FALSE
03864                 if the caller is responsible
03865     Outputs:    -
03866     Returns:    TRUE if succeeded, FALSE otherwise
03867     Purpose:    This 'Do' function adds an element into a path, recording undo information
03868     Errors:     -
03869     SeeAlso:    -
03870 
03871 ********************************************************************************************/
03872 
03873 BOOL UndoableOperation::DoInsertPathElement(NodePath* ThisPath, 
03874                                             INT32 Index, 
03875                                             DocCoord NewCoord,
03876                                             PathFlags NewFlags,
03877                                             PathVerb NewVerb,
03878                                             BOOL RedrawPath)
03879 {
03880 #ifndef STANDALONE
03881 
03882     // Create an undo action for this action, which is a RemoveElementAction
03883     RemovePathElementAction* pAction = NULL;
03884     ActionCode Act;
03885     Act = RemovePathElementAction::Init(this, 
03886                                     &UndoActions, 
03887                                     1,
03888                                     Index+1,
03889                                     (Action**)(&pAction));
03890     if (Act == AC_FAIL)
03891         return FALSE;
03892 
03893     // Tell the operation where the path is
03894     if (pAction != NULL)
03895         pAction->RecordPath(ThisPath);
03896 
03897     Document* pDocument = GetWorkingDoc();
03898     ERROR2IF(pDocument == NULL, FALSE, "There was no Document when deleteing path elements");
03899     Spread* pSpread = ThisPath->FindParentSpread();
03900     ERROR2IF(pSpread == NULL, FALSE, "Path had no parent spread");
03901 
03902     // Force a re-draw of the place where the path used to be
03903     if (RedrawPath)
03904     {
03905         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
03906         // Mark the region as requiring object to recache any cached
03907         // data they may be holding because the Op may have changed it
03908         pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath );
03909     }
03910 
03911     // Insert the new element
03912     ThisPath->InkPath.InsertSection(Index,1);
03913 
03914     // Get pointers to all the arrays of the path
03915     PathVerb* Verbs = NULL;
03916     DocCoord* Coords = NULL;
03917     PathFlags* Flags = NULL;
03918     ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags);
03919 
03920     // Update the coords
03921     Verbs[Index+1] = NewVerb;
03922     Flags[Index+1] = NewFlags;
03923     Coords[Index+1] = NewCoord;
03924 
03925     // The bounding rect may have changed
03926     ThisPath->InvalidateBoundingRect();
03927 
03928     // Redraw the new area
03929     if (RedrawPath)
03930     {
03931         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
03932         // Mark the region as requiring object to recache any cached
03933         // data they may be holding because the Op may have changed it
03934         pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath );
03935     }
03936 
03937 #endif
03938     return TRUE;
03939 }
03940 
03941 
03942 
03943 /********************************************************************************************
03944 
03945 >   BOOL UndoableOperation::DoReversePath (NodePath* ThisPath, BOOL RedrawPath = TRUE)
03946 
03947     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
03948     Created:    11/5/2000
03949     Inputs:     ThisPath points at the NodePath that we want to reverse
03950                 RedrawPath - TRUE if the screen area of the path should be redrawn, FALSE
03951                 if the caller is responsible
03952     Outputs:    The reversed path in ThisPath->InkPath
03953     Returns:    TRUE if succeeded, FALSE otherwise
03954     Purpose:    This 'Do' function reverse a path, recording undo information
03955     Errors:     -
03956     SeeAlso:    -
03957 
03958 ********************************************************************************************/
03959 
03960 BOOL UndoableOperation::DoReversePath (NodePath* ThisPath, BOOL RedrawPath /*= TRUE*/)
03961 {
03962     PathVerb* Verbs = NULL;
03963     PathFlags* Flags = NULL;
03964     DocCoord* Coords = NULL;
03965     ThisPath->InkPath.GetPathArrays(&Verbs, &Coords, &Flags);
03966     INT32 NumCoords = ThisPath->InkPath.GetNumCoords();
03967     //BOOL PrevSelected = FALSE;
03968     //INT32 PrevPos = 0;
03969     
03970     Document* pDocument = GetWorkingDoc();
03971     Spread* pSpread = ThisPath->FindParentSpread();
03972     
03973     if (RedrawPath)
03974     {
03975         // Mark the region as requiring object to recache any cached
03976         // data they may be holding because the Op may have changed it
03977         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
03978         pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath );
03979     }
03980 
03981     Path* NewEditPath = new Path ();
03982 
03983     if (!NewEditPath) { return (FALSE); }
03984 
03985     if (!NewEditPath->Initialise(NumCoords, 24))
03986     {
03987         return FALSE;
03988     }
03989 
03990     // for the new reversed path ....
03991 
03992     PathVerb* Verbs2 = NULL;
03993     PathFlags* Flags2 = NULL;
03994     DocCoord* Coords2 = NULL;
03995     NewEditPath->GetPathArrays(&Verbs2, &Coords2, &Flags2);
03996 
03997     NewEditPath->CopyPathDataFrom (&(ThisPath->InkPath));
03998 
03999     // the following loop as been optimised since this is the main part of the alg
04000     // (which just reverses the supplied path) ....
04001 
04002     NewEditPath->Reverse ();
04003     
04004     // now lets build an undo record ....
04005     
04006     ModifyPathAction* ModAction;                // since thats what were doing
04007     
04008     ActionCode Act;
04009     Act = ModifyPathAction::Init(this, &UndoActions, NumCoords, (Action**)(&ModAction));
04010     if (Act == AC_FAIL)
04011     {
04012         FailAndExecute();
04013         End();
04014         return FALSE;
04015     }
04016 
04017     // for use with the action ....
04018     
04019     PathVerb* ChangedVerbs = NULL;
04020     DocCoord* ChangedCoords = NULL;
04021     PathFlags* ChangedFlags = NULL;
04022     INT32* ChangedIndices = NULL;
04023 
04024     // If the function returned AC_NO_RECORD we shouldn't record any undo information in the action
04025     // NOTE - during unwind all actions return AC_OK with a NULL ModAction pointer!!
04026     if ((Act!=AC_NORECORD) && (ModAction!=NULL))
04027     {
04028         // This next bit is a bit hellish. Any one of these four allocations can fail, in which case 
04029         // we have to tidy up afterwards. Cue a lot of nested ifs and elses.
04030 
04031         ALLOC_WITH_FAIL(ChangedVerbs,(PathVerb*) CCMalloc(NumCoords * sizeof(PathVerb)),this);
04032         if (ChangedVerbs)
04033         {
04034             ALLOC_WITH_FAIL(ChangedCoords,(DocCoord*) CCMalloc(NumCoords * sizeof(DocCoord)),this);
04035             if (ChangedCoords)
04036             {
04037                 ALLOC_WITH_FAIL(ChangedFlags,(PathFlags*) CCMalloc(NumCoords * sizeof(PathFlags)),this);
04038                 if (ChangedFlags)
04039                 {
04040                     ALLOC_WITH_FAIL(ChangedIndices,(INT32*) CCMalloc(NumCoords * sizeof(INT32)),this);
04041                     if (!ChangedIndices)
04042                     {
04043                         CCFree( ChangedFlags );
04044                         CCFree( ChangedCoords );
04045                         CCFree( ChangedVerbs);
04046                         FailAndExecute();
04047                         End();
04048                         return FALSE;
04049                     }
04050                 }
04051                 else
04052                 {
04053                     CCFree( ChangedCoords );
04054                     CCFree( ChangedVerbs );
04055                     FailAndExecute();
04056                     End();
04057                     return FALSE;
04058                 }
04059             }
04060             else
04061             {
04062                 CCFree( ChangedVerbs);
04063                 FailAndExecute();
04064                 End();
04065                 return FALSE;
04066             }
04067         }
04068 
04069         // Now to put the undo data into the undo action
04070         for (INT32 loop = 0; loop < NumCoords; loop++)
04071         {
04072             ChangedVerbs [loop] = Verbs [loop];
04073             ChangedFlags [loop] = Flags [loop];
04074             ChangedCoords [loop] = Coords [loop];
04075             ChangedIndices [loop] = loop;
04076         }
04077     
04078         // Now we've allocated the arrays, let's tell the action about 'em
04079         ModAction->StoreArrays (ChangedVerbs, ChangedFlags, ChangedCoords, ChangedIndices, ThisPath);//pOrigPath);
04080 
04081     }
04082 
04084             
04085     // now we update the supplied path with the new path ....
04086 
04087     ThisPath->InkPath.CopyPathDataFrom (NewEditPath);
04088     
04089     delete (NewEditPath);       // and clean up
04090 
04091     if (ThisPath->IsANodeBlendPath ())
04092     {
04093         ((NodeBlendPath*) ThisPath)->DestroyCachedInformation ();
04094     }
04095 
04097 
04098     ThisPath->InvalidateBoundingRect();
04099 
04100     if (RedrawPath)
04101     {       
04102         DocRect Invalid = ThisPath->GetUnionBlobBoundingRect();
04103         // Mark the region as requiring object to recache any cached
04104         // data they may be holding because the Op may have changed it
04105         pDocument->ForceRedraw( pSpread, Invalid, FALSE, ThisPath );
04106     }
04107     return (TRUE);
04108 }
04109 
04110 
04111 
04112 /********************************************************************************************
04113 
04114 >   virtual void UndoableOperation::PerformMergeProcessing()  
04115 
04116 
04117     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04118     Created:    04/11/94
04119     Inputs:     
04120     Outputs:    -
04121     Returns:    - 
04122     Purpose:    This function gets called from the Operation::EndOp method after the operation
04123                 has ended successfuly and been added to the operation history. If the operation
04124                 could potentially merge itself with the previous operation then perform this 
04125                 merging here.
04126 
04127                 Note that this base class function does nothing.
04128                 
04129                 Here are some rules:
04130                 
04131                 1. Note that this function cannot fail - doing so would really be quite scarry.
04132                    If however you find yourself running out of memory in this function then 
04133                    simply tidyup (leaving things how they were) and then return.    
04134                    
04135                 2. After this function has been invoked the operation history size must not be
04136                    greater than it was prior to the function getting called. It would be quite
04137                    unimpressive if this was the case anyway.
04138 
04139                 3. In many cases you will want to delete 'this' operation from this function. so
04140                    just be careful huh!
04141 
04142                 The way that you merge the operation is basically up to you, however here are a few 
04143                 pointers. 
04144 
04145                 * The first thing you should do on entering this function is to check that the 
04146                 last Operation added to the operation history is this operation. Call 
04147                 OperationHistory::FindLastOp to verify this within an ERROR2 Macro.
04148 
04149                 * Next you will probably want to check if the previous operation performed can be
04150                 merged with this op. To obtain the previous op call OperationHistory::FindPrevToLastOp.
04151                 Testing the runtime class of this op is probably enough for you to determine if
04152                 a merge can take place or not.
04153 
04154                 * Usually you will want to augment the previous operation in some way so that it
04155                 includes the function which was performed by this operation. This could involve
04156                 changing an action in the previous operation, adding new actions etc. 
04157 
04158 
04159 
04160     Errors:     -
04161     SeeAlso:    OperationHistory::FindLastOp 
04162                 OperationHistory::FindPrevToLastOp
04163                 OperationHistory::DeleteLastOp
04164                 ActionList:: 
04165 
04166 
04167 ********************************************************************************************/
04168 void UndoableOperation::PerformMergeProcessing()
04169 {
04170     return; // The base class function does nowt
04171 } 
04172 
04173 
04174 
04175 /********************************************************************************************
04176 >   void UndoableOperation::MergeWithPrevious()
04177 
04178     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
04179     Created:    10/1/96
04180     Inputs:     -
04181     Outputs:    -
04182     Returns:    - 
04183     Purpose:    Moves all the actions in this operation to the end of the previous action, then
04184                 deletes this action
04185     SeeAlso:    UndoableOperation::PerformMergeProcessing()
04186 ********************************************************************************************/
04187 void UndoableOperation::MergeWithPrevious()
04188 {
04189     // Obtain a pointer to the operation history for the operation's document
04190     OperationHistory* pOpHist = &GetWorkingDoc()->GetOpHistory();
04191     if (pOpHist == NULL)
04192     {
04193         ERROR3("There's no OpHistory!?");
04194         return;
04195     }
04196 
04197     // Ensure that we are the last operation added to the operation history
04198     if (pOpHist->FindLastOp() != this)
04199     {
04200         ERROR3("Last Op should be this op");
04201         return;
04202     }
04203     
04204     Operation* pPrevOp = pOpHist->FindPrevToLastOp();
04205     if (pPrevOp != NULL)
04206     {   
04207         // Copy all the actions from us to the end of the action list in the previous op.
04208         ActionList* pPrevActions = pPrevOp->GetUndoActionList();
04209         ActionList* pPrevOtherActions = pPrevOp->GetRedoActionList();
04210         if ((pPrevActions == NULL) || (pPrevOtherActions == NULL))
04211         {
04212             ERROR3("Previous actions list pointer was NULL");
04213             return;
04214         }
04215 
04216         // Copy actions across
04217         Action* pCurrentAction = (Action*)UndoActions.RemoveHead();
04218         while (pCurrentAction != NULL)
04219         {
04220             pCurrentAction->TransferToOtherOp(pPrevOp, pPrevActions, pPrevOtherActions);
04221             pCurrentAction = (Action*)UndoActions.RemoveHead();
04222         }
04223     }
04224 
04225     // And remove ourself
04226     pOpHist->DeleteLastOp();
04227 } 
04228 
04229 /********************************************************************************************
04230 >   void UndoableOperation::IncludeParentTransformNodesInRange(Range * Rng)
04231 
04232     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
04233     Created:    15/5/99
04234     Inputs:     -
04235     Outputs:    A new range (to be deleted outside of this function)
04236     Returns:    - 
04237     Purpose:    Transforms the node, checking if any parents need to transform too
04238     SeeAlso:    
04239 ********************************************************************************************/
04240 Range * UndoableOperation::IncludeParentTransformNodesInRange(Range * Rng)
04241 {
04242     // do this by changing the selection, and then restoring it
04243     // first, save the current selection
04244     List * pSelList = GetApplication()->FindSelection()->MakeListOfNodes(TRUE);
04245     List * pRngList = Rng->MakeListOfNodes(FALSE);
04246 
04247     NodeRenderableInk::DeselectAll();
04248 
04249     NodeListItem * pItem = (NodeListItem *)pRngList->GetHead();
04250     Node * pParent = NULL;
04251 
04252     while (pItem)
04253     {
04254         if (pItem->pNode)
04255         {
04256             if (pItem->pNode->IsRenderable())
04257             {
04258                 pItem->pNode->SetSelected(TRUE);
04259                 
04260                 pParent = pItem->pNode->FindParent();
04261                 
04262                 while (pParent)
04263                 {
04264                     if (pParent->ShouldITransformWithChildren())
04265                     {
04266                         pParent->SetSelected(TRUE);
04267                     }
04268                     
04269                     pParent = pParent->FindParent();
04270                 }
04271             }               
04272         }
04273 
04274         pItem = (NodeListItem *)pRngList->GetNext(pItem);
04275     }
04276 
04277     pRngList->DeleteAll();
04278     delete pRngList;
04279 
04280     // update the selection range and make a copy of it
04281     GetApplication()->UpdateSelection();
04282 
04283     Range * RetnRng = new Range(*(GetApplication()->FindSelection()));
04284 
04285     // restore the selection
04286     NodeRenderableInk::DeselectAll();
04287 
04288     pItem = (NodeListItem *)pSelList->GetHead();
04289 
04290     while (pItem)
04291     {
04292         if (pItem->pNode)
04293         {
04294             pItem->pNode->SetSelected(TRUE);
04295         }
04296 
04297         pItem = (NodeListItem *)pSelList->GetNext(pItem);
04298     }
04299 
04300     // update the selection range and make a copy of it
04301     GetApplication()->UpdateSelection();
04302     
04303     pSelList->DeleteAll();
04304     delete pSelList;
04305 
04306     // set the range control flags
04307     RangeControl rg = RetnRng->GetRangeControlFlags();
04308     rg.PromoteToParent = TRUE;
04309 
04310     RetnRng->SetRangeControl(rg);
04311     
04312     return RetnRng;
04313 }
04314 
04315 
04316 /********************************************************************************************
04317 
04318 >   virtual BOOL UndoableOperation::UpdateChangedNodes( ObjChangeParam* pParam,
04319                                                         Spread* pSpread = NULL )
04320 
04321     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
04322     Created:    19/01/2000
04323     Inputs:     pParam      describes the way an op has changes the node(s)
04324                 pSpread     pointer to the spread containing the nodes affected by the Op
04325                             NULL => use the currently selected spread.
04326     Returns:    TRUE if all affected nodes were able to cope with the Op,
04327                 FALSE otherwise.
04328 
04329     Purpose:    This function overrides its namesake in Operation, calling the base function
04330                 first and then adding a call to NameGallery::PostTriggerEdit if this Op
04331                 admits it may change the bounds of nodes within the tree.
04332 
04333     Errors:     ERROR2 if pParam is NULL.
04334     SeeAlso:    Operation::UpdateChangedNodes(ObjChangeParam*, Spread*)
04335 
04336 ********************************************************************************************/
04337 
04338 BOOL UndoableOperation::UpdateChangedNodes(ObjChangeParam* pParam,Spread* pSpread)
04339 {
04340     ERROR2IF(pParam == NULL, FALSE, "pParam == NULL");
04341     BOOL    ok = Operation::UpdateChangedNodes(pParam, pSpread);
04342     
04344     // Karim MacDonald 19/01/2000.
04345     // If the call to the base class was successful, then we can try to call
04346     // NameGallery::PostTriggerEdit, to allow any nodes marked for stretching to
04347     // change in response to any bounds changes in the tree.
04348     if (ok)
04349     {
04350         // When querying the Op responsible for the change, I've decided to use 'this'.
04351         // Could've asked pParam for the Op's pointer, but 'this' is probably more reliable.
04352         UndoableOperation* pChangeOp = this;// pParam->GetOpPointer();
04353         if (pChangeOp != NULL && pChangeOp->MayChangeNodeBounds() && NameGallery::Instance())
04354         {
04355             NameGallery::Instance()->PostTriggerEdit(pChangeOp, pParam);
04356         }
04357     }
04359 
04360     return ok;
04361 }
04362                 
04363 
04364 
04365 /********************************************************************************************
04366 
04367 >   BOOL UndoableOperation::NoStretchUpdateChangedNodes(ObjChangeParam* pParam,
04368                                                         Document* pDoc)
04369 
04370     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
04371     Created:    19/01/2000
04372     Inputs:     pParam  description of the change.
04373                 pDoc    pointer to the document.
04374     Outputs:    Causes any nodes marked for updating to update themselves.
04375     Returns:    TRUE if successful,
04376                 FALSE otherwise.
04377 
04378     Purpose:    This is *not* an override of Operation::UpdateChangedNodes(..., Document*),
04379                 although it performs *almost* exactly the same job. It is provided solely
04380                 for NameGallery::PostTriggerEdit to call in order to get all affected nodes
04381                 to update themselves correctly after a stretch. PostTriggerEdit is itself
04382                 called in response to normal UpdateChangedNodes calls, so this avoids
04383                 recursion problems.
04384 
04385     Errors:     We ERROR2 if either of the parameters is NULL.
04386     See also:   Operation::UpdateChangedNodes(ObjChangeParam*, Document*).
04387 
04388 ********************************************************************************************/
04389 BOOL UndoableOperation::NoStretchUpdateChangedNodes(ObjChangeParam* pParam, Document* pDoc)
04390 {
04391     ERROR2IF(pParam == NULL, FALSE, "pParam == NULL");
04392     ERROR2IF(pDoc   == NULL, FALSE, "pDoc == NULL");
04393 
04394     BOOL ok = TRUE;
04395     Chapter* pChapter = Node::FindFirstChapter(pDoc);
04396     while (pChapter != NULL && ok)
04397     {
04398         // Now find the first child of the Chapter, because this is the level at which Spreads hang out
04399         Node* pNode = pChapter->FindFirstChild();
04400 
04401         // Update changed nodes in each of the chapter's spreads. We *don't* call
04402         // Operation::UpdateChangedNodes(..., Spread*), as that would lead to recursion...
04403         while (pNode != NULL && ok)
04404         {
04405             if (pNode->IsSpread())
04406             {
04407                 Spread* pSpread = (Spread*)pNode;
04408                 ok = UpdateChangedNode(pParam, pSpread->FindFirstChild());
04409             }
04410 
04411             pNode = pNode->FindNext();
04412         }
04413 
04414         pChapter = pChapter->FindNextChapter(); 
04415     }
04416 
04417     return ok;
04418 }
04419 

Generated on Sat Nov 10 03:47:17 2007 for Camelot by  doxygen 1.4.4