00001 // $Id: transop.cpp 1393 2006-06-30 10:27:41Z phil $ 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 00101 #include "camtypes.h" 00102 00103 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00104 #include "transop.h" 00105 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 //#include "selstate.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00107 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00108 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00110 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00111 #include "layer.h" 00112 #include "progress.h" 00113 #include "selector.h" 00114 #include "objchge.h" 00115 #include "keypress.h" 00116 //#include "rik.h" 00117 //#include "clikdrag.h" 00118 #include "cutop.h" 00119 //#include "mario.h" 00120 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00121 #include "nodecont.h" 00122 //#include "dragtool.h" // for DragTool. 00123 00124 //Matt 6/11/00 - I only wanted a function from the next line... 00125 #include "slicehelper.h" //For helper functions 00126 00127 //But I had to include all of these to get it to work!!... 00128 //#include "cxfrech.h" //For CamelotRecordHandler - in camtypes.h [AUTOMATICALLY REMOVED] 00129 #include "userattr.h" //For UserAttr 00130 #include "tmpltatr.h" //For TemplateAttribute 00131 #include "ophist.h" 00132 00133 //Graham 30/9/96: Need to maintain a node list for Drop Copy 00134 //#include "list.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00135 //#include "range.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00136 00137 // declare this files version number to the program and give our name in memory dumps 00138 DECLARE_SOURCE("$Revision: 1393 $"); 00139 CC_IMPLEMENT_DYNCREATE(TransOperation, UndoableOperation) 00140 00141 // Declare smart memory handling in Debug builds 00142 #define new CAM_DEBUG_NEW 00143 00144 // Control how the dragged copies of the selection are modified during solid dragging 00145 // 1 = delete and recopy objects (fails when background render suspends on object that gets deleted) 00146 // 2 = copy object contents (fails when the two trees get mismatched) 00147 // 3 = transform copies back to origin (fails when the scale gets very small, matrix inversion fails, errors get magnified horribly) 00148 // 4 = Combined: transform most of the time, but if scale gets very small fall back to delete and recopy 00149 00150 #define SOLID_TRANSFORM_METHOD 4 00151 00152 /******************************************************************************************** 00153 00154 Preference: ClickWhileDragFunc 00155 Section: Mouse 00156 Range: 0 to 3 00157 Purpose: Determine the function of a. clicking while dragging and b. the Num + 00158 key while dragging. 00159 00160 0,1 - Leave copy 00161 2,3 - Drop copy 00162 00163 At present, both a. and b. have the same function. However, this 00164 preference is set up with four values so that each of a. and b. 00165 could have either of the two functions. 00166 00167 ********************************************************************************************/ 00168 INT32 TransOperation::ClickWhileDragFunc = 3; 00169 00170 00171 /******************************************************************************************** 00172 00173 > TransOperation::TransOperation() 00174 00175 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00176 Created: 15/2/94 00177 Purpose: TransOperation constructor, calls the constructor of the base class 00178 00179 ********************************************************************************************/ 00180 00181 TransOperation::TransOperation(): UndoableOperation() 00182 { 00183 // Give all the flags default values 00184 LockAspect = FALSE; 00185 LeaveCopy = TRUE; 00186 00187 // The scale factor is used to scale the line widths and by default it is ignored 00188 ScaleLines = FALSE; 00189 CanScaleLines = TRUE; 00190 TransFills = TRUE; 00191 00192 StatusHelpID = 0; 00193 StatusHelpID2=0; 00194 00195 //Graham: We want this drag to transform the objects when it ends 00196 AbortTransAtEnd=FALSE; 00197 00198 ObjectUnderCursor = NULL; 00199 SelState = NULL; 00200 RecordTwice = FALSE; 00201 00202 m_pSelList = NULL; 00203 m_pDraggedRange = NULL; 00204 m_pTransformRange = NULL; 00205 m_bFirstRedraw = FALSE; 00206 00207 m_PrevTransform.SetTransform(); 00208 00209 m_bShowDraggedOutlines = FALSE; 00210 m_bShowOriginalOutlines = FALSE; 00211 m_bRangeCacheTransformed = TRUE; 00212 m_bRangeFullyTransformed = TRUE; 00213 00214 StartSpread = NULL; 00215 CurrentSpread = NULL; 00216 } 00217 00218 /******************************************************************************************** 00219 00220 > TransOperation::~TransOperation() 00221 00222 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00223 Created: 15/2/94 00224 Purpose: TransOperation destructor 00225 00226 ********************************************************************************************/ 00227 00228 TransOperation::~TransOperation() 00229 { 00230 if (m_pSelList) 00231 { 00232 m_pSelList->DeleteAll(); 00233 delete m_pSelList; 00234 m_pSelList = NULL; 00235 } 00236 00237 if (m_pDraggedRange) 00238 { 00239 delete m_pDraggedRange; 00240 m_pDraggedRange = NULL; 00241 } 00242 00243 if (m_pTransformRange) 00244 { 00245 delete m_pTransformRange; 00246 m_pTransformRange = NULL; 00247 } 00248 00249 } 00250 00251 /******************************************************************************************** 00252 00253 > BOOL TransOperation::DoStartTransOp(BOOL RecordSelTwice, Node* NodeToTransform = NULL, 00254 Range* RangeToTransform = NULL) 00255 00256 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00257 Created: 15/2/94 00258 Inputs: RecordSelTwice - TRUE if the selection should be recorded twice (once at 00259 the start of the operation and once at the end). If it is FALSE the selection 00260 is only recorded at the start of the operation. This should only be set to TRUE 00261 if the selection is different at the end of the operation (This is quite rare) 00262 00263 NodeToTransform: Specifies the object that will be transformed. 00264 The default NULL value specifies that the entire selection will be transformed. 00265 00266 RangeToTransform: Specifies the range of objects to use. 00267 The default NULL value specifies that the entire selection is the range. 00268 At present only used when NodeToTransform is NULL. Used by page resizing to 00269 move all the objects on the page regardless of selection. Hence, it does not 00270 call the AllowOp if the range is selected as AllowOp errors if no selection on 00271 things like NodeBlend and NodeText. 00272 00273 00274 Returns: TRUE if successful, FALSE if memory ran out (call End) 00275 Purpose: This function must be called by all TransOperations. It does the following: 00276 Records the current selection status 00277 Invalidates the rectangle covered by the selection and its blobs. 00278 SeeAlso: TransOperation::End() 00279 00280 ********************************************************************************************/ 00281 00282 BOOL TransOperation::DoStartTransOp(BOOL RecordSelTwice, Node* NodeToTransform, 00283 Range* RangeToTransform) 00284 { 00285 // Create a SelectionState object 00286 ALLOC_WITH_FAIL(SelState, (new SelectionState()), this); 00287 if (SelState == NULL) 00288 { 00289 return FALSE; // Failure 00290 } 00291 00292 // We have managed to create a SelectionState instance, now lets try and 00293 // record the current selections 00294 00295 BOOL Success; 00296 00297 CALL_WITH_FAIL(SelState->Record(), this, Success) 00298 00299 if (!Success) // We failed to allocate enough memory to store the selection state 00300 { 00301 // There was insufficient memory to record the selections 00302 delete SelState; // Delete the selection state 00303 SelState = NULL; 00304 return FALSE; 00305 } 00306 00307 // We have successfully managed to create a Selection state, create an action 00308 // to restore the selections when executed 00309 00310 // Recorded the current selection state ok 00311 RestoreSelectionsAction* RestoreAct; 00312 ActionCode ActCode; 00313 RecordTwice = RecordSelTwice; 00314 00315 // Attempt to initialise the action 00316 ActCode = RestoreSelectionsAction::Init(this, 00317 &UndoActions, 00318 SelState, 00319 !RecordTwice, // # Toggle 00320 FALSE, // This action does not restore the sels 00321 // restore the selection state but 00322 // its twin will 00323 !RecordTwice, // The SelState is shared 00324 FALSE, // Don't draw the selection blobs 00325 FALSE, 00326 FALSE, // End restore 00327 ( Action**)(&RestoreAct)); 00328 00329 if (ActCode == AC_FAIL) 00330 { 00331 delete SelState; // We won't be needing this 00332 return FALSE; 00333 } 00334 00335 // Create our local transform range 00336 SetTransformRange(RangeToTransform, NodeToTransform); 00337 00338 // Ask the range whether it's OK to do the transform... 00339 // Get an ObjChangeParam ready, so we can ask op permission from nodes. 00340 ObjChangeFlags cFlags(FALSE,FALSE,FALSE,FALSE,FALSE,TRUE); // flag this is a transform 00341 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); 00342 BOOL bOK = m_pTransformRange->AllowOp(&ObjChange); 00343 if (!bOK) 00344 return FALSE; 00345 00346 // Invalidate the regions covered by all the selected objects and their blobs 00347 // return (DoInvalidateNodesRegions(*m_pTransformRange, TRUE)); 00348 bOK = DoSmartInvalidate(); 00349 return bOK; 00350 } 00351 00352 00353 00354 /******************************************************************************************** 00355 00356 > BOOL TransOperation::DoEndTransOp() 00357 00358 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00359 Created: 15/2/94 00360 Purpose: This function gets called automatically by End() 00361 It Invalidates the rectangle covered by the selection and its blobs 00362 Scope: protected 00363 00364 ********************************************************************************************/ 00365 00366 BOOL TransOperation::DoEndTransOp() 00367 { 00368 // If this is a translation and the nodes all managed to transform their cached 00369 // info successfully then we should avoid forcing them to recache now. 00370 // So we must do some cleverer recaching 00371 BOOL bOK = DoSmartInvalidate(); 00372 if (!bOK) return FALSE; 00373 00374 // If we want to record the selection again, then build a new Selection State object 00375 // and record the selection into it. 00376 if (RecordTwice) 00377 { 00378 // Create a SelectionState object 00379 ALLOC_WITH_FAIL(SelState, (new SelectionState()), this); 00380 if (SelState == NULL) 00381 { 00382 return FALSE; // Failure 00383 } 00384 00385 // We have managed to create a SelectionState instance, now lets try and 00386 // record the current selections 00387 00388 BOOL Success; 00389 00390 CALL_WITH_FAIL(SelState->Record(), this, Success) 00391 00392 if (!Success) // We failed to allocate enough memory to store the selection state 00393 { 00394 // There was insufficient memory to record the selections 00395 delete SelState; // Delete the selection state 00396 SelState = NULL; 00397 return FALSE; 00398 } 00399 00400 // We have successfully managed to create a Selection state, create an action 00401 // to restore the selections when executed 00402 } 00403 00404 // Recorded the current selection state ok 00405 RestoreSelectionsAction* RestoreAct; 00406 ActionCode ActCode; 00407 00408 // The SelState should have been created in the DoStartSelectionOp method. 00409 // maybe it was never called !!! 00410 ERROR2IF(SelState==NULL, FALSE, "We don't have a selection state"); 00411 00412 // Attempt to initialise the action 00413 ActCode = RestoreSelectionsAction::Init(this, 00414 &UndoActions, 00415 SelState, 00416 !RecordTwice, // Toggle 00417 TRUE, // This action does not restore the sels 00418 // restore the selection state but 00419 // its twin will 00420 !RecordTwice, // The SelState is shared 00421 FALSE, // Don't draw the selection blobs 00422 FALSE, 00423 TRUE, // End restore 00424 ( Action**)(&RestoreAct)); 00425 00426 // See if it all worked 00427 if (ActCode == AC_FAIL) 00428 { 00429 // We won't be needing this 00430 delete SelState; 00431 return FALSE; 00432 } 00433 00434 // Inform all changed nodes that we have finished 00435 ObjChangeFlags cFlags; 00436 ObjChangeParam ObjChange(OBJCHANGE_FINISHED,cFlags,NULL,this); 00437 ObjChange.SetRetainCachedData(Transform.IsTranslation() && m_bRangeCacheTransformed); // Same condition in DoSmartInvalidate 00438 BOOL ok = UpdateChangedNodes(&ObjChange); 00439 00440 // BODGE TEXT - quick (harmless?) fix for bug 'transforming 2 selected text stories => sel blobs incorrect' 00441 // it is actually due to the fact that update change nodes is not called before the sel bounds are recached 00442 SelRange* pSelRange = Camelot.FindSelection(); 00443 if (pSelRange != NULL) 00444 { 00445 BOOL bOldValue = pSelRange->SetPromoteToParent(TRUE); 00446 00447 pSelRange->UpdateBounds(); 00448 00449 // Try to make sure that the spread's pasteboard is big enough to include the transformed 00450 // objects. This is very quick if the pasteboard is big enough already. 00451 Node *Bob = pSelRange->FindFirst(); 00452 if (Bob != NULL) 00453 { 00454 Spread *pSpread = Bob->FindParentSpread(); 00455 00456 DocRect IncludeRect = pSelRange->GetBoundingRect(); 00457 pSpread->ExpandPasteboardToInclude(IncludeRect); 00458 } 00459 00460 pSelRange->SetPromoteToParent(bOldValue); 00461 } 00462 00463 return ok; 00464 } 00465 00466 00467 00468 00469 /******************************************************************************************** 00470 00471 > BOOL TransOperation::DoSmartInvalidate() 00472 00473 00474 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00475 Created: 09/12/2004 00476 Purpose: Invalidate the transform range of nodes but be careful 00477 about releasing cache info so that translations can retain cached info 00478 if possible 00479 00480 ********************************************************************************************/ 00481 00482 BOOL TransOperation::DoSmartInvalidate() 00483 { 00484 // Issue undoable redraws... 00485 if (m_pTransformRange && !DoInvalidateNodesRegions(*m_pTransformRange, TRUE, FALSE, FALSE, FALSE)) 00486 return FALSE; 00487 00488 // Conditionally update cached info 00489 Node* pNode = m_pTransformRange->FindFirst(); 00490 while (pNode) 00491 { 00492 if (pNode->IsBounded()) 00493 { 00494 NodeRenderableBounded* pBoundNode = (NodeRenderableBounded*)pNode; 00495 00496 // If we have transformed all cached data successfully 00497 // And 00498 // We are translating (in which case the cached data will remain usable) 00499 // Or 00500 // The node was Direct (in which case the cached data comes from outside the tree where it can't have been affected by the transform) 00501 // Then 00502 // Don't force the recacheing of the data for this node, just its parents 00503 if (m_bRangeCacheTransformed && (Transform.IsTranslation() || pBoundNode->HasCachedDirectBitmap())) 00504 pBoundNode->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only 00505 else 00506 pBoundNode->ReleaseCached(TRUE, TRUE, TRUE, TRUE); // Parents, children, self and derived data 00507 } 00508 00509 pNode = m_pTransformRange->FindNext(pNode); 00510 } 00511 00512 return TRUE; 00513 } 00514 00515 00516 00517 00518 /******************************************************************************************** 00519 00520 > virtual void TransOperation::End() 00521 00522 00523 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00524 Created: 15/2/94 00525 Purpose: Calls DoEndTransOp(), then Operation::End() 00526 00527 ********************************************************************************************/ 00528 00529 void TransOperation::End() 00530 { 00531 if (OpStatus == DO && (!OpFlags.Failed)) 00532 { 00533 // We don't want to perform this if we are undoing or redoing do we 00534 DoEndTransOp(); 00535 } 00536 00537 Operation::End(); 00538 } 00539 00540 00541 00542 00543 00544 00545 00546 /******************************************************************************************** 00547 00548 > void TransOperation::DoWithParam(OpDescriptor* pDescriptor, OpParam* pOpParam) 00549 00550 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00551 Created: 19/7/94 00552 Inputs: pDescriptor - The operations descriptor 00553 pOpParam - The parameters for this operation 00554 Purpose: Does an immediate version of the transform. This attempts to transform the 00555 selection and build all the neccesary Undo information. Derived class only 00556 need overide InitTransformImmediate() to set up the specifics of there own 00557 transform unless they need to do something not covered by this base class 00558 version 00559 00560 ********************************************************************************************/ 00561 00562 void TransOperation::DoWithParam(OpDescriptor* /*pDescriptor*/, OpParam* pOpParam) 00563 { 00564 // Get at my data 00565 TransformData* TransData = (TransformData *)(void *)(pOpParam->Param1); 00566 00567 // copy across all the relavent bits of data for the transform 00568 CentreOfTrans = TransData->CentreOfTrans; 00569 LockAspect = TransData->LockAspect; 00570 LeaveCopy = TransData->LeaveCopy; 00571 ScaleLines = TransData->ScaleLines; 00572 TransFills = TransData->TransFills; 00573 00574 SetTransformRange(TransData->pRange, NULL); 00575 00576 // Set spread pointers now to keep CompleteTransformation happy even 00577 // though we don't expect them to be used or changed in Immediate transforms 00578 StartSpread = NULL; 00579 CurrentSpread = StartSpread; 00580 00581 // Where was this operation started from, and are we interested 00582 SetStartBlob(TransData->StartBlob); 00583 00584 // Allow the specific transforms to set up anything that they may need to set 00585 InitTransformImmediate(pOpParam); 00586 00587 // Build the transform matrix 00588 BuildMatrix(); 00589 00590 // Build the undo if there was a selection 00591 if (!CompleteTransformation()) 00592 FailAndExecute(); 00593 00594 // mark the selection cache as invalid (bounding rects etc will no longer be correct) 00595 m_pTransformRange->Update(); 00596 00597 // End the operation 00598 End(); 00599 } 00600 00601 00602 00603 00604 /******************************************************************************************** 00605 00606 > virtual void TransOperation::DragStarted(TransformData* TransData, SelectorTool* pTool, 00607 TransformBoundingData* pBounds, DocCoord ClickPos, Spread* pSpread, 00608 ClickModifiers ClickMods, 00609 DocCoord ClickOffset, 00610 Node* NodeToTransform = NULL, 00611 DragType dragtype = DRAGTYPE_AUTOSCROLL, 00612 BOOL bSolidDrag) 00613 00614 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00615 Created: 19/7/94 00616 Inputs: TransData - Extra data about the transform including info about the centre 00617 of transformation and several flags that may effect the behaviour of the 00618 operation 00619 pTool - A pointer to the selector tool so we can call it back (Can be NULL) 00620 pBounds - A pointer to the structure that contains all the bounds info about 00621 the current selection (Only required of pTool specified) 00622 ClickPos - The position of the click that is initiating this drag 00623 pSpread - The spread that the drag is starting on 00624 ClickMods - The click modifiers that were active at the start of the drag 00625 ClickOffset - The amount to offset the drag by as the Bounds Blobs are not 00626 on the exact corner of the selection, but further away. 00627 NodeToTransform - Specifies the object to transform (must be selected). 00628 If NULL the applications selection is used. 00629 dragtype --- what to do if the user drags the mouse beyond the view 00630 window, by default scroll the view. 00631 Purpose: Starts an interactive drag version of the transformation operation. 00632 00633 ********************************************************************************************/ 00634 00635 /******************************************************************************************** 00636 00637 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00638 Created: 14/10/1999 00639 Purpose: Changes made: pTool is now of type DragTool*, instead of SelectorTool*. 00640 00641 ********************************************************************************************/ 00642 00643 void TransOperation::DragStarted(TransformData* TransData, DragTool* pTool, 00644 TransformBoundingData* pBounds, DocCoord ClickPos, Spread* pSpread, 00645 ClickModifiers ClickMods, 00646 DocCoord ClickOffset, 00647 Node* NodeToTransform, 00648 DragType dragtype, 00649 BOOL bSolidDrag) 00650 { 00651 // Ensure all the data is acceptable 00652 ENSURE( TransData!=NULL, "The Transform Data was NULL in DragStarted"); 00653 00654 // What is the node that we should be concentrating on in this transform? 00655 // (even if many nodes are being transformed) 00656 // If we haven't been told of a specific node and the drag pos is not offset 00657 // Then we can try to look for one directly under the cursor. 00658 // Phil says: This is horrible because 00659 // (a) the caller already knows this info 00660 // (b) the primary dragged node might not be the simple one under the pointer 00661 ObjectUnderCursor = NodeToTransform; 00662 if (ObjectUnderCursor == NULL && !ShouldPointerBeOffset()) 00663 { 00664 ObjectUnderCursor = NodeRenderableInk::FindSimpleAtPoint(pSpread, ClickPos); 00665 } 00666 00667 // Copy all the data that we will want to be keeping 00668 StartSpread = pSpread; 00669 CurrentSpread = pSpread; 00670 m_pDragSpread = pSpread; 00671 RawStartPos = ClickPos; 00672 StartPos = ClickPos; 00673 MagStartPos = ClickPos; 00674 LastRawPos = ClickPos; 00675 Offset = ClickOffset; 00676 MouseHasMoved = FALSE; 00677 pSelTool = pTool; 00678 MagneticGripPoint = FALSE; 00679 00680 // Process raw mouse coordinate according to Offset distances... 00681 if (ShouldPointerBeOffset()) 00682 { 00683 StartPos.translate(Offset.x,Offset.y); 00684 MagStartPos = StartPos; 00685 } 00686 else if (ObjectUnderCursor != NULL) 00687 { 00688 // If the click position is the position we intend to drag (ie. not offset) 00689 // and we have been informed about the node that position refers to 00690 // then we can try magnetic snapping to get a more accurate start pos 00691 MagneticGripPoint = DocView::SnapToMagneticNode(pSpread, ObjectUnderCursor, &MagStartPos); 00692 } 00693 00694 // Get the information about the current size, position etc of the selection 00695 if (pBounds != NULL) 00696 { 00697 BoundingData.x = pBounds->x; 00698 BoundingData.y = pBounds->y; 00699 BoundingData.Width = pBounds->Width; 00700 BoundingData.Height = pBounds->Height; 00701 BoundingData.XScale = pBounds->XScale; 00702 BoundingData.YScale = pBounds->YScale; 00703 BoundingData.Rotation = pBounds->Rotation; 00704 BoundingData.Shear = pBounds->Shear; 00705 00706 // Get the bounding rectangle into an array ready for quick transformation 00707 OriginalBounds[0].x = BoundingData.x; 00708 OriginalBounds[0].y = BoundingData.y; 00709 OriginalBounds[1].x = BoundingData.x+BoundingData.Width; 00710 OriginalBounds[1].y = BoundingData.y; 00711 OriginalBounds[2].x = BoundingData.x+BoundingData.Width; 00712 OriginalBounds[2].y = BoundingData.y+BoundingData.Height; 00713 OriginalBounds[3].x = BoundingData.x; 00714 OriginalBounds[3].y = BoundingData.y+BoundingData.Height; 00715 00716 // Work out the centre of the selection 00717 BoundingCentre.x = BoundingData.x + (BoundingData.Width/2); 00718 BoundingCentre.y = BoundingData.y + (BoundingData.Height/2); 00719 00720 } 00721 m_AccuracyTestRect = DocRect(OriginalBounds[0].x, OriginalBounds[0].y, OriginalBounds[2].x, OriginalBounds[2].y); 00722 00723 // Get the info out of the structure 00724 OriginalCentre = TransData->CentreOfTrans; 00725 LockAspect = TransData->LockAspect; 00726 LeaveCopy = TransData->LeaveCopy; 00727 ScaleLines = TransData->ScaleLines; 00728 TransFills = TransData->TransFills; 00729 00730 BoundingData.XYChanged = FALSE; 00731 BoundingData.WHChanged = FALSE; 00732 BoundingData.ScaleChanged = FALSE; 00733 BoundingData.RotateChanged = FALSE; 00734 BoundingData.ShearChanged = FALSE; 00735 BoundingData.TransFillsChanged = FALSE; 00736 BoundingData.LeaveCopyChanged = FALSE; 00737 BoundingData.ScaleLinesChanged = FALSE; 00738 00739 // This section allows certain values passed into this func to be toggled 00740 // if their keyboard short-cuts are being pressed (markn 25/7/95) 00741 00742 // BOOL FlagsChanged = FALSE; 00743 00744 //Graham 28/6/96: If the + key is pressed do the following: 00745 //IF its function is to set the LeaveCopy flag, do it 00746 //IF its function is to drop a copy, do nothing 00747 if (KeyPress::IsKeyPressed(CAMKEY(NUMPAD_ADD))) 00748 { 00749 if (ClickWhileDragFunc>1) LeaveCopy = !LeaveCopy; 00750 } 00751 00752 if (KeyPress::IsKeyPressed(CAMKEY(NUMPAD_DIVIDE))) 00753 { 00754 ScaleLines = !ScaleLines; 00755 } 00756 00757 if (KeyPress::IsKeyPressed(CAMKEY(NUMPAD_SUBTRACT))) 00758 { 00759 TransFills = !TransFills; 00760 } 00761 00762 if (ClickMods.Menu) 00763 { 00764 AbortTransAtEnd = TRUE; 00765 StatusHelpID=_R(IDS_ABORT_TRANSFORMATION1); 00766 } 00767 00768 BoundingData.TransFills = TransFills; 00769 BoundingData.ScaleLines = ScaleLines; 00770 BoundingData.LeaveCopy = LeaveCopy; 00771 00772 // By default we will use the proper centre of transform. Adjust may change this later 00773 CentreOfTrans = OriginalCentre; 00774 00775 // Where was this operation started from, and are we interested 00776 SetStartBlob(TransData->StartBlob); 00777 00778 // Set up the rendering matrix parameters 00779 InitTransformOnDrag(GetStartPos(), ClickMods); 00780 00781 // Use the SliceHelper function to help the user to select buttons & bars... - Matt 9/11/2000 00782 SliceHelper::ModifySelectionToContainWholeButtonElements(); 00783 00784 // Go find the selection and make sure that there really is something in it 00785 SetTransformRange(TransData->pRange, NodeToTransform); 00786 00787 // Mike 01/02/95 - Change the range control to include NoneRenderables now 00788 RangeControl TransFlags = m_pTransformRange->GetRangeControlFlags(); 00789 TransFlags.IgnoreNoneRenderable=TRUE; 00790 00791 //Include invisible layers in the selection... Matt 9/11/2000 00792 TransFlags.IgnoreInvisibleLayers = TRUE; 00793 00794 m_pTransformRange->SetRangeControl(TransFlags); 00795 00796 Node* pFirstNode = m_pTransformRange->FindFirst(); 00797 ENSURE(pFirstNode!=NULL, "There was no selection to transform!"); 00798 if (pFirstNode==NULL) 00799 return; 00800 00801 // Reset the EORed stuff 00802 m_pTransformRange->ResetXOROutlineRenderer(); 00803 00804 // Build the transformation matrix 00805 BuildMatrix(); 00806 OriginalTransform = Transform; 00807 00808 // Setup the solid dragging flag for this transform drag instance 00809 bSolidDrag = DocView::SolidDragging; 00810 00811 // Hide the current selection and make a copy of it 00812 // Change the range flags before transforming to implement DMc's 00813 // horrible PromoteToParent bodge for his nasty objects 00814 // BOOL oldPromoteVal = TransFlags.PromoteToParent; 00815 TransFlags.PromoteToParent = TRUE; 00816 m_pTransformRange->SetRangeControl(TransFlags); 00817 00818 m_pDraggedRange = m_pTransformRange->CloneNodes(DocView::SolidDragTimeLimit, AbortTransAtEnd); 00819 m_bFirstRedraw = TRUE; 00820 00821 BOOL bEnableSolidDragging = TRUE; 00822 if (m_pDraggedRange==NULL) 00823 { 00824 bSolidDrag = FALSE; 00825 bEnableSolidDragging = FALSE; 00826 } 00827 else 00828 { 00829 m_pDraggedRange->SetDraggedNodes(TRUE); 00830 bEnableSolidDragging = TRUE; 00831 } 00832 00833 // Start XOR rendering going. We find the (ink) object which was clicked on, and 00834 // pass that in the the renderer, so that it can ensure that object outline is 00835 // rendered first (this allows the user to drag a small object interactively rather 00836 // than having to wait for the entire image to XOR onto screen before they can see 00837 // the precise object they are dragging) 00838 if (!bSolidDrag) 00839 { 00840 m_pTransformRange->SetRenderable(TRUE); 00841 if (m_pDraggedRange) m_pDraggedRange->SetRenderable(FALSE); 00842 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &Transform, ObjectUnderCursor); 00843 } 00844 else 00845 { 00846 // Hide the selection blobs, hide the selection, show the drag selection 00847 m_timeIdle.Sample(); 00848 if (!AbortTransAtEnd) m_pTransformRange->SetRenderable(FALSE); 00849 if (m_pDraggedRange) m_pDraggedRange->SetRenderable(TRUE); 00850 if (pSelTool) Camelot.GetBlobManager()->RenderToolBlobsOff(pSelTool, CurrentSpread, NULL); 00851 Camelot.GetBlobManager()->BlobRenderingOff(TRUE); 00852 00853 // Do we need to show the original outlines right away? 00854 if (DocView::IdleDragDelay==0 || DocView::OriginOutlineShowAlways) 00855 { 00856 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &OriginalTransform, NULL); 00857 m_bShowOriginalOutlines = TRUE; 00858 } 00859 } 00860 00861 // And tell the Dragging system that we need drags to happen 00862 StartDrag(dragtype, NULL, &StartPos, FALSE, bEnableSolidDragging); 00863 00864 // Now that everything is set up, show status help message 00865 // (FigureStatusText will now have all the info it needs to provide correct feedback) 00866 if (StatusHelpID!=0) 00867 { 00868 String_256 Text; 00869 FigureStatusText(&Text); 00870 GetApplication()->UpdateStatusBarText(&Text); 00871 } 00872 } 00873 00874 00875 /******************************************************************************************** 00876 00877 > BOOL TransOperation::ShouldPointerBeOffset() 00878 00879 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00880 Created: 13/9/94 00881 Returns: TRUE if the transform wants the mouse pointer to be offsets to the edge of 00882 the selection, FALSE if it wants it left alone. 00883 Purpose: Allows the transforms to decide if they want the mouse position to be 00884 moved for them to the edge of the selection. 00885 00886 ********************************************************************************************/ 00887 00888 BOOL TransOperation::ShouldPointerBeOffset() 00889 { 00890 return TRUE; 00891 } 00892 00893 00894 00895 /******************************************************************************************** 00896 00897 > DocCoord TransOperation::GetStartPos() 00898 00899 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 00900 Created: 05/04/95 00901 Returns: DocCoord of start of drag. 00902 Purpose: Get the raw start position or the magnetically snapped one depending on 00903 the state of the magnetic switch in the drag view. 00904 This function must be called instead of accessing StartPos directly in any 00905 place where the magnetically snapped start position is going to be used. 00906 It does not need to be used when magnetic snapping is not an issue. Then, 00907 StartPos can simply be read directly. 00908 00909 ********************************************************************************************/ 00910 00911 DocCoord TransOperation::GetStartPos() 00912 { 00913 DocView* pDocView = DocView::GetSelected(); 00914 if (pDocView && pDocView->GetSnapToObjectsState()) 00915 return MagStartPos; 00916 else 00917 return StartPos; 00918 } 00919 00920 /******************************************************************************************** 00921 00922 > void TransOperation::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) 00923 00924 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00925 Created: 19/7/94 00926 Inputs: PointerPos - The current position of the mouse 00927 ClickMods - Which of the modifiers are held down 00928 pSpread - The spread that the mouse is over 00929 Purpose: Handles the event of the mouse moving during a drag. This function will try 00930 to undraw all the eored data from the screen and start drawing it in again 00931 in a new location. 00932 00933 ********************************************************************************************/ 00934 00935 void TransOperation::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) 00936 { 00937 // Has the mouse position actually moved? Check raw PointerPos against raw LastPos 00938 if (PointerPos != LastRawPos) 00939 { 00940 // First off, we will note the fact that the mouse has moved 00941 MouseHasMoved = TRUE; 00942 RawPos = PointerPos; 00943 00944 // Right, the mouse pos changed, so go change the transform... 00945 if (bSolidDrag) 00946 DragSolidChanged(PointerPos, ClickMods, pSpread); 00947 else 00948 DragInputChanged(PointerPos, ClickMods, pSpread); 00949 00950 // Preserve last raw mouse position 00951 LastRawPos = RawPos; 00952 } 00953 } 00954 00955 00956 00957 /******************************************************************************************** 00958 00959 > void TransOperation::DragInputChanged(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread) 00960 00961 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 00962 Created: 19/7/94 00963 Inputs: PointerPos - The current position of the mouse 00964 ClickMods - Which of the modifiers are held down 00965 pSpread - The spread that the mouse is over 00966 Purpose: Deals with any input changing during a transform drag. This mostly means 00967 the mouse position changing but it is also called when the click modifiers 00968 change or potentially any other input. 00969 00970 ********************************************************************************************/ 00971 00972 void TransOperation::DragInputChanged(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread) 00973 { 00974 // Process raw mouse coordinate according to Offset distances... 00975 if (ShouldPointerBeOffset()) 00976 PointerPos.translate(Offset.x,Offset.y); 00977 00978 // Rub all that old EORed stuff out, 'cos we sure don't want it now. 00979 m_pTransformRange->RenderXOROutlinesOff(NULL, CurrentSpread, &Transform); 00980 00981 // Constrain the mouse position if required (45 degrees until we get a better system) 00982 if (ClickMods.Constrain) 00983 ConstrainDrag(&PointerPos); 00984 00985 // if the adjust button is down, we will use the centre 00986 // of the selection as the centre of transform 00987 if (ClickMods.Adjust) 00988 CentreOfTrans = BoundingCentre; 00989 else 00990 CentreOfTrans = OriginalCentre; 00991 00992 // rebuild the transform matrix 00993 UpdateTransformOnDrag(PointerPos, pSpread, ClickMods); 00994 BuildMatrix(); 00995 00996 // Draw the fabby new EORed stuff to the screen 00997 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &Transform, ObjectUnderCursor); 00998 00999 UpdateTransformBoundingData(); 01000 } 01001 01002 01003 01004 /******************************************************************************************** 01005 01006 > void TransOperation::DragSolidChanged(DocCoord PointerPos, 01007 ClickModifiers ClickMods, 01008 Spread* pSpread, 01009 BOOL bForceRecopy = FALSE) 01010 01011 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01012 Created: 1/12/2003 01013 Inputs: PointerPos - The current position of the mouse 01014 ClickMods - Which of the modifiers are held down 01015 pSpread - The spread that the mouse is over 01016 Purpose: Deals with any input changing during a solid transform drag. This mostly means 01017 the mouse position changing but it is also called when the click modifiers 01018 change or potentially any other input. 01019 01020 ********************************************************************************************/ 01021 01022 void TransOperation::DragSolidChanged(DocCoord PointerPos, 01023 ClickModifiers ClickMods, 01024 Spread* pSpread, 01025 BOOL bForceRecopy) 01026 { 01027 DocCoord modPointerPos = PointerPos; 01028 DocView* pDocView = DocView::GetSelected(); 01029 01030 m_timeIdle.Sample(); 01031 01032 if (m_bShowDraggedOutlines) 01033 { 01034 m_pDraggedRange->RenderXOROutlinesOff(NULL, CurrentSpread, &OriginalTransform); 01035 m_bShowDraggedOutlines = FALSE; 01036 } 01037 01038 if (!DocView::OriginOutlineShowAlways) 01039 { 01040 if (m_bShowOriginalOutlines && TransformOverlapsOriginal()) 01041 { 01042 m_pTransformRange->RenderXOROutlinesOff(NULL, CurrentSpread, &OriginalTransform); 01043 m_bShowOriginalOutlines = FALSE; 01044 } 01045 } 01046 01047 // Process raw mouse coordinate according to Offset distances... 01048 if (ShouldPointerBeOffset()) 01049 modPointerPos.translate(Offset.x,Offset.y); 01050 01051 // Constrain the mouse position if required (45 degrees until we get a better system) 01052 if (ClickMods.Constrain) 01053 ConstrainDrag(&modPointerPos); 01054 01055 // if the adjust button is down, we will use the centre 01056 // of the selection as the centre of transform 01057 if (ClickMods.Adjust) 01058 CentreOfTrans = BoundingCentre; 01059 else 01060 CentreOfTrans = OriginalCentre; 01061 01062 // rebuild the transform matrix 01063 UpdateTransformOnDrag(modPointerPos, pSpread, ClickMods); 01064 BuildMatrix(); 01065 01066 // Actually move the dragged objects here 01067 // Start a timer covering both the transform and the render... 01068 MonotonicTime timeRender; 01069 { 01070 SolidDragTransform(bForceRecopy); 01071 01072 // Tell the dragging tool about the changes 01073 UpdateTransformBoundingData(); 01074 01075 // Render the tree and DON'T do any EOR rendering! 01076 // Call ServiceRendering to do a render directly within this function (without waiting 01077 // for the idle processor to do it) 01078 pDocView->FlushRedraw(); 01079 GetApplication()->ServiceRendering(); 01080 } 01081 // If render took more than 150 ms (SolidDragTimeLimit), fall back 01082 // NOTE! This is clever, the 150ms time will only be exceeded if a single object takes that 01083 // INT32 to render. Otherwise the background rendering timeslice will suspend rendering 01084 // and return. So background rendering will cope with complex selections but this clause 01085 // will fire if certain objects are taking too long. 01086 if (timeRender.Elapsed(DocView::SolidDragTimeLimit)) 01087 { 01088 TRACEUSER( "Phil", _T("TOO COMPLEX TO DRAG SOLIDLY!\n")); 01089 pDocView->SetSolidDragState(FALSE); // Turn off solid dragging in this drag op 01090 DragModeChanged(FALSE); 01091 } 01092 01093 // Draw the EORed stuff to the screen if we need to... 01094 if (!DocView::OriginOutlineShowAlways) 01095 { 01096 if (!m_bShowOriginalOutlines && DocView::OriginOutlineShowNonOverlap && !TransformOverlapsOriginal()) 01097 { 01098 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &OriginalTransform, NULL); 01099 m_bShowOriginalOutlines = TRUE; 01100 } 01101 } 01102 } 01103 01104 01105 01106 /******************************************************************************************** 01107 01108 > void TransOperation::DragPointerIdle(DocCoord, ClickModifiers, Spread*, BOOL bSolidDrag) 01109 01110 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01111 Created: 8/12/94 01112 Inputs: None Used 01113 Purpose: This function is called during the drag when the mouse is not moving. 01114 It makes use of this idle time to draw a few more shapes from the selection 01115 to the screen. Thus, if you are tranforming a selection of objects you will 01116 see more detail when the mouse stops moving. 01117 01118 ********************************************************************************************/ 01119 01120 void TransOperation::DragPointerIdle(DocCoord Coord, ClickModifiers, Spread*, BOOL bSolidDrag) 01121 { 01122 // Draw a few more outlines 01123 if (!bSolidDrag) 01124 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &Transform, ObjectUnderCursor); 01125 else 01126 { 01127 if (m_timeIdle.Elapsed(DocView::IdleDragDelay)) 01128 { 01129 // Assume OriginalTransform is Identity. 01130 m_bShowOriginalOutlines = TRUE; 01131 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &OriginalTransform, ObjectUnderCursor); 01132 } 01133 01134 if (m_timeIdle.Elapsed(DocView::IdleDragDelay2)) 01135 { 01136 // Don't show the outlines at the dragged position if that position is the original 01137 // position 01138 if (!(Transform==OriginalTransform)) 01139 { 01140 // We need to ensure that the dragged objects have actually been transformed 01141 // (Their cached containers might have been dragged instead to save time) 01142 // Would be nice to be able to test whether this recreation of the dragged 01143 // objects is really necessary here but I can't think of a decent way right now 01144 if (!m_bRangeFullyTransformed) 01145 { 01146 SolidDragTransform(TRUE, FALSE); 01147 ERROR3IF(!this->m_bRangeFullyTransformed, "Failed to transform shapes in DragPointerIdle"); 01148 } 01149 01150 m_bShowDraggedOutlines = TRUE; 01151 if (m_pDraggedRange) 01152 { 01153 m_pDraggedRange->RenderXOROutlinesOn(NULL, CurrentSpread, &OriginalTransform, NULL); 01154 } 01155 } 01156 } 01157 } 01158 } 01159 01160 01161 01162 01163 /******************************************************************************************** 01164 01165 > void TransOperation::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, 01166 BOOL Worked, BOOL bSolidDrag) 01167 01168 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01169 Created: 19/7/94 01170 Inputs: PointerPos - The position of the mouse at the end of the drag 01171 ClickMods - the keyboard modifiers in place 01172 pSpread - the spread that was under the mouse 01173 Worked - TRUE if the drag was completed successfully 01174 Purpose: Marks the end of the drag. It is at this point that all the transformations 01175 should be applied to the selection if everything worked. 01176 01177 ********************************************************************************************/ 01178 01179 void TransOperation::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, 01180 BOOL Success, BOOL bSolidDrag) 01181 { 01182 // By default assume it will not work 01183 BOOL Worked = FALSE; 01184 01185 // Deregister our idle event handler. We don't want it to keep on rendering! 01186 GetApplication()->RemoveIdleProcessor(IDLEPRIORITY_HIGH, this); 01187 01188 // Process raw mouse coordinate according to Offset distances... 01189 if (ShouldPointerBeOffset()) PointerPos.translate(Offset.x,Offset.y); 01190 01191 // Find out how well the drag went . . . 01192 DragEndType HowEnded; 01193 if (!Success) 01194 { 01195 HowEnded = DT_CANCELLED; 01196 } 01197 else if (MouseHasMoved) 01198 { 01199 HowEnded = (PointerPos == RawStartPos) ? DT_MOVEDTOSAME : DT_MOVEDTODIFF; 01200 } 01201 else 01202 { 01203 HowEnded = DT_DIDNOTMOVE; 01204 } 01205 01206 // Get rid of the temporary solid drag objects... 01207 DocView* pDocView = GetWorkingDocView(); 01208 if (m_pDraggedRange) 01209 { 01210 // If we're not going to transform objects to this position 01211 // then force a redraw to remove them from the view 01212 if (AbortTransAtEnd || !Success) 01213 m_pDraggedRange->ForceRedrawView(pDocView, TRUE); 01214 else 01215 // Bodge to ensure that scaled cached data is fully wiped (because 01216 // the scaled cached data may be bigger than the actual scaled vector data) 01217 if (HowEnded==DT_MOVEDTODIFF && m_bRangeCacheTransformed && !Transform.IsTranslation()) 01218 m_pDraggedRange->ForceRedrawView(pDocView, TRUE); 01219 01220 m_pDraggedRange->DeleteNodes(); 01221 delete m_pDraggedRange; 01222 m_pDraggedRange = NULL; 01223 } 01224 m_pTransformRange->SetRenderable(TRUE); 01225 01226 // Rub all that old EORed stuff out, 'cos we sure don't want it now. 01227 if (!bSolidDrag) 01228 m_pTransformRange->RenderXOROutlinesOff(NULL, CurrentSpread, &Transform); 01229 else 01230 { 01231 if (m_bShowOriginalOutlines) 01232 m_pTransformRange->RenderXOROutlinesOff(NULL, CurrentSpread, &OriginalTransform); 01233 01234 if (!Success) 01235 m_pTransformRange->ForceRedrawView(pDocView, TRUE, FALSE, TRUE); // Don't recache objects - they haven't changed 01236 } 01237 pDocView->FlushRedraw(); 01238 01239 // Mike 01/02/95 - Change the range control to include NoneRenderables now 01240 RangeControl TransFlags = m_pTransformRange->GetRangeControlFlags(); 01241 TransFlags.IgnoreNoneRenderable=FALSE; 01242 01243 m_pTransformRange->SetRangeControl(TransFlags); 01244 01245 // Check to see if the operation was a success and if the tool wants me to continue. 01246 BOOL fToolDragOK = TRUE; 01247 01248 #ifndef STANDALONE 01249 if (pSelTool != NULL) fToolDragOK = pSelTool->DragFinished(HowEnded); 01250 #endif 01251 01252 //Graham 30/9/96: This code is a bit of a bodge to fix something before the deadline 01253 //Do we need to change the selection? 01254 if (!lstNodesToSelect.IsEmpty()) 01255 { 01256 //Yes. So first get rid of the current selection 01257 NodeRenderableInk::DeselectAll(!bSolidDrag, TRUE); 01258 01259 //And now we need to go through the list of nodes to select, selecting each one 01260 //in turn. 01261 NodeListItem* pThisItem=((NodeListItem*)lstNodesToSelect.GetHead()); 01262 01263 ERROR3IF(pThisItem==NULL, "TransOp::EndDrag - Node list has no head!"); 01264 01265 while (pThisItem!=NULL) 01266 { 01267 //Select the node 01268 Node* pThisNode=pThisItem->pNode; 01269 01270 ERROR3IF(pThisNode==NULL, "TransOp::EndDrag - Node list item is empty!"); 01271 01272 pThisNode->SetSelected(TRUE); 01273 01274 NodeRenderableBounded* pThisBoundedNode=((NodeRenderableBounded*) pThisNode); 01275 01276 if (pThisBoundedNode) 01277 DoInvalidateNodeRegion(pThisBoundedNode, TRUE, FALSE); 01278 01279 //And go on to the next node in the list. 01280 pThisItem=(NodeListItem*) lstNodesToSelect.GetNext(pThisItem); 01281 } 01282 01283 //And get rid of the items in the list now we've used them 01284 lstNodesToSelect.DeleteAll(); 01285 } 01286 01287 01288 //Graham: If AbortTransAtEnd is set, we don't want to complete the transformation 01289 if (fToolDragOK && Success && HowEnded!=DT_DIDNOTMOVE && !AbortTransAtEnd) 01290 { 01291 // Try to build all the undo information 01292 Worked = CompleteTransformation(); 01293 01294 if (CanChangeSpread()) 01295 Document::SetSelectedViewAndSpread(NULL, NULL, m_pDragSpread); 01296 01297 // mark the selection cache as invalid (bounding rects etc will no longer be correct) 01298 m_pTransformRange->Update(); 01299 } 01300 01301 // End the drag 01302 EndDrag(); 01303 01304 // Use the SliceHelper function to restore the selection back to how it was if it had been modified... 01305 SliceHelper::RestoreSelection(); 01306 01307 // If there was a problem, fail 01308 if (!Worked) 01309 FailAndExecute(); 01310 01311 // DON'T delete these ranges because they are used inside the End() function 01312 // Rely on ~Transop to get rid of them 01313 /* // tidy up member vars 01314 if (m_pTransformRange) 01315 { 01316 delete m_pTransformRange; 01317 m_pTransformRange = NULL; 01318 } 01319 01320 if (m_pDraggedRange) 01321 { 01322 delete m_pDraggedRange; 01323 m_pDraggedRange = NULL; 01324 } 01325 */ 01326 01327 // Preserve some data items we need to use below, after "this" has been deleted... 01328 DragTool* pSelToolCopy = pSelTool; 01329 Spread* pSpreadCopy = CurrentSpread; 01330 // pDocview was gained locally in the code above 01331 01332 // End the operation (and Delete ourselves!) 01333 End(); 01334 01335 // The following code may run when "this" has been deleted! 01336 // (It has to because we don't want to re-enable blob rendering until End() has done its stuff) 01337 // Be VERY careful here! 01338 01339 // Now turn selection blobs back on 01340 Camelot.GetBlobManager()->BlobRenderingOn(FALSE); 01341 if (bSolidDrag && pSelToolCopy) 01342 { 01343 // Note that this function call renders blobs into all current RenderRegions 01344 // so that they are back in sync with the selection state 01345 Camelot.GetBlobManager()->RenderToolBlobsOn(pSelToolCopy, pSpreadCopy, NULL); 01346 } 01347 01348 } 01349 01350 01351 01352 /******************************************************************************************** 01353 01354 > BOOL TransOperation::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag) 01355 01356 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01357 Created: 15/02/95 01358 Inputs: pKeyPress - pointer to a keypress object 01359 Returns: TRUE if it handled the keypress, FALSE otherwise 01360 Purpose: Find out if any of the key modifiers are being pressed when the mouse is 01361 not moving. 01362 01363 ********************************************************************************************/ 01364 01365 BOOL TransOperation::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag) 01366 { 01367 // Need something to see if there was a usful keypress 01368 BOOL KeyRecognised = FALSE; 01369 01370 // and something to keep them in 01371 ClickModifiers ClickMods; 01372 ClickMods.Adjust = FALSE; 01373 ClickMods.Menu = FALSE; 01374 ClickMods.Constrain = FALSE; 01375 ClickMods.Alternative1 = FALSE; 01376 ClickMods.Alternative2 = FALSE; 01377 ClickMods.Pressure = 0; 01378 01379 if (pKeyPress->IsPress() && !pKeyPress->IsRepeat()) // I.e. is this a non-auto-repeated key-down event? 01380 { 01381 switch (pKeyPress->GetVirtKey()) 01382 { 01383 // Numeric + key 01384 case CAMKEY(NUMPAD_ADD): 01385 { 01386 // Markn 31/5/95: Only allow the LeaveCopy flag to toggled if select-inside is NOT present 01387 if (!m_pTransformRange->ContainsSelectInside()) 01388 { 01389 // Graham 24/6/96: The '+' key's function depends on a preference 01390 // DragCopyAction() will perform whatever operation 01391 // is assigned to the + key by this preference. 01392 01393 KeyRecognised = DragCopyAction(bSolidDrag); 01394 } 01395 break; 01396 } 01397 01398 // Numeric / key 01399 case CAMKEY(NUMPAD_DIVIDE): 01400 { 01401 ScaleLines = !ScaleLines; 01402 BoundingData.ScaleLinesChanged = TRUE; 01403 KeyRecognised = TRUE; 01404 } 01405 01406 // Numeric - key 01407 case CAMKEY(NUMPAD_SUBTRACT): 01408 { 01409 TransFills = !TransFills; 01410 BoundingData.TransFillsChanged = TRUE; 01411 KeyRecognised = TRUE; 01412 } 01413 } 01414 01415 // If key recognised in the first part of this routine then it will have changed the status help... 01416 if (KeyRecognised) 01417 { 01418 BoundingData.ScaleLines = ScaleLines; 01419 BoundingData.LeaveCopy = LeaveCopy; 01420 BoundingData.TransFills = TransFills; 01421 01422 String_256 Text; 01423 FigureStatusText(&Text); 01424 GetApplication()->UpdateStatusBarText(&Text); 01425 } 01426 } 01427 01428 // If a modifier key has changed state, or the key pressed was one that we recognise, 01429 // update the drag data immediately 01430 if (KeyPress::ModifierChanged() || KeyRecognised) 01431 { 01432 // Set up the click modifier 01433 ClickMods.Constrain = pKeyPress->IsConstrainPressed(); 01434 ClickMods.Adjust = pKeyPress->IsAdjustPressed(); 01435 ClickMods.Alternative1 = pKeyPress->IsAlternativePressed(); 01436 01437 // Click modifiers changed (sort of) so go change the transform... 01438 // DragInputChanged(LastRawPos, ClickMods, CurrentSpread); 01439 // Right, the mouse pos changed, so go change the transform... 01440 if (bSolidDrag) 01441 DragSolidChanged(LastRawPos, ClickMods, CurrentSpread, KeyRecognised); 01442 else 01443 DragInputChanged(LastRawPos, ClickMods, CurrentSpread); 01444 01445 // Tell the keypress people that we used the keypress 01446 KeyRecognised = TRUE; 01447 } 01448 01449 // Did or did not use the click 01450 return KeyRecognised; 01451 } 01452 01453 01454 01455 01456 /******************************************************************************************** 01457 01458 > void TransOperation::DragModeChanged() 01459 01460 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01461 Created: 15/02/95 01462 Inputs: pKeyPress - pointer to a keypress object 01463 Returns: TRUE if it handled the keypress, FALSE otherwise 01464 Purpose: Find out if any of the key modifiers are being pressed when the mouse is 01465 not moving. 01466 01467 ********************************************************************************************/ 01468 01469 void TransOperation::DragModeChanged(BOOL bSolidDrag) 01470 { 01471 DocView* pDocView = DocView::GetSelected(); 01472 BlobManager* pBlobManager = Camelot.GetBlobManager(); 01473 ENSURE(pBlobManager, "Can't get BlobManager"); 01474 01475 if (bSolidDrag) 01476 { 01477 // The drag rendering mode has just become solid 01478 // Hide the original objects and show the dragged objects 01479 m_pTransformRange->SetRenderable(FALSE); 01480 m_pTransformRange->ForceRedrawView(pDocView, TRUE, FALSE, TRUE); // Don't recache objects - they haven't changed 01481 if (m_pDraggedRange) 01482 { 01483 m_bFirstRedraw = TRUE; 01484 // Actually move the dragged objects here 01485 SolidDragTransform(FALSE); 01486 01487 m_pDraggedRange->SetRenderable(TRUE); 01488 // m_pDraggedRange->ForceRedrawView(pDocView); 01489 01490 m_pTransformRange->RenderXOROutlinesOff(NULL, CurrentSpread, &Transform); 01491 if (m_bShowOriginalOutlines) 01492 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &OriginalTransform, ObjectUnderCursor); 01493 01494 if (pSelTool) pBlobManager->RenderToolBlobsOff(pSelTool, CurrentSpread, NULL); 01495 pBlobManager->BlobRenderingOff(FALSE); 01496 } 01497 } 01498 else 01499 { 01500 // The drag rendering mode has just become outline 01501 // Show the original objects and hide the dragged objects 01502 m_pTransformRange->SetRenderable(TRUE); 01503 m_pTransformRange->ForceRedrawView(pDocView, TRUE, FALSE, TRUE); // Don't recache objects - they haven't changed 01504 if (m_pDraggedRange) 01505 { 01506 pBlobManager->BlobRenderingOn(FALSE); 01507 if (pSelTool) pBlobManager->RenderToolBlobsOn(pSelTool, CurrentSpread, NULL); 01508 01509 if (m_bShowOriginalOutlines) 01510 m_pTransformRange->RenderXOROutlinesOff(NULL, CurrentSpread, &OriginalTransform); 01511 m_pTransformRange->RenderXOROutlinesOn(NULL, CurrentSpread, &Transform, ObjectUnderCursor); 01512 01513 m_pDraggedRange->SetRenderable(FALSE); 01514 m_pDraggedRange->ForceRedrawView(pDocView, TRUE); 01515 } 01516 } 01517 01518 // Show status help message 01519 if (StatusHelpID!=0) 01520 { 01521 String_256 Text; 01522 FigureStatusText(&Text); 01523 GetApplication()->UpdateStatusBarText(&Text); 01524 } 01525 01526 // Render any invalidated regions 01527 // Call ServiceRendering to do a render directly within this function (without waiting 01528 // for the idle processor to do it) 01529 pDocView->FlushRedraw(); 01530 GetApplication()->ServiceRendering(); 01531 } 01532 01533 01534 01535 01536 /******************************************************************************************** 01537 01538 void TransOperation::OnClickWhileDragging(OilCoord PointerPos, ClickType Click, ClickModifiers Mods, BOOL bSolidDrag) 01539 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 01540 Created: 18/6/96 01541 Inputs: OilCoord PointerPos: position of click 01542 ClickType Click: single, double or triple 01543 ClickModifiers Mods: whether shift, ctrl etc was pressed during the click 01544 Outputs: - 01545 Returns: - 01546 Purpose: This virtual function handles clicks with one mouse button while 01547 another mouse button is dragging. For example, the user drags with the 01548 left mouse button and clicks the right button during the drag. 01549 01550 This case of the virtual function overrides the empty virtual function 01551 Operation::OnClickWhileDragging. 01552 01553 This function calls DragCopyAction to find out what the function 01554 assigned to Clicks-While-Dragging is. 01555 01556 Feel free to add other overriding functions to handle clicks-while- 01557 dragging for other operations. 01558 01559 Errors: None. 01560 SeeAlso: ClickModifiers::ClickWhileDrag (member variable); DocView::OnClick; 01561 ScreenView::HandleDragEvent; ScreenView::HandleButtonUp; 01562 TransOperation::OnClickWhileDragging 01563 01564 ********************************************************************************************/ 01565 01566 void TransOperation::OnClickWhileDragging(OilCoord PointerPos, ClickType Click, ClickModifiers Mods, BOOL bSolidDrag) 01567 { 01568 //If Click is an up-click, we are not interested in this particular case 01569 if (Click==CLICKTYPE_UP) return; 01570 01571 // Karim 21/06/2000 similarly to the way we handle tapping '+' while dragging, 01572 // we only acknowledge the click if select-inside is not present. 01573 if (!m_pTransformRange->ContainsSelectInside()) 01574 { 01575 //Do whatever the ClickWhileDragging preference says we should do 01576 DragCopyAction(bSolidDrag); 01577 } 01578 01579 //And update the status bar 01580 String_256 Text; 01581 FigureStatusText(&Text); 01582 GetApplication()->UpdateStatusBarText(&Text); 01583 } 01584 01585 01586 /******************************************************************************************** 01587 01588 > void TransOperation::RenderDragBlobs( DocRect ClipRect, Spread *pSpread, BOOL bSolidDrag ) 01589 01590 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01591 Created: 22/10/93 01592 Inputs: ClipRect - NULL, or the region that needs to be redrawn 01593 pSpread - The spread to render to 01594 Purpose: Draws the EORed stuff ("blobs") to the screen during a drag. 01595 SeeAlso: Operation::RenderDragBlobs 01596 01597 ********************************************************************************************/ 01598 01599 void TransOperation::RenderDragBlobs(DocRect ClipRect, Spread *pSpread, BOOL bSolidDrag) 01600 { 01601 // If solid dragging is turned on, only render any if we have turned them on! 01602 if (bSolidDrag) 01603 { 01604 if (m_bShowOriginalOutlines) 01605 m_pTransformRange->RenderXOROutlinesToCatchUp(&ClipRect, CurrentSpread, &OriginalTransform); 01606 if (m_bShowDraggedOutlines) 01607 m_pDraggedRange->RenderXOROutlinesToCatchUp(&ClipRect, CurrentSpread, &OriginalTransform); 01608 return; 01609 } 01610 01611 // Build the transform matrix 01612 BuildMatrix(); 01613 01614 // draw the blobs for the specific region up to the same level as everywhere else 01615 if (pSpread==CurrentSpread) 01616 m_pTransformRange->RenderXOROutlinesToCatchUp(&ClipRect, CurrentSpread, &Transform); 01617 } 01618 01619 01620 01621 01622 01623 /******************************************************************************************** 01624 01625 > BOOL TransOperation::Declare() 01626 01627 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01628 Created: 19/7/94 01629 Returns: TRUE if it worked, FALSE otherwise 01630 Purpose: 01631 01632 ********************************************************************************************/ 01633 01634 BOOL TransOperation::Declare() 01635 { 01636 return TRUE; 01637 } 01638 01639 /******************************************************************************************** 01640 01641 > static BOOL TransOperation::DeclarePrefs() 01642 01643 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 01644 Created: 25/6/96 01645 Returns: TRUE if it worked, FALSE if it failed 01646 Purpose: Declares any preferences that the class needs to declare 01647 01648 ********************************************************************************************/ 01649 01650 BOOL TransOperation::DeclarePrefs() 01651 { 01652 GetApplication()->DeclarePref( _T("Mouse"), _T("ClickWhileDragFunc"), &ClickWhileDragFunc, 0, 3 ); 01653 01654 return TRUE; 01655 } 01656 01657 01658 01659 01660 /******************************************************************************************** 01661 01662 > OpState TransOperation::GetState(String_256* Description, OpDescriptor*) 01663 01664 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01665 Created: 19/7/94 01666 Inputs: - 01667 Outputs: - 01668 Returns: - 01669 Purpose: 01670 01671 ********************************************************************************************/ 01672 01673 OpState TransOperation::GetState(String_256* Description, OpDescriptor*) 01674 { 01675 OpState Blobby; 01676 return Blobby; 01677 } 01678 01679 01680 01681 01682 /******************************************************************************************** 01683 01684 > void TransOperation::InitTransformImmediate(OpParam*) 01685 01686 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01687 Created: 27/7/94 01688 Inputs: OpParam - The operations parameters 01689 Purpose: Allows a transformation to set itself up ready for an immediate transform. 01690 If your class wants to support immediate transforms it should overide this 01691 function or the DoWithParam function. This base class version of the function 01692 does nothing. 01693 01694 ********************************************************************************************/ 01695 01696 void TransOperation::InitTransformImmediate(OpParam*) 01697 { 01698 } 01699 01700 01701 /******************************************************************************************** 01702 01703 > virtual void TransOperation::InitTransformOnDrag(DocCoord PointerPos, ClickModifiers ClickMods) 01704 01705 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01706 Created: 19/7/94 01707 Inputs: PointerPos - The position of the mouse at the start of the drag 01708 ClickMods - the keyboard modifiers that were active at the start of the drag 01709 Purpose: Sets up the parameters needed to build the transform matrix at the start 01710 of the drag. This base class version of this function does nothing. 01711 01712 ********************************************************************************************/ 01713 01714 void TransOperation::InitTransformOnDrag(DocCoord PointerPos, ClickModifiers ClickMods) 01715 { 01716 // Overide this function if your transformation operation needs to do anything 01717 // set up the matrix builder in a drag version. 01718 } 01719 01720 01721 01722 /******************************************************************************************** 01723 01724 > virtual void TransOperation::UpdateTransformOnDrag(DocCoord PointerPos, Spread*, 01725 ClickModifiers&) 01726 01727 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01728 Created: 19/7/94 01729 Inputs: PointerPos - The position of the mouse 01730 Spread - The spread that the mouse is over right now 01731 ClickModifiers - The current click modifiers 01732 Purpose: This function re-calculates the parameters specific to the transform based 01733 on the mouse position. This is only used in interactive drag style transforms 01734 01735 ********************************************************************************************/ 01736 01737 void TransOperation::UpdateTransformOnDrag(DocCoord, Spread*, ClickModifiers&) 01738 { 01739 // Overide this function if you need to do anything during a mouse move 01740 } 01741 01742 01743 /******************************************************************************************** 01744 01745 > virtual BOOL TransOperation::CanChangeSpread() 01746 01747 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01748 Created: 05/June/2006 01749 Inputs: - 01750 Outputs: - 01751 Returns: TRUE if this transform allows the drag to be transferred to another spread 01752 Purpose: Tell the baseclass functions whether to draw drag feedback only on the start 01753 spread or to allow drag rendering to be done on other spreads too. 01754 01755 ********************************************************************************************/ 01756 01757 BOOL TransOperation::CanChangeSpread() 01758 { 01759 // Overide this function if you allow your transform to cross spread boundaries 01760 return FALSE; 01761 } 01762 01763 01764 /******************************************************************************************** 01765 01766 > virtual void TransOperation::BuildMatrix() 01767 01768 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01769 Created: 19/7/94 01770 Purpose: This function should build the approprate transform matrix based on all the 01771 params that are available. This base class version of the function does not 01772 build a matrix and should not be called, as a derived class version of it 01773 should exist. 01774 01775 ********************************************************************************************/ 01776 01777 void TransOperation::BuildMatrix() 01778 { 01779 ENSURE(FALSE, "Base class version of BuildMatrix() called. A derived class version must exist"); 01780 01781 // Sets the transform matrix to be the identity matrix in case of an emergency. 01782 Transform = Matrix(); 01783 } 01784 01785 01786 /******************************************************************************************** 01787 01788 > void TransOperation::ConstrainDrag(DocCoord* PointerPos) 01789 01790 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01791 Created: 19/7/94 01792 Inputs: PointerPos - The position of the mouse right about now 01793 Purpose: Gives the operation the chance to constrain the mouses movement if it is 01794 relavent. If it is not, then do not overide this function. 01795 01796 ********************************************************************************************/ 01797 01798 void TransOperation::ConstrainDrag(DocCoord* PointerPos) 01799 { 01800 // By default there is no constrain action 01801 // Write you own version of this function if you want your transformation to be constrained 01802 } 01803 01804 01805 /******************************************************************************************** 01806 01807 > void TransOperation::SetStartBlob(INT32 StartBlob) 01808 01809 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01810 Created: 25/7/94 01811 Inputs: StartBlob - the Number of the blob that the operation was initiated on, 01812 if applicabale. They are numbered as follows :- 01813 MonoOn 01814 1 2 3 01815 4 5 01816 6 7 8 01817 MonoOff 01818 All operations that were not started from one of the 8 blobs should use 0 01819 Purpose: Allows the operations to know how it was started. Some operation do 01820 different things depending on if they are going vertically or horizontally 01821 and this will allow you to figure that out. Overide this function if you 01822 need to deal with this situation (ie the Shear operation). The base class 01823 version does nothing. 01824 This function is called from the DragStarted() function. 01825 01826 ********************************************************************************************/ 01827 01828 void TransOperation::SetStartBlob(INT32 StartBlob) 01829 { 01830 } 01831 01832 01833 01834 /******************************************************************************************** 01835 01836 > void TransOperation::UpdateTransformBoundingData() 01837 01838 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01839 Created: 26/8/94 01840 Purpose: Should update the information in the TransformBoundingData object of the 01841 class and tell the Selector tool using the DragMove function. This is the 01842 base class version of the function that just tells the tool but does not 01843 change any of the data. This function is called at the end of the 01844 DragPointerMove function. It is possible for your transform to simple change 01845 the data in the BoundingData var as it goes and leave this base class version 01846 of the function to tell the tool about the changes 01847 01848 ********************************************************************************************/ 01849 01850 void TransOperation::UpdateTransformBoundingData() 01851 { 01852 #ifndef STANDALONE 01853 // Tell the tool about the current transform bounding data 01854 if (pSelTool != NULL) 01855 { 01856 pSelTool->DragMove(&BoundingData); 01857 } 01858 #endif 01859 } 01860 01861 /******************************************************************************************** 01862 01863 > void TransOperation::ComputeNewBounds() 01864 01865 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 01866 Created: 04/9/94 01867 Purpose: Applies the current transformation matrix to the original bounding box data 01868 to compute the new bounds of the objects. It also notes that the bounds may 01869 be severely distorted by the transform and so it sorts and recombines the 01870 output coordinates to construct a new bounding rectangle. 01871 01872 ********************************************************************************************/ 01873 01874 void TransOperation::ComputeNewBounds() 01875 { 01876 // Transform original bounding coordinates 01877 Coord NewBounds[4]; 01878 Transform.transform(NewBounds,OriginalBounds,4); 01879 01880 // Now recombine the output coords to see where the new bottom-left and top-right coords 01881 // really are... 01882 INT32 lox = NewBounds[0].x; 01883 INT32 loy = NewBounds[0].y; 01884 INT32 hix = NewBounds[0].x; 01885 INT32 hiy = NewBounds[0].y; 01886 UINT32 i; 01887 for(i=1;i!=4;i++) 01888 { 01889 if (NewBounds[i].x < lox) lox=NewBounds[i].x; 01890 if (NewBounds[i].y < loy) loy=NewBounds[i].y; 01891 if (NewBounds[i].x > hix) hix=NewBounds[i].x; 01892 if (NewBounds[i].y > hiy) hiy=NewBounds[i].y; 01893 } 01894 01895 // Now update the bounds structure... 01896 if (lox!=BoundingData.x || loy!=BoundingData.y) 01897 { 01898 // One or both of x or y have changed so update them and set the changed flag 01899 BoundingData.x=lox; 01900 BoundingData.y=loy; 01901 BoundingData.XYChanged = TRUE; 01902 } 01903 01904 // Compute width and height 01905 hix = hix-lox; 01906 hiy = hiy-loy; 01907 if (hix!=BoundingData.Width || hiy!=BoundingData.Height) 01908 { 01909 // One or both of w or h have changed so update them and set the changed flag 01910 BoundingData.Width=hix; 01911 BoundingData.Height=hiy; 01912 BoundingData.WHChanged = TRUE; 01913 } 01914 01915 } 01916 01917 /******************************************************************************************** 01918 01919 > BOOL TransOperation::CompleteTransformation() 01920 01921 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 01922 Created: 19/7/94 01923 Returns: TRUE if it worked 01924 Purpose: Builds the actual Undo information etc for the transformation as well as 01925 actually performing the transformation itself. This should be called by 01926 both DragFinished() and DoWithParams(). 01927 01928 ********************************************************************************************/ 01929 01930 BOOL TransOperation::CompleteTransformation() 01931 { 01932 // Build the appropriate transform object to transform the selection 01933 Trans2DMatrix* Trans; 01934 ALLOC_WITH_FAIL(Trans, new Trans2DMatrix(Transform), this); 01935 01936 // See if the memory allocation worked 01937 if (Trans == NULL) 01938 return FALSE; 01939 01940 // Ideally we should put the hourglass on here, it will be turned off in the End method 01941 BeginSlowJob(); 01942 01943 // Set the flags in the transform according to the flags set here 01944 Trans->TransFills = TransFills; 01945 Trans->TransLines = ScaleLines && CanScaleLines; 01946 Trans->bHaveTransformedAllCached = TRUE; // having transformed none we can say all caches have been transformed 01947 Trans->bHaveTransformedAllChildren = TRUE; // having transformed none we can say all children have been transformed 01948 Trans->bTransformYourChildren = TRUE; // The transform does not have to transform all its data 01949 m_bRangeCacheTransformed = TRUE; 01950 m_bRangeFullyTransformed = TRUE; 01951 01952 // if we need to leave a copy of the selection behind, then do it 01953 // We HAVE to do this before we call DoStartTransOp() as it builds a 01954 // run length coding of the selection, which is messed up if we insert 01955 // extra nodes into the tree. 01956 // 01957 // Markn 31/5/95: Only allow LeaveCopy if select-inside is NOT present 01958 // JustinF: 6/10/96 only do this if transforming the selection, not TransformRange! 01959 if (m_pTransformRange && LeaveCopy && !m_pTransformRange->ContainsSelectInside()) 01960 { 01961 if (!LeaveCopyOfSelection()) 01962 return FALSE; 01963 } 01964 01965 // Start the selection operation which records the current selection status and 01966 // invalidates the rectangle covered by the selection and its blobs. 01967 BOOL RecordSelectionTwice = (CanChangeSpread()) && (StartSpread!=CurrentSpread); 01968 01969 // If the Selection only contains a single object then we pass this object as 01970 // a parameter to DoStartTransOp. Otherwise the routine assumes that the 01971 // entire selection will be transformed. 01972 // JustinF: 7/10/96 only do this if transforming the selection, not TransformRange! 01973 if (SelState==NULL) 01974 { 01975 if (!DoStartTransOp(RecordSelectionTwice, NULL, m_pTransformRange)) 01976 return FALSE; 01977 } 01978 01979 // Scan the selection, and transform each selected object. 01980 // Karim 29/06/2000 - modified so that the range's PromoteToParent flag is not ruined. 01981 BOOL TransOK = DoTransformNodes(*m_pTransformRange, Trans); 01982 if (!TransOK) 01983 return FALSE; 01984 01985 // Move the selection to a new spread if it should be 01986 if (RecordSelectionTwice) 01987 { 01988 // Move the selection to a new spread here 01989 if (!DoMoveNodes(*m_pTransformRange, CurrentSpread->FindActiveLayer(), LASTCHILD)) 01990 return FALSE; 01991 01992 Document::GetSelected()->ResetInsertionPosition(); 01993 } 01994 01995 // Change the bounds then ripple them up the tree 01996 //SCANRANGEFORCLASS(Selection, UpdateInkBoundingRect(), NodeRenderableInk); 01997 //Selection->UpdateParentBoundsOfSelection(); 01998 01999 m_bRangeCacheTransformed = Trans->bHaveTransformedAllCached; 02000 m_bRangeFullyTransformed = Trans->bHaveTransformedAllChildren; 02001 02002 // It all worked, so say so. 02003 return TRUE; 02004 } 02005 02006 02007 02008 /******************************************************************************************** 02009 02010 > BOOL TransOperation::SolidDragTransform(BOOL bForceRecopy = FALSE, BOOL bForceRedraw = TRUE) 02011 02012 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02013 Created: 2/12/2003 02014 Returns: TRUE if it worked 02015 Purpose: Transform the selection into the drag range. 02016 02017 ********************************************************************************************/ 02018 02019 BOOL TransOperation::SolidDragTransform(BOOL bForceRecopy, BOOL bForceRedraw) 02020 { 02021 BOOL TransOK = FALSE; 02022 02023 if (m_pDraggedRange==NULL) 02024 return FALSE; 02025 02026 // Build the appropriate transform object to transform the selection 02027 Trans2DMatrix* Trans; 02028 Trans = new Trans2DMatrix(Transform); 02029 02030 // See if the memory allocation worked 02031 if (Trans == NULL) 02032 return FALSE; 02033 02034 //TRACEUSER( "Phil", _T("SolidDragTransform hscale = %f, %f\n"), Trans->GetScale().MakeDouble(), Trans->GetAspect().MakeDouble()); 02035 02036 // Set the flags in the transform according to the flags set here 02037 Trans->TransFills = TransFills; 02038 Trans->TransLines = ScaleLines && CanScaleLines; 02039 02040 // Get rid of the last set of dragged objects and make a new copy of the selection 02041 // Note! This would be much faster if just the coords of the drag selection could be transformed! 02042 // Redraw just the current view 02043 m_pDraggedRange->ForceRedrawView(GetWorkingDocView(), TRUE, m_bFirstRedraw); 02044 m_bFirstRedraw = FALSE; 02045 BOOL bQualNotSolid = !(GetWorkingDocView()->RenderQuality.GetFillQuality() > Quality::Solid); 02046 02047 #if SOLID_TRANSFORM_METHOD == 1 02048 // Update the dragged objects the dumb way - by deleting and recreating them 02049 m_pDraggedRange->DeleteNodes(); 02050 delete m_pDraggedRange; 02051 02052 m_pDraggedRange = m_pTransformRange->CloneNodes(DocView::SolidDragTimeLimit); 02053 02054 if (m_pDraggedRange==NULL) 02055 return FALSE; 02056 02057 m_pDraggedRange->SetDraggedNodes(TRUE); 02058 m_pDraggedRange->SetRenderable(TRUE); 02059 02060 // Scan the selection, and transform each selected object. 02061 Trans->bSolidDrag = TRUE; 02062 Trans->bHaveTransformedAllCached = TRUE; // having transformed none we can say all caches have been transformed 02063 Trans->bHaveTransformedAllChildren = TRUE; // having transformed none we can say all caches have been transformed 02064 Trans->bTransformYourChildren = bQualNotSolid; // The transform does not have to transform all its data 02065 TransOK = m_pDraggedRange->TransformNodes(Trans); 02066 m_bRangeCacheTransformed = Trans->bHaveTransformedAllCached; 02067 m_bRangeFullyTransformed = Trans->bHaveTransformedAllChildren; 02068 02069 #endif 02070 #if SOLID_TRANSFORM_METHOD == 2 02071 // Update the dragged objects the smart way by copying the contents of the selection 02072 // into the dragged nodes 02073 m_pTransformRange->CopyNodesContents(m_pDraggedRange, TRUE, TRUE); 02074 02075 // Scan the selection, and transform each selected object. 02076 Trans->bSolidDrag = TRUE; 02077 Trans->bHaveTransformedAllCached = TRUE; // having transformed none we can say all caches have been transformed 02078 Trans->bHaveTransformedAllChildren = TRUE; // having transformed none we can say all caches have been transformed 02079 Trans->bTransformYourChildren = bQualNotSolid; // The transform does not have to transform all its data 02080 TransOK = m_pDraggedRange->TransformNodes(Trans); 02081 m_bRangeCacheTransformed = Trans->bHaveTransformedAllCached; 02082 m_bRangeFullyTransformed = Trans->bHaveTransformedAllChildren; 02083 02084 #endif 02085 #if SOLID_TRANSFORM_METHOD == 3 02086 // Move objects back to original position simply by premultiplying the transformation 02087 // matrix by the inverse of the previous transform 02088 m_PrevTransform.Invert(); 02089 m_PrevTransform *= *Trans; 02090 02091 // Scan the selection, and transform each selected object. 02092 m_PrevTransform.bSolidDrag = TRUE; 02093 m_PrevTransform.bHaveTransformedAllCached = TRUE; // having transformed none we can say all caches have been transformed 02094 m_PrevTransform.bHaveTransformedAllChildren = TRUE; // having transformed none we can say all caches have been transformed 02095 m_PrevTransform.bTransformYourChildren = bQualNotSolid; // The transform does not have to transform all its data 02096 TransOK = m_pDraggedRange->TransformNodes(&m_PrevTransform); 02097 m_bRangeCacheTransformed = Trans->bHaveTransformedAllCached; 02098 m_bRangeFullyTransformed = Trans->bHaveTransformedAllChildren; 02099 m_PrevTransform = *Trans; // Store previous transform 02100 #endif 02101 #if SOLID_TRANSFORM_METHOD == 4 02102 // If the dragged objects have become innacurate compared to the transformed 02103 // originals then we must ReCopy, else we can continue to transform the dragged 02104 // objects 02105 // 02106 // Get width/height of dragged objects 02107 // Calc width height of transformed original bounds 02108 // If the two differ by more than half a pixel 02109 // Recopy 02110 // 02111 DocRect tr = DocRect(OriginalBounds[0].x, OriginalBounds[0].y, OriginalBounds[2].x, OriginalBounds[2].y); 02112 m_PrevTransform.Transform((DocCoord*)&tr, 2); 02113 //TRACEUSER( "Phil", _T("M4 Test %d %d\n"), abs(tr.lox-m_AccuracyTestRect.lox), abs(tr.loy-m_AccuracyTestRect.loy)); 02114 02115 // We may need to move the dragged range to a new spread if the transform requires it... 02116 if (CanChangeSpread() && (m_pDragSpread!=CurrentSpread)) 02117 { 02118 // Move m_DraggedRange to the new spread 02119 //TRACEUSER("Phil", _T("Move m_Dragged range to new spread!\n")); 02120 bForceRecopy = TRUE; 02121 } 02122 02123 if( bForceRecopy || 02124 abs( tr.lo.x - m_AccuracyTestRect.lo.x ) > 10 || 02125 abs( tr.lo.y - m_AccuracyTestRect.lo.y ) > 10 || 02126 abs( tr.hi.x - m_AccuracyTestRect.hi.x ) > 10 || 02127 abs( tr.hi.y - m_AccuracyTestRect.hi.y ) > 10 ) 02128 { 02129 //TRACEUSER( "Phil", _T("M4 Recopy %f\n"), Trans->GetScale().MakeDouble()); 02130 // Update the dragged objects the dumb way - by deleting and recreating them 02131 m_pDraggedRange->DeleteNodes(); 02132 delete m_pDraggedRange; 02133 02134 Layer* pTargetLayer = NULL; 02135 if (CurrentSpread!=m_pDragSpread) 02136 pTargetLayer = CurrentSpread->FindActiveLayer(); // This logic must match the final copy done in DragFinished 02137 02138 m_pDraggedRange = m_pTransformRange->CloneNodes(DocView::SolidDragTimeLimit, FALSE, FALSE, pTargetLayer); 02139 02140 if (m_pDraggedRange==NULL) 02141 return FALSE; 02142 02143 m_pDraggedRange->SetDraggedNodes(TRUE); 02144 m_pDraggedRange->SetRenderable(TRUE); 02145 m_pDragSpread = CurrentSpread; 02146 02147 // Scan the selection, and transform each selected object. 02148 Trans->bSolidDrag = TRUE; 02149 Trans->bHaveTransformedAllCached = TRUE; 02150 Trans->bHaveTransformedAllChildren = TRUE; 02151 Trans->bTransformYourChildren = bForceRecopy | bQualNotSolid; // if forced recopy then also force transformation of everything 02152 TransOK = m_pDraggedRange->TransformNodes(Trans); 02153 m_bRangeCacheTransformed = Trans->bHaveTransformedAllCached; 02154 m_bRangeFullyTransformed = Trans->bHaveTransformedAllChildren; 02155 02156 // Reset accuracy test rect because we have just reset the dragged objects 02157 m_AccuracyTestRect = DocRect(OriginalBounds[0].x, OriginalBounds[0].y, OriginalBounds[2].x, OriginalBounds[2].y); 02158 Trans->Transform((DocCoord*)&m_AccuracyTestRect, 2); 02159 } 02160 else 02161 { 02162 //TRACEUSER( "Phil", _T("M4 Transform\n")); 02163 // Move objects back to original position simply by premultiplying the transformation 02164 // matrix by the inverse of the previous transform 02165 m_PrevTransform.Invert(); 02166 m_PrevTransform *= *Trans; 02167 02168 // Scan the selection, and transform each selected object. 02169 m_PrevTransform.bSolidDrag = TRUE; 02170 m_PrevTransform.bHaveTransformedAllCached = TRUE; // having transformed none we can say all caches have been transformed 02171 m_PrevTransform.bHaveTransformedAllChildren = TRUE; // having transformed none we can say all caches have been transformed 02172 m_PrevTransform.bTransformYourChildren = bQualNotSolid; // The transform does not have to transform all its data 02173 TransOK = m_pDraggedRange->TransformNodes(&m_PrevTransform); 02174 m_bRangeCacheTransformed = m_PrevTransform.bHaveTransformedAllCached; 02175 m_bRangeFullyTransformed = m_PrevTransform.bHaveTransformedAllChildren; 02176 02177 // Transform accuracy test rect in the same way we have just done for the dragged objects 02178 // to track what's hapenning to them... 02179 m_PrevTransform.Transform((DocCoord*)&m_AccuracyTestRect, 2); 02180 } 02181 02182 m_PrevTransform = *Trans; 02183 02184 #endif 02185 02186 if (m_pDraggedRange && bForceRedraw) 02187 m_pDraggedRange->ForceRedrawView(GetWorkingDocView(), TRUE); 02188 02189 if (!TransOK) 02190 return FALSE; 02191 02192 delete Trans; 02193 02194 return TRUE; 02195 } 02196 02197 02198 02199 02200 /******************************************************************************************** 02201 02202 > BOOL TransOperation::LeaveCopyOfSelection() 02203 02204 Author: Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> 02205 Created: 21/7/94 02206 Returns: TRUE if it worked, FALSE if it did not 02207 Purpose: If the flag LeaveCopy is set (ie we have been instructed to leave a copy of 02208 the selection in its original location) then this function is called. It 02209 makes a copy of the current selection and places them in the tree. 02210 SeeAlso: TransOperation::CompleteTransformation() 02211 02212 ********************************************************************************************/ 02213 02214 BOOL TransOperation::LeaveCopyOfSelection() 02215 { 02216 // we have to go through the current selection and make copies of all the nodes 02217 Node* pNode = m_pTransformRange->FindFirst(); 02218 while (pNode!=NULL) 02219 { 02220 // Make sure that the node is bounded 02221 ENSURE(pNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)), 02222 "Selected node was not bounded"); 02223 02224 // Go and find the next selected node before we do any insertions 02225 Node* pNext = m_pTransformRange->FindNext(pNode); 02226 02227 // Make a copy of this node and return FALSE if it failed 02228 Node* pCopy; 02229 if (!pNode->NodeCopy(&pCopy)) 02230 return FALSE; 02231 02232 // Ensure that the copy is not selected 02233 pCopy->SetSelected(FALSE); 02234 02235 // Insert the new node into the tree just after the original, 02236 // without effecting the selection, or forcing a redraw. 02237 if (!DoInsertNewNode((NodeRenderableBounded*)pCopy, pNode, PREV, 02238 FALSE, FALSE, FALSE)) 02239 return FALSE; 02240 02241 // Find the next node to copy 02242 pNode = pNext; 02243 } 02244 02245 // All worked 02246 return TRUE; 02247 } 02248 02249 02250 02251 /***************************************************************************** 02252 > BOOL TransOperation::GetStatusLineText(String_256* pText, Spread*, DocCoord, ClickModifiers) 02253 02254 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02255 Created: 16/02/95 02256 Inputs: Spread*, DocCoord, ClickModifiers - not used 02257 Outputs: pText - 02258 Returns: TRUE if returning valid text 02259 Purpose: get status line hlep for drag op 02260 Errors: this==NULL 02261 *****************************************************************************/ 02262 02263 BOOL TransOperation::GetStatusLineText(String_256* pText, Spread*, DocCoord, ClickModifiers) 02264 { 02265 ERROR2IF(this==NULL, FALSE,"TransOperation::GetStatusLineText() - this==NULL"); 02266 ERROR2IF(pText==NULL,FALSE,"TransOperation::GetStatusLineText() - pText==NULL"); 02267 02268 FigureStatusText(pText); 02269 return TRUE; 02270 } 02271 02272 02273 02274 /***************************************************************************** 02275 > void TransOperation::FigureStatusText() 02276 02277 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02278 Created: 16/02/95 02279 Inputs: - 02280 Outputs: pText - 02281 Returns: - 02282 Purpose: Compute help text for this transform 02283 Errors: this==NULL 02284 *****************************************************************************/ 02285 02286 void TransOperation::FigureStatusText(String_256* pText) 02287 { 02288 pText->Empty(); 02289 02290 if (StatusHelpID!=0) 02291 pText->Load(StatusHelpID); 02292 02293 // Get info about current drag mode and show the user 02294 DocView* pDocView = GetWorkingDocView(); 02295 if (pDocView && pDocView->GetCurrentSolidDragAbility()) 02296 { 02297 if (pDocView->GetCurrentSolidDragState()) 02298 *pText += String_256(_R(IDS_TAB_SOLID_OFF)); 02299 else 02300 *pText += String_256(_R(IDS_TAB_SOLID_ON)); 02301 } 02302 02303 // Is the click-while-dragging preference is set for dropping copies? 02304 if (ClickWhileDragFunc>1) 02305 { 02306 //Yes. Show the "dropping copies" status bar text 02307 *pText += String_256(_R(IDS_DROP_COPY)); 02308 } 02309 else 02310 { 02311 //Otherwise, the preference is set so that clicking while dragging 02312 //sets the LeaveCopy flag. Show the "leaving copies" status bar text. 02313 if (LeaveCopy) 02314 *pText += String_256(_R(IDS_LEAVE_COPY_ON_B)); 02315 else 02316 *pText += String_256(_R(IDS_LEAVE_COPY_OFF_B)); 02317 } 02318 02319 if (TransFills) 02320 *pText += String_256(_R(IDS_TRANSFILLS_ON)); 02321 else 02322 *pText += String_256(_R(IDS_TRANSFILLS_OFF)); 02323 02324 02325 if (StatusHelpID2!=0) 02326 *pText += String_256(StatusHelpID2); 02327 02328 02329 if (CanScaleLines) 02330 { 02331 if (ScaleLines) 02332 *pText += String_256(_R(IDS_SCALELINES_ON)); 02333 else 02334 *pText += String_256(_R(IDS_SCALELINES_OFF)); 02335 } 02336 02337 } 02338 02339 /******************************************************************************************** 02340 02341 >>> BOOL DragCopyAction(BOOL bSolidDrag) 02342 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02343 Created: 21/6/96 02344 Inputs: None 02345 Outputs: - 02346 Returns: FALSE if the function it called did not work. 02347 Purpose: OnKeyPress and OnClickWhileDragging call this function when Num + is pressed 02348 during a drag or when the user clicks a mouse button while dragging with 02349 another button. 02350 02351 These two events have the same effect but that effect is set by a user 02352 preference. This function looks at that preference and calls the 02353 appropriate function. 02354 02355 Errors: None. 02356 SeeAlso: TransOperation::OnClickWhileDragging; TransOperation::DropCopy; 02357 TransOperation::SetLeaveCopy; 02358 02359 ********************************************************************************************/ 02360 BOOL TransOperation::DragCopyAction(BOOL bSolidDrag) 02361 { 02362 //Call the function appropriate to the preference assigned to the key pressed. 02363 02364 if (ClickWhileDragFunc > 1) 02365 { 02366 return DropCopy(bSolidDrag); 02367 } 02368 else 02369 { 02370 return SetLeaveCopy(); 02371 } 02372 } 02373 02374 /******************************************************************************************** 02375 02376 >>> BOOL DropCopy(BOOL bSolidDrag) 02377 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02378 Created: 21/6/96 02379 Inputs: None. 02380 Outputs: - 02381 Returns: FALSE if it did not work due to lack of memory 02382 Purpose: This function drops a copy of the dragged object during the drag, 02383 appropriately translated or transformed. 02384 02385 Because the user has dropped objects during the drag, we assume he does not 02386 want to transform the original objects at the end of the drag. That is, the user 02387 has used this drag to copy objects, not to move them. So we set the 02388 AbortTransAtEnd flag. 02389 02390 Errors: One memory error if no space for an error 02391 SeeAlso: TransOperation::DragCopyAction; OpCopyAndTransform::Do 02392 02393 ********************************************************************************************/ 02394 BOOL TransOperation::DropCopy(BOOL bSolidDrag) 02395 { 02396 //Because we are dropping a copy, we take that as a signal the user doesn't 02397 //want the drag to transform the selection. Setting this flag will abort the 02398 //transformation when the drag ends. 02399 AbortTransAtEnd=TRUE; 02400 02401 //Prepare the Trans2DMatrix to send to the CopyAndTransform operation 02402 Trans2DMatrix MatrixToSend(Transform); 02403 02404 MatrixToSend.TransFills = TransFills; 02405 MatrixToSend.TransLines = ScaleLines && CanScaleLines; 02406 02407 //Create a new CopyAndTransform operation with that matrix 02408 OpCopyAndTransform* pOpCopyAndTransform = new OpCopyAndTransform(MatrixToSend); 02409 02410 ERROR1IF(pOpCopyAndTransform==NULL, FALSE, _R(IDE_NOMORE_MEMORY)); 02411 02412 // DMc - store the original selection 02413 02414 // DMc - build a list of the original selection, so we can reselect them later 02415 Range Sel(*(GetApplication()->FindSelection())); 02416 02417 List * pSelList = Sel.MakeListOfNodes(FALSE); 02418 02419 //Do the operation 02420 pOpCopyAndTransform->Do(NULL); 02421 02422 // Make sure the dragged objects are on top by removing them and then putting them back on top 02423 // (Could just move them in the tree but there's no routine to do that at the moment...) 02424 if (m_pDraggedRange) 02425 { 02426 Range* pNewDraggedRange = m_pDraggedRange->CloneNodes(DocView::SolidDragTimeLimit, TRUE); 02427 if (pNewDraggedRange) 02428 { 02429 if (bSolidDrag) 02430 { 02431 pNewDraggedRange->SetRenderable(TRUE); 02432 } 02433 else 02434 { 02435 pNewDraggedRange->SetRenderable(FALSE); 02436 } 02437 pNewDraggedRange->SetDraggedNodes(TRUE); 02438 m_pDraggedRange->DeleteNodes(); 02439 delete m_pDraggedRange; 02440 m_pDraggedRange = pNewDraggedRange; 02441 } 02442 } 02443 02444 // DMc - restore the selection afterwards 02445 // restore the selection back to what it was before we entered the operation 02446 if (pSelList) 02447 { 02448 NodeRenderableInk::DeselectAll(FALSE, FALSE); 02449 02450 // get back the original selection 02451 NodeListItem * pItem = (NodeListItem *)pSelList->GetHead(); 02452 02453 while (pItem) 02454 { 02455 pItem->pNode->SetSelected(TRUE); 02456 02457 pItem = (NodeListItem *)pSelList->GetNext(pItem); 02458 } 02459 02460 pSelList->DeleteAll(); 02461 delete pSelList; 02462 GetApplication()->UpdateSelection(); 02463 } 02464 02465 //Change the initial status bar message to read "Selection will not move after drag" 02466 StatusHelpID=_R(IDS_ABORT_TRANSFORMATION1); 02467 02468 // Show the original objects if we are solid dragging, since they will be left behind 02469 // now that we have dropped a copy 02470 DocView* pDocView = GetWorkingDocView(); 02471 m_pTransformRange->SetRenderable(TRUE); 02472 m_pTransformRange->ForceRedrawView(pDocView, TRUE, FALSE, TRUE); // Don't recache objects - they haven't changed 02473 if (m_bShowOriginalOutlines) 02474 { 02475 m_pTransformRange->RenderXOROutlinesOff(NULL, CurrentSpread, &OriginalTransform); 02476 m_bShowOriginalOutlines = FALSE; 02477 } 02478 pDocView->FlushRedraw(); 02479 GetApplication()->ServiceRendering(); 02480 02481 //StatusHelpID2 contains all the information on SHIFT-dragging, CTRL-dragging etc. 02482 //This does not change when a copy is dropped, so the variable stays the same. 02483 //And return to say it worked 02484 return TRUE; 02485 } 02486 02487 /******************************************************************************************** 02488 02489 >>> BOOL SetLeaveCopy () 02490 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02491 Created: 21/6/96 02492 Inputs: None 02493 Outputs: - 02494 Returns: TRUE. 02495 Purpose: Sets the LeaveCopy flag and the corresponding flags in BoundingData. 02496 Errors: None. 02497 SeeAlso: TransOperation::DragCopyAction 02498 02499 ********************************************************************************************/ 02500 BOOL TransOperation::SetLeaveCopy () 02501 { 02502 //If SelectInside is off, set the LeaveCopy flag and corresponding BoundingData flags. 02503 if (!m_pTransformRange->ContainsSelectInside()) 02504 { 02505 LeaveCopy = !LeaveCopy; 02506 BoundingData.LeaveCopy = LeaveCopy; 02507 BoundingData.LeaveCopyChanged = TRUE; 02508 } 02509 02510 return TRUE; 02511 } 02512 02513 /******************************************************************************************** 02514 02515 >>> void TransOperation::SelectNodeAfterDrag (Node* NodeToSelect) 02516 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02517 Created: 21/6/96 02518 Inputs: The node we want to select after the drag 02519 Purpose Adds the node to list of nodes to select after the drag 02520 02521 ********************************************************************************************/ 02522 void TransOperation::SelectNodeAfterDrag (Node* NodeToSelect) 02523 { 02524 NodeListItem* pnliNode=new NodeListItem(NodeToSelect); 02525 02526 lstNodesToSelect.AddTail((ListItem*) pnliNode); 02527 } 02528 02529 /******************************************************************************************** 02530 02531 >>> void ClearNodesToSelect () 02532 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02533 Created: 21/6/96 02534 Purpose Empties the node list and deletes all the items in it. 02535 02536 ********************************************************************************************/ 02537 void TransOperation::ClearNodesToSelect () 02538 { 02539 lstNodesToSelect.DeleteAll(); 02540 } 02541 02542 02543 02544 02545 /******************************************************************************************** 02546 02547 > BOOL TransOperation::SetTransformRange(Range* pRange, Node* pNode) 02548 02549 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02550 Created: 17/12/2003 02551 Returns: - 02552 Purpose: Sets the m_pTransformRange member to be a local range created either 02553 as a copy of the supplied range or a range created to represent the node 02554 or a copy of the selection range. 02555 02556 ********************************************************************************************/ 02557 BOOL TransOperation::SetTransformRange(Range* pTransformRange, Node* pTransformNode) 02558 { 02559 // If we have a good range and we're being given the same range then do nothing 02560 if (m_pTransformRange!=NULL && pTransformRange==m_pTransformRange) 02561 return TRUE; 02562 02563 // If we have a good range and the caller is giving us nothing then do nothing 02564 if (m_pTransformRange && pTransformRange==NULL && pTransformNode==NULL) 02565 return TRUE; 02566 02567 if (pTransformNode) 02568 { 02569 // Only transforming single node. First check it's correct type 02570 ERROR3IF(!pTransformNode->IS_KIND_OF(NodeRenderableBounded), "Should be bounded"); 02571 02572 m_pTransformRange = new Range(pTransformNode, 02573 pTransformNode, 02574 RangeControl(TRUE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE)); 02575 ERROR2IF(m_pTransformRange==NULL, FALSE, "Can't create simple range"); 02576 } 02577 else 02578 { 02579 if (pTransformRange) 02580 { 02581 m_pTransformRange = new Range(*pTransformRange); 02582 ERROR2IF(m_pTransformRange == NULL, FALSE, "No supplied range"); 02583 } 02584 else 02585 { 02586 m_pTransformRange = new Range(*(GetApplication()->FindSelection())); 02587 ERROR2IF(m_pTransformRange == NULL, FALSE, "No selected range"); 02588 m_pTransformRange->SetPromoteToParent(TRUE); 02589 } 02590 } 02591 02592 // If this goes off there's a logical error in the above code that works 02593 // out the range to transform. 02594 ERROR3IF(!m_pTransformRange, "No range to transform in TransOperation::DoStartTransOp"); 02595 return (m_pTransformRange!=NULL); 02596 } 02597 02598 02599 02600 02601 /******************************************************************************************** 02602 02603 > BOOL TransOperation::TransformOverlapsOriginal() 02604 02605 Author: Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> 02606 Created: 19/03/2004 02607 Returns: TRUE if the original bounds overlap the transformed bounds 02608 Purpose: Check whether transform objects overlap originals 02609 02610 ********************************************************************************************/ 02611 BOOL TransOperation::TransformOverlapsOriginal() 02612 { 02613 DocRect TransformBounds(BoundingData.x, BoundingData.y, 02614 BoundingData.x+BoundingData.Width, BoundingData.y+BoundingData.Height 02615 ); 02616 DocRect OrigBounds(OriginalBounds[0].x, OriginalBounds[0].y, OriginalBounds[2].x, OriginalBounds[2].y); 02617 02618 return (TransformBounds.IsIntersectedWith(OrigBounds)); 02619 } 02620 02621 02622 02623