opliveeffects.cpp

Go to the documentation of this file.
00001 // $Id: opliveeffects.cpp 1282 2006-06-09 09:46:49Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 //
00099 // Implementation of Live Effects operations.
00100 //
00101 
00102 #include "camtypes.h"           // pre-compiled header
00103 #include "opliveeffects.h"      // our header file
00104 //#include "phil.h"             // for _R(IDS_GREY_WHEN_SELECT_INSIDE)
00105 #include "nodeliveeffect.h"
00106 #include "mainfrm.h"
00107 //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 #include "xpehost.h"
00109 #include "progress.h"
00110 #include "liveeffectstool.h"
00111 //#include "richard.h"          // for _R(IDS_REMOVE)
00112 //#include "simon.h"                // for _R(IDS_CONTINUE)
00113 //#include "resource.h"         // for _R(IDS_CANCEL)
00114 #include "nodecont.h"
00115 #include "opshadow.h"
00116 #include "nodeshad.h"
00117 #include "grnddib.h"
00118 #include "fthrattr.h"
00119 #include "opfeathr.h"
00120 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00121 #include "contmenu.h"
00122 #include "shadtool.h"
00123 #include "objchge.h"
00124 #include "effects_stack.h"
00125 
00126 // dynamic class creation stuff.
00127 CC_IMPLEMENT_DYNAMIC(OpLiveEffectParam, OpMenuParam);
00128 CC_IMPLEMENT_DYNCREATE(OpLiveEffect, SelOperation);
00129 CC_IMPLEMENT_DYNCREATE(OpApplyLiveEffect, OpLiveEffect);
00130 CC_IMPLEMENT_DYNCREATE(OpEditLiveEffect, OpLiveEffect);
00131 CC_IMPLEMENT_DYNCREATE(OpDeleteLiveEffect, OpLiveEffect);
00132 CC_IMPLEMENT_DYNCREATE(OpDeleteAllLiveEffect, OpLiveEffect);
00133 CC_IMPLEMENT_DYNCREATE(OpEffectRes, OpLiveEffect);
00134 CC_IMPLEMENT_DYNCREATE(OpEffectLock, OpLiveEffect);
00135 CC_IMPLEMENT_DYNCREATE(OpEffectLockAll, OpEffectLock);
00136 CC_IMPLEMENT_DYNCREATE(OpEditLegacyEffect, Operation);
00137 CC_IMPLEMENT_DYNCREATE(MarkEditListAction, Action);
00138 CC_IMPLEMENT_DYNCREATE(ChangeEffectResAction, Action);
00139 //CC_IMPLEMENT_DYNCREATE(ToggleLockAction, Action);
00140 
00141 #ifdef FEATHER_EFFECT
00142 CC_IMPLEMENT_DYNAMIC(OpFeatherEffectParam, OpLiveEffectParam);
00143 CC_IMPLEMENT_DYNCREATE(OpApplyFeatherEffect, OpLiveEffect);
00144 #endif
00145 
00146 DECLARE_SOURCE("$Revision: 1282 $");
00147 
00148 #define new CAM_DEBUG_NEW
00149 
00150 
00151 
00152 
00154 // The OpLiveEffect class                                                                    //
00156 
00157 /********************************************************************************************
00158 
00159 >   OpLiveEffect::OpLiveEffect()
00160 
00161     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00162     Created:    07/09/2004
00163     Purpose:    Constructor.
00164     Errors:     
00165     See also:   
00166 
00167 ********************************************************************************************/
00168 OpLiveEffect::OpLiveEffect()
00169 {
00170     m_pLERange=NULL;
00171     m_bAppliedNewEffects = FALSE;
00172     m_iStackPos = 0;
00173     m_bPreviewQuality = FALSE;
00174 }
00175 
00176 
00177 
00178 /********************************************************************************************
00179 
00180 >   OpLiveEffect::~OpLiveEffect()
00181 
00182     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00183     Created:    07/09/2004
00184     Purpose:    Destructor.
00185     Errors:     
00186     See also:   
00187 
00188 ********************************************************************************************/
00189 OpLiveEffect::~OpLiveEffect()
00190 {
00191     if (m_pLERange)
00192     {
00193         delete m_pLERange;
00194         m_pLERange = NULL;
00195     }
00196 }
00197 
00198 
00199 
00200 /********************************************************************************************
00201 
00202 >   static BOOL OpLiveEffect::Init()
00203 
00204     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00205     Created:    07/09/2004
00206     Inputs:     
00207     Outputs:    
00208     Returns:    
00209     Purpose:    
00210     Errors:     
00211     See also:   
00212 
00213 ********************************************************************************************/
00214 BOOL OpLiveEffect::Init()
00215 {
00216     BOOL bOK = TRUE;
00217     // Use an ParamOpDescriptor to allow OpApplyLiveEffect to receive extra information
00218     // about the menu item that invoked it...
00219     ParamOpDescriptor* pOD = NULL;
00220     pOD = new ParamOpDescriptor(OPTOKEN_APPLY_LIVEEFFECT,
00221                                 CC_RUNTIME_CLASS(OpApplyLiveEffect),
00222                                 OpApplyLiveEffect::GetParamState);
00223     bOK = bOK && (pOD!=NULL);
00224 
00225     pOD = new ParamOpDescriptor(OPTOKEN_EDIT_LIVEEFFECT,
00226                                 CC_RUNTIME_CLASS(OpEditLiveEffect),
00227                                 OpEditLiveEffect::GetParamState);
00228     bOK = bOK && (pOD!=NULL);
00229 
00230     pOD = new ParamOpDescriptor(OPTOKEN_EDIT_LEGACYEFFECT,
00231                                 CC_RUNTIME_CLASS(OpEditLegacyEffect),
00232                                 OpEditLegacyEffect::GetParamState);
00233     bOK = bOK && (pOD!=NULL);
00234 
00235 #ifdef FEATHER_EFFECT
00236     pOD = new ParamOpDescriptor(OPTOKEN_APPLY_FEATHEREFFECT,
00237                                 CC_RUNTIME_CLASS(OpApplyFeatherEffect),
00238                                 OpApplyFeatherEffect::GetParamState);
00239     bOK = bOK && (pOD!=NULL);
00240 #endif
00241 
00242 /*  bOK = bOK && RegisterOpDescriptor(0,                                // Tool ID
00243                                     _R(IDS_EDIT_LIVEEFFECT),                // String resource ID
00244                                     CC_RUNTIME_CLASS(OpEditLiveEffect), // Runtime class
00245                                     OPTOKEN_EDIT_LIVEEFFECT,            // Token string
00246                                     OpEditLiveEffect::GetState,     // GetState function
00247                                     0,                              // Help ID
00248                                     _R(IDBBL_EDIT_LIVEEFFECT),          // Bubble ID
00249                                     0,                              // Resource ID
00250                                     0,                              // Control ID
00251                                     SYSTEMBAR_ILLEGAL,              // Bar ID
00252                                     TRUE,                           // Receive system messages
00253                                     FALSE,                          // Smart duplicate operation
00254                                     FALSE,                          // Clean operation
00255                                     0,                              // No vertical counterpart
00256                                     GREY_WHEN_NO_CURRENT_DOC |
00257                                     DONT_GREY_WHEN_SELECT_INSIDE);  // automatic state checking.
00258 */
00259     bOK = bOK && RegisterOpDescriptor(0,                                // Tool ID
00260                                     _R(IDS_DELETE_LIVEEFFECT),              // String resource ID
00261                                     CC_RUNTIME_CLASS(OpDeleteLiveEffect),   // Runtime class
00262                                     OPTOKEN_DELETE_LIVEEFFECT,          // Token string
00263                                     OpDeleteLiveEffect::GetState,       // GetState function
00264                                     0,                              // Help ID
00265                                     _R(IDBBL_DELETE_LIVEEFFECT),            // Bubble ID
00266                                     0,                              // Resource ID
00267                                     0,                              // Control ID
00268                                     SYSTEMBAR_ILLEGAL,              // Bar ID
00269                                     TRUE,                           // Receive system messages
00270                                     FALSE,                          // Smart duplicate operation
00271                                     FALSE,                          // Clean operation
00272                                     0,                              // No vertical counterpart
00273                                     GREY_WHEN_NO_CURRENT_DOC |
00274                                     DONT_GREY_WHEN_SELECT_INSIDE);  // automatic state checking.
00275 
00276     bOK = bOK && RegisterOpDescriptor(0,                                // Tool ID
00277                                     _R(IDS_DELETEALL_LIVEEFFECT),               // String resource ID
00278                                     CC_RUNTIME_CLASS(OpDeleteAllLiveEffect),    // Runtime class
00279                                     OPTOKEN_DELETEALL_LIVEEFFECT,           // Token string
00280                                     OpDeleteAllLiveEffect::GetState,        // GetState function
00281                                     0,                              // Help ID
00282                                     _R(IDBBL_DELETEALL_LIVEEFFECT),         // Bubble ID
00283                                     0,                              // Resource ID
00284                                     0,                              // Control ID
00285                                     SYSTEMBAR_ILLEGAL,              // Bar ID
00286                                     TRUE,                           // Receive system messages
00287                                     FALSE,                          // Smart duplicate operation
00288                                     FALSE,                          // Clean operation
00289                                     0,                              // No vertical counterpart
00290                                     GREY_WHEN_NO_CURRENT_DOC |
00291                                     DONT_GREY_WHEN_SELECT_INSIDE);  // automatic state checking.
00292 
00293     bOK = bOK && RegisterOpDescriptor(0,                                // Tool ID
00294                                     _R(IDS_CHANGE_EFFECT_RES),              // String resource ID
00295                                     CC_RUNTIME_CLASS(OpEffectRes),  // Runtime class
00296                                     OPTOKEN_CHANGE_EFFECT_RES,          // Token string
00297                                     OpEffectRes::GetState,      // GetState function
00298                                     0,                              // Help ID
00299                                     _R(IDBBL_CHANGE_EFFECT_RES),            // Bubble ID
00300                                     0,                              // Resource ID
00301                                     0,                              // Control ID
00302                                     SYSTEMBAR_ILLEGAL,              // Bar ID
00303                                     TRUE,                           // Receive system messages
00304                                     FALSE,                          // Smart duplicate operation
00305                                     FALSE,                          // Clean operation
00306                                     0,                              // No vertical counterpart
00307                                     GREY_WHEN_NO_CURRENT_DOC |
00308                                     DONT_GREY_WHEN_SELECT_INSIDE);  // automatic state checking.
00309 
00310     bOK = bOK && RegisterOpDescriptor(0,                                // Tool ID
00311                                     _R(IDS_CHANGE_EFFECT_LOCK),             // String resource ID
00312                                     CC_RUNTIME_CLASS(OpEffectLock), // Runtime class
00313                                     OPTOKEN_CHANGE_EFFECT_LOCK,         // Token string
00314                                     OpEffectLock::GetState,     // GetState function
00315                                     0,                              // Help ID
00316                                     _R(IDBBL_CHANGE_EFFECT_LOCK),           // Bubble ID
00317                                     0,                              // Resource ID
00318                                     0,                              // Control ID
00319                                     SYSTEMBAR_ILLEGAL,              // Bar ID
00320                                     TRUE,                           // Receive system messages
00321                                     FALSE,                          // Smart duplicate operation
00322                                     FALSE,                          // Clean operation
00323                                     0,                              // No vertical counterpart
00324                                     GREY_WHEN_NO_CURRENT_DOC |
00325                                     DONT_GREY_WHEN_SELECT_INSIDE);  // automatic state checking.
00326 
00327     bOK = bOK && RegisterOpDescriptor(0,                                // Tool ID
00328                                     _R(IDS_CHANGE_EFFECT_LOCKALL),              // String resource ID
00329                                     CC_RUNTIME_CLASS(OpEffectLockAll),  // Runtime class
00330                                     OPTOKEN_CHANGE_EFFECT_LOCKALL,          // Token string
00331                                     OpEffectLockAll::GetState,      // GetState function
00332                                     0,                              // Help ID
00333                                     _R(IDBBL_CHANGE_EFFECT_LOCKALL),            // Bubble ID
00334                                     0,                              // Resource ID
00335                                     0,                              // Control ID
00336                                     SYSTEMBAR_ILLEGAL,              // Bar ID
00337                                     TRUE,                           // Receive system messages
00338                                     FALSE,                          // Smart duplicate operation
00339                                     FALSE,                          // Clean operation
00340                                     0,                              // No vertical counterpart
00341                                     GREY_WHEN_NO_CURRENT_DOC |
00342                                     DONT_GREY_WHEN_SELECT_INSIDE);  // automatic state checking.
00343 
00344     return bOK;
00345 }
00346 
00347 
00348 
00349 /********************************************************************************************
00350 
00351 >   static ListRange* OpLiveEffect::DoApplyLiveEffect(Range* pSel, String_256 strEffectID, String_64 strDisplayName, double dResolution, IXMLDOMDocumentPtr pEditsList = NULL, BOOL bMakeLocked = FALSE, BOOL bDetectDirectBitmaps = FALSE)
00352 
00353     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00354     Created:    29/09/2004
00355     Inputs:     pSel - pointer to range of nodes to insert LiveEffects above
00356                 strDispayName - The display name of the effect we're going to apply
00357     Outputs:    pNewSel - pointer to new range of LiveEffect nodes
00358                         - NULL if no new live effects added
00359     Returns:    
00360     Purpose:    
00361     Errors:     
00362     See also:   
00363 
00364 ********************************************************************************************/
00365 ListRange* OpLiveEffect::DoApplyLiveEffect(Range* pSel, String_256 strEffectID, String_64 strDisplayName, double dResolution, IXMLDOMDocumentPtr pEditsList, BOOL bMakeLocked, BOOL bDetectDirectBitmaps)
00366 {
00367     ENSURE(pSel, "DoApplyLiveEffect not given a useful range");
00368     ENSURE(strEffectID!=String(""), "DoApplyLiveEffect given blank effect name string");
00369 
00370     ListRange* pLERange = new ListRange();
00371     ENSURE(pLERange, "DoApplyEffect can't create a ListRange");
00372     if (pLERange==NULL)
00373         return NULL;
00374 
00375     Node* pNode = pSel->FindFirst();
00376     while (pNode)
00377     {
00378         // create a new NodeLiveEffect, which we will shortly insert into the tree;
00379         // note that ALLOC_WITH_FAIL automatically calls FailAndExecute() if things go wrong.
00380         NodeBitmapEffect* pBmpFXNode = NULL;
00381         pBmpFXNode = DoApplyLiveEffect(this, pNode, strEffectID, strDisplayName, dResolution, pEditsList, bMakeLocked);
00382 
00383         if (pBmpFXNode)
00384         {
00385             // If the node we're applying to can supply DirectBitmaps and we can accept them
00386             // Then change res to be "automatic"
00387             if (bDetectDirectBitmaps &&
00388                 pNode->IsAnObject() &&
00389                 ((NodeRenderableInk*)pNode)->CanSupplyDirectBitmap() &&
00390                 dResolution != 0
00391                 )
00392             {
00393                 // Child node can supply a DirectBitmap so it would be better if we were in
00394                 // "Automatic" mode.
00395                 pBmpFXNode->SetPixelsPerInch(0);            // Set "Automatic"
00396 
00397                 // We can only test EnableDirectCapture after we have set the res to automatic
00398                 // Otherwise it may return FALSE because the stored res (dResolution) may not match that
00399                 // of the DirectBitmap child.
00400                 // If it fails, then reset resolution back to what was specified.
00401                 if (!pBmpFXNode->EnableDirectCapture())
00402                     pBmpFXNode->SetPixelsPerInch(dResolution);
00403             }
00404 
00405             // Deselect the node and select the live effect instead
00406             // TODO: Really?
00407             ((NodeRenderable*)pNode)->DeSelect(FALSE);
00408             pBmpFXNode->Select(FALSE);
00409 
00410             pLERange->AddNode(pBmpFXNode);
00411 
00412             // Make sure the attributes are optimised correctly
00413             BOOL ok = DoFactorOutCommonChildAttributes(pBmpFXNode);
00414             if (!ok)
00415                 pBmpFXNode = NULL;
00416         }
00417 
00418         if (!pBmpFXNode)
00419         {
00420             if (pLERange->FindFirst()==NULL)
00421             {
00422                 // Don't let an empty list of LiveEffect nodes hang around
00423                 delete pLERange;
00424                 pLERange = NULL;
00425             }
00426             return pLERange;
00427         }
00428 
00429         pNode = pSel->FindNext(pNode);
00430     }
00431 
00432     if (pLERange->FindFirst()==NULL)
00433     {
00434         // Don't let an empty list of LiveEffect nodes hang around
00435         delete pLERange;
00436         pLERange = NULL;
00437     }
00438 
00439     return pLERange;
00440 }
00441 
00442 
00443 
00444 
00445 /********************************************************************************************
00446 
00447 >   static NodeBitmapEffect* OpLiveEffect::DoApplyLiveEffect(UndoableOperation* pOp,
00448                                                              Node* pNode,
00449                                                              String_256 strEffectID,
00450                                                              String_64 strDisplayName,
00451                                                              double dResolution,
00452                                                              IXMLDOMDocumentPtr pEditsList = NULL,
00453                                                              BOOL bMakeLocked = FALSE)
00454 
00455     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00456     Created:    07/02/2005
00457     Inputs:     pOp - Op to add undo info to
00458                 pNode - pointer to node to insert LiveEffects above
00459                 strEffectID - Unique identifier for Op
00460                 strDispayName - The display name of the effect we're going to apply
00461                 pEditsList - pointer to optional list of edit params for this effect
00462     Outputs:    -
00463     Returns:    TRUE if worked OK
00464     Purpose:    
00465     Errors:     
00466     See also:   
00467 
00468 ********************************************************************************************/
00469 NodeBitmapEffect* OpLiveEffect::DoApplyLiveEffect(UndoableOperation* pOp,
00470                                                   Node* pNode,
00471                                                   String_256 strEffectID,
00472                                                   String_64 strDisplayName,
00473                                                   double dResolution,
00474                                                   IXMLDOMDocumentPtr pEditsList,
00475                                                   BOOL bMakeLocked)
00476 {
00477     ERROR2IF(pOp==NULL, NULL, "Missing Op pointer");
00478     ERROR2IF(pNode==NULL, NULL, "Missing Node pointer");
00479     ERROR2IF(strEffectID==String(""), NULL, "DoApplyLiveEffect given blank effect name string");
00480     ERROR2IF(strDisplayName==String(""), NULL, "DoApplyLiveEffect given blank display name string");
00481 
00482     BOOL bSelectInside = pNode->IsSelected() || pNode->IsParentOfSelected();
00483 
00484     // Possible solution to issue of Bitmap with Feather attr under DirectBitmap effects
00485     // Detect that state, delete the feather attr, apply a feather effect with the same values
00486     //pNode = ApplyDirectFeatherBodge(pNode);
00487 
00488     // create a new NodeLiveEffect, which we will shortly insert into the tree;
00489     // note that ALLOC_WITH_FAIL automatically calls FailAndExecute() if things go wrong.
00490     NodeBitmapEffect* pBmpFXNode = NULL;
00491     if (bMakeLocked)
00492     {
00493         ALLOC_WITH_FAIL(pBmpFXNode, new NodeLockedEffect, pOp);
00494     }
00495     else
00496     {
00497         ALLOC_WITH_FAIL(pBmpFXNode, new NodeLiveEffect, pOp);
00498     }
00499     if (pBmpFXNode==NULL)
00500         return NULL;
00501 
00502     // Get details from XPE so that displayname gets translated into
00503     // locale language if not already
00504     BOOL bDestructive = FALSE;
00505     XPEHost::GetEffectDetails(strEffectID, &strDisplayName, &bDestructive);
00506     ERROR3IF(bDestructive, "DoApplyLiveEffect asked to apply destructive effect!!!");
00507     if (bDestructive)
00508         return NULL;
00509 
00510     // Set effect params
00511     pBmpFXNode->SetPostProcessorID(strEffectID);
00512     pBmpFXNode->SetDisplayName(strDisplayName);
00513     pBmpFXNode->SetPixelsPerInch(dResolution);
00514     if (pEditsList)
00515     {
00516         pBmpFXNode->SetEditList(pEditsList);
00517     }
00518     else if (!pBmpFXNode->SetDefaultEditList())
00519     {
00520         return NULL;
00521     }
00522 
00523     if (pBmpFXNode->IsLockedEffect())
00524         ((NodeLockedEffect*)pBmpFXNode)->SetLockPermanence(bDestructive);
00525 
00526     // put an action to hide the NodeLiveEffect onto the undo action-list,
00527     // so that if the user presses undo then it will be hidden.
00528     HideNodeAction* pUndoHideNodeAction = NULL;
00529     ActionCode ac = HideNodeAction::Init(pOp,
00530                                         pOp->GetUndoActionList(),
00531                                         pBmpFXNode,
00532                                         FALSE,      // don't include subtree size
00533                                         (Action**)&pUndoHideNodeAction,
00534                                         FALSE);     // don't tell subtree when undone
00535     if (ac == AC_FAIL)
00536         return NULL;
00537 
00538     // right! we've got our node, we've got our action - lets stick it in the tree
00539     // (at a position just next to the last node which will go in the group).
00540     pBmpFXNode->AttachNode(pNode, NEXT);
00541 
00542     // Move the node below the newly inserted LiveEffect
00543     BOOL ok = pOp->DoMoveNode(pNode, pBmpFXNode, FIRSTCHILD);
00544     if (!ok)
00545         return NULL;
00546 
00547 // Set the OpPersmission flags in the newly inserted node
00548 // Otherwise the tree scanner in UpdateChangedNodes won't go below this node
00549 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);  // ReplaceNode & RegenerateNode
00550 ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, pOp);
00551 pBmpFXNode->AllowOp(&ObjChange);
00552 
00553     // Deselect the node and select the live effect instead
00554     // TODO: Really?
00555 //  ((NodeRenderable*)pNode)->DeSelect(FALSE);
00556 //  pBmpFXNode->Select(FALSE);
00557 
00558     return pBmpFXNode;
00559 }
00560 
00561 
00562 
00563 
00564 #ifdef FEATHER_EFFECT
00565 /********************************************************************************************
00566 
00567 >   static ListRange* OpLiveEffect::DoApplyFeatherEffect(UndoableOperation* pOp,
00568                                                           Range* pSel,
00569                                                           String_256 strEffectID,
00570                                                           String_64 strDisplayName,
00571                                                           MILLIPOINT FeatherSize,
00572                                                           CProfileBiasGain Profile)
00573     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00574     Created:    24/11/2004
00575     Inputs:     pSel - pointer to range of nodes to insert LiveEffects above
00576                 strDispayName - The display name of the effect we're going to apply
00577     Outputs:    pNewSel - pointer to new range of LiveEffect nodes
00578                         - NULL if no new live effects added
00579     Returns:    
00580     Purpose:    
00581     Errors:     
00582     See also:   
00583 
00584 ********************************************************************************************/
00585 ListRange* OpLiveEffect::DoApplyFeatherEffect(UndoableOperation* pOp,
00586                                               Range* pSel,
00587                                               String_256 strEffectID,
00588                                               String_64 strDisplayName,
00589                                               MILLIPOINT FeatherSize,
00590                                               CProfileBiasGain Profile)
00591 {
00592     ENSURE(pSel, "DoApplyFeatherEffect not given a useful range");
00593     ENSURE(strEffectID!=String(""), "DoApplyFeatherEffect given blank effect name string");
00594 
00595     ListRange* pLERange = new ListRange();
00596     ENSURE(pLERange, "DoApplyEffect can't create a ListRange");
00597     if (pLERange==NULL)
00598         return NULL;
00599 
00600     Node* pNode = pSel->FindFirst();
00601     BOOL bOK = TRUE;
00602     while (pNode && bOK)
00603     {
00604         NodeFeatherEffect* pEffect = DoApplyFeatherEffectNode(pOp, pNode, strEffectID, strDisplayName, FeatherSize, Profile);
00605         if (pEffect)
00606         {
00607             pLERange->AddNode(pEffect);
00608 
00609             // Make sure the attributes are optimised correctly
00610             bOK = pOp->DoFactorOutCommonChildAttributes(pEffect);
00611         }
00612         else
00613             bOK = FALSE;
00614 
00615         pNode = pSel->FindNext(pNode);
00616     }
00617 
00618     if (pLERange->FindFirst()==NULL || bOK==FALSE)
00619     {
00620         // Don't let an empty list of LiveEffect nodes hang around
00621         delete pLERange;
00622         pLERange = NULL;
00623     }
00624 
00625     return pLERange;
00626 }
00627 
00628 
00629 
00630 
00631 /********************************************************************************************
00632 
00633 >   static NodeFeatherEffect* OpLiveEffect::DoApplyFeatherEffectNode(UndoableOperation* pOp,
00634                                                           Node* pSel,
00635                                                           String_256 strEffectID,
00636                                                           String_64 strDisplayName,
00637                                                           MILLIPOINT FeatherSize,
00638                                                           CProfileBiasGain Profile)
00639     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00640     Created:    03/03/2005
00641     Inputs:     pSel - pointer to node to insert Feather Effect above
00642                 strDispayName - The display name of the effect we're going to apply
00643     Outputs:    -
00644     Returns:    Pointer to newly created NodeFeatherEffect or NULL
00645     Purpose:    
00646     Errors:     
00647     See also:   
00648 
00649 ********************************************************************************************/
00650 NodeFeatherEffect* OpLiveEffect::DoApplyFeatherEffectNode(UndoableOperation* pOp,
00651                                               Node* pNode,
00652                                               String_256 strEffectID,
00653                                               String_64 strDisplayName,
00654                                               MILLIPOINT FeatherSize,
00655                                               CProfileBiasGain Profile)
00656 {
00657     ENSURE(pNode, "DoApplyFeatherEffectNode not given a useful node");
00658     ENSURE(strEffectID!=String(""), "DoApplyFeatherEffectNode given blank effect name string");
00659 
00660     BOOL bSelectInside = pNode->IsSelected() || pNode->IsParentOfSelected();
00661 
00662     // create a new NodeLiveEffect, which we will shortly insert into the tree;
00663     // note that ALLOC_WITH_FAIL automatically calls FailAndExecute() if things go wrong.
00664     NodeFeatherEffect* pBmpFXNode = NULL;
00665     ALLOC_WITH_FAIL(pBmpFXNode, new NodeFeatherEffect, pOp);
00666     if (pBmpFXNode == NULL)
00667         return NULL;
00668 
00669     // put an action to hide the NodeClipViewController onto the undo action-list,
00670     // so that if the user presses undo then it will be hidden.
00671     pBmpFXNode->SetPostProcessorID(strEffectID);
00672     pBmpFXNode->SetDisplayName(strDisplayName);
00673     pBmpFXNode->m_FeatherSize = FeatherSize;
00674     pBmpFXNode->m_Profile = Profile;
00675 
00676     HideNodeAction* pUndoHideNodeAction = NULL;
00677     ActionCode ac = HideNodeAction::Init(pOp,
00678                                         pOp->GetUndoActions(),
00679                                         pBmpFXNode,
00680                                         FALSE,      // don't include subtree size
00681                                         (Action**)&pUndoHideNodeAction,
00682                                         FALSE);     // don't tell subtree when undone
00683     if (ac == AC_FAIL)
00684     {
00685         delete pBmpFXNode;
00686         return NULL;
00687     }
00688 
00689     // right! we've got our node, we've got our action - lets stick it in the tree
00690     // (at a position just next to the last node which will go in the group).
00691     pBmpFXNode->AttachNode(pNode, NEXT);
00692 
00693     // Move the node below the newly inserted LiveEffect
00694     BOOL bOK = pOp->DoMoveNode(pNode, pBmpFXNode, FIRSTCHILD);
00695     if (!bOK)
00696         return NULL;
00697 
00698     // Deselect the node and select the live effect instead
00699     ((NodeRenderable*)pNode)->DeSelect(FALSE);
00700     pBmpFXNode->Select(FALSE);
00701 
00702 // Set the OpPersmission flags in the newly inserted node
00703 // Otherwise the tree scanner in UpdateChangedNodes won't go below this node
00704 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);  // ReplaceNode & RegenerateNode
00705 ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, pOp);
00706 pBmpFXNode->AllowOp(&ObjChange);
00707 
00708     return pBmpFXNode;
00709 }
00710 #endif
00711 
00712 
00713 
00714 
00715 /********************************************************************************************
00716 
00717 >   virtual ListRange* OpLiveEffect::DoApplyLockedEffect(Range* pSel,
00718                                                           String_256 strEffectID,
00719                                                           String_64 strDisplayName,
00720                                                           double dResolution,
00721                                                           IXMLDOMDocumentPtr pEditsList = NULL,
00722                                                           BOOL bDetectDirectBitmaps = FALSE)
00723 
00724 
00725     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00726     Created:    29/09/2004
00727     Inputs:     pSel - pointer to range of nodes to insert LiveEffects above
00728                 strDispayName - The display name of the effect we're going to apply
00729     Outputs:    pNewSel - pointer to new range of LiveEffect nodes
00730                         - NULL if no new live effects added
00731     Returns:    
00732     Purpose:    
00733     Errors:     
00734     See also:   
00735 
00736 ********************************************************************************************/
00737 ListRange* OpLiveEffect::DoApplyLockedEffect(Range* pSel,
00738                                               String_256 strEffectID,
00739                                               String_64 strDisplayName,
00740                                               double dResolution,
00741                                               IXMLDOMDocumentPtr pEditsList,
00742                                               BOOL bDetectDirectBitmaps)
00743 {
00744     ENSURE(pSel, "DoApplyLockedEffect not given a useful range");
00745     ENSURE(strEffectID!=String(""), "DoApplyLockedEffect given blank effect name string");
00746 
00747     ListRange* pLERange = new ListRange();
00748     ENSURE(pLERange, "DoApplyEffect can't create a ListRange");
00749     if (pLERange==NULL)
00750         return NULL;
00751 
00752     // we need to insert the controller node at the position of the highest
00753     // selected node in the z-order, ie last in the selection, so find it.
00754     Node* pFirstNode = pSel->FindFirst();
00755     Node* pNode = pFirstNode;
00756     Node* pLastNode = NULL;
00757     while (pNode != NULL)
00758     {
00759         pLastNode = pNode;
00760         pNode = pSel->FindNext(pLastNode);
00761     }   // loop terminates with pNode == NULL, pLastNode == last-node-in-sel.
00762 
00763     // create a new NodeClipViewController, which we will shortly insert into the tree;
00764     // note that ALLOC_WITH_FAIL automatically calls FailAndExecute() if things go wrong.
00765     NodeLockedEffect* pBmpFXNode = NULL;
00766     ALLOC_WITH_FAIL(pBmpFXNode, new NodeLockedEffect, this);
00767     BOOL ok = (pBmpFXNode != NULL);
00768 
00769     if (ok)
00770     {
00771         // Get details from XPE so that displayname gets translated into
00772         // locale language if not already and destructive state is updated
00773         // for the current machine (shouldn't really change, though)
00774         BOOL bDestructive = TRUE;
00775         XPEHost::GetEffectDetails(strEffectID, &strDisplayName, &bDestructive);
00776 
00777         pBmpFXNode->SetPostProcessorID(strEffectID);
00778         pBmpFXNode->SetDisplayName(strDisplayName);
00779         pBmpFXNode->SetPixelsPerInch(dResolution);
00780         if (pEditsList)
00781         {
00782             pBmpFXNode->SetEditList(pEditsList);
00783         }
00784         else if (!pBmpFXNode->SetDefaultEditList())
00785         {
00786             delete pBmpFXNode;
00787 
00788             // Don't let an empty list of LiveEffect nodes hang around
00789             delete pLERange;
00790             pLERange = NULL;
00791             return pLERange;
00792         }
00793         pBmpFXNode->SetLockPermanence(bDestructive);
00794 
00795         // put an action to hide the NodeClipViewController onto the undo action-list,
00796         // so that if the user presses undo then it will be hidden.
00797         HideNodeAction* pUndoHideNodeAction = NULL;
00798         ActionCode ac = HideNodeAction::Init(this,
00799                                             &UndoActions,
00800                                             pBmpFXNode,
00801                                             FALSE,      // don't include subtree size
00802                                             (Action**)&pUndoHideNodeAction,
00803                                             FALSE);     // don't tell subtree when undone
00804         if (ac == AC_FAIL)
00805         {
00806             delete pBmpFXNode;
00807 
00808             // Don't let an empty list of LiveEffect nodes hang around
00809             delete pLERange;
00810             pLERange = NULL;
00811             return pLERange;
00812         }
00813         else
00814         {
00815             // right! we've got our node, we've got our action - lets stick it in the tree
00816             // (at a position just next to the last node which will go in the group).
00817             pBmpFXNode->AttachNode(pLastNode, NEXT);
00818             pLERange->AddNode(pBmpFXNode);
00819         }
00820     }
00821 
00822     // move each item from the selection into our ClipView group,
00823     // remembering to deselect them as we go.
00824     // TODO:
00825     //  sneaky suspicion I should be putting this in a Do fn in UndoableOperation...
00826     if (ok)
00827     {
00828         pNode = pSel->FindNext(pFirstNode);             // the node we're moving now.
00829 
00830 //      if (pFirstNode->IsAnObject())
00831 //          ok = DoLocaliseCommonAttributes((NodeRenderableInk*)pFirstNode);
00832 
00833         ok = DoMoveNode(pFirstNode, pBmpFXNode, FIRSTCHILD);
00834         if (ok)
00835             ((NodeRenderable*)pFirstNode)->DeSelect(FALSE, TRUE);
00836     }
00837 
00838     Node* pNextNode     = NULL;                         // the next node to move.
00839     Node* pAnchorNode   = pFirstNode;                   // the node we've just moved.
00840     while (ok && pNode != NULL)
00841     {
00842         // get the next node to move.
00843         pNextNode = pSel->FindNext(pNode);
00844 
00845         // now move the current node next to the anchor and deselect it.
00846 //      if (pNode->IsAnObject())
00847 //          ok = DoLocaliseCommonAttributes((NodeRenderableInk*)pNode);
00848 
00849         ok = DoMoveNode(pNode, pAnchorNode, NEXT);
00850         if (ok)
00851             ((NodeRenderable*)pNode)->DeSelect(FALSE, TRUE);
00852 
00853         // get the new anchor node and the next node to move.
00854         pAnchorNode = pNode;
00855         pNode = pNextNode;
00856     }
00857 
00858     // If we're applied to a single node and that node can supply DirectBitmaps and we can accept them
00859     // Then change res to be same as child node
00860     NodeRenderableInk* pInkNode = pBmpFXNode->GetInkNodeFromController();
00861     if (bDetectDirectBitmaps &&
00862         pInkNode &&
00863         pInkNode->CanSupplyDirectBitmap()
00864         )
00865     {
00866         // Child node can supply a DirectBitmap so it would be better if we had same resolution
00867         // as the child node...
00868         double dDirectRes = 0;
00869         pInkNode->GetDirectBitmap(NULL, NULL, NULL, NULL, NULL, &dDirectRes);
00870         if (dDirectRes!=0)
00871         {
00872             pBmpFXNode->SetPixelsPerInch(dDirectRes);           // Set res
00873 
00874             // We can only test EnableDirectCapture after we have set the res correctly
00875             // Otherwise it may return FALSE because the stored res (dResolution) may not match that
00876             // of the DirectBitmap child.
00877             // If it fails, then reset resolution back to what was specified.
00878             if (!pBmpFXNode->EnableDirectCapture())
00879                 pBmpFXNode->SetPixelsPerInch(dResolution);
00880         }
00881     }
00882 
00883     if (ok)
00884     {
00885         // select the new destructive effect, but don't draw any blobs yet.
00886         pBmpFXNode->Select(FALSE);
00887 
00888         // factor out any common attributes.
00889         DoFactorOutCommonChildAttributes(pBmpFXNode);
00890 
00891 // Set the OpPersmission flags in the newly inserted node
00892 // Otherwise the tree scanner in UpdateChangedNodes won't go below this node
00893 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);  // ReplaceNode & RegenerateNode
00894 ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, this);
00895 pBmpFXNode->AllowOp(&ObjChange);
00896     }
00897 
00898     if (pLERange->FindFirst()==NULL)
00899     {
00900         // Don't let an empty list of LiveEffect nodes hang around
00901         delete pLERange;
00902         pLERange = NULL;
00903     }
00904 
00905     return pLERange;
00906 }
00907 
00908 
00909 
00910 
00911 /********************************************************************************************
00912 
00913 >   BOOL OpLiveEffect::IsTopOfStack(EffectsStack* pPPStack, INT32 iStackPos)
00914 
00915     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00916     Created:    07/10/2004
00917     Inputs:     iStackPos - position in LiveEffect stack to find nodes
00918                             0 means first stack pos above selection
00919     Outputs:    
00920     Returns:    
00921     Purpose:    
00922     Errors:     
00923     See also:   
00924 
00925 ********************************************************************************************/
00926 BOOL OpLiveEffect::IsTopOfStack(EffectsStack* pPPStack, INT32 iStackPos)
00927 {
00928     return (iStackPos>=(INT32)pPPStack->GetCount());
00929 }
00930 
00931 
00932 
00933 
00934 /********************************************************************************************
00935 
00936 >   BOOL OpLiveEffect::EnsureLiveEffectOriginalBitmaps()
00937 
00938     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00939     Created:    10/09/2004
00940     Inputs:     
00941     Outputs:    
00942     Returns:    
00943     Purpose:    Ensure that all NodeBitmapEffects in the current selection
00944                 have original bitmaps ready and wait for us to pick up
00945     Errors:     
00946     See also:   
00947 
00948 ********************************************************************************************/
00949 BOOL OpLiveEffect::EnsureLiveEffectOriginalBitmaps()
00950 {
00951     if (m_pLERange==NULL)
00952         return TRUE;
00953 
00954     BOOL bRenderingNeeded = FALSE;
00955     DocView* pDocView = DocView::GetSelected();
00956 
00957     bRenderingNeeded = FALSE;
00958     Node* pNode = m_pLERange->FindFirst();
00959     while (pNode)
00960     {
00961         ENSURE(pNode->IsBitmapEffect(), "LiveEffects list contains non-LiveEffect in EnsureLiveEffectOriginalBitmaps");
00962         NodeBitmapEffect* pLE = (NodeBitmapEffect*)pNode;
00963         if (!pLE->GetOriginalBitmap(NULL, NULL, NULL) && pLE->CanGenerateBitmap())
00964         {
00965             // We need to forcibly render this node to get its original
00966             // bitmap set up
00967             //
00968             // Version2 Bitmap Generation
00969             //
00970             // Grab bitmap without using on screen rendering
00971             // This is more efficient than rendering the whole tree and 
00972             // allows parameters to be controlled independently from screen view
00973             //
00974             // Ideally:
00975             // Start a render into a RenderRegion
00976             // Don't allow the RenderRegion to grab bitmap of its own - NOT DONE
00977             // Don't allow the RenderRegion to blit captures into it's own bitmap - NOT DONE
00978             // Ensure only the effect and it's children are rendered - DONE!
00979             // Just let the effect rendering functions create their capture bitmap - DONE!
00980 
00981             // Create a new GRenderDIB region
00982             // Most of the parameters here are immaterial because we are not really
00983             // interested in the results of the rendered region - just the capture
00984             pLE->ReleaseCached(TRUE, FALSE);                            // We will change so tell parents to uncache
00985 
00986             // Bodge? Ensure that locked effects will render their children
00987             // because they don't respond to ReleaseCached...
00988             if (pLE->IsLockedEffect())
00989                 pLE->SetProcessedBitmap(NULL, NULL, DocRect(), 0, 0, 0, 0);
00990 
00991             View* pView = View::GetCurrent();
00992             Spread* pSpread = Document::GetSelectedSpread();
00993             Matrix Mat = pView->ConstructScaledRenderingMatrix(pSpread, 1.0);
00994             FIXED16 Scale;
00995             Mat.Decompose(&Scale);
00996             double dpi = 0.0;   // use screen dpi (ie PIXELS_PER_INCH)
00997             GRenderDIB* pNewGRR = new GRenderDIB(pLE->GetBoundingRect(), Mat, Scale, 32, dpi);
00998             ERROR2IF(pNewGRR == NULL, FALSE, "Failed to create a GRenderDIB!");
00999 
01000             // State flags + pixel width calculations
01001             if (!pNewGRR->AttachDevice(pView, NULL, pSpread))       // view and spread from previous RR rather than Current - nb in create bitmap copy case
01002             {
01003                 ERROR3("Cannot attach devices");
01004                 
01005                 delete pNewGRR;
01006                 pNewGRR = NULL;
01007                 
01008                 return FALSE;
01009             }
01010 
01011             pNewGRR->m_DoCompression = FALSE;
01012             pNewGRR->InitDevice();
01013             pNewGRR->InitAttributes();
01014             pNewGRR->SetLineAttributes();
01015             pNewGRR->SetFillAttributes();
01016             pNewGRR->SetBackmostChangedNode(pSpread);       // Don't allow any cached 24BPP bitmaps to be used!
01017 
01018             // NB following call gets GRendRegion to create the bitmap into which GDraw will render
01019             BOOL Started = pNewGRR->StartRender();
01020 
01021             if (Started)
01022             {
01023                 // Setup attribute state outside this subtree
01024                 CCAttrMap* pAttrMap = new CCAttrMap;
01025                 if (pAttrMap!=NULL)
01026                 {
01027                     pLE->FindAppliedAttributes(pAttrMap, 5000, NULL, FALSE, FALSE);
01028                     pAttrMap->Render(pNewGRR);
01029                     delete pAttrMap;
01030                 }
01031 
01032                 // This is the important bit - force top quality rendering
01033                 // so that effects capture is done properly regardless of 
01034                 // the current document/view quality
01035                 pNewGRR->RRQuality.SetQuality(QUALITY_MAX);
01036                 pNewGRR->SetQualityLevel();
01037 
01038                 pNewGRR->SetImmediateRender(TRUE);
01039 
01040                 pNewGRR->RenderTree(pLE, FALSE, FALSE);
01041                 pNewGRR->StopRender();
01042 
01043             }
01044 
01045             // Sanity check
01046             // We really should have generated an original bitmap by now!
01047             ERROR2IF(!pLE->GetOriginalBitmap(NULL, NULL, NULL), FALSE, "EnsureLiveEffectOriginalBitmaps failed to generate an original bitmap");
01048 
01049             delete pNewGRR;
01050             pNewGRR = NULL;
01051         }
01052 
01053         pNode = m_pLERange->FindNext(pNode);
01054     }
01055 
01056     return TRUE;
01057 }
01058 
01059 
01060 
01061 
01062 /********************************************************************************************
01063 
01064 >   BOOL OpLiveEffect::GenerateBitmap(NodeBitmapEffect* pBitmapEffect,
01065                                       double dResolution,
01066                                       LPBITMAPINFO* ppInfo,
01067                                       LPBYTE* ppBits,
01068                                       DocRect* prectBounds)
01069 
01070     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01071     Created:    13/06/2005
01072     Inputs:     
01073     Outputs:    
01074     Returns:    
01075     Purpose:
01076     Errors:     
01077     See also:   
01078 
01079 ********************************************************************************************/
01080 BOOL OpLiveEffect::GenerateBitmap(NodeBitmapEffect* pBitmapEffect,
01081                                   double dResolution,
01082                                   LPBITMAPINFO* ppInfo,
01083                                   LPBYTE* ppBits,
01084                                   DocRect* prectBounds)
01085 {
01086     // We need to forcibly render this node to get its original
01087     // bitmap set up
01088     //
01089     // Version2 Bitmap Generation
01090     //
01091     // Grab bitmap without using on screen rendering
01092     // This is more efficient than rendering the whole tree and 
01093     // allows parameters to be controlled independently from screen view
01094     //
01095     // Ideally:
01096     // Start a render into a RenderRegion
01097     // Don't allow the RenderRegion to grab bitmap of its own - NOT DONE
01098     // Don't allow the RenderRegion to blit captures into it's own bitmap - NOT DONE
01099     // Ensure only the effect and it's children are rendered - DONE!
01100     // Just let the effect rendering functions create their capture bitmap - DONE!
01101 
01102     // Create a new GRenderDIB region
01103     // Most of the parameters here are immaterial because we are not really
01104     // interested in the results of the rendered region - just the capture
01105     View* pView = View::GetCurrent();
01106     Spread* pSpread = Document::GetSelectedSpread();
01107     Matrix Mat = pView->ConstructScaledRenderingMatrix(pSpread, 1.0);
01108     FIXED16 Scale;
01109     Mat.Decompose(&Scale);
01110     GRenderDIB* pNewGRR = new GRenderDIB(pBitmapEffect->GetBoundingRect(), Mat, Scale, 32, dResolution);
01111     ERROR2IF(pNewGRR == NULL, FALSE, "Failed to create a GRenderDIB!");
01112 
01113     // State flags + pixel width calculations
01114     if (!pNewGRR->AttachDevice(pView, NULL, pSpread))       // view and spread from previous RR rather than Current - nb in create bitmap copy case
01115     {
01116         ERROR3("Cannot attach devices");
01117         
01118         delete pNewGRR;
01119         pNewGRR = NULL;
01120         
01121         *ppInfo = NULL;
01122         *ppBits = NULL;
01123 
01124         *prectBounds = DocRect();
01125 
01126         return FALSE;
01127     }
01128 
01129     pNewGRR->m_DoCompression = TRUE;            // We do want transparency
01130     pNewGRR->InitDevice();
01131     pNewGRR->InitAttributes();
01132     pNewGRR->SetLineAttributes();
01133     pNewGRR->SetFillAttributes();
01134 
01135     // NB following call gets GRendRegion to create the bitmap into which GDraw will render
01136     BOOL Started = pNewGRR->StartRender();
01137 
01138     if (Started)
01139     {
01140         // Setup attribute state outside this subtree
01141         CCAttrMap* pAttrMap = new CCAttrMap;
01142         if (pAttrMap!=NULL)
01143         {
01144             pBitmapEffect->FindAppliedAttributes(pAttrMap, 5000, NULL, FALSE, FALSE);
01145             pAttrMap->Render(pNewGRR);
01146             delete pAttrMap;
01147         }
01148 
01149         // This is the important bit - force top quality rendering
01150         // so that effects capture is done properly regardless of 
01151         // the current document/view quality
01152         pNewGRR->RRQuality.SetQuality(QUALITY_MAX);
01153         pNewGRR->SetQualityLevel();
01154 
01155         pNewGRR->RenderTree(pBitmapEffect, FALSE, FALSE);
01156         pNewGRR->StopRender();
01157     }
01158 
01159     *prectBounds = pNewGRR->GetClipRect();          // Get pixel-aligned rect
01160     pNewGRR->GetBitmapPointers(ppInfo, ppBits);     // Get the bitmap data out of the region, converting T channel
01161     pNewGRR->SetBitmapPointers(NULL, NULL);         // We own the bitmap now, it will not be deallocated by
01162                                                     // the following renderegion deleteion
01163     delete pNewGRR;
01164     pNewGRR = NULL;
01165 
01166     return TRUE;
01167 }
01168 
01169 
01170 
01171 
01172 /********************************************************************************************
01173 
01174 >   BOOL OpLiveEffect::SetChanged(BOOL bNewState)
01175 
01176     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01177     Created:    30/09/2004
01178     Inputs:     
01179     Outputs:    
01180     Returns:    
01181     Purpose:
01182     Errors:     
01183     See also:   
01184 
01185 ********************************************************************************************/
01186 BOOL OpLiveEffect::SetChanged(BOOL bNewState)
01187 {
01188     if (m_pLERange==NULL)
01189         return TRUE;
01190 
01191     Node* pNode = m_pLERange->FindFirst();
01192     while (pNode)
01193     {
01194         ENSURE(pNode->IsBitmapEffect(), "LiveEffects list contains non-LiveEffect in EnsureLiveEffectOriginalBitmaps");
01195         NodeBitmapEffect* pLE = (NodeBitmapEffect*)pNode;
01196 
01197         pLE->SetChanged(bNewState);
01198 
01199         pNode = m_pLERange->FindNext(pNode);
01200     }
01201 
01202     return TRUE;
01203 }
01204 
01205 
01206 
01207 
01208 /********************************************************************************************
01209 
01210 >   BOOL OpLiveEffect::HasChanged()
01211 
01212     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01213     Created:    30/09/2004
01214     Inputs:     
01215     Outputs:    
01216     Returns:    TRUE if any of the nodes in the liveeffects list have changed since
01217                 this operation started
01218                 FALSE otherwise
01219     Purpose:    Detect whether any of the LiveEffect nodes created/edited by this
01220                 operation have actually been changed/do anything useful...
01221     Errors:     
01222     See also:   
01223 
01224 ********************************************************************************************/
01225 BOOL OpLiveEffect::HasChanged()
01226 {
01227     if (m_pLERange==NULL)
01228         return FALSE;
01229 
01230     Node* pNode = m_pLERange->FindFirst();
01231     while (pNode)
01232     {
01233         ENSURE(pNode->IsBitmapEffect(), "LiveEffects list contains non-LiveEffect in EnsureLiveEffectOriginalBitmaps");
01234         NodeBitmapEffect* pLE = (NodeBitmapEffect*)pNode;
01235 
01236         if (pLE->HasChanged())
01237             return TRUE;
01238 
01239         pNode = m_pLERange->FindNext(pNode);
01240     }
01241 
01242     return FALSE;
01243 }
01244 
01245 
01246 
01247 
01248 /********************************************************************************************
01249 >   static BOOL OpLiveEffect::UpdateCurrentEditor()
01250 
01251     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01252     Created:    20/10/2004
01253     Purpose:    Reset the current Op to refer to the new PPStack and tell the running
01254                 XPE edit that things are changing
01255 
01256 ********************************************************************************************/
01257 
01258 BOOL OpLiveEffect::UpdateCurrentEditor()
01259 {
01260     OpLiveEffect* pCurrentOp = XPEHost::GetCurrentLEOp();
01261 
01262     // If we haven't got a list of editors yet then get one
01263     if (pCurrentOp==NULL)
01264         return FALSE;
01265 
01266     // TODO: May need to force a redraw if the current op is in preview mode at the moment
01267     ENSURE(pCurrentOp->GetPreviewQuality()==FALSE, "We can't leave the bitmaps in the tree in preview mode!");
01268 
01269     // Read the current op's details...
01270     String_256 strEffectID = pCurrentOp->m_strEffectID;
01271     INT32 iStackPos = pCurrentOp->m_iStackPos;
01272 
01273     // Stop the current Op and commit all of its changes
01274     // Disconnect it from the XPE system so that, just for the moment at least, other routines
01275     // think that there's no running edit session
01276     pCurrentOp->DoEndEdit(TRUE, TRUE);              // Sends ScreenChangeMsg - and thus releases pPPStack
01277     XPEHost::SetCurrentLEOp(NULL);                  // Tells UpdateCurrentEditor not to try to update dying editor
01278 
01279     // Reset pPPStack in LiveEffectTool to reset the Tool after the ScreenChangeMsg sent above
01280     // (prevent idle handler looping forever calling this function)
01281     EffectsStack* pPPStack = EffectsStack::GetEffectsStackFromSelection();
01282 
01283     // Try to find the same effect in the new stack
01284     // If found then invoke a new Op to use the running editor on that effect
01285     BOOL bOK = pPPStack->FindBestProcessor(&strEffectID, &iStackPos);
01286 
01287     // Decide which sort of Operation we need to use instead...
01288     OpDescriptor* pOp = NULL;
01289     if (bOK)
01290     {
01291         // We found the right sort of Effect in the stack so we'll just edit that
01292         pOp = OpDescriptor::FindOpDescriptor(OPTOKEN_EDIT_LIVEEFFECT);
01293     }
01294     else
01295     {
01296         // We didn't find the right sort of effect so we'll apply a brand new one!
01297         // At the top of the stack
01298         pOp = OpDescriptor::FindOpDescriptor(OPTOKEN_APPLY_LIVEEFFECT);
01299         iStackPos = STACKPOS_INSERT_TOP;
01300     }
01301 
01302     // Start a new Op but tell it to use the existing edit session instead of making a new one
01303     if (pOp)
01304     {
01305         OpLiveEffectParam Param;
01306         Param.strOpUnique = strEffectID;
01307         Param.StackPosition = iStackPos;
01308         Param.pPPStack = pPPStack;
01309         Param.bReUseEditor = TRUE;              // This is the important param!
01310         BOOL bValid = XPEHost::GetEffectDetails(strEffectID, &Param.strMenuText, &Param.bIsDestructive);
01311 
01312         String_256 strDummy;
01313         if (bValid && pOp->GetOpsState(&strDummy).Greyed==FALSE)
01314         {
01315             pOp->Invoke(&Param);
01316 
01317             // Reset the Tool's current stack pointer now so that it doesn't need
01318             // to be updated under idle events with the consequent call to this function
01319             // and infinite loop...
01320             Tool* pTool = Tool::GetCurrent();
01321             if (pTool->GetID()==TOOLID_LIVEEFFECT)
01322                 ((LiveEffectsTool*)pTool)->UpdatePPStack(FALSE);
01323 
01324             // If that successfully set up a new editor op then we're fine to carry on
01325             if (XPEHost::GetCurrentLEOp()!=NULL)
01326             {
01327                 delete pPPStack;
01328                 return TRUE;
01329             }
01330         }
01331     }
01332 
01333     // If we get here then we must have failed in some way so close down the running editor
01334     XPEHost::EndEditLiveEffect();
01335     delete pPPStack;
01336     return FALSE;
01337 }
01338 
01339 
01340 
01341 
01342 /********************************************************************************************
01343 
01344 >   virtual void OpLiveEffect::DoEndEdit(BOOL bChangingSelection, BOOL bBroadcast, BOOL bCancelled)
01345 
01346     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01347     Created:    16/09/2004
01348     Inputs:     
01349     Outputs:    
01350     Returns:    
01351     Purpose:    
01352     Errors:     
01353     See also:   
01354 
01355 ********************************************************************************************/
01356 void OpLiveEffect::DoEndEdit(BOOL bChangingSelection, BOOL bBroadcast, BOOL bCancelled, BOOL bEmpty)
01357 {
01358     BOOL ok = FALSE;
01359     BOOL bRecache = FALSE;
01360 
01361     // If the are no changes in any of the LiveEffect nodes as a result of this edit
01362     // And we only just created new LiveEffects in this Op
01363     // Then remove them from the tree because they are redundant
01364     if (bCancelled || (m_bAppliedNewEffects && (!HasChanged() || bEmpty)))
01365     {
01366         if (bChangingSelection)
01367             FailAndExecuteIgnoreSelActions();
01368         else
01369             FailAndExecute();
01370         // Tell the selrange that things have changed!
01371         GetApplication()->FindSelection()->Update();
01372         End();
01373         return;
01374     }
01375 
01376     // If we were editing but we are told that the edit has resulted in a NULL effect
01377     // Then we can remove the effect to keep things neat and tidy
01378     if (bEmpty && !m_bAppliedNewEffects && m_pLERange!=NULL)
01379     {
01380         BOOL bOK = DoDeletePostProcessor(this, m_pLERange);
01381         if (!bOK)
01382         {
01383             FailAndExecute();
01384             // Tell the selrange that things have changed!
01385             GetApplication()->FindSelection()->Update();
01386             End();
01387             return;
01388         }
01389     }
01390 
01391     // We have finished with the range of edited nodes now so we should delete it
01392     // to reduce memory usage while this object sits on the undo/redo lists
01393     if (m_pLERange)
01394     {
01395         delete m_pLERange;
01396         m_pLERange = NULL;
01397     }
01398 
01399     // Check whether XPE's state machine ended up in the correct state
01400     if (GetPreviewQuality())
01401     {
01402         ERROR3("We would expect XPE to have ended with at least one non-preview bitmap!");
01403         SetPreviewQuality(FALSE);
01404         bRecache = TRUE;
01405     }
01406 
01407     Tool* pTool = Tool::GetCurrent();
01408 
01409     // Update the LiveEffect Tool's permanent status
01410     if (pTool->GetID()==TOOLID_LIVEEFFECT)
01411     {
01412         ((LiveEffectsTool*)pTool)->SetCurrentEffectID(m_strEffectID);
01413         ((LiveEffectsTool*)pTool)->SetCurrentStackPos(m_iStackPos);
01414     }
01415 
01416     // render blobs off for tools which don't automatically redraw their blobs.
01417     Spread* pSpread = Document::GetSelectedSpread();
01418 
01419     // invalidate the (new) region bounding the selection.
01420     if (!DoInvalidateNodesRegions(*(GetApplication()->FindSelection()), TRUE, FALSE, FALSE, bRecache))
01421     {
01422         End();
01423         return;
01424     }
01425 
01426     // render blobs back on if the current tool doesn't automatically redraw its blobs.
01427     if (pSpread != NULL && pTool != NULL && !pTool->AreToolBlobsRenderedOnSelection())
01428         pTool->RenderToolBlobs(pSpread, NULL);
01429 
01430 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);
01431 ObjChangeParam ObjChange(OBJCHANGE_FINISHED, cFlags, NULL, this);
01432 ok = UpdateChangedNodes(&ObjChange);
01433 
01434     if (bBroadcast)
01435     {
01436         BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::EFFECTSTACKCHANGED));
01437     }
01438 
01439     // Tell the selrange that things have changed!
01440     GetApplication()->FindSelection()->Update();
01441 
01442     End();
01443 }
01444 
01445 
01446 
01447 
01448 /********************************************************************************************
01449 
01450 >   static BOOL OpLiveEffect::DoCopyPPStackToSelection(UndoableOperation* pOp, EffectsStack* pPPStack, Document* pSrcDoc, Document* pDestDoc)
01451 
01452     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01453     Created:    25/10/2004
01454     Inputs:     pPPStack - pointer to EffectsStack to copy
01455     Outputs:    
01456     Returns:    
01457     Purpose:    Copy the specified LiveEffect stack onto the selection
01458                 Used by Paste Attributes
01459     Errors:     
01460     See also:   
01461 
01462 ********************************************************************************************/
01463 
01464 BOOL OpLiveEffect::DoCopyPPStackToSelection(UndoableOperation* pOp, EffectsStack* pPPStack, Document* pSrcDoc, Document* pDestDoc)
01465 {
01466     ERROR2IF(pPPStack==NULL || pPPStack->IsEmpty(), FALSE, "No stack/empty stack given to DoCopyPPStackToSelection\n");
01467 
01468     SelRange* pRange = GetApplication()->FindSelection();
01469     ERROR2IF(pRange==NULL, FALSE, "Can't find SelRange\n");
01470 
01471     if (pPPStack==NULL)
01472         return TRUE;            // That was easy!
01473 
01474     // Convert the pPPStack into a simple ListRange for DoCopyEffectsStack
01475     ListRange EffectsStack;
01476     INT32 l = 0;
01477     PPStackLevel* pLevel = pPPStack->GetLevel(l);
01478     while (pLevel)
01479     {
01480         ERROR3IF(pLevel->pPPNode==NULL, "PPStack has a badly formed level in it");
01481 
01482         if (pLevel->pPPNode->CanBeUnlocked())
01483             EffectsStack.AddNode(pLevel->pPPNode);
01484 
01485         pLevel = pPPStack->GetLevel(++l);
01486     }
01487 
01488     pOp->DoInvalidateNodesRegions(*pRange, TRUE, FALSE);
01489 
01490     // Now loop through the selection
01491     if (!EffectsStack.IsEmpty())
01492     {
01493         Node* pNode = pRange->FindFirst();
01494         Node* pNextNode = NULL;
01495         while (pNode)
01496         {
01497             pNextNode = pRange->FindNext(pNode);
01498 
01499             DoCopyEffectsStack(pOp, pNode, &EffectsStack, pSrcDoc, pDestDoc);
01500 
01501             pNode = pNextNode;
01502         }
01503     }
01504 
01505     pOp->DoInvalidateNodesRegions(*pRange, TRUE, FALSE);
01506 
01507     return TRUE;
01508 }
01509 
01510 
01511 
01512 
01513 /********************************************************************************************
01514 
01515 >   static BOOL OpLiveEffect::DoCopyEffectsStack(UndoableOperation* pOp, Node* pDestNode, ListRange* pEffectsStack, Document* pSrcDoc, Document* pDestDoc)
01516 
01517     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01518     Created:    07/02/2005
01519     Inputs:     pOp - pointer to Op
01520                 pPPStack - pointer to EffectsStack to copy
01521     Outputs:    
01522     Returns:    
01523     Purpose:    Copy the specified LiveEffect stack onto the selection
01524                 Used by Paste Attributes and OpCombineShapes
01525     Errors:     
01526     See also:   
01527 
01528 ********************************************************************************************/
01529 
01530 BOOL OpLiveEffect::DoCopyEffectsStack(UndoableOperation* pOp, Node* pDestNode, ListRange* pEffectsStack, Document* pSrcDoc, Document* pDestDoc)
01531 {
01532     ERROR2IF(pOp==NULL, FALSE, "No Op given to DoCopyEffectsStack\n");
01533     ERROR2IF(pEffectsStack==NULL || pEffectsStack->IsEmpty(), FALSE, "No stack/empty stack given to DoCopyEffectsStack\n");
01534     ERROR2IF(pDestNode==NULL, FALSE, "No destination node given to DoCopyEffectsStack\n");
01535 
01536     //-----------------------------------------------------------------------------
01537     // Remove all current LiveEffects on the selection
01538     //
01539     if (pDestNode->IsAnObject() && !pDestNode->NeedsParent(pDestNode->FindParent()))
01540         pDestNode = DoDeleteAllPostProcessors(pOp, (NodeRenderableInk*)pDestNode, FALSE, TRUE); // TRUE means delete all effects in stack, even those below pDestNode
01541 
01542     //-----------------------------------------------------------------------------
01543     // Apply new LiveEffects from the specified stack
01544     //
01545     BOOL bWasSelected = pDestNode->IsSelected() || pDestNode->IsParentOfSelected();
01546     Node* pSelectNode = pDestNode;
01547     if (pDestNode->IsNodeRenderableClass())
01548         ((NodeRenderable*)pDestNode)->DeSelect(FALSE, TRUE);
01549 
01550     Node* pTopNode = pDestNode;
01551 
01552     NodeEffect* pEffect = (NodeEffect*)pEffectsStack->FindFirst();
01553     while (pEffect)
01554     {
01555         // Test for legacy effect special cases first
01556         // Shadow -------------------------------------------------------------
01557         if (pEffect->IsAShadowController())
01558         {
01559             if (pTopNode->NeedsParent(pTopNode->FindParent()))
01560             {
01561                 if (pTopNode->IsAShadow())
01562                 {
01563                     // TODO: Copy shadow properties here?
01564                 }
01565             }
01566             else
01567             {
01568                 NodeShadowController* pController = (NodeShadowController*)pEffect;
01569 
01570                 NodeShadowParam param(pController->GetShadowType(),
01571                                         pController->GetFloorShadowAngle(),
01572                                         pController->GetFloorShadowHeight(),
01573                                         pController->GetOffsetX(),
01574                                         pController->GetOffsetY(),
01575                                         pController->GetPenumbraWidth(),
01576                                         pController->GetShadow()->GetDarkness(),
01577                                         pController->GetWallShadowScale(),
01578                                         pController->GetGlowWidth(),
01579                                         pController->GetShadow()->GetBiasGain(),
01580                                         TRUE);
01581                 param.strOpUnique = pEffect->GetPostProcessorID();
01582                 param.StackPosition = STACKPOS_INSERT_TOP;
01583                 pTopNode = OpApplyShadow::DoApplyShadow(pOp, pTopNode, &param, FALSE);
01584     //          BOOL ok = pOp->DoFactorOutCommonChildAttributes(pShadControl);
01585                 if (pTopNode == NULL)
01586                 {
01587                     pOp->FailAndExecute();
01588                     return FALSE;
01589                 }
01590 
01591                 // Copy attributes applied to Shadow node
01592                 NodeShadow* pShadow = pController->GetShadow();
01593                 NodeShadow* pNewShadow = NULL;
01594                 if (pTopNode->IsAShadowController())
01595                     pNewShadow = ((NodeShadowController*)pTopNode)->GetShadow();
01596                 ERROR3IF(pShadow==NULL || pNewShadow==NULL, "ShadowController doesn't seem to have a shadow");
01597                 if (pShadow && pNewShadow)
01598                 {
01599                     DoCopyAttributes(pOp, pShadow, pNewShadow, FALSE, pSrcDoc, pDestDoc);
01600                 }
01601 
01602                 delete param.pPPStack;
01603             }
01604         }
01605         // Feather -----------------------------------------------------------------
01606         else if (pEffect->IsFeatherEffect())
01607         {
01608             NodeFeatherEffect* pFeather = (NodeFeatherEffect*)pEffect;
01609             String_64 strDisplayName;
01610             XPEHost::GetEffectDetails(pEffect->GetPostProcessorID(), &strDisplayName);
01611             pTopNode = DoApplyFeatherEffectNode(pOp, pTopNode, pEffect->GetPostProcessorID(), strDisplayName, pFeather->GetFeatherSize(), pFeather->GetProfile());
01612             if (pTopNode == NULL)
01613             {
01614                 pOp->FailAndExecute();
01615                 return FALSE;
01616             }
01617         }
01618         // LiveEffects -------------------------------------------------------------
01619         else if (pEffect->IsBitmapEffect())
01620         {
01621             if (pEffect->IsLockedEffect() && ((NodeLockedEffect*)pEffect)->CanBeUnlocked())
01622             {
01623                 String_64 strDisplayName;
01624                 XPEHost::GetEffectDetails(pEffect->GetPostProcessorID(), &strDisplayName);
01625                 pTopNode = DoApplyLiveEffect(pOp, pTopNode, pEffect->GetPostProcessorID(), strDisplayName, ((NodeBitmapEffect*)pEffect)->GetPixelsPerInchValue(), ((NodeBitmapEffect*)pEffect)->GetEditList(), TRUE);   // MakeLocked
01626                 if (pTopNode == NULL)
01627                 {
01628                     pOp->FailAndExecute();
01629                     return FALSE;
01630                 }
01631             }
01632             else if (!pEffect->IsLockedEffect())
01633             {
01634                 String_64 strDisplayName;
01635                 XPEHost::GetEffectDetails(pEffect->GetPostProcessorID(), &strDisplayName);
01636                 pTopNode = DoApplyLiveEffect(pOp, pTopNode, pEffect->GetPostProcessorID(), strDisplayName, ((NodeBitmapEffect*)pEffect)->GetPixelsPerInchValue(), ((NodeBitmapEffect*)pEffect)->GetEditList(), FALSE);
01637                 if (pTopNode == NULL)
01638                 {
01639                     pOp->FailAndExecute();
01640                     return FALSE;
01641                 }
01642             }
01643             if (pTopNode->IsEffect() && ((NodeEffect*)pTopNode)->IsLockedEffect())
01644             {
01645                 // Don't allow anything to be selected inside a Locked effect!
01646                 pSelectNode = pTopNode;
01647             }
01648         }
01649 
01650         // Copy effect attributes that might be applied to this level
01651         if (pTopNode)
01652         {
01653             DoCopyAttributes(pOp, pEffect, pTopNode, TRUE, pSrcDoc, pDestDoc);
01654         }
01655 
01656         // ----------------------------------------------------------------------
01657         pEffect = (NodeEffect*)pEffectsStack->FindNext(pEffect);
01658     }
01659 
01660     if (pSelectNode && bWasSelected && pSelectNode->IsNodeRenderableClass())
01661     {
01662         ((NodeRenderable*)pSelectNode)->Select(FALSE);
01663     }
01664 
01665     return TRUE;
01666 }
01667 
01668 
01669 
01670 
01671 /********************************************************************************************
01672 
01673 >   static BOOL OpLiveEffect::DoCopyAttributes(UndoableOperation* pOp, Node* pSrcNode, Node* pDestNode, BOOL bEffectsOnly, Document* pSrcDoc, Document* pDestDoc)
01674 
01675     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01676     Created:    21/03/2005
01677     Inputs:     pOp - POinter to Op to add undo records to
01678                 pSrcNode - Node containing attrs to copy
01679                 pDestNode - Node to copy attrs into
01680                 bEffectsOnly - TRUE if should only copy Effect Attrs
01681                 Doc ptrs to allow component data to be copied
01682     Outputs:    
01683     Returns:    
01684     Purpose:    Copy the specified LiveEffect stack onto the selection
01685                 Used by Paste Attributes
01686                 Assumes that only simple attributes will be copied in simple situations
01687                 I.e. attrs that aren't optimsed so much
01688     Errors:     
01689     See also:   
01690 
01691 ********************************************************************************************/
01692 BOOL OpLiveEffect::DoCopyAttributes(UndoableOperation* pOp, Node* pSrcNode, Node* pDestNode, BOOL bEffectsOnly, Document* pSrcDoc, Document* pDestDoc)
01693 {
01694     if (!pDestNode->IsBounded() || !pSrcNode->IsBounded())
01695         return TRUE;
01696 
01697     ((NodeRenderableBounded*)pDestNode)->InvalidateBoundingRect();
01698     DocRect NodeBounds = ((NodeRenderableBounded*)pDestNode)->GetBoundingRect(TRUE);
01699     Node* pNode = pSrcNode->FindFirstChild();
01700     while (pNode)
01701     {
01702         if (pNode->IsAnAttribute())
01703         {
01704             NodeAttribute* pAttr = (NodeAttribute*)pNode;
01705             if (!bEffectsOnly || pAttr->IsEffectAttribute())
01706             {
01707                 if (pAttr->CanBeAppliedToObject())
01708                 {
01709                     NodeAttribute* pAttrCopy = NULL;
01710                     pAttr->NodeCopy((Node **)(&pAttrCopy));
01711 
01712                     if (pAttrCopy->IsAFillAttr())
01713                     {
01714                         // Set the bounds of the attribute. (Why is this wrong here?)
01715                         ((AttrFillGeometry*)pAttrCopy)->SetBoundingRect(((NodeRenderableBounded*)pSrcNode)->GetBoundingRect());
01716 
01717                         // I'm not sure if this is neccessary but Will does it when making an attribute
01718                         // current.
01719                         if (pAttrCopy->IsAFractalFill())
01720                         {
01721                             // The attr, should always use the default DPI for fractals.
01722                             ((AttrFillGeometry*)pAttrCopy)->SetFractalDPI(AttrFillGeometry::FractalDPI);
01723                         }
01724                     }
01725 
01726                     pAttrCopy->TransformToNewBounds(NodeBounds); 
01727                     pAttrCopy->AttachNode(pDestNode, LASTCHILD);
01728                     pAttrCopy->LinkToGeometry(pDestNode);
01729 
01730                     if (pAttrCopy->IsAFillAttr())
01731                     {
01732                         // Now the Attribute is in the tree, we need to tell the fill
01733                         // attribute to check that it's control points are valid.
01734                         // Unless the fill we transformed above, this will usually 
01735                         // involve the fill 'centring' itself within the bounds of its
01736                         // parent.
01737                         ((AttrFillGeometry*)pAttrCopy)->AttributeChanged();
01738                     }
01739 
01740                     // Inform all DocComponents in the destination doc that a copy is about to take place
01741                     BOOL ok;
01742                     CALL_WITH_FAIL((pDestDoc->StartComponentCopy()), pOp, ok)
01743                     if (ok)
01744                     {
01745                         if (!pAttrCopy->CopyComponentData(pSrcDoc, pDestDoc))
01746                         {
01747                             pDestDoc->AbortComponentCopy(); // Cancel all data which has been copied
01748                             ok = FALSE; // stop what were doing
01749                         }
01750 
01751                         if (ok)
01752                             ok = pDestDoc->EndComponentCopy();
01753                     }
01754 
01755                     // Remove similar nodes in this level
01756                     Node* pCurrent = pDestNode->FindFirstChild();
01757                     Node* pNext;
01758                     CCRuntimeClass* pAttrType = pAttrCopy->GetAttributeType();
01759                     while (pCurrent)
01760                     {
01761                         pNext =  pCurrent->FindNext();
01762                         // Determine if the Current node is to be hidden
01763                         if (pCurrent!=pAttrCopy && pCurrent->IsKindOf(CC_RUNTIME_CLASS(NodeAttribute)))
01764                         {
01765                             { 
01766                                 if (((NodeAttribute*)pCurrent)->GetAttributeType() == pAttrType)
01767                                 {
01768                                     pCurrent->CascadeDelete();
01769                                     delete pCurrent;
01770                                 }
01771                             }
01772                         }
01773                         pCurrent = pNext;
01774                     }
01775                 }
01776             }
01777         }
01778 
01779         pNode = pNode->FindNext();
01780     }
01781 
01782     return TRUE;
01783 }
01784 
01785 
01786 
01787 
01788 /********************************************************************************************
01789 
01790 >   static BOOL OpLiveEffect::DoLocaliseLiveEffects(UndoableOperation* pOp, Node* pNode)
01791 
01792     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01793     Created:    25/10/2004
01794     Inputs:     pPPStack - pointer to EffectsStack to copy
01795     Outputs:    
01796     Returns:    
01797     Purpose:    Copy the specified LiveEffect stack onto the selection
01798                 Used by Paste Attributes
01799     Errors:     
01800     See also:   
01801 
01802 ********************************************************************************************/
01803 BOOL OpLiveEffect::DoLocaliseLiveEffects(UndoableOperation* pOp, Node* pNode)
01804 {
01805     ListRange* pPPStack = EffectsStack::GetEffectsStackFromNode(pNode, FALSE, TRUE, TRUE);
01806     if (pPPStack==NULL)
01807         return TRUE;
01808 
01809     NodeRenderableInk* pChild = pNode->FindFirstChildInk();
01810     while (pChild)
01811     {
01812         NodeRenderableInk* pNextChild = pChild->FindNextInk();
01813 
01814         // Don't localise into "controlled" nodes
01815         if (pChild->GetParentController()==NULL)
01816         {
01817             NodeRenderableInk* pInsertContext = pChild;
01818             NodeBitmapEffect* pLENode = (NodeBitmapEffect*)pPPStack->FindFirst();
01819             ERROR2IF(!pLENode->CanBeUnlocked(), FALSE, "Can't localise Destructive effects");
01820             while (pLENode)
01821             {
01822                 // Make a copy of this LiveEffect
01823                 NodeBitmapEffect* pCopy = NULL;
01824                 BOOL bOK = FALSE;
01825                 CALL_WITH_FAIL(pCopy = (NodeBitmapEffect*)pLENode->SimpleCopy(), pOp, bOK);
01826                 if (!bOK) return FALSE; // No room to take a copy of the node
01827 
01828                 // Can't insert directly as parent because of restrictions with HiddenNode
01829                 // So insert as sibling and then move context node into sibling
01830                 bOK = pOp->DoInsertNewNode(pCopy, pInsertContext, NEXT, TRUE, FALSE, FALSE, FALSE);
01831                 if (!bOK) return FALSE;
01832 
01833                 bOK = pOp->DoMoveNode(pInsertContext, pCopy, FIRSTCHILD);
01834                 if (!bOK) return FALSE;
01835 
01836                 pInsertContext = pCopy;
01837 
01838                 // Copy any necessary children of the LiveEffect
01839                 // This could be much more developed, possibly using a virtual function
01840                 // on the node that's just been copied to copy it's necessary parts
01841                 // or ComplexCopy
01842                 // But this works well enough for now (NodeShadowControllers and NodeShadows)
01843                 Node* pNode = pLENode->FindFirstChild();
01844                 AttachNodeDirection dir = FIRSTCHILD;
01845                 while (pNode)
01846                 {
01847                     if (pNode->NeedsParent(pNode->FindParent()) && pNode->IsBounded())
01848                     {
01849                         // Make a copy of this "Needed" node
01850                         NodeRenderableBounded* pCopy = NULL;
01851                         BOOL bOK = FALSE;
01852                         CALL_WITH_FAIL(pNode->NodeCopy((Node**)&pCopy), pOp, bOK);      // Copy node and subtree
01853                         if (!bOK) return FALSE; // No room to take a copy of the node
01854 
01855                         bOK = pOp->DoInsertNewNode(pCopy, pInsertContext, dir, TRUE, FALSE, FALSE, FALSE);
01856                         if (!bOK) return FALSE;
01857                     }
01858 
01859                     pNode = pNode->FindNext();
01860                 }
01861 
01862                 pLENode = (NodeBitmapEffect*)pPPStack->FindNext(pLENode);
01863             }
01864         }
01865 
01866         pChild = pNextChild;
01867     }
01868 
01869     delete pPPStack;
01870 
01871     //-----------------------------------------------------------------------------
01872     // Remove all current LiveEffects on the selection
01873     //
01874     if (pNode->IsAnObject())
01875         return (OpLiveEffect::DoDeleteAllPostProcessors(pOp, (NodeRenderableInk*)pNode)!=NULL);
01876     else
01877         return TRUE;
01878 }
01879 
01880 
01881 
01882 
01883 /********************************************************************************************
01884 
01885 >   static BOOL OpLiveEffect::DoLocaliseEffectAttrs(UndoableOperation* pOp, Node* pNode)
01886 
01887     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01888     Created:    07/03/2005
01889     Inputs:     pOp - pointer to op
01890                 pNode - 
01891     Outputs:    
01892     Returns:    TRUE if it worked
01893     Purpose:    Copy the Effect attributes from the specified node down to lower nodes,
01894                 remaining effect attrs where possible but becoming normal attrs if that's
01895                 not possible
01896                 Used by ungroup
01897     Errors:     
01898     See also:   
01899 
01900 ********************************************************************************************/
01901 BOOL OpLiveEffect::DoLocaliseEffectAttrs(UndoableOperation* pOp, Node* pNode)
01902 {
01903     // From top of stack
01904     // find effect attr
01905     // Copy it down to children of specified node
01906     //   If node can cope with effect attr
01907     //   Then copy as effect attr
01908     //   Else
01909     //     If object already has that type of attr
01910     //     Then do nothing
01911     //     Else copy attribute into place and delete attrs in subtree
01912 
01913     ListRange* pPPStack = EffectsStack::GetEffectsStackFromNode(pNode, FALSE, TRUE, TRUE);
01914     if (pPPStack)
01915     {
01916         Node* pEffectNode = pPPStack->FindLast();
01917         while (pEffectNode)
01918         {
01919             NodeAttribute* pAttr = NodeAttribute::FindFirstAppliedAttr(pEffectNode);
01920             while (pAttr && pAttr->IsEffectAttribute() && pAttr->FindParent()==pEffectNode)
01921             {
01922                 if (!pAttr->HasEquivalentDefaultValue(TRUE))            // Only copy attr if it doesn't look like the default
01923                     DoCopyEffectAttr(pOp, pAttr, pNode);
01924 
01925                 pAttr = NodeAttribute::FindPrevAppliedAttr(pAttr);
01926             }
01927 
01928             pEffectNode = pPPStack->FindPrev(pEffectNode);
01929         }
01930     }
01931 
01932     // Finally, do the same thing for the node itself...
01933     NodeAttribute* pAttr = NodeAttribute::FindFirstAppliedAttr(pNode);
01934     while (pAttr && pAttr->IsEffectAttribute() && pAttr->FindParent()==pNode)
01935     {
01936         if (!pAttr->HasEquivalentDefaultValue(TRUE))                    // Only copy attr if it doesn't look like the default
01937             DoCopyEffectAttr(pOp, pAttr, pNode);
01938 
01939         pAttr = NodeAttribute::FindPrevAppliedAttr(pAttr);
01940     }
01941 
01942     delete pPPStack;
01943 
01944     return TRUE;
01945 }
01946 
01947 
01948 
01949 
01950 /********************************************************************************************
01951 
01952 >   static BOOL OpLiveEffect::DoCopyEffectAttr(UndoableOperation* pOp, NodeAttribute* pAttr, Node* pNode)
01953 
01954     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01955     Created:    07/03/2005
01956     Inputs:     pOp - pointer to op
01957                 pAttr - pointer to effect attribute to copy
01958                 pNode - pointer to node whose children should get copies of the attribute
01959     Outputs:    
01960     Returns:    TRUE if it worked
01961     Purpose:    Copy the Effect attributes from the specified node down to lower nodes,
01962                 remaining effect attrs where possible but becoming normal attrs if that's
01963                 not possible
01964                 Used by ungroup
01965     Errors:     
01966     See also:   
01967 
01968 ********************************************************************************************/
01969 BOOL OpLiveEffect::DoCopyEffectAttr(UndoableOperation* pOp, NodeAttribute* pAttr, Node* pNode)
01970 {
01971     Node* pChildNode = pNode->FindFirstChild();
01972     while (pChildNode)
01973     {
01974         if (pChildNode->IsAnObject())
01975         {
01976             NodeRenderableInk* pInkNode = (NodeRenderableInk*)pChildNode;
01977             if (pInkNode->IsValidEffectAttr(pAttr))
01978             {
01979                 NodeAttribute* pExistingAttr = pInkNode->FindAppliedAttribute(pAttr->GetRuntimeClass());
01980                 if (pExistingAttr==NULL || pExistingAttr->FindParent()!=pInkNode)
01981                 {
01982                     // We can copy our effect attr to pInkNode as an effect attr
01983                     // Make a copy of this LiveEffect
01984                     NodeAttribute* pCopyAttr = NULL;
01985                     BOOL bOK = FALSE;
01986                     CALL_WITH_FAIL(pCopyAttr = (NodeAttribute*)pAttr->SimpleCopy(), pOp, bOK);  
01987                     if (!bOK) return FALSE; // No room to take a copy of the node
01988 
01989 //                  pOp->DoInsertNewNode(pCopyAttr, pInkNode, LASTCHILD, FALSE, FALSE, FALSE, FALSE);
01990 
01991                     // Create a hide node action to hide the node when we undo 
01992                     pCopyAttr->AttachNode(pInkNode, LASTCHILD);
01993                     HideNodeAction* UndoHideNodeAction;
01994                     if (HideNodeAction::Init(pOp, pOp->GetUndoActions(), pCopyAttr, TRUE, (Action**)(&UndoHideNodeAction))
01995                             == AC_FAIL)
01996                     {
01997                         pCopyAttr->CascadeDelete();
01998                         delete pCopyAttr;
01999                         return FALSE;
02000                     }
02001                 }
02002             }
02003             else
02004             {
02005                 NodeAttribute* pExistingAttr = pInkNode->FindAppliedAttribute(pAttr->GetRuntimeClass());
02006                 if (pExistingAttr==NULL || pExistingAttr->FindParent()!=pInkNode)
02007                 {
02008                     // We can copy our effect attrs to pInkNode as a normal attr
02009                     NodeAttribute* pCopyAttr = NULL;
02010                     BOOL bOK = FALSE;
02011                     CALL_WITH_FAIL(pCopyAttr = (NodeAttribute*)pAttr->SimpleCopy(), pOp, bOK);  
02012                     if (!bOK) return FALSE; // No room to take a copy of the node
02013 
02014 //                  pOp->DoInsertNewNode(pCopyAttr, pInkNode, FIRSTCHILD, FALSE, FALSE, FALSE, FALSE);
02015 
02016                     // Create a hide node action to hide the node when we undo 
02017                     pCopyAttr->AttachNode(pInkNode, FIRSTCHILD);
02018                     HideNodeAction* UndoHideNodeAction;
02019                     if (HideNodeAction::Init(pOp, pOp->GetUndoActions(), pCopyAttr, TRUE, (Action**)(&UndoHideNodeAction))
02020                             == AC_FAIL)
02021                     {
02022                         pCopyAttr->CascadeDelete();
02023                         delete pCopyAttr;
02024                         return FALSE;
02025                     }
02026 
02027 //                  if (UndoHideNodeAction)
02028 //                      UndoHideNodeAction->DontPanic();
02029 
02030                     // Remove any deeper attributes of the same type to make a consistent structure
02031                     pOp->DoRemoveAttrTypeFromSubtree(pInkNode, pAttr->GetRuntimeClass(), pCopyAttr);
02032                 }
02033             }
02034         }
02035 
02036         pChildNode = pChildNode->FindNext();
02037     }
02038 
02039     return TRUE;
02040 }
02041 
02042 
02043 
02044 
02045 /********************************************************************************************
02046 
02047 >   virtual Node* OpLiveEffect::DoDeleteAllPostProcessors(UndoableOperation* pOp, NodeRenderableInk* pNode, BOOL bDeleteOldControllers = FALSE, BOOL bDeleteBelowLocked = FALSE)
02048 
02049     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02050     Created:    05/11/2004
02051     Inputs:     pOp - Operation to add undo actions to
02052                 pNode - Selected node (note, not parent of selected)
02053     Outputs:    
02054     Returns:    
02055     Purpose:    
02056     Errors:     
02057     See also:   
02058 
02059 ********************************************************************************************/
02060 Node* OpLiveEffect::DoDeleteAllPostProcessors(UndoableOperation* pOp, NodeRenderableInk* pNode, BOOL bDeleteOldControllers, BOOL bDeleteBelowLocked)
02061 {
02062     BOOL bOK = TRUE;
02063 
02064     // First, find pointer to child node to be moved into place as the LiveEffects stack is deleted
02065     Node* pNewBaseNode = pNode;
02066     if (pNode->IsEffect())
02067     {
02068         ENSURE(((NodeEffect*)pNode)->IsLockedEffect(), "Only destructive LiveEffects should be selectable");
02069         // Find the first ink node that's a child of this LiveEffect
02070         if (bDeleteBelowLocked)
02071         {
02072             // Locked effects can have several children - GetInkNodeFC only returns non-NULL
02073             // when there is a single node below the locked effect
02074             if (((NodeEffect*)pNewBaseNode)->GetInkNodeFromController()==NULL)
02075                 pNewBaseNode = pNewBaseNode->FindFirstChild();
02076             else
02077             {
02078                 Node* pChild = pNewBaseNode;
02079                 while (pChild && pChild->IsEffect())
02080                 {
02081                     pChild = ((NodeEffect*)pChild)->GetInkNodeFromController();
02082 
02083                     if (pChild)
02084                         pNewBaseNode = pChild;
02085                 }
02086             }
02087         }
02088     }
02089 
02090     Node* pHighestLE = NULL;
02091     Node* pEffectScanBase = NULL;
02092 
02093 /*  // [Phil, 22/08/2005] There must be a better way to arrange the following logic!
02094     //
02095     // Push common attributes down into the nodes we're going to retain
02096     Node* pAncestor = pChild->FindParent();
02097     if (pAncestor && !pAncestor->IsAnObject())
02098         pAncestor = NULL;
02099     if (pAncestor)
02100     {
02101         bOK = pOp->DoLocaliseCommonAttributes((NodeRenderableInk*)pAncestor, FALSE, TRUE);
02102         if (!bOK) return FALSE;
02103     }
02104 
02105     // If there are old style controller nodes applied to the object before any effects
02106     // Then get rid of them...
02107     if (bDeleteOldControllers)
02108     {
02109         while (pAncestor && pAncestor->IsController() && !pAncestor->IsEffect())
02110         {
02111             pHighestLE = pAncestor;
02112             pAncestor = pAncestor->FindParent();
02113         }
02114     }
02115     else
02116     {
02117         // Move the BaseNode pointer above the old controllers that we are not deleting
02118         while (pAncestor && pAncestor->IsController() && !pAncestor->IsEffect())
02119         {
02120             pNewBaseNode = pAncestor;
02121             pAncestor = pAncestor->FindParent();
02122         }
02123     }
02124 
02125     // Delete all LiveEffects from pChild upwards
02126     while (pAncestor && pAncestor->IsEffect())
02127     {
02128         pHighestLE = pAncestor;
02129         pAncestor = pAncestor->FindParent();
02130     }
02131 */
02132     // ------------------------------------------------------------------
02133     // Deal with old style controllers
02134     {
02135         Node* pAncestor = pNewBaseNode->FindParent();
02136         Node* pHighestOldController = NULL;
02137         pEffectScanBase = pNewBaseNode;
02138         while (pAncestor && pAncestor->IsController() && !pAncestor->IsEffect())
02139         {
02140             pHighestOldController = pAncestor;
02141             pAncestor = pAncestor->FindParent();
02142         }
02143 
02144         if (pHighestOldController)
02145         {
02146             if (bDeleteOldControllers)
02147                 pEffectScanBase = pHighestOldController;    // Leave base node pointer alone and tell scanner to start above the old controllers
02148             else
02149             {
02150                 pEffectScanBase = pHighestOldController;    // Tell scanner to start above the old controllers
02151                 pNewBaseNode = pHighestOldController;       // but Reset base node pointer to refer to it so we leave it in the tree
02152             }
02153         }
02154     }
02155 
02156     // ------------------------------------------------------------------
02157     // Find highest Effect above the base node
02158     {
02159         Node* pAncestor = pEffectScanBase->FindParent();
02160         while (pAncestor && pAncestor->IsEffect())
02161         {
02162             pHighestLE = pAncestor;
02163             pAncestor = pAncestor->FindParent();
02164         }
02165     }
02166 
02167     // ------------------------------------------------------------------
02168     // Push common attributes down into the nodes we're going to retain (the base node)
02169     {
02170         Node* pAncestor = pNewBaseNode->FindParent();
02171         if (pAncestor && pAncestor->IsAnObject())
02172         {
02173             bOK = pOp->DoLocaliseCommonAttributes((NodeRenderableInk*)pAncestor, FALSE, TRUE);
02174             if (!bOK) return NULL;
02175         }
02176     }
02177 
02178     // ------------------------------------------------------------------
02179     // If we have found Effects above the selected node (then we can
02180     // get rid of them)
02181     if (pHighestLE)
02182     {
02183         // Loop around all the children of the LiveEffect moving them up
02184         // (but note that only Destructive effects can have multiple children...)
02185         // (Er, but note again that effect attributes can be left in the tree...)
02186         Node* pInsertNode = pHighestLE;
02187         Node* pNextChild = NULL;
02188         Node* pChild = pNewBaseNode;
02189         while (pChild)
02190         {
02191             pNextChild = pChild->FindNext();                                // Find next child before we move it!
02192 
02193             if (pChild->IsAnObject())
02194             {
02195                 // Only move pChild if it's not linked to the PostProcessor above it!
02196                 // (Otherwise leave it in place so it gets hidden along with the PostPros)
02197                 if (!((NodeRenderableInk*)pChild)->NeedsParent(pChild->FindParent()))   // Controlled?
02198                 {
02199                     bOK = pOp->DoMoveNode(pChild, pInsertNode, NEXT);
02200                     if (!bOK) return NULL;
02201 
02202                     ((NodeRenderableInk*)pChild)->Select(FALSE);
02203 
02204                     if (pChild->IsAnObject())
02205                         pOp->DoFactorOutCommonAttributes((NodeRenderableInk*)pChild);
02206 
02207                     pInsertNode = pChild;
02208                 }
02209             }
02210 
02211             pChild = (NodeRenderableInk*)pNextChild;
02212         }
02213 
02214         // Now that all the children are moved outside all the LE nodes
02215         // we can delete all the LE nodes (by hiding the root node and thus
02216         // hiding the entire stack at one go!)
02217         if (pHighestLE->IsRenderable())
02218             ((NodeRenderable*)pHighestLE)->DeSelect(FALSE);
02219         bOK = pOp->DoHideNode(pHighestLE, FALSE, NULL, FALSE);
02220     }
02221 
02222     return pNewBaseNode;
02223 }
02224 
02225 
02226 
02227 
02228 /********************************************************************************************
02229 
02230 >   BOOL OpLiveEffect::DoDeletePostProcessor(UndoableOperation* pOp, Range* pRange)
02231 
02232     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02233     Created:    09/11/2004
02234     Inputs:     
02235     Outputs:    
02236     Returns:    
02237     Purpose:    
02238     Errors:     
02239     See also:   
02240 
02241 ********************************************************************************************/
02242 BOOL OpLiveEffect::DoDeletePostProcessor(UndoableOperation* pOp, Range* pRange)
02243 {
02244     BOOL ok = FALSE;
02245 
02246     // We should have a decent list of LiveEffect nodes...
02247     ERROR2IF(pRange==NULL, FALSE, "DoDeletePostProcessor given an empty range");
02248 
02249     // Go through all the LiveEffects, move their children up to be siblings and then
02250     // "delete" the LiveEffect nodes by hiding them.
02251     //
02252     Node* pNode = pRange->FindFirst();
02253     while (pNode)
02254     {
02255         if (pNode->IsEffect())
02256         {
02257             NodeEffect* pLE = (NodeEffect*)pNode;
02258             DoDeletePostProcessor(pOp, pLE);
02259         }
02260 
02261         pNode = pRange->FindNext(pNode);
02262     }
02263 
02264     return TRUE;
02265 }
02266 
02267 
02268 
02269 
02270 /********************************************************************************************
02271 
02272 >   BOOL OpLiveEffect::DoDeletePostProcessor(UndoableOperation* pOp, NodeEffect* pLE, BOOL bMoveEffectAttrs = FALSE)
02273 
02274     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02275     Created:    26/04/2005
02276     Inputs:     
02277     Outputs:    
02278     Returns:    
02279     Purpose:    
02280     Errors:     
02281     See also:   
02282 
02283 ********************************************************************************************/
02284 BOOL OpLiveEffect::DoDeletePostProcessor(UndoableOperation* pOp, NodeEffect* pLE, BOOL bMoveEffectAttrs)
02285 {
02286     BOOL ok = FALSE;
02287 
02288     // We should have a decent list of LiveEffect nodes...
02289     ERROR2IF(pLE==NULL, FALSE, "DoDeletePostProcessor given NULL node");
02290 
02291     // Go through all the LiveEffects, move their children up to be siblings and then
02292     // "delete" the LiveEffect nodes by hiding them.
02293     //
02294     BOOL bSelected = pLE->IsSelected();
02295 
02296     pOp->DoLocaliseCommonAttributes(pLE);
02297 
02298     // Loop around all the children of the LiveEffect moving them up
02299     // (but note that only Destructive effects can have multiple children...)
02300     // (Er, but note again that effect attributes can be left in the tree...)
02301     Node* pInsertNode = pLE;
02302     Node* pNextChild = NULL;
02303     Node* pParent = pLE->FindParent();
02304     NodeEffect* pParentEffect = NULL;
02305     if (pParent && pParent->IsEffect())
02306         pParentEffect = (NodeEffect*)pLE->FindParent();
02307     NodeRenderableInk* pChild = (NodeRenderableInk*)pLE->FindFirstChild(CC_RUNTIME_CLASS(NodeRenderableInk));
02308 
02309     while (pChild)
02310     {
02311         pNextChild = pChild->FindNext();                            // Find next child before we move it!
02312 
02313         if (pChild->IsAnObject())
02314         {
02315             // Only move pChild if it's not linked to the PostProcessor above it!
02316             // (Otherwise leave it in place so it gets hidden along with the PostPros)
02317             if (!((NodeRenderableInk*)pChild)->NeedsParent(pChild->FindParent()))   // Controlled?
02318             {
02319                 pOp->DoMoveNode(pChild, pInsertNode, NEXT);
02320                 if (bSelected)
02321                     pChild->Select(FALSE);
02322                 if (pChild->IsAnObject())
02323                     pOp->DoFactorOutCommonAttributes((NodeRenderableInk*)pChild);
02324 
02325                 pInsertNode = pChild;
02326             }
02327         }
02328 
02329         if (bMoveEffectAttrs && pParentEffect && pChild->IsAnAttribute() && ((NodeAttribute*)pChild)->IsEffectAttribute())
02330         {
02331             if (pParentEffect->IsValidEffectAttr((NodeAttribute*)pChild))
02332             {
02333                 pOp->DoMoveNode(pChild, pInsertNode, NEXT);
02334                 pInsertNode = pChild;
02335             }
02336         }
02337 
02338         pChild = (NodeRenderableInk*)pNextChild;
02339     }
02340 
02341     pLE->ReleaseCached(TRUE, FALSE, FALSE, FALSE);
02342     if (bSelected)
02343         pLE->DeSelect(FALSE);
02344     pOp->DoHideNode(pLE, FALSE, NULL, FALSE);
02345 
02346     return TRUE;
02347 }
02348 
02349 
02350 
02351 
02352 /********************************************************************************************
02353 
02354 >   BOOL OpLiveEffect::DoEffectRangeOp(UINT32 idsProgress, Range* pRange, OpLiveEffectParam* pParam)
02355 
02356     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02357     Created:    08/06/2005
02358     Inputs:     
02359     Outputs:    
02360     Returns:    
02361     Purpose:    
02362     Errors:     
02363     See also:   
02364 
02365 ********************************************************************************************/
02366 BOOL OpLiveEffect::DoEffectRangeOp(UINT32 idsProgress, Range* pRange, OpLiveEffectParam* pParam)
02367 {
02368     BOOL ok = FALSE;
02369     Progress progress(idsProgress, pRange->Count(), FALSE);
02370 
02371     // We should have a decent list of LiveEffect nodes...
02372     ERROR2IF(pRange==NULL, FALSE, "DoEffectRangeOp given an empty range");
02373 
02374     // Go through all the LiveEffects, move their children up to be siblings and then
02375     // "delete" the LiveEffect nodes by hiding them.
02376     //
02377     INT32 iCount = 0;
02378     INT32 iChanges = 0;
02379     Node* pNode = pRange->FindFirst();
02380     while (pNode && progress.Update(iCount, TRUE) && !OpHasFailed())
02381     {
02382         if (pNode->IsEffect())
02383         {
02384             // --------------------------------------------
02385             // This is the core of this function,
02386             // Call overridden specialist function to perform
02387             // the Op on this node alone
02388             NodeEffect* pLE = (NodeEffect*)pNode;
02389             NodeBitmapEffect* pNewEffect = NULL;
02390             BOOL bChanged = DoEffectNodeOp(pLE, pParam, &pNewEffect);
02391             // --------------------------------------------
02392 
02393             if (bChanged)
02394             {
02395                 iChanges++;
02396 
02397                 // Invalidate the region we just changed
02398                 // (Rely on DoEffectNodeop to have released the cache if it thinks it needs to
02399                 if (!DoInvalidateNodeRegion(pNewEffect, TRUE, FALSE, FALSE, FALSE))
02400                 {
02401                     FailAndExecute();
02402                     End();
02403                     return FALSE;
02404                 }
02405 
02406                 // Update the view inside our progress indication so the user gets
02407                 // better idea of where we are...
02408                 DocView* pDocView = GetWorkingDocView();
02409                 if (pDocView)
02410                 {
02411                     // Update just the working view immediately
02412                     pDocView->FlushRedraw();
02413                     GetApplication()->ServiceRendering();
02414                 }
02415             }
02416         }
02417 
02418         iCount++;
02419         pNode = pRange->FindNext(pNode);
02420     }
02421 
02422     // If we aborted with nodes remaining to be done
02423     // Then abort the whole Operation!
02424     if (pNode)
02425     {
02426         FailAndExecute();
02427         End();
02428         return FALSE;
02429     }
02430 
02431 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);
02432 ObjChangeParam ObjChange(OBJCHANGE_FINISHED, cFlags, NULL, this);
02433 ok = UpdateChangedNodes(&ObjChange);
02434 
02435     // If we didn't change anything then don't add this op to the undo history
02436     if (iChanges==0)
02437     {
02438         SucceedAndDiscard();
02439         End();
02440         return FALSE;
02441     }
02442 
02443     End();
02444     return FALSE;           // We don't need any of the normal tidy up to occur
02445 }
02446 
02447 
02448 
02449 
02450 /********************************************************************************************
02451 
02452 >   virtual void OpLiveEffect::Do(OpDescriptor* pOpDesc)
02453 
02454     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02455     Created:    08/06/2005
02456     Inputs:     
02457     Outputs:    
02458     Returns:    
02459     Purpose:    
02460     Errors:     
02461     See also:   
02462 
02463 ********************************************************************************************/
02464 void OpLiveEffect::Do(OpDescriptor* pOpDesc)
02465 {
02466 //  ERROR3("Why are you doing this? You need params, boy, params!");
02467     DoWithParam(pOpDesc, NULL);
02468 }
02469 
02470 
02471 
02472 
02473 /********************************************************************************************
02474 
02475 >   virtual void OpLiveEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
02476 
02477     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02478     Created:    08/06/2005
02479     Inputs:     
02480     Outputs:    
02481     Returns:    
02482     Purpose:    
02483     Errors:     
02484     See also:   
02485 
02486 ********************************************************************************************/
02487 void OpLiveEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
02488 {
02489     BOOL ok = FALSE;
02490 
02491     OpLiveEffectParam* pLEOpParam = NULL;
02492     if (pOpParam && pOpParam->IsKindOf(CC_RUNTIME_CLASS(OpLiveEffectParam)))
02493         pLEOpParam = (OpLiveEffectParam*) pOpParam;
02494 
02495     if (!CheckEffectOpParam(pLEOpParam))
02496     {
02497         End();
02498         return;
02499     }
02500 
02501     // Remember some details for later on when the Op ends successfully (?)
02502     if (pLEOpParam)
02503     {
02504         m_strEffectID = pLEOpParam->strOpUnique;
02505         m_iStackPos = pLEOpParam->StackPosition;
02506     }
02507 
02508     // obtain the current selection.
02509     Range Sel(*(GetApplication()->FindSelection()));
02510     RangeControl rc = Sel.GetRangeControlFlags();
02511     rc.PromoteToParent = TRUE;
02512     Sel.Range::SetRangeControl(rc);
02513 
02514     if (!AllowEmptySelection() && Sel.FindFirst()==NULL)
02515     {
02516         End();
02517         return;
02518     }
02519 
02520     // render blobs off for tools which don't automatically redraw their blobs.
02521     Tool* pTool = Tool::GetCurrent();
02522     Spread* pSpread = Document::GetSelectedSpread();
02523     if (pSpread != NULL && pTool != NULL && !pTool->AreToolBlobsRenderedOnSelection())
02524         pTool->RenderToolBlobs(pSpread, NULL);
02525 
02526     // record the current selection state and if required, render off any selection blobs.
02527     if (!DoStartSelOp(FALSE, FALSE))
02528     {
02529         End();
02530         return;
02531     }
02532 
02533 // Ask the range whether it's OK to do the transform...
02534 // Get an ObjChangeParam ready, so we can ask op permission from nodes.
02535 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);  // ReplaceNode & RegenerateNode
02536 ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, this);
02537 BOOL bOK = Sel.AllowOp(&ObjChange);
02538 if (!bOK)
02539 {
02540     End();
02541     return;
02542 }
02543 
02544     // invalidate the region bounding the selection.
02545     if (!DoInvalidateNodesRegions(Sel, TRUE, FALSE, FALSE, FALSE))
02546     {
02547         End();
02548         return;
02549     }
02550 
02551     // Get a list of the LiveEffects we will be working on
02552     ListRange* pLERange = NULL;
02553     if (pLEOpParam && pLEOpParam->pPPStack)
02554     {
02555         pLERange = pLEOpParam->pPPStack->GetLevelRange(&pLEOpParam->StackPosition); // We don't own the returned range
02556         // Make a local copy in case the original dies during selection processing
02557         if (pLERange)
02558             pLERange = new ListRange(pLERange);
02559     }
02560     m_bAppliedNewEffects = FALSE;
02561 
02562     // -------------------------------------------------------------------
02563     // Call virtual function of derived Ops to do the specialised function
02564     BOOL bEndNormally = DoEffectOp(pLERange, pLEOpParam);
02565     // -------------------------------------------------------------------
02566 
02567     if (pLERange)
02568     {
02569         delete pLERange;
02570         pLERange = NULL;
02571     }
02572 
02573     if (bEndNormally)
02574     {
02575         // invalidate the (new) region bounding the selection.
02576         if (!DoInvalidateNodesRegions(*(GetApplication()->FindSelection()), TRUE, FALSE, FALSE, FALSE))
02577         {
02578             End();
02579             return;
02580         }
02581 
02582         // render blobs back on if the current tool doesn't automatically redraw its blobs.
02583         if (pSpread != NULL && pTool != NULL && !pTool->AreToolBlobsRenderedOnSelection())
02584             pTool->RenderToolBlobs(pSpread, NULL);
02585 
02586 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);
02587 ObjChangeParam ObjChange(OBJCHANGE_FINISHED, cFlags, NULL, this);
02588 BOOL ok = UpdateChangedNodes(&ObjChange);
02589 
02590         BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::EFFECTSTACKCHANGED)); 
02591 
02592         // Tell the selrange that things have changed!
02593         GetApplication()->FindSelection()->Update();
02594 
02595         End();
02596     }
02597 }
02598 
02599 
02600 
02601 
02602 /********************************************************************************************
02603 
02604 >   virtual BOOL OpLiveEffect::CheckEffectOpParam(OpLiveEffectParam* pLEOpParam)
02605 
02606     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02607     Created:    08/06/2005
02608     Inputs:     pLEopParam - pointer to parameter object to check
02609     Outputs:    -
02610     Returns:    TRUE if Op can continue
02611                 FALSE otherwise
02612     Purpose:    
02613 
02614 ********************************************************************************************/
02615 BOOL OpLiveEffect::CheckEffectOpParam(OpLiveEffectParam* pLEOpParam)
02616 {
02617     if (pLEOpParam->StackPosition==-1)
02618     {
02619         ERROR3("OpLiveEffect must be given a valid position in the LiveEffects Stack");
02620         return FALSE;
02621     }
02622 
02623 //  ERROR3IF(pLEOpParam->pPPStack==NULL, "Can't find EffectsStack");
02624     if (pLEOpParam->pPPStack==NULL)
02625         return FALSE;
02626 
02627     return TRUE;
02628 }
02629 
02630     
02631     
02632     
02634 // The OpApplyLiveEffect class                                                               //
02636 
02637 /********************************************************************************************
02638 
02639 >   static OpState OpApplyLiveEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
02640 
02641     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02642     Created:    07/09/2004
02643     Inputs:     
02644     Outputs:    
02645     Returns:    
02646     Purpose:    Return state of the specified LiveEffect (name in pstrDescription) being
02647                 applied to the selection (taking into account the current position in the
02648                 LiveEffect Stack.
02649     Errors:     
02650     See also:   
02651 
02652 ********************************************************************************************/
02653 OpState OpApplyLiveEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
02654 {
02655     // default is an unticked, *GREYED*, on-menu state.
02656     OpState OpSt;
02657     OpSt.Greyed = FALSE;
02658 
02659     if (XPEHost::IsEditSessionRunning())
02660     {
02661         OpSt.Greyed = TRUE;
02662         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
02663     }
02664     else if (GetApplication()->FindSelection()->FindFirst()==NULL)
02665     {
02666         OpSt.Greyed = TRUE;
02667         *pstrDescription = String_256(_R(IDS_LE_NOTHING_SELECTED));
02668     }
02669 
02670     return OpSt;
02671 }
02672 
02673 
02674 
02675 /********************************************************************************************
02676 
02677 >   virtual void OpApplyLiveEffect::GetOpName(String_256* pstrOpName)
02678 
02679     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02680     Created:    07/09/2004
02681     Inputs:     
02682     Outputs:    
02683     Returns:    
02684     Purpose:    
02685     Errors:     
02686     See also:   
02687 
02688 ********************************************************************************************/
02689 void OpApplyLiveEffect::GetOpName(String_256* pstrOpName)
02690 {
02691     *pstrOpName = String_256(_R(IDS_LE_OPAPPLY));
02692 }
02693 
02694 
02695 
02696 /********************************************************************************************
02697 
02698 >   virtual void OpApplyLiveEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
02699 
02700     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02701     Created:    10/09/2004
02702     Inputs:     
02703     Outputs:    
02704     Returns:    
02705     Purpose:    Insert a new LiveEffect attribute in the stack above the selected object(s)
02706     Errors:     
02707     See also:   
02708 
02709 ********************************************************************************************/
02710 void OpApplyLiveEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
02711 {
02712     BOOL ok = FALSE;
02713 
02714     OpLiveEffectParam* pLEOpParam = NULL;
02715     if (pOpParam->IsKindOf(CC_RUNTIME_CLASS(OpLiveEffectParam)))
02716         pLEOpParam = (OpLiveEffectParam*) pOpParam;
02717 
02718     INT32 InsertPos = pLEOpParam->StackPosition;        // We want to find the surface below the current pos
02719     if (pLEOpParam->StackPosition<STACKPOS_TOP)
02720         InsertPos = InsertPos - 1;                  // We want to find the surface below the current pos
02721                                                     // because we are going to insert above that surface
02722 
02723     // Remember some details for later on when the Op ends successfully
02724     m_strEffectID = pLEOpParam->strOpUnique;
02725     m_iStackPos = InsertPos;
02726     if (m_strEffectID.IsEmpty())
02727     {
02728         End();
02729         return;
02730     }
02731 
02732     // obtain the current selection.
02733     SelRange* pSelRange = GetApplication()->FindSelection();
02734 
02735     Range* pInitialRange = NULL;
02736 
02737     // render blobs off for tools which don't automatically redraw their blobs.
02738     Tool* pTool = Tool::GetCurrent();
02739     Spread* pSpread = Document::GetSelectedSpread();
02740     if (pSpread != NULL && pTool != NULL && !pTool->AreToolBlobsRenderedOnSelection())
02741         pTool->RenderToolBlobs(pSpread, NULL);
02742 
02743     // record the current selection state and if required, render off any selection blobs.
02744     if (!DoStartSelOp(FALSE, FALSE))
02745     {
02746         End();
02747         return;
02748     }
02749 
02750     // We need to allow modeless editors to open even when there is no selection so that
02751     // they will remain open while the user changes the selection
02752     // So we must avoid calling AllowOp in that case because it returns bOK = FALSE for empty selections
02753     if (!(pLEOpParam->bReUseEditor && pSelRange->IsEmpty()))
02754     {
02755         // Ask the range whether it's OK to do the transform...
02756         // Get an ObjChangeParam ready, so we can ask op permission from nodes.
02757         ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);  // ReplaceNode & RegenerateNode
02758         ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, this);
02759         BOOL bOK = pSelRange->AllowOp(&ObjChange);
02760         if (!bOK)
02761         {
02762             End();
02763             return;
02764         }
02765     }
02766 
02767     // invalidate the region bounding the selection.
02768     if (!DoInvalidateNodesRegions(*pSelRange, TRUE, FALSE, FALSE, FALSE))
02769     {
02770         End();
02771         return;
02772     }
02773 
02774     // Force text subselection up to the whole parent text object
02775     pSelRange->MakePartialSelectionWhole(FALSE, FALSE, TRUE);
02776 
02777     // Get a list of the LiveEffects we will be working on
02778     ENSURE(m_pLERange==NULL, "!");
02779     if (InsertPos==-1)
02780     {
02781         // We are to apply the LiveEFfects to the selection
02782         pInitialRange = GetApplication()->FindSelection();
02783         m_iStackPos = 0;
02784     }
02785     else
02786     {
02787         ENSURE(pLEOpParam->pPPStack, "Can't find EffectsStack");
02788         pInitialRange = pLEOpParam->pPPStack->GetLevelRange(&InsertPos);
02789         m_iStackPos = InsertPos+1;          // Remember some details for later on when the Op ends successfully
02790                                             // (New stack pos refers to item to be shown in UI after successful insertion)
02791     }
02792 
02793     // Check whether we can edit an existing effect or whether we have to apply a new one
02794     if (pInitialRange==NULL)
02795     {
02796         ERROR3("If this happens, the UI logic disagrees with the Op about what the Op can do");
02797         FailAndExecute();
02798         End();
02799         return;
02800     }
02801 
02802     // Need to add a new LiveEffects node to the selection
02803     // Decide whether we need to create a group or can apply to
02804     // each selected object individually
02805     if (pLEOpParam && !pLEOpParam->bIsDestructive)
02806     {
02807         if (NodeBitmapEffect::DefaultLocked)
02808             m_pLERange = DoApplyLiveEffect(pInitialRange, pLEOpParam->strOpUnique, pLEOpParam->strMenuText, NodeBitmapEffect::DefaultLockedPixelsPerInch, pLEOpParam->pEditsList, TRUE, TRUE);
02809         else
02810             m_pLERange = DoApplyLiveEffect(pInitialRange, pLEOpParam->strOpUnique, pLEOpParam->strMenuText, NodeBitmapEffect::DefaultLivePixelsPerInch, pLEOpParam->pEditsList, FALSE, TRUE);
02811     }
02812     else
02813     {
02814         // Destructive effects can only be applied on the top of the stack
02815         // because of the grouping that they do
02816         if (!IsTopOfStack(pLEOpParam->pPPStack, pLEOpParam->StackPosition))                     // InsertPos?
02817         {
02818             InformWarning(_R(IDS_LE_DESTRUCTIVE_APPLY_TOP), _R(IDS_CONTINUE));
02819             FailAndExecute();
02820             End();
02821             return;
02822         }
02823         else
02824         {
02825             m_pLERange = DoApplyLockedEffect(pInitialRange, pLEOpParam->strOpUnique, pLEOpParam->strMenuText, NodeBitmapEffect::DefaultLockedPixelsPerInch, NULL, TRUE);
02826         }
02827     }
02828     m_bAppliedNewEffects = (m_pLERange!=NULL);
02829 
02830     // We should now have a decent list of LiveEffect nodes...
02831     if (pLEOpParam->pEditsList==NULL)
02832     {
02833         // Mark all LiveEffects as untouched at this stage for change detection later on...
02834         SetChanged(FALSE);
02835 
02836         // Make sure we've got all the bitmaps we need to proceed
02837         ok = EnsureLiveEffectOriginalBitmaps();
02838         if (!ok)
02839         {
02840             FailAndExecute();
02841             End();
02842             return;
02843         }
02844         
02845         // OK, LiveEffects nodes are in the tree and have the original bitmaps that we need
02846         //
02847         // Now decide which bitmap to pass to XPE
02848         NodeBitmapEffect* pLE = NULL;
02849         if (m_pLERange)
02850         {
02851             Node* pNode = m_pLERange->FindLast();
02852             while (pNode && !(pNode->IsBitmapEffect() && ((NodeBitmapEffect*)pNode)->CanGenerateBitmap()))
02853                 pNode = m_pLERange->FindPrev(pNode);
02854 
02855             if (pNode==NULL)
02856             {
02857                 FailAndExecute();
02858                 End();
02859                 return;
02860             }
02861 
02862             pLE = (NodeBitmapEffect*)pNode;
02863         }
02864 
02865         // ----------------------------------------------
02866         // This is the important bit!
02867         //
02868         // Pass the sample bitmap to the XPE for editing
02869         // REMEMBER! This is only starting an edit - the results won't be known until later
02870         //
02871         ok = XPEHost::EditLiveEffect(this, pLE, pLEOpParam->strOpUnique, pLEOpParam->bReUseEditor);
02872         if (!ok)
02873         {
02874             FailAndExecute();
02875             End();
02876             return;
02877         }
02878 
02879         GetApplication()->FindSelection()->Update();
02880 
02881         // DON'T end the operation!
02882         // It is INT32-running (wait for someone to call DoEndEdit...)
02883     }
02884     else
02885     {
02886         // invalidate the (new) region bounding the selection.
02887         DoInvalidateNodesRegions(*pSelRange, TRUE, FALSE, FALSE);
02888 
02889         // render blobs back on if the current tool doesn't automatically redraw its blobs.
02890         if (pSpread != NULL && pTool != NULL && !pTool->AreToolBlobsRenderedOnSelection())
02891             pTool->RenderToolBlobs(pSpread, NULL);
02892 
02893 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);
02894 ObjChangeParam ObjChange(OBJCHANGE_FINISHED, cFlags, NULL, this);
02895 BOOL ok = UpdateChangedNodes(&ObjChange);
02896 
02897         BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::EFFECTSTACKCHANGED)); 
02898 
02899         // Tell the selrange that things have changed!
02900         GetApplication()->FindSelection()->Update();
02901 
02902         End();
02903     }
02904 }
02905 
02906 
02907 
02909 // The OpEditLiveEffect class                                                                //
02911 
02912 /********************************************************************************************
02913 
02914 >   static OpState OpEditLiveEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
02915 
02916     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02917     Created:    07/09/2004
02918     Inputs:     
02919     Outputs:    
02920     Returns:    
02921     Purpose:    Return state of the specified LiveEffect (name in pstrDescription) being
02922                 applied to the selection (taking into account the current position in the
02923                 LiveEffect Stack.
02924     Errors:     
02925     See also:   
02926 
02927 ********************************************************************************************/
02928 OpState OpEditLiveEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
02929 {
02930     // default is an unticked, *GREYED*, on-menu state.
02931     OpState OpSt;
02932     OpSt.Greyed = FALSE;
02933 
02934 /*  if (XPEHost::IsEditSessionRunning())
02935     {
02936         OpSt.Greyed = TRUE;
02937         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
02938     }
02939     else*/ if (GetApplication()->FindSelection()->FindFirst()==NULL)
02940     {
02941         OpSt.Greyed = TRUE;
02942         *pstrDescription = String_256(_R(IDS_LE_NOTHING_SELECTED));
02943     }
02944 
02945     return OpSt;
02946 }
02947 
02948 
02949 
02950 /********************************************************************************************
02951 
02952 >   static OpState OpEditLiveEffect::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
02953 
02954     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02955     Created:    10/09/2004
02956     Inputs:     
02957     Outputs:    
02958     Returns:    
02959     Purpose:    
02960     Errors:     
02961     See also:   
02962 
02963 ********************************************************************************************/
02964 /*OpState OpEditLiveEffect::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
02965 {
02966     // default is an unticked, *GREYED*, on-menu state.
02967     OpState OpSt;
02968     OpSt.Greyed = FALSE;
02969     *pstrDescription = String_256(_R(IDS_DESCRIBE_LIVEEFFECT));
02970 
02971     if (XPEHost::IsEditSessionRunning())
02972     {
02973         OpSt.Greyed = TRUE;
02974         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
02975     }
02976 
02977     return OpSt;
02978 }*/
02979 
02980 
02981 
02982 /********************************************************************************************
02983 
02984 >   virtual void OpEditLiveEffect::GetOpName(String_256* pstrOpName)
02985 
02986     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02987     Created:    10/09/2004
02988     Inputs:     
02989     Outputs:    
02990     Returns:    
02991     Purpose:    
02992     Errors:     
02993     See also:   
02994 
02995 ********************************************************************************************/
02996 void OpEditLiveEffect::GetOpName(String_256* pstrOpName)
02997 {
02998     *pstrOpName = String_256(_R(IDS_LE_OPEDIT));
02999 }
03000 
03001 
03002 
03003 
03004 /********************************************************************************************
03005 
03006 >   virtual BOOL OpEditLiveEffect::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03007 
03008     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03009     Created:    10/09/2004
03010     Inputs:     
03011     Outputs:    
03012     Returns:    
03013     Purpose:    
03014     Errors:     
03015     See also:   
03016 
03017 ********************************************************************************************/
03018 BOOL OpEditLiveEffect::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03019 {
03020     m_pLERange = new ListRange(pLevelRange);    // Grab ownership of returned list by copying it
03021     BOOL ok = FALSE;
03022 
03023     // Find out whether the user is trying to edit a destructive effect...
03024     {
03025         Node* pNode = m_pLERange->FindFirst();
03026         INT32 count=0;
03027         BOOL bDestructive = FALSE;
03028         BOOL bLocked = FALSE;
03029         while (pNode)
03030         {
03031             if (pNode->IsBitmapEffect() && ((NodeBitmapEffect*)pNode)->IsLockedEffect())
03032             {
03033                 bLocked = TRUE;
03034                 if (!((NodeLockedEffect*)pNode)->CanBeUnlocked())
03035                     bDestructive = TRUE;
03036             }
03037 
03038             count++;
03039             pNode = m_pLERange->FindNext(pNode);
03040         }
03041         if (bDestructive)
03042         {
03043             ENSURE(count==1, "Shouldn't allow user to edit more than one destructive effects at a time - greying should prevent this");
03044 
03045             INT32 butt = AskQuestion(_R(IDS_LE_DESTRUCTIVE_EDIT), _R(IDS_LE_RECREATEEFFECT), _R(IDS_CANCEL));
03046             if (butt==2)
03047             {
03048                 FailAndExecute();
03049                 End();
03050                 return FALSE;
03051             }
03052 
03053             // We need to abandon this edit, delete the destructive effects and go re-apply new ones
03054             // by calling OpApplyLiveEffect
03055             //
03056             // Record list of nodes under this effect list before we delete it
03057             ListRange* pRangeUnderDeletedEffect = new ListRange();
03058             if (pRangeUnderDeletedEffect==NULL)
03059             {
03060                 FailAndExecute();
03061                 End();
03062                 return FALSE;
03063             }
03064             Node* pNode = m_pLERange->FindFirst();
03065             while (pNode)
03066             {
03067                 if (pNode->IsBitmapEffect())
03068                 {
03069                     Node* pChild = ((NodeBitmapEffect*)pNode)->GetInkNodeFromController();
03070                     if (pChild) pRangeUnderDeletedEffect->AddNode(pChild);
03071                 }
03072                 else
03073                     ERROR3("Something's gone horribly wrong - non bitmap effect foudn in list of locked effects to be deleted\n");
03074 
03075                 pNode = m_pLERange->FindNext(pNode);
03076             }
03077             if (pRangeUnderDeletedEffect->IsEmpty())
03078             {
03079                 ERROR3("List of nodes under locked effect unexpectedly empty\n");
03080                 FailAndExecute();
03081                 End();
03082                 return FALSE;
03083             }
03084 
03085             // Get rid of this edit list before calling XPE
03086             ok = OpLiveEffect::DoDeletePostProcessor(this, m_pLERange);
03087             if (ok)
03088             {
03089                 // OK, now insret new effects in place fo the ones we just deleted
03090                 // (Leave m_iStackPos alone because it must, by definition, remain the same)
03091                 delete m_pLERange;
03092                 m_pLERange = DoApplyLockedEffect(pRangeUnderDeletedEffect, pLEOpParam->strOpUnique, pLEOpParam->strMenuText, NodeBitmapEffect::DefaultLockedPixelsPerInch);
03093                 m_bAppliedNewEffects = (m_pLERange!=NULL);
03094             }
03095             else
03096             {
03097                 FailAndExecute();
03098                 End();
03099                 return FALSE;
03100             }
03101             if (pRangeUnderDeletedEffect)
03102             {
03103                 delete pRangeUnderDeletedEffect;
03104                 pRangeUnderDeletedEffect = NULL;
03105             }
03106         }
03107         else
03108         {
03109             // Record the current state of this edit list before calling XPE
03110             // Mark this point in the LiveEffect's life ("on my mark") so that it can be undone
03111             // back to this point
03112             ActionCode ac = MarkEditListAction::DoMarkEditList(this, &UndoActions, pLEOpParam->pPPStack, pLEOpParam->StackPosition);
03113         }
03114     }
03115 
03116     // Mark all LiveEffects as untouched at this stage for change detection later on...
03117     SetChanged(FALSE);
03118 
03119     // Make sure we've got all the bitmaps we need to proceed
03120     ok = EnsureLiveEffectOriginalBitmaps();
03121     if (!ok)
03122     {
03123         FailAndExecute();
03124         End();
03125         return FALSE;
03126     }
03127     
03128     // OK, LiveEffects nodes are in the tree and have the original bitmaps that we need
03129     //
03130     // Now decide which bitmap to pass to XPE
03131     NodeBitmapEffect* pLE = NULL;
03132     if (m_pLERange)
03133     {
03134         Node* pNode = m_pLERange->FindLast();
03135         while (pNode && !(pNode->IsBitmapEffect() && ((NodeBitmapEffect*)pNode)->CanGenerateBitmap()))
03136             pNode = m_pLERange->FindPrev(pNode);
03137 
03138         if (pNode==NULL)
03139         {
03140             FailAndExecute();
03141             End();
03142             return FALSE;
03143         }
03144 
03145         pLE = (NodeBitmapEffect*)pNode;
03146     }
03147 
03148     // ----------------------------------------------
03149     // This is the important bit!
03150     //
03151     // Pass the sample bitmap to the XPE for editing
03152     // REMEMBER! This is only starting an edit - the results won't be known until later
03153     //
03154     ok = XPEHost::EditLiveEffect(this, pLE, pLEOpParam->strOpUnique, pLEOpParam->bReUseEditor);
03155     if (!ok)
03156     {
03157         FailAndExecute();
03158         End();
03159         return FALSE;
03160     }
03161 
03162     GetApplication()->FindSelection()->Update();
03163 
03164     // DON'T end the operation!
03165     // Someone will call DoEndEdit
03166     return FALSE;
03167 }
03168 
03169 
03170 
03171 
03173 // The OpEditLegacyEffect class                                                              //
03175 
03176 /********************************************************************************************
03177 
03178 >   static OpState OpEditLegacyEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
03179 
03180     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03181     Created:    08/08/2005
03182     Inputs:     
03183     Outputs:    
03184     Returns:    
03185     Purpose:    -
03186     Errors:     
03187     See also:   
03188 
03189 ********************************************************************************************/
03190 OpState OpEditLegacyEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
03191 {
03192     // default is an unticked, *GREYED*, on-menu state.
03193     OpState OpSt;
03194     OpSt.Greyed = FALSE;
03195 
03196     if (XPEHost::IsEditSessionRunning())
03197     {
03198         OpSt.Greyed = TRUE;
03199         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
03200     }
03201     else if (GetApplication()->FindSelection()->FindFirst()==NULL)
03202     {
03203         OpSt.Greyed = TRUE;
03204         *pstrDescription = String_256(_R(IDS_LE_NOTHING_SELECTED));
03205     }
03206 
03207     return OpSt;
03208 }
03209 
03210 
03211 
03212 /********************************************************************************************
03213 
03214 >   virtual void OpEditLegacyEffect::GetOpName(String_256* pstrOpName)
03215 
03216     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03217     Created:    08/08/2005
03218     Inputs:     
03219     Outputs:    
03220     Returns:    
03221     Purpose:    
03222     Errors:     
03223     See also:   
03224 
03225 ********************************************************************************************/
03226 void OpEditLegacyEffect::GetOpName(String_256* pstrOpName)
03227 {
03228     *pstrOpName = String_256(_R(IDS_LE_OPEDIT));
03229 }
03230 
03231 
03232 
03233 
03234 /********************************************************************************************
03235 
03236 >   virtual void OpEditLegacyEffect::Do(OpDescriptor* pOpDesc)
03237 
03238     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03239     Created:    08/08/2005
03240     Inputs:     
03241     Outputs:    
03242     Returns:    
03243     Purpose:    
03244     Errors:     
03245     See also:   
03246 
03247 ********************************************************************************************/
03248 void OpEditLegacyEffect::Do(OpDescriptor* pOpDesc)
03249 {
03250 //  ERROR3("Why are you doing this? You need params, boy, params!");
03251     DoWithParam(pOpDesc, NULL);
03252 }
03253 
03254 
03255 
03256 
03257 /********************************************************************************************
03258 
03259 >   virtual void OpEditLegacyEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
03260 
03261     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03262     Created:    08/08/2005
03263     Inputs:     
03264     Outputs:    
03265     Returns:    
03266     Purpose:    
03267     Errors:     
03268     See also:   
03269 
03270 ********************************************************************************************/
03271 void OpEditLegacyEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
03272 {
03273     BOOL ok = FALSE;
03274 
03275     OpLiveEffectParam* pLEOpParam = NULL;
03276     if (pOpParam && pOpParam->IsKindOf(CC_RUNTIME_CLASS(OpLiveEffectParam)))
03277         pLEOpParam = (OpLiveEffectParam*) pOpParam;
03278 
03279     // Remember some details for later on when the Op ends successfully (?)
03280     if (pLEOpParam)
03281     {
03282         if (pLEOpParam->strOpUnique==String_256(POSTPRO_ID_SHADOW))
03283         {
03284             // Make sure the Shadow tool is current... (this func can be called from other tools)
03285             Tool* pShadowTool = Tool::FindTool(TOOLID_SOFTSHADOW);
03286             if (pShadowTool)
03287             {
03288                 INT32 iStackPos = pLEOpParam->StackPosition;
03289                 ListRange* pShadowRange = pLEOpParam->pPPStack->GetLevelRange(&iStackPos);
03290                 if (pShadowRange)
03291                 {
03292                     ((SoftShadowTool*)pShadowTool)->SelectShadowRange(pShadowRange);
03293                 }
03294 
03295                 pShadowTool->SetCurrent();
03296                 End();
03297                 return;
03298             }
03299         }
03300 
03301         if (pLEOpParam->strOpUnique==String_256(POSTPRO_ID_FEATHER))
03302         {
03303             INT32 iStackPos = pLEOpParam->StackPosition;
03304             ListRange* pFeatherRange = pLEOpParam->pPPStack->GetLevelRange(&iStackPos);
03305             OpChangeFeatherSize::SetEditContext(iStackPos, pFeatherRange);
03306             End();
03307             return;
03308         }
03309     }
03310 
03311     End();
03312 }
03313 
03314 
03315 
03316 
03318 // The OpDeleteLiveEffect class                                                              //
03320 
03321 /********************************************************************************************
03322 
03323 >   static OpState OpDeleteLiveEffect::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03324 
03325     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03326     Created:    10/09/2004
03327     Inputs:     
03328     Outputs:    
03329     Returns:    
03330     Purpose:    
03331     Errors:     
03332     See also:   
03333 
03334 ********************************************************************************************/
03335 OpState OpDeleteLiveEffect::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03336 {
03337     // default is an unticked, *GREYED*, on-menu state.
03338     OpState OpSt;
03339     OpSt.Greyed = FALSE;
03340     *pstrDescription = String_256(_R(IDS_DESCRIBE_LIVEEFFECT));
03341 
03342     if (XPEHost::IsEditSessionRunning())
03343     {
03344         OpSt.Greyed = TRUE;
03345         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
03346     }
03347 
03348     return OpSt;
03349 }
03350 
03351 
03352 
03353 /********************************************************************************************
03354 
03355 >   virtual void OpDeleteLiveEffect::GetOpName(String_256* pstrOpName)
03356 
03357     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03358     Created:    10/09/2004
03359     Inputs:     
03360     Outputs:    
03361     Returns:    
03362     Purpose:    
03363     Errors:     
03364     See also:   
03365 
03366 ********************************************************************************************/
03367 void OpDeleteLiveEffect::GetOpName(String_256* pstrOpName)
03368 {
03369     *pstrOpName = String_256(_R(IDS_LE_OPDELETE));
03370 }
03371 
03372 
03373 
03374 
03375 /********************************************************************************************
03376 
03377 >   virtual BOOL OpDeleteLiveEffect::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03378 
03379     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03380     Created:    10/09/2004
03381     Inputs:     
03382     Outputs:    
03383     Returns:    
03384     Purpose:    
03385     Errors:     
03386     See also:   
03387 
03388 ********************************************************************************************/
03389 BOOL OpDeleteLiveEffect::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03390 {
03391     OpLiveEffect::DoDeletePostProcessor(this, pLevelRange);
03392 
03393     return TRUE;
03394 }
03395 
03396 
03397 
03398 
03400 // The OpDeleteAllLiveEffect class                                                               //
03402 
03403 /********************************************************************************************
03404 
03405 >   static OpState OpDeleteAllLiveEffect::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03406 
03407     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03408     Created:    10/09/2004
03409     Inputs:     
03410     Outputs:    
03411     Returns:    
03412     Purpose:    
03413     Errors:     
03414     See also:   
03415 
03416 ********************************************************************************************/
03417 OpState OpDeleteAllLiveEffect::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03418 {
03419     // default is an unticked, *GREYED*, on-menu state.
03420     OpState OpSt;
03421     OpSt.Greyed = FALSE;
03422 //  *pstrDescription = String_256(_R(IDS_DESCRIBE_LIVEEFFECT));
03423 
03424     if (XPEHost::IsEditSessionRunning())
03425     {
03426         OpSt.Greyed = TRUE;
03427         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
03428     }
03429 
03430     return OpSt;
03431 }
03432 
03433 
03434 
03435 /********************************************************************************************
03436 
03437 >   virtual void OpDeleteAllLiveEffect::GetOpName(String_256* pstrOpName)
03438 
03439     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03440     Created:    10/09/2004
03441     Inputs:     
03442     Outputs:    
03443     Returns:    
03444     Purpose:    
03445     Errors:     
03446     See also:   
03447 
03448 ********************************************************************************************/
03449 void OpDeleteAllLiveEffect::GetOpName(String_256* pstrOpName)
03450 {
03451     *pstrOpName = String_256(_R(IDS_LE_OPDELETE));
03452 }
03453 
03454 
03455 
03456 
03457 /********************************************************************************************
03458 
03459 >   virtual BOOL OpDeleteAllLiveEffect::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03460 
03461     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03462     Created:    10/09/2004
03463     Inputs:     
03464     Outputs:    
03465     Returns:    
03466     Purpose:    
03467     Errors:     
03468     See also:   
03469 
03470 ********************************************************************************************/
03471 BOOL OpDeleteAllLiveEffect::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03472 {
03473     Range* pSelRange = GetApplication()->FindSelection();
03474     ENSURE(pSelRange, "OpDeleteAllLiveEffect can't find selection\n");
03475 
03476     // ----------------------------------------------
03477     // This is the important bit!
03478     //
03479     // Go through all the LiveEffects, move their children up to be siblings and then
03480     // "delete" the LiveEffect nodes by hiding them.
03481     //
03482     Node* pNode = pSelRange->FindFirst();
03483     while (pNode)
03484     {
03485         // First, find pointer to child list to be moved into place as the LiveEffects stack is deleted
03486         Node* pNextSel = pSelRange->FindNext(pNode);
03487 
03488         if (pNode->IsAnObject())
03489             DoDeleteAllPostProcessors(this, (NodeRenderableInk*)pNode, FALSE, TRUE);
03490 
03491         pNode = pNextSel;
03492     }
03493 
03494     return TRUE;
03495 }
03496 
03497 
03498 
03499 
03500 BOOL OpDeleteAllLiveEffect::CheckEffectOpParam(OpLiveEffectParam* pLEOpParam)
03501 {
03502 //  if (!pLEOpParam->bSilent)
03503 //  {
03504 //      INT32 button = InformWarning(_R(IDS_LE_DELETEALL_WARNING), _R(IDS_REMOVE), _R(IDS_CANCEL));
03505 //      if (button==2)  // Cancel
03506 //          return FALSE;
03507 //  }
03508 //
03509     // Don't check for valid effects stack - this op doesn't need it.
03510 
03511     return TRUE;
03512 }
03513 
03514 
03515 
03516 
03518 // The OpEffectRes class                                                                     //
03520 
03521 /********************************************************************************************
03522 
03523 >   static OpState OpEffectRes::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03524 
03525     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03526     Created:    08/06/2005
03527     Inputs:     
03528     Outputs:    
03529     Returns:    
03530     Purpose:    
03531     Errors:     
03532     See also:   
03533 
03534 ********************************************************************************************/
03535 OpState OpEffectRes::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03536 {
03537     // default is an unticked, *GREYED*, on-menu state.
03538     OpState OpSt;
03539     OpSt.Greyed = FALSE;
03540     *pstrDescription = String_256(_R(IDS_DESCRIBE_LIVEEFFECT));
03541 
03542     if (XPEHost::IsEditSessionRunning())
03543     {
03544         OpSt.Greyed = TRUE;
03545         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
03546     }
03547 
03548     return OpSt;
03549 }
03550 
03551 
03552 
03553 /********************************************************************************************
03554 
03555 >   virtual void OpEffectRes::GetOpName(String_256* pstrOpName)
03556 
03557     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03558     Created:    08/06/2005
03559     Inputs:     
03560     Outputs:    
03561     Returns:    
03562     Purpose:    
03563     Errors:     
03564     See also:   
03565 
03566 ********************************************************************************************/
03567 void OpEffectRes::GetOpName(String_256* pstrOpName)
03568 {
03569     *pstrOpName = String_256(_R(IDS_LE_OPEFFECTRES));
03570 }
03571 
03572 
03573 
03574 
03575 /********************************************************************************************
03576 
03577 >   virtual BOOL OpEffectRes::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03578 
03579     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03580     Created:    08/06/2005
03581     Inputs:     
03582     Outputs:    
03583     Returns:    
03584     Purpose:    
03585     Errors:     
03586     See also:   
03587 
03588 ********************************************************************************************/
03589 BOOL OpEffectRes::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03590 {
03591     BOOL bOK = FALSE;
03592 
03593     bOK = DoEffectRangeOp(_R(IDS_LE_CHANGE_RES_PROGRESS), pLevelRange, pLEOpParam);
03594 
03595     return bOK;
03596 }
03597 
03598 
03599 
03600 
03601 /********************************************************************************************
03602 
03603 >   BOOL OpEffectRes::DoEffectNodeOp(NodeEffect* pLE, OpLiveEffectParam* pParam, NodeBitmapEffect** ppNewEffect)
03604 
03605     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03606     Created:    08/06/2005
03607     Inputs:     
03608     Outputs:    
03609     Returns:    
03610     Purpose:    
03611     Errors:     
03612     See also:   
03613 
03614 ********************************************************************************************/
03615 BOOL OpEffectRes::DoEffectNodeOp(NodeEffect* pLE, OpLiveEffectParam* pParam, NodeBitmapEffect** ppNewEffect)
03616 {
03617     // We should have a decent list of LiveEffect nodes...
03618     ERROR2IF(pLE==NULL, FALSE, "OpEffectRes::DoEffectNodeOp given NULL node");
03619     ERROR2IF(pParam==NULL, FALSE, "OpEffectRes::DoEffectNodeOp given NULL param");
03620 
03621     if (pLE->IsLockedEffect())
03622     {
03623         // Only change the res of scriptable effects
03624         if (((NodeLockedEffect*)pLE)->CanBeUnlocked())
03625         {
03626             if (ChangeEffectResAction::CreateChangeEffectResAction(this, (NodeLockedEffect*)pLE, pParam->dResolution))
03627             {
03628                 ((NodeLockedEffect*)pLE)->SetPixelsPerInch(pParam->dResolution);
03629 
03630                 // Ensure that locked effects will render their children
03631                 // because they don't respond to ReleaseCached...
03632                 ((NodeLockedEffect*)pLE)->SetProcessedBitmap(NULL, NULL, DocRect(), 0, 0, 0, 0);
03633                 pLE->ReleaseCached(TRUE, FALSE);
03634 
03635                 *ppNewEffect = (NodeLockedEffect*)pLE;
03636 
03637                 return TRUE;
03638             }
03639         }
03640     }
03641     else
03642         if (pLE->IsBitmapEffect())
03643         {
03644             if (ChangeEffectResAction::CreateChangeEffectResAction(this, (NodeBitmapEffect*)pLE, pParam->dResolution))
03645             {
03646                 ((NodeBitmapEffect*)pLE)->SetPixelsPerInch(pParam->dResolution);
03647                 pLE->ReleaseCached();
03648                 *ppNewEffect = (NodeBitmapEffect*)pLE;
03649 
03650                 return TRUE;
03651             }
03652         }
03653         else
03654             ERROR3("Unknown effect type in OpEffectRes::DoEffectNodeOp");
03655 
03656     return FALSE;
03657 }
03658 
03659 
03660 
03661 
03663 // The OpEffectLock class                                                                    //
03665 
03666 /********************************************************************************************
03667 
03668 >   static OpState OpEffectLock::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03669 
03670     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03671     Created:    08/06/2005
03672     Inputs:     
03673     Outputs:    
03674     Returns:    
03675     Purpose:    
03676     Errors:     
03677     See also:   
03678 
03679 ********************************************************************************************/
03680 OpState OpEffectLock::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03681 {
03682     // default is an unticked, *GREYED*, on-menu state.
03683     OpState OpSt;
03684     OpSt.Greyed = FALSE;
03685     *pstrDescription = String_256(_R(IDS_DESCRIBE_LIVEEFFECT));
03686 
03687     return OpSt;
03688 }
03689 
03690 
03691 
03692 /********************************************************************************************
03693 
03694 >   virtual void OpEffectLock::GetOpName(String_256* pstrOpName)
03695 
03696     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03697     Created:    08/06/2005
03698     Inputs:     
03699     Outputs:    
03700     Returns:    
03701     Purpose:    
03702     Errors:     
03703     See also:   
03704 
03705 ********************************************************************************************/
03706 void OpEffectLock::GetOpName(String_256* pstrOpName)
03707 {
03708     *pstrOpName = String_256(_R(IDS_LE_OPEFFECTLOCK));
03709 }
03710 
03711 
03712 
03713 
03714 /********************************************************************************************
03715 
03716 >   virtual BOOL OpEffectLock::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pParam)
03717 
03718     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03719     Created:    08/06/2005
03720     Inputs:     
03721     Outputs:    
03722     Returns:    
03723     Purpose:    
03724     Errors:     
03725     See also:   
03726 
03727 ********************************************************************************************/
03728 BOOL OpEffectLock::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pParam)
03729 {
03730     BOOL bOK = FALSE;
03731 
03732     UINT32 idsProgress = _R(IDS_LE_UNLOCK_PROGRESS);
03733     if (pParam->bForceLock) idsProgress = _R(IDS_LE_LOCK_PROGRESS);
03734     bOK = DoEffectRangeOp(idsProgress, pLevelRange, pParam);
03735 
03736     return bOK;
03737 }
03738 
03739 
03740 
03741 
03742 /********************************************************************************************
03743 
03744 >   BOOL OpEffectLock::DoEffectNodeOp(NodeEffect* pLE, OpLiveEffectParam* pParam, NodeBitmapEffect** ppNewEffect)
03745 
03746     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03747     Created:    13/06/2005
03748     Inputs:     
03749     Outputs:    
03750     Returns:    
03751     Purpose:    
03752     Errors:     
03753     See also:   
03754 
03755 ********************************************************************************************/
03756 BOOL OpEffectLock::DoEffectNodeOp(NodeEffect* pLE, OpLiveEffectParam* pParam, NodeBitmapEffect** ppNewEffect)
03757 {
03758     // We should have decent inputs...
03759     ERROR2IF(pLE==NULL, FALSE, "OpEffectLock::DoEffectNodeOp given NULL node");
03760     ERROR2IF(pParam==NULL, FALSE, "OpEffectRes::DoEffectNodeOp given NULL param");
03761 
03762     double dViewPPI = 96.0;
03763     View* pView = View::GetCurrent();
03764     if (pView) dViewPPI = 72000.0 / pView->GetPixelWidth().MakeDouble();
03765     BOOL bForceLock = pParam->bForceLock;
03766     NodeBitmapEffect* pNewEffect = NULL;
03767 
03768     if (pLE->IsBitmapEffect() && !pLE->IsFeatherEffect())
03769     {
03770         NodeBitmapEffect* pBitmapEffect = (NodeBitmapEffect*)pLE;
03771         if (pBitmapEffect->IsLockedEffect())
03772         {
03773             if (((NodeLockedEffect*)pBitmapEffect)->CanBeUnlocked() && bForceLock==FALSE)
03774             {
03775                 // -----------------------------------------------------------------
03776                 // Unlock
03777                 BOOL bWasSelected = pBitmapEffect->IsSelected();
03778 
03779                 // Get resolution for live effect from locked effect but set to
03780                 // automatic if its the same as the current screen resolution
03781                 double dNewRes = pBitmapEffect->GetPixelsPerInch();
03782                 if (dNewRes==dViewPPI)
03783                     dNewRes = 0;            // "Automatic"
03784 
03785                 pNewEffect = DoApplyLiveEffect(this,
03786                                   pBitmapEffect,
03787                                   pBitmapEffect->GetPostProcessorID(),
03788                                   pBitmapEffect->GetDisplayName(),
03789                                   dNewRes,  // NodeBitmapEffect::DefaultLivePixelsPerInch, //pBitmapEffect->GetPixelsPerInch(),
03790                                   pBitmapEffect->GetEditList()
03791                                   );
03792                 CopyEffectAttrs(pBitmapEffect, pNewEffect);
03793                 DoFactorOutCommonChildAttributes(pNewEffect);
03794                 DoDeletePostProcessor(this, pBitmapEffect, TRUE);
03795 
03796                 if (bWasSelected)
03797                 {
03798                     pNewEffect->Select(FALSE);      // Should skip down to underlying node automatically
03799                 }
03800 
03801                 *ppNewEffect = pNewEffect;
03802                 return TRUE;
03803             }
03804         }
03805         else
03806         {
03807             if (bForceLock==TRUE)   // Prevent unwanted extra work
03808             {
03809                 // -----------------------------------------------------------------
03810                 // Lock
03811                 ERROR3IF(pBitmapEffect->IsLockedEffect(), "Unexpected type of BitmapEffect in OpEffectLock");
03812                 NodeLiveEffect* pLiveEffect = (NodeLiveEffect*)pBitmapEffect;
03813                 BOOL bWasSelected = pLiveEffect->IsParentOfSelected();
03814 //              double dReqdResolution = NodeBitmapEffect::DefaultLockedPixelsPerInch;
03815                 double dReqdResolution = pLiveEffect->GetPixelsPerInch();
03816                 LPBITMAPINFO lpInfo = NULL;
03817                 LPBYTE lpBits = NULL;
03818                 DocRect rectBounds;
03819                 double dRes = 0;
03820                 Matrix matTransform;    // Defaults to identity
03821 
03822                 if (bWasSelected)
03823                 {
03824                     // Deselect before modifying the tree so that flags get sorted out correctly
03825                     pLiveEffect->DeSelect(FALSE, TRUE);
03826                 }
03827 
03828                 if (pLiveEffect->GetPixelsPerInch()==dReqdResolution)
03829                 {
03830                     // We should already have a bitmap of the required resolution
03831                     // (unless its been chucked out of the cache)
03832                     // If cached bitmap was result of a DirectBitmap process
03833                     // Then get bounding rect, transformation and resolution from child
03834                     BOOL bDirect = pLiveEffect->GetChildDirectBitmap(NULL, NULL, NULL, NULL, &matTransform, &dRes);
03835                     if (dRes==0)
03836                         dRes = pLiveEffect->GetPixelsPerInch();
03837 
03838                     // We only want to get the bitmap bounds in direct space if the matrix indicates
03839                     // a valid transform from direct space into document space.
03840                     // If not then we want bounds from document space.
03841                     // See also HasCachedDirectBitmap
03842                     bDirect = bDirect && !matTransform.IsIdentity();
03843                     pLiveEffect->GetProcessedBitmap(bDirect, &lpInfo, &lpBits, &rectBounds);
03844 
03845                     pLiveEffect->RemoveBitmapFromCache();           // So that bitmap is no longer subject to auto-deletion
03846                 }
03847 
03848                 // NOTE: We don't need to regenerate here because the node will grab a bitmap automatically
03849                 // during rendering
03850 
03851                 pNewEffect = DoApplyLiveEffect(this,
03852                                                 pLiveEffect,
03853                                                 pLiveEffect->GetPostProcessorID(),
03854                                                 pLiveEffect->GetDisplayName(),
03855                                                 dReqdResolution,                    //pLiveEffect->GetPixelsPerInch(),
03856                                                 pLiveEffect->GetEditList(),
03857                                                 TRUE                                // bMakeLocked
03858                                                 );
03859                 CopyEffectAttrs(pLiveEffect, pNewEffect);
03860                 DoFactorOutCommonChildAttributes(pNewEffect);
03861 
03862                 // If we have an initialisation bitmap set it now
03863                 if (pNewEffect && lpInfo && lpBits && rectBounds.IsValid())
03864                 {
03865                     pNewEffect->SetProcessedBitmap(lpInfo, lpBits, rectBounds, 0, 0, 0, 0, 72000.0/dRes, &matTransform);
03866                 }
03867 
03868                 DoDeletePostProcessor(this, pLiveEffect, TRUE);
03869 
03870                 if (bWasSelected)
03871                 {
03872                     pNewEffect->Select(FALSE);
03873                 }
03874 
03875                 *ppNewEffect = pNewEffect;
03876                 return TRUE;
03877             }
03878         }
03879     }
03880     else
03881         ERROR3("Unknown effect type in OpEffectLock::DoEffectNodeOp");
03882 
03883     return FALSE;
03884 }
03885 
03886 
03887 
03888 
03889 /********************************************************************************************
03890 
03891 >   BOOL OpEffectLock::CopyEffectAttrs(Node* pSrcNode, Node* pDestNode)
03892 
03893     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03894     Created:    17/06/2005
03895     Inputs:     pOp - pointer to op
03896                 pNode - 
03897     Outputs:    
03898     Returns:    TRUE if it worked
03899     Purpose:    Copy the Effect attributes from the specified node to another specified node
03900     Errors:     
03901     See also:   
03902 
03903 ********************************************************************************************/
03904 BOOL OpEffectLock::CopyEffectAttrs(NodeBitmapEffect* pSrcNode, NodeBitmapEffect* pDestNode)
03905 {
03906     NodeAttribute* pAttr = NodeAttribute::FindFirstAppliedAttr(pSrcNode);
03907     while (pAttr && pAttr->IsEffectAttribute() && pAttr->FindParent()==pSrcNode)
03908     {
03909         if (!pAttr->HasEquivalentDefaultValue(TRUE))            // Only copy attr if it doesn't look like the default
03910         {
03911             if (pDestNode->IsValidEffectAttr(pAttr))
03912             {
03913                 NodeAttribute* pExistingAttr = pDestNode->FindAppliedAttribute(pAttr->GetRuntimeClass());
03914                 if (pExistingAttr==NULL || pExistingAttr->FindParent()!=pDestNode)
03915                 {
03916                     // We can copy our effect attr to pInkNode as an effect attr
03917                     // Make a copy of this LiveEffect
03918                     NodeAttribute* pCopyAttr = NULL;
03919                     BOOL bOK = FALSE;
03920                     CALL_WITH_FAIL(pCopyAttr = (NodeAttribute*)pAttr->SimpleCopy(), this, bOK);
03921                     if (!bOK) return FALSE; // No room to take a copy of the node
03922 
03923                     pCopyAttr->AttachNode(pDestNode, LASTCHILD);
03924                 }
03925             }
03926         }
03927 
03928         pAttr = NodeAttribute::FindPrevAppliedAttr(pAttr);
03929     }
03930 
03931     return TRUE;
03932 }
03933 
03934 
03935 
03936 
03938 // The OpEffectLockAll class                                                                 //
03940 
03941 /********************************************************************************************
03942 
03943 >   static OpState OpEffectLockAll::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03944 
03945     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03946     Created:    08/06/2005
03947     Inputs:     
03948     Outputs:    
03949     Returns:    
03950     Purpose:    
03951     Errors:     
03952     See also:   
03953 
03954 ********************************************************************************************/
03955 OpState OpEffectLockAll::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc)
03956 {
03957     // default is an unticked, *GREYED*, on-menu state.
03958     OpState OpSt;
03959     OpSt.Greyed = FALSE;
03960     *pstrDescription = String_256(_R(IDS_DESCRIBE_LIVEEFFECT));
03961 
03962     if (XPEHost::IsEditSessionRunning())
03963     {
03964         OpSt.Greyed = TRUE;
03965         *pstrDescription = String_256(_R(IDS_LE_EDIT_RUNNING));
03966     }
03967 
03968     return OpSt;
03969 }
03970 
03971 
03972 
03973 /********************************************************************************************
03974 
03975 >   virtual void OpEffectLockAll::GetOpName(String_256* pstrOpName)
03976 
03977     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
03978     Created:    08/06/2005
03979     Inputs:     
03980     Outputs:    
03981     Returns:    
03982     Purpose:    
03983     Errors:     
03984     See also:   
03985 
03986 ********************************************************************************************/
03987 void OpEffectLockAll::GetOpName(String_256* pstrOpName)
03988 {
03989     *pstrOpName = String_256(_R(IDS_LE_OPEFFECTLOCKALL));
03990 }
03991 
03992 
03993 
03994 
03995 /********************************************************************************************
03996 
03997 >   virtual BOOL OpEffectLockAll::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
03998 
03999     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04000     Created:    08/06/2005
04001     Inputs:     
04002     Outputs:    
04003     Returns:    
04004     Purpose:    
04005     Errors:     
04006     See also:   
04007 
04008 ********************************************************************************************/
04009 BOOL OpEffectLockAll::DoEffectOp(ListRange* pLevelRange, OpLiveEffectParam* pLEOpParam)
04010 {
04011     SelRange Sel(*(GetApplication()->FindSelection()));
04012 
04013     Sel.SetPromoteToParent(TRUE);
04014     Node* pNode = Sel.FindFirst();
04015     Node* pNextNode = NULL;
04016     NodeBitmapEffect* pNewEffect = NULL;
04017     BOOL bChangedSomething = FALSE;
04018     BOOL bChanged;
04019     while (pNode)
04020     {
04021         pNextNode = Sel.FindNext(pNode);
04022 
04023         // We must find all the uppermost effects in our subtree
04024         // Use a preorder traversal to do this, pruning off subtrees as soon as an effect is found
04025         Node* pScanNode = pNode->FindFirstPreorder();
04026         while (pScanNode)
04027         {
04028             if (pScanNode->IsBitmapEffect() && !((NodeBitmapEffect*)pScanNode)->IsFeatherEffect())
04029             {
04030                 pNewEffect = NULL;
04031                 bChanged = OpEffectLock::DoEffectNodeOp((NodeBitmapEffect*)pScanNode, pLEOpParam, &pNewEffect);
04032                 if (pNewEffect && bChanged)
04033                 {
04034                     if (pScanNode == pNode)     // Update root node so that FindNextPreorder isn't confused
04035                         pNode = pNewEffect;
04036                     pScanNode = pNewEffect;
04037                 }
04038                 bChangedSomething = bChangedSomething || bChanged;
04039 
04040                 // We don't want to lock any effects under this one
04041                 // So prune this subtree from the traversal -
04042                 // find the next node in preorder order outside the scan node
04043                 pScanNode = pScanNode->FindNextPreorder(pNode, TRUE);
04044             }
04045             else
04046                 pScanNode = pScanNode->FindNextPreorder(pNode);
04047         }
04048 
04049         pNode = pNextNode;
04050     }
04051 
04052     if (!bChangedSomething)
04053         SucceedAndDiscard();
04054 
04055     return TRUE;
04056 }
04057 
04058 
04059 
04060 
04061 BOOL OpEffectLockAll::CheckEffectOpParam(OpLiveEffectParam* pLEOpParam)
04062 {
04063     return TRUE;
04064 }
04065 
04066 
04067 
04068 
04069 #ifdef FEATHER_EFFECT
04070 
04071 // The OpApplyFeatherEffect class                                                            //
04073 
04074 /********************************************************************************************
04075 
04076 >   static OpState OpApplyFeatherEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
04077 
04078     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04079     Created:    07/09/2004
04080     Inputs:     
04081     Outputs:    
04082     Returns:    
04083     Purpose:    Return state of the specified LiveEffect (name in pstrDescription) being
04084                 applied to the selection (taking into account the current position in the
04085                 LiveEffect Stack.
04086     Errors:     
04087     See also:   
04088 
04089 ********************************************************************************************/
04090 OpState OpApplyFeatherEffect::GetParamState(String_256* pstrDescription, OpDescriptor* pOpDesc, OpParam* pOpParam)
04091 {
04092     // default is an unticked, *GREYED*, on-menu state.
04093     OpState OpSt;
04094     OpSt.Greyed = (GetApplication()->FindSelection()->FindFirst()==NULL);
04095 
04096     return OpSt;
04097 }
04098 
04099 
04100 
04101 /********************************************************************************************
04102 
04103 >   virtual void OpApplyFeatherEffect::GetOpName(String_256* pstrOpName)
04104 
04105     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04106     Created:    07/09/2004
04107     Inputs:     
04108     Outputs:    
04109     Returns:    
04110     Purpose:    
04111     Errors:     
04112     See also:   
04113 
04114 ********************************************************************************************/
04115 void OpApplyFeatherEffect::GetOpName(String_256* pstrOpName)
04116 {
04117     *pstrOpName = String_256(_R(IDS_LE_OPAPPLYFEATHER));
04118 }
04119 
04120 
04121 
04122 /********************************************************************************************
04123 
04124 >   virtual void OpApplyFeatherEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
04125 
04126     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04127     Created:    10/09/2004
04128     Inputs:     
04129     Outputs:    
04130     Returns:    
04131     Purpose:    Insert a new FeatherEffect attribute in the stack above the selected object(s)
04132     Errors:     
04133     See also:   
04134 
04135 ********************************************************************************************/
04136 void OpApplyFeatherEffect::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam)
04137 {
04138     BOOL ok = FALSE;
04139 
04140     OpFeatherEffectParam* pFeatherOpParam = NULL;
04141     if (pOpParam->IsKindOf(CC_RUNTIME_CLASS(OpFeatherEffectParam)))
04142         pFeatherOpParam = (OpFeatherEffectParam*) pOpParam;
04143 
04144     INT32 InsertPos = pFeatherOpParam->StackPosition - 1;   // We want to find the surface below the current pos
04145                                                     // because we are going to insert above that surface
04146 
04147     // Remember some details for later on when the Op ends successfully
04148     m_strEffectID = pFeatherOpParam->strOpUnique;
04149     m_iStackPos = InsertPos;
04150 
04151     // obtain the current selection.
04152     SelRange* pSelRange = GetApplication()->FindSelection();
04153 
04154     if (pSelRange->FindFirst()==NULL)
04155     {
04156         End();
04157         return;
04158     }
04159 
04160     Range* pInitialRange = NULL;
04161 
04162     // render blobs off for tools which don't automatically redraw their blobs.
04163     Tool* pTool = Tool::GetCurrent();
04164     Spread* pSpread = Document::GetSelectedSpread();
04165     if (pSpread != NULL && pTool != NULL && !pTool->AreToolBlobsRenderedOnSelection())
04166         pTool->RenderToolBlobs(pSpread, NULL);
04167 
04168     // record the current selection state and if required, render off any selection blobs.
04169     if (!DoStartSelOp(FALSE, FALSE))
04170     {
04171         End();
04172         return;
04173     }
04174 
04175 // Ask the range whether it's OK to do the transform...
04176 // Get an ObjChangeParam ready, so we can ask op permission from nodes.
04177 ObjChangeFlags cFlags(NULL, TRUE, NULL, NULL, NULL, NULL, NULL, TRUE);  // ReplaceNode & RegenerateNode
04178 ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, this);
04179 BOOL bOK = pSelRange->AllowOp(&ObjChange);
04180 if (!bOK)
04181 {
04182     End();
04183     return;
04184 }
04185     // invalidate the region bounding the selection.
04186     if (!DoInvalidateNodesRegions(*pSelRange, TRUE, FALSE, FALSE, FALSE))
04187     {
04188         End();
04189         return;
04190     }
04191 
04192     // Force text subselection up to the whole parent text object
04193     pSelRange->MakePartialSelectionWhole(FALSE, FALSE, TRUE);
04194 
04195     // Get a list of the LiveEffects we will be working on
04196     ENSURE(m_pLERange==NULL, "!");
04197     if (InsertPos==-1)
04198     {
04199         // We are to apply the LiveEFfects to the selection
04200         pInitialRange = GetApplication()->FindSelection();
04201         m_iStackPos = 0;
04202     }
04203     else
04204     {
04205         ENSURE(pFeatherOpParam->pPPStack, "Can't find EffectsStack");
04206         pInitialRange = pFeatherOpParam->pPPStack->GetLevelRange(&InsertPos);
04207         m_iStackPos = InsertPos+1;          // Remember some details for later on when the Op ends successfully
04208                                             // (New stack pos refers to item to be shown in UI after successful insertion)
04209     }
04210 
04211     // Check whether we can edit an existing effect or whether we have to apply a new one
04212     if (pInitialRange==NULL)
04213     {
04214         ERROR3("If this happens, the UI logic disagrees with the Op about what the Op can do");
04215         FailAndExecute();
04216         End();
04217         return;
04218     }
04219 
04220     m_pLERange = DoApplyFeatherEffect(this, pInitialRange, pFeatherOpParam->strOpUnique, pFeatherOpParam->strMenuText, pFeatherOpParam->FeatherSize, pFeatherOpParam->Profile);
04221     m_bAppliedNewEffects = TRUE;
04222 
04223     // We should now have a decent list of LiveEffect nodes...
04224     if (m_pLERange==NULL)
04225     {
04226         FailAndExecute();
04227         End();
04228         return;
04229     }
04230 
04231     // Inform the modeless Feather UI of the change
04232     // and give it its own copy of the range of feather effect nodes
04233     OpChangeFeatherSize::SetEditContext(m_iStackPos, m_pLERange);
04234 
04235     // Update the LiveEffect Tool's permanent status
04236     if (pTool->GetID()==TOOLID_LIVEEFFECT)
04237     {
04238         ((LiveEffectsTool*)pTool)->SetCurrentEffectID(m_strEffectID);
04239         ((LiveEffectsTool*)pTool)->SetCurrentStackPos(m_iStackPos);
04240     }
04241 
04242     // invalidate the (new) region bounding the selection.
04243     DoInvalidateNodesRegions(*pSelRange, TRUE, FALSE, FALSE);
04244 
04245     // render blobs back on if the current tool doesn't automatically redraw its blobs.
04246     if (pSpread != NULL && pTool != NULL && !pTool->AreToolBlobsRenderedOnSelection())
04247         pTool->RenderToolBlobs(pSpread, NULL);
04248 
04249 ObjChange.Define(OBJCHANGE_FINISHED, cFlags, NULL, this);
04250 ok = UpdateChangedNodes(&ObjChange);
04251 
04252     BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::EFFECTSTACKCHANGED)); 
04253 
04254     // Tell the selrange that things have changed!
04255     GetApplication()->FindSelection()->Update();
04256 
04257     End();
04258 }
04259 #endif
04260 
04261 
04262 
04263 
04265 // The ChangeEffectResAction class                                                           //
04267 
04268 /********************************************************************************************
04269 
04270 >   ChangeEffectResAction::ChangeEffectResAction()
04271 
04272     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04273     Created:    09/06/2005
04274     Inputs:     -
04275     Outputs:    -
04276     Returns:    -
04277     Purpose:    -
04278     Errors:     -
04279     SeeAlso:    -
04280 
04281 ********************************************************************************************/
04282 ChangeEffectResAction::ChangeEffectResAction()
04283 {
04284     m_dResolution = 0;
04285     m_pEffectNode = NULL;
04286 }
04287 
04288 
04289 /********************************************************************************************
04290 
04291 >   ActionCode ChangeEffectResAction::Init( Operation* pOp,
04292                                             ActionList* pActionList,
04293                                             Action** NewAction)
04294 
04295     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04296     Created:    09/06/2005
04297     Inputs:     pOp is the pointer to the operation to which this action belongs
04298                 pTool is 
04299                 pActionList is the action list to which this action should be added
04300     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to
04301                 return a pointer to the created action
04302     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
04303     Purpose:    This is the function which creates an instance of this action. If there is no
04304                 room in the undo buffer (which is determined by the base class Init function
04305                 called within) the function will either return AC_NO_RECORD which means the
04306                 operation can continue, but no undo information needs to be stored, or AC_OK
04307                 which means the operation should continue AND record undo information. If
04308                 the function returns AC_FAIL, there was not enough memory to record the undo
04309                 information, and the user has decided not to continue with the operation.
04310     Errors:     -
04311     SeeAlso:    Action::Init()
04312 
04313 ********************************************************************************************/
04314 ActionCode ChangeEffectResAction::Init( Operation* pOp,
04315                                         ActionList* pActionList,
04316                                         Action** NewAction)
04317 {
04318     UINT32 ActSize = sizeof(ChangeEffectResAction);
04319 
04320     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ChangeEffectResAction), NewAction);
04321 
04322     return Ac;
04323 }
04324 
04325 
04326 
04327 /********************************************************************************************
04328 
04329 >   BOOL ChangeEffectResAction::CreateChangeEffectResAction(Operation* pOp,
04330                                               NodeBitmapEffect* pEffectNode,
04331                                               double dRes
04332                                              )
04333 
04334     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04335     Created:    09/06/2005
04336     Inputs:     pOp is the currently running operation
04337                 pActionList is a pointer ot the action list to which the action should be appended
04338     Outputs:    -
04339     Returns:    Action code which indicates success or failure to create the action
04340     Purpose:    This static function makes it a little easier to use this action. It creates an instance
04341                 of this action and appends it to the action list.
04342     Errors:     -
04343 
04344 ********************************************************************************************/
04345 
04346 BOOL ChangeEffectResAction::CreateChangeEffectResAction(Operation* pOp,
04347                                               NodeBitmapEffect* pEffectNode,
04348                                               double dRes
04349                                              )
04350 {
04351     ERROR3IF(pOp==NULL, "CreateChangeEffectResAction given NULL op pointer");
04352     ERROR3IF(pEffectNode==NULL, "CreateChangeEffectResAction given NULL node pointer");
04353 
04354     if (dRes == pEffectNode->GetPixelsPerInchValue())   // Prevent unwanted extra work
04355         return FALSE;
04356 
04357     ChangeEffectResAction* RecAction = NULL;
04358     ActionCode Act = ChangeEffectResAction::Init(pOp, pOp->GetUndoActionList(), (Action**)&RecAction);
04359     if ( (Act == AC_OK) && (RecAction != NULL) )
04360     {
04361         RecAction->m_dResolution = pEffectNode->GetPixelsPerInchValue();
04362         RecAction->m_pEffectNode = pEffectNode;
04363     }
04364     else if (Act==AC_FAIL)
04365     {
04366         pOp->FailAndExecute();
04367         pOp->End();
04368     }
04369 
04370     return (Act != AC_FAIL);
04371 }
04372 
04373 
04374 
04375 /********************************************************************************************
04376 
04377 >   ActionCode ChangeEffectResAction::Execute()
04378 
04379     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04380     Created:    09/06/2005
04381     Inputs:     -
04382     Outputs:    -
04383     Returns:    ActionCode, either AC_OK, AC_NORECORD or AC_FAIL
04384     Purpose:    Inserts the floating endpoint into the Line Tool, creating an undo action to
04385                 remove the endpoint.
04386     Errors:     -
04387     SeeAlso:    RemoveFloaterAction::Execute
04388 
04389 ********************************************************************************************/
04390 ActionCode ChangeEffectResAction::Execute()
04391 {
04392     ChangeEffectResAction* ReAction = NULL;
04393     ActionCode Act = AC_FAIL;
04394     
04395     // Create a redo action for this action, which is another ChangeEffectResAction
04396     Act = ChangeEffectResAction::Init(pOperation, 
04397                                     pOppositeActLst, 
04398                                     (Action**)(&ReAction));
04399     if (Act == AC_FAIL)
04400         return AC_FAIL;
04401 
04402     // ---------------------------------------------------
04403     // Record the current values in the Action
04404     ReAction->m_pEffectNode = m_pEffectNode;
04405     ReAction->m_dResolution = m_pEffectNode->GetPixelsPerInchValue();;
04406 
04407     // ---------------------------------------------------
04408     // Now do the actual action
04409     if (m_pEffectNode->IsLockedEffect())
04410     {
04411         ((NodeLockedEffect*)m_pEffectNode)->SetPixelsPerInch(m_dResolution);
04412     }
04413     else
04414         if (m_pEffectNode->IsBitmapEffect())
04415         {
04416             ((NodeBitmapEffect*)m_pEffectNode)->SetPixelsPerInch(m_dResolution);
04417             m_pEffectNode->ReleaseCached();
04418         }
04419 
04420     return Act;
04421 }
04422 
04423 ChangeEffectResAction::~ChangeEffectResAction()
04424 {
04425     m_pEffectNode = NULL;
04426 }
04427 
04428 
04429 
04430 
04432 // The ToggleLockAction class                                                                //
04434 
04435 /********************************************************************************************
04436 
04437 >   ToggleLockAction::ToggleLockAction()
04438 
04439     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04440     Created:    13/06/2005
04441     Inputs:     -
04442     Outputs:    -
04443     Returns:    -
04444     Purpose:    -
04445     Errors:     -
04446     SeeAlso:    -
04447 
04448 ********************************************************************************************/
04449 /*ToggleLockAction::ToggleLockAction()
04450 {
04451     m_bLockedState = FALSE;
04452     m_pEffectNode = NULL;
04453 }*/
04454 
04455 
04456 /********************************************************************************************
04457 
04458 >   ActionCode ToggleLockAction::Init(  Operation* pOp,
04459                                             ActionList* pActionList,
04460                                             Action** NewAction)
04461 
04462     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04463     Created:    13/06/2005
04464     Inputs:     pOp is the pointer to the operation to which this action belongs
04465                 pTool is 
04466                 pActionList is the action list to which this action should be added
04467     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to
04468                 return a pointer to the created action
04469     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
04470     Purpose:    This is the function which creates an instance of this action. If there is no
04471                 room in the undo buffer (which is determined by the base class Init function
04472                 called within) the function will either return AC_NO_RECORD which means the
04473                 operation can continue, but no undo information needs to be stored, or AC_OK
04474                 which means the operation should continue AND record undo information. If
04475                 the function returns AC_FAIL, there was not enough memory to record the undo
04476                 information, and the user has decided not to continue with the operation.
04477     Errors:     -
04478     SeeAlso:    Action::Init()
04479 
04480 ********************************************************************************************/
04481 /*ActionCode ToggleLockAction::Init(    Operation* pOp,
04482                                         ActionList* pActionList,
04483                                         Action** NewAction)
04484 {
04485     UINT32 ActSize = sizeof(ToggleLockAction);
04486 
04487     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ToggleLockAction), NewAction);
04488 
04489     return Ac;
04490 }*/
04491 
04492 
04493 
04494 /********************************************************************************************
04495 
04496 >   BOOL ToggleLockAction::CreateToggleLockAction(Operation* pOp,
04497                                               NodeBitmapEffect* pEffectNode,
04498                                               BOOL bForceLock
04499                                              )
04500 
04501     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04502     Created:    13/06/2005
04503     Inputs:     pOp is the currently running operation
04504                 pActionList is a pointer ot the action list to which the action should be appended
04505     Outputs:    -
04506     Returns:    Action code which indicates success or failure to create the action
04507     Purpose:    This static function makes it a little easier to use this action. It creates an instance
04508                 of this action and appends it to the action list.
04509     Errors:     -
04510 
04511 ********************************************************************************************/
04512 
04513 /*BOOL ToggleLockAction::CreateToggleLockAction(Operation* pOp,
04514                                               NodeBitmapEffect* pEffectNode,
04515                                               BOOL bForceLock
04516                                              )
04517 {
04518     ERROR3IF(pOp==NULL, "CreateToggleLockAction given NULL op pointer");
04519     ERROR3IF(pEffectNode==NULL, "CreateToggleLockAction given NULL node pointer");
04520 
04521     if (pEffectNode->IsLockedEffect() == bForceLock)    // Prevent unwanted extra work
04522         return FALSE;
04523 
04524     ToggleLockAction* RecAction = NULL;
04525     ActionCode Act = ToggleLockAction::Init(pOp, pOp->GetUndoActionList(), (Action**)&RecAction);
04526     if ( (Act == AC_OK) && (RecAction != NULL) )
04527     {
04528         RecAction->m_bLockedState = pEffectNode->IsLockedEffect();
04529         RecAction->m_pEffectNode = pEffectNode;
04530     }
04531     else if (Act==AC_FAIL)
04532     {
04533         pOp->FailAndExecute();
04534         pOp->End();
04535     }
04536 
04537     return (Act != AC_FAIL);
04538 }*/
04539 
04540 
04541 
04542 /********************************************************************************************
04543 
04544 >   ActionCode ToggleLockAction::Execute()
04545 
04546     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04547     Created:    13/06/2005
04548     Inputs:     -
04549     Outputs:    -
04550     Returns:    ActionCode, either AC_OK, AC_NORECORD or AC_FAIL
04551     Purpose:    Inserts the floating endpoint into the Line Tool, creating an undo action to
04552                 remove the endpoint.
04553     Errors:     -
04554     SeeAlso:    RemoveFloaterAction::Execute
04555 
04556 ********************************************************************************************/
04557 /*ActionCode ToggleLockAction::Execute()
04558 {
04559     ToggleLockAction* ReAction = NULL;
04560     ActionCode Act = AC_FAIL;
04561     
04562     // Create a redo action for this action, which is another ToggleLockAction
04563     Act = ToggleLockAction::Init(pOperation, 
04564                                     pOppositeActLst, 
04565                                     (Action**)(&ReAction));
04566     if (Act == AC_FAIL)
04567         return AC_FAIL;
04568 
04569     // ---------------------------------------------------
04570     // Record the current values in the Action
04571     ReAction->m_pEffectNode = m_pEffectNode;
04572 ERROR3("Unimplemented!");
04573 //  ReAction->m_dResolution = m_pEffectNode->GetPixelsPerInchValue();;
04574 
04575     // ---------------------------------------------------
04576     // Now do the actual action
04577 
04578     return Act;
04579 }
04580 
04581 ToggleLockAction::~ToggleLockAction()
04582 {
04583     m_pEffectNode = NULL;
04584 }*/
04585 
04586 
04587 
04588 
04590 // The MarkEditListAction class                                                              //
04592 
04593 /********************************************************************************************
04594 
04595 >   MarkEditListAction::MarkEditListAction()
04596 
04597     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04598     Created:    17/09/2004
04599     Inputs:     -
04600     Outputs:    -
04601     Returns:    -
04602     Purpose:    -
04603     Errors:     -
04604     SeeAlso:    -
04605 
04606 ********************************************************************************************/
04607 MarkEditListAction::MarkEditListAction()
04608 {
04609     pEditList = NULL;
04610     iStackPos = -1;
04611 }
04612 
04613 
04614 /********************************************************************************************
04615 
04616 >   ActionCode MarkEditListAction::Init(    Operation* pOp,
04617                                             ActionList* pActionList,
04618                                             Action** NewAction)
04619 
04620     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04621     Created:    17/09/2004
04622     Inputs:     pOp is the pointer to the operation to which this action belongs
04623                 pTool is 
04624                 pActionList is the action list to which this action should be added
04625     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to
04626                 return a pointer to the created action
04627     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
04628     Purpose:    This is the function which creates an instance of this action. If there is no
04629                 room in the undo buffer (which is determined by the base class Init function
04630                 called within) the function will either return AC_NO_RECORD which means the
04631                 operation can continue, but no undo information needs to be stored, or AC_OK
04632                 which means the operation should continue AND record undo information. If
04633                 the function returns AC_FAIL, there was not enough memory to record the undo
04634                 information, and the user has decided not to continue with the operation.
04635     Errors:     -
04636     SeeAlso:    Action::Init()
04637 
04638 ********************************************************************************************/
04639 ActionCode MarkEditListAction::Init(    Operation* pOp,
04640                                         ActionList* pActionList,
04641                                         Action** NewAction)
04642 {
04643     UINT32 ActSize = sizeof(MarkEditListAction);
04644 
04645     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(MarkEditListAction), NewAction);
04646 
04647     return Ac;
04648 }
04649 
04650 
04651 
04652 /********************************************************************************************
04653 
04654 >   ActionCode MarkEditListAction::DoMarkEditList(Operation* pOp,
04655                                             ActionList* pActionList
04656                                             )
04657 
04658     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04659     Created:    17/09/2004
04660     Inputs:     pOp is the currently running operation
04661                 pActionList is a pointer ot the action list to which the action should be appended
04662                 pTool is a pointer to the Line tool.
04663     Outputs:    -
04664     Returns:    Action code which indicates success or failure to create the action
04665     Purpose:    This static function makes it a little easier to use this action. It creates an instance
04666                 of this action and appends it to the action list.
04667     Errors:     -
04668     SeeAlso:    RemoveFloaterAction::DoRemove
04669 
04670 ********************************************************************************************/
04671 
04672 ActionCode MarkEditListAction::DoMarkEditList(Operation* pOp,
04673                                               ActionList* pActionList,
04674                                               EffectsStack* pPPStack,
04675                                               INT32 iStackPos
04676                                              )
04677 {
04678     ERROR2IF(iStackPos<0, AC_FAIL, "Bad stack pos in DoMarkEditList");
04679 
04680     MarkEditListAction* RecAction = NULL;
04681     ActionCode Act = MarkEditListAction::Init(pOp, pActionList, (Action**)&RecAction);
04682     if ( (Act == AC_OK) && (RecAction != NULL) )
04683     {
04684         PPStackLevel* pLevel = (PPStackLevel*)pPPStack->FindItem(iStackPos);
04685         if (pLevel && pLevel->pPPNode && pLevel->pPPNode->IsBitmapEffect())
04686         {
04687             RecAction->pEditList = CXMLUtils::NewDocument(((NodeBitmapEffect*)pLevel->pPPNode)->GetEditList());
04688             RecAction->iStackPos = iStackPos;
04689         }
04690         else
04691             return AC_FAIL;
04692     }
04693     return Act;
04694 }
04695 
04696 
04697 
04698 /********************************************************************************************
04699 
04700 >   ActionCode MarkEditListAction::Execute()
04701 
04702     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
04703     Created:    17/09/2004
04704     Inputs:     -
04705     Outputs:    -
04706     Returns:    ActionCode, either AC_OK, AC_NORECORD or AC_FAIL
04707     Purpose:    Inserts the floating endpoint into the Line Tool, creating an undo action to
04708                 remove the endpoint.
04709     Errors:     -
04710     SeeAlso:    RemoveFloaterAction::Execute
04711 
04712 ********************************************************************************************/
04713 ActionCode MarkEditListAction::Execute()
04714 {
04715     ERROR2IF(this->iStackPos<0, AC_FAIL, "Bad stack pos in MarkEditListAction::Execute");
04716 
04717     MarkEditListAction* ReAction = NULL;
04718     ActionCode Act = AC_FAIL;
04719     
04720     // Create a redo action for this action, which is another MarkEditListAction
04721     Act = MarkEditListAction::Init(pOperation, 
04722                                     pOppositeActLst, 
04723                                     (Action**)(&ReAction));
04724     if (Act == AC_FAIL)
04725         return AC_FAIL;
04726 
04727     // ---------------------------------------------------
04728     // Record the current edit list in the Action?
04729     EffectsStack* pPPStack = EffectsStack::GetEffectsStackFromSelection();
04730     if (pPPStack==NULL)
04731         return AC_FAIL;
04732 
04733     PPStackLevel* pLevel = (PPStackLevel*)pPPStack->FindItem(this->iStackPos);
04734     if (pLevel && pLevel->pPPNode && pLevel->pPPNode->IsBitmapEffect())
04735     {
04736         ReAction->pEditList = CXMLUtils::NewDocument(((NodeBitmapEffect*)pLevel->pPPNode)->GetEditList());
04737         ReAction->iStackPos = this->iStackPos;
04738     }
04739     else
04740     {
04741         ReAction->pEditList = CXMLUtils::NewDocument(this->pEditList);
04742         ReAction->iStackPos = this->iStackPos;
04743     }
04744 
04745     // ---------------------------------------------------
04746     // Now do the actual action
04747     // Run through the selection, setting edit lists on LiveEffects
04748     Node* pNode = NULL;
04749     if (pLevel) pNode = pLevel->listSelNodes.FindFirst();
04750     while (pNode)
04751     {
04752         if (pNode->IsBitmapEffect())
04753         {
04754             NodeBitmapEffect* pLE = (NodeBitmapEffect*)pNode;
04755             pLE->SetProcessedBitmap(NULL, NULL, DocRect(), 0, 0, 0, 0);         // Remove cached bitmap to be sure
04756             pLE->SetEditList(CXMLUtils::NewDocument(this->pEditList));
04757         }
04758         else
04759             return AC_FAIL;
04760         
04761         pNode = pLevel->listSelNodes.FindNext(pNode);
04762     }
04763 
04764     delete pPPStack;
04765     pPPStack = NULL;
04766 
04767     return Act;
04768 }
04769 
04770 MarkEditListAction::~MarkEditListAction()
04771 {
04772     pEditList = NULL;
04773 }
04774 
04775 
04776 
04777 

Generated on Sat Nov 10 03:47:48 2007 for Camelot by  doxygen 1.4.4