SelOperation Class Reference

A SelOperation. More...

#include <selop.h>

Inheritance diagram for SelOperation:

UndoableOperation Operation MessageHandler ListItem CCObject SimpleCCObject CarbonCopyOp OpAddBlendPath OpAddPath OpApplyAttrib OpApplyClipView OpApplyNamesToSel OpBaseConvertPathSegment OpBlendNodes OpBreakAtPoints OpBreakShapes OpBringToFront OpChangeBlend OpChangeBlendDistance OpChangeBlendSteps OpChangeBrush OpChangeContourAttributeProfile OpChangeContourColourType OpChangeContourObjectProfile OpChangeContourStepDistance OpChangeContourSteps OpChangeContourToInner OpChangeContourToOuter OpChangeFillProfile OpChangeTranspFillProfile OpClipartImport OpClipboardImport OpCombineLayersToFrameLayer OpCombineShapes OpContourNodes OpConvertPathToShapes OpConvertToBitmap OpCopy OpCreateContour OpCut OpDelete OpDeletePoints OpDeleteTextStory OpDetachBlendPath OpDragRegularShape OpDroppedFile OpEditBlendEndObject OpEditRectangle OpEditRegularShape OpFreeHand OpGroup OpJoinShapes OpLiveEffect OpMakeShapes OpMakeStroke OpMenuExport OpMenuImport OpMenuLoad OpMouldLibSel OpMoveBackwards OpMoveForwards OpMoveToLayer OpMoveToLyrBehind OpMoveToLyrInFront OpNewRegShape OpNodePathEditBlob OpOverprintFill OpOverprintLine OpPaste OpPasteAttributes OpPrintOnAllPlates OpPutToBack OpRedefineNamesAsSel OpReformShapeEdge OpRemoveAttributeFromSelection OpRemoveBlend OpRemoveClipView OpRemoveContour OpRemoveNamesFromSel OpReplaceAttributes OpRetroFit OpRetroSmooth OpReversePath OpShapeMakeRegular OpShapeToggleBase OpShapeToggleSetNumSides OpSmoothSelection OpTextUndoable OpToggleContourInsetPath OpToggleSmooth OpUngroup PlugInUndoOp TraceOp List of all members.

Public Member Functions

 SelOperation ()
 SelOperation constructor, calls the constructor of the base class.
virtual ~SelOperation ()
 SelOperation destructor.
BOOL DoStartSelOp (BOOL RenderEndSelStateBlobs, BOOL RenderStartSelStateBlobs=TRUE, BOOL UndoRenderEndSelStateBlobs=FALSE, BOOL UndoRenderStartSelStateBlobs=FALSE)
 This function must be called by all SelOperations. It does the following:.
virtual void End ()
 Calls DoEndSelOp(), then Operation::End().
virtual BOOL MayChangeNodeBounds () const
virtual BOOL GetStarted () const
virtual BOOL DoAttributesSelected (List &AttribsToApply, UINT32 OpName, BOOL bPasteAttrs)
 This high level function should be called whenever we need to apply multiple attributes to the selection (eg. PasteAttributes).
virtual BOOL DoApplyAttribsToSelection (OpParam *pOpParam, BOOL bClearIfNoneApplied=FALSE)
 Performs the OpApplyAttribsToSelected operation. This tries to apply the list of attributes in the OpParam->AttribsToApplyList to the selection. If no attribute can be applied then the operation does not generate any actions, and so will be a NOP and be destroyed during End().
virtual BOOL DoApplyToSelection (List *Attribs, List *AttrGroupList, BOOL *pAttribWasRequired)
virtual BOOL DoApply (Node *CurrentNode, NodeAttribute *Attrib, BOOL Mutate, BOOL InvalidateRegion=TRUE, BOOL KeepExistingCharacteristics=TRUE, BOOL *AttribWasRequired=NULL, BOOL *pbCanDiscardUndo=NULL)
 Will not apply the attribute to CurrentNode if it does not require it.

Static Public Member Functions

static BOOL DoApply (SelOperation *pOp, Node *CurrentNode, NodeAttribute *Attrib, BOOL Mutate, BOOL InvalidateRegion=TRUE, BOOL KeepExistingCharacteristics=TRUE, BOOL bOptimise=TRUE, BOOL *AttribWasRequired=NULL, BOOL *pbCanDiscardUndo=NULL)
static BOOL InvalidateNodeRegion (SelOperation *pOp, NodeRenderableBounded *CurrentNode, NodeAttribute *Attrib, BOOL Mutate)
static BOOL ReleaseCachedForAttrApply (NodeRenderableBounded *pNode, BOOL bEffectRootOnly)
 If we have just set an effect attribute then we can avoid releasing cached info of the specified node and all its children... We RELY on invalidations associated with this function not calling ReleaseCached themselves!

Protected Member Functions

virtual BOOL ApplyAttributeToBrush (Node *pCurrentNode, NodeAttribute *pAttr)
 Works out if pCurrentNode has an applied brush attribute, and if so then sets up an action to tell the brush to use the attribute being applied.
virtual BOOL ApplyLineWidthToBrush (AttrBrushType *pAttrBrush, NodeRenderableInk *pBrushedNode, NodeAttribute *pAttr)
 If we are applying a line width to a brushed node we need to tell the brush that it must scale to line width. We must also do an additional invalidate region so that if we are increasing in size then the brush will be rerendered properly.
virtual BOOL ApplyStrokeColourToBrush (AttrBrushType *pAttrBrush, NodeRenderableInk *pBrushedNode)
 When a new stroke colour is applied to a brushed object we wish to set the flag that tells the brush to override the named colours in the brush with the colour applied.
virtual BOOL ApplyTransparencyToBrush (AttrBrushType *pAttrBrush, NodeRenderableInk *pBrushedNode)
 When a transparency is applied to a brushed object we need to set a flag in the brush to tell it to use this new transparency.
virtual BOOL ApplyBrushAttr (Node *pCurrentNode, AttrBrushType *pAttrBrush)
 When we apply a brush attribute to an object we want to check to see if the current line width attribute is the same as the default attribute. If it is then we will apply the default line width suggested by the brush itself. This is because people keep on applying brush attributes to 500MP lines and ending up with thousands of brush objects, which makes everything really slow.

Private Member Functions

BOOL DoEndSelOp ()
 This function gets called automatically by End().

Private Attributes

SelectionStateSelState
BOOL m_bStartCalled

Static Private Attributes

static BOOL RenderEndSelStateBlobs
static BOOL RenderStartSelStateBlobs
static BOOL UndoRenderEndSelStateBlobs
static BOOL UndoRenderStartSelStateBlobs

Detailed Description

A SelOperation.

Author:
Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
Date:
15/2/94
See also:
TransOperation

Definition at line 121 of file selop.h.


Constructor & Destructor Documentation

SelOperation::SelOperation  ) 
 

SelOperation constructor, calls the constructor of the base class.

Author:
Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
Date:
15/2/94
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
-

Definition at line 138 of file selop.cpp.

00138                           : UndoableOperation()
00139 {
00140     m_bStartCalled = FALSE;
00141 }

SelOperation::~SelOperation  )  [virtual]
 

SelOperation destructor.

Author:
Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
Date:
15/2/94
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
-

Definition at line 158 of file selop.cpp.

00159 {
00160 }


Member Function Documentation

BOOL SelOperation::ApplyAttributeToBrush Node pCurrentNode,
NodeAttribute pAttr
[protected, virtual]
 

Works out if pCurrentNode has an applied brush attribute, and if so then sets up an action to tell the brush to use the attribute being applied.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
24/2/2000
Parameters:
pCurrentNode - the node that the attribute is being applied to [INPUTS] pAttr - the attribute to apply
Returns:
TRUE if all ok.

Errors: -

See also:
-
Notes: This is only currently operational for fill attributes, others may be added in future. 18/4/2000 - Line widths added

Definition at line 770 of file attrappl.cpp.

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 }

BOOL SelOperation::ApplyBrushAttr Node pCurrentNode,
AttrBrushType pAttrBrush
[protected, virtual]
 

When we apply a brush attribute to an object we want to check to see if the current line width attribute is the same as the default attribute. If it is then we will apply the default line width suggested by the brush itself. This is because people keep on applying brush attributes to 500MP lines and ending up with thousands of brush objects, which makes everything really slow.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
24/2/2000
Parameters:
pCurrentNode - the node we are applying the brush to [INPUTS] pAttrBrush - the brush attribute we are applying
Returns:
TRUE if all ok.

Definition at line 961 of file attrappl.cpp.

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 }

BOOL SelOperation::ApplyLineWidthToBrush AttrBrushType pAttrBrush,
NodeRenderableInk pBrushedNode,
NodeAttribute pAttr
[protected, virtual]
 

If we are applying a line width to a brushed node we need to tell the brush that it must scale to line width. We must also do an additional invalidate region so that if we are increasing in size then the brush will be rerendered properly.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
24/2/2000
Parameters:
pAttrBrush - the brush attribute that we wish to update [INPUTS] pBrushedNode -the node that the brush applies to pAttr - the line width that we are applying
Returns:
TRUE if all ok.

Definition at line 921 of file attrappl.cpp.

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 }

BOOL SelOperation::ApplyStrokeColourToBrush AttrBrushType pAttrBrush,
NodeRenderableInk pBrushedNode
[protected, virtual]
 

When a new stroke colour is applied to a brushed object we wish to set the flag that tells the brush to override the named colours in the brush with the colour applied.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
24/2/2000
Parameters:
pAttrBrush - the brush attribute that we wish to update [INPUTS] pbrushed node - the node that the brush applies to
Returns:
TRUE if all ok.

Definition at line 871 of file attrappl.cpp.

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 }

BOOL SelOperation::ApplyTransparencyToBrush AttrBrushType pAttrBrush,
NodeRenderableInk pBrushedNode
[protected, virtual]
 

When a transparency is applied to a brushed object we need to set a flag in the brush to tell it to use this new transparency.

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
24/2/2000
Parameters:
pAttrBrush - the brush attribute that we wish to update [INPUTS] pAttr - the attribute to apply
Returns:
TRUE if all ok.

Definition at line 838 of file attrappl.cpp.

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 }

BOOL SelOperation::DoApply Node CurrentNode,
NodeAttribute Attrib,
BOOL  Mutate,
BOOL  InvalidateRegion = TRUE,
BOOL  KeepExistingCols = TRUE,
BOOL *  pbAttribWasRequired = NULL,
BOOL *  pbCanDiscardUndo = NULL
[virtual]
 

Will not apply the attribute to CurrentNode if it does not require it.

Author:
Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
Date:
5/4/95
Parameters:
InvalidateRegion,: When TRUE DoInvalidateNodeRegion is called before and [INPUTS] after the attribute has been applied. If the attribute is being applied to a range of objects then it is inefficient to do this. KeepExistingCols: When TRUE the new attribute takes on the colour characteristics of the attribute it is replacing.
AttribWasRequired:This output is optional. It indicates if the [OUTPUTS] Attrib was required by the object.
Returns:
TRUE if successful FALSE if the operation should be aborted (TIDYUP THEN CALL End()!)

Errors: -

See also:
NodeRenderableInk::GetObjectToApplyTo

Definition at line 229 of file attrappl.cpp.

00237 {
00238     return SelOperation::DoApply(this, CurrentNode, Attrib, Mutate, InvalidateRegion, KeepExistingCols, TRUE, pbAttribWasRequired, pbCanDiscardUndo);
00239 }

BOOL SelOperation::DoApply SelOperation pOp,
Node CurrentNode,
NodeAttribute Attrib,
BOOL  Mutate,
BOOL  InvalidateRegion = TRUE,
BOOL  KeepExistingCharacteristics = TRUE,
BOOL  bOptimise = TRUE,
BOOL *  AttribWasRequired = NULL,
BOOL *  pbCanDiscardUndo = NULL
[static]
 

Definition at line 241 of file attrappl.cpp.

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 }

BOOL SelOperation::DoApplyAttribsToSelection OpParam pOpParam,
BOOL  bClearIfNoneApplied = FALSE
[virtual]
 

Performs the OpApplyAttribsToSelected operation. This tries to apply the list of attributes in the OpParam->AttribsToApplyList to the selection. If no attribute can be applied then the operation does not generate any actions, and so will be a NOP and be destroyed during End().

Author:
Phil_Martin (Xara Group Ltd) <camelotdev@xara.com> (from Simon)
Date:
16/02/2005 (from 16/8/93)
Parameters:
OpParam,: This must be a ApplyAttribsToSelectedParam type [INPUTS]
OpParam->AttribsToApply: This is the list of attributes that is to be applied to the selection. Each item on this list must be a NodeAttributePtrItem.

OpParam->undoAttribStrID: Specifies the undo string to display (eg. Paste Atrributes)

Parameters:
OpParam->AttrGroupList,: This list must be empty on entry to this function [OUTPUTS] If (Success) then this list will contain one ListListItem per attribute on the AttribsToApplyList. Each of these items will point to a Set of AttributeGroupItems.
Attribute -> { Set of current attribute groups }

Whenever an attribute is applied to an object, the Current AttributeGroup associated with the object is added to the attributes set of current attribute groups. Therefore if an attribute's set is empty then we know that the attribute has not been applied.

OpParam->Success: FALSE if the operation failed. i.e. FailAndExecute() was called.

anyAttrsApplied: FALSE if the operation has not applied any attributes

This Operation currently does not support mutation

Definition at line 2388 of file attrappl.cpp.

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