cutop.cpp

Go to the documentation of this file.
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 

Generated on Sat Nov 10 03:44:56 2007 for Camelot by  doxygen 1.4.4