ops.cpp

Go to the documentation of this file.
00001 // $Id: ops.cpp 1361 2006-06-25 16:43:38Z 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 /*
00100     Implementation of the following classes:
00101 
00102     ActionList: Class used by an Operation to store UNDO, REDO, and Smart Duplicate actions
00103     Operation:  Class from which all Camelot operations are derived
00104     Action:     Class from which all operations actions are derived
00105                  
00106 */  
00107 
00108 
00109 /*
00110 
00111 */
00112 
00113 #include "camtypes.h"
00114 //#include "inetop.h"
00115 
00116 DECLARE_SOURCE("$Revision: 1361 $");
00117 
00118 #ifdef RALPH
00119 #include "nativeop.h"
00120 #include "ralphdoc.h"
00121 #endif
00122 
00123 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 //#include "range.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 #include "ophist.h"
00127 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00128 //#include "docview.h"  - in camtypes.h [AUTOMATICALLY REMOVED]
00129 //#include "resource.h" 
00130 //#include "simon.h"
00131 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00132 //#include "selstate.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00133 #include "noderect.h"
00134 //#include "tranform.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00135 //#include "mario.h"
00136 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00137 //#include "bars.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00138 #include "progress.h"
00139 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00140 #include "menuops.h"
00141 #include "blobs.h"
00142 #include "sprdmsg.h"
00143 #include "layer.h"
00144 #include "lineattr.h"
00145 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00146 #include "objchge.h"
00147 #include "chapter.h"
00148 #include "nodetxts.h"
00149 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00150 #include "cutop.h"
00151 #include "dbugtree.h"
00152 //#include "ralphcri.h"
00153 #include "layermsg.h"   // LayerMsg::LayerReason::REDRAW_LAYER
00154 
00155 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00156 //#include "attrval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00157 #include "userattr.h"
00158 #include "tmpltatr.h"
00159 
00160 #include "ngcore.h"
00161 //#include "ngsetop.h"
00162  
00163 CC_IMPLEMENT_MEMDUMP(ActionList, List)
00164 CC_IMPLEMENT_DYNAMIC(OpParam, CCObject)
00165 CC_IMPLEMENT_DYNCREATE( Operation, MessageHandler )
00166 CC_IMPLEMENT_DYNCREATE( Action, ListItem )
00167 CC_IMPLEMENT_DYNCREATE( InvalidateRegionAction, Action )
00168 CC_IMPLEMENT_DYNCREATE( InvalidateRegionIfBgRedrawAction, InvalidateRegionAction )
00169 CC_IMPLEMENT_DYNCREATE( HideNodeAction, Action )
00170 CC_IMPLEMENT_DYNCREATE( ShowNodeAction, Action )
00171 CC_IMPLEMENT_DYNCREATE( RestoreSelectionsAction, Action )
00172 CC_IMPLEMENT_DYNCREATE( SelectDeselectAction, Action )
00173 CC_IMPLEMENT_DYNCREATE( TransformNodeAction, Action )
00174 CC_IMPLEMENT_DYNCREATE( UnApplyAction, Action )
00175 CC_IMPLEMENT_DYNCREATE( ApplyAction, Action )
00176 
00177 // Define this to allow HideNode actions to detect effect attributes without
00178 // testing specific Tags. Fixes problems related to undo after using transp slider.
00179 #define _HIDENODE_DETECTEFFECTS
00180 
00181 // Declare smart memory handling in Debug builds
00182 #define new CAM_DEBUG_NEW
00183 
00184 //-------------------------------------------------------------------------------------------  
00185 // ActionList methods   
00186 /********************************************************************************************
00187 
00188 >   ActionList::ActionList()
00189 
00190     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00191     Created:    6/7/93
00192     Inputs:     -
00193     Outputs:    -
00194     Returns:    -
00195     Purpose:    ActionList constructor
00196     Errors:     -
00197     SeeAlso:    -
00198 
00199 ********************************************************************************************/
00200 
00201 ActionList::ActionList(): List(){}    
00202 
00203 /********************************************************************************************
00204 
00205 >   ActionList::~ActionList()
00206 
00207     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00208     Created:    6/7/93
00209     Inputs:     -
00210     Outputs:    -
00211     Returns:    -
00212     Purpose:    ActionList destructor, deletes the action list and all actions it contains. 
00213                 NOTE that REDO action lists must be deleted backwrds.
00214     Errors:     -
00215     SeeAlso:    ActionList::SlaughterBackwards;
00216 
00217 ********************************************************************************************/
00218 
00219 ActionList::~ActionList()
00220 {   
00221 /*
00222     ListItem* Current = GetHead();          // First action     
00223     while (Current != NULL)                 // While there are more actions to delete
00224     {
00225         ((Action*)RemoveHead())->Slaughter();  // Delete the action 
00226         Current = GetHead();                   // Get next action
00227         ContinueSlowJob(); 
00228     }
00229 */
00230     ListItem* Current = GetTail();          // Last action     
00231     while (Current != NULL)                 // While there are more actions to delete
00232     {
00233         ((Action*)RemoveTail())->Slaughter();  // Delete the action 
00234         Current = GetTail();                   // Get next action
00235         ContinueSlowJob(); 
00236     }
00237 
00238 } 
00239                  
00240 
00241 
00242 
00243 /********************************************************************************************
00244 
00245 >   BOOL ActionList::ExecuteForwards(BOOL AllButLast)
00246 
00247 
00248     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00249     Created:    5/7/93
00250     Inputs:     AllButLast: When FALSE execute all actions in the action list. 
00251                             When TRUE execute all actions except the last
00252     Outputs:    -
00253     Returns:    TRUE if the action list was successfully executed
00254                 FALSE if it was not 
00255                 
00256     Purpose:    This method executes all actions in the action list forwards.  
00257                 If there is no failure then all actions are deleted 
00258                 If an action fails whilst executing no actions are deleted and
00259                 FALSE is returned.  
00260     Errors:     -
00261     SeeAlso:    -
00262 
00263 ********************************************************************************************/
00264 
00265 BOOL ActionList::ExecuteForwards(BOOL AllButLast)
00266 {                                          
00267     ListItem* CurrentAction = GetHead();  
00268     ListItem* LastAction = GetTail(); 
00269     
00270     // If we are to execute all actions but the last then the last action to
00271     // execute is prev(tail)
00272     if ((LastAction != NULL) && (AllButLast))
00273         LastAction = LastAction->Previous; 
00274        
00275 //TRACE( _T("Before Redo\n"));
00276 //((Action*)CurrentAction)->GetWorkingDoc()->FindFirstSpread()->DST();
00277 
00278     BOOL Failed = FALSE; 
00279     while ((CurrentAction != LastAction) && (!Failed))  // For each action in the list
00280     {    
00281         // Execute the action                   
00282         Failed = (((Action*)CurrentAction)->Execute() == AC_FAIL); 
00283 
00284 //((Action*)CurrentAction)->Dump();
00285 //((Action*)CurrentAction)->GetWorkingDoc()->FindFirstSpread()->DST();
00286 
00287         CurrentAction = CurrentAction->Next;    
00288     }                   
00289     
00290     if (!Failed)
00291     {
00292         // Successfully executed action list so all actions are deleted. 
00293         Action* HeadItem = (Action *) GetHead(); 
00294 
00295         Document *pDoc = HeadItem->GetWorkingDoc();
00296         ERROR2IF(pDoc == NULL, FALSE, "No document attached to action in Action::ExecuteForwards");
00297         OperationHistory& OpHistory = pDoc->GetOpHistory();
00298 
00299         while (HeadItem != NULL)
00300         {         
00301             // Reduce the size of the operation history by the size of the action 
00302             // being deleted. 
00303             OpHistory.DecSize(HeadItem->GetSize()); 
00304 
00305             // Remove the action from the action list 
00306             delete (RemoveItem(HeadItem));
00307 
00308             // Get next action 
00309             HeadItem = (Action *) GetHead(); 
00310         }
00311     }
00312     
00313     return (!Failed);
00314 }    
00315 
00316 /********************************************************************************************
00317 
00318 >   BOOL ActionList::ExecuteBackwards(BOOL AllButLast, BOOL bIgnoreSelectActions)
00319 
00320 
00321     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00322     Created:    5/7/93
00323     Inputs:     AllButLast: When FALSE execute all actions in the action list. 
00324                             When TRUE execute all actions except the last
00325     Outputs:    -
00326     Returns:    TRUE if the action list was successfully executed
00327                 FALSE if it was not 
00328                 
00329     Purpose:    This method executes all actions in the action list backwards.  
00330                 If there is no failure then all actions are deleted 
00331                 If an action fails whilst executing no actions are deleted and
00332                 FALSE is returned.   
00333     Errors:     -
00334     SeeAlso:    ActionList::ExecuteForwards
00335 
00336 ********************************************************************************************/
00337 
00338 BOOL ActionList::ExecuteBackwards(BOOL AllButLast, BOOL bIgnoreSelectActions) 
00339 {        
00340     ListItem* CurrentAction = GetTail();   
00341 
00342     if (AllButLast && (CurrentAction != NULL))   
00343         CurrentAction = CurrentAction->Previous; // Don't execute the last action 
00344        
00345 //TRACE( _T("Before Undo\n"));
00346 //((Action*)CurrentAction)->GetWorkingDoc()->FindFirstSpread()->DST();
00347 
00348     BOOL Failed = FALSE;                                       
00349     while ((CurrentAction != NULL) && (!Failed)) // For each action in the list
00350     {
00351         // Attempt to execute action 
00352         if (!(bIgnoreSelectActions &&
00353             CurrentAction->IsKindOf(CC_RUNTIME_CLASS(RestoreSelectionsAction)))
00354            )
00355             Failed = (((Action*)CurrentAction)->Execute() == AC_FAIL);
00356 
00357 //((Action*)CurrentAction)->Dump();
00358 //((Action*)CurrentAction)->GetWorkingDoc()->FindFirstSpread()->DST();
00359 
00360         // Get next action 
00361         CurrentAction = CurrentAction->Previous; 
00362     }    
00363     
00364     if (!Failed)     
00365     { 
00366         // Successfully executed action list so all actions are deleted. 
00367         Action* HeadItem = (Action *) GetHead(); 
00368 
00369         Document *pDoc = HeadItem->GetWorkingDoc();
00370         ERROR2IF(pDoc == NULL, FALSE, "No document attached to action in Action::ExecuteBackwards");
00371         OperationHistory& OpHistory = pDoc->GetOpHistory();
00372 
00373         // Loop while there are still actions to delete
00374         while (HeadItem != NULL)
00375         {                      
00376             // Reduce the size of the operation history by the size of the action being deleted
00377             OpHistory.DecSize(((Action*)HeadItem)->GetSize()); 
00378 
00379             // Delete the action 
00380             delete (RemoveItem(HeadItem));
00381 
00382             // Get next action  
00383             HeadItem = (Action *) GetHead(); 
00384         }
00385         
00386     }
00387     
00388     return (!Failed); 
00389 }
00390 
00391 /********************************************************************************************
00392 
00393     Action* ActionList::FindActionOfClass(CCRuntimeClass* ClassOfActionToFind, 
00394                                           Action* LastAction = NULL); 
00395 
00396 
00397 
00398     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00399     Created:    04/11/94
00400     Inputs:     ClassOfActionToFind: the runtime class of the action we are to search for
00401                 LastAction:          if you need to search for more than one occurence of
00402                                      a particular action then specifying the LastAction parameter
00403                                      will cause the search to begin after this Action. If this
00404                                      parameter is not specified then the search will start at
00405                                      the head of the action list.
00406     Outputs:    -
00407     Returns:    A pointer to the first action with runtime class ClassOfActionToFind, or NULL
00408                 if no such action was found.
00409                 
00410     Purpose:    To search the action list for the first action with runtime class 
00411                 ClassOfActionToFind
00412     Errors:     -
00413     SeeAlso:    
00414 
00415 ********************************************************************************************/
00416 
00417  
00418 Action* ActionList::FindActionOfClass(CCRuntimeClass* ClassOfActionToFind,
00419                                       Action* LastAction)
00420 {
00421     Action* Current = LastAction;   // Will be NULL if LastAction is not specified
00422     do
00423     {
00424         if (Current == NULL)
00425         {
00426             Current = (Action*)GetHead(); 
00427         }
00428         else
00429         {
00430             Current = (Action*)GetNext(Current);
00431         }
00432         if ((Current != NULL) && (Current->GetRuntimeClass() == ClassOfActionToFind))
00433         {
00434             return Current;
00435         }
00436     } while (Current != NULL); 
00437     return NULL;    
00438 } 
00439 
00440 
00441 // ---------------------------------------------------------------------------------------- 
00442 // Operation methods                              
00443 
00444        
00445         
00446 Operation* Operation::CurrentDragOp = NULL; 
00447 
00448 static DocView* pDraggingDocView = 0;
00449 
00450 // Vars to do with stopping auto-repeats during drags
00451 // See StartDrag() & EndDrag()
00452 INT32 Operation::CurKeyboardSpeed;
00453 BOOL Operation::ResetKeyboardSpeed           = FALSE;
00454 BOOL Operation::KeyboardRepeatOffDuringDrags = FALSE;
00455 
00456 BOOL Operation::s_bQuickRender = FALSE;
00457 
00458 /*********************************************************************************************
00459 
00460 >    static BOOL Operation::Init()
00461 
00462      Author:    Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
00463      Created:   16/10/95
00464      Inputs:    -
00465      Outputs:   -
00466      Returns:   -
00467      Purpose:   Does any Operation initialisation necessary.
00468                 Declares an Operation INI setting
00469      Errors:    
00470 
00471 **********************************************************************************************/
00472  
00473 BOOL Operation::Initialise()
00474 {
00475     BOOL ok = TRUE;
00476 
00477 // I've taken out the ability to switch auto-repeats off during drags (Markn - 19/10/95)
00478 /*
00479     // Declare the section that the preferences for Operations will live in
00480     if (ok) ok= Camelot.DeclareSection(TEXT("Dragging"), 1);
00481 
00482     // declare the var
00483     if (ok) ok= Camelot.DeclarePref(TEXT("Dragging"), TEXT("KeyboardRepeatOffDuringDrags "),  &Operation::KeyboardRepeatOffDuringDrags,  0, 1);
00484 */
00485     return ok;
00486 }
00487 
00488 /********************************************************************************************
00489 
00490 >   Operation::Operation(CCRuntimeClass* MsgHandlerClass = CC_RUNTIME_CLASS(Operation))
00491 
00492     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00493     Created:    30/6/93
00494     Inputs:     -
00495     Outputs:    -
00496     Returns:    -
00497     Purpose:    Constructs a new operation object: setting default operation flags, and 
00498                 adding it to the Operation message list. Note that the routines which create 
00499                 operations must create an instance for every seperate input that they receive. 
00500                 They must not re-use an existing instance because operation objects have to be 
00501                 submitted to the undo system to represent user operations on a 1 to 1 basis.   
00502     Errors:     -
00503     SeeAlso:    -
00504 
00505 ********************************************************************************************/
00506 
00507 // All operations are added to the Operation Class list by default, and will receive system 
00508 // messages      
00509 Operation::Operation(CCRuntimeClass* MessageHandlerClass) : 
00510     MessageHandler(MessageHandlerClass, TRUE)
00511 {                 
00512     //Set default operation flag states. The defaults are for a clean non undoable operation
00513 
00514     OpFlags.Failed = FALSE;                                                                  
00515     OpFlags.ExecuteOnEnd = FALSE;       
00516     OpFlags.AllButLast = FALSE; 
00517     OpFlags.KeepOnEnd = FALSE;  
00518     OpFlags.UnwindingActions = FALSE;
00519     OpFlags.HasOwnTimeIndicator = FALSE;  
00520     OpFlags.SucceedAndDiscard = FALSE;   
00521     OpFlags.DeleteOnEnd = FALSE;
00522     OpFlags.IgnoreSelectActions = FALSE;
00523 
00524     DeleteUndoOpsToMakeSpace = FALSE;  
00525     pDocToInformOfOpStatus = NULL; 
00526     
00527     // Let the system know that we are currently doing the operation for the first time
00528     OpStatus = DO;
00529 
00530     // The only time Operations get created is when they are to be put on the "live ops"
00531     // list, and the only time they get put on the "live ops" lists is when they will
00532     // affect whatever is the current document.
00533     pOurDoc = Document::GetCurrent();
00534     pOurView = View::GetCurrent();
00535 
00536 }
00537 
00538 /********************************************************************************************
00539 
00540 >   Operation::~Operation()                                                      
00541 
00542 
00543     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00544     Created:    6/7/93
00545     Inputs:     -
00546     Outputs:    -
00547     Returns:    -
00548     Purpose:    Operation destructor
00549     Errors:     -
00550     SeeAlso:    -
00551 
00552 ********************************************************************************************/
00553 
00554 Operation::~Operation()
00555 {                                     
00556     // The destructors of the Operations action lists will automatically be called
00557 
00558     // However, we must ensure the REDO action list is slaughtered in the correct
00559     // (reverse) direction... the UNDO list will slaughter its items in the correct
00560     // direction in its destructor
00561 
00562     // Removed by Simon 19/04 All action lists now slaughter themselves backwards
00563     //RedoActions.SlaughterBackwards();
00564 }
00565 
00566 /********************************************************************************************
00567 
00568 >   void Operation::EndOp()
00569 
00570     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00571     Created:    6/7/93
00572     Inputs:     -
00573     Outputs:    -
00574     Returns:    -
00575     Purpose:    This function is called by End(), to end an operation. It removes the 
00576                 operation from the live list and then does various things depending on the 
00577                 state of the operation's flags. It is normally called by the operation's Do 
00578                 function. The way the operation flags are interpreted by EndOp are described 
00579                 by the following algorithm.      
00580     
00581     MonoOn  
00582                        
00583                 IF the Failed flag is TRUE
00584                     IF the ExecuteOnEndFlag is TRUE      
00585                         IF the AllButLast flag is TRUE
00586                             Execute all but the last action in the operations UNDO/REDO 
00587                             action list.    
00588                         ELSE
00589                             Execute all actions in the operation's UNDO/REDO action list   
00590                     ELSE
00591                         IF we are undoing 
00592                             Delete the operation and all redo operations
00593                         ELSE IF we are doing or redoing
00594                             Delete the operation and all undo operations
00595                         
00596                     IF the OperationStatus is DO
00597                         Delete the operation
00598                 ELSE 
00599                     IF the SystemUndo flag is TRUE and we are doing the operation
00600                         Add operation to the operation history
00601                     ELSE 
00602                         IF the keepOnEnd flag is FALSE
00603                             delete the operation
00604                     IF the Clean flag is FALSE
00605                         Inform the document that it has been modified
00606         
00607     MonoOff             
00608     Scope:      private         
00609     Errors:     -
00610     SeeAlso:    Operation::End
00611 
00612 ********************************************************************************************/
00613 
00614 void Operation::EndOp()
00615 {   
00616     OpFlags.DeleteOnEnd = FALSE; // Set to TRUE if the operation should be deleted
00617 
00618     // Some static attribute apply flags, added by Will
00619     AttributeManager::HaveAskedAboutContoneColours = FALSE;
00620     AttributeManager::UserCancelledContoneColours = FALSE;
00621 
00622     Document *pDocument = GetWorkingDoc();
00623 
00624 //  if (pDocument != NULL)
00625 //      pDocument->FlushRedraw();
00626 
00627     Document *pDoc = (Document *) Camelot.Documents.GetHead();
00628 
00629     while (pDoc != NULL)
00630     {
00631         // Force any pending redraw to be done now
00632         if (pDoc == pDocument)
00633             pDocument->FlushRedraw();
00634 
00635         pDoc = (Document *) Camelot.Documents.GetNext(pDoc);
00636     }
00637 
00638     // Remember that it is valid now for pOpDesc to be NULL (eg. for InformationBarOps 
00639     // which don't have OpDescriptors)
00640     /*OpDescriptor* pOpDesc =*/ OpDescriptor::FindOpDescriptor(this->GetRuntimeClass()); 
00641     
00642     OperationHistory*  OpHist;      
00643 
00644     // if there is not a current document and the operation in progress is undoable
00645     // then ensure
00646 PORTNOTE("other","Removed OpAsynchClipartImport usage")
00647     if( IsKindOf( CC_RUNTIME_CLASS(UndoableOperation) ) 
00648 #ifndef EXCLUDE_FROM_XARALX
00649          && !IsKindOf(CC_RUNTIME_CLASS(OpAsynchClipartImport)))
00650 #endif
00651         )
00652         ENSURE( (pDocument != NULL), "Document has been destroyed before operation has ended");
00653 
00654     // The operation is ending so don't send any more messages to it
00655     if (OpStatus == DO)
00656     {
00657         SendNoMoreMessages(this); 
00658     }
00659 
00660     // Has the operation copied any component data between documents 
00661     if (pDocToInformOfOpStatus != NULL)
00662     {
00663         if (OpFlags.Failed)
00664         {
00665             // We may have copied data accross but because the operation has failed
00666             // we must discard this.
00667             pDocToInformOfOpStatus->AbortComponentCopy(); 
00668         } 
00669         else
00670         {
00671             // Call EndComponentCopy to commit the new data
00672             if (!pDocToInformOfOpStatus->EndComponentCopy())
00673             {
00674                 // Note that AbortComponentCopy has been called
00675                 // Unable to complete the op so it must fail I'm afraid
00676                 OpFlags.Failed = TRUE; 
00677             } 
00678 
00679         }
00680     }
00681 
00682     // if the operation is not undoable then dispose of it and exit.
00683     if (!IsKindOf(CC_RUNTIME_CLASS(UndoableOperation)))
00684     {
00685                 
00686         if (!OpFlags.KeepOnEnd)
00687         {   
00688             OpFlags.DeleteOnEnd = TRUE;      
00689         }
00690         goto End;
00691     }
00692     else
00693     {
00694         // If the operation has been done but does not have any Undo actions then
00695         // dispose of the operation and then exit.
00696         if  ((UndoActions.GetHead() == NULL)  && (OpStatus == DO))
00697         {   
00698             if (!OpFlags.KeepOnEnd)
00699             {   
00700                 OpFlags.DeleteOnEnd = TRUE; 
00701             }
00702             goto End;
00703         }    
00704     }
00705 
00706     OpHist = &(pDocument->GetOpHistory());   
00707     
00708     // Check if the operation failed
00709     if (OpFlags.Failed || OpFlags.SucceedAndDiscard)
00710     {            
00711         if (OpFlags.ExecuteOnEnd)
00712         {                            
00713             // Set the unwindingActions flag so that no actions are created in further 
00714             // calls to the Action::Init function.  
00715             OpFlags.UnwindingActions = TRUE;      
00716              
00717             // Determine which action list to execute  
00718 
00719             // If we are UNDOING then the failure must have occured when trying to create 
00720             // REDO actions for the operation, so execute the RedoActions list (which will 
00721             // restore the UNDO actions list). 
00722 
00723 
00724             BOOL bOK = FALSE;
00725             if (OpStatus == UNDO)
00726             {
00727                 bOK = RedoActions.ExecuteBackwards(OpFlags.AllButLast, OpFlags.IgnoreSelectActions);
00728                 ERROR3IF(!bOK, "An operation failed whilst unwinding");
00729             }
00730             // If we are DOING or REDOING then the failure must have occured when trying to 
00731             // create UNDO actions, so execute the UndoActions list. 
00732             
00733             else
00734             {
00735                 bOK = UndoActions.ExecuteBackwards(OpFlags.AllButLast, OpFlags.IgnoreSelectActions);
00736                 ERROR3IF(!bOK, "An operation failed whilst unwinding");
00737             }
00738         }   
00739         else if (OpFlags.Failed) // Discard
00740         {   
00741             if (OpStatus == UNDO)
00742             {     
00743                 // Delete the operation and all redo operations
00744             
00745                 // One step back in time  
00746                 OpHist->NowPtr = OpHist->OpHistoryList.GetPrev(OpHist->NowPtr);  
00747                 // Delete all Redo operations including the current operation                 
00748                 OpHist->DeleteRedoableOps();   
00749                 goto End; 
00750             }   
00751             else // REDO and DO       
00752             {
00753                  // Delete all Undo records including the current operation 
00754                 OpHist->NowPtr = OpHist->OpHistoryList.GetNext(OpHist->NowPtr); 
00755                 OpHist->DeleteUndoableOps();                                   
00756                 
00757                 // In the case of a REDO we can return because the operation has been deleted. 
00758                 if (OpStatus == REDO)
00759                     goto End; 
00760             }
00761             
00762         }            
00763         // If we are DOING the operation for the first time delete the operation
00764         if (OpStatus == DO)          
00765         {
00766             if (!OpFlags.KeepOnEnd)
00767             {
00768                 OpHist->DecSize(this->GetSize()); // The size total in the operation history 
00769                                                  // will include the current size of the 
00770                                                  // operation.  
00771                 OpFlags.DeleteOnEnd = TRUE; 
00772             }
00773             goto End;   
00774         }
00775     }
00776     
00777     if (!OpFlags.Failed)   // Operation has not failed
00778     {  
00779         // Decide if the operation should be added to the documents operation history
00780         if  (OpStatus == DO && !OpFlags.SucceedAndDiscard)
00781         {    
00782             // The undo action list should contain actions, the redo action list should not
00783             ENSURE(UndoActions.GetHead() != NULL, "The undo action list is empty"); 
00784             ENSURE(RedoActions.GetHead() == NULL, "The redo action list is not empty"); 
00785 
00786             // Add operation to document's operation history
00787             pOurDoc->GetOpHistory().Add(this); 
00788         } 
00789         
00790         // If the Operation has no OpDescriptor then we assume that the operation is clean
00791         //if (pOpDesc != NULL)
00792         //{   
00793             //if (!(pOpDesc->GetOpFlags().Clean)) 
00794             //{ 
00795                 // BODGE We do not inspect the Clean flag, cos not everyone has set it correctly.
00796                 // We know this is an undoable operation so it must have affected the document.
00797                 // Therefore we set the documents modified flag.
00798                 
00799                 // Any operation with a FALSE clean flag has indicated that it 
00800                 // will or may have modified the document in some way. Therefore
00801                 // inform the document that it has been modified. 
00802 
00803 #if !defined(EXCLUDE_FROM_RALPH)
00804                 // The only exception to this is OpCopy         IS_A
00805                 if (!(GetRuntimeClass() == CC_RUNTIME_CLASS(OpCopy)))
00806                     pOurDoc->SetModified(TRUE);
00807 #endif
00808             //} 
00809         //}
00810     } 
00811 
00812     End:
00813 
00814     EndSlowJob();   
00815 
00816 #if !defined(EXCLUDE_FROM_RALPH)
00817     // Inform the world that the operation has ended
00818     // We don't want to do it at the end of an OpExit though
00819     if (GetRuntimeClass() != CC_RUNTIME_CLASS(OpExit))
00820     {
00821         BROADCAST_TO_ALL(OpMsg(this, OpMsg::END)); 
00822     }
00823 #endif
00824 
00825     // The Operation which has just been performed has probably changed the state
00826     // of the system in some way so we update the state of all the bars. We don't
00827     // do this if the operation has failed however because:
00828     // A. When an operation has failed the state of the system should be restored
00829     //    to the state it was in prior to the operation being performed. So it
00830     //    will not be necessary.
00831     // B. The fact that the operation has failed implies that we are probably low on 
00832     //    memory so it's probably safer not to update it anyway. Note that if the
00833     //    bar state is ever incorrect this should not be disastrous because the
00834     //    GetState fn of the operation is always called prior to Invoking an operation
00835     //    anyway. 
00836 
00837     if (!OpFlags.Failed && !OpFlags.SucceedAndDiscard)
00838     { 
00839 
00840         // Call the virtual PerformMergeProcessing function. This gives the operation a chance
00841         // to merge itself with the previous operation if appropriate.
00842         if(IS_KIND_OF(UndoableOperation) && !(OpFlags.DeleteOnEnd))
00843         { 
00844             if  (OpStatus == DO)  // We only want to merge ops when Doing the operation
00845             {    
00846                 // Note that the PerformMergeProcessing fn could delete this op (so be careful !)
00847                 ((UndoableOperation*)this)->PerformMergeProcessing(); 
00848             }
00849         }
00850     }
00851 
00852      
00853     // If there was any sort of error during that Op which hasn't been reported to the user yet
00854     // then we'd better report it now!
00855     InformLastError();
00856 
00857     // If the operation has survived all that then reset all failure and control 
00858     // flags.   
00859     OpFlags.Failed = FALSE;                                                                  
00860     OpFlags.ExecuteOnEnd = FALSE;       
00861     OpFlags.AllButLast = FALSE;  
00862     OpFlags.UnwindingActions = FALSE; 
00863     OpFlags.SucceedAndDiscard = FALSE; 
00864     OpFlags.IgnoreSelectActions = FALSE;
00865     DeleteUndoOpsToMakeSpace = FALSE;  
00866 
00867 
00868     // Be careful what is after this
00869     if (OpFlags.DeleteOnEnd)
00870         delete (this); 
00871     // The bar state will be refreshed  during idle time
00872     DialogBarOp::SetSystemStateChanged(); 
00873 }    
00874  
00875 
00876 
00877 /********************************************************************************************
00878 
00879 >   void Operation::End()
00880 
00881     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00882     Created:    14/10/93
00883     Inputs:     -
00884     Outputs:    -
00885     Returns:    -
00886     Purpose:    This function calls EndOp which performs the main end of operation processing.
00887                 see Operation::EndOp
00888                 
00889     Errors:     -
00890     SeeAlso:    Operation::EndOp
00891 
00892 ********************************************************************************************/
00893 
00894 void Operation::End()
00895 {
00896     // When an operation is ending all sorts of scary stuff is going on with nodes, so we 
00897     // do not want to be rendering at the time !..
00898 PORTNOTE("other","Removed RalphCriticalSection usage")
00899 #ifndef EXCLUDE_FROM_XARALX
00900 //  RalphCriticalSection rcs;  
00901     if ( (OpFlags.SystemUndo) && (OpStatus == DO) && (!(OpFlags.ExecuteOnEnd)) )
00902     { 
00903         // A call to StartUndoOperation is made which creates a RestoreSelections action 
00904         // which when executed  will restore the selection state to the state which existed 
00905         // just after performing the operation. 
00906         StartUndoOperation(); 
00907     }
00908 #endif
00909 
00910     // Slight bodge to ensure that all op permission flags of all nodes are set to 
00911     // PERMISSION_UNDEFINED regardless whether the op succeeded or failed.
00912     if (this->IS_KIND_OF(UndoableOperation))
00913     {
00914         UndoableOperation* pOp = (UndoableOperation*)this;
00915         ObjChangeFlags cFlags;
00916         ObjChangeParam ObjChange(OBJCHANGE_IGNORE,cFlags,NULL,pOp);
00917         pOp->UpdateChangedNodes(&ObjChange);
00918     }
00919 
00920     EndOp();
00921 
00922 }   
00923 
00924 /********************************************************************************************
00925 
00926 >   Document *Operation::GetWorkingDoc()
00927 
00928     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00929     Created:    02/13/95
00930     Returns:    The document this operation is working on.
00931     Purpose:    Find out which document this operation is working on.  This will remain
00932                 constant throughout the life of most operations with the exception of
00933                 DialogOps. (The File open operation also changes its working document
00934                 but this is a very rare exception).
00935     SeeAlso:    Operation::GetWorkingView; Operation::GetWorkingDocView
00936 
00937 ********************************************************************************************/
00938 
00939 
00940 /********************************************************************************************
00941 
00942 >   View *Operation::GetWorkingView()
00943 
00944     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00945     Created:    02/13/95
00946     Returns:    The View this operation is attached to.
00947     Purpose:    Returns the View that is associated with this operation. 
00948                 Note that the majority of operations don't care what view they work on - most
00949                 of them are document-based, and cause all views attached to a document to be
00950                 updated.  However, some operations such as zooming, changing the quality 
00951                 etc. only work on a particular view.
00952     SeeAlso:    Operation::GetWorkingDocView; Operation::GetWorkingDoc
00953 
00954 ********************************************************************************************/
00955 
00956 
00957 /********************************************************************************************
00958 
00959 >   DocView *Operation::GetWorkingDocView()
00960 
00961     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00962     Created:    02/13/95
00963     Returns:    The DocView this operation is attached to, or NULL if it is not attached
00964                 to a DocView.
00965     Purpose:    Returns the DocView that is associated with this operation.  This will return
00966                 NULL if the View the operation is attached to is not actually a DocView (e.g.
00967                 it may be a PrintView).  Note that the majority of operations don't care
00968                 what view they work on - most of them are document-based, and cause all
00969                 views attached to a document to be updated.  However, some operations such
00970                 as zooming, changing the quality etc. only work on a particular view.
00971     SeeAlso:    Operation::GetWorkingView; Operation::GetWorkingDoc
00972 
00973 ********************************************************************************************/
00974 
00975 DocView *Operation::GetWorkingDocView()
00976 {
00977     // Return the DocView if we are attached to one, otherwise just return NULL.
00978     if (pOurView->IS_KIND_OF(DocView))
00979         return (DocView *) pOurView;
00980     else
00981         return NULL;
00982 }
00983 
00984 /********************************************************************************************
00985 
00986 >   UINT32 Operation::GetSize()
00987 
00988     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00989     Created:    23/7/93
00990     Inputs:     -
00991     Outputs:    -
00992     Returns:    The size of the current operation
00993     Purpose:    To calculate the size of the operation, i.e the sum of the sizes of the 
00994                 operation's actions. 
00995     Errors:     An assertion failure will occur if the operation's UNDO and REDO action 
00996                 lists both contain actions. 
00997     SeeAlso:    -
00998 
00999 ********************************************************************************************/
01000 
01001 UINT32 Operation::GetSize()
01002 {
01003     UINT32 TotalOfActionSizes = 0;   
01004 
01005 //  Do we really need this ensure? It appears to run fine without it (sjk 29/3/00)
01006 //  ENSURE((RedoActions.IsEmpty() || UndoActions.IsEmpty()), 
01007 //      "An operation's UNDO and REDO action lists both contained actions");                                                        
01008                      
01009     // Calculate size of all Undo actions  
01010     
01011     // Get first action of operation                          
01012     ListItem* pAc = (UndoActions.GetHead());  
01013         
01014     while (pAc != NULL) // For each action
01015     {  
01016         TotalOfActionSizes += ((Action*)pAc)->GetSize(); 
01017         pAc = UndoActions.GetNext(pAc);   
01018     }  
01019                     
01020     // Calculate size of all Redo actions     
01021     
01022     pAc = (RedoActions.GetHead());  
01023         
01024     while (pAc != NULL) // For each action
01025     {  
01026         TotalOfActionSizes += ((Action*)pAc)->GetSize(); 
01027         pAc = RedoActions.GetNext(pAc);   
01028     }  
01029 
01030     // Calculate size of all Smart actions                  
01031     
01032     pAc = (SmartActions.GetHead());  
01033         
01034     while (pAc != NULL) // For each action
01035     {  
01036         TotalOfActionSizes += ((Action*)pAc)->GetSize(); 
01037         pAc = SmartActions.GetNext(pAc);   
01038     } 
01039     
01040     return (TotalOfActionSizes);  
01041 }
01042 
01043 
01044 /********************************************************************************************
01045 
01046 >   static Operation* Operation::GetCurrentDragOp()
01047 
01048     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01049     Created:    5/7/93
01050     Inputs:     -
01051     Outputs:    -
01052     Returns:    IF there is  a drag going on THEN
01053                     a pointer to the operation involved in the drag is returned
01054                 ELSE
01055                     NULL is returned 
01056     
01057     Purpose:    To check if there is a drag currently going on, and if there is to return 
01058                 the operation involved in the drag. 
01059     Errors:     -
01060     SeeAlso:    -
01061 
01062 ********************************************************************************************/
01063 
01064 Operation* Operation::GetCurrentDragOp()
01065 {   
01066     return (CurrentDragOp); 
01067 }                                                      
01068 
01069 
01070 
01071 
01072 /********************************************************************************************
01073 
01074 >   static BOOL Operation::GetQuickRender(Node* pNode = NULL)
01075 
01076     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01077     Created:    29/10/2004
01078     Inputs:     -
01079     Outputs:    -
01080     Returns:    TRUE if quick rendering is required at the moment
01081                 FALSE otherwise
01082     Purpose:    Gets the QuickRender state (composed of CurrentDragOp or quick render flag)
01083     Errors:     -
01084     SeeAlso:    SetQuickRender
01085 
01086 ********************************************************************************************/
01087 
01088 BOOL Operation::GetQuickRender(Node* pNode)
01089 {
01090     if (pNode==NULL)
01091         return (CurrentDragOp!=NULL || s_bQuickRender);
01092     else
01093         return ((CurrentDragOp!=NULL && pNode->IsDragged()) || s_bQuickRender);
01094 }                                                      
01095 
01096 
01097 
01098 
01099 /********************************************************************************************
01100 
01101 >   static void Operation::SetQuickRender(BOOL bNewState, Operation* pQROp)
01102 
01103     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01104     Created:    29/10/2004
01105     Inputs:     bNewState - new state for global, static "QuickRender" flag
01106     Outputs:    -
01107     Returns:    -
01108     Purpose:    Sets the QuickRender flag
01109     Errors:     -
01110     SeeAlso:    GetQuickRender
01111 
01112 ********************************************************************************************/
01113 
01114 void Operation::SetQuickRender(BOOL bNewState, Operation* pQROp)
01115 {   
01116     if (s_bQuickRender && !bNewState)
01117     {
01118         // Flush any pending renders now, before we reset the quickrender flag
01119         DocView* pDocView = DocView::GetCurrent();
01120         if (pDocView)
01121         {
01122             pDocView->FlushRedraw();
01123             GetApplication()->ServiceRendering(TRUE);
01124         }
01125     }
01126 
01127     // Now reset the flag
01128     s_bQuickRender = bNewState;
01129     if (bNewState && CurrentDragOp==NULL)
01130         CurrentDragOp = pQROp;
01131     if (!bNewState && CurrentDragOp==pQROp)
01132         CurrentDragOp = NULL;
01133 }                                                      
01134 
01135 
01136 
01137 
01138 // ------------------------------------------------------------------------------------------
01139 // The following functions are called by DocView
01140 
01141 /********************************************************************************************
01142 
01143     virtual void Operation::DragPointerMove( DocCoord PointerPos,
01144                                              ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
01145 
01146     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01147     Created:    5/7/93
01148     Inputs:     PointerPos: Position of the mouse pointer
01149                 ClickMods:  Click modifiers
01150     Outputs:    -
01151     Returns:    -
01152     Purpose:    Pure virtual function which tells the operation that the mouse has moved. 
01153     Errors:     -
01154     SeeAlso:    ClickModifiers
01155 
01156 ********************************************************************************************/
01157 
01158 void Operation::DragPointerMove( DocCoord PointerPos,
01159                         ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
01160 {
01161 }  
01162 
01163 /********************************************************************************************
01164 
01165     virtual void Operation::DragPointerIdle( DocCoord PointerPos,
01166                                              ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
01167 
01168     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01169     Created:    5/7/93
01170     Inputs:     PointerPos: Position of the mouse pointer
01171                 ClickMods:  Click modifiers
01172     Outputs:    -
01173     Returns:    -
01174     Purpose:    Pure virtual function which tells the operation that nothing is going on
01175                 so that it has time to do background jobs. 
01176     Errors:     -
01177     SeeAlso:    ClickModifiers
01178 
01179 ********************************************************************************************/
01180 
01181 
01182 void Operation::DragPointerIdle( DocCoord PointerPos,
01183                         ClickModifiers ClickMods, Spread *pSpread, BOOL bSolidDrag)
01184 {
01185 }       
01186 
01187 /********************************************************************************************
01188 
01189     virtual void Operation::DragFinished( DocCoord PointerPos,
01190                                           ClickModifiers ClickMods, 
01191                                           Spread *pSpread,
01192                                           BOOL Success,
01193                                           BOOL bSolidDrag)
01194 
01195     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01196     Created:    5/7/93
01197     Inputs:     PointerPos: Position of the mouse pointer
01198                 ClickMods:  Click modifiers      
01199                 Success:    Indicates if the drag was terminated by the user pressing escape
01200                             (FALSE) or by the user releasing the mouse buttons (TRUE). 
01201     Outputs:    -
01202     Returns:    -
01203     Purpose:    Pure virtual function which tells the operation that a drag has finished. 
01204                 Once the operation knows the drag has finished it should call EndDrag() 
01205                 to terminate the drag. 
01206                  
01207     Errors:     -
01208     SeeAlso:    ClickModifiers
01209 
01210 ********************************************************************************************/
01211 
01212 void Operation::DragFinished( DocCoord PointerPos,
01213                               ClickModifiers ClickMods,
01214                               Spread *pSpread,
01215                               BOOL Success,
01216                               BOOL bSolidDrag)
01217 {
01218 }    
01219 
01220 
01221 
01222 /********************************************************************************************
01223 >   virtual BOOL Operation::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag)
01224 
01225     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01226     Created:    6/10/94
01227     Inputs:     A pointer to the key-press event.
01228     Outputs:    -
01229     Returns:    TRUE if the Operation handles the key-press, FALSE if it isn't interested.
01230     Purpose:    Keyboard interface for operations.
01231     Errors:     -
01232     SeeAlso:    -
01233 ********************************************************************************************/
01234 
01235 BOOL Operation::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag)
01236 {
01237     return FALSE;
01238 }
01239 
01240 
01241 
01242 /********************************************************************************************
01243 >   virtual void Operation::DragModeChanged(BOOL bSolidDrag)
01244 
01245     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01246     Created:    12/12/2003
01247     Inputs:     Drag mode flag
01248     Outputs:    -
01249     Returns:    -
01250     Purpose:    Notify drag handler of change in drag mode.
01251     Errors:     -
01252     SeeAlso:    -
01253 ********************************************************************************************/
01254 
01255 void Operation::DragModeChanged(BOOL bSolidDrag)
01256 {
01257 }
01258 
01259 
01260 
01261 /********************************************************************************************
01262 
01263 >   DocCoord Operation::MakeRelativeToSpread(Spread *Dest, Spread *Src, DocCoord Pos)
01264 
01265     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01266     Created:    15/12/93
01267     Inputs:     Dest - the spread that the coord should be relative to.
01268                 Src - the spread that the coord is currently relative to.
01269                 Pos - the coord to convert.
01270     Returns:    The coordinate (now relative to Dest)_
01271     Purpose:    This function converts a coordinate relative to one spread so that it is
01272                 then relative to another spread.
01273 
01274 ********************************************************************************************/
01275 
01276 DocCoord Operation::MakeRelativeToSpread(Spread *Dest, Spread *Src, DocCoord Pos)
01277 {
01278     // Translate the Spread Coord 'Pos' into Document coordinates, and thence to logical coords
01279     Src->SpreadCoordToDocCoord(&Pos);
01280     WorkCoord LogicalPos = Pos.ToLogical(Src, GetWorkingView());
01281 
01282     // and get the dest spread origin in logical coords
01283     DocCoord DestSpreadOrigin = Dest->GetSpreadCoordOrigin(TRUE, GetWorkingView());
01284     WorkCoord LogicalDest = DestSpreadOrigin.ToLogical(Dest, GetWorkingView());
01285 
01286     // Work out the difference between the two
01287     XLONG OffsetX = LogicalPos.x - LogicalDest.x;
01288     XLONG OffsetY = LogicalPos.y - LogicalDest.y;
01289     DocCoord Result( OffsetX, OffsetY );
01290 
01291     // and return the result
01292     return Result;
01293 }
01294 
01295 
01296 
01297 /********************************************************************************************
01298 
01299 >   void Operation::RenderDragBlobs(DocRect, Spread*, BOOL bSolidDrag)
01300 
01301     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01302     Created:    18/8/93
01303     Purpose:    This function is called by other parts of the operation to put on
01304                 and take off drag EORing. It will also be called as part of the
01305                 main redraw loop in the case where part of the window has become
01306                 invalid (due to scrolling maybe). The function should contain a
01307                 RenderOnTop loop as described in DocView::RenderOnTop.
01308                 This function can make no assumptions about where it is being called
01309                 from.
01310     SeeAlso:    DocView::RenderOnTop
01311 
01312 ********************************************************************************************/
01313 
01314 void Operation::RenderDragBlobs(DocRect, Spread*, BOOL bSolidDrag)
01315 {
01316     // This function will do nothing. It should exist in derived classes of those
01317     // operations that want to render things during a drag.
01318 }
01319 
01320 //-------------------------------------------------------------------------------------------
01321 
01322 
01323 
01324 
01325 
01326 // ------------------------------------------------------------------------------------------
01327 // These functions tell the Op what to do when it ends.
01328 
01329 /********************************************************************************************
01330 
01331 >   void Operation::FailAndExecute()
01332 
01333     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01334     Created:    30/6/93
01335     Inputs:     -
01336     Outputs:    -
01337     Returns:    -
01338     Purpose:    This fail function will cause the following to occur when the operation ends
01339     
01340                 The operation will be removed from the live list. 
01341                 All actions in the operations action list will be executed (latest first)  
01342                 The operation will be deleted without being added to the document's operation 
01343                 history. 
01344 
01345                 
01346                 
01347     Errors:     -  
01348     SeeAlso:    Operation::FailAndExecuteAllButLast
01349     SeeAlso:    Operation::FailAndDiscard
01350 
01351 
01352 ********************************************************************************************/
01353 
01354 void Operation::FailAndExecute()
01355 {           
01356     OpFlags.Failed = TRUE;     
01357     OpFlags.ExecuteOnEnd = TRUE;      
01358 }
01359 
01360 void Operation::FailAndExecuteIgnoreSelActions()
01361 {           
01362     OpFlags.Failed = TRUE;
01363     OpFlags.ExecuteOnEnd = TRUE;
01364     OpFlags.IgnoreSelectActions = TRUE;
01365 }
01366 
01367 /********************************************************************************************
01368 
01369 >   void Operation::FailAndExecuteAllButLast()
01370 
01371     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01372     Created:    30/6/93
01373     Inputs:     -
01374     Outputs:    -
01375     Returns:    -
01376     Purpose:    This fail function will cause the following to occur when the operation ends
01377     
01378                 The operation will be removed from the live list
01379                 All but the last action in the operation's action list will be executed 
01380                     (latest first) 
01381                 The operation will be deleted without being added to the document's 
01382                 operation history.   
01383                 
01384     Errors:
01385     
01386     SeeAlso:    Operation::FailAndExecute
01387     SeeAlso:    OPeration::FailAndDiscard
01388 
01389 ********************************************************************************************/
01390 
01391 void Operation::FailAndExecuteAllButLast()
01392 {                    
01393     OpFlags.Failed = TRUE;           
01394     OpFlags.ExecuteOnEnd = TRUE; 
01395     OpFlags.AllButLast = TRUE; 
01396 }                                           
01397 
01398 /********************************************************************************************
01399 
01400 >   void Operation::FailAndDiscard()
01401 
01402     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01403     Created:    30/6/93
01404     Inputs:     -
01405     Outputs:    -
01406     Returns:    -   
01407     Purpose:    This fail function will cause the following to occur when the operation ends
01408     
01409                 The operation will be removed from the live list
01410                 The operation will be deleted without being added to the document's 
01411                 operation history.     
01412                 
01413                 Note: This  fail function can be called several times on the same operation,
01414                       however only the first call will have any effect on the operation's
01415                       flags.   
01416     Errors:
01417     
01418     SeeAlso:    Operation::FailAndExecute
01419     SeeAlso:    OPeration::FailAndExecuteAllButLast
01420 
01421 ********************************************************************************************/
01422 
01423 
01424 void Operation::FailAndDiscard()
01425 {   
01426     // Because the fail functions can be called several times on the same operation their 
01427     // implementation has to ensure that only the flags set by the FIRST call are used
01428     
01429     if (!OpFlags.Failed)
01430     {
01431         OpFlags.Failed = TRUE;   
01432     }                            
01433 }                                                            
01434 
01435 /********************************************************************************************
01436 
01437 >   void Operation::SucceedAndDiscard()
01438 
01439     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01440     Created:    03/05/95
01441     Inputs:     -
01442     Outputs:    -
01443     Returns:    -   
01444     Purpose:    This function can be called to discard the operation when it ends. 
01445                 Any hidden nodes which have been generated will remain
01446                 in the tree.            
01447     Errors:
01448     
01449     SeeAlso:    Operation::FailAndExecute
01450     SeeAlso:    OPeration::FailAndExecuteAllButLast
01451 
01452 ********************************************************************************************/
01453 
01454 void Operation::SucceedAndDiscard()
01455 {
01456     OpFlags.SucceedAndDiscard = TRUE; 
01457 }
01458 
01459 
01460 //------------------------------------------------------------------------------------------
01461 
01462 // All subclasses should provide a function named "Do" which executes 
01463 // their primary function. It is not supplied in the base class at 
01464 // the moment but might be one day!
01465 //  void Do()
01466 
01467 // These two functions execute the appropriate action list.
01468 // They should NOT be overridden by the subclasses of Operation.   
01469                                         
01470 /********************************************************************************************
01471 
01472 >   BOOL Operation::Undo()
01473 
01474     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01475     Created:    5/7/93
01476     Inputs:     -
01477     Outputs:    -
01478     Returns:    TRUE if the operation was undone without failing
01479                 false otherwise 
01480     
01481     Purpose:    To execute the operation's undo action list
01482     Errors:     An assertion failure will occur if the UNDO action list is empty 
01483     SeeAlso:    Operation::Redo
01484 
01485 ********************************************************************************************/
01486 
01487 BOOL Operation::Undo()
01488 {           
01489     ERROR2IF(UndoActions.IsEmpty(), FALSE, "Trying to execute an empty UNDO action list");
01490 
01491     OpStatus = UNDO;
01492     UndoActions.ExecuteBackwards(FALSE);  
01493     BOOL fSuccess = !OpFlags.Failed;
01494 PORTNOTE("other","Removed OpChangeBarProperty usage")
01495 #ifndef EXCLUDE_FROM_XARALX
01496     if (fSuccess)
01497     {
01498         NameGallery* pNameGallery = NameGallery::Instance();
01499         if (pNameGallery)
01500         {
01501             pNameGallery->PreTriggerEdit((UndoableOperation*) this, 0, (Node*) 0);
01502             if (IS_A(this, OpChangeBarProperty))
01503                 pNameGallery->m_TouchedBar = ((OpChangeBarProperty*) this)->m_BarIndex;
01504         }
01505     }
01506 #endif
01507 
01508     End(); // End the operation properly, remember that this may delete the operation 
01509     return fSuccess; 
01510 }               
01511 
01512 
01513 
01514 /********************************************************************************************
01515 
01516 >   BOOL Operation::Redo()
01517 
01518     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01519     Created:    5/7/93
01520     Inputs:     -
01521     Outputs:    -
01522     Returns:    TRUE if the operation was redone without failing
01523                 FALSE otherwise 
01524                 
01525     Purpose:    To execute the operation's redo action list
01526     Errors:     An assertion failure will occur if the redo action list is empty 
01527     SeeAlso:    Operation::Undo
01528 
01529 ********************************************************************************************/
01530 
01531 BOOL Operation::Redo()
01532 {                         
01533     ERROR2IF(RedoActions.IsEmpty(), FALSE, "Trying to execute an empty REDO action list");
01534 
01535     OpStatus = REDO;
01536     RedoActions.ExecuteBackwards(FALSE);  
01537     BOOL fSuccess = !OpFlags.Failed; 
01538     if (fSuccess)
01539     {
01540 PORTNOTE("other","Removed OpChangeBarProperty usage")
01541 #ifndef EXCLUDE_FROM_XARALX
01542         NameGallery* pNameGallery = NameGallery::Instance();
01543         if (pNameGallery != 0)
01544         {
01545             pNameGallery->PreTriggerEdit((UndoableOperation*) this, 0, (Node*) 0);
01546             if (IS_A(this, OpChangeBarProperty))
01547                 pNameGallery->m_TouchedBar = ((OpChangeBarProperty*) this)->m_BarIndex;
01548         }
01549 #endif
01550     }
01551 
01552     End();  
01553     return fSuccess;
01554 }
01555 
01556 
01557 
01558 // This function asks the operation to Do itself again. Notice that DoSmart 
01559 // can be polymorphic because no smart functions can take params - they 
01560 // all work on the selection.
01561 void Operation::DoSmart()
01562 {
01563     // Empty.
01564 }  
01565 
01566 
01567 
01568 // Every operation must provide a virtual Do function which gets called to initiate an 
01569 // an operation. 
01570 
01571 /********************************************************************************************
01572 
01573 >   virtual void Operation::Do(OpDescriptor* OpDesc)  
01574 
01575     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01576     Created:    9/6/94
01577     Inputs:     OpDesc: A pointer to the OpDescriptor which invoked the operation
01578     Outputs:    -
01579     Returns:    -
01580     Purpose:    This function should be over-ridden to perform an operation without 
01581                 passing a parameter
01582     Errors:     -
01583     SeeAlso:    Operation::DoWithParam
01584 
01585 ********************************************************************************************/
01586 
01587 void Operation::Do(OpDescriptor*)
01588 {
01589     // Empty.
01590 }   
01591 
01592 /********************************************************************************************
01593 
01594 >   void Operation::DoWithParam(OpDescriptor*, OpParam* pOpParam)
01595 
01596     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01597     Created:    9/6/94
01598     Inputs:     OpDesc:     A pointer to the OpDescriptor which invoked the operation
01599                 pOpParam:   Optional operator parameters, these are similar to a 
01600                             window procedures (lParam, wParam). Each operation requiring
01601                             parameters will describe what these should be. 
01602     Outputs:    -
01603     Returns:    -
01604 
01605     Purpose:    This function should be over-ridden to perform an operation which requires
01606                 parameters.
01607 
01608     Errors:     -
01609     SeeAlso:    Operation::Do
01610 
01611 ********************************************************************************************/
01612 
01613 void Operation::DoWithParam(OpDescriptor*, OpParam* pOpParam)
01614 {
01615     // empty        
01616 }   
01617                     
01618 /********************************************************************************************
01619 
01620 >   OpFlgsStr Operation::GetOpFlgs()
01621 
01622     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01623     Created:    6/7/93
01624     Inputs:     -
01625     Outputs:    -
01626     Returns:    Operation's flags
01627     Purpose:    For finding the status of the operations flags
01628     Errors:     -
01629     SeeAlso:    -
01630 
01631 ********************************************************************************************/
01632 
01633 OpFlgsStr Operation::GetOpFlgs()
01634 {
01635     return (OpFlags); 
01636 }   
01637      
01638 
01639 
01640 
01641 /********************************************************************************************
01642 
01643 >   void Operation::DeleteOnEnd()
01644 
01645     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01646     Created:    14/9/96
01647     Inputs:     -
01648     Outputs:    -
01649     Returns:    -
01650     Purpose:    Sets the DeleteOnEnd OpFlag telling the operation to delete itself at
01651                 the very end of EndOp. This is used in place of calling delete(this) for
01652                 safety reasons.
01653     Errors:     -
01654     SeeAlso:    -
01655 
01656 ********************************************************************************************/
01657 
01658 void Operation::DeleteOnEnd()
01659 {
01660     ERROR3IF(this==NULL, "DeleteOnEnd passed NULL this");
01661 
01662     OpFlags.DeleteOnEnd = TRUE;
01663 }   
01664      
01665 
01666 
01667 
01668 /********************************************************************************************
01669 
01670 >   ActionList* Operation::GetUndoActionList()
01671 
01672     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01673     Created:    22/7/93
01674     Inputs:     -
01675     Outputs:    -
01676     Returns:    A pointer to the operations UNDO action list
01677     Purpose:    To find the UNDO action list of the operation.
01678     Errors:     -
01679     SeeAlso:    -
01680 
01681 ********************************************************************************************/
01682 
01683 ActionList* Operation::GetUndoActionList()
01684 {
01685     return (&UndoActions); 
01686 }    
01687 
01688 /********************************************************************************************
01689 
01690 >   ActionList* Operation::GetRedoActionList()
01691 
01692     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01693     Created:    22/7/93
01694     Inputs:     -
01695     Outputs:    -
01696     Returns:    A pointer to the operations REDO action list
01697     Purpose:    To find the REDO action list of the operation.
01698     Errors:     -
01699     SeeAlso:    -
01700 
01701 ********************************************************************************************/
01702                  
01703                      
01704 ActionList* Operation::GetRedoActionList() 
01705 {                       
01706     return (&RedoActions); 
01707 }   
01708 
01709 
01710 // Functions called by subclasses of Operation...  
01711 
01712 /********************************************************************************************
01713 
01714 >   BOOL Operation::StartDrag(DragType type, 
01715                               DocRect* MoveBBox, DocCoord* StartPos,
01716                               BOOL KeepAccuracy = TRUE)
01717 
01718     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01719     Created:    5/7/93
01720     Inputs:     type: The drag type
01721                 KeepAccuracy - TRUE (default) if you want the drag's response to be biased
01722                                     to accurately track the user's mouse movements.
01723                               FALSE if you want the drag's response to be biased to be as
01724                                     up to date with the current mouse position as possible,
01725                                     at the expense of losing out on some intermediate
01726                                     mouse movements.
01727                 bSolidDragSupported
01728                             - FALSE (default) Do EORed outline dragging
01729                               TRUE  Update the tree during dragging
01730     Outputs:    - 
01731     Returns:    TRUE if the drag was started successfully otherwise FALSE. 
01732     Purpose:    To start a drag 
01733     Errors:     -    
01734     Scope:      protected
01735     SeeAlso:    -
01736 
01737 ********************************************************************************************/
01738 
01739 BOOL Operation::StartDrag(DragType type,
01740                           DocRect* MoveBBox,
01741                           DocCoord* StartPos,
01742                           BOOL KeepAccuracy,
01743                           BOOL bSolidDragSupported /* = FALSE*/)
01744 {                         
01745     if (CurrentDragOp == NULL)  // Check if any other operations are currently performing a 
01746                                 // drag
01747     {                           
01748         // No other operations are performing a drag 
01749         
01750         // Tell the OS to start dragging
01751         pDraggingDocView = DocView::GetCurrent();
01752         if (pDraggingDocView->StartDrag(this,
01753                                         type,
01754                                         MoveBBox,
01755                                         StartPos,
01756                                         KeepAccuracy,
01757                                         bSolidDragSupported))
01758         {
01759             CurrentDragOp = this;   // Record the operation    
01760 
01761             // I've taken out the ability to switch auto-repeats off during drags (Markn - 19/10/95)
01762             /*
01763             if (KeyboardRepeatOffDuringDrags && !ResetKeyboardSpeed)
01764             {
01765                 // Get the current keyboard repeat rate
01766                 ::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &CurKeyboardSpeed, 0);
01767 
01768                 // Reset the repeat rate to 0 (i.e. no repeat)
01769                 ResetKeyboardSpeed = ::SystemParametersInfo(SPI_SETKEYBOARDSPEED, 0, NULL, 0);
01770             }
01771             */
01772 
01773             return (TRUE);    
01774         } 
01775         else
01776             pDraggingDocView = 0;
01777     }
01778     return (FALSE); 
01779 }
01780 
01781 /********************************************************************************************
01782 
01783 >   BOOL Operation::EndDrag()
01784 
01785     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01786     Created:    5/7/93
01787     Inputs:     -
01788     Outputs:    -
01789     Returns:    TRUE if a drag was successfully ended, otherwise FALSE
01790     Purpose:    To end a drag
01791     Errors:     An assertion failure will occur if EndDrag is called and there is no current
01792                 drag taking place. 
01793     SeeAlso:    -
01794 
01795 ********************************************************************************************/
01796 
01797 BOOL Operation::EndDrag()
01798 {                         
01799     ERROR3IF(CurrentDragOp == NULL, "Trying to end a drag which is not taking place !");    
01800 
01801     // I've taken out the ability to switch auto-repeats off during drags (Markn - 19/10/95)
01802     /*
01803     // If we set the keyboard repeat rate to 0, reset it to the previous setting
01804     if (KeyboardRepeatOffDuringDrags && ResetKeyboardSpeed)
01805     {
01806         ::SystemParametersInfo(SPI_SETKEYBOARDSPEED, CurKeyboardSpeed, NULL, 0);
01807         ResetKeyboardSpeed = FALSE;
01808     }
01809     */
01810 
01811     // Rewritten so it won't blow up on OLE drags.  Keeping the kernel drag running during
01812     // the end of an OLE drag outside Camelot is a very risky business!
01813     BOOL fOK = FALSE;
01814     if (pDraggingDocView)
01815     {
01816         fOK = pDraggingDocView->EndDrag(this);
01817         pDraggingDocView = 0;
01818     }
01819 #ifdef _DEBUG
01820     else
01821     {
01822         ERROR3("No dragging DocView in Operation::EndDrag");
01823     }
01824 #endif
01825 
01826     CurrentDragOp = 0;
01827     return fOK;
01828 
01829 /*
01830     DocView *pDocView = DocView::GetSelected();
01831 
01832     ERROR3IF(pDocView == NULL, "No selected DocView in Operation::EndDrag()");
01833 
01834     if ((pDocView != NULL) && (pDocView->EndDrag(this))) // Tell OS to end drag
01835     {
01836         CurrentDragOp = NULL;  
01837 
01838         return (TRUE);  // Ended drag successfully  
01839     }
01840     else
01841         return (FALSE); // Failed to end the drag (and don't bother resetting CurrentDragOp!?!?)
01842 */
01843 }  
01844 
01845 /********************************************************************************************
01846 
01847 >   virtual void Operation::GetOpName(String_256* OpName)
01848 
01849     Author:     Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com>
01850     Created:    4/10/93
01851     Outputs:    The name of the operation
01852     Purpose:    To get the name of the operation
01853 
01854 ********************************************************************************************/
01855 
01856 void Operation::GetOpName(String_256* OpName)
01857 {                         
01858     OpDescriptor *Operation = OpDescriptor::FindOpDescriptor(this->GetRuntimeClass());
01859     
01860     if (Operation)             
01861         Operation->GetText(OpName, OP_UNDO_TEXT);
01862     
01863 }  
01864 
01865 BOOL Operation::UserWantsToDeleteUndoOps(void)
01866 {
01867     if (!DeleteUndoOpsToMakeSpace)    
01868     {                                     
01869         // Inform the user that there is not enough system memory. Give them the option of 
01870         // trying to make room for the action object by deleting undo records.            
01871         if (InformWarning(_R(IDS_DELETE_UNDO), 
01872                           _R(IDS_OK),          
01873                           _R(IDS_CANCEL)) == 2)  
01874         {   
01875             FailAndExecute(); 
01876             return (FALSE);
01877         }                   
01878     }                                      
01879     DeleteUndoOpsToMakeSpace = TRUE;      
01880     return (TRUE); 
01881 }  
01882 
01883 // Call this function from the Operation's Init method to register the operations OpDescriptor
01884 
01885 /********************************************************************************************
01886 
01887 >   static BOOL Operation::RegisterOpDescriptor(
01888                                                   UINT32 toolID,
01889                                                   UINT32 txID, 
01890                                                   CCRuntimeClass* RuntimeClass,
01891                                                   TCHAR* tok,
01892                                                   pfnGetState gs,
01893                                                   UINT32 helpId = 0,
01894                                                   UINT32 bubbleID = 0,
01895                                                   UINT32 resourceID = 0,
01896                                                   UINT32 controlID = 0,
01897                                                   SystemBarType GroupBarID = SYSTEMBAR_ILLEGAL,
01898                                                   BOOL ReceiveMessages = FALSE, 
01899                                                   BOOL Smart = FALSE, 
01900                                                   BOOL Clean = TRUE,
01901                                                   OpDescriptor* pVertOpDesc = NULL,
01902                                                   UINT32 OneOpenInstID = 0,     
01903                                                   UINT32 AutoStateFlags = 0)
01904 
01905     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01906     Created:    22/3/94
01907     Inputs:
01908                 toolID          : Tool identifier (from which the Module ID can be found)
01909                 txID            : String Resource ID
01910                 RuntimeClass    : The operation's runtime class
01911                 tok,            : pointer to the token string
01912                 gs,             : pointer to the GetState function
01913                 helpId          : help identifier 
01914                 bubbleID        : string resource for bubble help
01915                 resourceID,     : resource ID
01916                 controlID,      : control ID within that resource within that tool
01917                 GroupBarID      : The system bar group this op desc belongs to
01918                 ReceiveMessages : Does the OpDescriptor need to respond to system messages
01919                                 : NOTE ** This MUST be TRUE for ops connected to buttons, or your DO **
01920                                 :      ** function will never be called by a button-press!           **
01921                 Smart           : A smart duplicate operation ?
01922                 Clean           : Does the operation change the document
01923                 pVertOpDesc     : The op desc used to describe this op desc's vertical counterpart
01924                 OneOpenInstID   : When not 0 only one live instance
01925                                   of the operation is permitted, and
01926                                   OneOpenInstID is the string resource
01927                                   ID describing why.This will be useful
01928                                   for 1 open instance dialogs.
01929                 AutoStateFlags  : Flags which indicate conditions when
01930                                   the operation should automatically be
01931                                   greyed/ticked etc..(This cuts down the 
01932                                   number of tests that need to be made in the 
01933                                   GetState function). See Opdesc.h for a description
01934 
01935                                   
01936                   
01937     Outputs:    -
01938     Returns:    TRUE if successful, else ERROR is called and FALSE returned. 
01939 
01940     Purpose:    This function should be called from the operations Init method. It 
01941                 creates an OpDescriptor for the operation
01942 
01943     SeeAlso:    Opdescriptor::OpDescriptor
01944 
01945 ********************************************************************************************/
01946 
01947 
01948 BOOL Operation::RegisterOpDescriptor( 
01949                                      UINT32 toolID,
01950                                      UINT32 txID,
01951                                      CCRuntimeClass* RuntimeClass,
01952                                      TCHAR* tok,
01953                                      pfnGetState gs,
01954                                      UINT32 helpId,
01955                                      UINT32 bubbleID,
01956                                      UINT32 resourceID,
01957                                      UINT32 controlID,
01958                                      SystemBarType GroupBarID,
01959                                      BOOL ReceiveMessages,
01960                                      BOOL Smart,
01961                                      BOOL Clean,
01962                                      OpDescriptor* pVertOpDesc,
01963                                      UINT32 OneOpenInstID,      
01964                                      UINT32 AutoStateFlags,
01965                                      BOOL fCheckable /*= FALSE*/ )
01966 {
01967     BOOL ok = TRUE;
01968 
01969     // tok is going into a string which turns out to be a string 32, so do a sanity check (Neville 26/6/97)
01970     size_t len = camStrclen(tok);
01971     ERROR2IF(len > 32,FALSE,"Operation::RegisterOpDescriptor token buffer overflow!");
01972 
01973     // Try to create the OpDescriptor
01974     OpDescriptor* pHorzOpDesc = new OpDescriptor(
01975                                             toolID,
01976                                             txID,
01977                                             RuntimeClass, 
01978                                             tok,
01979                                             gs,
01980                                             helpId,
01981                                             bubbleID,
01982                                             resourceID,
01983                                             controlID,
01984                                             ReceiveMessages, 
01985                                             Smart,
01986                                             Clean,
01987                                             OneOpenInstID,
01988                                             AutoStateFlags,
01989                                             fCheckable
01990                                            );
01991 
01992     ok = (pHorzOpDesc != NULL);
01993 
01994 #if !defined(EXCLUDE_FROM_RALPH) && !defined(EXCLUDE_FROM_XARALX)
01995     // If a hidden group bar has been specified try to connect the new OpDescriptor to
01996     // a group bar...
01997     if (GroupBarID && ok)
01998     {
01999         ok = SystemBarOp::Connect(GroupBarID,pHorzOpDesc,pVertOpDesc);
02000     }
02001 #endif
02002 
02003     ERRORIF(!pHorzOpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
02004     return TRUE; 
02005 }  
02006 
02007 
02008 void Operation::OperationMemoryFailure()
02009 {
02010     
02011     // Inform the user that the operation has failed                        
02012     InformWarning(_R(IDS_UNDO_MEMORY_FAILURE),                                  
02013                   _R(IDS_OK));                                              
02014     FailAndExecute();                                                       
02015 }        
02016  
02017 
02018 
02019 /********************************************************************************************
02020 
02021 >   virtual BOOL Operation::OnIdleEvent()
02022 
02023     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02024     Created:    15/09/94
02025     Outputs:    TRUE if more idle events are required by the op
02026                 FALSE if it dosen't.
02027     Purpose:    Called on idle events if an operation has tegistered to recieve
02028                 them.
02029     SeeAlso:    Application::RegisterIdleProcessor, Application::RemoveIdleProcessor
02030 ********************************************************************************************/
02031 BOOL Operation::OnIdleEvent()
02032 {
02033     return FALSE;
02034 }
02035 
02036 
02037 /********************************************************************************************
02038 >   BOOL Operation::GetStatusLineText(String_256* pText, Spread* pSpread, DocCoord DocPos, ClickModifiers Mods)
02039 
02040     Author:     Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
02041     Created:    16/1/95
02042     Inputs:     pSpread - pioter to spread mouse is over
02043                 DocPos  - position of mouse in doc
02044                 Mods    - click modifiers
02045     Outputs:    pText   - buffer for StatusLine text
02046     Returns:    FALSE if fails
02047     Purpose:    Each DragOp should override this function to return valid status line text
02048 ********************************************************************************************/
02049 
02050 BOOL Operation::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
02051 {
02052     *ptext="";      // defaults to a null string
02053     return TRUE;
02054 }
02055 
02056 
02057 /********************************************************************************************
02058 
02059 >   virtual BOOL Operation::UpdateChangedNodes(ObjChangeParam* pParam,Spread* pSpread = NULL)
02060 
02061     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02062     Created:    6/02/95
02063     Inputs:     pParam  = describes the way an op has changes the node(s)
02064                 pSpread = ptr to spread that contains the nodes that were effected by the op
02065                           NULL means use selected spread
02066     Outputs:    -
02067     Returns:    TRUE if all effected nodes were able to cope with the op, FALSE otherwise
02068     Purpose:    This calls OnChildChange() on all nodes that were effected by the op.
02069                 This includes all parents of all effect nodes too.
02070                 It calls the effected nodes in a depth-first traversal.
02071 
02072                 Note:   All nodes that were asked if the op could happen to them (via AllowOp()) will have
02073                         their op permission states set to PERMISSION_UNDEFINED.  This includes all nodes
02074                         that said no (ie. op permission == PERMISSION_DENIED) plus their parents.
02075 
02076                         It is specified that even if this func returns FALSE, all nodes that were asked
02077                         (i.e. all nodes that have an op permission state of either PERMISSION_ALLOWED or 
02078                         PERMISSION_DENIED) will have their permission state reset to PERMISSION_UNDEFINED.
02079 
02080                 This function updates all nodes on the spread's layers.  Only effected nodes up to the layer level have 
02081                 their OnChildChange() function called.
02082 
02083                 EXCEPTION:  If pParam's ObjChangeType is OBJCHANGE_IGNORE, the effected nodes do NOT have their
02084                             OnChildChange() param called, but everything else applies, i.e. all effected nodes will
02085                             have their op permission state reset to PERMISSION_UNDEFINED.
02086                             This OBJCHANGE_IGNORE type is used by Operation::End() so that it can ensure the tree
02087                             is in a safe state, even if an op fails, and/or an op is not implemented correctly.
02088 
02089     SeeAlso:    Node::AllowOp(),Node::GetOpPermission(),Node::SetOpPermission()
02090 
02091 ********************************************************************************************/
02092 
02093 BOOL Operation::UpdateChangedNodes(ObjChangeParam* pParam,Spread* pSpread)
02094 {
02095     ERROR2IF(pParam == NULL,FALSE,"pParam == NULL");
02096     if (pSpread == NULL) pSpread = Document::GetSelectedSpread();
02097     //ERROR2IF(pSpread == NULL,FALSE,"Can't find a spread to work on");
02098 
02099     if (pSpread == NULL)
02100         return TRUE;
02101 
02102     // We need to invalidate the sel range just in case it's been cached in the middle of an op.
02103     // If the sel range is cached when nodes in the tree have op permission state that's PERMISSION_DENIED
02104     // then nodes that should potentially be included in the range will be excluded.
02105     // And as the thing's cached all hell breaks loose, so yet another independent system has to know
02106     // about sel ranges and the fact they're cached, and yes it does scare me.
02107 //  SelRange* pRange = GetApplication()->FindSelection();
02108 //  if (pRange != NULL)
02109 //      pRange->Update(FALSE,NULL);
02110 
02111     return UpdateChangedNode(pParam,pSpread->FindFirstChild());
02112 }
02113 
02114 /********************************************************************************************
02115 
02116 >   BOOL Operation::UpdateChangedNode(ObjChangeParam* pParam,Node* pNode)
02117 
02118     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02119     Created:    6/02/95
02120     Inputs:     pParam  = describes the way an op has changes the node(s)
02121                 pNode   = subtree root of nodes to update (Layer being top-level type)
02122     Outputs:    -
02123     Returns:    TRUE if all effected nodes were able to cope with the op, FALSE otherwise
02124     Purpose:    Helper function for UpdateChangedNodes() (which calls this for each layer in a spread).
02125                 This will call itself recursively for all children, to ensure a depth-first traversal
02126                 of the tree.
02127 
02128     SeeAlso:    UpdateChangedNodes()
02129 
02130 ********************************************************************************************/
02131 
02132 BOOL Operation::UpdateChangedNode(ObjChangeParam* pParam,Node* pNode)
02133 {
02134     if (pNode == NULL)  return TRUE;
02135     if (pParam == NULL) return FALSE;
02136 
02137     BOOL ok = TRUE;
02138 
02139     while (pNode != NULL)
02140     {
02141         // Get the op permission state for this node
02142         OpPermissionState OpState = pNode->GetOpPermission();
02143 
02144         // if NOT undefined, we are dealing with a node that's been involved in the op, so update children of node
02145         if (OpState != PERMISSION_UNDEFINED)
02146             ok = ok && UpdateChangedNode(pParam,pNode->FindFirstChild());
02147 
02148         // If the node allowed the op to happen, and the change type is NOT one to ignore,
02149         // inform the node via OnChildChange
02150         if (ok && OpState == PERMISSION_ALLOWED && pParam->GetChangeType() != OBJCHANGE_IGNORE)
02151             ok = (pNode->OnChildChange(pParam) == CC_OK);
02152         else
02153         {
02154             // BODGE TEXT - this is HORRIBLE but the safest way to clear flags in text story on failure
02155             if (IS_A(pNode,TextStory))
02156                 ((TextStory*)pNode)->ClearNodeAndDescendantsFlags();
02157         }
02158 
02159         // Set the op permission to undefined, as we've finished with this node now
02160         pNode->SetOpPermission(PERMISSION_UNDEFINED);
02161 
02162         // get the next sibling node
02163         pNode = pNode->FindNext();
02164     }
02165 
02166     return ok;
02167 }
02168 
02169 /********************************************************************************************
02170 
02171 >   BOOL Operation::UpdateChangedNode(ObjChangeParam* pParam,Document* pDoc)
02172 
02173     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02174     Created:    20/02/95
02175     Inputs:     pParam  = describes the way an op has changes the node(s)
02176                 pDoc    = ptr to doc 
02177     Outputs:    -
02178     Returns:    TRUE if all effected nodes were able to cope with the op, FALSE otherwise
02179     Purpose:    This calls the Spread version of this func for all spreads in the given doc 
02180 
02181     SeeAlso:    UpdateChangedNodes(...,Spread* pSpread)
02182 
02183 ********************************************************************************************/
02184 
02185 BOOL Operation::UpdateChangedNodes(ObjChangeParam* pParam,Document* pDoc)
02186 {
02187     ERROR2IF(pParam == NULL,FALSE,"pParam == NULL");
02188     ERROR2IF(pDoc   == NULL,FALSE,"pDoc == NULL");
02189 
02190     BOOL ok = TRUE;
02191     Chapter* pChapter = Node::FindFirstChapter(pDoc);
02192     while (pChapter != NULL && ok)
02193     {
02194         // Now find the first child of the Chapter, because this is the level at which Spreads hang out
02195         Node* pNode = pChapter->FindFirstChild();
02196 
02197         // Update changed nodes in each of the chapter's spreads
02198         while (pNode != NULL && ok)
02199         {
02200             if (pNode->IsSpread())
02201                 ok = UpdateChangedNodes(pParam,(Spread*)pNode);
02202 
02203             pNode = pNode->FindNext();
02204         }
02205 
02206         pChapter = pChapter->FindNextChapter(); 
02207     }
02208 
02209     return ok;
02210 }
02211 
02212 /********************************************************************************************
02213 
02214 >   BOOL Operation::UpdateAllChangedNode(ObjChangeParam* pParam)
02215 
02216     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02217     Created:    20/02/95
02218     Inputs:     pParam  = describes the way an op has changes the node(s)
02219     Outputs:    -
02220     Returns:    TRUE if all effected nodes were able to cope with the op, FALSE otherwise
02221     Purpose:    This calls the Document version of this func for all documents in the application
02222 
02223     SeeAlso:    UpdateChangedNodes(...,Document* pDoc)
02224 
02225 ********************************************************************************************/
02226 
02227 BOOL Operation::UpdateAllChangedNodes(ObjChangeParam* pParam)
02228 {
02229     ERROR2IF(pParam == NULL,FALSE,"pParam == NULL");
02230 
02231     BOOL ok = TRUE;
02232     Document* pDoc = (Document*)GetApplication()->Documents.GetHead();
02233     while (pDoc != NULL && ok)
02234     {
02235         ok   = UpdateChangedNodes(pParam,pDoc);
02236         pDoc = (Document*)GetApplication()->Documents.GetNext(pDoc);
02237     }
02238     return ok;
02239 }
02240 
02241 
02242 
02243 /********************************************************************************************
02244 
02245 >   virtual void Operation::Dump()
02246 
02247     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02248     Created:    15/8/95
02249     Inputs:     -
02250     Outputs:    Displays information on the debugging output
02251     Returns:    -
02252     Purpose:    Displays information on this operation, and its two action lists.  Override to
02253                 display information specific to your operation
02254     SeeAlso:    OperationHistory::DumpAll
02255                 OperationHistory::DumpLast
02256                 Action::Dump
02257 
02258 ********************************************************************************************/
02259 void Operation::Dump()
02260 {
02261 #if DEBUG_TREE
02262     // Display ops name
02263     TRACEALL( _T("\nOperation : %s\n"), GetRuntimeClass()->GetClassName() );
02264 
02265     // Display undo actions 
02266     if (UndoActions.IsEmpty())
02267     {
02268         TRACEALL( _T("    No Undo actions\n") );
02269     }
02270     else
02271     {
02272         TRACEALL( _T("    %d Undo actions\n"), UndoActions.GetCount() );
02273         ListItem* pAction = UndoActions.GetHead();
02274 
02275         while (pAction != NULL)
02276         {
02277             ((Action*)pAction)->Dump();
02278             pAction = UndoActions.GetNext(pAction);
02279         }
02280     }
02281 
02282     // Display redo actions 
02283     if (RedoActions.IsEmpty())
02284     {
02285         TRACEALL( _T("    No Redo actions\n") );
02286     }
02287     else
02288     {
02289         TRACEALL( _T("    %d Redo actions\n"), RedoActions.GetCount() );
02290         ListItem* pAction = RedoActions.GetHead();
02291 
02292         while (pAction != NULL)
02293         {
02294             ((Action*)pAction)->Dump();
02295             pAction = RedoActions.GetNext(pAction);
02296         }
02297     }
02298 #endif
02299 }
02300 
02301 
02302 //------------------------------------------------------------------------------------------
02303 //Action methods  
02304 
02305 //Action* Action::LastDiscardableAction = NULL;    
02306                 
02307 /********************************************************************************************
02308 
02309 >   Action::Action()
02310 
02311     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02312     Created:    6/7/93
02313     Inputs:     -
02314     Outputs:    -
02315     Returns:    -
02316     Purpose:    Action constructor
02317     Errors:     -
02318     SeeAlso:    -
02319 
02320 ********************************************************************************************/
02321 
02322 Action::Action():ListItem()
02323 {
02324 } 
02325 
02326 /********************************************************************************************
02327 
02328 >   virtual Action::~Action()
02329 
02330     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02331     Created:    6/7/93
02332     Inputs:     -
02333     Outputs:    -
02334     Returns:    -
02335     Purpose:    Action destructor
02336     Errors:     -
02337     SeeAlso:    Action::Slaughter
02338 
02339 ********************************************************************************************/
02340 
02341 Action::~Action()
02342 {                                  
02343     //if (IsUserName("Simon"))
02344     //  TRACE( _T("Action being deleted\n"));   
02345 }  
02346              
02347 /********************************************************************************************
02348 
02349 >   virtual Action::Slaughter()
02350 
02351     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02352     Created:    6/7/93
02353     Inputs:     -
02354     Outputs:    -
02355     Returns:    -
02356     Purpose:    destructor which gets called when an operation is deleted  
02357     Errors:     -
02358     SeeAlso:    Action::Slaughter
02359 
02360 ********************************************************************************************/
02361 
02362 void Action::Slaughter() 
02363 {               
02364     delete (this); // Call the destructor 
02365 }   
02366     
02367 /********************************************************************************************
02368 
02369 >   ActionCode Action::Execute()         
02370 
02371     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02372     Created:    5/8/93
02373     Inputs:     -
02374     Outputs:    -
02375     Returns:    -
02376     Purpose:    This is a pure virtual method which should be redefined for all derived
02377                 classes of Action.                                               
02378                 
02379     Errors:     If this method is ever called then an ENSURE failure will occur. 
02380     SeeAlso:    -
02381 
02382 ********************************************************************************************/
02383       
02384 ActionCode Action::Execute()         
02385 {
02386     ENSURE(FALSE, "Calling pure virtual method of abstract class Action");    
02387     return (AC_FAIL); // The function needs to return a value, even though it never will !. 
02388 } 
02389 
02390 /********************************************************************************************
02391 
02392     
02393 >   ActionCode Action::Init(Operation* pOp, 
02394                         ActionList* pActionList, 
02395                         UINT32 ActionSize, 
02396                         CCRuntimeClass* ActionClass, 
02397                         Action** NewAction) 
02398                             
02399     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02400     Created:    6/7/93                      
02401     
02402     Inputs:     pOp: The operation to which the action should be added
02403                 
02404                 pActionList: The action list in the operation object
02405                 
02406                 Size:        The size of the action in bytes. This should be the total  
02407                              size of the action (including any objects pointed to by the
02408                              action). 
02409 
02410     Outputs:    NewAction:   A pointer to the action if it could be allocated. This
02411                              will be NULL if we are unwinding (AC_OK gets returned)
02412 
02413     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
02414                              action and the user did not wish to continue. Usually 
02415                              End() should be called in this situation. 
02416                          
02417                 AC_NORECORD: There was not enough room in the operation history for
02418                              the action, but the user requested that he wished to 
02419                              continue without undo. 
02420 
02421                              Currently AC_RECORD is never returned, as we never 
02422                              fail an operation just because we exceed the max size
02423                              of the operation history !. See code for a reason why
02424                 
02425                 AC_OK      : The action was successfully initialised and added to the 
02426                              operation. OR we are unwinding. In this situation 
02427                              NewAction will be NULL.
02428                          
02429                            
02430     Purpose:    To check that there is sufficient room for the action in the operation
02431                 history, and if there is, then to add the action to the operations 
02432                 action list. 
02433                 
02434     Errors:     -
02435     SeeAlso:    -
02436 
02437 ********************************************************************************************/
02438 
02439 ActionCode Action::Init(Operation* pOp, 
02440                         ActionList* pActionList, 
02441                         UINT32 ActionSize, 
02442                         CCRuntimeClass* ActionClass, 
02443                         Action** NewAction) 
02444                           
02445 {  
02446     
02447     ERROR3IF(!(pOp->IsKindOf(CC_RUNTIME_CLASS(UndoableOperation))), 
02448         "Trying to create an action for a non-undoable operation"); 
02449 
02450     // Update the hour glass
02451     ContinueSlowJob(); 
02452 
02453 //  OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(pOp->GetRuntimeClass()); 
02454                                             
02455     OperationHistory& OpHist = pOp->GetWorkingDoc()->GetOpHistory(); 
02456             
02457     //if (Action::LastDiscardableAction != NULL) 
02458     //{                                          
02459     //  delete (LastDiscardableAction);             // LastDescardable action
02460     //  Action::LastDiscardableAction = NULL; 
02461     //}  
02462  
02463 
02464     // If we are unwinding an operation during Undo or Redo then 
02465     // we don't want to create an action as it will already exist on the oposite action
02466     // list. 
02467     
02468     // When unwinding a Do however things are a little different. Whilst we don't need
02469     // need to generatate Redo actions for redoing purposes, we do need them to tidy up 
02470     // the dying Operation. eg. A ShowNodeAction may need to delete its hidden node, and its
02471     // Slaughter method must be called to acheive this. However if we fail to allocate an 
02472     // action in this circumstance we don't want to return an AC_FAIL. Leaking memory is 
02473     // preferable to having nowhere to go. 
02474 
02475     if (pOp->GetOpFlgs().UnwindingActions && (pOp->OpStatus!=DO || pOp->GetOpFlgs().KeepOnEnd))
02476     {
02477         (*NewAction) = NULL; 
02478         return (AC_OK); 
02479     
02480     }
02481     
02482     
02483     //  TRACE( _T("Size of operation history = %ld\n"), OpHist.GetSize()); 
02484 
02485 
02486     // Try to allocate memory for the action, deleting undo if we get desperate
02487     ALLOC_WITH_FAIL((*NewAction),(Action*)(ActionClass->CreateObject()),pOp);         
02488     
02489     // oh no it's all gone horribly wrong, I wouldn't like to be in your shoes
02490     if ((*NewAction) == NULL)
02491     {
02492         // If we are unwinding at this point then we don't fail the action (See note above)
02493         if ((pOp->GetOpFlgs().UnwindingActions))
02494         {
02495             ERROR3IF(!(pOp->OpStatus == DO), "OpStatus should be Do");
02496             return (AC_OK);  
02497         }
02498         else
02499         {      
02500             return (AC_FAIL);
02501         }
02502     }
02503     //if (IsUserName("Simon"))
02504     //{
02505     //  TRACE( _T("Adding action %s, NumBytes = %lu\n"),(*NewAction)->GetRuntimeClass()->m_lpszClassName, ActionSize);  
02506     //} 
02507 
02508 
02509     // If fail and discard then there is no need to prompt the user more than once
02510     if (!(pOp->GetOpFlgs().Failed))
02511     {                                                
02512         // Check that there is enough room in the operation history for the action
02513         if ((OpHist.GetSize() + ActionSize) > OpHist.GetMaxSize())    
02514         {                      
02515             //BOOL NoRoom = FALSE; 
02516             // There is not enough room so first of all check if we can make room 
02517             if (OpHist.GetMaxSize() > ActionSize) 
02518             {                          
02519                 // See if we can make room by deleting UNDO records
02520                 //if (OpHist.ReduceSize((OpHist.GetMaxSize() - ActionSize),TRUE) == FALSE)
02521                 //  NoRoom = TRUE;
02522                 // The outcome does not matter
02523                 OpHist.ReduceSize((OpHist.GetMaxSize() - ActionSize),TRUE, TRUE);
02524             }
02525             else
02526             {
02527                 // Very big action
02528                 OpHist.ReduceSize(0,TRUE, TRUE);
02529             }                    
02530             // We used to give the user the option of either continuing and loosing all undo/redo
02531             // or have them abort their current operation. I didn't think this was a very good idea
02532             // so I have removed the code. Now we let the operation history grow to what
02533             // ever size it wants to, but keep the Maximum size the same. The operation history
02534             // will shrink towards the maximum size whenever there are undo operations which can be deleted.
02535             
02536             // I found that in practice we were reaching this NoRoom situation when the user was undoing
02537             // almost back to the start. If the user redoes then this shrinks the OpHist 
02538             // because there are undo ops to discard, if the user does then this also shrinks the OpHist because
02539             // all redo ops are discarded. 
02540             
02541             /*
02542             else
02543                 NoRoom = TRUE; 
02544             if (NoRoom)
02545             {   
02546                     
02547                 // We cannot make enough room in the operation history for the action
02548                 UINT32 _R(IDS_MSG) = (pOp->OpStatus == UNDO) ? 
02549                     (UINT32)_R(IDS_CANNOT_REDO_WARNING): (UINT32)_R(IDS_CANNOT_UNDO_WARNING);    
02550                                     
02551                 if (InformWarning(_R(IDS_MSG),
02552                                   _R(IDS_CONTINUE),   
02553                                   _R(IDS_CANCEL)) == 2) 
02554                 {      
02555                     // The user does not want to continue, so inform the action's operation that 
02556                     // when the operation ends the actions previously added to its action list 
02557                     // will be executed to bring the document back to the state it was in before the
02558                     // operation was* started.
02559                     pOp->FailAndExecute();   
02560                     LastDiscardableAction = *NewAction; 
02561                     return (AC_FAIL);  
02562                 }
02563                 else
02564                     // The user does want to continue, so inform the operation that when it ends 
02565                     // it should be deleted. 
02566                     pOp->FailAndDiscard();     
02567                 
02568             }
02569             */
02570         }
02571     }
02572     // Add the action to the operation's action list.        
02573     
02574     // Note that actions are added to the operation's action list even after a fail and discard 
02575     // error.  The reason for this is that if memory runs out whilst trying to execute an operation
02576     // after such an error, we need to be able to unwind all actions which have been performed. If 
02577     // we do not do this then all sorts of chaos will occur. 
02578     
02579     pActionList->AddTail(*NewAction);    
02580     (*NewAction)->pOperation = pOp;            // Record the operation to which the action is attached. 
02581     
02582     // Store a pointer to the opposite ActionList to that which the Action is atatched 
02583     (*NewAction)->pOppositeActLst = (pOp->GetUndoActionList() == pActionList) ? 
02584         (pOp->GetRedoActionList()) : (pOp->GetUndoActionList());
02585           
02586     (*NewAction)->Size = ActionSize;           // Record the size of the action           
02587                                              
02588     // Even though the operation has not yet been added to the operation history and maybe
02589     // never will, we increase the current size of the operation history to accomodate the 
02590     // action. If the operation fails then the Size of the operation history will be reduced 
02591     // by the same ammount in the action list's execute methods. 
02592          
02593     OpHist.IncSize(ActionSize);                                                                     
02594     
02595     return (AC_OK); // success                
02596 }                                     
02597 
02598 /********************************************************************************************
02599 
02600 >   Document *Action::GetWorkingDoc()
02601 
02602     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02603     Created:    02/13/95
02604     Returns:    The document this action is working on.
02605     Purpose:    Find out which document this action is working on.
02606     SeeAlso:    Action::GetWorkingView; Action::GetWorkingDocView
02607 
02608 ********************************************************************************************/
02609 
02610 
02611 /********************************************************************************************
02612 
02613 >   View *Action::GetWorkingView()
02614 
02615     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02616     Created:    02/13/95
02617     Returns:    The View this operation is attached to.
02618     Purpose:    Returns the View that is associated with this action. 
02619                 Note that the majority of actions don't care what view they work on - most
02620                 of them are document-based, and cause all views attached to a document to be
02621                 updated.
02622     SeeAlso:    Action::GetWorkingDocView; Action::GetWorkingDoc
02623 
02624 ********************************************************************************************/
02625 
02626 
02627 /********************************************************************************************
02628 
02629 >   DocView *Action::GetWorkingDocView()
02630 
02631     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02632     Created:    02/13/95
02633     Returns:    The DocView this action is attached to, or NULL if it is not attached
02634                 to a DocView.
02635     Purpose:    Returns the DocView that is associated with this action.  This will return
02636                 NULL if the View the action is attached to is not actually a DocView (e.g.
02637                 it may be a PrintView).  Note that the majority of actions don't care
02638                 what view they work on - most of them are document-based, and cause all
02639                 views attached to a document to be updated.
02640     SeeAlso:    Action::GetWorkingView; Action::GetWorkingDoc
02641 
02642 ********************************************************************************************/
02643                   
02644 /********************************************************************************************
02645 
02646 >   UINT32 Action::GetSize()
02647 
02648     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02649     Created:    22/7/93
02650     Inputs:     -
02651     Outputs:    -
02652     Returns:    The size of the action in bytes
02653     Purpose:    For finding the total size of the action
02654     Errors:     -
02655     SeeAlso:    -
02656 
02657 ********************************************************************************************/
02658 
02659 UINT32 Action::GetSize()
02660 {
02661     return (Size); 
02662 }          
02663 
02664 
02665 
02666 /********************************************************************************************
02667 
02668 >   virtual void Action::Dump()
02669 
02670     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02671     Created:    15/8/95
02672     Inputs:     -
02673     Outputs:    Outputs debugging information to debug display
02674     Returns:    -
02675     Purpose:    Shows debug information on the action.  Override to display information
02676                 specific to your action
02677     Errors:     -
02678     SeeAlso:    Operation::Dump
02679 
02680 ********************************************************************************************/
02681 void Action::Dump()
02682 {
02683 #if DEBUG_TREE
02684     TRACEALL( _T("        Action %s (Size = %d bytes)\n"), GetRuntimeClass()->GetClassName(), GetSize() );
02685 #endif
02686 }
02687 
02688 
02689 
02690 /********************************************************************************************
02691 >   BOOL Action::TransferToOtherOp(Operation* pOtherOp, ActionList* pAddActions, ActionList* pOtherActions)
02692 
02693     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02694     Created:    10/1/96
02695     Inputs:     pOtherOp - points to the operation to add this action to.
02696                 pAddActions - points to the action list to add this action to.
02697                 pOtherAction - points to the other action list.
02698     Outputs:    -
02699     Returns:    -
02700     Purpose:    Adds this action to the end of the specified action list.
02701     SeeAlso:    UndoableOperation::MergeWithPrevious
02702 ********************************************************************************************/
02703 BOOL Action::TransferToOtherOp(Operation* pOtherOp, ActionList* pAddActions, ActionList* pOtherActions)
02704 {
02705     ERROR2IF((pOtherOp==NULL) || (pAddActions==NULL) || (pOtherActions==NULL), FALSE, "NULL parameter");
02706 
02707     pOperation = pOtherOp;
02708     pOppositeActLst = pOtherActions;
02709     
02710     pAddActions->AddTail(this);
02711 
02712     return TRUE;
02713 }
02714 
02715 
02716  
02717 // -----------------------------------------------------------------------------------------
02718 // -----------------------------------------------------------------------------------------
02719 // All general purpose actions here   
02720    
02721  
02722 //------------------------------------------------------------------------------------------
02723 // InvalidateRegionAction methods
02724  
02725 
02726 /********************************************************************************************
02727 
02728 >   InvalidateRegionAction::InvalidateRegionAction()
02729 
02730     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02731     Created:    14/9/93
02732     Inputs:     -
02733     Outputs:    -
02734     Returns:    -
02735     Purpose:    InvalidateRegionAction constructor
02736     Errors:     -
02737     SeeAlso:    -
02738 
02739 ********************************************************************************************/
02740  
02741  
02742 InvalidateRegionAction::InvalidateRegionAction()
02743 {
02744 } 
02745 
02746 
02747 /********************************************************************************************
02748 
02749 >   ActionCode InvalidateRegionAction::Execute()   
02750 
02751     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02752     Created:    16/8/93
02753     Inputs:     -
02754     Outputs:    ActionCode indicating if the action was successfully executed or not
02755     Returns:    -
02756     Purpose:    Executes the InvalidateRegionAction which invalidates a region which is the 
02757                 union of the bounding rectangles of each node in the range. When the 
02758                 includeBlobs flag is TRUE the region which gets invalidated includes the 
02759                 objects blobs.   
02760     Errors:     -
02761     SeeAlso:    -
02762     
02763     
02764 ********************************************************************************************/
02765 
02766 ActionCode InvalidateRegionAction::Execute()
02767 {
02768     InvalidateRegionAction* InvRgnAct;  
02769     ActionCode ActCode;  
02770     // Attempt to initialise the action    
02771     if ((ActCode = InvalidateRegionAction::Init(pOperation,                    
02772                                                 pOppositeActLst,  
02773                                                 NodeRange,
02774                                                 pSpread, 
02775                                                 IncludeBlobs, 
02776                                                 (Action**)(&InvRgnAct))) != AC_FAIL) 
02777         
02778                 
02779     {   
02780         // The action was successfully initialised   
02781 
02782         // Invalidate each nodes bounds 
02783     
02784         Document* pDocument = pOperation->GetWorkingDoc();
02785         ENSURE(pDocument != 0, "There was no current document in InvalidateRegionAction" );
02786         
02787         if (pDocument != NULL)
02788         {
02789             BOOL bOldPTP = NodeRange.SetPromoteToParent(TRUE);
02790             NodeRenderableInk* CurrentNode = (NodeRenderableInk*)NodeRange.FindFirst();
02791 
02792             DocRect InvalidRgn; 
02793         
02794             while (CurrentNode != NULL)
02795             {
02796                 if (CurrentNode->IsAnObject() || CurrentNode->IsPaper())
02797                 {
02798                     // Find the region to invalidate
02799                     InvalidRgn = (IncludeBlobs ? 
02800                                     (((NodeRenderableInk*)CurrentNode)->GetUnionBlobBoundingRect()):
02801                                     (((NodeRenderableInk*)CurrentNode)->GetBoundingRect())
02802                                  ); 
02803 
02804                     pDocument->ForceRedraw(pSpread, InvalidRgn, TRUE, CurrentNode);
02805                 }
02806                 CurrentNode = (NodeRenderableInk*)NodeRange.FindNext(CurrentNode); 
02807             }
02808 
02809             NodeRange.SetPromoteToParent(bOldPTP);
02810         }
02811 
02812     }
02813     return ActCode;
02814 }   
02815 
02816 
02817 /********************************************************************************************
02818 
02819 >   ActionCode InvalidateRegionAction::Init(Operation* const pOp, 
02820                                             ActionList* pActionList,    
02821                                             Range NodeRange,  
02822                                             Spread* pSpread, 
02823                                             BOOL IncludeBlobs,   
02824                                             Action** NewAction) 
02825 
02826     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02827     Created:    14/9/93  
02828     
02829     Inputs:     pOp: The operation to which the action should be added
02830                 
02831                 pActionList: The action list in the operation object
02832                 
02833                 NodeRange:   The range of nodes to be invalidated
02834 
02835                 pSpread:     The ranges spread 
02836                 
02837                 IncludeBlobs: When TRUE invalidate the blob bounding rectangles of the
02838                               nodes in the range.                
02839 
02840     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
02841 
02842     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
02843                              action and the user did not wish to continue. Usually 
02844                              End() should be called in this situation. 
02845                          
02846                 AC_NORECORD: There was not enough room in the operation history for
02847                              the action, but the user requested that he wished to 
02848                              continue without undo. 
02849                 
02850                 AC_OK      : The action was successfully initialised and added to the 
02851                              operation. 
02852                          
02853                            
02854     Purpose:    To check that there is sufficient room for the action in the operation
02855                 history, and if there is, then to add the action to the operations 
02856                 action list. 
02857                 
02858                 The function calls the Action::Init function passing the runtime class 
02859                 of an InvalidateRegionAction. If successful it records the NodeTag.  
02860     Errors:     -
02861     SeeAlso:    Action::Init
02862 
02863 ********************************************************************************************/
02864  
02865 ActionCode InvalidateRegionAction::Init(Operation* const pOp, 
02866                                         ActionList* pActionList,    
02867                                         Range nodeRange, 
02868                                         Spread* pSpread,  
02869                                         BOOL includeBlobs,   
02870                                         Action** NewAction) 
02871 {       
02872     ActionCode Ac = (Action::Init(pOp,
02873                                   pActionList,
02874                                   sizeof(InvalidateRegionAction), 
02875                                   CC_RUNTIME_CLASS(InvalidateRegionAction), 
02876                                   NewAction));
02877     if (*NewAction != NULL)
02878     {
02879         ((InvalidateRegionAction*)(*NewAction))->NodeRange = nodeRange;
02880         ((InvalidateRegionAction*)(*NewAction))->IncludeBlobs = includeBlobs;
02881         ((InvalidateRegionAction*)(*NewAction))->pSpread = pSpread; 
02882     }
02883     return (Ac); 
02884 } 
02885 
02886 /********************************************************************************************
02887 
02888 >   InvalidateRegionIfBgRedrawAction::InvalidateRegionIfBgRedrawAction()
02889 
02890     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02891     Created:    10/5/95
02892     Inputs:     -
02893     Outputs:    -
02894     Returns:    -
02895     Purpose:    InvalidateRegionIfBgRedrawAction constructor
02896     Errors:     -
02897     SeeAlso:    -
02898 
02899 ********************************************************************************************/
02900  
02901  
02902 InvalidateRegionIfBgRedrawAction::InvalidateRegionIfBgRedrawAction()
02903 {
02904 } 
02905 
02906 
02907 /********************************************************************************************
02908 
02909 >   ActionCode InvalidateRegionIfBgRedrawAction::Execute()   
02910 
02911     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02912     Created:    10/5/95
02913     Inputs:     -
02914     Outputs:    ActionCode indicating if the action was successfully executed or not
02915     Returns:    -
02916     Purpose:    Executes the InvalidateRegionAction which invalidates a region which is the 
02917                 union of the bounding rectangles of each node in the range. When the 
02918                 includeBlobs flag is TRUE the region which gets invalidated includes the 
02919                 objects blobs.   
02920     Errors:     -
02921     SeeAlso:    -
02922     
02923     
02924 ********************************************************************************************/
02925 
02926 ActionCode InvalidateRegionIfBgRedrawAction::Execute()
02927 {
02928     InvalidateRegionIfBgRedrawAction* InvRgnAct;  
02929     ActionCode ActCode;  
02930     // Attempt to initialise the action    
02931     if ((ActCode = InvalidateRegionIfBgRedrawAction::Init(pOperation,                    
02932                                                           pOppositeActLst,  
02933                                                           NodeRange,
02934                                                           pSpread, 
02935                                                           IncludeBlobs, 
02936                                                           (Action**)(&InvRgnAct))) != AC_FAIL) 
02937         
02938                 
02939     {   
02940         // The action was successfully initialised   
02941 
02942         if (!GetApplication()->IsBgRendering())
02943             return AC_OK;
02944 
02945 //      GetApplication()->DeleteRenderRegions(DocView::GetSelected());
02946 //      GetApplication()->BgRendering = FALSE;
02947 
02948         // Invalidate each nodes bounds 
02949     
02950         Document* pDocument = pOperation->GetWorkingDoc();
02951         ENSURE(pDocument != 0, "There was no current document in InvalidateRegionAction" );
02952         
02953         if (pDocument != NULL)
02954         {
02955             NodeRenderableInk* CurrentNode = (NodeRenderableInk*)NodeRange.FindFirst();
02956 
02957             DocRect InvalidRgn; 
02958         
02959             while (CurrentNode != NULL)
02960             {
02961                 if (CurrentNode->IsAnObject() || CurrentNode->IsPaper())
02962                 {
02963                     // Find the region to invalidate
02964                     InvalidRgn = (IncludeBlobs ? 
02965                                     (((NodeRenderableInk*)CurrentNode)->GetUnionBlobBoundingRect()):
02966                                     (((NodeRenderableInk*)CurrentNode)->GetBoundingRect())
02967                                  ); 
02968 
02969                     pDocument->ForceRedraw(pSpread, InvalidRgn, TRUE, CurrentNode);
02970                 }
02971                 CurrentNode = (NodeRenderableInk*)NodeRange.FindNext(CurrentNode); 
02972             }
02973         }
02974 
02975     }
02976     return ActCode;
02977 }   
02978 
02979 /********************************************************************************************
02980 
02981 >   ActionCode InvalidateRegionIfBgRedrawAction::Init(Operation* const pOp, 
02982                                                       ActionList* pActionList,  
02983                                                       Range nodeRange, 
02984                                                       Spread* pSpread,  
02985                                                       BOOL includeBlobs,   
02986                                                       Action** NewAction)   
02987 
02988     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02989     Created:    10/5/95
02990     
02991     Inputs:     pOp: The operation to which the action should be added
02992                 
02993                 pActionList: The action list in the operation object
02994                 
02995                 NodeRange:   The range of nodes to be invalidated
02996 
02997                 pSpread:     The ranges spread 
02998                 
02999                 IncludeBlobs: When TRUE invalidate the blob bounding rectangles of the
03000                               nodes in the range.                
03001 
03002     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
03003 
03004     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
03005                              action and the user did not wish to continue. Usually 
03006                              End() should be called in this situation. 
03007                          
03008                 AC_NORECORD: There was not enough room in the operation history for
03009                              the action, but the user requested that he wished to 
03010                              continue without undo. 
03011                 
03012                 AC_OK      : The action was successfully initialised and added to the 
03013                              operation. 
03014                          
03015                            
03016     Purpose:    To check that there is sufficient room for the action in the operation
03017                 history, and if there is, then to add the action to the operations 
03018                 action list. 
03019                 
03020                 The function calls the Action::Init function passing the runtime class 
03021                 of an InvalidateRegionAction. If successful it records the NodeTag.  
03022     Errors:     -
03023     SeeAlso:    Action::Init
03024 
03025 ********************************************************************************************/
03026  
03027 ActionCode InvalidateRegionIfBgRedrawAction::Init(Operation* const pOp, 
03028                                                   ActionList* pActionList,  
03029                                                   Range nodeRange, 
03030                                                   Spread* pSpread,  
03031                                                   BOOL includeBlobs,   
03032                                                   Action** NewAction)   
03033 {       
03034     ActionCode Ac = (Action::Init(pOp,
03035                                   pActionList,
03036                                   sizeof(InvalidateRegionIfBgRedrawAction), 
03037                                   CC_RUNTIME_CLASS(InvalidateRegionIfBgRedrawAction), 
03038                                   NewAction));
03039     if (*NewAction != NULL)
03040     {
03041         ((InvalidateRegionIfBgRedrawAction*)(*NewAction))->NodeRange = nodeRange;
03042         ((InvalidateRegionIfBgRedrawAction*)(*NewAction))->IncludeBlobs = includeBlobs;
03043         ((InvalidateRegionIfBgRedrawAction*)(*NewAction))->pSpread = pSpread; 
03044     }
03045     return (Ac); 
03046 } 
03047 
03048 //------------------------------------------------------------------------------------------
03049 // HideNodeAction methods
03050     
03051 /********************************************************************************************
03052 
03053 >   HideNodeAction::HideNodeAction()
03054 
03055     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03056     Created:    16/8/93
03057     Inputs:     -
03058     Outputs:    -
03059     Returns:    -
03060     Purpose:    HideNodeAction constructor
03061     Errors:     -
03062     SeeAlso:    -
03063 
03064 ********************************************************************************************/
03065     
03066     
03067 HideNodeAction::HideNodeAction()
03068   : node(0),
03069     ClassOfAttributeToHide(0),
03070     m_nHiddenWixAttrTag(0),
03071     IncludeSubtreeSize(FALSE),
03072     TellSubtree(FALSE),
03073     m_bEffect(FALSE)
03074 {
03075     // Empty.
03076 }   
03077 
03078 
03079 // BODGE, to fix problems with common attrs becoming uncommon. (i.e. The node to hide is no longer there)
03080 // (Scaling line widths)
03081 ActionCode ExtremeTerror(CCRuntimeClass* AttrToHide, 
03082                          Node* pNode, 
03083                          Operation* pOp,
03084                          ActionList* pOppositeActLst,  
03085                          BOOL TellSubtree, 
03086                          BOOL IncludeSubtreeSize)
03087 {
03088     ShowNodeAction* ShwNodeAct;  
03089     ActionCode ActCode = AC_OK;  
03090 
03091     // Search pNode's subtree, to hide all attributes of AttrToHide type
03092     Node* pSearchNode = pNode->FindFirstDepthFirst();
03093     Node* pNext;
03094     while (pSearchNode)
03095     {
03096         pNext = pSearchNode->FindNextDepthFirst(pNode);
03097 
03098         if (pSearchNode->IsAnAttribute())
03099         {
03100             if (pSearchNode->GetRuntimeClass() == AttrToHide)
03101             {
03102                 Node* pAttrParent = pSearchNode->FindParent();
03103                 ERROR3IF(!pAttrParent, "Attribute has no parent");
03104 //              if (pAttrParent && (!pAttrParent->DiscardsAttributeChildren()))
03105                 if (pAttrParent)
03106                 {
03107                     if (pAttrParent->DiscardsAttributeChildren())
03108                     {
03109                         pSearchNode->CascadeDelete();
03110                         delete pSearchNode;
03111                     }
03112                     else
03113                     {
03114                         // Tell the node it's subtree, we are about to hide it
03115                         if (TellSubtree)
03116                         {
03117                             if (!pSearchNode->HidingNode())
03118                                 return AC_FAIL;
03119                         }
03120 
03121                         // The bounding rect of the parents to the node may well be changing
03122                         if (pSearchNode->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrLineWidth))
03123                         {
03124                             // If the node being hidden is an attribute which will effect the bounds of it's parent
03125                             // bounded object then we must invalidatate the parents bounds
03126                             Node* Parent = pSearchNode->FindParent(); 
03127                             if (Parent != NULL)
03128                             {
03129                                 if(Parent->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)))
03130                                 { 
03131                                     ((NodeRenderableBounded*)Parent)->InvalidateBoundingRect();     
03132                                 }
03133                             }
03134                         }                 
03135             
03136                         // Attempt to hide the node 
03137 
03138                         NodeHidden* HideNode; 
03139                         ALLOC_WITH_FAIL(HideNode, new NodeHidden(pSearchNode),pOp); 
03140 
03141                         if (HideNode == NULL)
03142                         {
03143                             // We were unable to hide the node so fail 
03144                             return AC_FAIL;         
03145                         }
03146 
03147                         // Attempt to initialise the show node action which will show the node that 
03148                         // we have just hidden. 
03149                         if ((ActCode = ShowNodeAction::Init(pOp,                    
03150                                                             pOppositeActLst,  
03151                                                             HideNode, 
03152                                                             IncludeSubtreeSize, 
03153                                                             ( Action**)(&ShwNodeAct),
03154                                                             TellSubtree)) == AC_FAIL) 
03155                         { 
03156                             // Show the node 
03157                             HideNode->ShowNode();                   
03158                         }   
03159                     
03160                         if (ActCode == AC_FAIL)
03161                             return AC_FAIL;
03162                     }
03163                 }
03164 
03165             }
03166         }
03167         pSearchNode = pNext;
03168     }
03169           
03170     return (ActCode);                        
03171 }
03172 
03173 /********************************************************************************************
03174 
03175 >   ActionCode HideNodeAction::Execute()
03176 
03177     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03178     Created:    16/8/93
03179     Inputs:     -
03180     Outputs:    ActionCode indicating if the action was successfully executed or not
03181     Returns:    -
03182     Purpose:    Executes the HideNodeAction which hides the node. It 
03183                 also creates a ShowNodeAction and adds it to the opposite ActionList. 
03184     Errors:     -
03185     SeeAlso:    -
03186                                                         
03187     
03188 ********************************************************************************************/
03189 
03190 ActionCode HideNodeAction::Execute()
03191 {
03192     ShowNodeAction* ShwNodeAct;  
03193     ActionCode ActCode;  
03194 
03195     NodeHidden* HideNode; 
03196 
03197     // Tell our parent that we have changed so any cached info he has must be removed
03198     // (Deals with cases where DoInvalidateRegion has been used and so no other
03199     //  cache release calls have been made)
03200     if (node->IsBounded())
03201         ((NodeRenderableBounded*)node)->ReleaseCached(TRUE, FALSE, TRUE, TRUE);
03202 
03203     // Determine which node needs hiding
03204     Node* NodeToHide = NULL;
03205     if (ClassOfAttributeToHide != NULL) // An attribute is being hidden
03206     {
03207 ERROR3IF(node->DiscardsAttributeChildren(), "HideNodeAction Execute under Caret\n");
03208         // We need to search the children of node to find the attribute
03209         Node* CurrentChild = node->FindFirstChild();
03210         while (CurrentChild != NULL)
03211         {
03212             if (CurrentChild->GetRuntimeClass() == ClassOfAttributeToHide)
03213             {
03214 #ifdef _HIDENODE_DETECTEFFECTS
03215                 if ((m_bEffect!=0) == ((NodeAttribute*)CurrentChild)->IsEffectAttribute() &&
03216                     (m_nHiddenWixAttrTag == 0 || m_nHiddenWixAttrTag == CurrentChild->GetTag()))
03217 #else
03218                 if ((m_nHiddenWixAttrTag == 0 || m_nHiddenWixAttrTag == CurrentChild->GetTag()))
03219 #endif
03220                 {
03221                     // We have found the attribute to hide
03222                     NodeToHide = CurrentChild;
03223                     break;
03224                 }
03225             }
03226             CurrentChild = CurrentChild->FindNext();
03227         }
03228 
03229         // This bit is v. scary
03230         if (NodeToHide == NULL)
03231         {
03232             // The attribute isnot where we left it last time. This could be due to a 
03233             // bitmap being deleted.
03234             ERROR3("Could not find attribute node to hide (Press continue for a fix :-)");
03235             return (ExtremeTerror(ClassOfAttributeToHide, node, 
03236                                   pOperation, pOppositeActLst, 
03237                                   TellSubtree, IncludeSubtreeSize)); 
03238         } 
03239     }
03240     else
03241     {
03242         NodeToHide = node; 
03243     }
03244 
03245     // Tell the node it's subtree, we are about to hide it
03246     if (TellSubtree)
03247     {
03248         Node* pNode = NodeToHide->FindFirstDepthFirst();
03249         while (pNode!=NULL)
03250         {
03251             if (!pNode->HidingNode())
03252                 return AC_FAIL;
03253 
03254             // And find the next node
03255             pNode = pNode->FindNextDepthFirst(NodeToHide);
03256         }
03257     }
03258 
03259     // The bounding rect of the parents to the node may well be changing
03260     if (NodeToHide->IsBounded())
03261     {
03262         ((NodeRenderableBounded*)NodeToHide)->InvalidateBoundingRect();
03263     }
03264     else if (NodeToHide->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrLineWidth))
03265     {
03266         // If the node being hidden is an attribute which will effect the bounds of it's parent
03267         // bounded object then we must invalidatate the parents bounds
03268         Node* Parent = NodeToHide->FindParent(); 
03269         if (Parent != NULL)
03270         {
03271             if(Parent->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)))
03272             { 
03273                 ((NodeRenderableBounded*)Parent)->InvalidateBoundingRect();     
03274             }
03275         }
03276     }                 
03277         
03278     // Attempt to hide the node 
03279     ALLOC_WITH_FAIL(HideNode, new NodeHidden(NodeToHide),pOperation); 
03280 
03281     if (HideNode == NULL)
03282     {
03283         // We were unable to hide the node so fail 
03284         return AC_FAIL;         
03285     }
03286 
03287     if (NodeToHide->IsSelected())
03288     {
03289         // Update the selection range 
03290         Camelot.UpdateSelection();
03291     }
03292 
03293     Spread* ParentOfNode = NULL;
03294 
03295     Layer * pLayer = NULL;
03296     if (NodeToHide->GetRuntimeClass() == CC_RUNTIME_CLASS(Layer))
03297     {
03298         ParentOfNode = (Spread*) HideNode->FindParent(CC_RUNTIME_CLASS(Spread));
03299         pLayer = (Layer*)NodeToHide;
03300     }
03301 
03302     // New 31/7/97 Neville
03303     // Mark the parent layer as edited as something has changed on it
03304     // Could use SetParentLayerAsEdited but only implemented for NodeRenderableInk at present
03305     if (pLayer == NULL)
03306         pLayer = (Layer*) HideNode->FindParent(CC_RUNTIME_CLASS(Layer));
03307     if (pLayer)
03308     {
03309             // Note that this layer has been edited
03310             pLayer->SetEdited(TRUE);
03311 #ifdef _DEBUG
03312             // Tell the frame gallery to update its display of the frame
03313             BROADCAST_TO_ALL( LayerMsg(pLayer, LayerMsg::REDRAW_LAYER ) );
03314 #endif
03315     }
03316 
03317     // Attempt to initialise the show node action which will show the node that 
03318     // we have just hidden. 
03319     if ((ActCode = ShowNodeAction::Init(pOperation,                    
03320                                         pOppositeActLst,  
03321                                         HideNode, 
03322                                         IncludeSubtreeSize, 
03323                                         ( Action**)(&ShwNodeAct),
03324                                         TellSubtree)) == AC_FAIL) 
03325     { 
03326         // Show the node 
03327         HideNode->ShowNode();                   
03328     }   
03329     
03330     // If the node being hidden is a layer then people need to know about it
03331     if (ParentOfNode)
03332     {
03333         BROADCAST_TO_ALL(SpreadMsg(ParentOfNode,SpreadMsg::LAYERCHANGES));
03334     }
03335           
03336     return (ActCode);                        
03337 }    
03338 
03339 /********************************************************************************************
03340 
03341 >   static ActionCode HideNodeAction::Init(Operation* const pOp, 
03342                                            ActionList* pActionList,
03343                                            Node* NodeToHide,  
03344                                            BOOL IncludeSubtreeSize,     
03345                                            Action** NewAction,
03346                                            BOOL TellSubtree); 
03347 
03348     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03349     Created:    14/9/93  
03350     
03351     Inputs:     pOp: The operation to which the action should be added
03352                 
03353                 pActionList: The action list in the operation object
03354 
03355                 NodeToHide:  The node to hide 
03356 
03357                 IncludeSubtreeSize:  The size of a HideNodeAction is always 
03358                                      sizeof(HideNodeAction), however the 
03359                                      IncludeSubtreeSize flag is required by the
03360                                      twin ShowNodeAction. 
03361 
03362     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
03363 
03364     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
03365                              action and the user did not wish to continue. Usually 
03366                              End() should be called in this situation. 
03367                          
03368                 AC_NORECORD: There was not enough room in the operation history for
03369                              the action, but the user requested that he wished to 
03370                              continue without undo. 
03371                 
03372                 AC_OK      : The action was successfully initialised and added to the 
03373                              operation. 
03374                          
03375                            
03376     Purpose:    To check that there is sufficient room for the action in the operation
03377                 history, and if there is, then to add the action to the operations 
03378                 action list. 
03379                 
03380                 The function calls the Action::Init function passing the runtime class 
03381                 of a HideNodeAction.
03382     Errors:     -
03383     SeeAlso:    Action::Init
03384 
03385 ********************************************************************************************/
03386 ActionCode HideNodeAction::Init(Operation* const pOp, 
03387                            ActionList* pActionList,
03388                            Node* NodeToHide,
03389                            BOOL IncludeSubtreeSize,     
03390                            Action** NewAction,
03391                            BOOL TellSubtree) 
03392 
03393 {  
03394     ActionCode Ac = (Action::Init(pOp,
03395                      pActionList,
03396                      sizeof(HideNodeAction), 
03397                      CC_RUNTIME_CLASS(HideNodeAction), 
03398                      NewAction)); 
03399 
03400     if (*NewAction != NULL) 
03401     {
03402         // Only used for attributes
03403         ((HideNodeAction*) (*NewAction))->ClassOfAttributeToHide = 0;
03404 
03405         // Do special things if the node to be hidden is an attribute
03406         if (NodeToHide->IsAnAttribute())
03407         {
03408             // Find the parent of the attribute node
03409             Node* AttributeParent = NodeToHide->FindParent();
03410 
03411             // We store the attribute's parent
03412             ((HideNodeAction*)(*NewAction))->node = AttributeParent; 
03413             ERROR2IF(AttributeParent == NULL, AC_FAIL, "Attribute has no parent"); 
03414 
03415             // It is bad if the attributes parent is an attribute, attributes are not safe anchors
03416             // they can be deleted. 
03417             ERROR2IF(AttributeParent->IsAnAttribute(), AC_FAIL, "Attribute's parent is an attribute");  
03418 
03419             // Store the runtime class of the attribute so that we can find it
03420             ((HideNodeAction*)(*NewAction))->ClassOfAttributeToHide = NodeToHide->GetRuntimeClass(); 
03421 
03422             // If it's also a Wix attribute then store its tag so it can be uniquely identified
03423             // when the attribute must be hidden again.
03424 #ifdef _HIDENODE_DETECTEFFECTS
03425             if (NodeToHide->IS_KIND_OF(TemplateAttribute))
03426 #else
03427             if (NodeToHide->IS_KIND_OF(TemplateAttribute) || ((NodeAttribute*)NodeToHide)->IsEffectAttribute())
03428 #endif
03429                 ((HideNodeAction*) (*NewAction))->m_nHiddenWixAttrTag = NodeToHide->GetTag();
03430             else
03431                 ((HideNodeAction*) (*NewAction))->m_nHiddenWixAttrTag = 0;
03432 
03433             ((HideNodeAction*) (*NewAction))->m_bEffect = ((NodeAttribute*)NodeToHide)->IsEffectAttribute();
03434         }
03435         else
03436         {
03437             ((HideNodeAction*)(*NewAction))->node = NodeToHide;
03438         }
03439 
03440         ((HideNodeAction*)(*NewAction))->IncludeSubtreeSize = IncludeSubtreeSize; 
03441         ((HideNodeAction*)(*NewAction))->TellSubtree = TellSubtree; 
03442     }
03443                   
03444     return (Ac); 
03445 }
03446 
03447 void HideNodeAction::RecordTag(Node* NodeToHide)
03448 {
03449     if (NodeToHide->IsAnAttribute())
03450     {
03451         // Find the parent of the attribute node
03452 //      Node* AttributeParent = NodeToHide->FindParent();
03453 
03454         // If it's also a Wix attribute then store its tag so it can be uniquely identified
03455         // when the attribute must be hidden again.
03456 #ifdef _HIDENODE_DETECTEFFECTS
03457         if (NodeToHide->IS_KIND_OF(TemplateAttribute))
03458 #else
03459         if (NodeToHide->IS_KIND_OF(TemplateAttribute) || ((NodeAttribute*)NodeToHide)->IsEffectAttribute())
03460 #endif
03461             m_nHiddenWixAttrTag = NodeToHide->GetTag();
03462         else
03463             m_nHiddenWixAttrTag = 0;
03464 
03465         m_bEffect = ((NodeAttribute*)NodeToHide)->IsEffectAttribute();
03466     }
03467 }
03468 
03469 //------------------------------------------------------------------------------------------                           
03470 // ShowNodeAction methods
03471     
03472 /********************************************************************************************
03473 
03474 >   ShowNodeAction::ShowNodeAction()
03475 
03476     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03477     Created:    14/9/93
03478     Inputs:     -
03479     Outputs:    -
03480     Returns:    -
03481     Purpose:    ShowNodeAction constructor
03482     Errors:     -
03483     SeeAlso:    -
03484 
03485 ********************************************************************************************/
03486     
03487 ShowNodeAction::ShowNodeAction()
03488 {
03489 }
03490              
03491 ShowNodeAction::~ShowNodeAction() 
03492 {
03493 }   
03494 
03495 /********************************************************************************************
03496 
03497 >   virtual void ShowNodeAction::Slaughter()
03498 
03499     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03500     Created:    6/7/93
03501     Inputs:     -
03502     Outputs:    -
03503     Returns:    -
03504     Purpose:    destructor which gets called when an operation is deleted  
03505     Errors:     -
03506     SeeAlso:    Action::Slaughter
03507 
03508 ********************************************************************************************/
03509 
03510 void ShowNodeAction::Slaughter() 
03511 {               
03512     // If the node which the NodeHidden hides has no parent and the node is not refferenced 
03513     // by any other NodeHidden nodes, then the node can safely be deleted.  
03514 
03515     // On the other-hand if the node which the NodeHidden hides has a parent, then it must not
03516     // be deleted. This situation arises when we have moved a node and the NodeHidden is simply
03517     // used as a place holder for when we undo/redo.  
03518                       
03519     // First delete the NodeHidden                                              
03520     Node* Hidden = node->HiddenNd; // Remember the node which it hides !
03521     node->CascadeDelete(); // Simply unlinks the node from the tree, node should 
03522                            // never have any children because it's a NodeHidden.  
03523     delete (node);  
03524     
03525     Hidden->DecHiddenCnt(); // Decrement the number of refferences to the hidden node.            
03526                       
03527     if (Hidden->FindParent() == NULL)
03528     {
03529         // The node which is hidden has no parent but it could be reffered to by other 
03530         // hidden nodes which as yet have not been deleted.                              
03531         
03532         if (Hidden->GetHiddenCnt() == 0) // The node is not hidden by any NodeHidden nodes.    
03533         {
03534             Hidden->CascadeDelete(); // Delete the children of the hidden node.
03535             delete (Hidden);         // Delete the hidden node itself 
03536         }
03537         
03538     } 
03539     Action::Slaughter(); // Call base class to destroy this 
03540 }   
03541  
03542 /********************************************************************************************
03543 
03544 >   virtual ActionCode ShowNodeAction::Execute()
03545 
03546     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03547     Created:    16/8/93
03548     Inputs:     -
03549     Outputs:    ActionCode indicating if the action was successfully executed or not
03550     Returns:    -
03551     Purpose:    Executes the ShowNodeAction which un-hides the node. It 
03552                 also creates a HideNodeAction and adds it to the opposite ActionList. 
03553     Errors:     -
03554     SeeAlso:    -
03555     
03556     
03557 ********************************************************************************************/
03558  
03559 ActionCode ShowNodeAction::Execute()
03560 {  
03561     HideNodeAction* HideNodeAct;  
03562     ActionCode ActCode;  
03563 
03564     // If the HideNodeAction is for a NodeAttribute then this node must have a parent before
03565     // we call the HideNodeAction's Init method. However because it is hidden it does not have one at
03566     // the moment. So we use the SetParentDangerous function to give it the parent of the NodeHidden.
03567     // This is horrible but it is the safest thing to do. If we call node->ShowNode before calling
03568     // HideNodeAction::Init then we are shafted if this function fails. (The action will not be atomic)
03569   
03570     node->HiddenNd->SetParentDangerous(node->FindParent()); 
03571 
03572      // Attempt to initialise the hide node action  
03573     if ((ActCode = HideNodeAction::Init(pOperation,                    
03574                                         pOppositeActLst, 
03575                                         node->HiddenNd, 
03576                                         IncludeSubtreeSize,  
03577                                         ( Action**)(&HideNodeAct),
03578                                         TellSubtree)) != AC_FAIL) 
03579     {                 
03580         Node* BackFromTheDead = node->ShowNode();
03581         Node* Parent = BackFromTheDead->FindParent(); 
03582 ERROR3IF(Parent->DiscardsAttributeChildren(), "ShowNodeAction Execute under Caret\n");
03583 
03584         // Urgh - we can only tell whether an attribute is an effect attr when it's in the tree
03585         // And we need to give the HideNodeAction the Tag of the attribute if it is an
03586         // Effect Attribute so that HideNodeExecute finds it correctly...
03587         if (HideNodeAct)
03588             HideNodeAct->RecordTag(BackFromTheDead);
03589 
03590         // Tell the node and it's subtree, we have re-shown it
03591         if (TellSubtree)
03592         {
03593             Node* pNode = BackFromTheDead->FindFirstDepthFirst();
03594             while (pNode!=NULL)
03595             {
03596                 if (!pNode->ShowingNode())
03597                     return AC_FAIL;
03598 
03599                 // And find the next node
03600                 pNode = pNode->FindNextDepthFirst(BackFromTheDead);
03601             }
03602         }
03603 
03604         if (BackFromTheDead->IsSelected())
03605         {
03606             // Update the selection range 
03607             Camelot.UpdateSelection();
03608         }
03609 
03610         // If the node being shown is a layer then people need to know about it
03611         Layer * pLayer = NULL;
03612         if (BackFromTheDead->GetRuntimeClass() == CC_RUNTIME_CLASS(Layer))
03613         {
03614             Spread* pSpread = (Spread*) BackFromTheDead->FindParent(CC_RUNTIME_CLASS(Spread)); 
03615             BROADCAST_TO_ALL(SpreadMsg(pSpread,SpreadMsg::LAYERCHANGES));
03616             pLayer = (Layer*)BackFromTheDead;
03617         }
03618 
03619         // Mark the bounding rects as out of date
03620         if (BackFromTheDead->IsBounded())
03621         {
03622             // Since this nodes bounding rect may well be invalid while it was hidden, we
03623             // have to validate it to be sure the invalidation ripples up through the tree
03624             ((NodeRenderableBounded*)BackFromTheDead)->ValidateBoundingRect();
03625             ((NodeRenderableBounded*)BackFromTheDead)->InvalidateBoundingRect();
03626         }
03627         else if (BackFromTheDead->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrLineWidth))
03628         {
03629             // If the node being shown is an attribute which will effect the bounds of it's parent
03630             // bounded object then we must invalidatate the parent's bounds
03631             if (Parent != NULL)
03632             {
03633                 if(Parent->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)))
03634                 { 
03635                     ((NodeRenderableBounded*)Parent)->InvalidateBoundingRect();     
03636                 }
03637             }
03638         }
03639 
03640         // New 31/7/97 Neville
03641         // Mark the parent layer as edited as something has changed on it
03642         // Could use SetParentLayerAsEdited but only implemented for NodeRenderableInk at present
03643         if (pLayer == NULL)
03644             pLayer = (Layer*) BackFromTheDead->FindParent(CC_RUNTIME_CLASS(Layer));
03645         if (pLayer)
03646         {
03647                 // Note that this layer has been edited
03648                 pLayer->SetEdited(TRUE);
03649 #ifdef _DEBUG
03650                 // Tell the frame gallery to update its display of the frame
03651                 BROADCAST_TO_ALL( LayerMsg(pLayer, LayerMsg::REDRAW_LAYER ) );
03652 #endif
03653 
03654     // Tell our parent that we have changed so any cached info he has must be removed
03655     // (Deals with cases where DoInvalidateRegion has been used and so no other
03656     //  cache release calls have been made)
03657     if (BackFromTheDead->IsBounded())
03658         ((NodeRenderableBounded*)BackFromTheDead)->ReleaseCached(TRUE, FALSE, TRUE, TRUE);
03659     else if (Parent->IsBounded())
03660         ((NodeRenderableBounded*)Parent)->ReleaseCached(TRUE, FALSE, TRUE, TRUE);
03661 
03662         }
03663     }
03664     else
03665     {
03666         node->HiddenNd->SetParentDangerous(NULL); // The node was never un-hidden so we must give it a NULL parent
03667 
03668     }                 
03669     return (ActCode);                   
03670 }     
03671 
03672 
03673 /********************************************************************************************
03674 >   static ActionCode ShowNodeAction::Init(Operation* const pOp, 
03675                                            ActionList* pActionList, 
03676                                            NodeHidden* HiddenToShow, 
03677                                            BOOL IncludeSubtreeSize,     
03678                                            Action** NewAction,
03679                                            BOOL TellSubtree) 
03680 
03681     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03682     Created:    14/9/93  
03683     
03684     Inputs:     pOp:                The operation to which the action should be added
03685                 
03686                 pActionList:        The action list in the operation object
03687                 
03688                 HiddenToShow:       The hidden node to show when the action is executed
03689 
03690                 IncludeSubtreeSize: This flag should be set if the action size 
03691                                     should include the size of the subtree which is 
03692                                     hidden. 
03693                              
03694     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
03695 
03696     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
03697                              action and the user did not wish to continue. Usually 
03698                              End() should be called in this situation. 
03699                          
03700                 AC_NORECORD: There was not enough room in the operation history for
03701                              the action, but the user requested that he wished to 
03702                              continue without undo. 
03703                 
03704                 AC_OK      : The action was successfully initialised and added to the 
03705                              operation. 
03706                          
03707                            
03708     Purpose:    To check that there is sufficient room for the action in the operation
03709                 history, and if there is, then to add the action to the operations 
03710                 action list. 
03711                 
03712                 The function calls the Action::Init function passing the runtime class 
03713                 of a ShowNodeAction.
03714     Errors:     -
03715     SeeAlso:    Action::Init
03716 
03717 ********************************************************************************************/
03718 
03719 ActionCode ShowNodeAction::Init(Operation* const pOp, 
03720                                 ActionList* pActionList, 
03721                                 NodeHidden* HiddenToShow, 
03722                                 BOOL IncludeSubtreeSize,    
03723                                 Action** NewAction, 
03724                                 BOOL TellSubtree)
03725 { 
03726     // Determine the size of the action 
03727     UINT32 ActionSize; 
03728 
03729     ActionSize = sizeof(ShowNodeAction); 
03730     
03731     // If the subtree being hidden is to be considered to belong to the operation history 
03732     // then the ActionSize must include the size of the hidden subtree. 
03733 
03734     if (IncludeSubtreeSize)
03735     {
03736         ActionSize += ( HiddenToShow->HiddenNd->GetSubtreeSize());
03737         //if (IsUserName("Simon"))
03738         //  TRACE( _T("ShowNodeActionSize = %lu"), ActionSize);  
03739         
03740     }
03741 
03742     ActionCode Ac = (Action::Init(pOp,
03743                      pActionList,
03744                      ActionSize,
03745                      CC_RUNTIME_CLASS(ShowNodeAction), 
03746                      NewAction));
03747     if (*NewAction != NULL) 
03748     {
03749         ((ShowNodeAction*)(*NewAction))->node = HiddenToShow;
03750         ((ShowNodeAction*)(*NewAction))->IncludeSubtreeSize = IncludeSubtreeSize; 
03751         ((ShowNodeAction*)(*NewAction))->TellSubtree = TellSubtree; 
03752     }
03753 
03754     return (Ac); 
03755 } 
03756  
03757 
03758 
03759 
03760 //------------------------------------------------------------------------------------------
03761 // UnApplyAction methods
03762     
03763 /********************************************************************************************
03764 
03765 >   UnApplyAction::UnApplyAction()
03766 
03767     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03768     Created:    26/07/2005
03769     Inputs:     -
03770     Outputs:    -
03771     Returns:    -
03772     Purpose:    UnApplyAction constructor
03773     Errors:     -
03774     SeeAlso:    -
03775 
03776 ********************************************************************************************/
03777     
03778     
03779 UnApplyAction::UnApplyAction()
03780 {
03781     m_pApplyNode = NULL;
03782     m_pAttribute = NULL;
03783     m_nAttrTag = 0;
03784     IncludeSubtreeSize = FALSE;
03785     TellSubtree = FALSE;
03786 }
03787 
03788 UnApplyAction::~UnApplyAction()
03789 {
03790     if (m_pAttribute)
03791     {
03792         delete m_pAttribute;
03793         m_pAttribute = NULL;
03794     }
03795 }
03796 
03797 
03798 /********************************************************************************************
03799 
03800 >   ActionCode UnApplyAction::Execute()
03801 
03802     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03803     Created:    26/07/2005
03804     Inputs:     -
03805     Outputs:    ActionCode indicating if the action was successfully executed or not
03806     Returns:    -
03807     Purpose:    Executes the UnApplyAction which hides the node. It 
03808                 also creates a ShowNodeAction and adds it to the opposite ActionList. 
03809     Errors:     -
03810     SeeAlso:    -
03811                                                         
03812     
03813 ********************************************************************************************/
03814 
03815 ActionCode UnApplyAction::Execute()
03816 {
03817     ApplyAction* pApplyAct;  
03818     ActionCode ActCode;  
03819 
03820 //  NodeHidden* HideNode = NULL;
03821 
03822     // Attempt to initialise the apply action which will re-apply the node that 
03823     // we are about to unapply.
03824     if ((ActCode = ApplyAction::Init(pOperation,
03825                                         pOppositeActLst,
03826                                         m_pApplyNode,
03827                                         m_pAttribute,
03828                                         IncludeSubtreeSize,
03829                                         ( Action**)(&pApplyAct),
03830                                         TellSubtree)) == AC_FAIL)
03831         return ActCode;
03832     
03833     // Tell our parent that we have changed so any cached info he has must be removed
03834     // (Deals with cases where DoInvalidateRegion has been used and so no other
03835     //  cache release calls have been made)
03836     if (m_pApplyNode->IsBounded())
03837         ((NodeRenderableBounded*)m_pApplyNode)->ReleaseCached(TRUE, FALSE, TRUE, TRUE);
03838 
03839     // Determine which node needs hiding
03840     CCRuntimeClass* pClass = m_pAttribute->GetRuntimeClass();
03841 
03842     // We need to search the children of node to find the attribute
03843     // Note that if this action has recorded a Tag, that attribute should not have been
03844     // mutated before trying to execute this action. If it has then the mutated attribute
03845     // will have a new tag and will not be found. c.f. interactive dragging...
03846     Node* NodeToHide = m_pApplyNode->FindFirstChild();
03847     BOOL bLookingForEffect = m_pApplyNode->IsAnObject() && ((NodeRenderableInk*)m_pApplyNode)->IsValidEffectAttr(m_pAttribute);
03848     while (NodeToHide != NULL)
03849     {
03850         if (NodeToHide->GetRuntimeClass() == pClass &&
03851             (m_nAttrTag == 0 || m_nAttrTag == NodeToHide->GetTag()) &&
03852             (bLookingForEffect == (NodeToHide->IsAnAttribute() && ((NodeAttribute*)NodeToHide)->IsEffectAttribute()))
03853             )
03854             // We have found the attribute to hide
03855             break;
03856 
03857         NodeToHide = NodeToHide->FindNext();
03858     }
03859 
03860     if (NodeToHide != NULL)
03861     {
03862         // If the node being hidden is an attribute which will effect the bounds of it's parent
03863         // bounded object then we must invalidate the parents bounds
03864         if (m_pApplyNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)))
03865             ((NodeRenderableBounded*)m_pApplyNode)->InvalidateBoundingRect();   
03866             
03867         NodeToHide->CascadeDelete();
03868         delete NodeToHide;
03869         NodeToHide = NULL;
03870 
03871         // Mark the parent layer as edited as something has changed on it
03872         // Could use SetParentLayerAsEdited but only implemented for NodeRenderableInk at present
03873         Layer* pLayer = (Layer*) m_pApplyNode->FindParent(CC_RUNTIME_CLASS(Layer));
03874         if (pLayer)
03875             pLayer->SetEdited(TRUE);
03876 
03877     } 
03878 
03879     return ActCode;
03880 }    
03881 
03882 /********************************************************************************************
03883 
03884 >   static ActionCode UnApplyAction::Init(Operation* const pOp, 
03885                                            ActionList* pActionList,
03886                                            Node* pActionApplyNode,
03887                                            NodeAttribute* pActionAttribute,
03888                                            BOOL IncludeSubtreeSize,     
03889                                            Action** NewAction,
03890                                            BOOL TellSubtree); 
03891 
03892     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03893     Created:    26/07/2005
03894     Inputs:     pOp: The operation to which the action should be added
03895                 
03896                 pActionList: The action list in the operation object
03897 
03898                 NodeToHide:  The node to hide 
03899 
03900                 IncludeSubtreeSize:  The size of a UnApplyAction is always 
03901                                      sizeof(UnApplyAction), however the 
03902                                      IncludeSubtreeSize flag is required by the
03903                                      twin ShowNodeAction. 
03904 
03905     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
03906 
03907     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
03908                              action and the user did not wish to continue. Usually 
03909                              End() should be called in this situation. 
03910                          
03911                 AC_NORECORD: There was not enough room in the operation history for
03912                              the action, but the user requested that he wished to 
03913                              continue without undo. 
03914                 
03915                 AC_OK      : The action was successfully initialised and added to the 
03916                              operation. 
03917                          
03918                            
03919     Purpose:    To check that there is sufficient room for the action in the operation
03920                 history, and if there is, then to add the action to the operations 
03921                 action list. 
03922                 
03923                 The function calls the Action::Init function passing the runtime class 
03924                 of a UnApplyAction.
03925     Errors:     -
03926     SeeAlso:    Action::Init
03927 
03928 ********************************************************************************************/
03929 ActionCode UnApplyAction::Init(Operation* const pOp, 
03930                            ActionList* pActionList,
03931                            Node* pActionApplyNode,
03932                            NodeAttribute* pActionAttribute,
03933                            BOOL IncludeSubtreeSize,     
03934                            Action** NewAction,
03935                            BOOL TellSubtree) 
03936 
03937 {  
03938     ActionCode Ac = (Action::Init(pOp,
03939                      pActionList,
03940                      sizeof(UnApplyAction) + sizeof(pActionAttribute),
03941                      CC_RUNTIME_CLASS(UnApplyAction), 
03942                      NewAction)); 
03943 
03944     if (*NewAction != NULL) 
03945     {
03946         UnApplyAction* pAction = (UnApplyAction*)(*NewAction);
03947 
03948         // Store the runtime class of the attribute so that we can find it
03949         pAction->m_pApplyNode = pActionApplyNode;
03950         pAction->m_pAttribute = (NodeAttribute*)pActionAttribute->SimpleCopy();
03951         pAction->IncludeSubtreeSize = IncludeSubtreeSize; 
03952         pAction->TellSubtree = TellSubtree; 
03953         pAction->RecordTag(pActionAttribute);
03954     }
03955                   
03956     return (Ac); 
03957 }
03958 
03959 void UnApplyAction::RecordTag(NodeAttribute* pAttr)
03960 {
03961     // If it's also a Wix attribute then store its tag so it can be uniquely identified
03962     // when the attribute must be hidden again.
03963 //  if (pAttr->IS_KIND_OF(TemplateAttribute) || pAttr->IsEffectAttribute())
03964     if (pAttr->IS_KIND_OF(TemplateAttribute))
03965         m_nAttrTag = pAttr->GetTag();
03966     else
03967         m_nAttrTag = 0;
03968 }
03969 
03970 //------------------------------------------------------------------------------------------                           
03971 // ApplyAction methods
03972     
03973 /********************************************************************************************
03974 
03975 >   ApplyAction::ApplyAction()
03976 
03977     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03978     Created:    26/07/2005
03979     Inputs:     -
03980     Outputs:    -
03981     Returns:    -
03982     Purpose:    ApplyAction constructor
03983     Errors:     -
03984     SeeAlso:    -
03985     Notes:      Phil, 26/07/2005
03986                 What a mess! Attribute application needs to be rewritten
03987                 * ShowNode/HideNode to control attribute undo is suspect because attribute
03988                   optimisation can remove attributes outside of the undo system.
03989                 * UnapplyAction and ApplyAction are implemented to overcome this limitation
03990                   but really, they are the way attribute application should always have been
03991                   recorded in undo, given the above.
03992                 * OpRepeatApplyAttribToSelected is all wrong. It makes quick, non-undoable
03993                   changes to a set of attributes after OpApplyAttribToSelected has applied
03994                   them. This means that attributes may be optimised away AFTER the undo
03995                   actions have been recorded. It should work the way most other solid dragging
03996                   works - create a temporary set of attrs for fast manipulation druing dragging
03997                   then remove them and do a one-shot undoable apply when the drag ends.
03998 
03999 ********************************************************************************************/
04000     
04001 ApplyAction::ApplyAction()
04002 {
04003     m_pApplyNode = NULL;
04004     m_pAttribute = NULL;
04005 }
04006 
04007 ApplyAction::~ApplyAction() 
04008 {
04009     if (m_pAttribute)
04010     {
04011         delete m_pAttribute;
04012         m_pAttribute = NULL;
04013     }
04014 }   
04015 
04016 /********************************************************************************************
04017 
04018 >   virtual ActionCode ApplyAction::Execute()
04019 
04020     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04021     Created:    26/07/2005
04022     Inputs:     -
04023     Outputs:    ActionCode indicating if the action was successfully executed or not
04024     Returns:    -
04025     Purpose:    Executes the ApplyAction which un-hides the node. It 
04026                 also creates a UnApplyAction and adds it to the opposite ActionList. 
04027     Errors:     -
04028     SeeAlso:    -
04029     
04030     
04031 ********************************************************************************************/
04032  
04033 ActionCode ApplyAction::Execute()
04034 {  
04035     UnApplyAction* pUnApplyAction;  
04036     ActionCode ActCode;  
04037 
04038      // Attempt to initialise the hide node action  
04039     if ((ActCode = UnApplyAction::Init(pOperation,                    
04040                                         pOppositeActLst,
04041                                         m_pApplyNode,
04042                                         m_pAttribute,
04043                                         IncludeSubtreeSize,  
04044                                         ( Action**)(&pUnApplyAction),
04045                                         TellSubtree)) != AC_FAIL) 
04046     {
04047         ERROR3IF(m_pAttribute==NULL, "ApplyAction::Execute has no attribute!\n");
04048         if (m_pAttribute)
04049         {
04050             NodeAttribute* pAttr = (NodeAttribute*)m_pAttribute->SimpleCopy();
04051             BOOL bEffectRootOnly = (m_pApplyNode->IsAnObject() && ((NodeRenderableInk*)m_pApplyNode)->IsValidEffectAttr(pAttr));
04052             if (bEffectRootOnly)
04053                 pAttr->AttachNode(m_pApplyNode, LASTCHILD);
04054             else
04055                 pAttr->AttachNode(m_pApplyNode, FIRSTCHILD);
04056 
04057             if (pUnApplyAction)
04058                 pUnApplyAction->RecordTag(pAttr);
04059         }
04060 
04061         // If the node being shown is an attribute which will effect the bounds of it's parent
04062         // bounded object then we must invalidatate the parent's bounds
04063         if (m_pApplyNode && m_pApplyNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)))
04064             ((NodeRenderableBounded*)m_pApplyNode)->InvalidateBoundingRect();
04065 
04066         // New 31/7/97 Neville
04067         // Mark the parent layer as edited as something has changed on it
04068         // Could use SetParentLayerAsEdited but only implemented for NodeRenderableInk at present
04069         Layer* pLayer = (Layer*)m_pApplyNode->FindParent(CC_RUNTIME_CLASS(Layer));
04070         if (pLayer)
04071             pLayer->SetEdited(TRUE);
04072 
04073         // Tell our parent that we have changed so any cached info he has must be removed
04074         // (Deals with cases where DoInvalidateRegion has been used and so no other
04075         //  cache release calls have been made)
04076         if (m_pApplyNode->IsBounded())
04077             ((NodeRenderableBounded*)m_pApplyNode)->ReleaseCached(TRUE, FALSE, TRUE, TRUE);
04078     }
04079 
04080     return (ActCode);                   
04081 }     
04082 
04083 
04084 /********************************************************************************************
04085 >   static ActionCode ApplyAction::Init(Operation* const pOp, 
04086                                            ActionList* pActionList, 
04087                                            Node* pActionApplyNode, 
04088                                            NodeAttribute* pActionAttribute,
04089                                            BOOL IncludeSubtreeSize,     
04090                                            Action** NewAction,
04091                                            BOOL TellSubtree) 
04092 
04093     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04094     Created:    26/07/2005
04095     Inputs:     pOp:                The operation to which the action should be added
04096                 
04097                 pActionList:        The action list in the operation object
04098                 
04099                 HiddenToShow:       The hidden node to show when the action is executed
04100 
04101                 IncludeSubtreeSize: This flag should be set if the action size 
04102                                     should include the size of the subtree which is 
04103                                     hidden. 
04104                              
04105     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
04106 
04107     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
04108                              action and the user did not wish to continue. Usually 
04109                              End() should be called in this situation. 
04110                          
04111                 AC_NORECORD: There was not enough room in the operation history for
04112                              the action, but the user requested that he wished to 
04113                              continue without undo. 
04114                 
04115                 AC_OK      : The action was successfully initialised and added to the 
04116                              operation. 
04117                          
04118                            
04119     Purpose:    To check that there is sufficient room for the action in the operation
04120                 history, and if there is, then to add the action to the operations 
04121                 action list. 
04122                 
04123                 The function calls the Action::Init function passing the runtime class 
04124                 of a ShowNodeAction.
04125     Errors:     -
04126     SeeAlso:    Action::Init
04127 
04128 ********************************************************************************************/
04129 
04130 ActionCode ApplyAction::Init(Operation* const pOp, 
04131                                 ActionList* pActionList, 
04132                                 Node* pActionApplyNode, 
04133                                 NodeAttribute* pActionAttribute,
04134                                 BOOL IncludeSubtreeSize,    
04135                                 Action** NewAction, 
04136                                 BOOL TellSubtree)
04137 { 
04138     // Determine the size of the action 
04139     ActionCode Ac = (Action::Init(pOp,
04140                      pActionList,
04141                      sizeof(ApplyAction) + sizeof(pActionAttribute),
04142                      CC_RUNTIME_CLASS(ApplyAction), 
04143                      NewAction));
04144     if (*NewAction != NULL) 
04145     {
04146         ApplyAction* pAction = (ApplyAction*)(*NewAction);
04147 
04148         pAction->m_pApplyNode       = pActionApplyNode;
04149         pAction->m_pAttribute       = (NodeAttribute*)pActionAttribute->SimpleCopy();
04150         pAction->IncludeSubtreeSize = IncludeSubtreeSize; 
04151         pAction->TellSubtree        = TellSubtree; 
04152     }
04153 
04154     return (Ac); 
04155 } 
04156  
04157 
04158 
04159 
04160 //------------------------------------------------------------------------------------------                           
04161 // RestoreSelectionsAction methods
04162     
04163 /********************************************************************************************
04164 
04165 >   RestoreSelectionsAction::RestoreSelectionsAction()
04166 
04167     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04168     Created:    14/9/93
04169     Inputs:     -
04170     Outputs:    -
04171     Returns:    -
04172     Purpose:    RestoreSelectionsAction constructor
04173     Errors:     -
04174     SeeAlso:    -
04175 
04176 ********************************************************************************************/
04177     
04178 RestoreSelectionsAction::RestoreSelectionsAction()
04179 {
04180 }
04181 
04182 RestoreSelectionsAction::~RestoreSelectionsAction() 
04183 { 
04184     // Note that the SelectionState object is not deleted in the destructor. This is because 
04185     // it will be used by the spawned RestoreSelectionAction generated in the Execute method.
04186 }   
04187 
04188 /********************************************************************************************
04189 
04190 >   virtual RestoreSelectionsAction::Slaughter()
04191 
04192     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04193     Created:    6/7/93
04194     Inputs:     -
04195     Outputs:    -
04196     Returns:    -
04197     Purpose:    All RestoreSelections actions have a pointer to a selection state. This
04198                 selection state is passed on to the actions twin, and therefore should
04199                 not be deleted in the actions destructor. The slaughter method is responsible
04200                 for deleting the SelState. 
04201 
04202                 If this is a togling action, and ToggleStatus = FALSE then the SelState 
04203                 is not deleted, it will be deleted by the other RestoreSelectionsAction
04204                 with ToggleStatus = TRUE. 
04205 
04206     Errors:     -
04207     SeeAlso:    Action::Slaughter
04208 
04209 ********************************************************************************************/
04210                       
04211 void RestoreSelectionsAction::Slaughter() 
04212 {   
04213     // Determine if the SelState should be deleted here or in another RestoreSelectionsAction
04214     if (SelStateShared)
04215     {
04216         ENSURE(toggle, "If the SelState is shared then the toggle flag should be set"); 
04217         if (toggleStatus)
04218         {
04219             ENSURE (SelState != NULL, "Trying to delete a NULL SelectionState"); 
04220             delete SelState; 
04221         }
04222     } 
04223     else // The SelState is not shared so we can safely delete the selection state
04224     {
04225         delete SelState; 
04226     }
04227     Action::Slaughter(); 
04228 }   
04229  
04230 /********************************************************************************************
04231 
04232 >   virtual ActionCode RestoreSelectionsAction::Execute()
04233 
04234     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04235     Created:    16/8/93
04236     Inputs:     -
04237     Outputs:    ActionCode indicating if the action was successfully executed or not
04238     Returns:    -
04239     Purpose:    Executes the RestoreSelectionsAction which restores the selection state. It also
04240                 creates another identical RestoreSelectionsAction and adds this to the  
04241                 opposite ActionList.      
04242     Errors:     -
04243     SeeAlso:    -
04244     
04245     
04246 ********************************************************************************************/
04247  
04248 ActionCode RestoreSelectionsAction::Execute()
04249 {  
04250     RestoreSelectionsAction* RestoreAct;  
04251     ActionCode ActCode;  
04252     // Attempt to initialise the action    
04253     if ((ActCode = RestoreSelectionsAction::Init(pOperation,                    
04254                                                  pOppositeActLst,  
04255                                                  SelState, 
04256                                                  toggle, 
04257                                                  !toggleStatus,   // Toggle it
04258                                                  SelStateShared, 
04259                                                  RenderStartBlobs,
04260                                                  RenderEndBlobs,
04261                                                  !StartRestore,   // Will become the last restore !
04262                                                  ( Action**)(&RestoreAct))) != AC_FAIL) 
04263     {   
04264         // The action was successfully initialised. If this is a toggle 
04265         // action then only restore the selection state when the toggleStatus is
04266         // true. 
04267         if ((!toggle) || (toggleStatus))  // Determine if we have to do anything 
04268         {   
04269             // Only render the blobs if StartRestore is FALSE and either
04270             // a. RenderStartBlobs is TRUE and this action is an undo action 
04271             // b. RenderEndBlobs is TRUE and this is a redo action
04272 
04273             BOOL RenderBlobs = FALSE;
04274                              
04275             if (!StartRestore)
04276             {
04277                 if  (pOppositeActLst == pOperation->GetUndoActionList())
04278                 {
04279                     // This action is a redo action
04280                     RenderBlobs = RenderEndBlobs;
04281                 }
04282 
04283                 else
04284                 {
04285                     // This action is an undo action
04286                     RenderBlobs = RenderStartBlobs;
04287                 }
04288             }
04289 
04290             // Turn blob rendering on if this is the last restore
04291             //if (!StartRestore)
04292             //{
04293             //  Camelot.GetBlobManager()->BlobRenderingOn(FALSE); 
04294             //}
04295 
04296             // Always remove the selection blobs when StartRestore is TRUE           
04297             SelState->Restore(RenderBlobs, StartRestore); // Restore selections   
04298             
04299             // Turn blob rendering off if this is the first restore
04300             //if (StartRestore)
04301             //{
04302             //  Camelot.GetBlobManager()->BlobRenderingOff(FALSE); 
04303             //}
04304  
04305         }
04306     }             
04307     return (ActCode);                
04308 }     
04309 
04310 
04311 /********************************************************************************************
04312 
04313 >   static ActionCode RestoreSelectionsAction::Init(Operation* const pOp, 
04314                                                     ActionList* pActionList,    
04315                                                     SelectionState* SelState,
04316                                                     BOOL Toggle, 
04317                                                     BOOL ToggleStatus, 
04318                                                     BOOL SelStateShared,  
04319                                                     BOOL RenderStartBlobs,
04320                                                     BOOL RenderEndBlobs,
04321                                                     BOOL StartRestore, 
04322                                                     Action** NewAction) 
04323 
04324     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04325     Created:    14/9/93  
04326     
04327     Inputs:     pOp: The operation to which the action should be added
04328                 
04329                 pActionList: The action list in the operation object
04330                 
04331                 SelState:    Pointer to a selection State - this gets destroyed 
04332                              in the actions slaughter method. 
04333 
04334                 Toggle:      When TRUE this flag indicates that the selection 
04335                              state should only be restored when the ToggleStatus
04336                              is TRUE. The ToggleStatus of the actions twin = 
04337                              !ToggleStatus. This mechanism allows two RestoreSelectionActions
04338                              to be created for an operation, only one of which does anything. 
04339                              Usually an operation creates two RestoreSelectionsActions one 
04340                              at the head and one at the tail of the operations action list.
04341                              When undoing it is desirable that the tail 
04342                              RestoreSelectionsAction restores the selection state and that
04343                              the head RestoreSelectionsAction does nothing (except create 
04344                              a twin). when redoing the RestoreSelectionsAction which
04345                              was originally at the head of the undo action list is now at 
04346                              the tail of the redo list and so it needs to restore the 
04347                              selections.Likewise the RestoreSelectionsAction which was at 
04348                              the tail of the undo action list will be at the head of the redo
04349                              action list and so it is this actions turn to do nothing.
04350 
04351 
04352                                 
04353                 ToggleStatus:  The initial toggle status
04354 
04355                 SelStateShared: This flag indicates that the SelState is shared by a pair of 
04356                                 RestoreSelActions. If this flag is set then the Toggle
04357                                 flag must also be set.  
04358 
04359                 RenderStartBlobs: Should the selection blobs be rendered for the start selection
04360                                   state ?
04361 
04362                 RenderEndBlobs:   Should the selection blobs be rendered for the end selection 
04363                                   state ?
04364 
04365                 StartRestore:  TRUE indicates that this is the first restore action executed when 
04366                                the operation is undone. 
04367 
04368                              
04369     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
04370 
04371     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
04372                              action and the user did not wish to continue. Usually 
04373                              End() should be called in this situation. 
04374                          
04375                 AC_NORECORD: There was not enough room in the operation history for
04376                              the action, but the user requested that he wished to 
04377                              continue without undo. 
04378                 
04379                 AC_OK      : The action was successfully initialised and added to the 
04380                              operation. 
04381                          
04382                            
04383     Purpose:    To check that there is sufficient room for the action in the operation
04384                 history, and if there is, then to add the action to the operations 
04385                 action list. 
04386                 
04387                 The function calls the Action::Init function passing the runtime class 
04388                 of a RestoreSelectionsAction.
04389     Errors:     -
04390     SeeAlso:    Action::Init
04391 
04392 ********************************************************************************************/
04393 
04394 ActionCode RestoreSelectionsAction::Init(Operation* const pOp, 
04395                                          ActionList* pActionList,   
04396                                          SelectionState* SelState,
04397                                          BOOL Toggle, 
04398                                          BOOL ToggleStatus,
04399                                          BOOL SelStateShared,
04400                                          BOOL RenderStartBlobs, 
04401                                          BOOL RenderEndBlobs, 
04402                                          BOOL StartRestore,  
04403                                          Action** NewAction)       
04404 {    
04405     // If the SelState is shared then Toggle must be TRUE. This is so that we know when
04406     // to delete the SelState. 
04407     ENSURE( (!SelStateShared || Toggle), "Invalid flags passed to RestoreSelectionsAction"); 
04408     
04409     // Try to allocate memory for the action 
04410     ActionCode Ac = (Action::Init(pOp,
04411                      pActionList,
04412                      sizeof(RestoreSelectionsAction) + SelState->GetSize(),
04413                      CC_RUNTIME_CLASS(RestoreSelectionsAction), 
04414                      NewAction));    
04415     if (*NewAction != NULL) 
04416     {
04417         ((RestoreSelectionsAction*)(*NewAction))->SelState = SelState;
04418         ((RestoreSelectionsAction*)(*NewAction))->toggle = Toggle; 
04419         ((RestoreSelectionsAction*)(*NewAction))->toggleStatus = ToggleStatus; 
04420         ((RestoreSelectionsAction*)(*NewAction))->SelStateShared = SelStateShared; 
04421         ((RestoreSelectionsAction*)(*NewAction))->RenderStartBlobs = RenderStartBlobs; 
04422         ((RestoreSelectionsAction*)(*NewAction))->RenderEndBlobs = RenderEndBlobs; 
04423         ((RestoreSelectionsAction*)(*NewAction))->StartRestore = StartRestore; 
04424 
04425     }
04426     return (Ac); 
04427 }
04428 
04429 
04430 //------------------------------------------------------------------------------------------                           
04431 // SelectDeselectAction methods
04432     
04433 /********************************************************************************************
04434 
04435 >   SelectDeselectAction::SelectDeselectAction()
04436 
04437     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04438     Created:    14/9/93
04439     Inputs:     -
04440     Outputs:    -
04441     Returns:    -
04442     Purpose:    SelectDeselectAction constructor
04443     Errors:     -
04444     SeeAlso:    -
04445 
04446 ********************************************************************************************/
04447     
04448 SelectDeselectAction::SelectDeselectAction()
04449 {
04450 }
04451              
04452 SelectDeselectAction::~SelectDeselectAction() 
04453 {
04454 }
04455  
04456 /********************************************************************************************
04457 
04458 >   virtual ActionCode SelectDeselectAction::Execute()
04459 
04460     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04461     Created:    16/8/93
04462     Inputs:     -
04463     Outputs:    ActionCode indicating if the action was successfully executed or not
04464     Returns:    -
04465     Purpose:    Executes the SelectDeselectAction which will render node's selection blobs 
04466                 (which could remove them), and set it's selection state. If node is 
04467                 currently selected then the node is deselected. If node is currently 
04468                 deselected it is selected. It creates a second SelectDeselectAction for the 
04469                 node and adds it to the opposite action list.  
04470     Errors:     -
04471     SeeAlso:    -
04472     
04473     
04474 ********************************************************************************************/
04475  
04476 ActionCode SelectDeselectAction::Execute()
04477 {  
04478     SelectDeselectAction* SelDeAct;  
04479     ActionCode ActCode;  
04480     // Attempt to initialise the action    
04481     if ((ActCode = SelectDeselectAction::Init(pOperation,                    
04482                                               pOppositeActLst,  
04483                                               node,
04484                                               Parent,
04485                                               ( Action**)(&SelDeAct))) != AC_FAIL) 
04486     {   
04487         // The action was successfully initialised   
04488         ENSURE((node->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk))), 
04489             "Cannot select/deselect a non NodeRenderableInk node"); 
04490             
04491         if (node->IsSelected())  
04492             ((NodeRenderableInk*)node)->DeSelect(FALSE); // DeSelect   
04493         else 
04494             ((NodeRenderableInk*)node)->Select(FALSE);  // Select 
04495     }             
04496     return (ActCode);                                       
04497 }     
04498 
04499 
04500 /********************************************************************************************
04501 
04502 >   static ActionCode SelectDeselectAction::::Init(Operation* const pOp, 
04503                                                    ActionList* pActionList,     
04504                                                    UINT32 ActionSize, 
04505                                                    Node* SelDeNode, 
04506                                                    Action** NewAction)
04507 
04508     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04509     Created:    14/9/93  
04510     
04511     Inputs:     pOp: The operation to which the action should be added
04512                 
04513                 pActionList: The action list in the operation object
04514                 
04515                 Size:        The size of the action in bytes. This should be the total  
04516                              size of the action (including any objects pointed to by the
04517                              action).      
04518                              
04519     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
04520 
04521     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
04522                              action and the user did not wish to continue. Usually 
04523                              End() should be called in this situation. 
04524                          
04525                 AC_NORECORD: There was not enough room in the operation history for
04526                              the action, but the user requested that he wished to 
04527                              continue without undo. 
04528                 
04529                 AC_OK      : The action was successfully initialised and added to the 
04530                              operation. 
04531                          
04532                            
04533     Purpose:    To check that there is sufficient room for the action in the operation
04534                 history, and if there is, then to add the action to the operations 
04535                 action list. 
04536                 
04537                 The function calls the Action::Init function passing the runtime class 
04538                 of a ShowNodeAction.
04539     Errors:     -
04540     SeeAlso:    Action::Init
04541 
04542 ********************************************************************************************/
04543         
04544 ActionCode SelectDeselectAction::Init(Operation* const pOp, 
04545                                 ActionList* pActionList,    
04546                                 Node* SelDeNode,
04547                                 Spread* pSpread,
04548                                 Action** NewAction)
04549 { 
04550     ActionCode Ac = (Action::Init(pOp,
04551                      pActionList,
04552                      sizeof(SelectDeselectAction), 
04553                      CC_RUNTIME_CLASS(SelectDeselectAction), 
04554                      NewAction)); 
04555                        
04556     if (Ac != AC_FAIL)
04557         if (*NewAction != NULL)
04558         {
04559             ((SelectDeselectAction*)(*NewAction))->node = SelDeNode;
04560             ((SelectDeselectAction*)(*NewAction))->Parent = pSpread;
04561         }
04562                          
04563     return (Ac); 
04564 } 
04565  
04566 //------------------------------------------------------------------------------------------                           
04567 // TransformNodeAction methods
04568     
04569 /********************************************************************************************
04570 
04571 >   TransformNodeAction::TransformNodeAction()
04572 
04573     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04574     Created:    14/9/93
04575     Inputs:     -
04576     Outputs:    -
04577     Returns:    -
04578     Purpose:    TransformNodeAction constructor
04579     Errors:     -
04580     SeeAlso:    -
04581 
04582 ********************************************************************************************/
04583     
04584 TransformNodeAction::TransformNodeAction()
04585 {
04586     m_pNodeList = NULL;
04587     pTrans = NULL;
04588 }
04589              
04590 TransformNodeAction::~TransformNodeAction() 
04591 {
04592     if (m_pNodeList)
04593     {
04594         m_pNodeList->DeleteAll();
04595         delete m_pNodeList;
04596         m_pNodeList = NULL;
04597     }
04598 
04599     if (pTrans)
04600     {
04601         delete pTrans;
04602         pTrans = NULL;
04603     }
04604 }   
04605 
04606 
04607 /********************************************************************************************
04608 
04609 >   virtual ActionCode TransformNodeAction::Execute()
04610 
04611     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04612     Created:    16/8/93
04613     Inputs:     -
04614     Outputs:    ActionCode indicating if the action was successfully executed or not
04615     Returns:    -
04616     Purpose:    Executes the TransformNodeAction which transforms the range of nodes. It also 
04617                 creates another TransformNodeAction passing it the inverse of the 
04618                 transform 
04619     Errors:     -
04620     SeeAlso:    -
04621     
04622     
04623 ********************************************************************************************/
04624 ActionCode TransformNodeAction::Execute()
04625 {  
04626     // DMc - changed 17/8/99
04627     // It didn't work in its old state with compound nodes as the node range seemed to
04628     // be corrupted in the transformations
04629     // Thus, made it more robust by having a node list of the range which is first passed in
04630 
04631     // Further comment. Looks like things have been fiddled with too much to rely on the
04632     // range to be correct when redoing the op (check beveled shapes with text stories)
04633     // So I tidied up Daves node lists and corrected his transform matrix (sjk 9/8/00)
04634     TransformNodeAction* TransformNodeAct;  
04635     ActionCode ActCode;  
04636 
04637     Trans2DMatrix * pTransform = new Trans2DMatrix((const Trans2DMatrix&) *pTrans);
04638     pTransform->Invert();
04639     
04640     // Create an action to transform the node back to its initial position
04641     if ( (ActCode = TransformNodeAction::Init(pOperation, 
04642                                               pOppositeActLst, 
04643                                               NodeRange, 
04644                                               pTransform,
04645                                               m_pNodeList,
04646                                               (Action**)(&TransformNodeAct)))
04647         != AC_FAIL)  
04648     {   
04649         NodeListItem * pItem = (NodeListItem *)m_pNodeList->GetHead();
04650         Node * pNode = NULL;
04651 
04652         while (pItem)
04653         {
04654             pNode = pItem->pNode;
04655             
04656             if (((NodeRenderable *)pNode)->IsNodeRenderableClass())
04657             {       
04658                 ((NodeRenderable *)pNode)->Transform(*pTrans);              
04659                 // set the parent layer as having being edited
04660                 pNode->SetParentLayerAsEdited();
04661             }
04662 
04663             pItem = (NodeListItem *)m_pNodeList->GetNext(pItem);
04664         }       
04665     };  
04666                     
04667     return (ActCode);                   
04668 }     
04669 
04670 
04671 /********************************************************************************************
04672 
04673 >   static ActionCode TransformNodeAction::Init(Operation* const pOp, 
04674                                                 ActionList* pActionList, 
04675                                                 Range NodeRangeIn, 
04676                                                 TransformBase* Trans    
04677                                                 Action** NewAction) 
04678     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04679     Created:    14/9/93  
04680     
04681     Inputs:     pOp: The operation to which the action should be added
04682                 
04683                 pActionList: The action list in the operation object
04684                 
04685                              
04686                 NodeRange:   The range of nodes to transform 
04687 
04688                 Trans:       The Transform to apply to the nodes in the range 
04689 
04690     Outputs:    NewAction:   A pointer to the action if it could be allocated. 
04691 
04692     Returns:    AC_FAIL:     There was not enough room in the operation history for the 
04693                              action and the user did not wish to continue. Usually 
04694                              End() should be called in this situation. 
04695                          
04696                 AC_NORECORD: There was not enough room in the operation history for
04697                              the action, but the user requested that he wished to 
04698                              continue without undo. 
04699                 
04700                 AC_OK      : The action was successfully initialised and added to the 
04701                              operation. 
04702                          
04703                            
04704     Purpose:    To check that there is sufficient room for the action in the operation
04705                 history, and if there is, then to add the action to the operations 
04706                 action list. 
04707                 
04708                 The function calls the Action::Init function passing the runtime class 
04709                 of a TransformNodeAction.
04710     Errors:     -
04711     SeeAlso:    Action::Init
04712 
04713 ********************************************************************************************/
04714 ActionCode TransformNodeAction::Init(Operation* const pOp, 
04715                                      ActionList* pActionList, 
04716                                      Range NodeRangeIn, 
04717                                      TransformBase* Trans,
04718                                      List * pNodeList,
04719                                      Action** NewAction)
04720 { 
04721     ActionCode Ac = (Action::Init(pOp,
04722                                   pActionList,
04723                                   sizeof(TransformNodeAction),
04724                                   CC_RUNTIME_CLASS(TransformNodeAction), 
04725                                   NewAction));   
04726                            
04727     if (Ac != AC_FAIL)
04728     {
04729         if (*NewAction != NULL)  
04730         {
04731             // Store the range and transform in the action 
04732             ((TransformNodeAction*)(*NewAction))->NodeRange = NodeRangeIn;
04733             ((TransformNodeAction*)(*NewAction))->pTrans = Trans;
04734             if (((TransformNodeAction*)(*NewAction))->m_pNodeList)
04735             {
04736                 ((TransformNodeAction*)(*NewAction))->m_pNodeList->DeleteAll();
04737                 delete ((TransformNodeAction*)(*NewAction))->m_pNodeList;
04738                 ((TransformNodeAction*)(*NewAction))->m_pNodeList = NULL;
04739             }
04740 
04741             // run through the original node range, collecting all the selected nodes
04742         
04743             ((TransformNodeAction*)(*NewAction))->m_pNodeList = new List;
04744             if (pNodeList == NULL)
04745             {
04746                 Node *pNode = ((TransformNodeAction*)(*NewAction))->NodeRange.FindFirst();                      
04747                 while (pNode)                                           
04748                 {                                                       
04749                     if (((NodeRenderable *)pNode)->IsNodeRenderableClass())
04750                     {
04751                         TRACEUSER( "SimonK", _T("trans node : %s\n"), (LPCTSTR) pNode->GetRuntimeClass()->GetClassName() );
04752 
04753                         NodeListItem * pItem = new NodeListItem(pNode);
04754                         if (pItem)
04755                             ((TransformNodeAction*)(*NewAction))->m_pNodeList->AddTail(pItem);
04756                     }
04757                     pNode = ((TransformNodeAction*)(*NewAction))->NodeRange.FindNext(pNode, FALSE);                     
04758                 }
04759             }
04760             else
04761             {
04762                 NodeListItem * pItem = (NodeListItem *)(pNodeList->GetHead());
04763                 Node * pNode = NULL;
04764 
04765                 while (pItem)
04766                 {
04767                     pNode = pItem->pNode;
04768                     
04769                     if (((NodeRenderable *)pNode)->IsNodeRenderableClass())
04770                     {                       
04771                         NodeListItem * pItem = new NodeListItem(pNode);
04772                         if (pItem)
04773                             ((TransformNodeAction*)(*NewAction))->m_pNodeList->AddTail(pItem);
04774                     }
04775 
04776                     pItem = (NodeListItem *)pNodeList->GetNext(pItem);
04777                 }       
04778             }
04779         }
04780 
04781     }            
04782     return (Ac); 
04783 } 
04784 
04785 
04786 /********************************************************************************************
04787 
04788 >   virtual void TransformNodeAction::Slaughter()
04789 
04790     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04791     Created:    6/7/93
04792     Inputs:     -
04793     Outputs:    -
04794     Returns:    -
04795     Purpose:    destructor which gets called when an operation is deleted  
04796     Errors:     -
04797     SeeAlso:    Action::Slaughter
04798 
04799 ********************************************************************************************/
04800 
04801 void TransformNodeAction::Slaughter() 
04802 {         
04803     // Destroy the Trans object      
04804     delete pTrans;
04805     pTrans = NULL;
04806     Action::Slaughter(); // Call base class to destroy this 
04807 }   
04808 
04809 /********************************************************************************************
04810 
04811 >   TransformBase* TransformNodeAction::GetTransform()
04812 
04813     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04814     Created:    7/11/94
04815     Inputs:     -
04816     Outputs:    -
04817     Returns:    -
04818     Purpose:    Used to find the transform within the TransformNodeAction
04819     Errors:     -
04820     SeeAlso:    -
04821 
04822 ********************************************************************************************/
04823 
04824 TransformBase* TransformNodeAction::GetTransform()
04825 {
04826     return (pTrans);    
04827 } 
04828 
04829 
04830 /********************************************************************************************
04831 
04832 >   void TransformNodeAction::CombineWith(TransformNodeAction* pMatrixTransformAction)
04833 
04834     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
04835     Created:    7/11/94
04836     Inputs:     pMatrixTransformAction: A pointer to a TransformNodeAction containing a 
04837                 Trans2DMatrix Transform
04838     Outputs:    -
04839     Returns:    -
04840     Purpose:    This function can only be called on a TransformNodeAction with a Trans2DMatrix
04841                 Transform. The transform's matrix is multiplied by the matrix in the 
04842                 pTransformAction therby combining the transforms.
04843     Errors:     An Error3 will occur if either this Transform or the pMatrixTransformAction
04844                 do not contain Trans2DMatrix transforms. 
04845     SeeAlso:    -
04846 
04847 ********************************************************************************************/
04848 
04849 void TransformNodeAction::CombineWith(TransformNodeAction* pMatrixTransformAction) 
04850 {
04851     // First lets make sure that we can combine the transforms
04852     ERROR3IF(!(pMatrixTransformAction->GetTransform()->IS_KIND_OF(Trans2DMatrix)), 
04853              "Cannot combine Transform actions");
04854     ERROR3IF(!(GetTransform()->IS_KIND_OF(Trans2DMatrix)), 
04855              "Cannot combine transform actions");
04856     
04857     Trans2DMatrix* Trans1 = ((Trans2DMatrix*)GetTransform()); 
04858     Trans2DMatrix* Trans2 = ((Trans2DMatrix*)(pMatrixTransformAction->GetTransform()));
04859     (*Trans1)*=(*Trans2);        
04860 }
04861 
04862 
04863 
04864 /********************************************************************************************
04865 
04866     void Operation::OnClickWhileDragging(OilCoord PointerPos, ClickType Click, ClickModifiers Mods, BOOL bSolidDrag)
04867     Author:     Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com>
04868     Created:    18/6/96
04869     Inputs:     OilCoord PointerPos: position of click
04870                 ClickType Click: single, double or triple
04871                 ClickModifiers Mods: whether shift, ctrl etc was pressed during the click
04872     Outputs:    -
04873     Returns:    -
04874     Purpose:    This virtual function handles clicks with one mouse button while
04875                 another mouse button is dragging. For example, the user drags with the
04876                 left mouse button and clicks the right button during the drag.
04877 
04878                 This particular case of the virtual function does nothing. It is
04879                 overridden if the operation is a TransOperation, by TransOperation::
04880                 OnClickWhileDragging.
04881 
04882                 Feel free to add other overriding function to handle clicks-while-
04883                 dragging for other operations.
04884 
04885     Errors:     None. 
04886     SeeAlso:    ClickModifiers::ClickWhileDrag (member variable); DocView::OnClick;
04887                 ScreenView::HandleDragEvent; ScreenView::HandleButtonUp;
04888                 TransOperation::OnClickWhileDragging
04889 
04890 ********************************************************************************************/
04891 
04892 
04893 void Operation::OnClickWhileDragging(OilCoord PointerPos, ClickType Click, ClickModifiers Mods, BOOL bSolidDrag)
04894 {
04895     //This function does nothing. It's overridden by TransOperation::OnClickWhileDragging.
04896 }

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