OpCombineShapes Class Reference

Base class op that does all the shape combining ops, like add, subtract and intersect,etc. More...

#include <combshps.h>

Inheritance diagram for OpCombineShapes:

SelOperation UndoableOperation Operation MessageHandler ListItem CCObject SimpleCCObject OpAddShapes OpIntersectShapes OpSliceShapes OpSubtractShapes List of all members.

Public Member Functions

 OpCombineShapes ()
 Default constructor.
 ~OpCombineShapes ()
 Default destructor - does nothing.
virtual void Do (OpDescriptor *)
 This is it! The routine that gets all the hard work underway.
virtual void GetOpName (String_256 *)
 The GetOpName fn is overridden so that we return back a description appropriate to the type of attribute that the operation applies.

Static Public Member Functions

static BOOL Init ()
 Declares a preference that allows you to clear memory in delete().
static OpState GetState (String_256 *, OpDescriptor *)
 Gives the op an oppertunity to say whether the op is executable or not.

Protected Attributes

CombineReason Reason

Private Member Functions

BOOL ConvertToShapes (Range *pRange, BOOL bCombineEffectBitmaps)
 This subtracts all the shapes in all the lists from all the lines in all the lists This subtracts all the shapes in all the lists from pLine This subtracts all the shapes in pSelObjPathList from pLine This generates the data structure that holds all the paths generated by the selected objects. NOTE! Pointers to paths in the tree are stored in the lists (unlike CreateSelObjPathLists).
BOOL CreateSelObjPathLists (BOOL bCombineEffectBitmaps)
 This generates the data structure that holds all the paths generated by the selected objects.
BOOL CreateSelObjPathList (Node *pNode, SelObjPathList *pSelObjPathList, BOOL IsFirstNode=TRUE, BOOL bCombineEffectBitmaps=TRUE, BecomeAReason reason=BECOMEA_PASSBACK)
 This generates the data structure that holds all the paths generated by a given selected object.
BOOL DoAddShapes ()
 This adds all the paths together to form a combined silhouette path of the selection.
BOOL AddPathsFromList (Path *pPath, SelObjPathList *pSelObjPathList)
 This adds all the paths in the list together to form a combined silhouette path of the selection.
BOOL AddPaths (Path *pPath1, Path *pPath2)
 This adds the two paths together using ClipPathToPath, storing the result in pPath1.
BOOL AddOrMergePaths (Path *pPath1, Path *pPath2)
 Helper function for DoAddShapes and AddPathsFromList.
BOOL DoCombineShapes (ClipStyle Style, BOOL bCombineEffectBitmaps)
 Firstly, this adds all the paths together in the last list to form the clip path. Then all the other paths in the other lists are combined with the clip path, using the method specified in 'Flags'.
BOOL CombinePathsFromList (ClipStyle Style, Path *pClipPath, Path *pStrokedClipPath, SelObjPathList *pSelObjPathList, BOOL bCombineEffectBitmaps=TRUE)
 This combines pClipPath with all the paths in pSelObjPathList.
BOOL CombinePaths (ClipStyle Style, Path *pClipPath, Path *pStrokedClipPath, Path *pSrcPath, Path *pDestPath)
 This combines pClipPath from pSrcPath using ClipPathToPath, storing the result in pDestPath.
BOOL DoHideListedNodes (BOOL bCombineEffectBitmaps)
 This hides all the nodes that were selected when the op was invoked.
BOOL DoSelectResultNodes (BOOL bCombineEffectBitmaps)
 This selects all the nodes in the results list.
BOOL ApplyAttributes (NodeRenderableInk *pSrcNode, NodeRenderableInk *pDestNode, CCAttrMap *pAttrMap, SelObjPathListItem *pListItem=NULL)
 This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.
BOOL ApplyEffects (SelObjPathListItem *pListItem, NodeRenderableInk *pDestNode, NodeRenderableInk *pParentEffect)
 To delete the extra attributes we allocated in AdjustAttributesForStroke This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.
BOOL StrokePathToPath (Path *pPath, Path *pDest)
 This picks out a subpath from Src and places it into pDest Strokes pPath, and places result in pDest.
BOOL ConvertClipLineToShape (Path *pClipPath, Path *pSrcPath, Path *pDestPath)
 Only call this if pClipPath is a line.
DocRect GetBoundingRect (Path *pPath)
 This gets the exact pure path bounds, using GDraw_CalcStrokeBBox.
BOOL DoInsertNewNode (NodeRenderableBounded *pNewNode)
 High-level interface to UndoableOperation::DoInsertNode().
UINT32 AdjustAttributesForStroke (CCAttrMap *pAttrMap, SelObjPathListItem *pListItem=NULL)
 If we are doing combine shapes on a stroked shape then we need to make the following changes: 1) Do not keep the AttrStrokeType and VariableWidth around, this is because their effects have been achieved via the DoBecomeA so if we keep them then effectively we are stroking our final output twice. 2) Set line width to zero. Once again the line width effect has been extracted from the DoBecomeA 3) Use the current stroke colour for the new fill colour. 4) Ditch the stroke colour?
BOOL BeginSlowJob ()
 Wrapper around BeginSlowJob() that sets up the hour glass counter correctly.
SelObjPathListGetFirstList ()
SelObjPathListGetLastList ()
SelObjPathListGetNextList (SelObjPathList *pItem)
ListRangeCopyRange (Range *pRange)
 Copy the specified range of nodes.

Private Attributes

List ListOfSelObjPathLists
ListRangepListOfResults
DocRect OriginalBoundingRect
SelRangepSelRange
INT32 NumPaths
INT32 JobCount
INT32 NodeInsertCount
NodepContextNode
AttachNodeDirection AttachDir

Detailed Description

Base class op that does all the shape combining ops, like add, subtract and intersect,etc.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
11/1/95

Definition at line 232 of file combshps.h.


Constructor & Destructor Documentation

OpCombineShapes::OpCombineShapes  ) 
 

Default constructor.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
-

Definition at line 167 of file combshps.cpp.

00168 {
00169     Reason      = COMBINE_NONE;
00170     pSelRange   = NULL;
00171     NumPaths    = 0;
00172     JobCount    = 0;
00173 
00174     pContextNode = NULL;
00175     pListOfResults = NULL;
00176 }

OpCombineShapes::~OpCombineShapes  ) 
 

Default destructor - does nothing.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-

Errors: -

See also:
-

Definition at line 193 of file combshps.cpp.

00194 {
00195     ListOfSelObjPathLists.DeleteAll();
00196     if (pListOfResults)
00197     {
00198         delete pListOfResults;
00199         pListOfResults = NULL;
00200     };
00201 }


Member Function Documentation

BOOL OpCombineShapes::AddOrMergePaths Path pPath1,
Path pPath2
[private]
 

Helper function for DoAddShapes and AddPathsFromList.

Author:
Gerry_Iles (Xara Group Ltd) <camelotdev@xara.com>
Date:
08/02/2005
Parameters:
pPath1 - pointer to a Path [INPUTS] pPath2 - pointer to a Path
Returns:
TRUE if ok, FALSE if bother

Definition at line 749 of file combshps.cpp.

00750 {
00751     BOOL ok = TRUE;
00752     // if num coords == 0, the path is effectively empty, so just copy this node path's path
00753     if (pPath1->GetNumCoords() == 0)
00754         ok = pPath1->MergeTwoPaths(*pPath2);
00755     else
00756     {
00757         // Otherwise actually 'add' the paths together, using ClipPathToPath
00758         ok = AddPaths(pPath1,pPath2);
00759 
00760         // If this fails, we can just merge the two paths together as this achieves the desired
00761         // silhouette effect
00762         if (!ok) ok = pPath1->MergeTwoPaths(*pPath2);
00763 
00764     }
00765     return(ok);
00766 }

BOOL OpCombineShapes::AddPaths Path pPath1,
Path pPath2
[private]
 

This adds the two paths together using ClipPathToPath, storing the result in pPath1.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
pPath1 = ptr to first path, and also the place to store resultant path [INPUTS] pPath2 = ptr to second path, i.e. the path to add to pPath1
*pPath1 contains the result of adding the two paths (if TRUE returned). [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
OpCombineShapes::AddPathsFromList()

Definition at line 785 of file combshps.cpp.

00786 {
00787     ERROR2IF(pPath1 == NULL,FALSE,"pPath1 == NULL");
00788     ERROR2IF(pPath2 == NULL,FALSE,"pPath2 == NULL");
00789 
00790     // check for a zero-bounding box path
00791     DocRect dr = pPath2->GetBoundingRect();
00792 
00793     if (dr.Width() <= 1 && dr.Height() <= 1)
00794     {
00795         ERROR3("We have a zero bounding rect path");
00796         return FALSE;
00797     }
00798 
00799     Path StrokedPath2;
00800     if (!StrokedPath2.Initialise()) return FALSE;
00801 
00802     // is path 2 a line?
00803     if (pPath2->GetPathType() == PATHTYPE_LINE)
00804     {
00805         if (!StrokePathToPath(pPath2,&StrokedPath2))
00806             return FALSE;
00807     }
00808 
00809     INT32 len = CombinePaths(CLIP_STYLE_ADD,pPath2,&StrokedPath2,pPath1,pPath1);
00810 
00811     return (len > 0);
00812 }

BOOL OpCombineShapes::AddPathsFromList Path pPath,
SelObjPathList pSelObjPathList
[private]
 

This adds all the paths in the list together to form a combined silhouette path of the selection.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
pPath = ptr to path to place the result [INPUTS] pSelObjPathList = ptr to list of paths
pPath contains the union of all the paths in pSelObjPathList [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
This can be thought of as a logical OR of the paths
Returns:
Errors: -
See also:
OpCombineShapes::Do()

Definition at line 671 of file combshps.cpp.

00672 {
00673     // Check our input params
00674     ERROR3IF(pPath == NULL,"pPath == NULL");
00675     ERROR3IF(pSelObjPathList == NULL,"pSelObjPathList== NULL");
00676     if (pPath == NULL || pSelObjPathList == NULL) return FALSE;
00677 
00678     NodePath* pTempPath = new NodePath;
00679     if (pTempPath == NULL)       return FALSE;
00680     if (!pTempPath->SetUpPath()) return FALSE;
00681 
00682     // Get the first path in this list
00683     SelObjPathListItem* pSelObjPathListItem = pSelObjPathList->GetFirstItem();
00684 
00685     BOOL ok = TRUE;
00686 
00687     while (ok && pSelObjPathListItem != NULL)
00688     {
00689         // Get a pointer to the actual node path in this list item
00690         Path* pSrcPath = pSelObjPathListItem->GetPath();
00691 
00692         if (pSrcPath != NULL)
00693         {
00694             // Add this path into the TempPath
00695             ok = AddOrMergePaths(&(pTempPath->InkPath), pSrcPath);
00696 
00697             // If the TempPath is now too large
00698             if (ok && pTempPath->InkPath.GetNumCoords() > AddPathGranularity)
00699             {
00700                 // Combine the TempPath with the output path
00701                 ok = AddOrMergePaths(pPath, &(pTempPath->InkPath));
00702                 // And clear the TempPath
00703                 if (ok) ok = pTempPath->InkPath.ClearPath(FALSE);
00704             }
00705             if (ok) pSelObjPathList->SetProducedPaths(TRUE);
00706         }
00707         else
00708         {
00709             // What? No Src path? Surely not!
00710             ERROR3("pSrcPath == NULL");
00711             ok = FALSE;
00712         }
00713 
00714         if (ok)
00715             ok = ::ContinueSlowJob(++JobCount);
00716 
00717         // Get the next item in the selected object path list.
00718         pSelObjPathListItem = pSelObjPathList->GetNextItem(pSelObjPathListItem);
00719     }
00720 
00721     // If the TempPath has anything in
00722     if (ok && pTempPath->InkPath.GetNumCoords() > 0)
00723     {
00724         // Combine the TempPath with the output path
00725         ok = AddOrMergePaths(pPath, &(pTempPath->InkPath));
00726     }
00727 
00728     delete pTempPath;
00729 
00730     return (ok);
00731 }

UINT32 OpCombineShapes::AdjustAttributesForStroke CCAttrMap pAttrMap,
SelObjPathListItem pListItem = NULL
[private]
 

If we are doing combine shapes on a stroked shape then we need to make the following changes: 1) Do not keep the AttrStrokeType and VariableWidth around, this is because their effects have been achieved via the DoBecomeA so if we keep them then effectively we are stroking our final output twice. 2) Set line width to zero. Once again the line width effect has been extracted from the DoBecomeA 3) Use the current stroke colour for the new fill colour. 4) Ditch the stroke colour?

Author:
Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
Date:
4/9/2000
Parameters:
pAttrMap - the attribute map that we are about to apply to our combined node [INPUTS]
swaps a few things around in the map [OUTPUTS]
Returns:
0 - if we do not have an active stroke attribute (therefore do nothing) 1 - if we do have an active stroke and everything went well 2 - if we have an active stroke and something went wrong (e.g. out of memory)
We must actually make new nodes for these attributes as changing the existing ones will mess up our undo. These attributes must be deleted after they are applied by calling DeleteExtraAttributesForStroke()
Returns:
Errors: -
See also:
- Notes: Phil, 16/11/2005 We know that we own the attributes referred to by the Atrtibute Map so we can party on them without worrying about affecting other data structures. This allows us to remove attributes from the map completely, leaving it in a consistent state.

Definition at line 2135 of file combshps.cpp.

02136 {
02137     if (pAttrMap == NULL)
02138         return 0;
02139 
02140     // first see if we have an active stroke, if not we can just quit already
02141     AttrStrokeType* pStroke = NULL;
02142     pAttrMap->Lookup(CC_RUNTIME_CLASS(AttrStrokeType), (void*&)pStroke);
02143     
02144     if (pStroke == NULL || !pStroke->HasPathProcessor())
02145         return 0;
02146 
02147     AttrVariableWidth* pVarWidth = NULL;
02148     pAttrMap->Lookup(CC_RUNTIME_CLASS(AttrVariableWidth), (void*&)pVarWidth);
02149     
02150     // CGS:  we should only really continue IF we have an active value function!  Otherwise, we seem to run
02151     // into problems when we have give new objects most recent attributes turned on ....
02152     if ((pVarWidth == NULL) || !pVarWidth->HasActiveValueFunction ())
02153         return (0);
02154 
02155     // right so we want to set the line width to zero
02156     AttrLineWidth* pLineWidth = new AttrLineWidth;
02157     if (pLineWidth != NULL)
02158     {
02159         pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrLineWidth));
02160         pLineWidth->Value.LineWidth = 0;
02161         pAttrMap->SetAt(CC_RUNTIME_CLASS(AttrLineWidth), (void*)pLineWidth);
02162     }
02163 
02164     // now get the stroke colour and use it to make a fill colour
02165     AttrStrokeColour * pColour = NULL;
02166 
02167     // look up the stroke colour attribute          
02168     pAttrMap->Lookup(CC_RUNTIME_CLASS(AttrStrokeColour),
02169                         (void *&)pColour);
02170     
02171 
02172     // make a new flat fill attribute and apply this to the node
02173     if (pColour)
02174     {
02175         if (!pListItem || pListItem->GetStrokeCreatedPassbackPath ())
02176         {
02177             StrokeColourAttribute * pAttrVal = (StrokeColourAttribute *)pColour->GetAttributeValue();
02178 
02179             if (pAttrVal)
02180             {
02181                 AttrFlatColourFill* pFill = new AttrFlatColourFill;
02182                 
02183                 if (pFill)
02184                 {
02185                     DocColour Col = pAttrVal->Colour;
02186                     pFill->SetStartColour(&Col);
02187                     
02188                     // set back in the map
02189                     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrFillGeometry));
02190                     pAttrMap->SetAt(CC_RUNTIME_CLASS(AttrFillGeometry), (void*)pFill);
02191                 }
02192                 else
02193                     return 2;
02194             }
02195         }
02196         // now make a new stroke colour and set it transparent      
02197         AttrStrokeColour* pNewStrokeCol = new AttrStrokeColour;
02198         if (pNewStrokeCol)
02199         {
02200             pNewStrokeCol->Value.Colour = DocColour(COLOUR_NONE);
02201             pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrStrokeColour));
02202             pAttrMap->SetAt(CC_RUNTIME_CLASS(AttrStrokeColour), (void*)pNewStrokeCol);
02203         }
02204     }
02205 
02206     // now remove the stroke and var width attributes, and any brushes whilst we're here
02207     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrVariableWidth));
02208     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrStrokeType));
02209     pAttrMap->RemoveAttribute(CC_RUNTIME_CLASS(AttrBrushType));
02210 
02211     return 1;
02212 }

BOOL OpCombineShapes::ApplyAttributes NodeRenderableInk pSrcNode,
NodeRenderableInk pDestNode,
CCAttrMap pAttrMap,
SelObjPathListItem pListItem = NULL
[private]
 

This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
pSrcNode = ptr to node to get attrs from [INPUTS] pDestNode = ptr to node to apply pSrcNode's attrs to pAttrMap = ptr to attr map (NULL means get attrs applied to pSrcNode) pListItem = so that the can control processing based upon the values of this
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
-

Definition at line 2063 of file combshps.cpp.

02064 {
02065     ERROR3IF(pSrcNode  == NULL,"pSrcNode == NULL");
02066     ERROR3IF(pDestNode == NULL,"pDestNode == NULL");
02067     if (pSrcNode == NULL || pDestNode == NULL) return FALSE;
02068 
02069     BOOL ok = TRUE;             // Set to TRUE if we successfully apply attrs to pDestNode
02070     BOOL DeleteAttrs = FALSE;   // TRUE if we need to delete attr map before returning
02071 
02072 
02073     if (pAttrMap == NULL)
02074     {
02075         // Find the currently applied attributes and make a local copy of the map (like most
02076         // passback functions do)
02077         CCAttrMap AppliedMap(30);
02078         ok = pSrcNode->FindAppliedAttributes(&AppliedMap);
02079         pAttrMap = AppliedMap.Copy();
02080         DeleteAttrs = TRUE;
02081     }
02082 
02083     // if we have a stroked object we need to do some attribute juggling
02084     if (AdjustAttributesForStroke(pAttrMap, pListItem) == 2)
02085         return FALSE;
02086 
02087     // If all's OK, apply attrs to pDestNode
02088     ok = pDestNode->ApplyAttributes(pAttrMap,TRUE);
02089 
02090     // Delete the copied attr map if we have to
02091     if (DeleteAttrs)
02092     {
02093         pAttrMap->DeleteAttributes();
02094         delete pAttrMap;
02095     }
02096 
02097     return ok;
02098 }

BOOL OpCombineShapes::ApplyEffects SelObjPathListItem pListItem,
NodeRenderableInk pDestNode,
NodeRenderableInk pParentEffect
[private]
 

To delete the extra attributes we allocated in AdjustAttributesForStroke This applies attrs pDestNode. If pAttrMap == NULL, the attrs applied to pSrcNode are applied to pDestNode, else the attrs in pAttrMap are applied.

Author:
Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
Date:
04/02/2005
Parameters:
pListItem = so that the can control processing based upon the values of this [INPUTS] pDestNode = ptr to node to apply pSrcNode's attrs to
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
-

Definition at line 2297 of file combshps.cpp.

02298 {
02299     BOOL ok = TRUE;
02300     PORTNOTE("other", "Removed OpCombineShapes::ApplyEffects");
02301 #ifndef EXCLUDE_FROM_XARALX
02302 
02303     ERROR3IF(pListItem  == NULL, "pListItem == NULL");
02304     ERROR3IF(pDestNode == NULL, "pDestNode == NULL");
02305 
02306     NodeRenderableInk* pSrcNode = pListItem->GetCreatedByNode();
02307     ERROR3IF(pSrcNode  == NULL, "pSrcNode == NULL");
02308 
02309     // Get the effect stack for the object which produced this path (pSrcNode)
02310     // If the source node had effects applied to it
02311     // And that stack is different than the stack above the context node
02312     // Then we must copy the effect stack onto the destination node
02313     ListRange* pStack = pListItem->GetEffectStack();
02314     Node* pEffect = NULL;
02315     if (pStack)
02316         pEffect = pStack->FindFirst();
02317     if (pEffect)
02318     {
02319         if (pParentEffect==NULL || pParentEffect!=pEffect)
02320         {
02321             // We need to copy the effect stack onto this object
02322             OpLiveEffect::DoCopyEffectsStack(this, pDestNode, pStack, GetWorkingDoc(), GetWorkingDoc());
02323         }
02324     }
02325 #endif
02326     return ok;
02327 }

BOOL OpCombineShapes::BeginSlowJob  )  [private]
 

Wrapper around BeginSlowJob() that sets up the hour glass counter correctly.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
25/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK

Errors: -

See also:
-

Definition at line 382 of file combshps.cpp.

00383 {
00384     NumPaths = 0;
00385     JobCount = 0;
00386     SelObjPathList* pList = GetFirstList();
00387     while (pList != NULL)
00388     {
00389         NumPaths += pList->GetCount();
00390         pList = GetNextList(pList);
00391     }   
00392 
00393     INT32 SlowJobCount = NumPaths;
00394     if (Reason == COMBINE_SLICE)
00395         SlowJobCount *= 2;
00396     String_64 Str(_R(IDS_COMBINE_SLOWJOB));
00397     return (::BeginSlowJob(SlowJobCount,FALSE,&Str));
00398 }

BOOL OpCombineShapes::CombinePaths ClipStyle  Style,
Path pClipPath,
Path pStrokedClipPath,
Path pSrcPath,
Path pDestPath
[private]
 

This combines pClipPath from pSrcPath using ClipPathToPath, storing the result in pDestPath.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
Style = High level style of clipping [INPUTS] pClipPath = ptr to the path to combine with pSrcPath pStrokedClipPath = ptr to stroked version of pClipPath ONLY if pClipPath is a line pSrcPath = ptr to path that will be combined with pClipPath pDestPath = ptr to path to place the result in
*pDestPath contains the result of combining pCliPath with pSrcPath(if TRUE returned). [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
OpCombineShapes::CombinePathsFromList()

Definition at line 1136 of file combshps.cpp.

01137 {
01138     // Check those entry params
01139     ERROR2IF(pClipPath          == NULL,FALSE,"pClipPath == NULL");
01140     ERROR2IF(pStrokedClipPath   == NULL,FALSE,"pStrokedClipPath == NULL");
01141     ERROR2IF(pSrcPath           == NULL,FALSE,"pSrcPath == NULL");
01142     ERROR2IF(pDestPath          == NULL,FALSE,"pDestPath == NULL");
01143 
01144     UINT32 Flags = 0;   // Flags passed to ClipPathToPath()
01145     BOOL Clip = TRUE;   // If this is TRUE, ClipPathToPath() is called at the end of this func
01146     INT32 len = -1;     // This is the length of the path produced by ClipPathToPath
01147 
01148     BOOL SrcIsLine  = (pSrcPath->GetPathType()  == PATHTYPE_LINE);  // TRUE if src path is a line
01149     BOOL ClipIsLine = (pClipPath->GetPathType() == PATHTYPE_LINE);  // TRUE if clip path is a line
01150 
01151     Path DestPath;      // Used when using a line to slice the source path up
01152 
01153     // If the clip path is a line and the op is slice, try and make a closed path using pClipPath
01154     // and the bounding rect of pSrcPath
01155     if (ClipIsLine && Reason == COMBINE_SLICE)
01156     {
01157         if (DestPath.Initialise() && ConvertClipLineToShape(pClipPath,pSrcPath,&DestPath))
01158         {
01159             // If successfully created a closed path, then pretend func was entered with the new path
01160             // by pointing pClipPath to it. Also the clip path is no longer a line
01161             pClipPath = &DestPath;
01162             ClipIsLine = FALSE;
01163         }
01164     }
01165     
01166     switch (Style)
01167     {
01168         case CLIP_STYLE_SUBTRACT:
01169             Flags = 1;  // 1 is Source AND NOT Clip
01170             if (SrcIsLine)  Flags |= CLIPPING_IS_STROKED;   // If source is a line, set the stroked flag
01171 
01172             if (ClipIsLine)
01173             {
01174                 // If slicing with a line, don't bother doing the subtraction stage 
01175                 if (Reason == COMBINE_SLICE)
01176                     Clip = FALSE;
01177                 else
01178                 // Otherwise subtract with a stroked version of the line.
01179                 // If the user's mad enough to try it, then we'd better try our best
01180                 { pClipPath = pStrokedClipPath; Flags |= CLIPPING_CLIP_WINDING; }
01181             }
01182             break;
01183 
01184         case CLIP_STYLE_INTERSECT:
01185             // If src is a line, Flags = 0 | stroked flag, else Flags = Source AND Clip
01186             if (SrcIsLine) Flags = CLIPPING_IS_STROKED; else Flags = 2; 
01187             if (ClipIsLine)
01188             {
01189                 // If slicing with a line, don't bother doing the intersection stage 
01190                 if (Reason == COMBINE_SLICE)
01191                     Clip = FALSE;
01192                 else
01193                 // Otherwise intersect with a stroked version of the line.
01194                 // If the user's mad enough to try it, then we'd better try our best
01195                 { pClipPath = pStrokedClipPath; Flags |= CLIPPING_CLIP_WINDING; }
01196             }
01197             break;
01198 
01199         case CLIP_STYLE_ADD:
01200             Flags = 7;  // 7 is Source OR Clip
01201             // convert lines into paths by stroking them
01202             // Also we must use a Non-zero winding rule to avoid cross segment lines taking effect
01203             if (SrcIsLine)  { StrokePathToPath(pSrcPath,pSrcPath);   Flags |= CLIPPING_SOURCE_WINDING; }
01204             if (ClipIsLine) { pClipPath = pStrokedClipPath; Flags |= CLIPPING_CLIP_WINDING; }
01205             break;
01206     }
01207 
01208     if (Clip)
01209     {
01210         // Clip those paths together
01211         // Mark Howitt 31/10/00
01212         // I`ve removed the ClipPathToPathWithAutoFlatness function and replaced it with
01213         // the function that returns you a flatness value to use with the ClipPath function
01214         double ClippingFlatness = pClipPath->CalculateFlatnessValueFromPath(750.0, 2.0, 375.0);
01215         double SourceFlatness = pSrcPath->CalculateFlatnessValueFromPath(750.0, 2.0, 375.0);
01216 
01217         len  = pClipPath->ClipPathToPath(*pSrcPath, pDestPath, Flags, DefaultTolerance, ClippingFlatness, SourceFlatness);
01218 /*
01219         // show debugging info about the path we have clipped
01220         INT32 NumCoords = pDestPath->GetNumCoords();
01221         PathVerb * pVerbs = pDestPath->GetVerbArray();
01222         DocCoord * pCoord = pDestPath->GetCoordArray();
01223 
01224         for (INT32 i = 0; i < NumCoords; i++)
01225         {
01226             if (pVerbs[i] == PT_MOVETO)
01227                 TRACE( _T("MOVE TO "));
01228             else
01229             if (pVerbs[i] == PT_BEZIERTO)
01230                 TRACE( _T("BEZIER TO "));
01231             else
01232             if (pVerbs[i] == PT_LINETO)
01233                 TRACE( _T("LINE TO "));
01234 
01235             TRACE( _T("%d, %d\n"), pCoord[i].x, pCoord[i].y);
01236         }
01237         TRACE( _T("end line\n"));
01238 */
01239     }
01240 
01241     return (len > 0);
01242 }

BOOL OpCombineShapes::CombinePathsFromList ClipStyle  Style,
Path pClipPath,
Path pStrokedClipPath,
SelObjPathList pSelObjPathList,
BOOL  bCombineEffectBitmaps = TRUE
[private]
 

This combines pClipPath with all the paths in pSelObjPathList.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
Style = High level style of clipping [INPUTS] pClipPath = ptr to path to combine with the paths in pSelObjPathList pStrokedClipPath= ptr to stroked version of pClipPath ONLY if pClipPath is a line pSelObjPathList = ptr to list of paths
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
For each path it tries to combine it with pClipPath. A new NodePath is generated and placed in the tree at the place the selected object lives.
Returns:
Errors: -
See also:
OpCombineShapes::DoCombineShapes()

Definition at line 1009 of file combshps.cpp.

01014 {
01015     // Check our input params
01016     ERROR3IF(pClipPath == NULL,"pClipPath == NULL");
01017     ERROR3IF(pSelObjPathList == NULL,"pSelObjPathList == NULL");
01018     if (pClipPath == NULL || pSelObjPathList == NULL) return FALSE;
01019 
01020     // Get the first path in this list
01021     SelObjPathListItem* pFirstItem = pSelObjPathList->GetFirstItem();
01022     SelObjPathListItem* pSelObjPathListItem = pFirstItem;
01023     BOOL ok = TRUE;
01024 
01025     while (ok && pSelObjPathListItem != NULL)
01026     {
01027         // Get a pointer to the actual node path in this list item
01028         NodePath* pNodePath = pSelObjPathListItem->GetNodePath();
01029         NodePath* pNewNodePath = new NodePath;
01030         NodeRenderableInk* pCreatedByNode = pSelObjPathListItem->GetCreatedByNode();
01031 
01032         if (pNodePath != NULL && pNewNodePath != NULL && pNewNodePath->SetUpPath())
01033         {
01034             //pNewNodePath->InkPath.Initialise(pNodePath->InkPath.GetNumCoords(), 64);
01035             //if    (pNewNodePath->InkPath.CopyPathDataFrom(&(pNodePath->InkPath)))
01036             if (CombinePaths(Style, pClipPath, pStrokedClipPath, &(pNodePath->InkPath), &(pNewNodePath->InkPath)))
01037             {
01038                 if (bCombineEffectBitmaps)
01039                     ok = DoInsertNewNode(pNewNodePath);
01040                 else
01041                 {
01042                     if (pSelObjPathListItem==pFirstItem)
01043                     {
01044                         // The first path produced can live where it's originating node lived
01045                         // inside whatever Effect stack was in place
01046                         ok = UndoableOperation::DoInsertNewNode(pNewNodePath, pNodePath, NEXT, TRUE, FALSE, FALSE, TRUE);
01047                     }
01048                     else
01049                     {
01050                         // If more than one path is produced under an Effect node
01051                         // Then we must make a new Effect Stack for it
01052                         //
01053                         // BUT NOTE! I don't think any multiple set of nodes are produced outside
01054                         // a group at the moment - so the effect applciation code found here
01055                         // has never yet been tested!
01056                         Node* pInsertNode = pNodePath;
01057                         ListRange* pStack = EffectsStack::GetEffectsStackFromNode(pNodePath, FALSE, TRUE, TRUE);    // Include locked effects
01058                         if (pStack)
01059                             pInsertNode = pStack->FindLast();
01060                         ok = UndoableOperation::DoInsertNewNode(pNewNodePath, pInsertNode, NEXT, TRUE, FALSE, FALSE, TRUE);
01061                         pInsertNode = pNewNodePath;
01062                         delete pStack;
01063 
01064                         // Now copy the effects stack for this new child...
01065                         if (ok) ok = ApplyEffects(pSelObjPathListItem, pNewNodePath, NULL);
01066 
01067                     }
01068 
01069                     // Select the resulting path now
01070                     if (ok && pCreatedByNode->IsSelected())
01071                         pNewNodePath->Select(FALSE);
01072 
01073                     if (ok)
01074                     {
01075                         pListOfResults->AddNode(pNewNodePath);
01076                         NodeInsertCount++;
01077                     }
01078                 }
01079 
01080                 // Find out who created the path, and apply its attrs to the new NodePath
01081                 CCAttrMap*          pAttrMap        = pSelObjPathListItem->GetAttrMap();
01082                 
01083                 if (ok) ok = ApplyAttributes(pCreatedByNode, pNewNodePath, pAttrMap, pSelObjPathListItem);
01084 
01085                 // Set up a set of default path flags to enable the bezier tool to edit this node
01086                 if (ok) pNewNodePath->InkPath.InitialiseFlags();
01087 
01088                 if (ok) pSelObjPathList->SetProducedPaths(TRUE);
01089             }
01090             else
01091             {
01092                 if (pNewNodePath!=NULL) { delete pNewNodePath; pNewNodePath = NULL; }
01093 
01094                 // Don't worry if the actual combine fails.  We'll just not put anything in the tree
01095                 // because, hmmm, I wonder....
01096             }
01097         }
01098         else
01099         {
01100             // What? Error? Surely not!
01101             ERROR3("pNodePath == NULL || pNewNodePath == NULL || !pNewNodePath->SetUpPath())");
01102             ok = FALSE;
01103 
01104             if (pNewNodePath!=NULL) { delete pNewNodePath; pNewNodePath = NULL; }
01105         }
01106 
01107         if (ok)
01108             ok = ::ContinueSlowJob(++JobCount);
01109 
01110         // Get the next item in the selected object path list.
01111         pSelObjPathListItem = pSelObjPathList->GetNextItem(pSelObjPathListItem);
01112     }
01113 
01114     return (ok);
01115 }

BOOL OpCombineShapes::ConvertClipLineToShape Path pClipPath,
Path pSrcPath,
Path pDestPath
[private]
 

Only call this if pClipPath is a line.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
pClipPath = ptr to the path to convert [INPUTS] pSrcPath = ptr to path that will be combined with pClipPath pDestPath = ptr to path to place the result in
*pDestPath contains the result of converting pCliPath with regards to pSrcPath (if TRUE returned). [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
This creates a shape out of pClipPath such that the path from the start point of pClipPath to the end point of pClipPath does NOT intersect with pSrcPath. It can only do this if the start and end point of pClipPath are outside the coord bounds of pSrcPath. If either pClipPath ends are inside pSrcPath's bounds, FALSE is returned.

The shape is created by tacking on parts of the bounding rectangle to pClipPath, then closing the resultant path. Note: pDestPath must not point to pClipPath OR pSrcPath (i.e. you'll need three paths)

Returns:
Errors: -
See also:
OpCombineShapes::CombinePaths()

Definition at line 1272 of file combshps.cpp.

01273 {
01274     // Check for stupid entry parama
01275     ERROR2IF(pClipPath == NULL,FALSE,"pClipPath == NULL");
01276     ERROR2IF(pSrcPath  == NULL,FALSE,"pSrcPath == NULL");
01277     ERROR2IF(pDestPath == NULL,FALSE,"pClipPath == NULL");
01278 
01279     // Get the coord bounds of the source path.  Inflate it slightly so that we don't run into tolerance
01280     // problems when calling ClipPathToPath()
01281     //DocRect Rect = GetBoundingRect(pSrcPath);
01282 
01283     // we must be consistant as to the rect we use to get a consistant shape
01284     // otherwise some items will all get cut along the line but it is luck
01285     // which side of the cut the shapes appear in (sjk 15/11/00)
01286 //  DocRect Rect = GetApplication()->FindSelection()->GetBoundingRect(); // use a consistant rect for all the shapes
01287     DocRect Rect = OriginalBoundingRect; // use a consistant rect for all the shapes
01288 
01289     Rect.Inflate((DefaultTolerance*2)+10);
01290     
01291     INT32       NumClipCoords = pClipPath->GetNumCoords();
01292     DocCoord*   pClipCoords   = pClipPath->GetCoordArray();
01293     DocCoord    ClipStart     = pClipCoords[0];                 // Start point of pClipPath
01294     DocCoord    ClipEnd       = pClipCoords[NumClipCoords-1];   // End   point of pClipPath
01295     DocCoord    ExtraClipStart= pClipCoords[0];                 // Start point of pClipPath
01296     DocCoord    ExtraClipEnd  = pClipCoords[NumClipCoords-1];   // End   point of pClipPath
01297     BOOL        NeedExtraClipStart  = FALSE;
01298     BOOL        NeedExtraClipEnd    = FALSE;
01299 
01300     Path        FlatPath;
01301 //  BOOL        SetFlatPath = FALSE;
01302 //  double      FLATTNESS =  1024;  // arbitry figure of how flattened the curve will be
01303                                     // only used for test for extra cuts
01304 
01305     // We have to choose a corner point of the bounding rect for both the start and end points
01306     // of pClipPath.
01307 
01308     DocCoord ClipCornerStart,ClipCornerEnd;
01309 
01310     DocCoord mid = Rect.Centre();
01311 
01312     if (ClipStart.x < mid.x)
01313         ClipCornerStart.x = Rect.lo.x;
01314     else
01315         ClipCornerStart.x = Rect.hi.x;
01316 
01317     if (ClipStart.y < mid.y)
01318         ClipCornerStart.y = Rect.lo.y;
01319     else
01320         ClipCornerStart.y = Rect.hi.y;
01321 
01322     if (ClipEnd.x < mid.x)
01323         ClipCornerEnd.x = Rect.lo.x;
01324     else
01325         ClipCornerEnd.x = Rect.hi.x;
01326 
01327     if (ClipEnd.y < mid.y)
01328         ClipCornerEnd.y = Rect.lo.y;
01329     else
01330         ClipCornerEnd.y = Rect.hi.y;
01331 
01332     // start clip point in the rect?
01333     // move it to the nearest edge
01334     if (Rect.ContainsCoord(ClipStart))
01335     {
01336         NeedExtraClipStart = TRUE;
01337         DocCoord v = ClipStart - pClipCoords[1];
01338         INT32 giveup = 250; // number of times to try
01339 
01340         // extrapperlate the last part of the line until it breaks out of the bounding rect
01341         // give up if it fails to break out after an arbitry number of goes
01342         while (Rect.ContainsCoord(ExtraClipStart) && giveup > 0)
01343         {
01344             ExtraClipStart.x += v.x * 10;
01345             ExtraClipStart.y += v.y * 10;
01346             giveup--;
01347         }
01348 
01349         // failled to leave the shape
01350         if (giveup == 0) 
01351             return FALSE;
01352 
01353         // moving the start may change which is the nearest corner to go for
01354         DocCoord mid = Rect.Centre();
01355 
01356         if (ExtraClipStart.x < mid.x)
01357             ClipCornerStart.x = Rect.lo.x;
01358         else
01359             ClipCornerStart.x = Rect.hi.x;
01360 
01361         if (ExtraClipStart.y < mid.y)
01362             ClipCornerStart.y = Rect.lo.y;
01363         else
01364             ClipCornerStart.y = Rect.hi.y;
01365     }
01366 
01367     // perform all the same calcualtions for the other end of the clip line
01368     if (Rect.ContainsCoord(ClipEnd))
01369     {
01370         NeedExtraClipEnd = TRUE;
01371         DocCoord v = ClipEnd - pClipCoords[NumClipCoords-2];
01372         INT32 giveup = 250; // number of times to try
01373 
01374         // extrapperlate the last part of the line until it breaks out of the bounding rect
01375         // give up if it fails to break out after an arbitry number of goes
01376         while (Rect.ContainsCoord(ExtraClipEnd) && giveup > 0)
01377         {
01378             ExtraClipEnd.x += v.x * 10;
01379             ExtraClipEnd.y += v.y * 10;
01380             giveup--;
01381         }
01382 
01383         // failled to leave the shape
01384         if (giveup == 0) 
01385             return FALSE;
01386 
01387         // moving the end may change which is the nearest corner to go for
01388         DocCoord mid = Rect.Centre();
01389 
01390         if (ExtraClipEnd.x < mid.x)
01391             ClipCornerEnd.x = Rect.lo.x;
01392         else
01393             ClipCornerEnd.x = Rect.hi.x;
01394 
01395         if (ExtraClipEnd.y < mid.y)
01396             ClipCornerEnd.y = Rect.lo.y;
01397         else
01398             ClipCornerEnd.y = Rect.hi.y;            
01399     }
01400 
01401     // We now have two sensible corners of Rect to connect pClipPath's end points to
01402     // and if required also optimal points on this Rect boundry to that are
01403     // in direct line with the ends of the cutting line
01404 
01405     // Firstly, copy pClipPath to pDestPath
01406     pDestPath->ClearPath(FALSE);
01407     if (!pDestPath->MergeTwoPaths(*pClipPath)) return FALSE;
01408 
01409     // add on an extra end part to make this end of the clip path outside the bounds of the selection
01410     if (NeedExtraClipEnd)
01411     {
01412         if (!pDestPath->AddLineTo(ExtraClipEnd)) return FALSE;
01413     }
01414 
01415     // Connect the end of the line to the end corner point
01416     if (!pDestPath->AddLineTo(ClipCornerEnd)) return FALSE;
01417 
01418     // add in the corner nearest the end
01419     // If end corner x != start corner x, we need to connect these up
01420     if (ClipCornerEnd.x != ClipCornerStart.x)
01421     {
01422         ClipCornerEnd.x = ClipCornerStart.x;
01423         if (!pDestPath->AddLineTo(ClipCornerEnd)) return FALSE;
01424     }
01425 
01426     // if the start and end corners are opposing we need to use an intermediate corner to avoid the selection
01427     // If end corner y != start corner y, we need to connect these up
01428     if (ClipCornerEnd.y != ClipCornerStart.y)
01429     {
01430         ClipCornerEnd.y = ClipCornerStart.y;
01431         if (!pDestPath->AddLineTo(ClipCornerEnd)) return FALSE;
01432     }
01433 
01434     // add in the corner nearest the start
01435     // add on an extra end part to make this end of the clip path outside the bounds of the selection
01436     if (NeedExtraClipStart)
01437     {
01438         if (!pDestPath->AddLineTo(ExtraClipStart)) return FALSE;
01439     }
01440     
01441     // Finally, close the path up by connecting the start corner point to the start of the line
01442     // and setting PT_CLOSEFIGURE in the last verb
01443     if (!pDestPath->AddLineTo(ClipStart)) return FALSE;
01444 
01445     INT32 n = pDestPath->GetNumCoords();
01446     PathVerb* pVerbs = pDestPath->GetVerbArray();
01447     pVerbs[n-1] |= PT_CLOSEFIGURE;
01448 
01449     return TRUE;
01450 }

BOOL OpCombineShapes::ConvertToShapes Range pRange,
BOOL  bCombineEffectBitmaps
[private]
 

This subtracts all the shapes in all the lists from all the lines in all the lists This subtracts all the shapes in all the lists from pLine This subtracts all the shapes in pSelObjPathList from pLine This generates the data structure that holds all the paths generated by the selected objects. NOTE! Pointers to paths in the tree are stored in the lists (unlike CreateSelObjPathLists).

Author:
Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
Date:
06/04/2005
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
This consists of a top level list than contains a series of secondary lists of paths. There is one instance of a secondary list per selected object.

List Of Selected Object Path lists List of paths made by first selected object SelObjPathListItem SelObjPathListItem SelObjPathListItem ... List of paths made by second selected object ...

Returns:
Errors: -
See also:
CreateSelObjPathList()

Definition at line 1642 of file combshps.cpp.

01643 {
01644     CombineBecomeA BecomePath(BECOMEA_REPLACE, this, NULL, NULL, FALSE);
01645     BecomePath.SetInsertComplexBlendStepsAsPaths (TRUE);        // compound nodes MUST be inserted as paths!
01646     BecomePath.SetResultsStayInPlace(!bCombineEffectBitmaps);   // Only stay in place if notcaptruing effect bitmaps
01647 
01648     Node* pNode = pRange->FindFirst(FALSE);
01649     Node* pNextNode = NULL;
01650     BOOL ok = TRUE;
01651 
01652     while (pNode!=NULL && ok)
01653     {
01654         pNextNode = pRange->FindNext(pNode, FALSE);
01655 
01656         // Make a path list for this selected object if it can become a NodePath
01657         SelObjPathList* pSelObjPathList = new SelObjPathList(pNode);
01658         ok = (pSelObjPathList!=NULL);
01659         if (ok)
01660         {
01661             ListOfSelObjPathLists.AddTail(pSelObjPathList);
01662 
01663             // --- This code ripped from CreateSelObjPathList - looks suspect! ---
01664             // Check to see if this is the Top most visible object. This is used by the
01665             // Cached Compound Nodes to help determine whether or not to use the Cached nodes or BMP!
01666             BOOL IsTopObject = (pNextNode == NULL);
01667             // --- ! ---
01668 
01669             if (pNode->CanBecomeA(&BecomePath))
01670                 ok = CreateSelObjPathList(pNode, pSelObjPathList, IsTopObject, bCombineEffectBitmaps, BECOMEA_REPLACE);
01671 
01672             // Tidy up if things have gone wrong
01673             if (!ok)
01674             {
01675                 ListOfSelObjPathLists.RemoveTail();
01676                 delete pSelObjPathList;
01677             }
01678 
01679             if (pNode->IsAnObject()) ((NodeRenderableInk*)pNode)->ReleaseCached();
01680         }
01681 
01682         pNode = pNextNode;
01683 
01684         if (ok)
01685             ok = ::ContinueSlowJob(++JobCount);
01686     }
01687 
01688     return ok;
01689 }

ListRange * OpCombineShapes::CopyRange Range pRange  )  [private]
 

Copy the specified range of nodes.

Author:
Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
Date:
06/05/2005
Parameters:
pRange = ptr to the range to copy [INPUTS]
- [OUTPUTS]
Returns:
ptr to ListRange if OK, NULL otherwise
See also:
-

Definition at line 2501 of file combshps.cpp.

02502 {
02503     BOOL ok = TRUE;
02504     Node* pNode = pRange->FindFirst();
02505     ListRange* pNewRange = new ListRange();
02506 
02507     while (pNode && ok && pNewRange)
02508     {
02509         Node* pNext = pRange->FindNext(pNode);
02510 
02511         // Make a copy of the current node and all its children
02512         Node* TheCopy;
02513         BOOL CopiedOK; 
02514         CALL_WITH_FAIL(pNode->NodeCopy(&TheCopy), this, CopiedOK);
02515         if (CopiedOK)
02516         {
02517             // make sure that it is bounded
02518             ERROR2IF(!TheCopy->IsBounded(), FALSE, "Object being copied is not a NodeRenderableBounded"); 
02519             NodeRenderableBounded* BoundCopy = (NodeRenderableBounded*)TheCopy;
02520 
02521             // Insert the copied node next to it's originator
02522             ok = UndoableOperation::DoInsertNewNode(BoundCopy, pNode, NEXT,
02523                                                      FALSE,     // Don't Invalidate region 
02524                                                      FALSE,     // Don't Clear the selections
02525                                                      FALSE,     // Don't select new node
02526                                                      FALSE      // Don't normalise attributes
02527                                                      );
02528             if (!ok)
02529             {
02530                 // Failed, so tidy up before returning
02531                 TheCopy->CascadeDelete(); 
02532                 delete TheCopy; 
02533             } 
02534             else
02535             {
02536                 // Record the copied node in the ListRange
02537                 pNewRange->AddNode(TheCopy);
02538 
02539                 // Call PostDuplicate on the copied node and all it's children
02540                 Node* pCurrent = BoundCopy->FindFirstDepthFirst();
02541                 while (pCurrent!=NULL && ok)
02542                 {
02543                     ok = pCurrent->PostDuplicate(this);
02544                     pCurrent = pCurrent->FindNextDepthFirst(BoundCopy);
02545                 }
02546             }
02547         }
02548 
02549         pNode = pNext;
02550     }
02551 
02552     if (!ok)
02553     {
02554         delete pNewRange;
02555         pNewRange = NULL;
02556     }
02557 
02558     return pNewRange;
02559 }

BOOL OpCombineShapes::CreateSelObjPathList Node pNode,
SelObjPathList pSelObjPathList,
BOOL  IsFirstNode = TRUE,
BOOL  bCombineEffectBitmaps = TRUE,
BecomeAReason  reason = BECOMEA_PASSBACK
[private]
 

This generates the data structure that holds all the paths generated by a given selected object.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
pNode = ptr to the node that will generate one or more NodePaths [INPUTS] pSelObjPathList = the list to add the paths to using SelObjPathListItems
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
It uses a derived BecomeA class to receive the paths and generate SelObjPathListItems to add to pSelObjPathList
Returns:
Errors: -
See also:
CreateSelObjPathLists()

Definition at line 1806 of file combshps.cpp.

01807 {
01808     ERROR2IF(pNode == NULL || pSelObjPathList == NULL,FALSE,"One or more params are NULL");
01809 
01810     // Set up a BecomeA derived object, so that we can receive all the paths generated by pNode.
01811     CombineBecomeA ParamBecomeA(reason, this,pSelObjPathList,pNode,IsFirstNode);
01812     ParamBecomeA.SetInsertComplexBlendStepsAsPaths(TRUE);       // compound nodes MUST be inserted as paths!
01813     ParamBecomeA.SetResultsStayInPlace(!bCombineEffectBitmaps);
01814 
01815     // if we have a brush attribute we have to do it independently (ugh)
01816     AttrBrushType* pBrush = NULL;
01817     ((NodeRenderableInk*)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), (NodeAttribute **)&pBrush);
01818     BOOL gotAValidBrush = FALSE;
01819     if (pBrush != NULL && pBrush->GetBrushHandle() != BrushHandle_NoBrush)
01820     {
01821         gotAValidBrush = TRUE;
01822 
01823         BOOL useBrush = FALSE;          // CGS:  decide whether to use the brush for DoBecomeA or pNode ....
01824         
01825         if (Reason == COMBINE_ADD)
01826         {
01827             useBrush = TRUE;
01828         }
01829         /*else                  // put this back in if you want the slicing of lines to be sliced accurately in two.
01830                                 // if you want two new lines with brushes applied, leave like this
01831         {
01832             if (IS_A (pNode, NodePath))
01833             {
01834                 NodePath* pPath = (NodePath*) pNode;
01835                 if (!pPath->InkPath.IsClosed ())
01836                 {
01837                     useBrush = TRUE;                // its a nodepath thats filled
01838                 }
01839             }
01840         }*/
01841 
01842         if (useBrush)
01843         {
01844             return (pBrush->DoBecomeA(&ParamBecomeA, (NodeRenderableInk*)pNode));
01845         }
01846     }
01847 
01848     // if we haven't got a brush then see if we have a stroke
01849     BOOL ok = TRUE;
01850     AttrStrokeType* pStroke = NULL;
01851     ((NodeRenderableInk*)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute**)&pStroke);
01852     AttrVariableWidth* pVarWidth = NULL;
01853     ((NodeRenderableInk*)pNode)->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute**)&pVarWidth);
01854     
01855     if (pStroke && pStroke->HasPathProcessor() && pVarWidth && pVarWidth->HasActiveValueFunction())
01856     {
01857         if (!gotAValidBrush)
01858         {
01859             // Convert stroke to paths first, allow it to place any nodes it creates
01860             // after the original but without deleting the original
01861             if (pStroke)
01862             {
01863                 ParamBecomeA.SetSecondary();                    // Tell the pathprocessor that it doesn't own the node
01864                 ok = pStroke->DoBecomeA(&ParamBecomeA, pNode);
01865                 ParamBecomeA.SetSecondary(FALSE);
01866             }
01867 
01868             // Now convert the node to paths, placing nodes after the original (before stroke paths)
01869             // and deleting the original
01870             if (ok)
01871                 ok = pNode->DoBecomeA(&ParamBecomeA);       // Attributes will be shuffled
01872 
01873             return ok;
01874         }
01875         else
01876         {
01877             // We've got a brush and a stroke on the same node!
01878             // The brush already knows about the stroke, so let that handle it ....
01879             //
01880             // Convert stroke to paths first, allow it to place any nodes it creates
01881             // after the original but without deleting the original
01882             if (pBrush)
01883             {
01884                 ParamBecomeA.SetSecondary();                    // Tell the pathprocessor that it doesn't own the node
01885                 ok = pBrush->DoBecomeA(&ParamBecomeA, (NodeRenderableInk*)pNode);
01886                 ParamBecomeA.SetSecondary(FALSE);
01887             }
01888 
01889             // Now convert the node to paths, placing nodes after the original (before stroke paths)
01890             // and deleting the original
01891             if (ok)
01892                 ok = pNode->DoBecomeA(&ParamBecomeA);       // Attributes will be shuffled
01893 
01894             return ok;
01895         }
01896     }
01897 
01898     // Get pNode to generate its paths for us
01899     return (pNode->DoBecomeA(&ParamBecomeA));
01900 }

BOOL OpCombineShapes::CreateSelObjPathLists BOOL  bCombineEffectBitmaps  )  [private]
 

This generates the data structure that holds all the paths generated by the selected objects.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
This consists of a top level list than contains a series of secondary lists of paths. There is one instance of a secondary list per selected object.

List Of Selected Object Path lists List of paths made by first selected object SelObjPathListItem SelObjPathListItem SelObjPathListItem ... List of paths made by second selected object ...

Returns:
Errors: -
See also:
CreateSelObjPathList()

Definition at line 1718 of file combshps.cpp.

01719 {
01720     ERROR2IF(pSelRange == NULL,FALSE,"pSelRange == NULL");
01721 
01722     Node* pSelNode = pSelRange->FindFirst();
01723     UINT32 Count = 0;   // Used for a check made at end of routine
01724     BOOL ok = TRUE;
01725     BOOL IsTopObject = TRUE;
01726 
01727 	::BeginSlowJob();
01728 
01729     BecomeA BecomePath(BECOMEA_REPLACE, CC_RUNTIME_CLASS(NodePath));
01730     BecomePath.SetResultsStayInPlace(!bCombineEffectBitmaps);
01731 
01732     while (ok && pSelNode != NULL)
01733     {
01734         if (pSelNode->CanBecomeA(&BecomePath))
01735         {
01736             Count++;    // Count num objs that can become a NodePath
01737 
01738             // Make a path list for this selected object if it can become a NodePath
01739             SelObjPathList* pSelObjPathList = new SelObjPathList(pSelNode);
01740 
01741             if (pSelObjPathList != NULL)
01742             {
01743                 // Check to see if this is the Top most visable object. This is used by the
01744                 // Cached Compound Nodes to help determine wether or not to use the Cached nodes or BMP!
01745                 IsTopObject = (pSelRange->FindNext(pSelNode) == NULL);
01746 
01747                 // Get the selected object to create the actual NodePath objects
01748                 // and if successful, stuff the list on our list of sel obj path lists
01749                 if (CreateSelObjPathList(pSelNode, pSelObjPathList, IsTopObject, bCombineEffectBitmaps))
01750                 {
01751                     ListOfSelObjPathLists.AddTail(pSelObjPathList);
01752                 }
01753                 else
01754                 {
01755                     ok = FALSE;
01756                 }
01757             }
01758             else
01759                 ok = FALSE;
01760         }
01761 
01762         if (ok)
01763             ok = ::ContinueSlowJob(++JobCount);
01764 
01765         // Get the next selected object
01766         pSelNode = pSelRange->FindNext(pSelNode);
01767     }
01768 
01769 	::EndSlowJob();
01770 
01771     if (ok)
01772     {
01773         UINT32 MinCount = 2;
01774         if (Reason == COMBINE_ADD)
01775             MinCount = 1;
01776 
01777         if (Count < MinCount)
01778         {
01779             ERROR3_PF(("Less than %d of the selected objects could become paths",MinCount));
01780             return FALSE;
01781         }
01782     }
01783 
01784     return ok;
01785 }

void OpCombineShapes::Do OpDescriptor  )  [virtual]
 

This is it! The routine that gets all the hard work underway.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
-
It acts upon the value in member var 'Reason', performing the shape-combining op specified by that value
Returns:
Errors: -
See also:
-

Reimplemented from Operation.

Definition at line 419 of file combshps.cpp.

00420 {
00421     BeginSlowJob();
00422     DoStartSelOp(TRUE, FALSE);
00423     
00424     BOOL ok = TRUE;
00425     NodeInsertCount = 0;
00426     pListOfResults = new ListRange();
00427     BOOL bCombineEffectBitmaps = CombineEffectBitmaps;
00428 
00429     // If shift key is pressed toggle the effect handling method
00430     ClickModifiers ClickMods = ClickModifiers::GetClickModifiers();
00431     if (ClickMods.Adjust)
00432         bCombineEffectBitmaps = !bCombineEffectBitmaps;
00433 
00434     // ---------------------------------------------------------
00435     // Phil's new version: Scan the selection and call DoBecomeA once only!
00436     //
00437     // Convert all shapes in the selection in-place into paths.
00438     // NOTE! this allows all compound nodes to become groups in place
00439     // (which doesn't happen with the passback method). This is important
00440     // for retaining the tree structure that the paths are found in so that
00441     // paths remain in place inside groups and effects subtrees.
00442     //
00443     pSelRange = GetApplication()->FindSelection();
00444     Range OldRange(*pSelRange);
00445     RangeControl rg = OldRange.GetRangeControlFlags();
00446     rg.PromoteToParent = TRUE;
00447     OldRange.SetRangeControl(rg);
00448 
00449 	::BeginSlowJob();
00450 
00451     switch (Reason)
00452     {
00453         // act upon the reason
00454         case COMBINE_ADD:
00455             ok = ConvertToShapes(&OldRange, bCombineEffectBitmaps);
00456             if (ok) ok = DoAddShapes();
00457             if (ok && NodeInsertCount==0) {ok=FALSE; InformWarning(_R(IDS_COMBINE_NORESULT));}
00458             if (ok) ok = DoHideListedNodes(bCombineEffectBitmaps);
00459 //          if (ok) ok = DoSelectResultNodes(bCombineEffectBitmaps);
00460             break;
00461 
00462         case COMBINE_SUBTRACT:
00463             ok = ConvertToShapes(&OldRange, bCombineEffectBitmaps);
00464             if (ok) ok = DoCombineShapes(CLIP_STYLE_SUBTRACT, bCombineEffectBitmaps);
00465             if (ok && NodeInsertCount==0) {ok=FALSE; InformWarning(_R(IDS_COMBINE_NORESULT));}
00466             if (ok) ok = DoHideListedNodes(bCombineEffectBitmaps);
00467 //          if (ok) ok = DoSelectResultNodes(bCombineEffectBitmaps);
00468             break;
00469 
00470         case COMBINE_INTERSECT:
00471             ok = ConvertToShapes(&OldRange, bCombineEffectBitmaps);
00472             if (ok) ok = DoCombineShapes(CLIP_STYLE_INTERSECT, bCombineEffectBitmaps);
00473             if (ok && NodeInsertCount==0) {ok=FALSE; InformWarning(_R(IDS_COMBINE_NORESULT));}
00474             if (ok) ok = DoHideListedNodes(bCombineEffectBitmaps);
00475 //          if (ok) ok = DoSelectResultNodes(bCombineEffectBitmaps);
00476             break;
00477 
00478         case COMBINE_SLICE:
00479             {
00480                 // Slice = Subtract + Intersect
00481                 // Make a copy of the selection before we do anything else
00482                 Range* pNewRange = CopyRange(&OldRange);
00483                 OriginalBoundingRect = pSelRange->GetBoundingRect(TRUE);        // Use PromoteToParent to get TRUE bounds!
00484 
00485                 // Subtract on the original selection
00486                 ok = ConvertToShapes(&OldRange, bCombineEffectBitmaps);
00487                 if (ok) ok = DoCombineShapes(CLIP_STYLE_SUBTRACT, bCombineEffectBitmaps);
00488                 if (ok) ok = DoHideListedNodes(bCombineEffectBitmaps);
00489 
00490                 // Have to delete lists because they are global! Argh!
00491                 ListOfSelObjPathLists.DeleteAll();
00492                 pListOfResults->Clear();
00493 
00494                 // Intersect on the copy
00495                 if (ok) ok = ConvertToShapes(pNewRange, bCombineEffectBitmaps);
00496                 if (ok) ok = DoCombineShapes(CLIP_STYLE_INTERSECT, bCombineEffectBitmaps);
00497                 if (ok) ok = DoHideListedNodes(bCombineEffectBitmaps);
00498 
00499                 if (ok && NodeInsertCount==0) {ok=FALSE; InformWarning(_R(IDS_COMBINE_SLICE_NORESULT));}
00500 
00501 //              if (ok) ok = DoSelectResultNodes(bCombineEffectBitmaps);
00502 
00503                 delete pNewRange;
00504                 pNewRange = NULL;
00505             }
00506             break;
00507 
00508         default:
00509             ERROR3_PF(("Unknown combine reason : %d",Reason));
00510             ok = FALSE;
00511             break;
00512     }
00513 
00514 	::EndSlowJob();
00515 
00516     if (!ok)
00517     {
00518         FailAndExecute();
00519 
00520         // Get ptr to selected doc
00521         Document* pDoc = Document::GetSelected();
00522         Spread*  pSpread  = Document::GetSelectedSpread();
00523 
00524         // If there's a selected docview, redraw the selection
00525         if (pDoc != NULL && pSelRange != NULL && pSpread != NULL)
00526             pDoc->ForceRedraw(pSpread, pSelRange->GetBlobBoundingRect());
00527     }
00528     else
00529         pSelRange->Update(TRUE);
00530 
00531     // Destroy our lists of selected object paths
00532     ListOfSelObjPathLists.DeleteAll();
00533     if (pListOfResults)
00534     {
00535         delete pListOfResults;
00536         pListOfResults = NULL;
00537     };
00538 
00539     GetApplication()->UpdateSelection();
00540 
00541     // ::EndSlowJob();
00542 
00543     End();
00544 }

BOOL OpCombineShapes::DoAddShapes  )  [private]
 

This adds all the paths together to form a combined silhouette path of the selection.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed
This can be thought of as a logical OR of the paths
Returns:
Errors: -
See also:
OpCombineShapes::Do()

Definition at line 565 of file combshps.cpp.

00566 {
00567     // We'll need a new NodePath to put in the tree
00568     NodePath* pNodePath = new NodePath;
00569 
00570     // If we have trouble setting up the NodePath, return FALSE
00571     if (pNodePath == NULL)       return FALSE;
00572     if (!pNodePath->SetUpPath()) return FALSE;
00573 
00574 //  if (!SubtractAllShapesFromAllLines()) return FALSE;
00575 
00576     // Get the list of paths produced by the first selected object
00577     SelObjPathList* pSelObjPathList = GetFirstList();
00578 
00579     // This algorithm was very inefficient for large numbers of nodes 
00580     // as it adds each node to the output path in turn.  As the output path 
00581     // grows it takes longer and longer to add each successive object.
00582     // To avoid this we add each path to a temporary path and when 
00583     // it grows too large we add the temporary path into the output path 
00584     // and clear the temporary one.
00585 
00586     NodePath* pTempPath = new NodePath;
00587     if (pTempPath == NULL)       return FALSE;
00588     if (!pTempPath->SetUpPath()) return FALSE;
00589 
00590     while (pSelObjPathList != NULL)
00591     {
00592         // Add all the paths in this list to the path inside pNodePath
00593         if (!AddPathsFromList(&(pTempPath->InkPath),pSelObjPathList))
00594             return FALSE;
00595 
00596         if (pTempPath->InkPath.GetNumCoords() > AddPathGranularity)
00597         {
00598             // Combine the TempPath with the output path
00599             if (!AddOrMergePaths(&(pNodePath->InkPath), &(pTempPath->InkPath)))
00600                 return(FALSE);
00601 
00602             // And clear out the TempPath
00603             if (!pTempPath->InkPath.ClearPath(FALSE))
00604                 return(FALSE);
00605         }
00606 
00607         // Get the next list of paths generated by the next selected object
00608         pSelObjPathList = GetNextList(pSelObjPathList);
00609     }
00610 
00611     // If the TempPath has anything in
00612     if (pTempPath->InkPath.GetNumCoords() > 0)
00613     {
00614         // Combine the TempPath with the output path
00615         if (!AddOrMergePaths(&(pNodePath->InkPath), &(pTempPath->InkPath)))
00616             return(FALSE);
00617     }
00618 
00619     delete pTempPath;
00620 
00621     // We have successfully added all the paths together to form the union inside pNodePath
00622 
00623     // Find the last path item in the last list (i.e. the top-most path in the selection)
00624     pSelObjPathList     = GetLastList();
00625     SelObjPathListItem* pSelObjPathListItem = pSelObjPathList->GetLastItem();
00626     NodeRenderableInk*  pCreatedByNode  = pSelObjPathListItem->GetCreatedByNode();
00627 
00628     // Get ptr to last selected object because that is where we're going to put the new node path
00629 //  pContextNode = pSelRange->FindLast();
00630     pContextNode = pSelObjPathListItem->GetNodePath();
00631     if (pContextNode==NULL)
00632         pContextNode = pCreatedByNode;
00633     AttachDir = NEXT;
00634     if (!DoInsertNewNode(pNodePath))
00635         return FALSE;
00636 
00637     // Set up a set of default path flags to enable the bezier tool to edit this node (Naughty gavin!)
00638     pNodePath->InkPath.InitialiseFlags();
00639 
00640     // Select the new node path (and don't redraw - this will be done due to DoInsertNewNode())
00641     pNodePath->Select(FALSE);
00642 
00643     // Apply the attrs of the node that created the last path to the new NodePath
00644     CCAttrMap*          pAttrMap        = pSelObjPathListItem->GetAttrMap();
00645     if (!ApplyAttributes(pCreatedByNode,pNodePath,pAttrMap))
00646         return FALSE;
00647 
00648     return TRUE;
00649 }

BOOL OpCombineShapes::DoCombineShapes ClipStyle  Style,
BOOL  bCombineEffectBitmaps
[private]
 

Firstly, this adds all the paths together in the last list to form the clip path. Then all the other paths in the other lists are combined with the clip path, using the method specified in 'Flags'.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
Style = High level style of clipping [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
OpCombineShapes::Do()

Definition at line 833 of file combshps.cpp.

00834 {
00835     Path ClipPath;
00836     Path StrokedClipPath;
00837     if (!ClipPath.Initialise())         return FALSE;
00838     if (!StrokedClipPath.Initialise())  return FALSE;
00839 
00840     // Get the list of paths produced by the top selected object
00841     SelObjPathList* pLastSelObjPathList = GetLastList();
00842 
00843     // Add all the paths in the last list to form the clip path to combine with
00844     if (!AddPathsFromList(&ClipPath,pLastSelObjPathList))
00845         return FALSE;
00846 
00847     // is the clip path is a line?
00848     if (ClipPath.GetPathType() == PATHTYPE_LINE)
00849     {
00850         if (!StrokePathToPath(&ClipPath,&StrokedClipPath))
00851             return FALSE;
00852     }
00853 
00854     // Get the list of paths produced by the first selected object
00855     SelObjPathList* pSelObjPathList = GetFirstList();
00856 
00857     ERROR2IF(pSelObjPathList == NULL,FALSE,"ptr to First list is NULL");
00858     ERROR2IF(pSelObjPathList->GetSelNode() == NULL,FALSE,"First list has no sel node");
00859 
00860     if (bCombineEffectBitmaps)
00861     {
00862         //-----------------------------------------------
00863         // Effects stacks will not be retained - the topmost effect will turn itself into
00864         // a bitmap
00865         //
00866         // All paths the op will produce will be placed into a group for convenience.
00867         // We don't know how many paths will be produced until after the op has done its stuff, so we
00868         // stick everything in a group, and if only one path is produced, we take it out and hide the group.
00869 
00870         // So, firstly create a group to put the paths in.
00871         pContextNode = new NodeGroup;
00872         if (pContextNode == NULL)
00873             return FALSE;
00874 
00875         // Insert the group into the tree
00876         if (!UndoableOperation::DoInsertNewNode((NodeGroup*)pContextNode,pSelObjPathList->GetSelNode(),NEXT,FALSE,FALSE,FALSE,FALSE))
00877             return FALSE;
00878 
00879         // We want all new paths to be inserted as children of the group
00880         AttachDir = LASTCHILD;
00881 
00882         // Make a note of how many paths have been inserted into the tree so far, so we can tell after the main
00883         // loop how many paths have been produced
00884         INT32 PrevInsertCount = NodeInsertCount;
00885 
00886         // This will point to a node to select, if indeed we want to select the node (see later)
00887         NodeRenderable* pSelectNode = NULL;
00888 
00889         // Main loop.  This produces all the paths and places them into the group
00890         while (pSelObjPathList != NULL && pSelObjPathList != pLastSelObjPathList)
00891         {
00892             // Combine all the paths in this list with the ClipPath
00893             // pContextNode is static! Argh!
00894             if (!CombinePathsFromList(Style,&ClipPath,&StrokedClipPath,pSelObjPathList))
00895                 return FALSE;
00896 
00897             // Get the next list of paths generated by the next selected object
00898             pSelObjPathList = GetNextList(pSelObjPathList);
00899         }
00900 
00901         // We now need to decide what to do based on the number of paths produced.
00902         if (NodeInsertCount == (PrevInsertCount+1))
00903         {
00904             // Only one path was produced, so localise the attrs (just in case), move the path out of the
00905             // group, and hide the unneeded group
00906             if (!DoLocaliseCommonAttributes((NodeGroup*)pContextNode))
00907                 return FALSE;
00908 
00909             Node* pNode = pContextNode->FindFirstChild();
00910             while (pNode != NULL && !pNode->IsAnObject())
00911                 pNode = pNode->FindNext();
00912 
00913             if (pNode != NULL)
00914             {
00915                 if (!DoMoveNode(pNode,pContextNode,NEXT))
00916                     return FALSE;
00917             }
00918 
00919             if (!DoHideNode(pContextNode,TRUE))
00920                 return FALSE;
00921 
00922             pSelectNode = (NodeRenderable*)pNode;
00923             pListOfResults->AddNode(pSelectNode);
00924         }
00925         else if (NodeInsertCount > (PrevInsertCount+1))
00926         {
00927             // More than one path was produced, so keep the group and factor out any common attributes.
00928             if (!DoFactorOutCommonChildAttributes((NodeGroup*)pContextNode))
00929                 return FALSE;
00930 
00931             pSelectNode = (NodeGroup*)pContextNode;
00932             pListOfResults->AddNode(pSelectNode);
00933         }
00934         else
00935         {
00936             // The op produced not paths so just hide the empty group
00937             if (!DoHideNode(pContextNode,TRUE))
00938                 return FALSE;
00939         }
00940 
00941         // Select the new node (and don't redraw - this will be done due to DoInsertNewNode())
00942         //
00943         // When slicing, only select the objects produced on the intersection stage
00944 //      if (pSelectNode != NULL && (Reason != COMBINE_SLICE || Style != CLIP_STYLE_SUBTRACT))
00945 //          pSelectNode->Select(FALSE);
00946     }
00947     else
00948     {
00949         //-----------------------------------------------
00950         // Effects stacks will be retained
00951         // So all shape combination must be done within the effects stack structure
00952         //
00953         // Make a note of how many paths have been inserted into the tree so far, so we can tell after the main
00954         // loop how many paths have been produced
00955 //      INT32 PrevInsertCount = NodeInsertCount;
00956 
00957         // Main loop.  This produces all the paths
00958         while (pSelObjPathList != NULL && pSelObjPathList != pLastSelObjPathList)
00959         {
00960             // Combine all the paths in this list with the ClipPath
00961             // pContextNode is static! Argh!
00962             if (!CombinePathsFromList(Style, &ClipPath, &StrokedClipPath, pSelObjPathList, bCombineEffectBitmaps))
00963                 return FALSE;
00964 
00965             // Factor out any common attributes.
00966             Node* pSelNode = pSelObjPathList->GetSelNode();
00967             if (pSelNode && pSelNode->IsCompoundClass())
00968                 if (!DoFactorOutCommonChildAttributes((NodeCompound*)pSelNode, TRUE))
00969                     return FALSE;
00970 
00971             // Get the next list of paths generated by the next selected object
00972             pSelObjPathList = GetNextList(pSelObjPathList);
00973         }
00974 
00975     }
00976 
00977     // We have successfully combined all the paths
00978     return TRUE;
00979 }

BOOL OpCombineShapes::DoHideListedNodes BOOL  bCombineEffectBitmaps  )  [private]
 

This hides all the nodes that were selected when the op was invoked.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
-

Definition at line 1917 of file combshps.cpp.

01918 {
01919     // Get the list of paths produced by the first selected object
01920     SelObjPathList* pSelObjPathList = GetFirstList();
01921     BOOL ok = TRUE;
01922 
01923     while (pSelObjPathList != NULL)
01924     {
01925         if (bCombineEffectBitmaps)
01926         {
01927             Node* pSelNode = pSelObjPathList->GetSelNode();
01928             ERROR3IF(pSelNode == NULL,"pSelNode == NULL");
01929             if (pSelNode == NULL) return FALSE;
01930 
01931             if (pSelNode->IsAnObject())
01932             {
01933                 // Invalidate the region
01934                 if (!DoInvalidateNodeRegion((NodeRenderableInk*)pSelNode,TRUE,FALSE))
01935                     return FALSE;
01936 
01937                 if (pSelObjPathList->HasProducedPaths())
01938                 {
01939                     // Hide the node that generated the list
01940 
01941                         if (!DoHideNode(pSelNode,TRUE)) return FALSE;
01942                 }
01943                 else
01944                     pSelNode->SetSelected(FALSE);       // If it didn't contribute, just deselect it
01945             }
01946         }
01947         else
01948         {
01949             // Hide the individual path-producing nodes, leaving the
01950             // tree structure around them intact
01951             //
01952             // Get the first path in this list
01953             SelObjPathListItem* pFirstItem = pSelObjPathList->GetFirstItem();
01954             SelObjPathListItem* pListItem = pFirstItem;
01955 
01956             while (ok && pListItem != NULL)
01957             {
01958                 // Always hide the creator node
01959                 NodeRenderableInk* pCreator = pListItem->GetCreatedByNode();
01960                 Node* pParent = pCreator->FindParent();
01961                 if (pParent)
01962                 {
01963                     if (ok) ok = DoInvalidateNodeRegion(pCreator, TRUE, FALSE);
01964                     if (ok) ok = DoHideNode(pCreator, TRUE);
01965                 }
01966 
01967                 // Hide the created path node if its in the tree
01968                 NodePath* pPathNode = pListItem->GetNodePath();
01969                 if (pPathNode->FindParent())
01970                 {
01971                     if (pParent==NULL)
01972                         pParent = pPathNode->FindParent();
01973                     if (ok) ok = DoInvalidateNodeRegion(pPathNode, TRUE, FALSE);
01974                     if (ok) ok = DoHideNode(pPathNode, TRUE);
01975                 }
01976 
01977                 // If this hide has resulted in our parent becoming empty
01978                 // Then hide that too...
01979                 while (pParent && pParent->IsCompoundClass() && !((NodeCompound*)pParent)->HasVisibleChildren())
01980                 {
01981                     Node* pNextParent = pParent->FindParent();
01982 
01983                     if (ok) ok = DoInvalidateNodeRegion((NodeCompound*)pParent, TRUE, FALSE);
01984                     if (ok) ok = DoHideNode((NodeCompound*)pParent, TRUE);
01985 
01986                     pParent = pNextParent;
01987                 }
01988 
01989                 // Get the next item in the selected object path list.
01990                 pListItem = pSelObjPathList->GetNextItem(pListItem);
01991             }
01992         }
01993 
01994         // Get the next list of paths generated by the next selected object
01995         pSelObjPathList = GetNextList(pSelObjPathList);
01996     }
01997 
01998     // it all went well
01999     return ok;
02000 }

BOOL OpCombineShapes::DoInsertNewNode NodeRenderableBounded pNewNode  )  [private]
 

High-level interface to UndoableOperation::DoInsertNode().

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
30/1/95
Parameters:
pNewNode = ptr to the node to insert [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if it goes bang
Every time this call is successful, it incs a counter so that the op can tell how many new nodes have been placed in the tree as a result of the shape combining operation.

Uses member vars pContextNode & AttachDir

If no new nodes are created, the op can then fail, rather than just giving the effect of deleting the selection

See also:
-

Definition at line 2469 of file combshps.cpp.

02470 {
02471     ERROR2IF(pNewNode == NULL,FALSE,"Given NULL node ptr");
02472     ERROR2IF(pContextNode == NULL,FALSE,"Context node is NULL");
02473 
02474     if (UndoableOperation::DoInsertNewNode(pNewNode,pContextNode,AttachDir,TRUE,FALSE,FALSE,TRUE))
02475     {
02476         NodeInsertCount++;
02477         return TRUE;
02478     }
02479     else
02480         return FALSE;
02481 }

BOOL OpCombineShapes::DoSelectResultNodes BOOL  bCombineEffectBitmaps  )  [private]
 

This selects all the nodes in the results list.

Author:
Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
Date:
07/04/2005
Parameters:
- [INPUTS]
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if failed

Errors: -

See also:
-

Definition at line 2020 of file combshps.cpp.

02021 {
02022     // Get the list of paths produced by the first selected object
02023     BOOL ok = TRUE;
02024 
02025     NodeRenderableInk::DeselectAll(FALSE, FALSE);
02026 
02027     Node* pNode = pListOfResults->FindFirst();
02028     while (pNode)
02029     {
02030         if (pNode->IsNodeRenderableClass())
02031             ((NodeRenderable*)pNode)->Select(FALSE);
02032 
02033         pNode = pListOfResults->FindNext(pNode);
02034     }
02035 
02036     // it all went well
02037     return ok;
02038 }

DocRect OpCombineShapes::GetBoundingRect Path pPath  )  [private]
 

This gets the exact pure path bounds, using GDraw_CalcStrokeBBox.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
17/2/95
Parameters:
pPath = ptr to path [INPUTS]
- [OUTPUTS]
Returns:
The stroked bounding rect of this path, disregarding attributes

Errors: -

See also:
OpCombineShapes::DoCombineShapes()

Definition at line 1468 of file combshps.cpp.

01469 {
01470     DocRect Rect;
01471 
01472     if (pPath != NULL)
01473     {
01474         #if REAL_GDRAW
01475         // Get GDraw to get the stroked bounds for this path
01476         GDrawContext *GD = GRenderRegion::GetStaticDrawContext();
01477         
01478         if (GD != NULL)
01479         {
01480             GD->CalcStrokeBBox((POINT*)pPath->GetCoordArray(),pPath->GetVerbArray(),pPath->GetNumCoords(),
01481                                 (tagRECT*)(&Rect),pPath->IsFilled, 0, CAPS_ROUND, JOIN_ROUND, NULL);
01482         }
01483         #else
01484         Rect = pPath->GetBoundingRect();
01485         #endif
01486     }
01487 
01488     if (!Rect.IsValid())
01489         Rect.MakeEmpty();
01490 
01491     return Rect;
01492 }

SelObjPathList* OpCombineShapes::GetFirstList  )  [inline, private]
 

Definition at line 287 of file combshps.h.

SelObjPathList* OpCombineShapes::GetLastList  )  [inline, private]
 

Definition at line 288 of file combshps.h.

SelObjPathList* OpCombineShapes::GetNextList SelObjPathList pItem  )  [inline, private]
 

Definition at line 289 of file combshps.h.

00289 { return (SelObjPathList*) ListOfSelObjPathLists.GetNext(pItem); }

void OpCombineShapes::GetOpName String_256 OpName  )  [virtual]
 

The GetOpName fn is overridden so that we return back a description appropriate to the type of attribute that the operation applies.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
16/1/95
Parameters:
OpName = ptr to str to place op name in [INPUTS]
The undo string for the operation [OUTPUTS]
Returns:

Errors: -

See also:
-

Reimplemented from Operation.

Definition at line 348 of file combshps.cpp.

00349 { 
00350     UINT32 IDS = 0;
00351     switch (Reason)
00352     {
00353         case COMBINE_ADD:       IDS = _R(IDBBL_COMBINE_ADD);        break;
00354         case COMBINE_SUBTRACT:  IDS = _R(IDBBL_COMBINE_SUBTRACT);   break;
00355         case COMBINE_INTERSECT: IDS = _R(IDBBL_COMBINE_INTERSECT);  break;
00356         case COMBINE_SLICE:     IDS = _R(IDBBL_COMBINE_SLICE);      break;
00357 
00358         default:
00359             ERROR3_PF(("Unknown combine reason : %d",Reason));
00360             break;
00361     }
00362 
00363     if (IDS > 0)
00364         *OpName = String_256(IDS);
00365 }  

OpState OpCombineShapes::GetState String_256 pStr,
OpDescriptor pOpDesc
[static]
 

Gives the op an oppertunity to say whether the op is executable or not.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
12/1/95
Parameters:
pStr = string to place messages in [INPUTS]
- [OUTPUTS]
Returns:
The op state, i.e. the ticked and greyed state of the op
A combine op is only available if there are more than one selected nodes that are or can become paths.
Returns:
Errors: -
See also:
-

Definition at line 284 of file combshps.cpp.

00285 {
00286     OpState OpSt;
00287     UINT32 Count = 0;
00288     UINT32 MinCount = 2;
00289 
00290     String Str(OPTOKEN_ADDSHAPES);
00291     if (pOpDesc->Token == Str)
00292         MinCount = 1;
00293 
00294     // Get the selected range
00295     SelRange* pSelRange = GetApplication()->FindSelection();
00296     BOOL bRestorePromote = pSelRange->SetPromoteToParent(TRUE);
00297 
00298     BecomeA BecomePath(BECOMEA_REPLACE, CC_RUNTIME_CLASS(NodePath));
00299     BecomePath.SetInsertComplexBlendStepsAsPaths (TRUE);        // compound nodes MUST be inserted as paths!
00300 
00301     // Count the number of selected nodes that are or can become NodePaths
00302     if (pSelRange != NULL)
00303     {
00304         Node* pNode = pSelRange->FindFirst();
00305 
00306         while (pNode != NULL)
00307         {
00308             if (pNode->CanBecomeA(&BecomePath))
00309                 Count++;
00310 
00311             pNode = pSelRange->FindNext(pNode);
00312         }
00313     }
00314 
00315     pSelRange->SetPromoteToParent(bRestorePromote);
00316 
00317     // if at least 'MinCount' objects of the selection can be combined, ungrey the op
00318     if (Count >= MinCount)
00319         OpSt.Greyed = FALSE;
00320     else
00321     {
00322         // The op can't be done, so put an explanation in the string provided
00323         OpSt.Greyed = TRUE;
00324 
00325         String_256 Str(_R(IDS_COMBINE_SEL_ERROR));
00326         *pStr = Str;
00327     }
00328 
00329     return OpSt;
00330 }

BOOL OpCombineShapes::Init void   )  [static]
 

Declares a preference that allows you to clear memory in delete().

Author:
Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
Date:
19/4/94
Returns:
TRUE if it worked OK, FALSE if not

Reimplemented from SimpleCCObject.

Definition at line 247 of file combshps.cpp.

00248 {
00249     REG_COMBINE_OP(ADD,         Add);
00250     REG_COMBINE_OP(SUBTRACT,    Subtract);
00251     REG_COMBINE_OP(INTERSECT,   Intersect);
00252     REG_COMBINE_OP(SLICE,       Slice);
00253 
00254     if (Camelot.DeclareSection(_T("CombineShapes"),5))
00255     {
00256         Camelot.DeclarePref(_T("CombineShapes"), _T("DefaultTolerance"),    &DefaultTolerance,  1,1000);
00257         Camelot.DeclarePref(_T("CombineShapes"), _T("DefaultFlatness"),     &DefaultFlatness,   10,2000);
00258         Camelot.DeclarePref(_T("CombineShapes"), _T("DefaultStrokeWidth"),  &DefaultStrokeWidth,10,72000);
00259         Camelot.DeclarePref(_T("CombineShapes"), _T("AddPathGranularity"),  &AddPathGranularity,1,1024*1024);
00260         Camelot.DeclarePref(_T("CombineShapes"), _T("CombineEffectsBitmaps"), &CombineEffectBitmaps, FALSE, TRUE);
00261     }
00262 
00263     return TRUE;
00264 }

BOOL OpCombineShapes::StrokePathToPath Path pPath,
Path pDest
[private]
 

This picks out a subpath from Src and places it into pDest Strokes pPath, and places result in pDest.

Author:
Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
Date:
26/1/95
Parameters:
pPath = ptr to path [INPUTS] pDest = ptr to path to place result in
- [OUTPUTS]
Returns:
TRUE if OK, FALSE if it goes bang
See also:
-

Definition at line 2397 of file combshps.cpp.

02398 {
02399     ERROR2IF(pPath == NULL,FALSE,"pPath == NULL");
02400     ERROR2IF(pDest == NULL,FALSE,"pDest == NULL");
02401 
02402     return pPath->StrokePathToPath(DefaultStrokeWidth,LineCapButt,MitreJoin,NULL,pDest,200);
02403 
02404  /*
02405     INT32 NumCoords = pPath->GetNumCoords();
02406     INT32 OutputSize = (NumCoords*40)+1000;
02407     Path OutputPath;
02408     if (!OutputPath.Initialise(OutputSize)) return FALSE;
02409 
02410     DocCoord*   IPoints =           pPath->GetCoordArray();
02411     BYTE*       ITypes  = (BYTE*)   pPath->GetVerbArray();
02412     DWORD       ILength = (DWORD)   NumCoords;
02413 
02414     DocCoord*   OPoints =           OutputPath.GetCoordArray();
02415     BYTE*       OTypes  = (BYTE*)   OutputPath.GetVerbArray();
02416     DWORD       OLength = (DWORD)   OutputSize;
02417 
02418     DashType Dash;
02419     Dash.Length = 0;
02420 
02421     INT32 len = GRenderRegion::StrokePathToPath(IPoints,ITypes,ILength,
02422                                     OPoints,OTypes,OLength,
02423                                     FALSE,(TOLERANCE*5)/2,200,LineCapButt,MitreJoin,&Dash);
02424 
02425     if (!OutputPath.MergeTwoPaths(OutputPath.GetCoordArray(),OutputPath.GetVerbArray(),OutputPath.GetFlagArray(),
02426                                 len,TRUE)) return FALSE;
02427 
02428     if (len > 0)
02429     {
02430         pDest->ClearPath(FALSE);
02431         if (!pDest->MakeSpaceInPath(len)) return FALSE;
02432         if (!pDest->MergeTwoPaths(  OutputPath.GetCoordArray(),OutputPath.GetVerbArray(),OutputPath.GetFlagArray(),
02433                                     len,TRUE)) return FALSE;
02434 
02435         pDest->InitialiseFlags();
02436         RemoveBogusCloseFigures(pDest);
02437         INT32 n = pDest->GetNumCoords();
02438         PathVerb* pVerbs = pDest->GetVerbArray();
02439         pVerbs[n-1] |= PT_CLOSEFIGURE;
02440     }
02441 
02442     return TRUE;
02443 */
02444 }


Member Data Documentation

AttachNodeDirection OpCombineShapes::AttachDir [private]
 

Definition at line 302 of file combshps.h.

INT32 OpCombineShapes::JobCount [private]
 

Definition at line 298 of file combshps.h.

List OpCombineShapes::ListOfSelObjPathLists [private]
 

Definition at line 293 of file combshps.h.

INT32 OpCombineShapes::NodeInsertCount [private]
 

Definition at line 299 of file combshps.h.

INT32 OpCombineShapes::NumPaths [private]
 

Definition at line 297 of file combshps.h.

DocRect OpCombineShapes::OriginalBoundingRect [private]
 

Definition at line 295 of file combshps.h.

Node* OpCombineShapes::pContextNode [private]
 

Definition at line 301 of file combshps.h.

ListRange* OpCombineShapes::pListOfResults [private]
 

Definition at line 294 of file combshps.h.

SelRange* OpCombineShapes::pSelRange [private]
 

Definition at line 296 of file combshps.h.

CombineReason OpCombineShapes::Reason [protected]
 

Definition at line 247 of file combshps.h.


The documentation for this class was generated from the following files:
Generated on Sat Nov 10 03:57:46 2007 for Camelot by  doxygen 1.4.4