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 attribs from the clipboard pasted object (sjk 15/8/00) 01243 Node * pAttrib = SliceHelper::FindNextNameNode(pBoundCopy,pBoundCopy); 01244 while (pAttrib) 01245 { 01246 BOOL leave = RemoveNamesAlreadyUsedInStretching(pAttrib, this); 01247 01248 Node * pDelMe = pAttrib; 01249 pAttrib = SliceHelper::FindNextNameNode(pAttrib,pBoundCopy); 01250 if (!leave) 01251 { 01252 pDelMe->UnlinkNodeFromTree(); 01253 delete pDelMe; 01254 } 01255 } 01256 01257 // Get the next object from the clipboard to copy 01258 Current = Current->FindNext(); 01259 FirstObjectCopied = FALSE; 01260 } 01261 01262 ENSURE(FirstCopiedObj != NULL, "No nodes were copied"); 01263 if (FirstCopiedObj == NULL) 01264 goto EndOperation; 01265 01266 01267 // We now need to ask all copied nodes to copy their component data to the 01268 // current document 01269 Range CopiedRange(FirstCopiedObj, Copy, RangeControl(TRUE,TRUE,FALSE)); 01270 01271 CALL_WITH_FAIL((CopiedRange.CopyComponentDataToDoc(IntClip, pOurDoc)), this, ok); 01272 if (!ok) 01273 goto EndOperation; 01274 01275 // THIS MUST BE LAST! 01276 HasFailed = FALSE; 01277 } 01278 01279 EndOperation: 01280 return (!HasFailed); 01281 } 01282 01283 01284 01285 /******************************************************************************************** 01286 01287 > static void OpPaste::RemoveNamesAlreadyUsedInStretching(Node * pAttrib) 01288 01289 Author: Simon_Knight (Xara Group Ltd) <camelotdev@xara.com> (put in a function of its own by Matt) 01290 Created: 22/01/2001 01291 Inputs: - 01292 Returns: - False if the set info should not be pasted 01293 Purpose: Remove set information from pasted attributes if that name 01294 would interfere with stretching if added... 01295 Notes: Made static so that it can also be used by the PasteAttributes operation 01296 01297 ********************************************************************************************/ 01298 01299 BOOL OpPaste::RemoveNamesAlreadyUsedInStretching(Node * pAttrib, UndoableOperation* pOp) 01300 { 01301 // try only removing names that has anything to do with extending 01302 String_256 Temp = SliceHelper::GetBarName((TemplateAttribute *)pAttrib); 01303 // is it a bar element? 01304 BOOL ok = Temp.IsEmpty(); 01305 SGNameItem* pGalleryItem = NULL; 01306 if (ok) 01307 { 01308 Temp = ((TemplateAttribute *)pAttrib)->GetParam(); 01309 pGalleryItem = SliceHelper::LookupNameGalleryItem(Temp); 01310 if (pGalleryItem) 01311 { 01312 // is it a trigger? 01313 if (pGalleryItem->m_IsATrigger == TRUE) // use == TRUE since it is often undefined 01314 ok = FALSE; 01315 else 01316 { 01317 // is it a target? 01318 NamedStretchProp* pProp = (NamedStretchProp*)pGalleryItem->GetProperty(NamedStretchProp::nIndex); 01319 if (pProp && !pProp->GetTriggers().empty()) 01320 ok = FALSE; 01321 } 01322 } 01323 else 01324 { 01325 // create a default property for a new set 01326 SliceHelper::CreatePropertiesForSet(Temp, 01327 TEXT(""), 01328 TRUE, FALSE, FALSE, 0, 01329 TEXT(""), TRUE, NULL, NULL, pOp); 01330 } 01331 } 01332 01333 return ok; 01334 } 01335 01336 // ------------------------------------------------------------------------------------------ 01337 // OpDelete methods 01338 01339 /******************************************************************************************** 01340 01341 > OpDelete::OpDelete() 01342 01343 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01344 Created: 29/9/93 01345 Inputs: - 01346 Outputs: - 01347 Returns: - 01348 Purpose: OpDelete constructor 01349 Errors: - 01350 SeeAlso: - 01351 01352 ********************************************************************************************/ 01353 01354 01355 OpDelete::OpDelete(): SelOperation() 01356 { 01357 } 01358 01359 /******************************************************************************************** 01360 01361 > BOOL OpDelete::Init() 01362 01363 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01364 Created: 28/9/93 01365 Inputs: - 01366 Outputs: - 01367 Returns: TRUE if the operation could be successfully initialised 01368 FALSE if no more memory could be allocated 01369 01370 Purpose: OpDelete initialiser method 01371 Errors: ERROR will be called if there was insufficient memory to allocate the 01372 operation. 01373 SeeAlso: - 01374 01375 ********************************************************************************************/ 01376 01377 BOOL OpDelete::Init() 01378 { 01379 return (RegisterOpDescriptor(0, 01380 _R(IDS_DELETEOP), 01381 CC_RUNTIME_CLASS(OpDelete), 01382 OPTOKEN_DELETE, 01383 OpDelete::GetState, 01384 0, // help ID 01385 _R(IDBBL_DELETE), 01386 0, 01387 0, 01388 SYSTEMBAR_ILLEGAL, // For now ! 01389 TRUE, // Receive messages 01390 FALSE, 01391 FALSE, 01392 0, 01393 0 // (GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION) 01394 )); 01395 01396 return true; 01397 } 01398 01399 /******************************************************************************************** 01400 01401 > OpState OpDelete::GetState(String_256*, OpDescriptor*) 01402 01403 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01404 Created: 28/9/93 01405 Inputs: - 01406 Outputs: - 01407 Returns: The state of the OpDelete 01408 Purpose: For finding OpDelete's state. 01409 Errors: - 01410 SeeAlso: - 01411 01412 ********************************************************************************************/ 01413 01414 OpState OpDelete::GetState(String_256* UIDescription, OpDescriptor*) 01415 { 01416 OpState OpSt; 01417 01418 SelRange *pSelRange = GetApplication()->FindSelection(); 01419 01420 // Set up the ObjChangeParam so we can ask the selected nodes if they mind being deleted 01421 ObjChangeFlags cFlags(TRUE); 01422 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL); 01423 01424 // Will one or more selected nodes allow this op? 01425 if (!pSelRange->AllowOp(&ObjChange,FALSE)) 01426 { 01427 // No nodes can be deleted 01428 OpSt.Greyed = TRUE; 01429 01430 // Load reason why operation is disabled 01431 UINT32 IDS = ObjChange.GetReasonForDenial(); 01432 if (IDS == 0) IDS = _R(IDS_NO_OBJECTS_SELECTED); // if 0 (i.e. not been set), then assume there's no selection 01433 *UIDescription = String_256(IDS); // Resolve the string ID 01434 } 01435 else 01436 SetCutOpText(UIDescription, _R(IDS_CLIPBOARD_PREDELETE)); 01437 01438 return(OpSt); 01439 } 01440 01441 /******************************************************************************************** 01442 01443 > void OpDelete::Do(OpDescriptor*) 01444 01445 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01446 Created: 16/8/93 01447 Inputs: OpDescriptor (unused) 01448 Outputs: - 01449 Returns: - 01450 Purpose: Performs the Delete operation. 01451 01452 Errors: - 01453 SeeAlso: - 01454 01455 ********************************************************************************************/ 01456 01457 void OpDelete::Do(OpDescriptor*) 01458 { 01459 ObjectSet CompoundSet; 01460 01461 // DMc 01462 // first, try deleting selected fill ramp blobs 01463 // check the fill interest first 01464 BlobManager * pBlobMgr = GetApplication()->GetBlobManager(); 01465 01466 if (pBlobMgr) 01467 { 01468 if (pBlobMgr->GetCurrentInterest().Fill) 01469 { 01470 if (DeleteFillRampBlobs()) 01471 { 01472 // we've deleted fill ramp blobs, then drop out - job done 01473 return; 01474 } 01475 } 01476 } 01477 01478 // Obtain the current selections 01479 Range Sel(*(GetApplication()->FindSelection())); 01480 RangeControl rg = Sel.GetRangeControlFlags(); 01481 rg.PromoteToParent = TRUE; 01482 Sel.Range::SetRangeControl(rg); 01483 01484 // Prepare an ObjChangeParam so we can mark which nodes will allow this op to happen to them 01485 ObjChangeFlags cFlags(TRUE); 01486 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); 01487 01488 if (!DoStartSelOp(TRUE,FALSE)) // Try to record the selection state 01489 goto EndOperation; 01490 01491 // Mark nodes that will allow this to happen, and error if no nodes will let it happen 01492 if (!Sel.AllowOp(&ObjChange)) 01493 { 01494 ERROR3("AllowOp() returned FALSE, i.e. op should have been greyed out"); 01495 goto EndOperation; 01496 } 01497 01498 01499 // We need to invalidate the region 01500 if (!DoInvalidateNodesRegions(Sel, TRUE)) 01501 goto EndOperation; 01502 01503 // Prior to hiding anything lets get ourselves a set of compound nodes which 01504 // we will need to factor out attributes on. We cannot use the SelRange because 01505 // this will be empty after the deletion has taken place. 01506 01507 if (!(Sel.GetCompoundObjectSet(&CompoundSet, TRUE /* Exclude TextObjects*/))) 01508 { 01509 goto EndOperation; 01510 } 01511 01512 // Because of complex deletion we must also add the TextStory objects 01513 // of selected chars. This code will hopefuly be removed in future. It is 01514 // inefficient cos we will need to factor out more than once for each line !. 01515 01516 //if (!(Sel.AddTextStoryCompoundsForDel(&CompoundSet))) 01517 //{ 01518 // goto EndOperation; 01519 //} 01520 01521 // We know must localise the attributes on every item marked for deletion 01522 if (!DoLocaliseForAttrChange(&Sel, (AttrTypeSet*)NULL, TRUE /* Exclude TextStory objects */)) 01523 { 01524 goto EndOperation; 01525 } 01526 01527 // Go through one pass, asking nodes to hide themselves 01528 if (!DoHideComplexRange(Sel)) 01529 goto EndOperation; 01530 01531 { 01532 SelRange* pRange = GetApplication()->FindSelection(); 01533 01534 rg = Sel.GetRangeControlFlags(); 01535 rg.PromoteToParent = TRUE; 01536 01537 if (pRange) 01538 { 01539 // freshen the selection range by counting the currently selected objects 01540 pRange->Update(FALSE,NULL); 01541 pRange->Range::SetRangeControl(rg); 01542 01543 /*INT32 num =*/ pRange->Count(); 01544 01545 // Obtain the current selection (should be up-to-date now!) 01546 Range NewSel(*pRange); 01547 01548 NewSel.Range::SetRangeControl(rg); 01549 01550 // Now go through a second pass snip the remainder out of the tree 01551 if (!DoHideNodes(NewSel,TRUE)) // Hide the nodes (IncludeSubtreeSize) 01552 goto EndOperation; 01553 } 01554 } 01555 01556 // Finally try to factor out the attributes on all compounds. Note that if a particular compound 01557 // is hidden then it will be it's parent that has its attributes factored out ! 01558 if (!DoFactorOutAfterAttrChange(&CompoundSet,(AttrTypeSet*)NULL)) 01559 { 01560 goto EndOperation; 01561 } 01562 01563 01564 EndOperation: 01565 // Delete all items in the CompoundSet 01566 CompoundSet.DeleteAll(); 01567 01568 // Update all the changed nodes, i.e tell all the parents of the children that have been effected 01569 ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this); 01570 if (!UpdateChangedNodes(&ObjChange)) 01571 FailAndExecute(); 01572 01573 // Invalidate sel range as it may have been cached in the middle of the op while the tree is 01574 // in an invalid state (i.e. some selected nodes may have had the PERMISSION_DENIED state set during caching). 01575 { 01576 SelRange* pRange = GetApplication()->FindSelection(); 01577 if (pRange) 01578 pRange->Update(FALSE,NULL); 01579 01580 rg.PromoteToParent = FALSE; 01581 pRange->Range::SetRangeControl(rg); 01582 } 01583 01584 End(); 01585 } 01586 01587 /******************************************************************************************** 01588 01589 > BOOL OpDelete::DeleteFillRampBlobs() 01590 01591 Author: David_McClarnon (Xara Group Ltd) <camelotdev@xara.com> Mc 01592 Created: 24/10/99 01593 Inputs: 01594 Outputs: - 01595 Returns: - 01596 Purpose: Deletes the selected fill ramp blobs. If any exist, we shouldn't delete 01597 any objects as this could be confusing. Only delete objects i.e. return FALSE 01598 if (a) the blob interest is for fill blobs and (b) if no fill ramp blobs 01599 are selected 01600 01601 Errors: - 01602 SeeAlso: - 01603 01604 ********************************************************************************************/ 01605 BOOL OpDelete::DeleteFillRampBlobs() 01606 { 01607 // run through the selection seeing if any fill ramp blobs are selected 01608 Range * pSel = GetApplication()->FindSelection(); 01609 01610 if (!pSel) 01611 return FALSE; 01612 01613 Node * pNode = pSel->FindFirst(); 01614 AttrFillGeometry * pFill = NULL; 01615 // AttrFillGeometry * pNewFill = NULL; 01616 01617 Document * pDoc = Document::GetCurrent(); 01618 01619 BOOL bHasBegun = FALSE; 01620 01621 BOOL bFoundBlobs = FALSE; 01622 // NodeHidden * pHidden = NULL; 01623 01624 while (pNode) 01625 { 01626 if (pNode->IsAnObject()) 01627 { 01628 // find out the fill attribute associated with this node 01629 if (((NodeRenderableInk *)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry), 01630 (NodeAttribute **)(&pFill))) 01631 { 01632 // we've found a fill 01633 if (pFill) 01634 { 01635 // now, does this fill have a colour ramp ? 01636 if (pFill->GetColourRamp()) 01637 { 01638 // if it does have a colour ramp, see if any of the ramp's nodes are 01639 // selected 01640 FillRamp * pRamp = pFill->GetColourRamp(); 01641 01642 if (pRamp->CountSelBlobs() != 0) 01643 { 01644 // ok, we have more than one so remove the selected items 01645 bFoundBlobs = TRUE; 01646 01647 if (!bHasBegun) 01648 { 01649 if (!DoStartSelOp(TRUE, TRUE)) 01650 { 01651 goto ErrorEnd; 01652 } 01653 01654 bHasBegun = TRUE; 01655 } 01656 01657 // invalidate the node region 01658 ((NodeRenderableInk *)pNode)->ReleaseCached(); 01659 if (!DoInvalidateRegion(pNode->FindParentSpread(), 01660 ((NodeRenderableInk *)pNode)->GetBoundingRect())) 01661 // if (!DoInvalidateNodeRegion((NodeRenderableBounded*)pItem->pNode)) 01662 goto ErrorEnd; 01663 01664 RestoreFillRampAction * pAction = NULL; 01665 01666 if (RestoreFillRampAction::Init(this, this->GetUndoActionList(), pFill, 01667 &pAction) != AC_OK) 01668 { 01669 goto ErrorEnd; 01670 } 01671 01672 pRamp = pFill->GetColourRamp(); 01673 01674 // now, remove all selected nodes from the colour ramp 01675 RampItem * pThisItem = (RampItem *)pRamp->GetHead(); 01676 RampItem * pNextItem = NULL; 01677 01678 while (pThisItem) 01679 { 01680 if (pThisItem->IsSelected()) 01681 { 01682 // remove it ! 01683 pNextItem = (RampItem *)pRamp->GetNext(pThisItem); 01684 delete pRamp->RemoveItem(pThisItem); 01685 pThisItem = pNextItem; 01686 01687 if (pRamp->IsEmpty ()) 01688 { 01689 //delete (pRamp); 01690 01691 // CGS 01692 // we cannot do the following line - since deleting the fillramp leads 01693 // us into trouble with the undo/redo system! 01694 ((GradFillAttribute*) (pFill->GetAttributeValue ()))->DeleteColourRamp (); 01695 01696 // CGS 01697 // we also need to re-toggle the status of the profile gadget .... 01698 01699 //Tool* CurrentTool = Tool::GetCurrent(); 01700 //CurrentTool-> // possibly test this as well .... 01701 01702 PORTNOTE("other", "Removed use of DialogBarOp in OpDelete::DeleteFillRampBlobs") 01703 #ifndef EXCLUDE_FROM_XARALX 01704 String_32 str = String_32(_R(IDS_FILLTOOL_FILLINFOBARNAME)); 01705 GradInfoBarOp* pDialogBarOp = (GradInfoBarOp*) DialogBarOp::FindDialogBarOp(str); 01706 01707 if (pDialogBarOp) 01708 { 01709 if (pDialogBarOp->IsVisible ()) 01710 { 01711 pDialogBarOp->EnableControls (); 01712 } 01713 } 01714 #endif 01715 01716 pThisItem = NULL; 01717 } 01718 } 01719 else 01720 { 01721 pThisItem = (RampItem *)pRamp->GetNext(pThisItem); 01722 } 01723 } 01724 01725 ((NodeRenderableInk *)pNode)->ReleaseCached(); 01726 if (!DoInvalidateRegion(pNode->FindParentSpread(), 01727 ((NodeRenderableInk *)pNode)->GetBoundingRect())) 01728 // if (!DoInvalidateNodeRegion((NodeRenderableBounded*)pItem->pNode)) 01729 goto ErrorEnd; 01730 01731 if (pDoc) 01732 pDoc->ForceRedraw(pNode->FindParentSpread(), 01733 ((NodeRenderableInk *)pNode)->GetBoundingRect(), FALSE, pNode); 01734 01735 } 01736 } 01737 } 01738 } 01739 } 01740 01741 pNode = pSel->FindNext(pNode); 01742 } 01743 01744 if (bHasBegun) 01745 End(); 01746 01747 return bFoundBlobs; 01748 01749 ErrorEnd: 01750 if (bHasBegun) 01751 { 01752 FailAndExecute(); 01753 End(); 01754 } 01755 return TRUE; 01756 } 01757 01758 // ------------------------------------------------------------------------------------------ 01759 // CarbonCopyOp methods 01760 01761 /******************************************************************************************** 01762 01763 > CarbonCopyOp::CarbonCopyOp() 01764 01765 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 01766 Created: 18/11/94 01767 Inputs: - 01768 Outputs: - 01769 Returns: - 01770 Purpose: CarbonCopyOp constructor - does nothing itself, just calls the parent constructor 01771 Errors: - 01772 SeeAlso: OpDuplicate, OpClone 01773 01774 ********************************************************************************************/ 01775 01776 CarbonCopyOp::CarbonCopyOp(): SelOperation() 01777 { 01778 } 01779 01780 01781 /******************************************************************************************** 01782 01783 > OpState CarbonCopyOp::GetState(String_256*, OpDescriptor*) 01784 01785 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 01786 Created: 18/11/94 01787 Inputs: - 01788 Outputs: - 01789 Returns: The state of the CarbonCopyOp or any operations derived from it that dosen't 01790 overide this function. 01791 Purpose: For finding the operations state. 01792 Errors: - 01793 SeeAlso: - 01794 01795 ********************************************************************************************/ 01796 01797 OpState CarbonCopyOp::GetState(String_256* UIDescription, OpDescriptor *Bob) 01798 { 01799 OpState OpSt; 01800 01801 return(OpSt); 01802 } 01803 01804 01805 /******************************************************************************************** 01806 01807 > BOOL CarbonCopyOp::DoProcessing(BOOL Translate, INT32 XOffset = 0, INT32 YOffset = 0) 01808 01809 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> (from Simon M code). Altered by Graham 25/6/96. 01810 Created: 18/11/94 01811 Inputs: A Trans2DMatrix (Graham 25/6/96) giving details of how the copy 01812 should be transformed/translated. 01813 01814 SelectCopy: TRUE if the original is to be deselected and the copy selected 01815 (as in Clone and Duplicate) 01816 01817 FALSE if the original is to remain selected and the copy 01818 unselected (as in dropping copies with CopyAndTransform) 01819 Outputs: - 01820 Returns: TRUE if the operation completed OK, FALSE otherwise 01821 Purpose: Performs the common processing of the Clone, Duplicate and CopyAndTransform 01822 operations. Clones are not offset from the original, whilst Duplicated objects 01823 are offset by the amount in the parameter matrix. CopyAndTransform creates 01824 a copy transformed by the specified matrix. 01825 01826 Errors: ERROR2IF's to check there is no selection, Selection is not an ink object, or 01827 Selection is not bounded. 01828 SeeAlso: - 01829 01830 ********************************************************************************************/ 01831 01832 BOOL CarbonCopyOp::DoProcessing(Trans2DMatrix Transformer, BOOL SelectCopy = TRUE) 01833 { 01834 //Help the user with their selection... 01835 Range* Selection = GetApplication()->FindSelection(); 01836 RangeControl TransFlags = Selection->GetRangeControlFlags(); 01837 TransFlags.IgnoreNoneRenderable=TRUE; 01838 TransFlags.IgnoreInvisibleLayers = TRUE; 01839 Selection->SetRangeControl(TransFlags); 01840 SliceHelper::ModifySelectionToContainWholeButtonElements(); 01841 01842 // Obtain the current selections 01843 NodeListItem * pItem = NULL; 01844 01845 // DMc - build a list of the original selection, so we can reselect them later 01846 Range Sel(*(GetApplication()->FindSelection())); 01847 01848 List * pSelList = Sel.MakeListOfNodes(FALSE); 01849 01850 RangeControl rg = Sel.GetRangeControlFlags(); 01851 rg.PromoteToParent = TRUE; 01852 Sel.Range::SetRangeControl(rg); 01853 01854 ObjChangeFlags cFlags; 01855 ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); 01856 01857 if (!Sel.AllowOp(&ObjChange)) 01858 { 01859 SliceHelper::RestoreSelection(); 01860 return FALSE; 01861 } 01862 01863 01864 // Find the first node which is selected 01865 Node* FirstSelectedNode = Sel.FindFirst(); 01866 01867 Node* Current; 01868 01869 // RangeControl LyrCntrl = { TRUE, FALSE, FALSE }; // Selected + don't cross layers 01870 Range LayerRange; 01871 01872 Node* FirstNodeToCopyOnLyr; 01873 Node* LastNodeToCopyOnLyr; 01874 Node* Tail; 01875 01876 ERROR2IF(FirstSelectedNode == NULL, FALSE, "Called CarbonCopy operation with no nodes selected"); 01877 01878 // init the vars needed for the mesh layers bit later on 01879 Node * pFirstNodeOfImportedLayer[5]; 01880 String_256 StateLayerNames[5]; 01881 Node * pLayer = NULL; 01882 01883 StateLayerNames[0].Load(_R(IDS_ROLLOVER_DEFAULT)); // = "Default"; 01884 StateLayerNames[1].Load(_R(IDS_ROLLOVER_MOUSE)); // = "Mouse"; 01885 StateLayerNames[2].Load(_R(IDS_ROLLOVER_CLICKED)); // = "Clicked"; 01886 StateLayerNames[3].Load(_R(IDS_ROLLOVER_SELECTED)); // = "Selected"; 01887 StateLayerNames[4].Load(_R(IDS_BACK_BAR)); // = "BackBar"; 01888 01889 INT32 i; 01890 for (i = 0; i < 5; i++) 01891 pFirstNodeOfImportedLayer[i] = NULL; 01892 01893 //Graham 30/9/96: Tell the current drag operation to clear its list 01894 //of nodes to select at the end of the drag. 01895 TransOperation* pTransOperation=(TransOperation*) GetCurrentDragOp(); 01896 01897 if(pTransOperation) 01898 pTransOperation->ClearNodesToSelect(); 01899 01900 if (!DoStartSelOp(FALSE,TRUE)) // Try to record the selection state 01901 { 01902 goto EndOperation; 01903 } 01904 01905 // The following is error checking to make sure the copy doesn't go off the edge of the 01906 // pasteboard. 01907 01908 // Is the transform matrix going to move or transform the copy? 01909 if (Transformer.GetMatrix().Type!=TRANS_IDENTITY) 01910 { 01911 //Yes. So check the copy will not spill off the pasteboard. 01912 01913 // Find the bounding rectangle of the selection 01914 DocRect CurrentBounds = Sel.GetBoundingRect(); 01915 01916 //And the bounding rectangle of the selection once transformed 01917 DocRect NewBounds=CurrentBounds; 01918 Transformer.GetMatrix().TransformBounds(&NewBounds); 01919 01920 //And the current spread if there is one 01921 Spread* pSpread = pOurDoc->GetSelectedSpread(); 01922 01923 //Is there a spread currently selected? 01924 if (pSpread!=NULL) 01925 { 01926 //Yes. Will its pasteboard expand to include the bounds of the transformed selection? 01927 //(Note: ExpandPasteboardToInclude doesn't actually expand the pasteboard 01928 //currently. It just checks whether NewBounds fits within the pasteboard area.) 01929 if(!pSpread->ExpandPasteboardToInclude(NewBounds)) 01930 { 01931 //No, the transformed copy will not fit in the pasteboard. 01932 //Is the transform a translation? 01933 if(Transformer.GetMatrix().IsTranslation()) 01934 { 01935 //Yes. So restrict the translation so it fits within the 01936 //pasteboard. 01937 01938 //First get the translation in "INT32" form 01939 INT32 XOffset; 01940 INT32 YOffset; 01941 01942 Transformer.GetMatrix().GetTranslation(XOffset, YOffset); 01943 01944 DocRect PasteRect = pSpread->GetPasteboardRect(); 01945 pSpread->DocCoordToSpreadCoord(&PasteRect); 01946 01947 // Find out how far we can move 01948 INT32 MaxX = 0; 01949 INT32 MaxY = 0; 01950 01951 // First the X direction 01952 if (XOffset<0) 01953 { 01954 // The offset is negative, so work out the maximum negative offset allowed 01955 MaxX = PasteRect.lo.x - CurrentBounds.lo.x; 01956 if (XOffset<MaxX) XOffset = MaxX; 01957 } 01958 else 01959 { 01960 MaxX = PasteRect.hi.x - CurrentBounds.hi.x; 01961 if (XOffset>MaxX) 01962 XOffset = MaxX; 01963 } 01964 01965 // and then the Y direction 01966 if (YOffset<0) 01967 { 01968 // The offset is negative, so work out the maximum negative offset allowed 01969 MaxY = PasteRect.lo.y - CurrentBounds.lo.y; 01970 if (YOffset<MaxY) YOffset = MaxY; 01971 } 01972 else 01973 { 01974 MaxY = PasteRect.hi.y - CurrentBounds.hi.y; 01975 if (YOffset>MaxY) 01976 YOffset = MaxY; 01977 } 01978 01979 //And put the translations back into the Transformer matrix 01980 Matrix TranslateMatrix=Transformer.GetMatrix(); 01981 TranslateMatrix.SetTranslation(XOffset, YOffset); 01982 01983 Transformer=Trans2DMatrix(TranslateMatrix); 01984 } 01985 else 01986 { 01987 //No, this is not a translation. 01988 //End the operation without copying the selection. 01989 //This should not be confusing to the user, because the user can see 01990 //he has dragged the selection over the edge of the pasteboard. 01991 //So he should not be surprised that the operation does nothing. 01992 01993 goto EndOperation; 01994 } 01995 } 01996 } 01997 else 01998 { 01999 //No, there is no spread selected. 02000 //We do not want to risk creating a copy outside the current bounds 02001 //of the object. So we set the Transformer matrix to the identity, which simply 02002 //clones the object 02003 Transformer=Trans2DMatrix(); 02004 } 02005 } 02006 02007 // Now start the copying procedure, which we do one layer at a time 02008 02009 //The first node to copy on the first layer is the first selected node 02010 FirstNodeToCopyOnLyr = FirstSelectedNode; 02011 02012 // When there are no more objects to copy, FirstNodeToCopyOnLyr will be NULL 02013 while (FirstNodeToCopyOnLyr != NULL) 02014 { 02015 02016 // Tail is the position to insert the new Duplicate nodes 02017 Tail = FirstNodeToCopyOnLyr->FindParent()->FindLastChild(TRUE); // Excludes InsertionNode 02018 02019 // Create a range of selected nodes on this layer 02020 // LayerRange = Range(FirstNodeToCopyOnLyr, NULL, RangeControl(TRUE,FALSE,FALSE,FALSE, 02021 // FALSE,FALSE,FALSE,TRUE) ); 02022 Range temp(FirstNodeToCopyOnLyr, NULL, RangeControl(TRUE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,TRUE)); 02023 LayerRange = temp; 02024 02025 //When we enter the loop, Current should signify the last copied node on this layer. 02026 //Set to null to signify we need to copy the first node on this layer. 02027 Current=NULL; 02028 LastNodeToCopyOnLyr = LayerRange.FindLast(); 02029 02030 Node * pFirstNewNodeInLayer = NULL; 02031 02032 // Loop for each node on this layer 02033 do 02034 { 02035 //Get the next node in the layer to copy. If Current is NULL, this 02036 //is the first node in the layer to be copied. 02037 02038 if(Current==NULL) 02039 Current=FirstNodeToCopyOnLyr; 02040 else 02041 Current = LayerRange.FindNext(Current); 02042 02043 // Make a copy of the current node 02044 Node* TheCopy; 02045 BOOL CopiedOK; 02046 02047 CALL_WITH_FAIL(Current->NodeCopy(&TheCopy), this, CopiedOK); 02048 if (!CopiedOK) 02049 { 02050 goto EndOperation; 02051 } 02052 02053 // store this to tidy up later in meshing layers 02054 if (!pFirstNewNodeInLayer) 02055 pFirstNewNodeInLayer = TheCopy; 02056 02057 // make sure that it is bounded 02058 ERROR2IF(!TheCopy->IsBounded(), FALSE, "Object being pasted is not a NodeRenderableBounded"); 02059 NodeRenderableBounded* BoundCopy = (NodeRenderableBounded*)TheCopy; 02060 02061 // Insert the copied node at the tail position 02062 if (!DoInsertNewNode(BoundCopy, Tail, NEXT, 02063 FALSE, // Don't Invalidate region 02064 FALSE)) // Don't Clear the selections 02065 { 02066 // Failed, so tidy up before returning 02067 TheCopy->CascadeDelete(); 02068 delete TheCopy; 02069 goto EndOperation; 02070 } 02071 02072 // DMc - to fix bug with r-hand clicks on drags 02073 BoundCopy->SetSelected(TRUE); 02074 BoundCopy->SetRender(TRUE, TRUE); 02075 02076 //If we need to Transform the copy, do so 02077 if (Transformer.GetMatrix().Type!=TRANS_IDENTITY) 02078 { 02079 BoundCopy->Transform(Transformer); 02080 } 02081 02082 ERROR2IF(!Current->IsAnObject(), FALSE, "Current should be an ink node"); 02083 02084 // Call PostDuplicate on the copied node and all it's children 02085 BOOL ok = TRUE; 02086 Node* pCurrent = BoundCopy->FindFirstDepthFirst(); 02087 while (pCurrent!=NULL && ok) 02088 { 02089 ok = pCurrent->PostDuplicate(this); 02090 pCurrent = pCurrent->FindNextDepthFirst(BoundCopy); 02091 } 02092 if (!ok) goto EndOperation; 02093 02094 02095 //Now we need to select the copy and deselect the original. 02096 02097 //This code isn't too elegant, because it's four days until deadline 02098 //and I want to get this "bug" fixed. So... 02099 02100 //Is there a transform drag going on? 02101 Operation* pDragOp=GetCurrentDragOp(); 02102 TransOperation* pTransDragOp=(TransOperation*) pDragOp; 02103 02104 if (!pTransDragOp) 02105 { 02106 //No. So we can deselect the original now. 02107 //((NodeRenderableInk*)Current)->DeSelect(TRUE); 02108 } 02109 else 02110 { 02111 //Yes, there's a transform drag going on. 02112 //Transform drags start acting strangely if you 02113 //change the selection halfway through. 02114 //So first we need to deselect the copy, to ensure 02115 //we haven't changed the selection. 02116 //((NodeRenderableInk*)BoundCopy)->DeSelect(TRUE); 02117 02118 //And tell the drag to select the copy after the 02119 //drag has finished. 02120 pTransDragOp->SelectNodeAfterDrag(BoundCopy); 02121 } 02122 02123 // We also need to invalidate the region of the node now that it has been transformed 02124 BoundCopy->ReleaseCached(TRUE, FALSE, FALSE, FALSE); // Release Parents because of change 02125 if (!DoInvalidateNodeRegion(BoundCopy, TRUE, FALSE, FALSE, FALSE)) // Don't recache everything 02126 { 02127 // Tidyup 02128 TheCopy->CascadeDelete(); 02129 delete TheCopy; 02130 goto EndOperation; 02131 } 02132 02133 // We also need to invalidate the region of the node now that it has been transformed 02134 BoundCopy->ReleaseCached(TRUE, FALSE, FALSE, FALSE); // Release Parents because of change 02135 DoInvalidateNodeRegion((NodeRenderableBounded*) Current, TRUE, FALSE, FALSE, FALSE); // Don't recache everything 02136 02137 02138 //New insertion position is after the last copied node 02139 Tail = BoundCopy; 02140 02141 } while (Current != LastNodeToCopyOnLyr); 02142 02143 02144 // is this a rollover layer? 02145 pLayer = pFirstNewNodeInLayer->FindParent(); 02146 if (pLayer->IsLayer()) 02147 for (i = 0; i < 5; i++) 02148 { 02149 if (pFirstNodeOfImportedLayer[i] == NULL 02150 && StateLayerNames[i].CompareTo(((Layer *)pLayer)->GetLayerID() ) == 0) 02151 pFirstNodeOfImportedLayer[i] = pFirstNewNodeInLayer; 02152 } 02153 02154 02155 //And get the first node on the next layer to copy 02156 FirstNodeToCopyOnLyr = Sel.FindNext(Tail); 02157 } 02158 02159 // Mesh the buttons duplicated onto the layers 02160 // if we have any of these special layers 02161 if (pFirstNodeOfImportedLayer[0] || pFirstNodeOfImportedLayer[1] || pFirstNodeOfImportedLayer[2] || pFirstNodeOfImportedLayer[3]) 02162 SliceHelper::MeshImportedLayersWithExistingButtonBars(pFirstNodeOfImportedLayer, this, FALSE); 02163 02164 // DMc - deselect the original selection 02165 if (pSelList) 02166 { 02167 pItem = (NodeListItem *)pSelList->GetHead(); 02168 02169 while (pItem) 02170 { 02171 if (pItem->pNode) 02172 { 02173 pItem->pNode->SetSelected(FALSE); 02174 } 02175 02176 pItem = (NodeListItem *)pSelList->GetNext(pItem); 02177 } 02178 02179 pSelList->DeleteAll(); 02180 delete pSelList; 02181 } 02182 02183 //Just incase we helped the user with their selection - restore it back again 02184 TRACEUSER( "Matt", _T("Changing Selection in CarbonCopyOp::DoProcessing()\n")); 02185 SliceHelper::RestoreSelection(); 02186 02187 ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this); 02188 UpdateChangedNodes(&ObjChange); 02189 02190 End(); 02191 02192 return TRUE; 02193 02194 EndOperation: 02195 02196 if (pSelList) 02197 { 02198 pSelList->DeleteAll(); 02199 delete pSelList; 02200 } 02201 02202 //Just incase we helped the user with their selection - restore it back again 02203 TRACEUSER( "Matt", _T("Restoring Selection in CarbonCopyOp::DoProcessing()\n")); 02204 SliceHelper::RestoreSelection(); 02205 02206 End(); 02207 return FALSE; 02208 } 02209 02210 02211 02212 // ------------------------------------------------------------------------------------------ 02213 // OpDuplicate methods 02214 02215 // Duplicate preference variables (They have the same name as the preference) 02216 INT32 OpDuplicate::DuplicatePlacementX = INT32(DEFAULT_DUPLICATE_PLACEMENT_X); 02217 INT32 OpDuplicate::DuplicatePlacementY = INT32(DEFAULT_DUPLICATE_PLACEMENT_Y); 02218 02219 /******************************************************************************************** 02220 02221 > OpDuplicate::OpDuplicate() 02222 02223 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02224 Created: 29/9/93 02225 Inputs: - 02226 Outputs: - 02227 Returns: - 02228 Purpose: OpDuplicate constructor 02229 Errors: - 02230 SeeAlso: CarbonCopyOp 02231 02232 ********************************************************************************************/ 02233 02234 02235 OpDuplicate::OpDuplicate(): CarbonCopyOp() 02236 { 02237 } 02238 02239 /******************************************************************************************** 02240 02241 > BOOL OpDuplicate::Init() 02242 02243 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02244 Created: 28/9/93 02245 Inputs: - 02246 Outputs: - 02247 Returns: TRUE if the operation could be successfully initialised 02248 FALSE if no more memory could be allocated 02249 02250 Purpose: OpDuplicate initialiser method 02251 Errors: ERROR will be called if there was insufficient memory to allocate the 02252 operation. 02253 SeeAlso: - 02254 02255 ********************************************************************************************/ 02256 02257 BOOL OpDuplicate::Init() 02258 { 02259 BOOL ok = Camelot.DeclareSection(TEXT("Duplicate"), 10) && 02260 Camelot.DeclarePref(TEXT("Duplicate"), TEXT("DuplicatePlacementX"), 02261 &OpDuplicate::DuplicatePlacementX, INT_MIN, INT_MAX) && 02262 Camelot.DeclarePref(TEXT("Duplicate"), TEXT("DuplicatePlacementY"), 02263 &OpDuplicate::DuplicatePlacementY,INT_MIN, INT_MAX) && 02264 02265 (RegisterOpDescriptor(0, 02266 _R(IDS_DUPLICATEOP), 02267 CC_RUNTIME_CLASS(OpDuplicate), 02268 OPTOKEN_DUPLICATE, 02269 OpDuplicate::GetState, 02270 0, // help ID 02271 _R(IDBBL_DUPLICATE), 02272 0, // bitmap ID 02273 0, 02274 SYSTEMBAR_ILLEGAL, // For now ! 02275 TRUE, // Receive messages 02276 FALSE, 02277 FALSE, 02278 0, 02279 (GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION) 02280 )); 02281 return ok; 02282 return true; 02283 } 02284 02285 02286 /******************************************************************************************** 02287 02288 > OpState OpDuplicate::GetState(String_256*, OpDescriptor*) 02289 02290 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 02291 Created: 22/5/95 02292 Inputs: - 02293 Outputs: - 02294 Returns: The state of the OpDuplicate 02295 Purpose: For finding the operations state. 02296 Errors: - 02297 SeeAlso: - 02298 02299 ********************************************************************************************/ 02300 02301 OpState OpDuplicate::GetState(String_256* UIDescription, OpDescriptor *Bob) 02302 { 02303 OpState OpSt; 02304 02305 SetCutOpText(UIDescription, _R(IDS_CLIPBOARD_PREDUPLICATE)); 02306 02307 return(OpSt); 02308 } 02309 02310 02311 02312 /******************************************************************************************** 02313 02314 > void OpDuplicate::Do(OpDescriptor*) 02315 02316 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02317 Created: 16/8/93 02318 Inputs: OpDescriptor (unused) 02319 Outputs: - 02320 Returns: - 02321 Purpose: Performs the Duplicate operation, duplicates are placed as Last children of 02322 their layer. 02323 Errors: - 02324 SeeAlso: CarbonCopyOp::DoProcessing 02325 02326 ********************************************************************************************/ 02327 02328 void OpDuplicate::Do(OpDescriptor*) 02329 { 02330 //Graham 25/6/96: Now you pass a matrix instead of individual parameters. 02331 02332 INT32 dx = DuplicatePlacementX; 02333 INT32 dy = DuplicatePlacementY; 02334 Document* pDoc = Document::GetCurrent(); 02335 if (pDoc) 02336 { 02337 DocCoord offset = pDoc->GetDuplicationOffset(); 02338 dx = offset.x; 02339 dy = offset.y; 02340 } 02341 02342 Trans2DMatrix MatrixToPass(dx, dy); 02343 02344 DoProcessing(MatrixToPass); 02345 } 02346 02347 02348 02349 // ------------------------------------------------------------------------------------------ 02350 // OpClone methods 02351 02352 /******************************************************************************************** 02353 02354 > OpClone::OpClone() 02355 02356 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 02357 Created: 18/11/94 02358 Inputs: - 02359 Outputs: - 02360 Returns: - 02361 Purpose: OpClone constructor 02362 Errors: - 02363 SeeAlso: CarbonCopyOp 02364 02365 ********************************************************************************************/ 02366 02367 OpClone::OpClone(): CarbonCopyOp() 02368 { 02369 } 02370 02371 02372 02373 /******************************************************************************************** 02374 02375 > BOOL OpClone::Init() 02376 02377 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 02378 Created: 18/11/94 02379 Inputs: - 02380 Outputs: - 02381 Returns: TRUE if the operation could be successfully initialised 02382 FALSE if no more memory could be allocated 02383 Purpose: OpClone initialiser method 02384 Errors: ERROR will be called if there was insufficient memory to allocate the 02385 operation. 02386 SeeAlso: - 02387 02388 ********************************************************************************************/ 02389 02390 BOOL OpClone::Init() 02391 { 02392 return (RegisterOpDescriptor(0, 02393 _R(IDS_CLONEOP), 02394 CC_RUNTIME_CLASS(OpClone), 02395 OPTOKEN_CLONE, 02396 OpClone::GetState, 02397 0, /* help ID */ 02398 _R(IDBBL_CLONE), 02399 _R(IDD_BARCONTROLSTORE), // resource ID 02400 _R(IDC_CLONE), 02401 SYSTEMBAR_EDIT, // Bar ID 02402 TRUE, // Receive messages 02403 FALSE, 02404 FALSE, 02405 0, 02406 (GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION) )); 02407 } 02408 02409 02410 02411 /******************************************************************************************** 02412 02413 > OpState OpClone::GetState(String_256*, OpDescriptor*) 02414 02415 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 02416 Created: 22/5/95 02417 Inputs: - 02418 Outputs: - 02419 Returns: The state of the OpClone 02420 Purpose: For finding the operations state. 02421 Errors: - 02422 SeeAlso: - 02423 02424 ********************************************************************************************/ 02425 02426 OpState OpClone::GetState(String_256* UIDescription, OpDescriptor *Bob) 02427 { 02428 OpState OpSt; 02429 02430 SetCutOpText(UIDescription, _R(IDS_CLIPBOARD_PRECLONE)); 02431 02432 return(OpSt); 02433 } 02434 02435 02436 /******************************************************************************************** 02437 02438 > void OpClone::Do(OpDescriptor*) 02439 02440 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 02441 Created: 18/11/94 02442 Inputs: OpDescriptor (unused) 02443 Outputs: - 02444 Returns: - 02445 Purpose: Performs the Clone operation, duplicates are placed as Last children of 02446 their layer. 02447 Errors: - 02448 SeeAlso: CarbonCopyOp::DoProcessing 02449 02450 ********************************************************************************************/ 02451 02452 void OpClone::Do(OpDescriptor*) 02453 { 02454 //Graham 25/6/96: Now you pass a matrix to transform by instead of parameters 02455 //to translate by. We don't want to transform our copy at all - so create 02456 //an Identity matrix to pass 02457 02458 Trans2DMatrix MatrixToPass; 02459 02460 DoProcessing(MatrixToPass); 02461 } 02462 02463 // ------------------------------------------------------------------------------------------ 02464 // OpCopyAndTransform methods. All heavily copied from Peter's code above. 02465 02466 /******************************************************************************************** 02467 02468 > OpCopyAndTransform::OpCopyAndTransform() 02469 02470 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02471 Created: 25/6/96 02472 Inputs: - 02473 Outputs: - 02474 Returns: - 02475 Purpose: OpCopyAndTransform default constructor. The CopyTransform matrix 02476 will be constructed, by default, as an Identity matrix. Therefore, 02477 using this constructor essentially creates a Clone operation. 02478 Errors: - 02479 SeeAlso: CarbonCopyOp 02480 02481 ********************************************************************************************/ 02482 02483 OpCopyAndTransform::OpCopyAndTransform(): CarbonCopyOp() 02484 { 02485 } 02486 02487 /******************************************************************************************** 02488 02489 > OpCopyAndTransform::OpCopyAndTransform(Trans2DMatrix Transform, BOOL SelectCopy=FALSE) 02490 02491 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02492 Created: 25/6/96 02493 Inputs: Transform: The matrix by which to transform the copy 02494 02495 PassSelectCopy: TRUE to select the copy and deselect the original 02496 FALSE (default) to leave the original selected and 02497 the copy deselected. 02498 Outputs: - 02499 Returns: - 02500 Purpose: OpCopyAndTransform constructor. 02501 02502 This creates a CopyAndTransform operation that transforms the copy 02503 by the Transform matrix. 02504 02505 Errors: - 02506 SeeAlso: CarbonCopyOp 02507 02508 ********************************************************************************************/ 02509 02510 OpCopyAndTransform::OpCopyAndTransform(Trans2DMatrix Transform, BOOL PassSelectCopy):CarbonCopyOp() 02511 { 02512 CopyTransform=Transform; 02513 SelectCopy=PassSelectCopy; 02514 } 02515 02516 02517 /******************************************************************************************** 02518 02519 > BOOL OpCopyAndTransform::Init() 02520 02521 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02522 Created: 25/6/96 02523 Inputs: - 02524 Outputs: - 02525 Returns: TRUE if the operation could be successfully initialised 02526 FALSE if no more memory could be allocated 02527 Purpose: OpCopyAndTransform initialiser method 02528 02529 Note that CopyAndTransform never appears on the menu or toolbar at 02530 the moment, but I have created menu strings just in case anyone 02531 wants it to. There is no button icon defined for this operation, however. 02532 02533 Errors: ERROR will be called if there was insufficient memory to allocate the 02534 operation. 02535 SeeAlso: - 02536 02537 ********************************************************************************************/ 02538 02539 BOOL OpCopyAndTransform::Init() 02540 { 02541 return (RegisterOpDescriptor(0, 02542 _R(IDS_COPYANDTRANSFORMOP), 02543 CC_RUNTIME_CLASS(OpCopyAndTransform), 02544 OPTOKEN_COPYANDTRANSFORM, 02545 OpCopyAndTransform::GetState, 02546 0, /* help ID */ 02547 _R(IDBBL_COPYANDTRANSFORM), 02548 0, // resource ID 02549 0, 02550 SYSTEMBAR_ILLEGAL, // Bar ID 02551 TRUE, // Receive messages 02552 FALSE, 02553 FALSE, 02554 0, 02555 (GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION) )); 02556 } 02557 02558 02559 02560 /******************************************************************************************** 02561 02562 > OpState OpCopyAndTransform::GetState(String_256*, OpDescriptor*) 02563 02564 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02565 Created: 25/6/96 02566 Inputs: - 02567 Outputs: - 02568 Returns: The state of the OpCopyAndTransform 02569 Purpose: For finding the operations state. 02570 Errors: - 02571 SeeAlso: - 02572 02573 ********************************************************************************************/ 02574 02575 OpState OpCopyAndTransform::GetState(String_256* UIDescription, OpDescriptor *Bob) 02576 { 02577 OpState OpSt; 02578 02579 SetCutOpText(UIDescription, _R(IDS_CLIPBOARD_PRECOPYANDTRANSFORM)); 02580 02581 return(OpSt); 02582 } 02583 02584 02585 /******************************************************************************************** 02586 02587 > void OpCopyAndTransform::Do(OpDescriptor*) 02588 02589 Author: Graham_Walmsley (Xara Group Ltd) <camelotdev@xara.com> 02590 Created: 25/6/96 02591 Inputs: OpDescriptor (unused) 02592 Outputs: - 02593 Returns: - 02594 Purpose: Performs the CopyAndTransform operation with the current CopyTransform matrix 02595 and the current SelectCopy status. 02596 02597 This creates a copy of the current selection, transformed by the 02598 CopyTransform matrix. 02599 02600 It keeps the original selection selected rather than selecting the copy. 02601 02602 Errors: - 02603 SeeAlso: CarbonCopyOp::DoProcessing 02604 02605 ********************************************************************************************/ 02606 02607 void OpCopyAndTransform::Do(OpDescriptor*) 02608 { 02609 DoProcessing(CopyTransform, SelectCopy); 02610 } 02611 02612 02613 02614 // ------------------------------------------------------------------------------------------ 02615 // OpPasteAttributes methods 02616 02617 /******************************************************************************************** 02618 02619 > OpPasteAttributesAttributes::OpPasteAttributes(): SelOperation 02620 02621 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02622 Created: 24/08/95 02623 Inputs: - 02624 Outputs: - 02625 Returns: - 02626 Purpose: OpPasteAttributesAttributes constructor 02627 Errors: - 02628 SeeAlso: - 02629 02630 ********************************************************************************************/ 02631 02632 02633 OpPasteAttributes::OpPasteAttributes(): SelOperation() 02634 { 02635 } 02636 02637 /******************************************************************************************** 02638 02639 > BOOL OpPasteAttributes::Init() 02640 02641 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02642 Created: 24/08/95 02643 Inputs: - 02644 Outputs: - 02645 Returns: TRUE if the operation could be successfully initialised 02646 FALSE if no more memory could be allocated 02647 02648 Purpose: OpPasteAttributes initialiser method 02649 Errors: ERROR will be called if there was insufficient memory to allocate the 02650 operation. 02651 SeeAlso: - 02652 02653 ********************************************************************************************/ 02654 02655 BOOL OpPasteAttributes::Init() 02656 { 02657 BOOL ok = RegisterOpDescriptor(0, 02658 _R(IDS_PASTEATTRIBUTESOP), 02659 CC_RUNTIME_CLASS(OpPasteAttributes), 02660 OPTOKEN_PASTEATTRIBUTES, 02661 OpPasteAttributes::GetState, 02662 0, // help ID 02663 _R(IDBBL_PASTEATTRIBUTES), 02664 0, // bitmap ID 02665 0, 02666 SYSTEMBAR_ILLEGAL, // For now ! 02667 TRUE, // Receive messages 02668 FALSE, 02669 FALSE, 02670 0, 02671 (GREY_WHEN_NO_CURRENT_DOC | DONT_GREY_WHEN_SELECT_INSIDE) 02672 ); 02673 02674 return ok; 02675 } 02676 02677 /******************************************************************************************** 02678 02679 > OpState OpPasteAttributes::GetState(String_256*, OpDescriptor*) 02680 02681 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02682 Created: 24/08/95 02683 Inputs: - 02684 Outputs: - 02685 Returns: The state of the OpPasteAttributes 02686 Purpose: For finding OpPasteAttributes's state. 02687 Errors: - 02688 SeeAlso: - 02689 02690 ********************************************************************************************/ 02691 02692 OpState OpPasteAttributes::GetState(String_256* UIDescription, OpDescriptor*) 02693 { 02694 OpState OpSt; 02695 // Greyed if the clipboard is empty 02696 if (InternalClipboard::IsEmpty(FALSE)) // Only look at internal clipboard 02697 { 02698 OpSt.Greyed = TRUE; 02699 // Determine if the External clipboard is empty 02700 if (InternalClipboard::IsEmpty(TRUE)) 02701 { 02702 // There is no data on either the Internal or external clipboards. 02703 // tell the user that the clipboard is empty 02704 *UIDescription = String_256(_R(IDS_CLIPBOARD_EMPTY)); 02705 } 02706 else 02707 { 02708 // There is data on the external clipboard. 02709 // We can't paste attributes until the user has done a paste 02710 *UIDescription = String_256(_R(IDS_EXTERNAL_CLIP_OBJECTS)); 02711 } 02712 } 02713 else 02714 { 02715 // There is data on the internal clipboard 02716 // Are there any common attributes 02717 BOOL CommonAttrsExist = FALSE; 02718 BOOL bCommonLEsExist = FALSE; 02719 Range* pClipboardRange = InternalClipboard::GetClipboardRange(); 02720 // Finds all common attributes. Remember common attributes are cached 02721 if (pClipboardRange) 02722 { 02723 CommonAttrSet CommonAttrs; 02724 if( pClipboardRange->FindCommonAttributes(&CommonAttrs , TRUE ) ) 02725 { 02726 if (CommonAttrs.IsAnyItemCommon()) 02727 CommonAttrsExist = TRUE; 02728 } 02729 } 02730 02731 // Check for LiveEffects 02732 if (pClipboardRange) 02733 { 02734 Range* pTempRange = new Range(*pClipboardRange); 02735 ENSURE(pTempRange!=NULL, "OpPasteAttributes::Do couldn't allocate memory for Range\n"); 02736 if (pTempRange) 02737 { 02738 RangeControl rg = pTempRange->GetRangeControlFlags(); 02739 rg.PromoteToParent = FALSE; 02740 pTempRange->SetRangeControl(rg); 02741 02742 EffectsStack* pEffectsStack = EffectsStack::GetEffectsStackFromSelection(pTempRange, TRUE, TRUE); // Include locked effects in this stack 02743 bCommonLEsExist = (pEffectsStack!=NULL && pEffectsStack->LockableNonEmpty()); 02744 if (pEffectsStack) 02745 delete pEffectsStack; 02746 } 02747 delete pTempRange; 02748 } 02749 02750 if (!CommonAttrsExist && !bCommonLEsExist) 02751 { 02752 OpSt.Greyed = TRUE; 02753 // The objects on the clipboard share no common attributes. Report this to the user 02754 *UIDescription = String_256(_R(IDS_NO_COMMON_CLIP_ATTRS)); 02755 } 02756 } 02757 return(OpSt); 02758 } 02759 02760 02761 02762 /******************************************************************************************** 02763 02764 > void OpPasteAttributes::Do(OpDescriptor*) 02765 02766 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 02767 Created: 24/08/95 02768 Inputs: OpDescriptor (unused) 02769 Purpose: Performs the PasteAttributes operation. 02770 02771 ********************************************************************************************/ 02772 02773 void OpPasteAttributes::Do(OpDescriptor* WhichOp) 02774 { 02775 BOOL ok = TRUE; // Until something goes wrong 02776 InternalClipboard* pSrcDoc = InternalClipboard::Instance(); 02777 Document* pDestDoc = GetWorkingDoc(); 02778 02779 02780 // Start the op in the usual way 02781 DoStartSelOp(FALSE, FALSE, TRUE, TRUE); 02782 02783 // Obtain a set of common attribute values for all attribute types 02784 // remember these values are cached 02785 Range* pClipboardRange = InternalClipboard::GetClipboardRange(); 02786 if (!pClipboardRange) 02787 { 02788 ok = FALSE; 02789 } 02790 02791 CommonAttrSet CommonAttrs; 02792 02793 if (ok) 02794 { 02795 if (!pClipboardRange->FindCommonAttributes(&CommonAttrs, TRUE ) ) 02796 { 02797 // Our GetState fn will have greyed the op if there were no common attributes 02798 ok = FALSE; 02799 } 02800 } 02801 02802 if (ok) 02803 { 02804 ERROR3IF(CommonAttrs.IsEmpty(), "Empty CommonAttribute set in OpPasteAttributes::Do"); 02805 02806 // We will require the bounds of all objects on the clipboard to scale AttrFillGeometry 02807 // attributes. 02808 DocRect ClipBounds = pClipboardRange->GetBoundingRect(); 02809 02810 // Create a list of attributes to apply 02811 List AttribsToApply; 02812 NodeAttributePtrItem* pAttrToApplyItem; 02813 NodeAttribute* pAttr; 02814 CommonAttributeItem* pAttrItem; 02815 // For each item in the common attribute set 02816 //TRACE( _T("Attributes being applied\n\n")); 02817 02818 for (pAttrItem = (CommonAttributeItem*)(CommonAttrs.GetHead()); 02819 pAttrItem; 02820 pAttrItem = (CommonAttributeItem*)(CommonAttrs.GetNext(pAttrItem))) 02821 { 02822 pAttr = pAttrItem->pAttr; 02823 if (pAttrItem->Status == Range::ATTR_COMMON && pAttr) 02824 { 02825 // Ask the attribute if it wants to be applied. 02826 if (pAttr->CanBeAppliedToObject()) 02827 { 02828 // DEBUG 02829 //TRACE( _T("%s \n"), pAttrItem->AttrType->m_lpszClassName); 02830 // This attribute is common. Add it to the list of attributes to apply 02831 pAttrToApplyItem = new NodeAttributePtrItem; 02832 if (pAttrToApplyItem) 02833 { 02834 // We need to make a copy of the attribute because it's component data 02835 // will soon be in the document we are pasting into. 02836 NodeAttribute* pAttribClone = (NodeAttribute*)(pAttr->SimpleCopy()); 02837 if (pAttribClone) 02838 { 02839 if (!pAttribClone->IsAnObjectName() || OpPaste::RemoveNamesAlreadyUsedInStretching(pAttribClone, (UndoableOperation *)this)) 02840 { 02841 AttribsToApply.AddHead(pAttrToApplyItem); 02842 02843 pAttrToApplyItem->NodeAttribPtr = pAttribClone; 02844 02845 if (pAttribClone->IsAFillAttr()) 02846 { 02847 // Set the bounds of the attribute. This is of course the bounds 02848 // of the clipboard objects. 02849 ((AttrFillGeometry*)pAttribClone)->SetBoundingRect(ClipBounds); 02850 } 02851 02852 // I'm not sure if this is neccessary but Will does it when making an attribute 02853 // current. 02854 if (pAttribClone->IsAFractalFill()) 02855 { 02856 // The attr, should always use the default DPI for fractals. 02857 ((AttrFillGeometry*)pAttribClone)->SetFractalDPI(AttrFillGeometry::FractalDPI); 02858 } 02859 } 02860 } 02861 else 02862 ok = FALSE; 02863 } 02864 else 02865 ok = FALSE; 02866 } 02867 } 02868 } 02869 02870 if (ok) 02871 { 02872 // Copy the component data from all attributes before we try to apply them. 02873 // (this is the safest way). 02874 // Inform all DocComponents in the destination doc that a copy is about to take place 02875 BOOL ok; 02876 CALL_WITH_FAIL((pDestDoc->StartComponentCopy()),this, ok) 02877 if (ok) 02878 { 02879 // Now ask all attributes to copy accross their component data 02880 NodeAttributePtrItem* pAttrItem = (NodeAttributePtrItem*)AttribsToApply.GetHead(); 02881 while (pAttrItem && ok) 02882 { 02883 NodeAttribute* pAttr = pAttrItem->NodeAttribPtr; 02884 ERROR3IF(pAttr == NULL, "Where has our attribute gone ??"); 02885 if (!pAttr->CopyComponentData(pSrcDoc, pDestDoc)) 02886 { 02887 pDestDoc->AbortComponentCopy(); // Cancel all data which has been copied 02888 ok = FALSE; // stop what were doing 02889 } 02890 pAttrItem = (NodeAttributePtrItem*)AttribsToApply.GetNext(pAttrItem); 02891 } 02892 if (ok) 02893 ok = pDestDoc->EndComponentCopy(); 02894 } 02895 } 02896 02897 if (ok) 02898 { 02899 // This fn will invoke an operation to apply the attributes to the selection. 02900 02901 // DMc - change the range to include parents 02902 // This is inconsistent with all other apply operations 02903 // So don't do it! 02904 // SelRange * pSel = GetApplication()->FindSelection(); 02905 // RangeControl rg = pSel->GetRangeControlFlags(); 02906 // rg.PromoteToParent = TRUE; 02907 // pSel->Range::SetRangeControl(rg); 02908 02909 // Do the attribute application, adding actions to this op 02910 // AttributeManager::AttributesSelected(AttribsToApply, _R(IDS_PASTE_ATTRIBUTES)); 02911 DoAttributesSelected(AttribsToApply, _R(IDS_PASTE_ATTRIBUTES), TRUE); 02912 02913 // rg.PromoteToParent = FALSE; 02914 // pSel->Range::SetRangeControl(rg); 02915 } 02916 02917 { 02918 // We don't need the list of attrs anymore 02919 NodeAttributePtrItem* pAttrItem = (NodeAttributePtrItem*)AttribsToApply.GetHead(); 02920 while (pAttrItem) 02921 { 02922 delete (pAttrItem->NodeAttribPtr); 02923 pAttrItem->NodeAttribPtr = NULL; 02924 pAttrItem = (NodeAttributePtrItem*)AttribsToApply.GetNext(pAttrItem); 02925 } 02926 AttribsToApply.DeleteAll(); // tidyup 02927 } 02928 } 02929 02930 // --------------------------------------------------------------------------- 02931 // Now do the LiveEffects 02932 if (pClipboardRange && ok) 02933 { 02934 Range* pTempRange = new Range(*pClipboardRange); 02935 ENSURE(pTempRange!=NULL, "OpPasteAttributes::Do couldn't allocate memory for Range\n"); 02936 if (pTempRange) 02937 { 02938 RangeControl rg = pTempRange->GetRangeControlFlags(); 02939 rg.PromoteToParent = FALSE; 02940 pTempRange->SetRangeControl(rg); 02941 02942 EffectsStack* pEffectsStack = EffectsStack::GetEffectsStackFromSelection(pTempRange, TRUE, TRUE); // Include locked effects 02943 if (pEffectsStack!=NULL && !pEffectsStack->IsEmpty()) 02944 { 02945 PORTNOTE("effects", "Call to DoCopyPPStackToSelection removed from OpPasteAttributes") 02946 #ifndef EXCLUDE_FROM_XARALX 02947 // Do the effect stack copy, adding actions to this op 02948 OpLiveEffect::DoCopyPPStackToSelection(this, pEffectsStack, pSrcDoc, pDestDoc); 02949 #endif 02950 } 02951 if (pEffectsStack) 02952 delete pEffectsStack; 02953 02954 delete pTempRange; 02955 } 02956 } 02957 02958 End(); 02959 } 02960 02961 02962 02963