attrappl.cpp

Go to the documentation of this file.
00001 // $Id: attrappl.cpp 1664 2006-08-04 10:06:16Z gerry $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // AttrAppl.cpp - Implementation of the OpApplyAttribToSelected class
00099 
00100 #include "camtypes.h"
00101 
00102 #include "attrappl.h"
00103 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00104 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 #include "lineattr.h"
00107 #include "linwthop.h"
00108 //#include "mario.h"
00109 #include "nodedoc.h"
00110 //#include "simon.h"
00111 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "toollist.h"
00115 //#include "will.h"     // for _R(IDS_APPLYTONODEOP)
00116 #include "ndoptmz.h"    // for NodeAttributePtrItem
00117 #include "objchge.h"
00118 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00119 #include "progress.h"
00120 #include "fillndge.h"
00121 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00122 #include "nodetxtl.h"
00123 //#include "jason.h"
00124 #include "colormgr.h"
00125 //#include "bubbleid.h"
00126 #include "nodecont.h"
00127 #include "lineattr.h"
00128 #include "opdrbrsh.h"
00129 #include "brshattr.h"
00130 #include "effects_stack.h"
00131 #include "ophist.h"
00132 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00133 #include "nodetext.h"
00134 #include "ppbrush.h"
00135 
00136 CC_IMPLEMENT_DYNCREATE(OpApplyAttrib, SelOperation)
00137 CC_IMPLEMENT_DYNCREATE(OpApplyAttribToSelected, OpApplyAttrib)
00138 CC_IMPLEMENT_DYNCREATE(OpApplyAttrInteractive, OpApplyAttrib)
00139 CC_IMPLEMENT_DYNCREATE(OpApplyAttribsToSelected, OpApplyAttrib)
00140 CC_IMPLEMENT_DYNCREATE(OpRepeatApplyAttribToSelected, Operation)
00141 CC_IMPLEMENT_DYNCREATE(OpApplyAttribToNode, OpApplyAttrib)
00142 CC_IMPLEMENT_DYNCREATE(OpReplaceAttributes, SelOperation)
00143 CC_IMPLEMENT_DYNCREATE(ReplaceAttributesParam, OpParam)
00144 CC_IMPLEMENT_MEMDUMP(ApplyAttribsToSelectedParam, OpParam)
00145 
00146 // Declare smart memory handling in Debug builds
00147 #define new CAM_DEBUG_NEW
00148 
00149 // Static Fill Blob selection state, used to determine whether or not to
00150 // merge apply ops
00151 FillBlobSelectionState OpRepeatApplyAttribToSelected::FillBlobState;
00152 
00153 
00154 /********************************************************************************************
00155 
00156 >   NodeRenderableInk * OpApplyAttrib::FindCompoundNodeWhichRequiresAttribute(
00157                                                                 NodeRenderableInk * pStartNode)
00158 
00159     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
00160     Created:    8/4/2000
00161     Inputs:     pStartNode : the node to start from
00162     Outputs:    The compound node which requires the attribute
00163 
00164     Returns:    pStartNode when not finding any appropriate nodes
00165     Purpose:    When applying an attribute it's necessary to test its parents to see if
00166                 the attribute application should be promoted
00167                 Uses the PromoteAttributeApplicationToMe virtual function in Node to
00168                 discover whether to promote or not
00169     
00170 
00171 ********************************************************************************************/
00172 NodeRenderableInk * OpApplyAttrib::FindCompoundNodeWhichRequiresAttribute(
00173                                                     NodeRenderableInk * pStartNode,
00174                                                     CCRuntimeClass * pClass)
00175 {
00176     NodeRenderableInk * pNodeToApply = pStartNode;
00177 
00178     if (!pStartNode->NeedsParent(NULL))
00179     {
00180         Node * pParent = pNodeToApply->FindParent();
00181 
00182         while (pParent)
00183         {
00184             if (pParent->IsCompoundClass())
00185             {
00186                 if (((NodeCompound*)pParent)->PromoteAttributeApplicationToMe(pClass))
00187                 {
00188                     pNodeToApply = (NodeRenderableInk *)pParent;
00189                 }
00190             }
00191 
00192             // we need to go all the way up the tree to find the top-most node
00193             pParent = pParent->FindParent();
00194         }
00195     }
00196 
00197     return pNodeToApply;
00198 }
00199 
00200 /********************************************************************************************
00201 
00202 >   BOOL SelOperation::DoApply(Node* CurrentNode,
00203                                 NodeAttribute* Attrib,
00204                                 BOOL Mutate, 
00205                                 BOOL InvalidateRegion = TRUE
00206                                 BOOL KeepExistingCharacteristics  = TRUE,
00207                                 BOOL* AttribWasRequired = NULL,
00208                                 BOOL* pbCanDiscardUndo = NULL)
00209 
00210     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com> 
00211     Created:    5/4/95
00212     Inputs:     InvalidateRegion: When TRUE DoInvalidateNodeRegion is called before and
00213                                   after the attribute has been applied. If the attribute
00214                                   is being applied to a range of objects then it is 
00215                                   inefficient to do this.
00216                 KeepExistingCols: When TRUE the new attribute takes on the colour
00217                                   characteristics of the attribute it is replacing.
00218     Outputs:    AttribWasRequired:This output is optional. It indicates if  the
00219                                   Attrib was required by the object.
00220     Returns:    TRUE if successful 
00221                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
00222     Purpose:    
00223                 Will not apply the attribute to CurrentNode if it does not require it
00224     Errors:     -
00225     SeeAlso:    NodeRenderableInk::GetObjectToApplyTo
00226 
00227 ********************************************************************************************/
00228 
00229 BOOL SelOperation::DoApply(Node* CurrentNode, 
00230                             NodeAttribute* Attrib, 
00231                             BOOL Mutate, 
00232                             BOOL InvalidateRegion,
00233                             BOOL KeepExistingCols, /* = TRUE */
00234                             BOOL* pbAttribWasRequired,    /* = NULL */
00235                             BOOL* pbCanDiscardUndo /* = NULL */
00236                             )
00237 {
00238     return SelOperation::DoApply(this, CurrentNode, Attrib, Mutate, InvalidateRegion, KeepExistingCols, TRUE, pbAttribWasRequired, pbCanDiscardUndo);
00239 }
00240 
00241 BOOL SelOperation::DoApply( SelOperation* pOp,
00242                             Node* CurrentNode, 
00243                             NodeAttribute* Attrib, 
00244                             BOOL Mutate, 
00245                             BOOL InvalidateRegion,
00246                             BOOL KeepExistingCols, /* = TRUE */
00247                             BOOL bOptimise,
00248                             BOOL* pbAttribWasRequired,    /* = NULL */
00249                             BOOL* pbCanDiscardUndo /* = NULL */
00250                             )
00251 {
00252     BOOL AttributeExists = FALSE;  // Until we find that the attribute does exist
00253 
00254     CCRuntimeClass* AttrType = Attrib->GetAttributeType();
00255     
00256     ERROR3IF(!CurrentNode->IsAnObject(), "Trying to apply an attribute to a non-NodeRenderableInk");
00257     
00258     // We don't allow attributes like 'Quality' to be applied to objects directly.
00259     ERROR3IF(!(Attrib->CanBeAppliedToObject()), "Trying to apply an illegal attribute to the object"); 
00260     
00261     if (!(Attrib->CanBeAppliedToObject()))
00262         return TRUE;  // Just pretend they ever tried.
00263 
00264     Node* OriginalCurrentNode = CurrentNode;
00265 
00266     if (CurrentNode->IsAnObject())
00267     {
00268 
00269         // Obtain the real object to apply the attribute to 
00270         CurrentNode = ((NodeRenderableInk*)CurrentNode)->GetObjectToApplyTo(Attrib->GetAttributeType()); 
00271         if (!CurrentNode)
00272         {
00273             ERROR3("OpApplyAttrib::DoApply, Unable to find object to apply to");
00274             return TRUE;
00275         }
00276     }
00277 
00278     // NOTE! Better to get the value of this flag here, now that GetObjectToApplyTo has been called
00279     BOOL bEffectRootOnly = (CurrentNode->IsAnObject() && ((NodeRenderableInk*)CurrentNode)->IsValidEffectAttr(Attrib));
00280 
00281     // If we are applying attributes to the Caret alone then the caller should not retain an undo record
00282     // for this operation...
00283     if (pbCanDiscardUndo)
00284         *pbCanDiscardUndo = *pbCanDiscardUndo && CurrentNode->DiscardsAttributeChildren();
00285 
00286     // BODGE! ------------------------------------------------------------
00287     // Don't apply stroke transparency as an effect attribute - nothing
00288     // needs it. Test has to be done here because other objects in the
00289     // selection might need it...
00290     if (bEffectRootOnly && AttrType == CC_RUNTIME_CLASS(AttrStrokeTransp))
00291         return TRUE;
00292     // END-BODGE! --------------------------------------------------------
00293 
00294     // Needed to support adding triggers, who can be multiply applied
00295     BOOL AddEvenIfMultiple = FALSE;
00296 
00297     // Do not apply the attribute if CurrentNode does not require it 
00298     if ((((NodeRenderableInk*)CurrentNode)->RequiresAttrib(Attrib)))
00299     {
00300 
00301         if (pbAttribWasRequired)
00302             *pbAttribWasRequired = TRUE;
00303 
00304         if (CurrentNode != OriginalCurrentNode)
00305         {
00306             // BODGE TEXT
00307             // We need to call AllowOp, for the TextStories benefit, 
00308             // so that lines know they have been affected 
00309 
00310             ObjChangeFlags cFlags;
00311             cFlags.Attribute = TRUE;
00312             ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, pOp);
00313 
00314             if (!CurrentNode->AllowOp(&ObjChange))
00315             {
00316                 // It's already passed the test once.
00317                 ERROR3("Doing op when no nodes will allow it");
00318             }
00319         }
00320             
00321         // -----------------------------------------------------------------------------
00322         // Determine if the current node already has an attribute which is the same 
00323         // runtime class as Attrib. 
00324         Node* n = CurrentNode->FindFirstChild(); 
00325         Node* pLastBoundedNode = NULL;
00326         if (bEffectRootOnly)
00327         {
00328             pLastBoundedNode = CurrentNode->FindLastChild(CC_RUNTIME_CLASS(NodeRenderableBounded));
00329             ERROR3IF(pLastBoundedNode==NULL, "Attempt to apply effect attr to node with no children");
00330 
00331             if (pLastBoundedNode) n = pLastBoundedNode->FindNext();
00332         }
00333 
00334         while (n  && (n->IsOrHidesAnAttribute()))
00335         {
00336             // Now lets see if we can find an attribute of the same type
00337             // as the one we are interested in.
00338             if (n->IsAnAttribute())
00339             {
00340                 NodeAttribute* pNdAttr = (NodeAttribute*)n;
00341                  
00342                 if( pNdAttr->GetAttributeType() == AttrType)
00343                 {
00344                     // Attributes are of the same type (whatever that might mean)
00345 
00346                     // Look for another attribute of the same Type if we're allowed more than
00347                     // one per object
00348                     BOOL LookForMultiple = FALSE;
00349 
00350                     if (Attrib->CanBeMultiplyApplied())
00351                     {
00352                         // If the classID's are the same then we want to replace the old one
00353                         // (unless they're identical)
00354                         if (Attrib->GetAttributeClassID() == pNdAttr->GetAttributeClassID())
00355                         {
00356                             LookForMultiple = FALSE;    // stop looking now
00357                             AddEvenIfMultiple = FALSE;  // replace the old one
00358                         }
00359                         else
00360                         {
00361                             // they're not the same classID's then we'll add a new one
00362                             // but keep looking for a duplicate
00363                             LookForMultiple = TRUE;
00364                             AddEvenIfMultiple = TRUE;
00365                         }
00366                     }
00367     
00368                     // Do the attributes have the same value ?. If they do then there is no point in 
00369                     // applying it a second time
00370 
00371                     // to have the same value they must share the same runtime class
00372                     if (IS_SAME_CLASS(pNdAttr, Attrib))
00373                     {
00374                         // Found an identical attribute, so ignore this apply
00375                         if ((*pNdAttr)==(*Attrib))
00376                             return TRUE;     // We are not failing, just doing nothing.
00377 
00378                         if (Attrib->IsABrush())
00379                             ((AttrBrushType*)Attrib)->OnReplaceAttribute((AttrBrushType*)pNdAttr);
00380                     }
00381                     
00382                     // if applying a brush attribute we must copy some data over
00383                     if (!LookForMultiple)
00384                     {
00385                         AttributeExists = TRUE; 
00386                         break; 
00387                     }
00388                 }
00389             }
00390             n = n->FindNext(); // Find next child of CurrentNode
00391         }   
00392 
00393         // >>>> Temporary BODGE to aid select-inside...
00394         // >>>> ALWAYS clear any attributes of the same type from the subtree (even if the
00395         // >>>> attribute just applied replaced an existing one) so that dubious
00396         // >>>> attribute states (due to this routine not dealing with Select-inside
00397         // >>>> properly yet) can be cleared.
00398         // >>>> NOTE! the current att (n) is passed in to DoRemoveAttTypeFromSubtree so
00399         // >>>> that it won't be deleted along with atts of the same type - that would be
00400         // >>>> (has been) disastrous!
00401         // Triggers can be multiply applied to groups, so we need to check CanBeMultiplyApplied
00402         if (CurrentNode->IsCompound() && !Attrib->CanBeMultiplyApplied() && !bEffectRootOnly)
00403         {
00404             // Remove all instances of attributes of the same type from the subtree.
00405             // This is not neccessary if the AttributeExists flag is TRUE because
00406             // we know in this situation that the subtree cannot contain any other
00407             // instances of the attribute !.
00408             if (pOp && !pOp->DoRemoveAttrTypeFromSubtree(CurrentNode, Attrib->GetAttributeType(), n))
00409             {
00410                 pOp->FailAndExecute(); // Just to make sure
00411                 return FALSE;
00412             }           
00413         }
00414 
00415         // At this point we have Either .....
00416         //      1. Found an attribute of the same type as the new one, and we will
00417         //         now replace it.
00418         //      2. Found the specific attribute we were looking for and will replace
00419         //         that.
00420         //      3. Found an attribute to mutate.
00421         //      4. Found no attribute of the correct type, indicating that it has
00422         //         been factored out, and we need to put a new one in here.
00423         //      5. The attribute had CanBeMultiplyApplied() set, and no exact same
00424         //         Attribute was found.
00425         //
00426         // Have we have got an Attribute to do something with ?
00427         NodeAttribute* AttribClone = NULL;
00428         if ( !AddEvenIfMultiple && n && n->IsAnAttribute())
00429         {
00430             // Yes !
00431             NodeAttribute* pAttr = (NodeAttribute*)n;
00432 
00433             if (Mutate)
00434             {
00435                 // Mutate it into the new type.
00436                 AttribClone = ((AttrFillGeometry*)n)->Mutate((AttrFillGeometry*)Attrib, bOptimise);
00437             }
00438             else
00439             {
00440                 // We're gunna just replace the attribute with the new one.
00441 
00442                 // First make a copy of the new attribute.
00443                 if (pOp)
00444                     ALLOC_WITH_FAIL(AttribClone ,((NodeAttribute*)Attrib->SimpleCopy()), pOp)
00445                 else
00446                     AttribClone = (NodeAttribute*)Attrib->SimpleCopy();
00447                 if (AttribClone == NULL)
00448                     return FALSE; 
00449 
00450                 // Complication !!
00451                 // If we are replacing a Fill Attribute and the attribute we are replacing
00452                 // is already filled, then we need to extract the colour of the existing
00453                 // fill and use them for the new fill.
00454                 if ( KeepExistingCols && ((NodeAttribute*)n)->IsAFillAttr() )
00455                 {
00456                     AttrFillGeometry* NodeReplaced = (AttrFillGeometry*)n;
00457 
00458                     // Copy the old fill characteristics into the new Fill
00459                     if (!OpApplyAttrib::KeepExistingCharacteristics(NodeReplaced, (AttrFillGeometry*)AttribClone))
00460                         return FALSE;
00461                 }
00462 
00463                 if ( Attrib->IsATranspFill() && Attrib->IsAFlatFill())
00464                 {
00465                     ((AttrFillGeometry*)n)->RenderFillBlobs();
00466                 }
00467             }
00468 
00469             // If Mutate returned NULL, which means the Mutate
00470             // did nothing, we should return TRUE, which will move
00471             // onto the next object.
00472             if (AttribClone == NULL)
00473                 return TRUE; 
00474 
00475             // ----------------------------------------------------------
00476             // If we have just set an effect attribute then we can avoid releasing
00477             // cached info of the specified node and all its children...
00478             //
00479             // We RELY on invalidations associated with this function not
00480             // calling ReleaseCached themselves!
00481             //
00482             ReleaseCachedForAttrApply((NodeRenderableBounded*)CurrentNode, bEffectRootOnly);
00483             // ----------------------------------------------------------
00484 
00485             if (InvalidateRegion)
00486             {
00487                 // Invalidate the Object before the attribute is applied
00488                 if (!InvalidateNodeRegion(pOp, (NodeRenderableBounded*)CurrentNode, Attrib, Mutate))    // Doesn't invalidate cached info!
00489                     return FALSE;
00490             }
00491 
00492             // Now we have done with the old attribute, so lets hide it, so
00493             // the changes can be undone
00494             //
00495             // Don't write any undo info if we don't have an op or the node
00496             // discards attributes by itself
00497             if (pOp && !CurrentNode->DiscardsAttributeChildren())
00498             {
00499                 // Note that we wouldn't need this test and we wouldn't need
00500                 // to use the HideNode/ShowNode actions here if solid fill
00501                 // dragging used OpApplyAttrInteractive. Instead it uses an
00502                 // older system which relies on the HideNodeAction recording
00503                 // a pointer to an attribute at the start of the drag which
00504                 // is subsequently updated during the drag.
00505                 if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpApplyAttrInteractive)))
00506                 {
00507                     ApplyAction* pUndoApply; 
00508                     // Create an action to re-apply the attribute when we undo 
00509                     if ( ApplyAction::Init(pOp, 
00510                                             pOp->GetUndoActionList(),
00511                                             CurrentNode,
00512                                             pAttr,
00513                                             TRUE,               // When the attribute gets hidden we
00514                                                                 // must include its size 
00515                                             (Action**)(&pUndoApply))
00516                          == AC_FAIL)
00517                     {
00518                         return FALSE;
00519                     }
00520                     pAttr->CascadeDelete();
00521                     delete pAttr;
00522                     pAttr = NULL;
00523                 }
00524                 else
00525                 {
00526                     if (!pOp->DoHideNode(n, TRUE))          // Include the subtree size 
00527                         return FALSE;
00528                 }
00529             }
00530             else
00531             {
00532                 pAttr->CascadeDelete();
00533                 delete pAttr;
00534                 pAttr = NULL;
00535             }
00536 
00537         }
00538         else  // if (n != NULL)
00539         {
00540             // We've not found an attribute to replace, so we'll have to put
00541             // a new one in
00542             NodeAttribute* TempAttr = NULL;
00543 
00544             BOOL FoundAttr = ((NodeRenderableInk*)CurrentNode)->
00545                                 FindAppliedAttribute(Attrib->GetAttributeType(), &TempAttr);
00546 
00547             if (!FoundAttr || TempAttr == NULL)
00548                 return FALSE;
00549 
00550             if (Mutate)
00551             {
00552                 AttribClone = ((AttrFillGeometry*)TempAttr)->Mutate((AttrFillGeometry*)Attrib, bOptimise);
00553             }
00554             else
00555             {
00556                 // We'll just put a copy of our attribute in the tree.
00557                 if (pOp)
00558                     ALLOC_WITH_FAIL(AttribClone ,((NodeAttribute*)Attrib->SimpleCopy()), pOp)
00559                 else
00560                     AttribClone = (NodeAttribute*)Attrib->SimpleCopy();
00561 
00562                 if ( KeepExistingCols && (TempAttr->IsAFillAttr()) )
00563                 {
00564                     AttrFillGeometry* NodeReplaced = (AttrFillGeometry*)TempAttr;
00565 
00566                     // Copy the old fill characteristics into the new Fill
00567                     if (!OpApplyAttrib::KeepExistingCharacteristics(NodeReplaced, (AttrFillGeometry*)AttribClone))
00568                         return FALSE;
00569                 }
00570 
00571                 if (Attrib->IsATranspFill() && Attrib->IsAFlatFill())
00572                 {
00573                     // If we are mutating into a flat fill, then we need
00574                     // to make sure we remove any existing fill blobs.
00575                     ((AttrFillGeometry*)TempAttr)->RenderFillBlobs();
00576                 }
00577             }
00578 
00579             if (AttribClone)
00580             {
00581                 ReleaseCachedForAttrApply((NodeRenderableBounded*)CurrentNode, bEffectRootOnly);
00582 
00583                 if (InvalidateRegion)
00584                 {
00585                     // Make sure the node is redrawn
00586                     if (!InvalidateNodeRegion(pOp, (NodeRenderableBounded*)CurrentNode, Attrib, Mutate))    // Doesn't invalidate cached info!
00587                         return FALSE;
00588                 }
00589             }
00590         }
00591 
00592         // If the AttribClone has the same value as an applied attribute 
00593         // (A default attr if the attribs have been localised). Then we don't want to apply it !
00594         if (AttribClone && AttribClone->ShouldBeOptimized() && !bEffectRootOnly && bOptimise)
00595         {
00596             NodeAttribute* pAppliedAttr;
00597             if (((NodeRenderableInk*)CurrentNode)->FindAppliedAttribute(AttribClone->GetAttributeType(), 
00598                                                   &pAppliedAttr))
00599             {
00600                 // Do the attributes have the same value ?
00601                 if ((IS_SAME_CLASS(AttribClone, pAppliedAttr)))
00602                 {
00603                     if ((*AttribClone)==(*pAppliedAttr))
00604                     {
00605                         AttribClone->CascadeDelete();       // Delete the attribute
00606 
00607                         // If attribute affect's the bounds of the object (eg. a LineWidth) then invalidate the
00608                         // bounds of the object
00609 
00610                         if (pAppliedAttr->EffectsParentBounds())
00611                         {
00612                             ((NodeRenderableBounded*)CurrentNode)->InvalidateBoundingRect(TRUE);
00613                         }
00614 
00615                         delete AttribClone;
00616                         AttribClone = NULL;
00617                     }
00618                 }
00619             }
00620         }
00621 
00622         // Effect attributes don't optimise so if the value of this is the
00623         // same as the default value we should not add it here
00624         //
00625         if (AttribClone && bEffectRootOnly && AttribClone->HasEquivalentDefaultValue() && bOptimise)
00626         {
00627             // Just allow the attribute to be hidden
00628             AttribClone->CascadeDelete();       // Delete the attribute
00629             delete AttribClone;
00630             AttribClone = NULL;
00631         }
00632 
00633         if (AttribClone)
00634         {
00635             // Finally !! We can add the new attribute node into the tree.
00636             if (bEffectRootOnly)
00637                 AttribClone->AttachNode(pLastBoundedNode, NEXT);
00638             else
00639                 AttribClone->AttachNode(CurrentNode, FIRSTCHILD);
00640 
00641             AttributeManager::pLastNodeAppliedTo = (NodeRenderableInk*)CurrentNode;
00642 
00643             // And now it's in the tree, we need to make sure that any fill control
00644             // points are valid.
00645             if (AttribClone->IsAFillAttr())
00646             {
00647                 ((AttrFillGeometry*)AttribClone)->AttributeChanged();
00648             }
00649 
00650             if (pOp && !CurrentNode->DiscardsAttributeChildren())
00651             {
00652                 // Note that we wouldn't need this test and we wouldn't need
00653                 // to use the HideNode/ShowNode actions here if solid fill
00654                 // dragging used OpApplyAttrInteractive. Instead it uses an
00655                 // older system which relies on the HideNodeAction recording
00656                 // a pointer to an attribute at the start of the drag and the
00657                 // contents of the attribute being updated during the drag.
00658                 if (pOp->IsKindOf(CC_RUNTIME_CLASS(OpApplyAttrInteractive)))
00659                 {
00660                     UnApplyAction* pUndoApply; 
00661                     // Create an action to hide the attribute when we undo 
00662                     if ( UnApplyAction::Init(pOp, 
00663                                               pOp->GetUndoActionList(),
00664                                               CurrentNode,
00665                                               AttribClone,
00666                                               TRUE,             // When the attribute gets hidden we
00667                                                                 // must include its size 
00668                                              (Action**)(&pUndoApply))
00669                          == AC_FAIL)
00670                     {   
00671                         AttribClone->CascadeDelete();       
00672                         delete (AttribClone); 
00673                         return FALSE;  
00674                     }
00675                 }
00676                 else
00677                 {
00678                     HideNodeAction* UndoHideNodeAction; 
00679                     // Create an action to hide the attribute when we undo 
00680                     if ( HideNodeAction::Init(pOp,
00681                                               pOp->GetUndoActionList(),
00682                                               AttribClone,
00683                                               TRUE,             // When the attribute gets hidden we
00684                                                                 // must include its size 
00685                                              (Action**)(&UndoHideNodeAction))
00686                          == AC_FAIL)  
00687                     {   
00688                         AttribClone->CascadeDelete();       
00689                         delete (AttribClone); 
00690                         return FALSE;  
00691                     }
00692                 }
00693             }
00694 
00695         }
00696 
00697         ReleaseCachedForAttrApply((NodeRenderableBounded*)CurrentNode, bEffectRootOnly);
00698 
00699         if (InvalidateRegion)
00700         {
00701             // Invalidate the node rectangle, now the new attr has been applied
00702             if (!InvalidateNodeRegion(pOp, (NodeRenderableBounded*)CurrentNode, Attrib, Mutate))    // Doesn't invalidate cached info!
00703                 return FALSE; 
00704         }
00705     }
00706 
00707     // we may have to apply this to a brushed node
00708     if (pOp)                                                // TODO: Make this work when no undo op specified?
00709     {
00710         if (!pOp->ApplyAttributeToBrush(CurrentNode, Attrib))
00711             return FALSE;
00712     }
00713     return TRUE; // success
00714 }
00715 
00716 
00717 
00718 
00719 /********************************************************************************************
00720 
00721 >   static BOOL SelOperation::ReleaseCachedForAttrApply(NodeRenderableBounded* pNode, BOOL bEffectRootOnly) 
00722 
00723     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00724     Created:    08/03/2005
00725     Inputs:     pCurrentNode    - the node that the attribute is being applied to 
00726                 bEffectRootOnly - TRUE if
00727     Returns:    TRUE if all ok.
00728     Purpose:    If we have just set an effect attribute then we can avoid releasing
00729                 cached info of the specified node and all its children...
00730                 We RELY on invalidations associated with this function not
00731                 calling ReleaseCached themselves!
00732     Errors:     -
00733     SeeAlso:    -
00734 
00735 ********************************************************************************************/
00736 
00737 BOOL SelOperation::ReleaseCachedForAttrApply(NodeRenderableBounded* pNode, BOOL bEffectRootOnly)
00738 {
00739     if (bEffectRootOnly)
00740         pNode->ReleaseCached(TRUE, FALSE, FALSE, TRUE);                     // Parents and Derived data only
00741     else
00742         pNode->ReleaseCached();                                             // Self, Parents, Children and derived data
00743 
00744     return TRUE;
00745 }
00746 
00747 
00748 
00749 
00750 /********************************************************************************************
00751 
00752 >       BOOL SelOperation::ApplyAttributeToBrush(Node* pCurrentNode, NodeAttribute* pAttr) 
00753                         
00754 
00755     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00756     Created:    24/2/2000
00757     Inputs:     pCurrentNode - the node that the attribute is being applied to 
00758                 pAttr        - the attribute to apply
00759     Returns:    TRUE if all ok.
00760     Purpose:    Works out if pCurrentNode has an applied brush attribute, and if so then sets
00761                 up an action to tell the brush to use the attribute being applied.
00762     Errors:     -
00763     SeeAlso:    -
00764 
00765     Notes:      This is only currently operational for fill attributes, others may be added
00766                 in future.
00767                 18/4/2000 - Line widths added
00768 ********************************************************************************************/
00769 
00770 BOOL SelOperation::ApplyAttributeToBrush(Node* pCurrentNode, NodeAttribute* pAttr)
00771 {
00772     ERROR2IF(pCurrentNode == NULL, FALSE, "Current node is NULL");
00773     ERROR2IF(pAttr == NULL, FALSE, "Attribute is NULL");
00774     
00775     // currently this only works for colour changes and transparent fills
00776     if (!pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeColourChange)) && !pAttr->IsATranspFill()
00777         && !pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrLineWidth)) && !pAttr->IsABrush())
00778         return TRUE;
00779     
00780     // first get the existing brush attribute
00781     AttrBrushType* pAttrBrush = NULL;
00782     // for some reason we don't check the return value here??
00783     /*BOOL ok =*/ ((NodeRenderableInk*)pCurrentNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType),
00784                                                                         (NodeAttribute**)&pAttrBrush);
00785 
00786     // if we're applying a brush attribute then do something else
00787     if (pAttr->IsABrush())
00788     {
00789         AttrBrushType* pNewAttrBrush = (AttrBrushType*)pAttr;
00790         return ApplyBrushAttr(pCurrentNode, pNewAttrBrush);
00791     }
00792 
00793 
00794     // there isn't one - thats ok
00795     if (pAttrBrush == NULL || pAttrBrush->GetBrushHandle() == BrushHandle_NoBrush)
00796         return TRUE;
00797 
00798     // so now we have a brush attribute, so lets determine what to do:
00799     if (pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrStrokeColourChange)))
00800     {
00801         // only do this is the new colour is not transparent
00802         AttrStrokeColourChange* pStrokeCol = static_cast<AttrStrokeColourChange*>(pAttr);
00803         DocColour* pCol = pStrokeCol->GetStartColour();
00804 //PORTNOTE("other","Removed DocColour usage") - AMB Unremoved
00805         if (pCol->IsTransparent())
00806             return TRUE;
00807 
00808         return ApplyStrokeColourToBrush(pAttrBrush, (NodeRenderableInk*)pCurrentNode);
00809     }
00810 
00811 // Is this a porting comment-out? AMB
00812 //  if (pAttr->IsATranspFill())
00813 //      return ApplyTransparencyToBrush(pAttrBrush, (NodeRenderableInk*)pCurrentNode);
00814     if (pAttr->IsKindOf(CC_RUNTIME_CLASS(AttrLineWidth)))
00815         return ApplyLineWidthToBrush(pAttrBrush, (NodeRenderableInk*)pCurrentNode, pAttr);
00816 
00817     // trash the cached bounding rect
00818     pAttrBrush->ClearCachedRect();
00819 
00820     // if it isn't one of the above then we dont have to do anything
00821     return TRUE;
00822 }
00823 
00824 /********************************************************************************************
00825 
00826 >   BOOL SelOperation::ApplyTransparencyToBrush(AttrBrushType* pAttrBrush, NodeAttribute* pAttr) 
00827                         
00828 
00829     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00830     Created:    24/2/2000
00831     Inputs:     pAttrBrush   - the brush attribute that we wish to update
00832                 pAttr        - the attribute to apply
00833     Returns:    TRUE if all ok.
00834     Purpose:    When a transparency is applied to a brushed object we need to set a flag
00835                 in the brush to tell it to use this new transparency
00836 ********************************************************************************************/
00837 
00838 BOOL SelOperation::ApplyTransparencyToBrush(AttrBrushType* pAttrBrush, NodeRenderableInk* pBrushedNode)
00839 {
00840     ERROR2IF(pAttrBrush == NULL, FALSE, "Attribute pointer is NULL in OnApplyAttrib::ApplyTransparencyToBrush");
00841     ERROR2IF(pBrushedNode == NULL, FALSE,  "Attribute pointer is NULL in OnApplyAttrib::ApplyTransparencyToBrush");
00842 
00843     // make the OpParam
00844     ChangeBrushOpParam Param;
00845 
00846     Param.ChangeType = CHANGEBRUSH_USELOCALTRANSP;
00847     Param.m_bNewUseLocalTransp = TRUE;
00848     ChangeBrushAction* pAction = NULL;
00849     // initialise the action
00850     if (ChangeBrushAction::Init(this, &UndoActions, pBrushedNode, &Param, &pAction) == AC_FAIL)
00851         return FALSE;
00852 
00853     return TRUE;
00854 }
00855 /********************************************************************************************
00856 
00857 >   BOOL SelOperation::ApplyStrokeColourToBrush(AttrBrushType* pAttrBrush,NodeRenderableInk* pBrushedNode) 
00858                         
00859 
00860     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00861     Created:    24/2/2000
00862     Inputs:     pAttrBrush   - the brush attribute that we wish to update
00863                 pbrushed node   - the node that the brush applies to
00864     Returns:    TRUE if all ok.
00865     Purpose:    When a new stroke colour is applied to a brushed object we wish to set the
00866                 flag that tells the brush to override the named colours in the brush with
00867                 the colour applied
00868 
00869 ********************************************************************************************/
00870 
00871 BOOL SelOperation::ApplyStrokeColourToBrush(AttrBrushType* pAttrBrush, NodeRenderableInk* pBrushedNode)
00872 {
00873     ERROR2IF(pAttrBrush == NULL, FALSE, "Attribute pointer is NULL in OnApplyAttrib::ApplyStrokeColourToBrush");
00874     ERROR2IF(pBrushedNode == NULL, FALSE,  "Attribute pointer is NULL in OnApplyAttrib::ApplyStrokeColourToBrush");
00875 
00876     
00877     
00878 
00879     // if the brush already uses local colours then don't bother doing anything
00880     PathProcessorBrush *pPPB = pAttrBrush->GetPathProcessor();
00881     if (pPPB == NULL)
00882         return TRUE;
00883 
00884     if (pPPB->GetUseLocalFillColour() || !pPPB->GetUseNamedColours())
00885         return TRUE;
00886 
00887     // make the OpParam
00888     ChangeBrushOpParam Param;
00889 
00890     Param.ChangeType = CHANGEBRUSH_USENAMEDCOL;
00891     Param.m_bNewUseNamed = FALSE;
00892     ChangeBrushAction* pAction = NULL;
00893     // initialise the action
00894     if (ChangeBrushAction::Init(this, &UndoActions, pBrushedNode, 
00895                             &Param, &pAction) == AC_FAIL)
00896         return FALSE;
00897 
00898     return TRUE;
00899 }
00900 
00901 
00902 
00903 /********************************************************************************************
00904 
00905 >   BOOL SelOperation::ApplyLineWidthToBrush(AttrBrushType* pAttrBrush, NodeRenderableInk* pBrushedNode,
00906                                 NodeAttribute* pAttr) 
00907                         
00908 
00909     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00910     Created:    24/2/2000
00911     Inputs:     pAttrBrush   - the brush attribute that we wish to update
00912                 pBrushedNode   -the node that the brush applies to
00913                 pAttr      - the line width that we are applying
00914     Returns:    TRUE if all ok.
00915     Purpose:    If we are applying a line width to a brushed node we need to tell the brush that
00916                 it must scale to line width.
00917                 We must also do an additional invalidate region so that if we are increasing in
00918                 size then the brush will be rerendered properly
00919 ********************************************************************************************/
00920 
00921 BOOL SelOperation::ApplyLineWidthToBrush(AttrBrushType* pAttrBrush, NodeRenderableInk* pBrushedNode,
00922                                           NodeAttribute* pAttr)
00923 {
00924     ERROR2IF(pAttrBrush == NULL, FALSE, "Attribute pointer is NULL in OnApplyAttrib::ApplyLineWidthToBrush");
00925     ERROR2IF(pBrushedNode == NULL, FALSE,  "Node pointer is NULL in OnApplyAttrib::ApplyLineWidthToBrush");
00926     ERROR2IF(pAttr == NULL, FALSE, "Attribute pointer is NULL in OnApplyAttrib::ApplyLineWidthToBrush");
00927 
00928     // if we are on a closed path then ask the PPB to even out the spacing
00929     PathProcessorBrush* pPPB = pAttrBrush->GetPathProcessor();
00930     if (pPPB != NULL)
00931     {
00932         // scale to the new line width
00933         AttrLineWidth* pLineWidth = (AttrLineWidth*)pAttr;
00934         BOOL UsesPressure = pAttrBrush->GetPressureCache() != NULL;
00935         pPPB->ScaleToValue(pLineWidth->Value.LineWidth, !UsesPressure);
00936         pPPB->AdjustSpacingForClosedPath(pBrushedNode);
00937     }
00938 
00939     return TRUE;
00940 }
00941 
00942 
00943 
00944 /********************************************************************************************
00945 
00946 >BOOL SelOperation::ApplyBrushAttr(Node* pCurrentNode, AttrBrushType* pAttrBrush)
00947                         
00948 
00949     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00950     Created:    24/2/2000
00951     Inputs:     pCurrentNode - the node we are applying the brush to
00952                 pAttrBrush   - the brush attribute we are applying
00953     Returns:    TRUE if all ok.
00954     Purpose:    When we apply a brush attribute to an object we want to check to see if the
00955                 current line width attribute is the same as the default attribute.  If it is then
00956                 we will apply the default line width suggested by the brush itself.
00957                 This is because people keep on applying brush attributes to 500MP lines
00958                 and ending up with thousands of brush objects, which makes everything really slow.
00959 ********************************************************************************************/
00960 
00961 BOOL SelOperation::ApplyBrushAttr(Node* pCurrentNode, AttrBrushType* pAttrBrush)
00962 {
00963     ERROR2IF(pCurrentNode == NULL || pAttrBrush == NULL, FALSE, "Null input pointers to OpApplyAttrib::ApplyBrushAttr");
00964     ERROR2IF(!pCurrentNode->IsAnObject(), FALSE, "Input node is not an ink node in OpApplyAttrib::ApplyBrushAttr");
00965 
00966     // its too complicated when you have compound nodes
00967     if (pCurrentNode->IsCompound())
00968         return TRUE;
00969 
00970     AttrLineWidth* pCurrentLineWidth = NULL;
00971     ((NodeRenderableInk*)pCurrentNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrLineWidth), (NodeAttribute**)&pCurrentLineWidth);
00972     
00973     if (pCurrentLineWidth != NULL)
00974     {
00975         // Get the default line width
00976         AttrLineWidth* pDefault = (AttrLineWidth*)(AttributeManager::GetDefaultAttribute(ATTR_LINEWIDTH));
00977         if (pDefault == NULL)
00978         {
00979             ERROR3("Unable to get default line width, theres no way this can happen Jim!");
00980             // continue anyway
00981         }
00982         else
00983         {
00984             // check to see if default is the same as current, if not then just leave
00985             if (pCurrentLineWidth->Value.LineWidth != pDefault->Value.LineWidth-1)
00986             {
00987                 delete pDefault;
00988                 PathProcessorBrush* pPPB = pAttrBrush->GetPathProcessor();
00989                 if (pPPB)
00990                 {
00991                     BOOL UsesPressure = pAttrBrush->GetPressureCache() != NULL;
00992                     pPPB->ScaleToValue(pCurrentLineWidth->Value.LineWidth, !UsesPressure);
00993                     pPPB->AdjustSpacingForClosedPath((NodeRenderableInk*)pCurrentNode);
00994                 }
00995 
00996                 return TRUE;
00997             }
00998             delete pDefault;
00999         }
01000     }
01001 
01002 
01003     // otherwise we want a new line width attribute
01004     MILLIPOINT LineWidth = pAttrBrush->GetDefaultLineWidth(TRUE);
01005     
01006     //DoApply makes a copy so we only need to make this local
01007     AttrLineWidth NewLineWidth;
01008 
01009     NewLineWidth.Value.LineWidth = LineWidth;
01010 
01011     // now apply it
01012     return DoApply(pCurrentNode, &NewLineWidth, FALSE, FALSE);
01013     
01014 }
01015 
01016 /********************************************************************************************
01017 
01018 >   static BOOL OpApplyAttrib::KeepExistingCharacteristics(AttrFillGeometry* OldAttr, 
01019                                                     AttrFillGeometry* NewAttr)
01020 
01021     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01022     Created:    6/6/95
01023     Inputs:     -
01024     Returns:    TRUE if all ok.
01025     Purpose:    Retains the previous fill charateristics when applying a new fill.
01026     Errors:     -
01027     SeeAlso:    -
01028 
01029 ********************************************************************************************/
01030 
01031 BOOL OpApplyAttrib::KeepExistingCharacteristics(AttrFillGeometry* OldAttr, 
01032                                                 AttrFillGeometry* NewAttr)
01033 {
01034     NewAttr->SetStartTransp(OldAttr->GetStartTransp());
01035     NewAttr->SetEndTransp(OldAttr->GetEndTransp());
01036 
01037     NewAttr->SetTranspType(OldAttr->GetTranspType());
01038 
01039     if (OldAttr->IsAFlatFill())
01040     {
01041         if (OldAttr->FindParent()->IsNodeDocument())
01042         {
01043             // If the applied attr is the default fill at the top of the tree,
01044             // then we need to 'bodge' it to be black.
01045             DocColour DefFillCol;
01046             AttributeManager::FindDefaultColour(ColourManager::GetCurrentColourList(),
01047                                                 _R(IDS_BLACKNAME), &DefFillCol);
01048             NewAttr->SetEndColour(&DefFillCol);
01049         }
01050         else
01051             NewAttr->SetEndColour(OldAttr->GetStartColour());
01052     }
01053     else
01054     {
01055         NewAttr->SetStartColour(OldAttr->GetStartColour());
01056         NewAttr->SetEndColour(OldAttr->GetEndColour());
01057     }
01058 
01059     NewAttr->SetTesselation(OldAttr->GetTesselation());
01060     NewAttr->AttachBitmap(OldAttr->GetBitmap());
01061 
01062     if (OldAttr->IsAFractalFill())
01063     {
01064         NewAttr->SetSeed(OldAttr->GetSeed());
01065         NewAttr->SetGraininess(OldAttr->GetGraininess());
01066         NewAttr->SetGravity(OldAttr->GetGravity());
01067         NewAttr->SetSquash(OldAttr->GetSquash());
01068         NewAttr->SetTileable(OldAttr->GetTileable());
01069         NewAttr->SetFractalDPI(AttrFillGeometry::FractalDPI);
01070         NewAttr->RecalcFractal();
01071     }
01072 
01073     return TRUE;
01074 }
01075 
01076 /********************************************************************************************
01077 
01078 >   BOOL SelOperation::InvalidateNodeRegion(    SelOperation* pOp,
01079                                                 NodeRenderableBounded* CurrentNode, 
01080                                                 NodeAttribute* Attrib, 
01081                                                 BOOL Mutate)
01082     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01083     Created:    6/6/95
01084     Inputs:     -
01085     Returns:    TRUE if region invalidated ok.
01086     Purpose:    Invalidates the Bounds of a Node.
01087     Errors:     -
01088     SeeAlso:    -
01089 
01090 ********************************************************************************************/
01091 
01092 BOOL SelOperation::InvalidateNodeRegion(    SelOperation* pOp,
01093                                             NodeRenderableBounded* CurrentNode, 
01094                                             NodeAttribute* Attrib, 
01095                                             BOOL Mutate)
01096 {
01097     // Include the blobs, if the attribute will change the parents bounds.
01098     BOOL IncludeBlobs = Attrib->EffectsParentBounds();
01099 
01100     if ( (Mutate && !Attrib->IsAValueChange()) ||
01101          Attrib->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrBitmapDpiChange) )
01102     {
01103         // Check for special cases of, either a mutate from one fill type to another,
01104         // or a dpi change.  Both of these can change the blob bounds.
01105         IncludeBlobs = TRUE;
01106     }
01107 
01108     // Invalidate the bounds of the node, including the blobs if necessary.
01109     if (pOp)
01110     {
01111         if (!pOp->DoInvalidateNodeRegion(CurrentNode, IncludeBlobs, FALSE, FALSE, FALSE))   // NOTE! We do not release cached info!
01112                                                                                         // Must be used in conjunction with ApplyToSelection
01113         {
01114             // Summit went wrong.
01115             return FALSE;
01116         }
01117     }
01118     else
01119     {
01120         BaseDocument* pBaseDoc = CurrentNode->FindOwnerDoc();
01121         if (pBaseDoc && pBaseDoc->IsKindOf(CC_RUNTIME_CLASS(Document)))
01122         {
01123             Document* pDoc = (Document*)pBaseDoc;
01124             Spread* pSpread = (Spread*)CurrentNode->FindParent(CC_RUNTIME_CLASS(Spread));
01125             DocRect TempRect; 
01126             TempRect = (IncludeBlobs ? 
01127                             (CurrentNode->GetUnionBlobBoundingRect()):
01128                             (CurrentNode->GetBoundingRect())
01129                          );
01130             TempRect = TempRect.Union(CurrentNode->GetEffectStackBounds());
01131             pDoc->ForceRedraw(pSpread, TempRect, TRUE, CurrentNode, FALSE);
01132         }
01133     }
01134 
01135     return TRUE;    // All ok
01136 }
01137 
01138 /********************************************************************************************
01139 
01140 >   BOOL OpApplyAttrib::DoInvalidateRegions(Range* NodeRange, 
01141                                             NodeAttribute* Attrib, 
01142                                             BOOL Mutate, 
01143                                             NodeAttribute* OtherAttr,
01144                                             BOOL OtherMuate)
01145     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01146     Created:    10/6/95
01147 
01148     Inputs:     NodeRange: The range of nodes to invalidate
01149                 Attrib: The attribute to be applied
01150                 Mutate: 
01151 
01152     Returns:    TRUE if region invalidated ok.
01153     Purpose:    Invalidates the regions of every node in the range
01154     Errors:     -
01155     SeeAlso:    OpApplyAttrib::InvalidateNodeRegion
01156 
01157 ********************************************************************************************/
01158 
01159 BOOL OpApplyAttrib::DoInvalidateRegions(Range* NodeRange, 
01160                                         NodeAttribute* Attrib, 
01161                                         BOOL Mutate,
01162                                         NodeAttribute* OtherAttr,
01163                                         BOOL OtherMutate)
01164 {
01165     // Include the blobs, if the attribute will change the parents bounds.
01166     BOOL IncludeBlobs = ((Attrib->EffectsParentBounds()) || (OtherAttr && (OtherAttr->EffectsParentBounds())));
01167 
01168     if (((Mutate && !Attrib->IsAValueChange()) ||
01169          Attrib->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrBitmapDpiChange)) ||
01170          (OtherAttr && ((OtherMutate && !OtherAttr->IsAValueChange()) ||
01171          (OtherAttr->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrBitmapDpiChange))) )
01172         )
01173     {
01174         // Check for special cases of, either a mutate from one fill type to another,
01175         // or a dpi change.  Both of these can change the blob bounds.
01176         IncludeBlobs = TRUE;
01177     }
01178 
01179     // Invalidate the bounds of the node, including the blobs if necessary.
01180     if (!DoInvalidateNodesRegions(*NodeRange, IncludeBlobs, FALSE, FALSE, FALSE))   // NOTE! We do not release cached info!
01181                                                                                     // Must be used in conjunction with ApplyToSelection
01182     {
01183         // Summit went wrong.
01184         return FALSE;
01185     }
01186 
01187     return TRUE;    // All ok
01188 }
01189 
01190 
01191 /********************************************************************************************
01192 
01193 >   OpApplyAttribToSelected::OpApplyAttribToSelected() 
01194 
01195     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01196     Created:    29/9/93
01197     Inputs:     -
01198     Outputs:    -
01199     Returns:    -
01200     Purpose:    OpApplyAttribToSelected constructor
01201     Errors:     -
01202     SeeAlso:    -
01203 
01204 ********************************************************************************************/
01205             
01206             
01207 OpApplyAttribToSelected::OpApplyAttribToSelected(): OpApplyAttrib()                             
01208 {                              
01209     ValueChangeType = NULL;
01210     MergeRepeats = FALSE;
01211     m_pAttr = NULL;
01212 }
01213 
01214 
01215 /********************************************************************************************
01216 
01217 >   void OpApplyAttribToSelected::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)
01218 
01219     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01220     Created:    16/8/93
01221     Inputs:     OpDescriptor 
01222     Outputs:    pOpParam: Param1 contains a pointer to the NodeAttribute to apply to the 
01223                           currently selected nodes
01224     Returns:    -
01225     Purpose:    Performs the OpApplyAttribToSelected operation. This function applies the
01226                 NodeAttribute to all selected objects.
01227     Errors:     -
01228     SeeAlso:    -
01229 
01230 ********************************************************************************************/
01231         
01232 void OpApplyAttribToSelected::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)         
01233 {   
01234 #ifndef STANDALONE
01235 
01236     AttrTypeSet AttrTypes; 
01237 
01238     Range SelRng(*(GetApplication()->FindSelection()));
01239 
01240  
01241     ERROR3IF(pOpParam == NULL, "The OpApplyAttribToSelected operation requires an attribute parameter"); 
01242 
01243     BeginSlowJob();
01244     AttributeManager::pLastNodeAppliedTo = NULL;
01245 
01246     // Obtain a pointer to the attribute which we will need to apply to all selected nodes 
01247     NodeAttribute* Attrib = (NodeAttribute*)(void *)pOpParam->Param1;
01248     m_pAttr = Attrib;
01249 
01250     // DMc - make sure that the change type is set
01251     if (Attrib)
01252     {
01253         ValueChangeType = Attrib->GetRuntimeClass();
01254     }
01255 
01256     NodeAttribute* pAppliedAttrib; 
01257 
01258     BOOL Mutate = FALSE;
01259     BOOL RedrawBlobs = FALSE;
01260     NodeAttribute* OldAttr = NULL;
01261 
01262     if (Attrib == NULL)
01263     {
01264         // If the first param is NULL then we are doing a mutation
01265         Attrib = (NodeAttribute*)(void *)pOpParam->Param2;
01266         m_pAttr = Attrib;
01267 
01268         // DMc - make sure that the change type is set
01269         if (Attrib)
01270         {
01271             ValueChangeType = Attrib->GetRuntimeClass();
01272         }
01273     
01274         Mutate = TRUE;
01275 
01276         ValueChangeType = Attrib->GetRuntimeClass();
01277 
01278         if( ValueChangeType == CC_RUNTIME_CLASS(AttrTranspChange)
01279             || ValueChangeType == CC_RUNTIME_CLASS(FillGeometryNudger)
01280             )
01281         {
01282             MergeRepeats = TRUE;
01283         }
01284     }   
01285     else
01286     {
01287         // Someone has specified a Particular Attribute that we should replace
01288         // and ignore all others.
01289         OldAttr = (NodeAttribute*)(void *)pOpParam->Param2;
01290         RedrawBlobs = Attrib->EffectsParentBounds();
01291     }
01292 
01293     SelRange* Sel;
01294     ObjChangeFlags cFlags;
01295     cFlags.Attribute = TRUE;
01296     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
01297 
01298     Sel = GetApplication()->FindSelection(); 
01299 
01300     if (!Sel->AllowOp(&ObjChange))
01301     {
01302         FailAndExecute();
01303         goto EndOperation;
01304     }
01305 
01306     // Get a description of the attribute being applied so that we can use it to create the 
01307     // undo string. 
01308     UndoAttribStrID = Attrib->GetAttrNameID();
01309 
01310     if (!DoStartSelOp(RedrawBlobs, RedrawBlobs, TRUE,TRUE))
01311     {
01312         goto EndOperation;  
01313     }
01314 
01315     NodeAttribute* OtherAttrib;
01316 
01317     // Some Attributes require a second attribute to be changed as well,
01318     // which has to be done within this op.
01319 
01320     BOOL IsMutate;
01321     OtherAttrib = AttributeManager::GetOtherAttrToApply(Attrib, &IsMutate);
01322             
01323     // Before we apply the attribute to the selection we must localise all attributes
01324     // with the same type that we are going to apply. If we don't do this then the
01325     // tree will be left in an invalid state. 
01326 
01327     AttrTypes.AddToSet((Attrib->GetAttributeType())); 
01328     if (OtherAttrib != NULL)
01329     {
01330         AttrTypes.AddToSet((OtherAttrib->GetAttributeType())); 
01331     }
01332     
01333     // Invalidate before
01334     if (!DoInvalidateRegions(&SelRng, 
01335                              Attrib, 
01336                              Mutate,
01337                              OtherAttrib,
01338                              IsMutate))  //Mutators have to include bounds
01339     {
01340         goto EndOperation;
01341     }
01342 
01343         
01344     if (!DoLocaliseForAttrChange(&SelRng, &AttrTypes))
01345     {
01346         goto EndOperation;
01347     }
01348 
01349     if (!ApplyToSelection(Attrib, Mutate))
01350     {
01351         goto EndOperation;
01352     }
01353     
01354     pAppliedAttrib = Attrib;    
01355     Attrib = OtherAttrib;
01356 
01357     // Karim 26/06/2000
01358     // Some attributes (only AttrLineWidth at the mo') need to *selectively*
01359     // apply the second attribute - hence it must *not* be applied here.
01360     if (Attrib != NULL && !pAppliedAttrib->OtherAttrIsAppliedSelectively())
01361     {
01362         if (!ApplyToSelection(Attrib, IsMutate))
01363         {
01364             delete Attrib;
01365             goto EndOperation;
01366         }
01367     }
01368     
01369     // Invalidate after
01370     if (!DoInvalidateRegions(&SelRng, 
01371                              pAppliedAttrib, 
01372                              Mutate,
01373                              OtherAttrib,
01374                              IsMutate))  //Mutators have to include bounds
01375     {
01376         delete Attrib;
01377         goto EndOperation; 
01378     }
01379 
01380     delete Attrib;
01381     
01382     // Having applied the attributes, we  must now try and factor out the newly 
01383     // applied attributes
01384     if (!DoFactorOutAfterAttrChange(&SelRng, &AttrTypes))
01385     {
01386         goto EndOperation;
01387     }
01388 
01389     AttrFillGeometry::LastRenderedMesh = NULL;
01390 
01391     if (Document::GetSelected())
01392         Document::GetSelected()->SetModified(TRUE);
01393 
01394     ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this);
01395     if (!UpdateChangedNodes(&ObjChange))
01396         FailAndExecute();
01397 
01398     AttributeManager::LastAppliedBounds = Sel->GetBoundingRect();
01399 
01400     EndOperation:           
01401     EndSlowJob();
01402 
01403     AttrTypes.DeleteAll();
01404 
01405 #endif
01406     End(); // End of operation
01407 } 
01408 
01409 /********************************************************************************************
01410 
01411 >   BOOL OpApplyAttribToSelected::ApplyToSelection(NodeAttribute* Attrib, BOOL Mutate)         
01412 
01413     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01414     Created:    5/4/95
01415     Inputs:     -
01416     Outputs:    -
01417     Returns:    TRUE if successful 
01418                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
01419 
01420 
01421     Purpose:    
01422     Errors:     -
01423     SeeAlso:    -
01424 
01425 ********************************************************************************************/
01426 
01427 BOOL OpApplyAttribToSelected::ApplyToSelection(NodeAttribute* Attrib, BOOL Mutate)         
01428 {
01429     return DoApplyToSelection(this, Attrib, Mutate, TRUE);              // Do undo and Do optimisations
01430 }
01431 
01432 BOOL OpApplyAttrib::DoApplyToSelection(SelOperation* pOp, NodeAttribute* Attrib, BOOL bMutate, BOOL bOptimise)
01433 {
01434     SelRange* Sel = GetApplication()->FindSelection();
01435     ERROR3IF(Sel==NULL,"Can't find SelRange!");
01436     if (Sel==NULL) return FALSE;
01437     Node* FirstSelNode = Sel->FindFirst();
01438     BOOL bAttrWasRequired = FALSE;
01439 
01440     // Find out which spread the selection is on
01441     if (FirstSelNode != NULL)     // Can't do anything if nothing is selected
01442     {   
01443         ERROR2IF(!FirstSelNode->IsAnObject(), FALSE, "ApplyToSelection SelRange doesn't have an Ink node in it");
01444 //      NodeRenderableInk* FirstSelectedNode = (NodeRenderableInk*)FirstSelNode;
01445 //      Spread *pSpread = FirstSelectedNode->FindParentSpread();
01446 
01447         // Set up a range of all selected nodes 
01448         EffectsStack* pStack = Sel->GetEffectsStack(FALSE, FALSE);  // Get cached stack, don't escape derived objects (allow attrs to be applie to derived objects)
01449         ERROR3IF(pStack==NULL, "Failed to get PPStack in ApplyToSelection");
01450         INT32 stackpos = STACKPOS_TOP;
01451         Range* pLevel = pStack->GetLevelRange(&stackpos, FALSE);            // Don't escape old controllers, apply attr to base nodes
01452         Node* CurrentNode = pLevel->FindFirst();
01453 //      Node* FirstNode = CurrentNode;
01454 
01455         BOOL bCanDiscardUndo = TRUE;
01456 
01457         // While there are still nodes to apply the attribute to
01458         while (CurrentNode != NULL)
01459         {
01460             // move to the next one but remember the current one
01461             Node* OldNode = CurrentNode;
01462             CurrentNode = pLevel->FindNext(CurrentNode);
01463 
01464             if (OldNode->IsAnObject())
01465             {
01466                 // Scan up through the stack to find the appropriate level to apply this attribute
01467                 NodeRenderableInk * pNodeToApply = OpApplyAttrib::FindCompoundNodeWhichRequiresAttribute((NodeRenderableInk *)OldNode,
01468                                                                                 Attrib->GetRuntimeClass());
01469 
01470                 if (pNodeToApply != OldNode)
01471                 {
01472                     // apply the attribute to all ink-children of the given node.
01473                     Node * pChildNode = pNodeToApply->FindFirstChild();
01474                     
01475                     while (pChildNode != NULL)
01476                     {
01477                         Node * pNextChildNode = pChildNode->FindNext();
01478 
01479                         if (pChildNode->IsAnObject() && ((NodeRenderableInk*)pChildNode)->RequiresAttrib(Attrib))
01480                         {
01481                             if (!DoApply(pOp, pChildNode, Attrib, bMutate, FALSE, TRUE, bOptimise, &bAttrWasRequired, &bCanDiscardUndo))    // Will only apply Attrib if the node requires it.
01482                                 return FALSE; 
01483                         }
01484 
01485                         pChildNode = pNextChildNode;
01486                     }
01487                 }
01488                 else
01489                 {
01490                     if (!DoApply(pOp, OldNode, Attrib, bMutate, FALSE, TRUE, bOptimise, &bAttrWasRequired, &bCanDiscardUndo))    // Will not apply Attrib if the node does
01491                     // not require it.
01492                     {
01493                         return FALSE; 
01494                     }
01495                 }
01496 
01497 
01498                 // now, if the node to apply is still a parent of the other nodes, skip past them
01499                 if (pNodeToApply != OldNode && pNodeToApply->IsCompoundClass())
01500                 {
01501                     if (((NodeCompound*)pNodeToApply)->PromoteAttributeApplicationToMe(Attrib->GetRuntimeClass()))
01502                     {
01503                         while (CurrentNode != NULL && CurrentNode->FindParent(pNodeToApply->GetRuntimeClass()) == pNodeToApply)
01504                         {
01505                             CurrentNode = pLevel->FindNext(CurrentNode);
01506                         }
01507                     }
01508                 }
01509             }
01510             else
01511             {
01512                 if (!DoApply(pOp, OldNode, Attrib, bMutate, FALSE, TRUE, bOptimise, &bAttrWasRequired, &bCanDiscardUndo))    // Will not apply Attrib if the node does not require it.
01513                 {
01514                     return FALSE; 
01515                 }
01516             }
01517 
01518             // a bodge to handle the case when a line width is applied to a transparent line.
01519             // what we want in this case is to change the line colour (as well as the line width)
01520             // so the line becomes visible. Black colour is set on those lines.
01521 
01522             if ((IS_A(Attrib, AttrLineWidth)) && OldNode->IsAnObject())
01523             {
01524                 NodeRenderableInk *pRendNode = ((NodeRenderableInk*)OldNode)->GetObjectToApplyTo(Attrib->GetAttributeType());
01525 
01526                 // find the line colour attribute applied to that node
01527                 NodeAttribute *pAppliedAttr = NULL;
01528                 pRendNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), &pAppliedAttr);
01529                 if (!pAppliedAttr) // should always find one 
01530                     continue; 
01531 
01532                 // get the colour attribute from it
01533                 ColourFillAttribute *pColAttr = (ColourFillAttribute *)pAppliedAttr->GetAttributeValue();
01534                 if (!pColAttr)
01535                     continue;
01536 
01537                 // get the actual colour
01538                 DocColour *Col = pColAttr->GetStartColour();
01539                 if (!Col)
01540                     continue;
01541                 
01542                 // check if the line colour for that node is set to 'no colour'
01543                 if (Col->IsTransparent())
01544                 {
01545                     // we have to replace the applied attribute with black colour
01546 
01547                     // create a new stroke colour attribute, 
01548                     AttrStrokeColour *pAttr = new AttrStrokeColour();
01549                     if (pAttr == NULL)
01550                         continue;
01551 
01552                     // and set it with black colour
01553                     DocColour ColourToApply(COLOUR_BLACK);
01554                     ((AttrStrokeColour *)pAttr)->SetStartColour(&ColourToApply);
01555 
01556                     // apply the new line colour to the node
01557                     if (!DoApply(pOp, OldNode, pAttr, bMutate, FALSE, FALSE, bOptimise, &bAttrWasRequired, &bCanDiscardUndo))
01558                         continue; 
01559 
01560                     // remove the attribute
01561                     delete pAttr;
01562                     pAttr = NULL;
01563                 }
01564             }
01565 
01566             ContinueSlowJob();
01567         }
01568 
01569         // Nasty textism.  EOL nodes can now have attributes, but the last EOL cannot be selected by
01570         // the user, causing the text tool to show <multiple> when it shouldn't really.
01571         // So if the last node in the range is the node before the last EOL in a text story then we
01572         // apply the attribute to the EOL too.
01573         Node* pLastSelected = pLevel->FindLast();
01574         if (pLastSelected!=NULL && pLastSelected->IsABaseTextClass())
01575         {
01576             // Alternative approach to fix other EOLNode problems that have appeared. (Bugs 5371 & 5372)
01577             // If the AbstractTextChar following the last selected node is an EOLNode then apply to it too.
01578             if (pLastSelected->IsAVisibleTextNode())
01579             {
01580                 AbstractTextChar* pNextATChar = (AbstractTextChar*)pLastSelected->FindNext(CC_RUNTIME_CLASS(AbstractTextChar));
01581                 if (pNextATChar!=NULL && pNextATChar->IsAnEOLNode())
01582                 {
01583                     if (!DoApply(pOp, pNextATChar, Attrib, bMutate, FALSE, TRUE, bOptimise, &bAttrWasRequired, &bCanDiscardUndo))
01584                         return FALSE; 
01585                 }
01586             }
01587 
01588 //          TextStory* pStory = ((BaseTextClass*)pLastSelected)->FindParentStory();
01589 //          if (pStory!=NULL)
01590 //          {
01591 //              // Get a pointer to the last EOL in the story
01592 //              VisibleTextNode* pLastEOL = pStory->FindLastVTN();
01593 //              ERROR3IF(!pLastEOL->IsAnEOLNode(), "Last node in story was not EOL");
01594 //
01595 //              // is the EOL next to the last node in the selected?
01596 //              if (pLastEOL!=NULL && pLastEOL==pLastSelected->FindNext(CC_RUNTIME_CLASS(AbstractTextChar)))
01597 //              {
01598 //                  if (!DoApply(pLastEOL, Attrib, Mutate, FALSE))
01599 //                      return FALSE; 
01600 //              }
01601 //          }
01602         }
01603 
01604         // If all nodes report DiscardsAttributeChildren() = TRUE
01605         // Then we can abandon the entire Op once it's finished
01606         if (bCanDiscardUndo)
01607             pOp->SucceedAndDiscard();
01608 
01609     }
01610     return TRUE;
01611 }
01612 
01613 
01614 /********************************************************************************************
01615 
01616 >   BOOL OpApplyAttribToSelected::Init()
01617 
01618     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01619     Created:    28/9/93
01620     Inputs:     -
01621     Outputs:    -
01622     Returns:    TRUE if the operation could be successfully initialised 
01623                 FALSE if no more memory could be allocated 
01624                 
01625     Purpose:    OpApplyAttribToSelected initialiser method
01626     Errors:     ERROR will be called if there was insufficient memory to allocate the 
01627                 operation.
01628     SeeAlso:    -
01629 
01630 ********************************************************************************************/
01631 
01632 BOOL OpApplyAttribToSelected::Init()
01633 {
01634     // Register the opdescriptors for the OpApplyAttribToSelected operation
01635     OpDescriptor* OpDesc = new OpDescriptor(
01636                                             0,
01637                                             _R(IDS_APPLYATTRIBOP),              
01638                                             CC_RUNTIME_CLASS(OpApplyAttribToSelected),
01639                                             OPTOKEN_APPLYATTRIB,
01640                                             OpApplyAttribToSelected::GetState); 
01641     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
01642 
01643     OpDesc = new OpDescriptor(
01644                                             0,
01645                                             _R(IDS_APPLYATTR_INTERACTIVE),
01646                                             CC_RUNTIME_CLASS(OpApplyAttrInteractive),
01647                                             OPTOKEN_APPLYATTRINTERACTIVE,
01648                                             OpApplyAttrInteractive::GetState);
01649     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
01650 
01651 
01652         // Bodge this needs to use faby Macro technology so that the op is attached to
01653         // a system bar.
01654     OpDesc = new OpChangeLineWidthOpDesc(0,
01655                                          _R(IDS_CHANGELINEWIDTH),
01656                                          CC_RUNTIME_CLASS(OpApplyAttribToSelected),
01657                                          OPTOKEN_CHANGELINEWIDTH,
01658                                          OpChangeLineWidthOpDesc::GetState,
01659                                          0,
01660                                          _R(IDBBL_CHANGELINEWIDTH),
01661                                          _R(IDCB_LINEWIDTH_COMBO_BOX),
01662                                          _R(IDCB_LINEWIDTH_COMBO_BOX));
01663     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
01664 
01665     if (!OpRepeatApplyAttribToSelected::Init())
01666         return FALSE;   
01667     
01668     return TRUE;
01669 }               
01670 
01671 /********************************************************************************************
01672 
01673 >   OpState OpApplyAttribToSelected::GetState(String_256*, OpDescriptor* OpDesc)
01674 
01675     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01676     Created:    28/9/93
01677     Inputs:     -
01678     Outputs:    -
01679     Returns:    The state of the OpApplyAttribToSelected operation
01680     Purpose:    For finding OpApplyAttribToSelected state. 
01681     Errors:     -
01682     SeeAlso:    -
01683 
01684 ********************************************************************************************/
01685 
01686 OpState OpApplyAttribToSelected::GetState(String_256*, OpDescriptor* OpDesc)
01687 {
01688     OpState OpSt;
01689 
01690     SelRange* Sel = GetApplication()->FindSelection();
01691     ERROR2IF(Sel==NULL,OpSt,"Can't find SelRange!");
01692 
01693     // The Operation is greyed if there are no nodes selected 
01694     OpSt.Greyed = ( (Sel->FindFirst() == NULL) );  
01695                                                 
01696     return(OpSt);   
01697 }
01698 
01699 
01700 /********************************************************************************************
01701 
01702 >   virtual void OpApplyAttribToSelected::GetOpName(String_256* OpName) 
01703 
01704     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01705     Created:    21/2/94
01706     Inputs:     -
01707     Outputs:    The undo string for the operation
01708     Returns:    
01709     Purpose:    The GetOpName fn is overridden so that we return back a description 
01710                 appropriate to the type of attribute that the operation applies. 
01711                     
01712     Errors:     -
01713     SeeAlso:    -
01714 
01715 ********************************************************************************************/
01716 
01717 void OpApplyAttribToSelected::GetOpName(String_256* OpName) 
01718 { 
01719     
01720     *OpName = String_256(UndoAttribStrID); 
01721     *OpName += String_256(_R(IDS_CHANGE)); 
01722 }  
01723 
01724 
01725 
01726 
01727 
01728 
01729 
01730 
01731 // -----------------------------------------------------------------------------------------
01732 // OpApplyAttrInteractive methods
01733 
01734 /********************************************************************************************
01735 
01736 >   OpApplyAttrInteractive::OpApplyAttrInteractive() 
01737 
01738     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01739     Created:    10/08/2005
01740     Inputs:     -
01741     Outputs:    -
01742     Returns:    -
01743     Purpose:    OpApplyAttrInteractive constructor
01744     Errors:     -
01745     SeeAlso:    -
01746 
01747 ********************************************************************************************/
01748 
01749 OpApplyAttrInteractive::OpApplyAttrInteractive(): OpApplyAttrib()                               
01750 {                              
01751 }
01752 
01753 
01754 
01755 
01756 /********************************************************************************************
01757 
01758 >   OpState OpApplyAttrInteractive::GetState(String_256*, OpDescriptor* OpDesc)
01759 
01760     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01761     Created:    10/08/2005
01762     Inputs:     -
01763     Outputs:    -
01764     Returns:    The state of the OpApplyAttrInteractive operation
01765     Purpose:    For finding OpApplyAttrInteractive state. 
01766     Errors:     -
01767     SeeAlso:    -
01768 
01769 ********************************************************************************************/
01770 
01771 OpState OpApplyAttrInteractive::GetState(String_256*, OpDescriptor* OpDesc)
01772 {
01773     OpState OpSt;
01774 
01775     SelRange* Sel = GetApplication()->FindSelection();
01776     ERROR2IF(Sel==NULL,OpSt,"Can't find SelRange!");
01777 
01778     // The Operation is greyed if there are no nodes selected 
01779     OpSt.Greyed = ( (Sel->FindFirst() == NULL) );  
01780                                                 
01781     return OpSt;
01782 }
01783 
01784 
01785 
01786 
01787 /********************************************************************************************
01788 
01789 >   virtual void OpApplyAttrInteractive::GetOpName(String_256* OpName) 
01790 
01791     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01792     Created:    10/08/2005
01793     Inputs:     -
01794     Outputs:    The undo string for the operation
01795     Returns:    
01796     Purpose:    The GetOpName fn is overridden so that we return back a description 
01797                 appropriate to the type of attribute that the operation applies.
01798     Errors:     -
01799     SeeAlso:    -
01800 
01801 ********************************************************************************************/
01802 
01803 void OpApplyAttrInteractive::GetOpName(String_256* OpName) 
01804 { 
01805     *OpName = String_256(m_UndoAttribStrID); 
01806     *OpName += String_256(_R(IDS_CHANGE)); 
01807 }  
01808 
01809 
01810 
01811 
01812 /********************************************************************************************
01813 
01814 >   virtual BOOL OpApplyAttrInteractive::DoDragAttr(AttrValueChange* pNewValue)
01815 
01816     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01817     Created:    10/08/2005
01818     Inputs:     -
01819     Outputs:    -
01820     Returns:    -
01821     Purpose:    -
01822     Errors:     -
01823     SeeAlso:    -
01824 
01825 ********************************************************************************************/
01826 
01827 BOOL OpApplyAttrInteractive::DoDragAttr(AttrValueChange* pNewValue)
01828 {
01829     // ApplyAttribute without optimisations, without undo, get list of attributes affected and their initial values
01830     // Assume the value supplied is a "Mutator"
01831     AttrFillGeometry* pMutator = NULL;
01832     //BOOL MakeAttrCurrent = FALSE; // until we know better
01833     BOOL ApplyAttribute = FALSE;
01834 
01835     ERROR2IF(pNewValue==NULL, FALSE, "DoDragattr not given a decent attribute pointer");
01836     ERROR3IF(!pNewValue->IsAFillAttr(), "A Mutator must be a fill attribute");
01837     pMutator = (AttrFillGeometry*)pNewValue;
01838 
01839     SelRange* Sel = GetApplication()->FindSelection();
01840     ERROR3IF(Sel==NULL, "Can't find SelRange!");
01841     if (Sel==NULL) return FALSE;
01842 
01843     ApplyAttribute = AttributeManager::CanBeAppliedToSelection(pMutator, &m_AttrGroupList);
01844 
01845     if (ApplyAttribute)
01846     {
01847         // ------------------------------------------------------------------
01848         // Apply attributes without preamble and without optimisation or
01849         // localisation so that dragging starts up quickly.
01850         // Noe that the initial application is done undoably so that the intial
01851         // states of the attributes are recorded (see DragAttrFinished).
01852         //
01853         // Some Attributes require a second attribute to be changed as well,
01854         // which has to be done within this op.
01855         BOOL bMutateOther;
01856         NodeAttribute* pOtherAttrib = AttributeManager::GetOtherAttrToApply(pNewValue, &bMutateOther);
01857 
01858         // Apply attributes with undo - The undo info is used to record the initial states
01859         // of the attributes so that the drag can be abandoned correctly and finalised correctly
01860         DoApplyToSelection(this, pNewValue, TRUE, FALSE);       // Don't allow optimisations yet
01861 
01862         if (pOtherAttrib && !pNewValue->OtherAttrIsAppliedSelectively())
01863             DoApplyToSelection(this, pOtherAttrib, bMutateOther, FALSE);
01864 
01865         if (pOtherAttrib)
01866         {
01867             delete pOtherAttrib;
01868             pOtherAttrib = NULL;
01869         }
01870 
01871 //      StartDrag(DRAGTYPE_NOSCROLL);
01872         Operation::SetQuickRender(TRUE, this);
01873 
01874         // Quick redraw
01875         Sel->ForceRedrawView(GetWorkingDocView(), TRUE, TRUE, TRUE);
01876         GetWorkingDocView()->FlushRedraw();
01877         GetApplication()->ServiceRendering();
01878     }
01879 
01880     return ApplyAttribute;
01881 }
01882 
01883 
01884 
01885 
01886 /********************************************************************************************
01887 
01888 >   virtual BOOL OpApplyAttrInteractive::DragAttrChanged(AttrValueChange* pNewValue)
01889 
01890     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01891     Created:    10/08/2005
01892     Inputs:     -
01893     Outputs:    -
01894     Returns:    -
01895     Purpose:    -
01896     Errors:     -
01897     SeeAlso:    -
01898 
01899 ********************************************************************************************/
01900 
01901 BOOL OpApplyAttrInteractive::DragAttrChanged(AttrValueChange* pNewValue)
01902 {
01903     // Change attrs in the list and update without optimisation, without undo
01904     SelRange* Sel = GetApplication()->FindSelection();
01905     ERROR3IF(Sel==NULL, "Can't find SelRange!");
01906     if (Sel==NULL) return FALSE;
01907 
01908     BOOL bMutateOther;
01909     NodeAttribute* pOtherAttrib = AttributeManager::GetOtherAttrToApply(pNewValue, &bMutateOther);
01910 
01911     DoApplyToSelection(NULL, pNewValue, TRUE, FALSE);       // Don't allow optimisations yet
01912 
01913     if (pOtherAttrib && !pNewValue->OtherAttrIsAppliedSelectively())
01914         DoApplyToSelection(NULL, pOtherAttrib, bMutateOther, FALSE);
01915 
01916     if (pOtherAttrib)
01917     {
01918         delete pOtherAttrib;
01919         pOtherAttrib = NULL;
01920     }
01921 
01922     // Quick redraw
01923     Sel->ForceRedrawView(GetWorkingDocView(), TRUE, TRUE, TRUE);
01924     GetWorkingDocView()->FlushRedraw();
01925     GetApplication()->ServiceRendering();
01926 
01927     return TRUE;
01928 }
01929 
01930 
01931 
01932 
01933 /********************************************************************************************
01934 
01935 >   virtual BOOL OpApplyAttrInteractive::DragAttrFinished(AttrValueChange* pNewValue, BOOL bSuccess)
01936 
01937     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
01938     Created:    10/08/2005
01939     Inputs:     -
01940     Outputs:    -
01941     Returns:    -
01942     Purpose:    -
01943     Errors:     -
01944     SeeAlso:    -
01945 
01946 ********************************************************************************************/
01947 
01948 BOOL OpApplyAttrInteractive::DragAttrFinished(AttrValueChange* pNewValue, BOOL bSuccess)
01949 {
01950     SelRange* Sel = GetApplication()->FindSelection();
01951     ERROR3IF(Sel==NULL, "Can't find SelRange!");
01952     if (Sel==NULL)
01953     {
01954         FailAndExecute();
01955         End();
01956         return FALSE;
01957     }
01958 
01959 //  bSuccess = EndDrag();
01960     Operation::SetQuickRender(FALSE, this);
01961 
01962     if (!bSuccess)
01963     {
01964         // Just restore back to how things were when the drag started
01965         BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::NONCOLOURATTCHANGED)); 
01966         FailAndExecute();
01967         End();
01968         return FALSE;
01969     }
01970 
01971     BeginSlowJob();
01972 
01973     // ---------------------------------------------------------------------
01974     // Before we record the real undo info
01975     // restore the original attributes by executing the undo info
01976     // recorded when the drag started.
01977     //
01978     // This is a bit low level but it leverages the existing undo system to
01979     // record the initial states of the attributes rather than writing
01980     // something new to do the same job.
01981     //
01982     FailAndExecute();
01983     KeepOnEnd();            // This ensures that this Op is not self-deleted inside End()!
01984     End();
01985 
01986     // -----------------------------------------------------------------
01987     m_AttrTypes.DeleteAll();
01988     AttributeManager::pLastNodeAppliedTo = NULL;
01989     ValueChangeType = pNewValue->GetRuntimeClass();
01990     BOOL RedrawBlobs = FALSE;
01991     NodeAttribute* pOtherAttrib = NULL;
01992 
01993     ObjChangeFlags cFlags;
01994     cFlags.Attribute = TRUE;
01995     ObjChangeParam ObjChange(OBJCHANGE_STARTING, cFlags, NULL, this);
01996 
01997     if (!Sel->AllowOp(&ObjChange))
01998     {
01999         FailAndExecute();
02000         goto EndOperation;
02001     }
02002 
02003     // Get a description of the attribute being applied so that we can use it to create the 
02004     // undo string. 
02005     m_UndoAttribStrID = pNewValue->GetAttrNameID();
02006 
02007     if (!DoStartSelOp(RedrawBlobs, RedrawBlobs, TRUE, TRUE))
02008     {
02009         FailAndExecute();
02010         goto EndOperation;
02011     }
02012 
02013     // Some Attributes require a second attribute to be changed as well,
02014     // which has to be done within this op.
02015     BOOL bMutateOther;
02016     pOtherAttrib = AttributeManager::GetOtherAttrToApply(pNewValue, &bMutateOther);
02017             
02018     // Before we apply the attribute to the selection we must localise all attributes
02019     // with the same type that we are going to apply. If we don't do this then the
02020     // tree will be left in an invalid state. 
02021     m_AttrTypes.AddToSet((pNewValue->GetAttributeType())); 
02022     if (pOtherAttrib)
02023         m_AttrTypes.AddToSet((pOtherAttrib->GetAttributeType())); 
02024     
02025     // Invalidate before
02026     if (!DoInvalidateRegions(Sel, 
02027                              pNewValue, 
02028                              TRUE,              // Always mutate new value attribute
02029                              pOtherAttrib,
02030                              bMutateOther))     // Mutators have to include bounds
02031     {
02032         FailAndExecute();
02033         goto EndOperation;
02034     }
02035 
02036     if (!DoLocaliseForAttrChange(Sel, &m_AttrTypes))
02037     {
02038         FailAndExecute();
02039         goto EndOperation;
02040     }
02041 
02042     // -----------------------------------------------------------------
02043     DoApplyToSelection(this, pNewValue, TRUE, TRUE);        // Now we can undo, Now we can optimise!
02044 
02045     if (pOtherAttrib && !pNewValue->OtherAttrIsAppliedSelectively())
02046         DoApplyToSelection(this, pOtherAttrib, bMutateOther, TRUE);
02047 
02048     if (pOtherAttrib)
02049     {
02050         delete pOtherAttrib;
02051         pOtherAttrib = NULL;
02052     }
02053 
02054     // -----------------------------------------------------------------
02055     // Invalidate after
02056     if (!DoInvalidateRegions(Sel,
02057                              pNewValue, 
02058                              TRUE,
02059                              pOtherAttrib,
02060                              bMutateOther))  //Mutators have to include bounds
02061     {
02062         FailAndExecute();
02063         goto EndOperation;
02064     }
02065 
02066     // Having applied the attributes, we  must now try and factor out the newly 
02067     // applied attributes
02068     if (!DoFactorOutAfterAttrChange(Sel, &m_AttrTypes))
02069     {
02070         FailAndExecute();
02071         goto EndOperation;
02072     }
02073 
02074     AttrFillGeometry::LastRenderedMesh = NULL;
02075 
02076     if (Document::GetSelected())
02077         Document::GetSelected()->SetModified(TRUE);
02078 
02079     cFlags.Attribute = TRUE;
02080     ObjChange.Define(OBJCHANGE_FINISHED, cFlags, NULL, this);
02081     if (!UpdateChangedNodes(&ObjChange))
02082         FailAndExecute();
02083 
02084     AttributeManager::LastAppliedBounds = Sel->GetBoundingRect();
02085 
02086     // -----------------------------------------------------------------
02087     AttributeManager::UpdateCurrentAppliedAttr(pNewValue, &m_AttrGroupList, TRUE, TRUE);
02088     AttributeManager::UpdateAfterAttrApplied(pNewValue);
02089 
02090 // ------------------------------------------
02091 EndOperation:           
02092     EndSlowJob();
02093 
02094     m_AttrTypes.DeleteAll();
02095     m_AttrGroupList.DeleteAll();
02096 
02097     End(); // End of operation
02098 
02099     Sel->BroadcastAnyPendingMessages();
02100 
02101     return TRUE;
02102 }
02103 
02104 
02105 
02106 
02107 /********************************************************************************************
02108 
02109 >   virtual void OpApplyAttrInteractive::PerformMergeProcessing() 
02110 
02111     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02112     Created:    01/10/2005
02113     Inputs:     -
02114     Outputs:    -
02115     Returns:    -
02116     Purpose:    This virtual function gets called to allow for the merging of nudge operations
02117     Errors:     -
02118     SeeAlso:    -
02119 
02120 ********************************************************************************************/
02121 
02122 void OpApplyAttrInteractive::PerformMergeProcessing()
02123 {    
02124     // Obtain a pointer to the operation history for the current document
02125     OperationHistory* pOpHist = &pOurDoc->GetOpHistory();
02126 
02127     // Ensure that we are the last operation added to the operation history
02128     // Note cannot be an ERROR2 cos this function cannot fail. 
02129     ERROR3IF(pOpHist->FindLastOp() != this, "Last Op should be this op"); 
02130     if (pOpHist->FindLastOp() != this)
02131         return;
02132     
02133     // OK lets see if the operation performed before this was an OpNudge operation
02134     Operation* pPrevOp = pOpHist->FindPrevToLastOp();
02135     if (pPrevOp == NULL)   // Check if there was a previous op
02136         return;
02137     
02138     if (!IS_A(pPrevOp, OpApplyAttrInteractive))
02139         return;
02140 
02141     // Yes it was
02142     // We can merge this op with pPrevOp if they both apply to the same set of objects
02143     // This will be TRUE is their SelectionStates are the same. 
02144     RestoreSelectionsAction* pRestoreSelAct = (RestoreSelectionsAction*)
02145         GetUndoActionList()->FindActionOfClass(CC_RUNTIME_CLASS(RestoreSelectionsAction));  
02146     ERROR3IF(pRestoreSelAct == NULL, "This op should have a RestoreSelectionsAction");
02147     SelectionState* ThisOpsSelection = pRestoreSelAct->GetSelState();
02148 
02149     pRestoreSelAct = (RestoreSelectionsAction*)
02150         pPrevOp->GetUndoActionList()->FindActionOfClass(CC_RUNTIME_CLASS(RestoreSelectionsAction));     
02151     ERROR3IF(pRestoreSelAct == NULL, "OpNudge op should have a RestoreSelectionsAction");
02152     SelectionState* LastOpsSelection = pRestoreSelAct->GetSelState();
02153 
02154     if (! ((*ThisOpsSelection) == (*LastOpsSelection)))
02155         return;
02156 
02157     ActionList* pPrevList = pPrevOp->GetUndoActionList();
02158     ActionList* pThisList = this->GetUndoActionList();
02159     if (pPrevList->GetCount()!=pThisList->GetCount())
02160         return;
02161 
02162     // Check that all actions are the same before we go any further...
02163     Action* pPrevAct = (Action*)pPrevList->GetHead();
02164     Action* pThisAct = (Action*)pThisList->GetHead();
02165     while (pPrevAct && pThisAct)
02166     {
02167         if (pPrevAct->GetRuntimeClass() != pThisAct->GetRuntimeClass())
02168             return;
02169 
02170         if (pPrevAct->IsKindOf(CC_RUNTIME_CLASS(UnApplyAction)))
02171         {
02172             UnApplyAction* pPrevApplyAct = (UnApplyAction*)pPrevAct;
02173             UnApplyAction* pThisApplyAct = (UnApplyAction*)pThisAct;
02174             if (pPrevApplyAct->m_pApplyNode != pThisApplyAct->m_pApplyNode)
02175                 return;
02176         }
02177 
02178         pPrevAct = (Action*)pPrevList->GetNext(pPrevAct);
02179         pThisAct = (Action*)pThisList->GetNext(pThisAct);
02180     }
02181 
02182     // Now we know all actions are the same, we can go ahead and transfer
02183     // The attribute information from this action list to the previous
02184     // action list...
02185     pPrevAct = (Action*)pPrevList->GetHead();
02186     pThisAct = (Action*)pThisList->GetHead();
02187     while (pPrevAct && pThisAct)
02188     {
02189         ERROR3IF(pPrevAct->GetRuntimeClass() != pThisAct->GetRuntimeClass(), "This can't happen!");
02190         if (pPrevAct->GetRuntimeClass() != pThisAct->GetRuntimeClass())
02191             return;
02192 
02193         if (pPrevAct->IsKindOf(CC_RUNTIME_CLASS(UnApplyAction)))
02194         {
02195             UnApplyAction* pPrevApplyAct = (UnApplyAction*)pPrevAct;
02196             UnApplyAction* pThisApplyAct = (UnApplyAction*)pThisAct;
02197             ERROR3IF(pPrevApplyAct->m_pApplyNode != pThisApplyAct->m_pApplyNode, "This can't happen!");
02198             if (pPrevApplyAct->m_pApplyNode != pThisApplyAct->m_pApplyNode)
02199                 return;
02200 
02201             // Update the previous UnApply action to hold the latest
02202             // attribute value from this Operation
02203             if (pPrevApplyAct->m_pAttribute)
02204                 delete pPrevApplyAct->m_pAttribute;
02205 
02206             pPrevApplyAct->m_pAttribute = pThisApplyAct->m_pAttribute;
02207 
02208             pThisApplyAct->m_pAttribute = NULL;
02209         }
02210 
02211         pPrevAct = (Action*)pPrevList->GetNext(pPrevAct);
02212         pThisAct = (Action*)pThisList->GetNext(pThisAct);
02213     }
02214 
02215     // This op is no longer required, so let's vape it
02216     pOpHist->DeleteLastOp();
02217 }
02218 
02219 
02220 
02221 
02222 /********************************************************************************************
02223 
02224 >   void OpApplyAttrInteractive::DragFinished(DocCoord PointerPos,
02225                                               ClickModifiers ClickMods,
02226                                               Spread* pSpread,
02227                                               BOOL Worked,
02228                                               BOOL bSolidDrag)
02229 
02230     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02231     Created:    14/08/2005
02232     Inputs:     PointerPos - The position of the mouse at the end of the drag
02233                 ClickMods - the keyboard modifiers in place
02234                 pSpread - the spread that was under the mouse
02235                 Worked - TRUE if the drag was completed successfully
02236     Purpose:    Marks the end of the drag. It is at this point that all the transformations
02237                 should be applied to the selection if everything worked.
02238                 OpApplyAttrInteractive implements this just to pick up Escape handling
02239                 through the Success flag
02240 
02241 ********************************************************************************************/
02242 
02243 void OpApplyAttrInteractive::DragFinished(DocCoord PointerPos,
02244                                           ClickModifiers ClickMods,
02245                                           Spread* pSpread,
02246                                           BOOL Success,
02247                                           BOOL bSolidDrag)
02248 {
02249     DragAttrFinished(NULL, Success);
02250 }
02251 
02252 
02253 
02254 
02255 // ---
02256 
02257 
02258 
02259 
02260 // -----------------------------------------------------------------------------------------
02261 // OpApplyAttribsToSelected methods
02262 
02263 /********************************************************************************************
02264 
02265 >   OpApplyAttribsToSelected::OpApplyAttribsToSelected() 
02266 
02267     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02268     Created:    29/9/93
02269     Purpose:    OpApplyAttribsToSelected constructor
02270 
02271 ********************************************************************************************/
02272             
02273             
02274 OpApplyAttribsToSelected::OpApplyAttribsToSelected(): OpApplyAttrib()                               
02275 {
02276     ValueChangeType = NULL;  // ??
02277     MergeRepeats = FALSE;    // ??
02278     m_pAttribList = NULL;
02279    
02280 }
02281 
02282 /********************************************************************************************
02283 
02284 >   void OpApplyAttribsToSelected::DoWithParam(OpDescriptor* OpDesc, 
02285                                                OpParam* pOpParam)
02286 
02287     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02288     Created:    16/8/93
02289     Inputs:     OpDescriptor: A pointer to the OpDescriptor invoking the operation
02290                 OpParam:      This must be a ApplyAttribsToSelectedParam type
02291 
02292                 OpParam->AttribsToApply: This is the list of attributes that is to be applied
02293                                          to the selection. Each item on this list must be
02294                                          a NodeAttributePtrItem.
02295 
02296                 OpParam->undoAttribStrID: Specifies the undo string to display (eg. Paste Atrributes)
02297     
02298     Outputs:    OpParam->AttrGroupList:  This list must be empty on entry to this function
02299                                          If (Success) then this list will contain one ListListItem
02300                                          per attribute on the AttribsToApplyList. Each of these
02301                                          items will point to a Set of  AttributeGroupItems.
02302 
02303                                          Attribute -> { Set of current attribute groups }
02304                                          
02305                                          Whenever an attribute is applied to an object, the Current
02306                                          AttributeGroup associated with the object is added to
02307                                          the attributes set of current attribute groups. 
02308                                          Therefore if an attribute's set is empty then we know
02309                                          that the attribute has not been applied.
02310 
02311                                             
02312                 OpParam->Success:        FALSE if the operation failed. i.e. FailAndExecute()
02313                                          was called.
02314 
02315                 anyAttrsApplied:         FALSE if the operation has not applied any attributes
02316     
02317     Purpose:    Performs the OpApplyAttribsToSelected operation. This tries to apply the list
02318                 of attributes in the OpParam->AttribsToApplyList to the selection. If no attribute
02319                 can be applied then the operation does not generate any actions, and so will
02320                 be a NOP and be destroyed during End().
02321                 
02322                 This Operation currently does not support mutation 
02323 
02324 ********************************************************************************************/
02325         
02326 void OpApplyAttribsToSelected::DoWithParam(OpDescriptor* OpDesc, 
02327                                             OpParam* pOpParam 
02328                                           )        
02329 {   
02330 #ifndef STANDALONE
02331     BeginSlowJob();
02332 
02333     UndoAttribStrID = ((ApplyAttribsToSelectedParam*)pOpParam)->UndoAttribStrID;    // TODO: Fix this?
02334     
02335     /*BOOL bOK = */DoApplyAttribsToSelection(pOpParam, TRUE);
02336 
02337     EndSlowJob();
02338 
02339 #else
02340     End();
02341 #endif  // STAND_ALONE
02342 
02343 } 
02344 
02345 
02346 /********************************************************************************************
02347 
02348 >   BOOL SelOperation::DoApplyAttribsToSelection(OpParam* pOpParam, BOOL bClearIfNoneApplied = FALSE)
02349 
02350     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> (from Simon)
02351     Created:    16/02/2005 (from 16/8/93)
02352     Inputs:     OpParam:      This must be a ApplyAttribsToSelectedParam type
02353 
02354                 OpParam->AttribsToApply: This is the list of attributes that is to be applied
02355                                          to the selection. Each item on this list must be
02356                                          a NodeAttributePtrItem.
02357 
02358                 OpParam->undoAttribStrID: Specifies the undo string to display (eg. Paste Atrributes)
02359     
02360     Outputs:    OpParam->AttrGroupList:  This list must be empty on entry to this function
02361                                          If (Success) then this list will contain one ListListItem
02362                                          per attribute on the AttribsToApplyList. Each of these
02363                                          items will point to a Set of  AttributeGroupItems.
02364 
02365                                          Attribute -> { Set of current attribute groups }
02366                                          
02367                                          Whenever an attribute is applied to an object, the Current
02368                                          AttributeGroup associated with the object is added to
02369                                          the attributes set of current attribute groups. 
02370                                          Therefore if an attribute's set is empty then we know
02371                                          that the attribute has not been applied.
02372 
02373                                             
02374                 OpParam->Success:        FALSE if the operation failed. i.e. FailAndExecute()
02375                                          was called.
02376 
02377                 anyAttrsApplied:         FALSE if the operation has not applied any attributes
02378     
02379     Purpose:    Performs the OpApplyAttribsToSelected operation. This tries to apply the list
02380                 of attributes in the OpParam->AttribsToApplyList to the selection. If no attribute
02381                 can be applied then the operation does not generate any actions, and so will
02382                 be a NOP and be destroyed during End().
02383                 
02384                 This Operation currently does not support mutation 
02385 
02386 ********************************************************************************************/
02387 
02388 BOOL SelOperation::DoApplyAttribsToSelection(OpParam* pOpParam, BOOL bClearIfNoneApplied)
02389 {   
02390     // Ensure that the OpParam is valid
02391     ERROR3IF(!pOpParam, "No parameters passed to OpApplyAttribsToSelected");
02392     ApplyAttribsToSelectedParam* pParam = (ApplyAttribsToSelectedParam*)pOpParam;
02393     // get inputs        
02394     List* pAttrsToApply = pParam->AttribsToApply;
02395 //  m_pAttribList = pAttrsToApply;
02396 
02397 //  UndoAttribStrID = pParam->UndoAttribStrID;  // TODO: Fix this?
02398 
02399     // and outputs
02400     List* AttrGroupList = pParam->AttrGroupList;
02401     ERROR3IF(!(AttrGroupList->IsEmpty()), 
02402         "AttributeGroupList not empty on entry to OpApplyAttribsToSelected"); 
02403     BOOL* Success = pParam->Success;
02404     *Success = FALSE;                   // Let's be pessimistic
02405     BOOL* AppliedAttrs = pParam->AnyAttrsApplied;
02406     *AppliedAttrs = FALSE;
02407     BOOL bAnyApplied = FALSE;
02408     BOOL bWeStartedThisOp = FALSE;
02409  
02410     // Create a stable range of objects to apply to
02411     Range SelRng(*(GetApplication()->FindSelection()));
02412     
02413     // We need two sets of attribute types
02414     // one for Line level attributes, and one for non Line level attributes. This is because the localise
02415     // and factor out routines require each attribute in the set to be all applicable to the same 
02416     // object. This would not be true if our set contained a BOLD, and a LINE_SPACING attribute for example.
02417         
02418     // Make our attribute type sets
02419     AttrTypeSet LineLevelAttrTypes;
02420     AttrTypeSet OtherAttrTypes;
02421     NodeAttributePtrItem* pAttrItem = (NodeAttributePtrItem*)(pAttrsToApply->GetHead());
02422     NodeAttribute* pAttr;
02423     AttrTypeSet* SetToAddTo;
02424     while (pAttrItem)
02425     {
02426         pAttr = pAttrItem->NodeAttribPtr;
02427         ERROR3IF(!pAttr, "Should be an attribute");
02428         if (pAttr->IsALineLevelAttrib())
02429             SetToAddTo = &LineLevelAttrTypes;
02430         else
02431             SetToAddTo = &OtherAttrTypes;
02432 
02433         if (!SetToAddTo->AddToSet(pAttr->GetAttributeType()))
02434         {
02435             FailAndExecute();
02436             goto EndOperation;
02437         }
02438         pAttrItem = (NodeAttributePtrItem*)(pAttrsToApply->GetNext(pAttrItem));
02439     }
02440 
02441     // -----------------------------------------------------------
02442     if (SelRng.FindFirst()!=NULL)
02443     {
02444         SelRange* Sel;
02445         ObjChangeFlags cFlags;
02446         cFlags.Attribute = TRUE;
02447         ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
02448 
02449         Sel = GetApplication()->FindSelection(); 
02450 
02451         if (!Sel->AllowOp(&ObjChange))
02452         {
02453             FailAndExecute();
02454             goto EndOperation;
02455         }
02456     }
02457 
02458     if (!GetStarted())
02459     {
02460         if (!DoStartSelOp(FALSE,FALSE, TRUE,TRUE))   // Try to record the selection state , don't
02461                                                      // render the blobs though 
02462             goto EndOperation;
02463 
02464         bWeStartedThisOp = TRUE;
02465     }
02466 
02467     // Make a stable range
02468 //  Range SelRng(*(GetApplication()->FindSelection()));
02469 
02470     // Invalidate objects regions (include blobs because this may be neccessary, 
02471     //                             there is little overhead anyway).
02472     if (!DoInvalidateNodesRegions(SelRng, TRUE, FALSE))
02473         goto EndOperation;
02474 
02475     // Before we apply the attribute to the selection we must localise all attributes
02476     // with the same type that we are going to apply. If we don't do this then the
02477     // tree will be left in an invalid state. 
02478     
02479     if (!LineLevelAttrTypes.IsEmpty() && SelRng.FindFirst()!=NULL)
02480     {
02481         if (!DoLocaliseForAttrChange(&SelRng, &LineLevelAttrTypes))
02482             goto EndOperation;
02483     }
02484 
02485     if (!OtherAttrTypes.IsEmpty() && SelRng.FindFirst()!=NULL)
02486     {
02487         if (!DoLocaliseForAttrChange(&SelRng, &OtherAttrTypes))
02488             goto EndOperation;
02489     }
02490 
02491     // -----------------------------------------------------------
02492     if (!DoApplyToSelection(pAttrsToApply, AttrGroupList, &bAnyApplied))
02493     {
02494         goto EndOperation;
02495     }
02496 
02497     // -----------------------------------------------------------
02498     // If attributes were applied we must factor them out
02499     if (bAnyApplied)
02500     {
02501         // Having applied the attributes, we  must now try and factor out the newly 
02502         // applied attributes
02503         if (!LineLevelAttrTypes.IsEmpty())
02504         {
02505             if (!DoFactorOutAfterAttrChange(&SelRng, 
02506                                         &LineLevelAttrTypes))
02507             {
02508                 goto EndOperation;
02509             }
02510         }
02511 
02512         if (!OtherAttrTypes.IsEmpty())
02513         {
02514             if (!DoFactorOutAfterAttrChange(&SelRng, 
02515                                         &OtherAttrTypes))
02516             {
02517                 goto EndOperation;
02518             }
02519         }
02520 
02521         {   
02522             ObjChangeFlags cFlags;
02523             cFlags.Attribute = TRUE;
02524 
02525             ObjChangeParam ObjChange(OBJCHANGE_FINISHED,cFlags,NULL,this);
02526             if (!UpdateChangedNodes(&ObjChange))
02527             {
02528                 FailAndExecute();
02529                 goto EndOperation;
02530             }
02531         }
02532 
02533         // Invalidate after
02534         if (!DoInvalidateNodesRegions(SelRng, TRUE, FALSE))
02535         {
02536             goto EndOperation;
02537         }
02538     }
02539     else if (bClearIfNoneApplied)
02540     {
02541         // We applied no attributes so we can just chuck this undo record
02542         // and all its actions away
02543         SucceedAndDiscard();
02544     }
02545 
02546     // Output values
02547     *Success = TRUE;                // If we got here we must have succeeded
02548     *AppliedAttrs = bAnyApplied;    // Tell caller if any attributes were applied
02549   
02550 EndOperation:
02551     
02552     // Delete our lists of attribute types
02553     LineLevelAttrTypes.DeleteAll();
02554     OtherAttrTypes.DeleteAll();
02555 
02556     if (bWeStartedThisOp)
02557         End();
02558 
02559     return *Success;
02560 } 
02561 
02562 
02563 /********************************************************************************************
02564 
02565 >   BOOL SelOperation::ApplyToSelection(List* Attribs, List* AttrGroupList)        
02566 
02567     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02568     Created:    4/10/95
02569 
02570     Inputs:     A list of attributes to apply to the selection. Each item in the list is
02571                 a NodeAttributePtrItem.
02572 
02573     
02574     Outputs:    AttrGroupList:   This list must be empty on entry to this function
02575                                  If (Success) then this list will contain one ListListItem
02576                                  per attribute on the Attribs List. Each of these
02577                                  items will point to a Set of  AttributeGroupItems.
02578 
02579                                  Attribute -> { Set of current attribute groups }
02580                                  
02581                                  Whenever an attribute is applied to an object, the Current
02582                                  AttributeGroup associated with the object is added to
02583                                  the attributes set of current attribute groups. 
02584                                  Therefore if an attribute's set is empty then we know
02585                                  that the attribute has not been applied.
02586 
02587     Returns:    TRUE if successful 
02588                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!)
02589                 
02590     SeeAlso:    OpApplyAttribsToSelected::DoWithParam 
02591 
02592 ********************************************************************************************/
02593 
02594 BOOL SelOperation::DoApplyToSelection(List* Attribs, List* AttrGroupList, BOOL* pAttribWasRequired)
02595 {
02596     ERROR3IF(!(AttrGroupList->IsEmpty()), "Invalid output param");
02597 
02598     // Set up a range of all selected nodes 
02599     SelRange* pSel = GetApplication()->FindSelection();
02600     EffectsStack* pStack = pSel->GetEffectsStack(FALSE, FALSE); // Get cached stack, don't escape derived objects (allow attrs to be applie to derived objects)
02601     ERROR3IF(pStack==NULL, "Failed to get PPStack in ApplyToSelection");
02602     INT32 stackpos = STACKPOS_TOP;
02603     Range* pLevel = pStack->GetLevelRange(&stackpos, FALSE);            // Don't escape old controllers, apply attr to base nodes
02604 
02605     BOOL bCanDiscardUndo = TRUE;
02606 
02607     NodeAttributePtrItem* pAttrItem = (NodeAttributePtrItem*)(Attribs->GetHead());
02608     while (pAttrItem)
02609     {
02610         NodeAttribute* pAttr = pAttrItem->NodeAttribPtr;
02611 
02612         // Create the pAttr's set of current attribute groups { currently empty }
02613         ListListItem* pAttrsSetItem = new ListListItem;
02614         if (!pAttrsSetItem)
02615             goto Failed;
02616         List* pAttrsSet = &(pAttrsSetItem->list);
02617         AttrGroupList->AddTail(pAttrsSetItem); 
02618         
02619         // While there are still nodes to apply the attribute to
02620         Node* CurrentNode = pLevel->FindFirst();
02621 //      Node* FirstNode = CurrentNode;
02622         while (CurrentNode)   
02623         {
02624             NodeAttribute* AttrToApply = pAttr;
02625             BOOL AppliedAttribIsCopy = FALSE;
02626             BOOL ThisAttrWasRequired = FALSE;
02627             if (pAttr->IsAFillAttr())
02628             {
02629                 // Transform the attribute to fit the node's bounds
02630                 DocRect NodeBounds = ((NodeRenderableInk*)CurrentNode)->GetBoundingRect(TRUE);
02631                 // We will transform a copy of the attribute.
02632                 AttrToApply = (NodeAttribute*)(pAttr->SimpleCopy());
02633                 AppliedAttribIsCopy = TRUE; // cos it is
02634                 if (!AttrToApply)
02635                     goto Failed;
02636                 AttrToApply->TransformToNewBounds(NodeBounds);
02637             }
02638 
02639             if (!DoApply(CurrentNode, 
02640                          AttrToApply, 
02641                          FALSE,  // Don't mutate
02642                          FALSE,  // Don't invalidate region
02643                          FALSE,  // Don't keep existing characteristics
02644                          &ThisAttrWasRequired,
02645                          &bCanDiscardUndo))
02646             {
02647                 goto Failed;
02648             }
02649 
02650             if (ThisAttrWasRequired)
02651             {
02652                 *pAttribWasRequired = TRUE;
02653 
02654                 // This next bit is a bit yucky, we are in desperate need of templates to implement
02655                 // proper sets.
02656                 // The attribute was required by the object
02657                 // Add the current attribute group associated with the object to the attributes set
02658                 // Search the pAttrsSet set to see if it's already there
02659 
02660                 CCRuntimeClass* NewAttrGroup = ((NodeRenderableInk*)CurrentNode)->GetCurrentAttribGroup();
02661                 ERROR3IF(NewAttrGroup == NULL, "Object has a NULL attribute group"); 
02662                 BOOL InSet = FALSE;
02663                 AttributeGroupItem* pAttrGrpItem = (AttributeGroupItem*)pAttrsSet->GetHead();
02664                 while(pAttrGrpItem != NULL)
02665                 {
02666                     CCRuntimeClass* InSetAttrGroup = pAttrGrpItem->AttrGroup;
02667                     ERROR3IF(InSetAttrGroup == NULL, "NULL attribute group found");
02668                     if (NewAttrGroup == InSetAttrGroup)
02669                     {
02670                         InSet = TRUE; 
02671                         break; // It's already in the set don't add it
02672                     }
02673                     pAttrGrpItem = (AttributeGroupItem*)pAttrsSet->GetNext(pAttrGrpItem);
02674                 }
02675             
02676                 if (!InSet) // The AttrGroup needs adding to the pAttrsSet 
02677                 {
02678                     // Create a new AttrGroupItem
02679                     pAttrGrpItem = new AttributeGroupItem();
02680                     if (!pAttrGrpItem)
02681                         goto Failed;
02682 
02683                     pAttrGrpItem->AttrGroup = NewAttrGroup;
02684 
02685                     // And add it to our set
02686                     pAttrsSet->AddHead(pAttrGrpItem); 
02687                 }  
02688             }
02689 
02690             if (CurrentNode->IsAnObject())
02691                 ((NodeRenderableInk*)CurrentNode)->InvalidateBoundingRect();
02692 
02693             CurrentNode = pLevel->FindNext(CurrentNode);
02694             if (AppliedAttribIsCopy)
02695             {
02696                 delete AttrToApply; // not required
02697                 AttrToApply = NULL; 
02698             }
02699         }
02700         ContinueSlowJob();
02701         pAttrItem = (NodeAttributePtrItem*)(Attribs->GetNext(pAttrItem));
02702     }
02703 
02704     // If all nodes report DiscardsAttributeChildren() = TRUE
02705     // Then we can abandon the entire Op once it's finished
02706     if (bCanDiscardUndo)
02707         SucceedAndDiscard();
02708 
02709     return TRUE; 
02710 
02711     Failed:
02712     // Tidyup, and return FALSE
02713     AttrGroupList->DeleteAll(); 
02714     return FALSE;
02715 }
02716 
02717 /********************************************************************************************
02718 
02719 >   BOOL OpApplyAttribsToSelected::Init()
02720 
02721     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02722     Created:    28/9/93
02723     Returns:    TRUE if the operation could be successfully initialised 
02724                 FALSE if no more memory could be allocated 
02725                 
02726     Purpose:    OpApplyAttribsToSelected initialiser method
02727     Errors:     ERROR will be called if there was insufficient memory to allocate the 
02728                 operation.
02729 
02730 ********************************************************************************************/
02731 
02732 BOOL OpApplyAttribsToSelected::Init()
02733 {
02734     // Register the opdescriptor. Don't need to do it the posh way cos this op will
02735     // not be connected directly to the interface.
02736     OpDescriptor* OpDesc = new OpDescriptor(0,
02737                                             _R(IDS_APPLYATTRIBOP), // Never used                
02738                                             CC_RUNTIME_CLASS(OpApplyAttribsToSelected),
02739                                             OPTOKEN_APPLYATTRIBS,
02740                                             OpApplyAttribsToSelected::GetState);
02741     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
02742     
02743     return(TRUE);
02744 }               
02745 
02746 /********************************************************************************************
02747 
02748 >   OpState OpApplyAttribsToSelected::GetState(String_256*, OpDescriptor* OpDesc)
02749 
02750     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02751     Created:    28/9/93
02752 
02753     Returns:    The state of the OpApplyAttribsToSelected operation. This fn will 
02754                 probably never get called !, because the operation is not attached to the UI.
02755 
02756     Purpose:    For finding OpApplyAttribsToSelected state. 
02757 
02758 ********************************************************************************************/
02759 
02760 OpState OpApplyAttribsToSelected::GetState(String_256*, OpDescriptor* OpDesc)
02761 {
02762     OpState OpSt;
02763     return(OpSt);   
02764 }
02765 
02766 
02767 /********************************************************************************************
02768 
02769 >   virtual void OpApplyAttribsToSelected::GetOpName(String_256* OpName) 
02770 
02771     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
02772     Created:    21/2/94
02773     Outputs:    The undo string for the operation
02774     Purpose:    The GetOpName fn is overridden so that we can return a string
02775                 suitable for our purposes.
02776 
02777 ********************************************************************************************/
02778 
02779 void OpApplyAttribsToSelected::GetOpName(String_256* OpName) 
02780 { 
02781 
02782     *OpName = String_256(UndoAttribStrID); 
02783 }  
02784 
02785 
02786 /********************************************************************************************
02787 ********************************************************************************************/
02788 
02789 
02790 /********************************************************************************************
02791 
02792 >   OpRepeatApplyAttribToSelected::OpRepeatApplyAttribToSelected() 
02793 
02794     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02795     Created:    7/3/95
02796     Inputs:     -
02797     Outputs:    -
02798     Returns:    -
02799     Purpose:    OpRepeatApplyAttribToSelected constructor
02800     Errors:     -
02801     SeeAlso:    -
02802 
02803 ********************************************************************************************/
02804             
02805             
02806 OpRepeatApplyAttribToSelected::OpRepeatApplyAttribToSelected(): Operation()                             
02807 {                              
02808 }
02809 
02810 
02811 /********************************************************************************************
02812 
02813 >   void OpRepeatApplyAttribToSelected::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)
02814 
02815     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02816     Created:    7/3/95
02817     Inputs:     OpDescriptor 
02818     Outputs:    pOpParam: Param1 contains a pointer to the NodeAttribute to apply to the 
02819                           currently selected nodes
02820     Returns:    -
02821     Purpose:    Performs the OpApplyAttribToSelected operation. This function applies the
02822                 NodeAttribute to all selected objects.
02823     Errors:     -
02824     SeeAlso:    -
02825 
02826 ********************************************************************************************/
02827         
02828 void OpRepeatApplyAttribToSelected::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)           
02829 {    
02830     ERROR3IF(pOpParam == NULL, "The OpApplyAttribToSelected operation requires an attribute parameter"); 
02831 
02832     AttrTypeSet AttrTypes; 
02833     NodeRenderableInk* pLocalisedCompound = NULL;
02834     NodeRenderableInk* pFactoredOutCompound = NULL;
02835     Node* pCurrent;
02836 
02837     Range SelRng(*(GetApplication()->FindSelection()));
02838 
02839     BeginSlowJob();
02840     AttributeManager::pLastNodeAppliedTo = NULL;
02841 
02842     // Obtain a pointer to the attribute which we will need to apply to all selected nodes 
02843     NodeAttribute* Attrib = (NodeAttribute*)(void *)pOpParam->Param1;
02844 
02845     BOOL Mutate = FALSE;
02846     NodeAttribute* OldAttr = NULL;
02847 
02848     if (Attrib == NULL)
02849     {
02850         // If the first param is NULL then we are doing a mutation
02851         Attrib = (NodeAttribute*)(void *)pOpParam->Param2;
02852         Mutate = TRUE;
02853     }   
02854     else
02855     {
02856         // Someone has specified a Particular Attribute that we should replace
02857         // and ignore all others.
02858         OldAttr = (NodeAttribute*)(void *)pOpParam->Param2;
02859     }
02860 
02861     SelRange* Sel;
02862     ObjChangeFlags cFlags;
02863     cFlags.Attribute = TRUE;
02864     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,NULL);
02865 
02866     Sel = GetApplication()->FindSelection();
02867     if (!Sel->AllowOp(&ObjChange))
02868     {
02869         FailAndExecute();
02870         goto EndOperation;
02871     }
02872 
02873     // Some Attributes require a second attribute to be changed as well,
02874     // which has to be done within this op.
02875     NodeAttribute* OtherAttrib;
02876     BOOL IsMutate;
02877     OtherAttrib = AttributeManager::GetOtherAttrToApply(Attrib, &IsMutate);
02878 
02879     // Before we apply the attribute to the selection we must localise all attributes
02880     // with the same type that we are going to apply. If we don't do this then the
02881     // tree will be left in an invalid state. 
02882 
02883     AttrTypes.AddToSet((Attrib->GetAttributeType())); 
02884     if (OtherAttrib != NULL)
02885     {
02886         AttrTypes.AddToSet((OtherAttrib->GetAttributeType())); 
02887     }
02888         
02889 //  if (!DoLocaliseForAttrChange(&SelRng, &AttrTypes))
02890 //  {
02891 //      goto EndOperation;
02892 //  }
02893 
02894     pCurrent = SelRng.FindFirst();
02895 
02896     // There is no need to localise a compound more than once so we remember the last localised 
02897 
02898     CCRuntimeClass* AttrType;
02899     Node* pParent;
02900     Node* pObject;
02901                         
02902     while (pCurrent)
02903     {
02904         pObject = pCurrent;
02905         // Determine if the attribute will get applied to this object, or an alternative object
02906         // (probably its parent)
02907         if (!AttrTypes.IsEmpty())
02908         {
02909             AttrType = ((AttrTypeItem*)AttrTypes.GetHead())->AttributeType;
02910             ERROR3IF(!AttrType, "AttrType set contains NULL attribute type");  
02911             pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(AttrType);
02912         }
02913 
02914         {
02915             // We only need to localise those nodes which have a compound parent
02916             pParent = pObject->FindParent(); 
02917             ERROR3IF(pParent == NULL, "Range::DoLocaliseForAttrChange, node found without a parent"); 
02918         
02919             // Only localise the parent if it is a compound node which has not already been localised
02920             if ((pParent->IsCompound()) && (pParent != pLocalisedCompound))
02921             {
02922                 ((NodeRenderableInk*)pParent)->LocaliseCommonAttributes(FALSE,      // No need to check for duplicates
02923                                                                         TRUE,       // Localise globally
02924                                                                         &AttrTypes);// Only attributes of these types
02925 
02926                 pLocalisedCompound = (NodeRenderableInk*)pParent;  // Remember that it's been localised
02927             }
02928         }
02929         
02930         pCurrent = SelRng.FindNext(pCurrent); 
02931     }
02932 
02933     ApplyToSelection(Attrib, Mutate);
02934 
02935     if (OtherAttrib != NULL)
02936     {
02937         ApplyToSelection(OtherAttrib, IsMutate);
02938         delete OtherAttrib;
02939     }   
02940 
02941     // Having applied the attributes, we  must now try and factor out the newly 
02942     // applied attributes
02943 //  if (!DoFactorOutAfterAttrChange(&SelRng, 
02944 //                                  &AttrTypes))
02945 //  {
02946 //      goto EndOperation;
02947 //  }
02948 
02949     // Scan the range
02950     pCurrent = SelRng.FindFirst();
02951 
02952     // There is no need to factor out the attributes on a compound more than once, so we 
02953     // remember the last compound node which has had its attributes factored out 
02954 
02955     while (pCurrent)
02956     {
02957         // If the object can discard its attribute children then we should not try to factor
02958         // out its attributes
02959         CCRuntimeClass* AttrType;
02960         
02961         pObject = pCurrent;
02962         if (!AttrTypes.IsEmpty())
02963         {
02964             AttrType = ((AttrTypeItem*)AttrTypes.GetHead())->AttributeType;
02965             ERROR3IF(!AttrType, "AttrType set contains NULL attribute type");  
02966             pObject = ((NodeRenderableInk*)pObject)->GetObjectToApplyTo(AttrType);
02967         }
02968 
02969         {
02970             // We only need to factor out attributes on nodes which have a compound parent
02971             pParent = pObject->FindParent(); 
02972             ERROR3IF(pParent == NULL, "Range::DoFactorOutAfterAttrChange, node found without a parent"); 
02973         
02974             // Only factor out attribs if  the parent has not already had its attribs factored out
02975             if ((pParent->IsCompound()) && (pParent != pFactoredOutCompound))
02976             {
02977                 ((NodeRenderableInk*)pParent)->FactorOutCommonChildAttributes(TRUE, &AttrTypes);
02978                  
02979                 pFactoredOutCompound = (NodeRenderableInk*)pParent;  // Remember that it's been localised
02980             }
02981         }
02982         
02983         pCurrent = SelRng.FindNext(pCurrent); 
02984     }
02985 
02986     AttrFillGeometry::LastRenderedMesh = NULL;
02987 
02988     if (Document::GetSelected())
02989         Document::GetSelected()->SetModified(TRUE);
02990 
02991     ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,NULL);
02992     if (!UpdateChangedNodes(&ObjChange))
02993         FailAndExecute();
02994 
02995     AttributeManager::LastAppliedBounds = Sel->GetBoundingRect();
02996 
02997 EndOperation:           
02998     AttrTypes.DeleteAll();
02999 
03000     EndSlowJob();
03001     End();              // End of operation
03002 } 
03003 
03004 /********************************************************************************************
03005 
03006 >   BOOL OpRepeatApplyAttribToSelected::ApplyToSelection(NodeAttribute* Attrib, BOOL Mutate)           
03007 
03008     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03009     Created:    5/4/95
03010     Inputs:     -
03011     Outputs:    -
03012     Returns:    TRUE if successful 
03013                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
03014 
03015 
03016     Purpose:    
03017     Errors:     -
03018     SeeAlso:    -
03019 
03020 ********************************************************************************************/
03021 
03022 
03023 
03024 BOOL OpRepeatApplyAttribToSelected::ApplyToSelection(NodeAttribute* Attrib, BOOL Mutate)           
03025 {
03026     SelRange* Sel = GetApplication()->FindSelection();
03027     ERROR3IF(Sel==NULL,"Can't find SelRange!");
03028     if (Sel==NULL) return FALSE;
03029     Node* FirstSelectedNode = Sel->FindFirst();
03030 
03031     // Find out which spread the selection is on
03032 //  Spread* pSpread = FirstSelectedNode->FindParentSpread();
03033 
03034     if (FirstSelectedNode != NULL)    // Can't do anything if nothing is selected
03035     {   
03036         EffectsStack* pStack = Sel->GetEffectsStack(FALSE, FALSE);  // Get cached stack, don't escape derived objects (allow attrs to be applie to derived objects)
03037         ERROR3IF(pStack==NULL, "Failed to get PPStack in ApplyToSelection");
03038         INT32 stackpos = STACKPOS_TOP;
03039         Range* pLevel = pStack->GetLevelRange(&stackpos, FALSE);        // Don't escape old controllers, apply attr to base nodes
03040         Node* pCurrentNode = pLevel->FindFirst();
03041 
03042         // While there are still nodes to apply the attribute to
03043         while (pCurrentNode != NULL)   
03044         {
03045             BOOL bEffect = (pCurrentNode->IsAnObject() && ((NodeRenderableInk*)pCurrentNode)->IsValidEffectAttr(Attrib));
03046 
03047             Node* pApplyNode = pCurrentNode;
03048             if (pApplyNode->IsAnObject())
03049                 pApplyNode = ((NodeRenderableInk*)pApplyNode)->GetObjectToApplyTo(Attrib->GetRuntimeClass());
03050 
03051             if (!DoApply(pApplyNode, Attrib, Mutate, bEffect))
03052                 return FALSE;
03053 
03054             ContinueSlowJob();
03055 
03056             pCurrentNode = pLevel->FindNext(pCurrentNode);
03057         } 
03058     }
03059 
03060     return TRUE;
03061 }
03062 
03063 
03064 
03065 
03066 /********************************************************************************************
03067 
03068 >   BOOL OpRepeatApplyAttribToSelected::DoApply(Node* CurrentNode, NodeAttribute* Attrib, BOOL Mutate, BOOL bEffectRootOnly)
03069 
03070     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
03071     Created:    5/4/95
03072     Inputs:     -
03073     Outputs:    -
03074     Returns:    Returns:    TRUE if successful 
03075                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
03076 
03077     Purpose:    
03078     Errors:     -
03079     SeeAlso:    -
03080     Note:       This routine may no longer be needed now that OpApplyAttrib::DoApply is static
03081                 and can be told not to write undo info.
03082 
03083 ********************************************************************************************/
03084 
03085 BOOL OpRepeatApplyAttribToSelected::DoApply(Node* CurrentNode, NodeAttribute* Attrib, BOOL Mutate, BOOL bEffectRootOnly)
03086 {
03087     ERROR3IF(!CurrentNode->IsBounded(), "DoApply assumes CurrentNode is bounded");
03088 
03089     // have we been allowed to do this ???
03090     Spread* pSpread = (Spread*)CurrentNode->FindParent(CC_RUNTIME_CLASS(Spread)); 
03091     DocRect TempRect = ((NodeRenderableBounded*)CurrentNode)->GetBoundingRect();
03092 
03093     BOOL AttributeExists = FALSE;  // Until we find that the attribute does exist
03094 
03095     CCRuntimeClass* AttrType = Attrib->GetAttributeType();
03096 
03097     // -----------------------------------------------------------------------------
03098     // Determine if the current node already has an attribute which is the same 
03099     // runtime class as Attrib. 
03100     Node* n = CurrentNode->FindFirstChild(); 
03101     Node* pLastBoundedNode = NULL;
03102     if (bEffectRootOnly)
03103     {
03104         // BODGE! ------------------------------------------------------------
03105         // Don't apply stroke transparency as an effect attribute - nothing
03106         // needs it. Test has to be done here because other objects in the
03107         // selection might need it...
03108         if (AttrType==CC_RUNTIME_CLASS(AttrStrokeTransp))
03109             return TRUE;
03110         // END-BODGE! --------------------------------------------------------
03111 
03112         pLastBoundedNode = CurrentNode->FindLastChild(CC_RUNTIME_CLASS(NodeRenderableBounded));
03113         ERROR3IF(pLastBoundedNode==NULL, "Attempt to apply effect attr to node with no children");
03114 
03115         if (pLastBoundedNode) n = pLastBoundedNode->FindNext();
03116     }
03117 
03118     while (n != NULL)
03119     {
03120         // Now lets see if we can find an attribute of the same type
03121         // as the one we are interested in.
03122         if (n->IsAnAttribute())
03123         {
03124             if( ((NodeAttribute*)n)->GetAttributeType() == AttrType)
03125             {
03126                 AttributeExists = TRUE; 
03127                 break; 
03128             }
03129         }
03130         n = n->FindNext(); // Find next child of CurrentNode
03131     }   
03132 
03133     // At this point we have Either .....
03134     //      1. Found an attribute of the same type as the new one, and we will
03135     //         now replace it.
03136     //      2. Found the specific attribute we were looking for and will replace
03137     //         that.
03138     //      3. Found an attribute to mutate.
03139     //      4. Found no attribute of the correct type, indicating that it has
03140     //         been factored out, and we need to put a new one in here.
03141 
03142     // Have we have got an Attribute to do something with ?
03143     NodeAttribute* AttribClone = NULL; 
03144     NodeAttribute* pAttr = (NodeAttribute*)n;
03145     if (pAttr)
03146     {
03147         // Yes !
03148         if (Mutate)
03149         {
03150             // Mutate it into the new type.
03151             AttribClone = ((AttrFillGeometry*)pAttr)->Mutate((AttrFillGeometry*)Attrib);
03152         }
03153 
03154         if (AttribClone != NULL)
03155         {
03156             // Set the attribute directly
03157             *((AttrFillGeometry*)pAttr) = *((AttrFillGeometry*)AttribClone);
03158 
03159             AttributeManager::pLastNodeAppliedTo = (NodeRenderableInk*)CurrentNode;
03160 
03161             delete AttribClone;
03162             AttribClone = NULL;
03163         }
03164     }
03165     else
03166     {
03167         // We've not found an attribute to replace, so we'll have to put
03168         // a new one in
03169         NodeAttribute* TempAttr = NULL;
03170 
03171         BOOL FoundAttr = ((NodeRenderableInk*)CurrentNode)->
03172                             FindAppliedAttribute(Attrib->GetAttributeType(), &TempAttr);
03173 
03174         if (!FoundAttr || TempAttr == NULL)
03175             return FALSE;
03176 
03177         if (Mutate)
03178         {
03179             AttribClone = ((AttrFillGeometry*)TempAttr)->Mutate((AttrFillGeometry*)Attrib);
03180         }
03181         else
03182         {
03183             // We'll just put a copy of our attribute in the tree.
03184             ALLOC_WITH_FAIL(AttribClone ,((NodeAttribute*)Attrib->SimpleCopy()), this)
03185 
03186             if (TempAttr->IsAFillAttr())
03187             {
03188                 AttrFillGeometry* NodeReplaced = (AttrFillGeometry*)TempAttr;
03189 
03190                 // Copy the old fill characteristics into the new Fill
03191                 if (!OpApplyAttrib::KeepExistingCharacteristics(NodeReplaced, (AttrFillGeometry*)AttribClone))
03192                     return FALSE;
03193             }
03194 
03195             if (Attrib->IsATranspFill() && Attrib->IsAFlatFill())
03196             {
03197                 // If we are mutating into a flat fill, then we need
03198                 // to make sure we remove any existing fill blobs.
03199                 ((AttrFillGeometry*)TempAttr)->RenderFillBlobs();
03200             }
03201         }
03202 
03203         // If the AttribClone has the same value as an applied attribute 
03204         // (A default attr if the attribs have been localised). Then we don't want to apply it !
03205         if (AttribClone && AttribClone->ShouldBeOptimized() && !bEffectRootOnly)
03206         {
03207             NodeAttribute* pAppliedAttr;
03208             if (((NodeRenderableInk*)CurrentNode)->FindAppliedAttribute(AttribClone->GetAttributeType(), 
03209                                                   &pAppliedAttr))
03210             {
03211                 // Do the attributes have the same value ?
03212                 if ((IS_SAME_CLASS(AttribClone, pAppliedAttr)))
03213                 {
03214                     if ((*AttribClone)==(*pAppliedAttr))
03215                     {
03216                         AttribClone->CascadeDelete();       // Delete the attribute
03217 
03218                         // If attribute affect's the bounds of the object (eg. a LineWidth) then invalidate the
03219                         // bounds of the object
03220 
03221                         if (pAppliedAttr->EffectsParentBounds())
03222                         {
03223                             ((NodeRenderableBounded*)CurrentNode)->InvalidateBoundingRect(TRUE);
03224                         }
03225 
03226                         delete AttribClone;
03227                         AttribClone = NULL;
03228                     }
03229                 }
03230             }
03231         }
03232 
03233         // Effect attributes don't optimise so if the value of this is the
03234         // same as the default value we should not add it here
03235         //
03236         if (AttribClone && bEffectRootOnly && AttribClone->HasEquivalentDefaultValue())
03237         {
03238             // Just allow the attribute to be hidden
03239             AttribClone->CascadeDelete();       // Delete the attribute
03240             delete AttribClone;
03241             AttribClone = NULL;
03242         }
03243 
03244         if (AttribClone)
03245         {
03246             // Finally !! We can add the new attribute node into the tree.
03247             if (bEffectRootOnly)
03248                 AttribClone->AttachNode(pLastBoundedNode, NEXT);
03249             else
03250                 AttribClone->AttachNode(CurrentNode, FIRSTCHILD);
03251 
03252             AttributeManager::pLastNodeAppliedTo = (NodeRenderableInk*)CurrentNode;
03253 
03254             // And now it's in the tree, we need to make sure that any fill control
03255             // points are valid.
03256             if (AttribClone->IsAFillAttr())
03257             {
03258                 ((AttrFillGeometry*)AttribClone)->AttributeChanged();
03259             }
03260         }
03261     }
03262 
03263     // Now that the attr value has been set (or left unchanged) test to see if we can remove effect attrs
03264     // Only try to "optimise" effect attributes here if this is going to be a permanent tree change
03265     // Don't bother in the middle of slider dragging
03266     if (pAttr && pAttr->IsEffectAttribute() && bEffectRootOnly)
03267     {
03268         // Effect attributes don't optimise so if the value of this is the
03269         // same as the default value  we should remove it here
03270         //
03271         if (pAttr->HasEquivalentDefaultValue())
03272         {
03273             // "Hide" the node
03274             pAttr->CascadeDelete();
03275             delete pAttr;
03276         }
03277     }
03278 
03279     // Now force a redraw on the attribute
03280     // We release cached data manually, because we need to be clever about effect attributes
03281     // only releasing parent cached data...
03282 //  ReleaseCachedForAttrApply((NodeRenderableBounded*)CurrentNode, bEffectRootOnly); // Function not available in this class!
03283     if (bEffectRootOnly)
03284         ((NodeRenderableBounded*)CurrentNode)->ReleaseCached(TRUE, FALSE, FALSE, TRUE);     // Parents and Derived data only
03285     else
03286         ((NodeRenderableBounded*)CurrentNode)->ReleaseCached();                                                 // Self, Parents and Children
03287 
03288     // (GetSelected... URGH.)
03289     Document::GetSelected()->ForceRedraw(pSpread, TempRect, TRUE, CurrentNode, FALSE);  // No automatic call to ReleaseCached
03290     return TRUE;
03291 }
03292 
03293 
03294 /********************************************************************************************
03295 
03296 >   BOOL OpRepeatApplyAttribToSelected::Init()
03297 
03298     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03299     Created:    7/3/95
03300     Inputs:     -
03301     Outputs:    -
03302     Returns:    TRUE if the operation could be successfully initialised 
03303                 FALSE if no more memory could be allocated 
03304                 
03305     Purpose:    OpApplyAttribToSelected initialiser method
03306     Errors:     ERROR will be called if there was insufficient memory to allocate the 
03307                 operation.
03308     SeeAlso:    -
03309 
03310 ********************************************************************************************/
03311 
03312 BOOL OpRepeatApplyAttribToSelected::Init()
03313 {
03314     // Register the opdescriptors for the OpApplyAttribToSelected operation
03315     OpDescriptor* OpDesc = new OpDescriptor(
03316                                             0,
03317                                             _R(IDS_APPLYATTRIBOP),              
03318                                             CC_RUNTIME_CLASS(OpRepeatApplyAttribToSelected),
03319                                             OPTOKEN_REPEATAPPLYATTRIB,
03320                                             OpRepeatApplyAttribToSelected::GetState);   
03321 
03322     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
03323     return(OpDesc != NULL);
03324 }               
03325 
03326 /********************************************************************************************
03327 
03328 >   OpState OpRepeatApplyAttribToSelected::GetState(String_256*, OpDescriptor* OpDesc)
03329 
03330     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03331     Created:    7/3/95
03332     Inputs:     -
03333     Outputs:    -
03334     Returns:    The state of the OpRepeatApplyAttribToSelected operation
03335     Purpose:    For finding OpRepeatApplyAttribToSelected state. 
03336     Errors:     -
03337     SeeAlso:    -
03338 
03339 ********************************************************************************************/
03340 
03341 OpState OpRepeatApplyAttribToSelected::GetState(String_256*, OpDescriptor* OpDesc)
03342 {
03343     OpState OpSt;
03344 
03345     SelRange* Sel = GetApplication()->FindSelection();
03346     ERROR2IF(Sel==NULL,OpSt,"Can't find SelRange!");
03347 
03348     // The Operation is greyed if there are no nodes selected 
03349     OpSt.Greyed = ( (Sel->FindFirst() == NULL) );  
03350                                                 
03351     return(OpSt);   
03352 }
03353 
03354 
03355 /********************************************************************************************
03356 
03357 >   virtual void OpRepeatApplyAttribToSelected::GetOpName(String_256* OpName) 
03358 
03359     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03360     Created:    7/3/95
03361     Inputs:     -
03362     Outputs:    The undo string for the operation
03363     Returns:    
03364     Purpose:    The GetOpName fn is overridden so that we return back a description 
03365                 appropriate to the type of attribute that the operation applies. 
03366                     
03367     Errors:     -
03368     SeeAlso:    -
03369 
03370 ********************************************************************************************/
03371 
03372 void OpRepeatApplyAttribToSelected::GetOpName(String_256* OpName) 
03373 { 
03374     *OpName = "";
03375 }  
03376 
03377 CCRuntimeClass* OpApplyAttrib::GetValueChangeType()
03378 {
03379     return ValueChangeType;
03380 }
03381 
03382 BOOL OpApplyAttrib::IsMergeableApplyOp()
03383 {
03384     return MergeRepeats;
03385 }
03386 
03387 
03388 /********************************************************************************************
03389 
03390                                 OpApplyAttribToNode
03391 
03392 ********************************************************************************************/
03393 
03394 
03395 
03396 /********************************************************************************************
03397 
03398 >   OpApplyAttribToNode::OpApplyAttribToNode() 
03399 
03400     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03401     Created:    29/1/95
03402     Inputs:     -
03403     Outputs:    -
03404     Returns:    -
03405     Purpose:    OpApplyAttribToNode constructor
03406     Errors:     -
03407     SeeAlso:    -
03408 
03409 ********************************************************************************************/
03410             
03411             
03412 OpApplyAttribToNode::OpApplyAttribToNode(): OpApplyAttrib()                             
03413 {
03414     m_pAttr = NULL;
03415 }
03416 
03417 
03418 /********************************************************************************************
03419 
03420 >   void OpApplyAttribToNode::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)
03421 
03422     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>/Simon
03423     Created:    29/1/95
03424     Inputs:     OpDescriptor 
03425     Outputs:    pOpParam: Param1 contains a pointer to the NodeAttribute to apply to the 
03426                           currently selected nodes
03427     Returns:    -
03428     Purpose:    Performs the OpApplyAttribToNode operation. This function applies the
03429                 NodeAttribute to all selected objects.
03430     Errors:     -
03431     SeeAlso:    -
03432 
03433 ********************************************************************************************/
03434 
03435 void OpApplyAttribToNode::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)         
03436 {
03437     ERROR3IF(pOpParam == NULL, "The OpApplyAttribToNode operation requires an attribute parameter"); 
03438 
03439     // Obtain a pointer to the attribute which we will need to apply to all selected nodes 
03440     NodeRenderableInk* InkNode = (NodeRenderableInk*)(void *)pOpParam->Param1;
03441     NodeAttribute* NewAttr = (NodeAttribute*)(void *)pOpParam->Param2;
03442     m_pAttr = NewAttr;
03443 
03444     ValueChangeType = NewAttr->GetRuntimeClass();
03445 
03446     if (NewAttr)
03447         UndoAttribStrID = NewAttr->GetAttrNameID();
03448 
03449     ObjectSet LocalisedCompoundSet; // Every time we localise a compound's attributes we will add the
03450                                     // compound to this set.
03451 
03452     AttrTypeSet AttrTypes;          // The attribute types to localise and factor out 
03453     
03454     BOOL OtherIsMutate;
03455     NodeAttribute* OtherAttr = AttributeManager::GetOtherAttrToApply(NewAttr, &OtherIsMutate);
03456 
03457     // Prepare an ObjChangeParam for use when asking the ink node if we can do this op to it.
03458     ObjChangeFlags cFlags;
03459     cFlags.Attribute = TRUE;
03460     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
03461 
03462     // If this attribute affects the bounds if its parents then we must take the blobs off
03463     // at the start.  We can rely on the redraw at the end to put them back on
03464     BOOL AttrAffectsBounds = NewAttr->EffectsParentBounds();
03465     if (!DoStartSelOp(AttrAffectsBounds, FALSE, TRUE, TRUE)) 
03466     {
03467         goto End;
03468     }
03469 
03470     // Find out the types of attribute about to be applied, and add them to the AttrTypes set
03471     ERROR3IF(NewAttr==NULL,"NewAttr == NULL");
03472     AttrTypes.AddToSet((NewAttr->GetAttributeType()));
03473 
03474     if (OtherAttr != NULL)
03475     {
03476         AttrTypes.AddToSet((OtherAttr->GetAttributeType())); 
03477     }
03478 
03479     // If we are applying an attribute to a VTN which is selected then apply the attribute
03480     // to this node and all other selected nodes.
03481     if (InkNode->IsAnAbstractTextChar() && InkNode->IsSelected())
03482     {
03483         AbstractTextChar* Scan = (AbstractTextChar*)InkNode;
03484         AbstractTextChar* Last;
03485 
03486         // Find the first selected AbstractTextChar
03487         do
03488         {
03489             InkNode = Scan;
03490             Scan = Scan->FindPrevAbstractTextCharInStory();
03491         } while ((Scan != NULL) && (Scan->IsSelected()));
03492 
03493         // Find the last
03494         Scan = (AbstractTextChar*)InkNode;
03495         do
03496         {
03497             Last = Scan;
03498             Scan = Scan->FindNextAbstractTextCharInStory();
03499         } while ((Scan != NULL) && (Scan->IsSelected()));
03500 
03501         Range SubSelRange(InkNode, Last, RangeControl(TRUE));
03502 
03503         // Will the ink node (and all its parents) allow this op to happen?
03504         if (!SubSelRange.AllowOp(&ObjChange))
03505         {
03506             FailAndExecute();
03507             goto End;   
03508         }
03509 
03510         // Invalidate the region before applying the attribute/s
03511         if (!DoInvalidateRegions(&SubSelRange, 
03512                                  NewAttr, 
03513                                  NewAttr->IsAFillAttr(),
03514                                  OtherAttr,
03515                                  OtherIsMutate))  //Mutators have to include bounds
03516         {
03517             goto End;
03518         }
03519 
03520         // Apply the attr to all selected AbstractTextChars
03521         do
03522         {
03523             if (!DoApplyAttrib(InkNode, NewAttr, &AttrTypes, &LocalisedCompoundSet))
03524             {
03525                 // error will have been set
03526                 FailAndExecute(); // Just make sure
03527                 goto End;
03528             }
03529             InkNode = ((AbstractTextChar*)InkNode)->FindNextAbstractTextCharInStory();
03530         } while ((InkNode != NULL) && (InkNode->IsSelected()));
03531 
03532 
03533         // Invalidate the region after applying the attribute/s
03534         if (!DoInvalidateRegions(&SubSelRange, 
03535                                  NewAttr, 
03536                                  NewAttr->IsAFillAttr(),
03537                                  OtherAttr,
03538                                  OtherIsMutate))  //Mutators have to include bounds
03539         {
03540             goto End;       
03541         }
03542     }
03543     else
03544     {
03545         // Will the ink node (and all its parents) allow this op to happen?
03546         if (!InkNode->AllowOp(&ObjChange))
03547         {
03548             FailAndExecute(); 
03549             goto End;   
03550         }
03551 
03552         if (!DoApplyAttrib(InkNode, NewAttr, &AttrTypes, &LocalisedCompoundSet, TRUE))
03553         {
03554             goto End;
03555         }
03556 
03557     }
03558     
03559     // Finally factor out the neccessary attributes
03560     if (!DoFactorOutAfterAttrChange(&LocalisedCompoundSet,
03561                                     &AttrTypes))
03562     {
03563         goto End;
03564     }
03565 
03566     // Update all effected parent nodes
03567 
03568     ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this);
03569     if (!UpdateChangedNodes(&ObjChange))
03570     {
03571         FailAndExecute();
03572     }
03573 
03574     End:
03575     // First tidy up
03576     AttrTypes.DeleteAll();
03577     LocalisedCompoundSet.DeleteAll();
03578 
03579     if (OtherAttr)
03580     {
03581         delete OtherAttr;
03582     }
03583 
03584     // then end
03585     End(); // End of operation
03586 }
03587 
03588 /********************************************************************************************
03589 
03590 >   BOOL OpApplyAttribToNode::DoApplyAttrib(NodeRenderableInk* InkNode, 
03591                                             NodeAttribute* NewAttr, 
03592                                             AttrTypeSet* pAttrTypesToBeApplied, 
03593                                             ObjectSet* pLocalisedCompounds, 
03594                                             BOOL InvalidateBounds = FALSE)         
03595 )          
03596 
03597     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>/Simon
03598     Created:    14/4/95
03599     Inputs:     InkNode:               The node to apply the attribute to
03600                 NewAttr:               The attribute to apply
03601 
03602                 pAttrTypesToBeApplied: A set of the types of the attributes which 
03603                                        are to be applied. Before we apply an attribute 
03604                                        we localise all attrs in this set.
03605 
03606                 InvalidateBounds:      When TRUE bounds will be invalidated.
03607                 
03608     Outputs:    pLocalisedCompounds:   This is also an input. We maintain a set of the
03609                                        compound objects which have been localised, so that
03610                                        we don't localise more than once on each.
03611 
03612     Returns:    TRUE if successful, otherwise FALSE (Tidyup and call end)
03613     Purpose:    Applies NewAttr to InkNode
03614     Errors:     -
03615     SeeAlso:    -
03616 
03617 ********************************************************************************************/
03618 
03619 
03620 
03621 BOOL OpApplyAttribToNode::DoApplyAttrib(NodeRenderableInk* InkNode, NodeAttribute* NewAttr, 
03622                                         AttrTypeSet* pAttrTypesToBeApplied, 
03623                                         ObjectSet* pLocalisedCompounds, 
03624                                         BOOL InvalidateBounds)         
03625 {
03626     NodeAttribute* OtherAttr;
03627 
03628     AttributeManager::pLastNodeAppliedTo = NULL;
03629 
03630     ERROR3IF(InkNode==NULL,"InkNode == NULL");
03631     ERROR3IF(NewAttr==NULL,"NewAttr == NULL");
03632     if (InkNode == NULL) return FALSE;
03633 
03634 
03635     // Get a description of the attribute being applied so that we can use it to create the 
03636     // undo string. 
03637     UndoAttribStrID = NewAttr->GetAttrNameID();
03638 
03639     if (!AttrFillGeometry::HitList.IsEmpty())
03640     {
03641         // Bodge Alert !!
03642         // This is not nice, but it cure the problem !!
03643         // When a colour drop occurs on a blob which is part of a multi-select
03644         // fill mesh, then the colour drop will have to change several fills
03645         // at once.
03646         // To make this happen, I have made the colour drop checking routine
03647         // build a list of all the hit fills, in the 'HitList'.
03648 
03649         // The 'HitList' has some items in it, so lets apply the mutator to all
03650         // of the fills parents.
03651 
03652         ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
03653 
03654         while (pAttrPtr != NULL)
03655         {
03656             // It's a list of Attribute Pointer Items,
03657             // so lets extract the pointer
03658             NodeAttribute* pAttr = ((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
03659             NodeRenderableInk* pParent = (NodeRenderableInk*)pAttr->FindParent();
03660 
03661             // Before we apply the attributes, we localise all attrs of the same type
03662             if (!DoLocaliseForAttrChange(pParent, 
03663                                          pAttrTypesToBeApplied,
03664                                          pLocalisedCompounds))
03665             {
03666                 return FALSE; 
03667             }
03668 
03669             if (!DoApply(pParent, NewAttr, NewAttr->IsAFillAttr(), InvalidateBounds))
03670             {
03671                 return FALSE; 
03672             }
03673 
03674 
03675             // Some Attributes require a second attribute to be changed as well,
03676             // which has to be done within this op.
03677 
03678             BOOL IsMutate;
03679             OtherAttr = AttributeManager::GetOtherAttrToApply(NewAttr, &IsMutate);
03680     
03681             if (OtherAttr != NULL)
03682             {
03683                 if (!DoApply(pParent, OtherAttr, IsMutate, InvalidateBounds))
03684                 {
03685                     return FALSE; 
03686                 }
03687                 delete OtherAttr;
03688             }   
03689 
03690             pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr);
03691         }
03692         
03693         // Now make sure the HitList is cleared out for next time
03694         AttrFillGeometry::HitList.DeleteAll();
03695     }
03696     else
03697     {
03698         // Before we apply the attributes, we localise all attrs of the same type
03699         if (!DoLocaliseForAttrChange(InkNode, 
03700                                      pAttrTypesToBeApplied,
03701                                      pLocalisedCompounds))
03702         {
03703             return FALSE; 
03704         }
03705 
03706         if (!DoApply(InkNode, NewAttr, NewAttr->IsAFillAttr(), InvalidateBounds))   // The attrib is always a Mutator
03707             return FALSE; 
03708 
03709         // Some Attributes require a second attribute to be changed as well,
03710         // which has to be done within this op.
03711 
03712         BOOL IsMutate;
03713         OtherAttr = AttributeManager::GetOtherAttrToApply(NewAttr, &IsMutate);
03714     
03715         if (OtherAttr != NULL)
03716         {
03717             if (!DoApply(InkNode, OtherAttr, IsMutate, InvalidateBounds))
03718             {
03719                 return FALSE; 
03720             }
03721             delete OtherAttr;
03722         }   
03723     }
03724 
03725     if (Document::GetSelected())
03726         Document::GetSelected()->SetModified(TRUE);
03727 
03728 
03729     AttributeManager::LastAppliedBounds = InkNode->GetBoundingRect();
03730 
03731     return TRUE;
03732 } 
03733  
03734 
03735 /********************************************************************************************
03736 
03737 >   BOOL OpApplyAttribToNode::Init()
03738 
03739     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03740     Created:    29/1/95
03741     Inputs:     -
03742     Outputs:    -
03743     Returns:    TRUE if the operation could be successfully initialised 
03744                 FALSE if no more memory could be allocated 
03745                 
03746     Purpose:    OpApplyAttribToNode initialiser method
03747     Errors:     ERROR will be called if there was insufficient memory to allocate the 
03748                 operation.
03749     SeeAlso:    -
03750 
03751 ********************************************************************************************/
03752 
03753 BOOL OpApplyAttribToNode::Init()
03754 {
03755     
03756     // Register the opdescriptors for the OpApplyAttribToNode operation
03757     OpDescriptor* OpDesc = new OpDescriptor(
03758                                             0,
03759                                             _R(IDS_APPLYTONODEOP),              
03760                                             CC_RUNTIME_CLASS(OpApplyAttribToNode),
03761                                             OPTOKEN_APPLYTONODE,
03762                                             OpApplyAttribToNode::GetState); 
03763 
03764     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
03765     return(OpDesc != NULL);
03766 }               
03767 
03768 /********************************************************************************************
03769 
03770 >   OpState OpApplyAttribToNode::GetState(String_256*, OpDescriptor* OpDesc)
03771 
03772     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03773     Created:    29/1/95
03774     Inputs:     -
03775     Outputs:    -
03776     Returns:    The state of the OpApplyAttribToNode operation
03777     Purpose:    For finding OpApplyAttribToNode state. 
03778     Errors:     -
03779     SeeAlso:    -
03780 
03781 ********************************************************************************************/
03782 
03783 OpState OpApplyAttribToNode::GetState(String_256*, OpDescriptor* OpDesc)
03784 {
03785     OpState OpSt;
03786 
03787     OpSt.Greyed = ( FALSE );  
03788                                                 
03789     return(OpSt);   
03790 }
03791 
03792 
03793 /********************************************************************************************
03794 
03795 >   virtual void OpApplyAttribToNode::GetOpName(String_256* OpName) 
03796 
03797     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03798     Created:    29/1/95
03799     Inputs:     -
03800     Outputs:    The undo string for the operation
03801     Returns:    
03802     Purpose:    The GetOpName fn is overridden so that we return back a description 
03803                 appropriate to the type of attribute that the operation applies. 
03804                     
03805     Errors:     -
03806     SeeAlso:    -
03807 
03808 ********************************************************************************************/
03809 
03810 void OpApplyAttribToNode::GetOpName(String_256* OpName) 
03811 { 
03812     
03813     *OpName = String_256(UndoAttribStrID); 
03814     *OpName += String_256(_R(IDS_CHANGE)); 
03815 }  
03816 
03817 
03818 /********************************************************************************************
03819 
03820                                 OpReplaceAttributes
03821 
03822 ********************************************************************************************/
03823 
03824 
03825 
03826 /********************************************************************************************
03827 
03828 >   OpReplaceAttributes::OpReplaceAttributes() 
03829 
03830     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03831     Created:    29/1/95
03832     Inputs:     -
03833     Outputs:    -
03834     Returns:    -
03835     Purpose:    OpReplaceAttributes constructor
03836     Errors:     -
03837     SeeAlso:    -
03838 
03839 ********************************************************************************************/
03840             
03841             
03842 OpReplaceAttributes::OpReplaceAttributes(): SelOperation()                              
03843 {                
03844     m_pAttr = NULL;
03845 }
03846 
03847 
03848 /********************************************************************************************
03849 
03850 >   void OpReplaceAttributes::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)
03851 
03852     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03853     Created:    29/1/95
03854     Inputs:     OpDescriptor 
03855     Outputs:    pOpParam: Param1 contains a pointer to the NodeAttribute to apply to the 
03856                           currently selected nodes
03857     Returns:    -
03858     Purpose:    Performs the OpReplaceAttributes operation. This function applies the
03859                 NodeAttribute to all selected objects.
03860     Errors:     -
03861     SeeAlso:    -
03862 
03863 ********************************************************************************************/
03864         
03865 void OpReplaceAttributes::DoWithParam(OpDescriptor* OpDesc, OpParam* pOpParam)         
03866 {    
03867     ERROR3IF(pOpParam == NULL, "The OpReplaceAttributes operation requires an attribute parameter");
03868     ERROR3IF(!IS_A(pOpParam, ReplaceAttributesParam), "Wrong kind of param in OpReplaceAttributes");
03869 
03870     AttributeManager::pLastNodeAppliedTo = NULL;
03871 
03872     // Objects used to mark changed nodes, so that parents will update after attr replacement
03873     ObjChangeFlags cFlags;
03874     cFlags.Attribute = TRUE;
03875     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
03876     BOOL ok;
03877 
03878     // Obtain a pointer to the attribute which we will need to apply to all selected nodes 
03879     NodeAttribute* NewAttr = ((ReplaceAttributesParam*)pOpParam)->m_pAttr;
03880 
03881     m_pAttr = NewAttr;
03882 
03883     List* OldAttrs = ((ReplaceAttributesParam*)pOpParam)->m_pOldAttrs;
03884 
03885     ListItem* pAttr;
03886 
03887     // Get a description of the attribute being applied so that we can use it to create the 
03888     // undo string. 
03889     UndoAttribStrID = NewAttr->GetAttrNameID();
03890 
03891     if (!DoStartSelOp(FALSE,FALSE, TRUE,TRUE))  // Try to record the selection state , don't
03892                                      // render the blobs though 
03893     {
03894         goto EndOperation;  
03895     }
03896 
03897     // Ask all parents of all attrs in the list if it's ok to do the op
03898     ok = TRUE;
03899     pAttr = OldAttrs->GetHead();
03900     while (pAttr != NULL && ok)
03901     {
03902         Node* pParent = ((NodeAttributePtrItem*)pAttr)->NodeAttribPtr->FindParent();
03903 ObjChange.SetRetainCachedData(((NodeAttributePtrItem*)pAttr)->NodeAttribPtr->IsEffectAttribute());
03904         ok = (pParent != NULL) && pParent->AllowOp(&ObjChange);
03905         pAttr = OldAttrs->GetNext(pAttr);
03906     }
03907     if (!ok) 
03908     {
03909         FailAndExecute();
03910         goto EndOperation;
03911     }
03912 
03913     pAttr = OldAttrs->GetHead();
03914     while (pAttr != NULL)
03915     {
03916         DoReplace(((NodeAttributePtrItem*)pAttr)->NodeAttribPtr, NewAttr);
03917 
03918         pAttr = OldAttrs->GetNext(pAttr);
03919     }
03920 
03921     // If the user has specified that the Last Attribute applied should
03922     // become current, then we need to update the Current Attribute now.
03923     if (AttributeManager::LastAttrAppliedBecomesCurrent)
03924     {
03925         NodeAttribute* NewCurrent;
03926         NodeAttribute* LastAttr;
03927     
03928         if (AttributeManager::pLastNodeAppliedTo != NULL)
03929         {
03930             LastAttr = AttributeManager::pLastNodeAppliedTo->FindAppliedAttribute(NewAttr->GetAttributeType());
03931         
03932             if (LastAttr != NULL)
03933             {
03934                 ALLOC_WITH_FAIL(NewCurrent ,((NodeAttribute*)LastAttr->SimpleCopy()), this)
03935 
03936                 if (NewCurrent->IsAFillAttr())
03937                 {
03938                     DocRect Bounds = AttributeManager::pLastNodeAppliedTo->GetBoundingRect();
03939                     ((AttrFillGeometry*)NewCurrent)->SetBoundingRect(Bounds);
03940                 }
03941 
03942                 if (LastAttr->IsAFractalFill())
03943                 {
03944                     // The current attr, should always use the default DPI for fractals.
03945                     ((AttrFillGeometry*)NewCurrent)->SetFractalDPI(AttrFillGeometry::FractalDPI);
03946                 }
03947 
03948                 // Find the Attr Groups that need updating
03949                 List AttrGroups;
03950                 if (AttributeManager::CanBeAppliedToNode(AttributeManager::pLastNodeAppliedTo,
03951                                                             LastAttr, &AttrGroups))
03952                 {
03953                     AttributeManager::UpdateCurrentAttr(NewCurrent, FALSE, &AttrGroups);
03954                 }
03955 
03956                 AttrGroups.DeleteAll();
03957                 delete NewCurrent;
03958             }
03959         }
03960     }
03961 
03962     AttrFillGeometry::LastRenderedMesh = NULL;
03963 
03964     if (Document::GetSelected())
03965         Document::GetSelected()->SetModified(TRUE);
03966 
03967     // Update all parents of this
03968     ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this);
03969 ObjChange.SetRetainCachedData(TRUE);    // Tell TextStory not to invalidate everything in OnChildChange
03970     if (!UpdateChangedNodes(&ObjChange))
03971         FailAndExecute();
03972 
03973     EndOperation:           
03974     End(); // End of operation
03975 } 
03976 
03977 /********************************************************************************************
03978 
03979 >   BOOL OpReplaceAttributes::DoReplace(NodeAttribute* OldAttr, NodeAttribute* NewAttr)
03980 
03981     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03982     Created:    5/4/95
03983     Inputs:     -
03984     Outputs:    -
03985     Returns:    TRUE if successful 
03986                 FALSE if the operation should be aborted (TIDYUP THEN CALL End()!) 
03987 
03988 
03989     Purpose:    
03990     Errors:     -
03991     SeeAlso:    -
03992 
03993 ********************************************************************************************/
03994 
03995 
03996 BOOL OpReplaceAttributes::DoReplace(NodeAttribute* OldAttr, NodeAttribute* NewAttr)
03997 {
03998     Node* CurrentNode = OldAttr->FindParent();
03999     ERROR3IF(CurrentNode == NULL, "Can't find parent node in Replace Attr");
04000 
04001     BOOL bOldWasEffect = OldAttr->IsEffectAttribute();
04002 
04003     if (!DoInvalidateNodeRegion((NodeRenderableBounded*)CurrentNode, FALSE, TRUE, FALSE, FALSE))    // Do not recache
04004         return FALSE; 
04005 
04006 // >>>> Temporary BODGE to aid select-inside...
04007 // >>>> ALWAYS clear any attributes of the same type from the subtree (even if the
04008 // >>>> attribute just applied replaced an existing one) so that dubious
04009 // >>>> attribute states (due to this routine not dealing with Select-inside
04010 // >>>> properly yet) can be cleared.
04011 // >>>> NOTE! the current att (n) is passed in to DoRemoveAttTypeFromSubtree so
04012 // >>>> that it won't be deleted along with atts of the same type - that would be
04013 // >>>> (has been) disastrous!
04014     if (CurrentNode->IsCompoundClass() && !((NodeCompound*)CurrentNode)->IsValidEffectAttr(NewAttr))
04015 //          if ((!AttributeExists) && (CurrentNode->IsCompound()))
04016     {
04017         // Remove all instances of attributes of the same type from the subtree.
04018         // This is not neccessary if the AttributeExists flag is TRUE because
04019         // we know in this situation that the subtree cannot contain any other
04020         // instances of the attribute !.
04021         if (!DoRemoveAttrTypeFromSubtree(CurrentNode, NewAttr->GetAttributeType(), OldAttr))
04022         {
04023             return FALSE; 
04024         }           
04025     }
04026 
04027     NodeAttribute* AttribClone = NULL;
04028 
04029     // Have we have got an Attribute to do something with ?
04030     if (OldAttr != NULL)
04031     {
04032         // We're gunna just replace the attribute with the new one.
04033 
04034         // First make a copy of the new attribute.
04035         ALLOC_WITH_FAIL(AttribClone ,((NodeAttribute*)NewAttr->SimpleCopy()), this)
04036 
04037         if (AttribClone == NULL)
04038         {
04039             // We have failed to create the clone 
04040             return FALSE; 
04041         }
04042 
04043         // Complication !!
04044         // If we are replacing a Fill Attribute and the attribute we are replacing
04045         // is already filled, then we need to extract the colour of the existing
04046         // fill and use them for the new fill.
04047         if ( OldAttr->IsAFillAttr() )
04048         {
04049             AttrFillGeometry* NodeReplaced = (AttrFillGeometry*)OldAttr;
04050 
04051             // Are these both Graduated Fills ?
04052             if ( (NodeReplaced->GetAttributeType() == CC_RUNTIME_CLASS(AttrFillGeometry)) ||
04053                  (NodeReplaced->GetAttributeType() == CC_RUNTIME_CLASS(AttrTranspFillGeometry)) )
04054             {
04055                 // Copy the old colours into the new Fill
04056             
04057                 // NOTE:
04058                 // This should really be done with a virtual function
04059 
04060                 if (NodeReplaced->IsAFlatFill())
04061                 {
04062                     ((AttrFillGeometry*)AttribClone)->SetEndColour(NodeReplaced->GetStartColour());
04063                     ((AttrFillGeometry*)AttribClone)->SetEndTransp(NodeReplaced->GetStartTransp());
04064                 }
04065                 else
04066                 {
04067                     ((AttrFillGeometry*)AttribClone)->SetStartColour(NodeReplaced->GetStartColour());
04068                     ((AttrFillGeometry*)AttribClone)->SetEndColour(NodeReplaced->GetEndColour());
04069                     ((AttrFillGeometry*)AttribClone)->SetStartTransp(NodeReplaced->GetStartTransp());
04070                     ((AttrFillGeometry*)AttribClone)->SetEndTransp(NodeReplaced->GetEndTransp());
04071                 }
04072                 
04073                 ((AttrFillGeometry*)AttribClone)->SetTranspType(NodeReplaced->GetTranspType());
04074                 ((AttrFillGeometry*)AttribClone)->SetTesselation(NodeReplaced->GetTesselation());
04075                 ((AttrFillGeometry*)AttribClone)->SetSeed(NodeReplaced->GetSeed());
04076                 ((AttrFillGeometry*)AttribClone)->SetGraininess(NodeReplaced->GetGraininess());
04077                 ((AttrFillGeometry*)AttribClone)->SetGravity(NodeReplaced->GetGravity());
04078                 ((AttrFillGeometry*)AttribClone)->SetSquash(NodeReplaced->GetSquash());
04079                 ((AttrFillGeometry*)AttribClone)->SetTileable(NodeReplaced->GetTileable());
04080                 ((AttrFillGeometry*)AttribClone)->AttachBitmap(NodeReplaced->GetBitmap());
04081                 ((AttrFillGeometry*)AttribClone)->RecalcFractal();
04082 
04083             }
04084 
04085             if ( NewAttr->IsATranspFill() && NewAttr->IsAFlatFill())
04086             {
04087                 ((AttrFillGeometry*)OldAttr)->RenderFillBlobs();
04088             }
04089         }
04090 
04091         // Now we have done with the old attribute, so lets hide it, so
04092         // the changes can be undone
04093         if(!DoHideNode(OldAttr, 
04094                        TRUE         // Include the subtree size 
04095                        ))
04096             {
04097                 return FALSE; 
04098             } 
04099     }
04100         
04101     // Finally !! We can add the new attribute node into the tree.
04102     if (bOldWasEffect)
04103         AttribClone->AttachNode(CurrentNode, LASTCHILD);
04104     else
04105         AttribClone->AttachNode(CurrentNode, FIRSTCHILD);
04106 
04107     AttributeManager::pLastNodeAppliedTo = (NodeRenderableInk*)CurrentNode;
04108 
04109     // And now it's in the tree, we need to make sure that any fill control
04110     // points are valid.
04111     if (AttribClone->IsAFillAttr())
04112     {
04113         ((AttrFillGeometry*)AttribClone)->AttributeChanged();
04114     }
04115 
04116     HideNodeAction* UndoHideNodeAction; 
04117 
04118     // Create an action to hide the attribute when we undo 
04119     if ( HideNodeAction::Init(this, 
04120                               &UndoActions, 
04121                               AttribClone, 
04122                               TRUE,             // When the attribute gets hidden we
04123                                                 // must include its size 
04124                              (Action**)(&UndoHideNodeAction))
04125          == AC_FAIL)  
04126     {   
04127         AttribClone->CascadeDelete();       
04128         delete (AttribClone); 
04129         return FALSE; 
04130     }
04131 
04132     ReleaseCachedForAttrApply((NodeRenderableBounded*)CurrentNode, bOldWasEffect);
04133 
04134     if (!DoInvalidateNodeRegion((NodeRenderableBounded*)CurrentNode, FALSE, TRUE, FALSE, FALSE))    // Do not recache
04135         return FALSE;
04136 
04137     return TRUE;
04138 }
04139 
04140 /********************************************************************************************
04141 
04142 >   BOOL OpReplaceAttributes::Init()
04143 
04144     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
04145     Created:    29/1/95
04146     Inputs:     -
04147     Outputs:    -
04148     Returns:    TRUE if the operation could be successfully initialised 
04149                 FALSE if no more memory could be allocated 
04150                 
04151     Purpose:    OpReplaceAttributes initialiser method
04152     Errors:     ERROR will be called if there was insufficient memory to allocate the 
04153                 operation.
04154     SeeAlso:    -
04155 
04156 ********************************************************************************************/
04157 
04158 BOOL OpReplaceAttributes::Init()
04159 {
04160     
04161     // Register the opdescriptors for the OpReplaceAttributes operation
04162     OpDescriptor* OpDesc = new OpDescriptor(
04163                                             0,
04164                                             _R(IDS_REPLACEATTRSOP),                 
04165                                             CC_RUNTIME_CLASS(OpReplaceAttributes),
04166                                             OPTOKEN_REPLACEATTRS,
04167                                             OpReplaceAttributes::GetState); 
04168 
04169 
04170     if (OpDesc != NULL)
04171     {
04172         // Bodge this needs to use faby Macro technology so that the op is attached to
04173         // a system bar.
04174         OpDesc = new OpChangeLineWidthOpDesc(0,
04175                                              _R(IDS_CHANGELINEWIDTH),
04176                                              CC_RUNTIME_CLASS(OpReplaceAttributes),
04177                                              OPTOKEN_CHANGELINEWIDTH,
04178                                              OpChangeLineWidthOpDesc::GetState,
04179                                              0,
04180                                              _R(IDBBL_CHANGELINEWIDTH));
04181          
04182     }
04183 
04184 
04185 
04186     
04187     ERRORIF(!OpDesc, _R(IDE_NOMORE_MEMORY), FALSE);
04188     return(OpDesc != NULL);
04189 }               
04190 
04191 /********************************************************************************************
04192 
04193 >   OpState OpReplaceAttributes::GetState(String_256*, OpDescriptor* OpDesc)
04194 
04195     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
04196     Created:    29/1/95
04197     Inputs:     -
04198     Outputs:    -
04199     Returns:    The state of the OpReplaceAttributes operation
04200     Purpose:    For finding OpReplaceAttributes state. 
04201     Errors:     -
04202     SeeAlso:    -
04203 
04204 ********************************************************************************************/
04205 
04206 OpState OpReplaceAttributes::GetState(String_256*, OpDescriptor* OpDesc)
04207 {
04208     OpState OpSt;
04209 
04210     OpSt.Greyed = ( FALSE );  
04211                                                 
04212     return(OpSt);   
04213 }
04214 
04215 
04216 /********************************************************************************************
04217 
04218 >   virtual void OpReplaceAttributes::GetOpName(String_256* OpName) 
04219 
04220     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
04221     Created:    29/1/95
04222     Inputs:     -
04223     Outputs:    The undo string for the operation
04224     Returns:    
04225     Purpose:    The GetOpName fn is overridden so that we return back a description 
04226                 appropriate to the type of attribute that the operation applies. 
04227                     
04228     Errors:     -
04229     SeeAlso:    -
04230 
04231 ********************************************************************************************/
04232 
04233 void OpReplaceAttributes::GetOpName(String_256* OpName) 
04234 { 
04235     
04236     *OpName = String_256(UndoAttribStrID); 
04237     *OpName += String_256(_R(IDS_CHANGE)); 
04238 }  
04239 
04240 

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