00001 // $Id: cutop.cpp 1467 2006-07-18 17:00:07Z gerry $ 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 // Implementation of cut/copy and paste operations 00099 00100 /* 00101 //*/ 00102 00103 #include "camtypes.h" 00104 #include "cutop.h" 00105 00106 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00107 #include "clipint.h" 00108 //#include "docrect.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00109 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00110 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00111 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 #include "layer.h" 00113 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00114 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00115 #include "wrkrect.h" 00116 #include "objchge.h" 00117 00118 //#include "clikdrag.h" 00119 00120 //#include "jason.h" 00121 //#include "peter.h" 00122 //#include "simon.h" 00123 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00124 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00125 00126 #include "textops.h" 00127 #include "nodetxts.h" // For temporary disabling of text subselection copying/cutting 00128 //#include "barsdlgs.h" // button controls 00129 00130 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00131 #include "ndoptmz.h" 00132 #include "progress.h" 00133 #include "transop.h" 00134 //#include "bubbleid.h" 00135 00136 #include "camdoc.h" // for CCamDoc::GetKernelPathName & CCamDoc::SetPathName 00137 00138 #include "fillramp.h" 00139 #include "opgrad.h" 00140 #include "blobs.h" 00141 00142 //#include "cxfdefs.h" // for TemplateAttribute - in camtypes.h [AUTOMATICALLY REMOVED] 00143 #include "cxftags.h" 00144 //#include "cxfrec.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00145 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00146 //#include "attrval.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00147 #include "userattr.h" 00148 #include "tmpltatr.h" 00149 00150 #include "ngsentry.h" // for NodeSetProperty etc 00151 #include "slicehelper.h" // for the MeshLayers fn 00152 00153 //#include "will2.h" // for _R(IDS_FILLTOOL_FILLINFOBARNAME) 00154 #include "filltool.h" // for GradInfoBarOp 00155 #include "ngitem.h" // for SGNameItem 00156 //#include "units.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00157 #include "effects_stack.h" 00158 #include "ophist.h" 00159 00160 DECLARE_SOURCE("$Revision: 1467 $"); 00161 00162 CC_IMPLEMENT_DYNCREATE(OpCut, SelOperation) 00163 CC_IMPLEMENT_DYNCREATE(OpCopy, UndoableOperation) 00164 CC_IMPLEMENT_DYNCREATE(OpPaste, SelOperation) 00165 CC_IMPLEMENT_DYNCREATE(OpDelete, SelOperation) 00166 CC_IMPLEMENT_DYNCREATE(CarbonCopyOp, SelOperation) 00167 CC_IMPLEMENT_DYNCREATE(OpDuplicate, CarbonCopyOp) 00168 CC_IMPLEMENT_DYNCREATE(OpClone, CarbonCopyOp) 00169 CC_IMPLEMENT_DYNCREATE(OpCopyAndTransform, CarbonCopyOp) 00170 CC_IMPLEMENT_DYNCREATE(OpPasteAttributes, SelOperation) 00171 CC_IMPLEMENT_DYNAMIC(OpParamPasteAtPosition,OpParam) 00172 00173 00174 /******************************************************************************************** 00175 00176 > static void SetCutOpText(String_256 *UIDescription, UINT32 PrefixStringID) 00177 00178 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00179 Created: 22/5/95 00180 Inputs: UIDescription - points to the UOIDescription for a GetState method 00181 PrefixStringID - the ID of a prefix to use (e.g. "Cut ") 00182 00183 Outputs: UIDescription will either be left unchanged (no special description 00184 available) or will be filled in (e.g. "Cut lines") 00185 00186 Purpose: Shared code used by almost all the GetState methods in kernel cutop.cpp 00187 Given a prefix string ("Copy "), it scans the selection for a description 00188 and generates an op description string ("Cut shape") 00189 00190 ********************************************************************************************/ 00191 00192 static void SetCutOpText(String_256 *UIDescription, UINT32 PrefixStringID) 00193 { 00194 String_256 Description = GetApplication()->FindSelection()->Describe(MENU); 00195 00196 if (!Description.IsEmpty()) 00197 { 00198 *UIDescription = String_256(PrefixStringID); 00199 *UIDescription += Description; 00200 } 00201 // else Leave the UIDescription as it stands (probably a simple "Cut" etc) 00202 } 00203 00204 00205 00206 // ------------------------------------------------------------------------------------------ 00207 // OpCut methods 00208 00209 /******************************************************************************************** 00210 00211 > OpCut::OpCut() 00212 00213 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00214 Created: 29/9/93 00215 Inputs: - 00216 Outputs: - 00217 Returns: - 00218 Purpose: OpCut constructor 00219 Errors: - 00220 SeeAlso: - 00221 00222 ********************************************************************************************/ 00223 00224 00225 OpCut::OpCut(): SelOperation() 00226 { 00227 } 00228 00229 /******************************************************************************************** 00230 00231 > BOOL OpCut::Init() 00232 00233 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00234 Created: 28/9/93 00235 Inputs: - 00236 Outputs: - 00237 Returns: TRUE if the operation could be successfully initialised 00238 FALSE if no more memory could be allocated 00239 00240 Purpose: OpCut initialiser method 00241 Errors: ERROR will be called if there was insufficient memory to allocate the 00242 operation. 00243 SeeAlso: - 00244 00245 ********************************************************************************************/ 00246 00247 BOOL OpCut::Init() 00248 { 00249 return (RegisterOpDescriptor(0, 00250 _R(IDS_CUTOP), 00251 CC_RUNTIME_CLASS(OpCut), 00252 OPTOKEN_CUT, 00253 OpCut::GetState, 00254 0, // help ID 00255 _R(IDBBL_CUT), 00256 0, 00257 0, 00258 SYSTEMBAR_ILLEGAL, // For now ! 00259 TRUE, // Receive messages 00260 FALSE, 00261 FALSE, 00262 0, 00263 0 //(GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION) 00264 )); 00265 } 00266 00267 /******************************************************************************************** 00268 00269 > OpState OpCut::GetState(String_256*, OpDescriptor*) 00270 00271 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00272 Created: 28/9/93 00273 Inputs: - 00274 Outputs: - 00275 Returns: The state of the OpCut 00276 Purpose: For finding OpCut's state. 00277 Errors: - 00278 SeeAlso: - 00279 00280 ********************************************************************************************/ 00281 00282 OpState OpCut::GetState(String_256* UIDescription, OpDescriptor*) 00283 { 00284 OpState OpSt; 00285 00286 // BODGE Temporarily disable copying TextStory sub-selections 00287 /* 00288 Range Sel(*(GetApplication()->FindSelection())); 00289 Node* Current = Sel.FindFirst(); 00290 while(Current != NULL) 00291 { 00292 if (Current->IS_KIND_OF(BaseTextClass)) 00293 { 00294 if (!(IS_A(Current, TextStory))) 00295 { 00296 OpSt.Greyed = TRUE; 00297 return(OpSt); 00298 } 00299 } 00300 Current = Sel.FindNext(Current, TRUE); 00301 } 00302 */ 00303 00304 SelRange* pSelRange = GetApplication()->FindSelection(); // get the selected range 00305 00306 // Set up the ObjChangeParam so we can ask the selected nodes if they minds being cut 00307 ObjChangeFlags cFlags(TRUE); 00308 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL); 00309 00310 // Will one or more selected nodes allow this op? 00311 if (!pSelRange->AllowOp(&ObjChange,FALSE)) 00312 { 00313 // None of the nodes can be deleted 00314 OpSt.Greyed = TRUE; 00315 00316 // Load reason why operation is disabled 00317 UINT32 IDS = ObjChange.GetReasonForDenial(); 00318 if (IDS == 0) IDS = _R(IDS_NO_OBJECTS_SELECTED); // if 0 (i.e. not been set), then assume there's no selection 00319 *UIDescription = String_256(IDS); // Resolve the string ID 00320 } 00321 else 00322 SetCutOpText(UIDescription, _R(IDS_CLIPBOARD_PRECUT)); 00323 00324 return(OpSt); 00325 } 00326 00327 /******************************************************************************************** 00328 00329 > void OpCut::Do(OpDescriptor*) 00330 00331 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00332 Created: 16/8/93 00333 Inputs: OpDescriptor (unused) 00334 Outputs: - 00335 Returns: - 00336 Purpose: Performs the Cut operation. 00337 00338 Errors: - 00339 SeeAlso: - 00340 00341 ********************************************************************************************/ 00342 00343 void OpCut::Do(OpDescriptor*) 00344 { 00345 ObjectSet CompoundSet; 00346 00347 BeginSlowJob(-1, FALSE); // Make sure we have a simple hourglass, without a delay 00348 00349 // Check with the clipboard if it is OK to go ahead 00350 InternalClipboard *Clipboard = InternalClipboard::Instance(); 00351 if (Clipboard == NULL || !Clipboard->PrepareForCopy()) 00352 { 00353 FailAndExecute(); 00354 00355 EndSlowJob(); 00356 End(); 00357 return; 00358 } 00359 00360 // Obtain the current selection 00361 Range Sel(*(GetApplication()->FindSelection())); 00362 00363 // DMc change to range control 00364 RangeControl rg = Sel.GetRangeControlFlags(); 00365 rg.PromoteToParent = TRUE; 00366 Sel.Range::SetRangeControl(rg); 00367 00368 00369 // Prepare an ObjChangeParam so we can mark which nodes will allow this op to happen to them 00370 ObjChangeFlags cFlags(TRUE); 00371 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); 00372 00373 if (!DoStartSelOp(TRUE,FALSE)) // Try to record the selection state 00374 goto EndOperation; 00375 00376 // Mark nodes that will allow this to happen, and error if no nodes will let it happen 00377 if (!Sel.AllowOp(&ObjChange)) 00378 { 00379 ERROR3("AllowOp() returned FALSE, i.e. op should have been greyed out"); 00380 goto EndOperation; 00381 } 00382 00383 00384 // We need to invalidate the region 00385 if (!DoInvalidateNodesRegions(Sel, TRUE)) 00386 goto EndOperation; 00387 00388 // Prior to hiding anything lets get ourselves a set of compound nodes which 00389 // we will need to factor out attributes on. We cannot use the SelRange because 00390 // this will be empty after the deletion has taken place. 00391 00392 if (!(Sel.GetCompoundObjectSet(&CompoundSet, 00393 TRUE ))) 00394 { 00395 goto EndOperation; 00396 } 00397 00398 // Removed 24/07/95 (We now leave it up to the text story deletion code to handle its own attributes) 00399 00400 // Because of complex deletion we must also add the TextStory objects 00401 // of selected chars. This code will hopefuly be removed in future. It is 00402 // inefficient cos we will need to factor out more than once for each line !. 00403 00404 //if (!(Sel.AddTextStoryCompoundsForDel(&CompoundSet))) 00405 //{ 00406 // goto EndOperation; 00407 //} 00408 00409 // We now must localise the attributes on every item marked for deletion 00410 if (!DoLocaliseForAttrChange(&Sel, (AttrTypeSet*)NULL, TRUE )) 00411 { 00412 goto EndOperation; 00413 } 00414 00415 00416 // Copy the selection to the internal clipboard 00417 if (!DoCopyNodesToClipboard(Sel)) 00418 goto EndOperation; 00419 00420 // Go through one pass, asking nodes to hide themselves 00421 if (!DoHideComplexRange(Sel)) 00422 goto EndOperation; 00423 { 00424 SelRange* pRange = GetApplication()->FindSelection(); 00425 if (pRange) 00426 { 00427 // freshen the selection range by counting the currently selected objects 00428 pRange->Update(FALSE,NULL); 00429 // INT32 num = pRange->Count(); 00430 pRange->Count(); // side-effect? 00431 00432 // Obtain the current selection (should be up-to-date now!) 00433 Range NewSel(*pRange); 00434 00435 // Now go through a second pass snip the remainder out of the tree 00436 if (!DoHideNodes(NewSel,TRUE)) // Hide the nodes (IncludeSubtreeSize) 00437 goto EndOperation; 00438 } 00439 } 00440 00441 // Finally try to factor out the attributes on all compounds. Note that if a particular compound 00442 // is hidden then it will be it's parent that has its attributes factored out ! 00443 if (!DoFactorOutAfterAttrChange(&CompoundSet,(AttrTypeSet*)NULL)) 00444 { 00445 goto EndOperation; 00446 } 00447 00448 // We need to inform the DocComponents of the clipboard that the copy is complete 00449 00450 EndOperation: 00451 00452 CompoundSet.DeleteAll(); // Delete all items in the CompoundSet 00453 00454 // Update all the changed nodes, i.e. tell all the parents of the children that have been effected 00455 ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this); 00456 if (!UpdateChangedNodes(&ObjChange)) 00457 FailAndExecute(); 00458 00459 // Invalidate sel range as it may have been cached in the middle of the op while the tree is 00460 // in an invalid state (i.e. some selected nodes may have had the PERMISSION_DENIED state set during caching). 00461 { 00462 SelRange* pRange = GetApplication()->FindSelection(); 00463 if (pRange) 00464 pRange->Update(FALSE,NULL); 00465 } 00466 00467 // Copy the doc's file-path etc, so that the clipboard doc knows if it is possible to 00468 // OLE link to the doc. 00469 Document* pSourceDoc = Document::GetSelected(); 00470 if (pSourceDoc && !pSourceDoc->GetOilDoc()->GetKernelPathName().IsEmpty()) 00471 { 00472 // Don't add to the MRU list! 00473 String_256 str = pSourceDoc->GetOilDoc()->GetKernelPathName(); 00474 InternalClipboard::Instance()->GetOilDoc()->SetPathName(str, FALSE); 00475 TRACEUSER( "JustinF", _T("Copied file-path %s to clipboard document\n"), 00476 (LPCTSTR) InternalClipboard::Instance()->GetOilDoc()->GetKernelPathName()); 00477 } 00478 else 00479 { 00480 // Set the path-name to be empty (what a delicious potential bug!) 00481 InternalClipboard::Instance()->GetOilDoc()->SetPathNameEmpty(); 00482 TRACEUSER( "JustinF", _T("Setting clipboard file-path to nothing\n")); 00483 } 00484 00485 // And finally, tell the clipboard we've finished copying to it 00486 // (This is the point where it is made available to other applications, etc) 00487 if (!Clipboard->CopyCompleted()) 00488 FailAndExecute(); 00489 00490 EndSlowJob(); 00491 End(); 00492 } 00493 00494 00495 // ------------------------------------------------------------------------------------------ 00496 // OpCopy methods 00497 00498 /******************************************************************************************** 00499 00500 > OpCopy::OpCopy() 00501 00502 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00503 Created: 29/9/93 00504 Inputs: - 00505 Outputs: - 00506 Returns: - 00507 Purpose: OpCopy constructor 00508 Errors: - 00509 SeeAlso: - 00510 00511 ********************************************************************************************/ 00512 00513 00514 OpCopy::OpCopy(): SelOperation() 00515 { 00516 } 00517 00518 /******************************************************************************************** 00519 00520 > BOOL OpCopy::Init() 00521 00522 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00523 Created: 28/9/93 00524 Inputs: - 00525 Outputs: - 00526 Returns: TRUE if the operation could be successfully initialised 00527 FALSE if no more memory could be allocated 00528 00529 Purpose: OpCopy initialiser method 00530 Errors: ERROR will be called if there was insufficient memory to allocate the 00531 operation. 00532 SeeAlso: - 00533 00534 ********************************************************************************************/ 00535 00536 BOOL OpCopy::Init() 00537 { 00538 return (RegisterOpDescriptor(0, 00539 _R(IDS_COPYOP), 00540 CC_RUNTIME_CLASS(OpCopy), 00541 OPTOKEN_COPY, 00542 OpCopy::GetState, 00543 0, // help ID 00544 _R(IDBBL_COPY), 00545 0, // bitmap ID 00546 0, 00547 SYSTEMBAR_ILLEGAL, // For now ! 00548 TRUE, // Receive messages 00549 FALSE, 00550 FALSE, 00551 0, 00552 (DONT_GREY_WHEN_SELECT_INSIDE | GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION) 00553 )); 00554 } 00555 00556 /******************************************************************************************** 00557 00558 > OpState OpCopy::GetState(String_256*, OpDescriptor*) 00559 00560 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00561 Created: 28/9/93 00562 Inputs: - 00563 Outputs: - 00564 Returns: The state of the OpCopy 00565 Purpose: For finding OpCopy's state. 00566 Errors: - 00567 SeeAlso: - 00568 00569 ********************************************************************************************/ 00570 00571 OpState OpCopy::GetState(String_256* UIDescription, OpDescriptor*) 00572 { 00573 OpState OpSt; 00574 00575 // BODGE Temporarily disable copying TextStory sub-selections 00576 Range Sel(*(GetApplication()->FindSelection())); 00577 /*Node* Current =*/ Sel.FindFirst(); 00578 00579 // Set up the ObjChangeParam so we can ask the selected nodes if they minds being copied 00580 ObjChangeFlags cFlags; 00581 cFlags.CopyNode=TRUE; 00582 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL); 00583 00584 // Will one or more selected nodes allow this op? 00585 if (!Sel.AllowOp(&ObjChange,FALSE)) 00586 { 00587 // None of the nodes can be deleted 00588 OpSt.Greyed = TRUE; 00589 00590 // Load reason why operation is disabled 00591 UINT32 IDS = ObjChange.GetReasonForDenial(); 00592 if (IDS == 0) IDS = _R(IDS_NO_OBJECTS_SELECTED); // if 0 (i.e. not been set), then assume there's no selection 00593 *UIDescription = String_256(IDS); // Resolve the string ID 00594 } 00595 else 00596 SetCutOpText(UIDescription, _R(IDS_CLIPBOARD_PRECOPY)); 00597 00598 return(OpSt); 00599 } 00600 00601 /******************************************************************************************** 00602 00603 > void OpCopy::Do(OpDescriptor*) 00604 00605 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00606 Created: 16/8/93 00607 Inputs: OpDescriptor (unused) 00608 Outputs: - 00609 Returns: - 00610 Purpose: Performs the Copy operation. 00611 00612 Errors: - 00613 SeeAlso: - 00614 00615 ********************************************************************************************/ 00616 00617 void OpCopy::Do(OpDescriptor*) 00618 { 00619 BeginSlowJob(-1, FALSE); // Make sure we have a simple hourglass, without a delay 00620 00621 // Check with the clipboard if it is OK to go ahead 00622 InternalClipboard *Clipboard = InternalClipboard::Instance(); 00623 if (Clipboard == NULL || !Clipboard->PrepareForCopy()) 00624 { 00625 FailAndExecute(); 00626 00627 EndSlowJob(); 00628 End(); 00629 return; 00630 } 00631 00632 // Obtain the current selections 00633 Range Sel(*(GetApplication()->FindSelection())); 00634 00635 // DMc change to range control 00636 00637 RangeControl rg = Sel.GetRangeControlFlags(); 00638 rg.PromoteToParent = TRUE; 00639 Sel.Range::SetRangeControl(rg); 00640 00641 // Prepare an ObjChangeParam so we can mark which nodes will allow this op to happen to them 00642 ObjChangeFlags cFlags; 00643 cFlags.CopyNode=TRUE; 00644 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); 00645 00646 // Find the first node which is selected 00647 Node* FirstSelectedNode = Sel.FindFirst(); 00648 00649 ENSURE(FirstSelectedNode != NULL, "Called copy operation with no nodes selected"); 00650 00651 // In the retail build it is best to do nothing if we find there are no selected nodes 00652 if (FirstSelectedNode != NULL) // No nodes selected so End 00653 { 00654 if (!DoStartSelOp(TRUE,TRUE)) // Try to record the selection state 00655 { 00656 goto EndOperation; 00657 } 00658 00659 // Mark nodes that will allow this to happen, and error if no nodes will let it happen 00660 if (!Sel.AllowOp(&ObjChange)) 00661 { 00662 ERROR3("AllowOp() returned FALSE, i.e. op should have been greyed out"); 00663 goto EndOperation; 00664 } 00665 00666 // Copy the selection to the internal clipboard 00667 if (!DoCopyNodesToClipboard(Sel)) 00668 { 00669 goto EndOperation; 00670 } 00671 } 00672 00673 00674 EndOperation: 00675 // Update all the changed nodes, i.e. tell all the parents of the children that have been effected 00676 ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this); 00677 if (!UpdateChangedNodes(&ObjChange)) 00678 FailAndExecute(); 00679 00680 // Copy the doc's file-path etc, so that the clipboard doc knows if it is possible to 00681 // OLE link to the doc. 00682 Document* pSourceDoc = Document::GetSelected(); 00683 if (pSourceDoc && !pSourceDoc->GetOilDoc()->GetKernelPathName().IsEmpty()) 00684 { 00685 // Don't add to the MRU list! 00686 String_256 str = pSourceDoc->GetOilDoc()->GetKernelPathName(); 00687 InternalClipboard::Instance()->GetOilDoc()->SetPathName(str, FALSE); 00688 TRACEUSER( "JustinF", _T("Copied file-path %s to clipboard document\n"), 00689 (LPCTSTR) InternalClipboard::Instance()->GetOilDoc()->GetKernelPathName()); 00690 } 00691 else 00692 { 00693 // Set the path-name to be empty (what a delicious potential bug!) 00694 InternalClipboard::Instance()->GetOilDoc()->SetPathNameEmpty(); 00695 TRACEUSER( "JustinF", _T("Setting clipboard file-path to nothing\n")); 00696 } 00697 00698 // And finally, tell the clipboard we've finished copying to it 00699 // (This is the point where it is made available to other applications, etc) 00700 if (!Clipboard->CopyCompleted()) 00701 FailAndExecute(); 00702 00703 EndSlowJob(); 00704 End(); 00705 } 00706 00707 00708 // ------------------------------------------------------------------------------------------ 00709 // OpPaste methods 00710 00711 /******************************************************************************************** 00712 00713 > OpPaste::OpPaste() 00714 00715 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00716 Created: 29/9/93 00717 Inputs: - 00718 Outputs: - 00719 Returns: - 00720 Purpose: OpPaste constructor 00721 Errors: - 00722 SeeAlso: - 00723 00724 ********************************************************************************************/ 00725 00726 00727 OpPaste::OpPaste(): SelOperation() 00728 { 00729 } 00730 00731 /******************************************************************************************** 00732 00733 > BOOL OpPaste::Init() 00734 00735 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00736 Created: 28/9/93 00737 Inputs: - 00738 Outputs: - 00739 Returns: TRUE if the operation could be successfully initialised 00740 FALSE if no more memory could be allocated 00741 00742 Purpose: OpPaste initialiser method 00743 Errors: ERROR will be called if there was insufficient memory to allocate the 00744 operation. 00745 SeeAlso: - 00746 00747 ********************************************************************************************/ 00748 00749 BOOL OpPaste::Init() 00750 { 00751 BOOL ok = (RegisterOpDescriptor(0, 00752 _R(IDS_PASTEOP), 00753 CC_RUNTIME_CLASS(OpPaste), 00754 OPTOKEN_PASTE, 00755 OpPaste::GetState, 00756 0, // help ID 00757 _R(IDBBL_PASTE), 00758 0, // bitmap ID 00759 0, 00760 SYSTEMBAR_ILLEGAL, // For now ! 00761 TRUE, // Receive messages 00762 FALSE, 00763 FALSE, 00764 0, 00765 (GREY_WHEN_NO_CURRENT_DOC | DONT_GREY_WHEN_SELECT_INSIDE) 00766 )); 00767 if (ok) 00768 { 00769 // Register a second OpDescriptor, for paste at same position. This OpDescriptor 00770 // will probably never appear directly on a bar/menu so it uses the same strings 00771 // as the above. 00772 ok = (RegisterOpDescriptor(0, 00773 _R(IDS_PASTEATSAMEPOSOP), 00774 CC_RUNTIME_CLASS(OpPaste), 00775 OPTOKEN_PASTEATSAMEPOS, 00776 OpPaste::GetState, 00777 0, // help ID 00778 _R(IDBBL_PASTEATSAMEPOS), 00779 _R(IDD_BARCONTROLSTORE), // resource ID 00780 _R(IDC_PASTEATSAMEPOS), // control ID 00781 SYSTEMBAR_EDIT, // Bar ID 00782 TRUE, // Receive messages 00783 FALSE, 00784 FALSE, 00785 0, 00786 (GREY_WHEN_NO_CURRENT_DOC | DONT_GREY_WHEN_SELECT_INSIDE) 00787 )); 00788 } 00789 00790 return ok; 00791 } 00792 00793 /******************************************************************************************** 00794 00795 > OpState OpPaste::GetState(String_256*, OpDescriptor*) 00796 00797 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00798 Created: 28/9/93 00799 Inputs: - 00800 Outputs: - 00801 Returns: The state of the OpPaste 00802 Purpose: For finding OpPaste's state. 00803 Errors: - 00804 SeeAlso: - 00805 00806 ********************************************************************************************/ 00807 00808 OpState OpPaste::GetState(String_256* UIDescription, OpDescriptor* pOpDesc) 00809 { 00810 OpState OpSt; 00811 00812 if (InternalClipboard::IsEmpty()) 00813 { 00814 // There is no data to paste, so grey out, and return a reason for greying 00815 OpSt.Greyed = TRUE; 00816 *UIDescription = String_256(_R(IDS_CLIPBOARD_EMPTY)); 00817 } 00818 else 00819 { 00820 // Otherwise, determine a string to display - always show "Paste", but if possible 00821 // add a description, e.g. "Paste quickshape", "Paste bitmap" 00822 00823 String_64 Type; 00824 InternalClipboard::DescribeContents(&Type); 00825 if (!Type.IsEmpty() && pOpDesc->Token==String_256(OPTOKEN_PASTE)) 00826 { 00827 *UIDescription = String_256(_R(IDS_CLIPBOARD_PREPASTE)); 00828 *UIDescription += Type; 00829 } 00830 } 00831 return(OpSt); 00832 } 00833 00834 00835 00836 /******************************************************************************************** 00837 00838 > void OpPaste::Do(OpDescriptor*) 00839 00840 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00841 Created: 16/8/93 00842 Inputs: OpDescriptor (unused) 00843 Outputs: - 00844 Returns: - 00845 Purpose: Performs the Paste operation. 00846 00847 Errors: - 00848 SeeAlso: - 00849 00850 ********************************************************************************************/ 00851 00852 void OpPaste::Do(OpDescriptor* WhichOp) 00853 { 00854 DoPaste(WhichOp,NULL,NULL); 00855 } 00856 00857 /******************************************************************************************** 00858 00859 > void OpPaste::DoWithParam(OpDescriptor* WhichOp, OpParam* pOpParam) 00860 00861 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 00862 Created: 10/10/96 00863 Inputs: WhichOp = ptr to op descriptor 00864 pOpParam = ptr to the params (must be an OpParamPasteAtPosition object) 00865 Outputs: - 00866 Returns: - 00867 Purpose: Performs the Paste operation using the give param object. This mechanism 00868 is used by the "paste at position" op, utilised by the OLE drag & drop system. 00869 00870 pOpParam should be a ptr to an OpParamPasteAtPosition, so that the spread 00871 and centre coord of the paste can be passed in. 00872 00873 If pOpParam is not an OpParamPasteAtPosition, the op fails 00874 00875 Errors: - 00876 SeeAlso: - 00877 00878 ********************************************************************************************/ 00879 00880 void OpPaste::DoWithParam(OpDescriptor* WhichOp, OpParam* pOpParam) 00881 { 00882 if (pOpParam != NULL && IS_A(pOpParam,OpParamPasteAtPosition)) 00883 { 00884 OpParamPasteAtPosition* pPasteParam = (OpParamPasteAtPosition*)pOpParam; 00885 00886 Spread* pSpread = pPasteParam->GetSpread(); 00887 DocCoord Centre = pPasteParam->GetCentre(); 00888 00889 DoPaste(WhichOp,pSpread,&Centre); 00890 } 00891 else 00892 { 00893 FailAndExecute(); 00894 End(); 00895 } 00896 } 00897 00898 /******************************************************************************************** 00899 00900 > void OpPaste::DoPaste(OpDescriptor* WhichOp,Spread* pSpread, DocCoord* pCentre) 00901 00902 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 00903 Created: 10/10/96 00904 Inputs: WhichOp = ptr to op descriptor 00905 pSpread = ptr to spread to paste objects into (can be NULL) 00906 pCentre = coord of centre point of paste destination (can be NULL if pSpread is NULL) 00907 Outputs: - 00908 Returns: - 00909 Purpose: Helper function for Do() & DoWithParam(), which in turn interfaces with 00910 DoPasteStandard() (don't look at me - I didn't design this stuff) 00911 00912 Errors: - 00913 SeeAlso: - 00914 00915 ********************************************************************************************/ 00916 00917 void OpPaste::DoPaste(OpDescriptor* WhichOp,Spread* pSpread, DocCoord* pCentre) 00918 { 00919 BeginSlowJob(-1, FALSE); // Make sure we have a simple hourglass, without a delay 00920 00921 // Check with the clipboard if it is OK to go ahead. If the clipboard contents are 00922 // currently external, this means it'll have to import them into the InternalClipboard 00923 // before we can actually copy them into our document. 00924 InternalClipboard *Clipboard = InternalClipboard::Instance(); 00925 BOOL ExternalObjects; 00926 BOOL ok = (Clipboard!=NULL && Clipboard->PrepareForPaste(&ExternalObjects)); 00927 00928 if (ok) 00929 { 00930 // We paste at the same position if the operation was invoked via the PASTEATSAMEPOS OpDescriptor 00931 // and the stuff we are pasting has not come from an external source. 00932 BOOL PasteAtSamePos = (((WhichOp->Token) == String(OPTOKEN_PASTEATSAMEPOS)) && (!ExternalObjects)); 00933 ok = DoPasteStandard(PasteAtSamePos, ExternalObjects, pSpread, pCentre); 00934 } 00935 00936 if (ok) 00937 ok = Clipboard->PasteCompleted(); 00938 00939 if (!ok) 00940 FailAndExecute(); 00941 00942 EndSlowJob(); 00943 End(); 00944 } 00945 00946 /******************************************************************************************** 00947 00948 > BOOL OpPaste::FindCentreInsertionPosition(Spread** Spread, DocCoord* Position) 00949 00950 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00951 Created: 28/7/94 00952 Inputs: - 00953 Outputs: Spread: The spread to place the clipboard objects on 00954 Position:The centre of the view (Spread coords) 00955 00956 Returns: FALSE if it failed. In this case, Spread is returned containing a NULL, and 00957 Position is returned as DocCoord(0,0) 00958 00959 Purpose: Finds the centre insertion position for clipboard objects 00960 Errors: - 00961 Scope: private 00962 SeeAlso: - 00963 00964 ********************************************************************************************/ 00965 00966 BOOL OpPaste::FindCentreInsertionPosition(Spread** Spread, DocCoord* Position) 00967 { 00968 // Let's chuck in a smattering of defensive programming 00969 ERROR3IF(Spread == NULL || Position == NULL, "Illegal NULL params"); 00970 *Spread = NULL; 00971 *Position = DocCoord(0,0); // A default value if we fail 00972 00973 // --------------------------------------------------------------------------------- 00974 // Find out which spread is in the centre of the view 00975 // this is the spread that the pasted objects will be placed on 00976 00977 // Obtain the current DocView 00978 DocView* CurDocView = DocView::GetCurrent(); 00979 00980 ENSURE(CurDocView != NULL, "The current DocView is NULL"); 00981 if (CurDocView == NULL) 00982 { 00983 return FALSE; // No DocView 00984 } 00985 00986 // Get the view rect 00987 WorkRect WrkViewRect = CurDocView->GetViewRect(); 00988 00989 if (WrkViewRect.IsEmpty() || (!WrkViewRect.IsValid()) ) 00990 { 00991 return FALSE; // Defensive 00992 } 00993 00994 // Determine the centre of the view 00995 WorkCoord WrkCentreOfView; 00996 WrkCentreOfView.x = WrkViewRect.lo.x + (WrkViewRect.Width()/2); 00997 WrkCentreOfView.y = WrkViewRect.lo.y + (WrkViewRect.Height()/2); 00998 00999 // FindEnclosing spread requires an OilCoord 01000 OilCoord OilCentreOfView = WrkCentreOfView.ToOil(CurDocView->GetScrollOffsets()); 01001 01002 // Find out which spread to insert the pasteboard objects onto 01003 (*Spread) = CurDocView->FindEnclosingSpread(OilCentreOfView); 01004 if ((*Spread) == NULL) 01005 { 01006 // There is no spread 01007 return FALSE; 01008 } 01009 01010 // Phew 01011 // --------------------------------------------------------------------------------- 01012 // Now lets find the spread coordinate of the centre of the view 01013 DocRect DocViewRect = CurDocView->GetDocViewRect(*Spread); 01014 01015 if ( DocViewRect.IsEmpty() || (!DocViewRect.IsValid()) ) 01016 { 01017 ERROR3("DocViewRect is invalid"); 01018 return FALSE; // Defensive 01019 } 01020 01021 // Find the centre of the DocViewRect 01022 DocCoord DocCentreOfView; 01023 DocCentreOfView.x = DocViewRect.lo.x + (DocViewRect.Width()/2); 01024 DocCentreOfView.y = DocViewRect.lo.y + (DocViewRect.Height()/2); 01025 01026 // -------------------------------------------------------------------------------- 01027 // Now convert from DocCoords to spread coords 01028 (*Spread)->DocCoordToSpreadCoord(&DocCentreOfView); 01029 01030 // Finally, copy the result into the output parameter 01031 *Position = DocCentreOfView; 01032 01033 return TRUE; 01034 } 01035 01036 01037 /******************************************************************************************** 01038 01039 > BOOL OpPaste::DoPasteStandard(BOOL PasteAtSamePos, BOOL ExternalData, Spread* pSpread = NULL, DocCoord* pCentre = NULL) 01040 01041 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> / Mike 01042 Created: 28/7/94 01043 Inputs: PasteAtSamePos: TRUE if we should paste the objects at their existing positions. 01044 FALSE if they should be pasted in the centre of the view 01045 01046 ExternalData: This flag indicates that the data has come from the external 01047 clipboard. We transform all objects in the clipboard to the 01048 centre of the view whenever this flag is set. This is so that 01049 subsequent PasteAtSamePos operations position the objects somewhere 01050 sensible. 01051 01052 pSpread: ptr to insertion spread. If NULL, spread in the centre 01053 of the current view is used AND pCentre is ignored. 01054 01055 pCentre: ptr to coord defining the centre of the paste. 01056 if PasteAtSamePos is TRUE, this coord is not used (I think! - Markn) 01057 01058 Outputs: - 01059 Returns: - 01060 Purpose: Pastes the contents of the clipboard into the current document. 01061 Errors: - 01062 Scope: protected 01063 SeeAlso: - 01064 01065 Changes: 1/6/95 - Fixed the error recovery mechanisms so they don't leave the undo 01066 system in a state. 01067 Made this function more accessible to derived classes. 01068 01069 ********************************************************************************************/ 01070 01071 BOOL OpPaste::DoPasteStandard(BOOL PasteAtSamePos, BOOL ExternalData, Spread* pSpread, DocCoord* pCentre) 01072 { 01073 // Assume failure until told otherwise 01074 BOOL HasFailed=TRUE; 01075 01076 Spread* InsertionSpread = pSpread; 01077 DocCoord PastePosition; 01078 01079 if (InsertionSpread == NULL) 01080 { 01081 if (!FindCentreInsertionPosition(&InsertionSpread, &PastePosition)) 01082 return FALSE; 01083 } 01084 else 01085 { 01086 ERROR3IF(pCentre == NULL,"You've supplied a spread, but no centre coord. Are you mad?"); 01087 01088 if (pCentre != NULL) 01089 PastePosition = *pCentre; 01090 } 01091 01092 ERROR3IF(InsertionSpread == NULL,"Spread is NULL"); 01093 01094 // ----------------------------------- 01095 // This is an horrendous bodge! 01096 // What's the point passing in a centre of the routine insists on calculating it for itself? 01097 // Ho hum... 01098 // If the data has come from the outside world it's original position is unknown 01099 // So now that the user has committed themselves to pasting at a certain position 01100 // record that as being the data's "original position". 01101 if (ExternalData) 01102 { 01103 InternalClipboard::SetOriginalCentrePoint(PastePosition); 01104 } 01105 01106 // If trying to paste data at its original position 01107 // Then retrieve that info from the clipboard and use it as the paste position 01108 if (PasteAtSamePos) 01109 { 01110 PastePosition = InternalClipboard::GetOriginalCentrePoint(); 01111 } 01112 // ----------------------------------- 01113 01114 if (InsertionSpread != NULL) 01115 { 01116 Node* Current; 01117 01118 InternalClipboard* IntClip = InternalClipboard::Instance(); 01119 if (IntClip==NULL) 01120 { 01121 ERROR3("Could not find internal clipboard"); 01122 goto EndOperation; 01123 } 01124 01125 BOOL FirstObjectCopied = TRUE; 01126 DocRect ObjectsBounds; 01127 DocCoord CentreOfObjectsBounds; 01128 01129 // Create the transform to be applied to all pasted objects 01130 ObjectsBounds = IntClip->GetObjectsBounds(); 01131 01132 CentreOfObjectsBounds.x = ObjectsBounds.lo.x + (ObjectsBounds.Width()/2); 01133 CentreOfObjectsBounds.y = ObjectsBounds.lo.y + (ObjectsBounds.Height()/2); 01134 01135 Trans2DMatrix ObjectTrans (PastePosition.x - CentreOfObjectsBounds.x, 01136 PastePosition.y - CentreOfObjectsBounds.y); 01137 01138 // Set the selected spread (assume it is in the Selected Doc and DocView - an 01139 // ENSURE will occur if this turns out not to be the case!) 01140 // NOTE selected spread should change due to a change in the sel range! 01141 Document::SetSelectedViewAndSpread(NULL, NULL, InsertionSpread); 01142 01143 // Get the current tool 01144 Tool* pTool = Tool::GetCurrent(); 01145 Spread* pSelSpread = Document::GetSelectedSpread(); 01146 01147 // Get the tool to remove all its blobs before we deselect the nodes. 01148 // Only do this if the current tool dosent update itself on sel changed messages 01149 if (pSelSpread!=NULL && pTool!=NULL && !pTool->AreToolBlobsRenderedOnSelection()) 01150 pTool->RenderToolBlobs(pSelSpread,NULL); 01151 01152 if (!DoStartSelOp(FALSE, TRUE)) // Try to record the selection state 01153 goto EndOperation; 01154 01155 // After the operation ends we will need to inform all DocComponents in the 01156 // destination document of the outcome. 01157 InformDocComponentsOfOperationsOutcome(GetWorkingDoc()); 01158 01159 // Inform all DocComponents in the destination doc that a copy is about to take place 01160 BOOL ok; 01161 CALL_WITH_FAIL((GetWorkingDoc()->StartComponentCopy()),this, ok) 01162 01163 if (!ok) 01164 { 01165 // Start Component copy has failed so abort operation 01166 // Note that AbortComponentCopy will have been called 01167 goto EndOperation; 01168 } 01169 01170 01171 Current = IntClip->GetInsertionLayer()->FindFirstChild(); 01172 01173 // Create a range of all clipboard objects which are about to be copied 01174 // Selected + Unselected + don't cross layers 01175 01176 // DMc - also need to set the PromoteToParent flag 01177 Range ClipRange(Current,NULL,RangeControl(TRUE,TRUE,FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)); 01178 01179 Node* FirstCopiedObj = NULL; 01180 Node* Copy; 01181 01182 // Now we try and copy all clipboard nodes 01183 while (Current != NULL) 01184 { 01185 // Make a copy of Current 01186 CALL_WITH_FAIL(Current->NodeCopy(&Copy), this, ok); 01187 01188 if (!ok) 01189 goto EndOperation; 01190 01191 // Sanity check, will vapourise in retails 01192 ENSURE(Copy->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)), 01193 "Object being pasted is not a NodeRenderableBounded"); 01194 01195 // Remember the first copy we make 01196 if (FirstCopiedObj == NULL) 01197 FirstCopiedObj = Copy; 01198 01199 NodeRenderableBounded* pBoundCopy = (NodeRenderableBounded*)Copy; 01200 01201 // Insert object 01202 if (!DoInsertNewNode(pBoundCopy, 01203 (Spread*)NULL, // Selected spread 01204 FALSE, // Dont Invalidate region 01205 FirstObjectCopied // Only clear the selection if this is the 01206 // first object being copied 01207 )) 01208 { 01209 // Tidyup 01210 Copy->CascadeDelete(); 01211 delete Copy; 01212 goto EndOperation; 01213 } 01214 01215 // Now translate the object into the correct position. 01216 // Note: This has to be done even when PasteInPlace is operational because 01217 // the clipboard may have moved the objects ready for OLE external use. 01218 pBoundCopy->Transform(ObjectTrans); 01219 01220 // Now call PostDuplicate on the copied node aand all it's children 01221 BOOL ok = TRUE; 01222 Node* pCurrent = pBoundCopy->FindFirstDepthFirst(); 01223 while (pCurrent!=NULL && ok) 01224 { 01225 ok = pCurrent->PostDuplicate(this); 01226 01227 pCurrent = pCurrent->FindNextDepthFirst(pBoundCopy); 01228 } 01229 01230 // We also need to invalidate the region of the node now that it has been transformed 01231 if (!ok || !DoInvalidateNodeRegion(pBoundCopy, TRUE, FALSE)) 01232 { 01233 // Tidyup 01234 Copy->CascadeDelete(); 01235 delete Copy; 01236 goto EndOperation; 01237 } 01238 01239 // Normalise the attributes 01240 ((NodeRenderableInk*) pBoundCopy)->NormaliseAttributes(); 01241 01242 // remove any name attr