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 }