// $Id: moldedit.cpp 1282 2006-06-09 09:46:49Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // This file contains a library of mould functions defined in the mould lib class. // This class can be derived from by other operations outside the mould tool to // provide mould functionality. /* */ #include "camtypes.h" //#include "resource.h" #include "moldedit.h" //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "nodemold.h" #include "nodemldr.h" #include "ndmldgrp.h" #include "ndmldpth.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "moldpers.h" #include "pathedit.h" //#include "mike.h" #include "helpuser.h" #include "progress.h" #include "keypress.h" #include "objchge.h" #include "ophist.h" class Node; DECLARE_SOURCE("$Revision: 1282 $"); CC_IMPLEMENT_DYNCREATE( OpMouldLibSel,SelOperation ) CC_IMPLEMENT_DYNCREATE( OpMouldLib,UndoableOperation ) CC_IMPLEMENT_DYNCREATE( OpDragOrigin, SelOperation ) CC_IMPLEMENT_DYNCREATE( InvalidateRectAction, Action ) CC_IMPLEMENT_DYNCREATE( RecordGeometryAction, Action ) CC_IMPLEMENT_DYNCREATE( SaveDetachAction, Action ) CC_IMPLEMENT_DYNCREATE( RedrawBoundsAction, Action ) CC_IMPLEMENT_DYNCREATE( BuildMouldAction, Action ) CC_IMPLEMENT_DYNCREATE( StartRebuildMouldAction, Action ) CC_IMPLEMENT_DYNCREATE( EndRebuildMouldAction, Action ) #define new CAM_DEBUG_NEW /******************************************************************************************** > OpMouldLibSel::OpMouldLibSel() Author: Mike_Kenny (Xara Group Ltd) Created: 18/01/95 Purpose: OpMouldLibSel() constructor SeeAlso: - ********************************************************************************************/ OpMouldLibSel::OpMouldLibSel() { // Dummy constructor } /******************************************************************************************** > BOOL OpMouldLibSel::CreateNewMould(Path* pShape, MouldSpace m_space, BOOL FitSelection, BOOL LockAspect) Author: Mike_Kenny (Xara Group Ltd) Created: 11/12/94 Inputs: pShape = A path to use as a mould shape mSpace = The mould space to create, currently only envelope and perspective spaces are defined ScaleToFit = If TRUE then the shape will be scaled to fit over the bounds of the objects. LockAspect = If TRUE then the paths current dimensions are scaled so that the minor dimension fits over pBounds Outputs: - Returns: TRUE if CreateNewMould has successfully created the mould object and children FALSE if failed. Purpose: Creates a complete mould object from the selected objects. Certain rules are used for creating moulds when moulds are already selected. If the selection contains mould objects only, then each mould will have its geometry changed to that of m_space. If however there are a selection of unlike objects, they will all be placed in a new mould whose geometry is specified in this function call. **********************************************************************************************/ BOOL OpMouldLibSel::CreateNewMould(Path* pShape, MouldSpace m_space, BOOL FitSelection, BOOL LockAspect) { ERROR3IF(pShape==NULL,"CreateNewMould called with a NULL shape pointer"); // Obtain the current selections SelRange* Selected = GetApplication()->FindSelection(); if (!Selected) return FALSE; // Trash the old selection bounds Range Rnge = (*Selected); if (!DoInvalidateNodesRegions(Rnge,FALSE,FALSE)) return FALSE; // Remember the selection before the operation if (!DoStartSelOp(FALSE,TRUE)) return FALSE; // Check that we are actually allowed to do this operation ObjChangeFlags cFlags; cFlags.MultiReplaceNode=TRUE; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); // Mark nodes that will allow this to happen, and error if no nodes will let it happen if (!Selected->AllowOp(&ObjChange)) { ERROR3("AllowOp() returned FALSE, i.e. op should have been greyed out"); return FALSE; } // using this list create or replace the mould bits BOOL ok = DoCreateOrReplaceMould(Selected, pShape, m_space, FitSelection, LockAspect); if (!ok) return FALSE; // Update all the changed nodes, i.e. tell all the parents of the children that have been effected ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this); return UpdateChangedNodes(&ObjChange); } /******************************************************************************************** > BOOL OpMouldLibSel::DoCreateOrReplaceMould( SelRange* pSelected, Path* pShape, MouldSpace m_space, BOOL FitSelection, BOOL LockAspect) Author: Mike_Kenny (Xara Group Ltd) Created: 16/03/95 Inputs: pShape = A path to use as a mould shape mSpace = The mould space to create, currently only envelope and perspective spaces are defined ScaleToFit = If TRUE then the shape will be scaled to fit over the bounds of the objects. LockAspect = If TRUE then the paths current dimensions are scaled so that the minor dimension fits over pBounds Returns: TRUE if the job has been done FALSE if not Purpose: ********************************************************************************************/ BOOL OpMouldLibSel::DoCreateOrReplaceMould( SelRange* pSelected, Path* pShape, MouldSpace m_space, BOOL FitSelection, BOOL LockAspect) { BOOL ok; // Create a list of all the selected nodes List* pNodeList = pSelected->MakeListOfNodes(); if (pNodeList==NULL) return FALSE; // Get the bounds of the selection DocRect Bounds = pSelected->GetBoundingRect(); if (AllMouldObjects(pNodeList)) { // Ohh arr, we need to replace all selected mould shapes, here goes!! ok = DoReplaceAllMoulds(pNodeList, pShape, m_space, FitSelection, LockAspect); } else { // call the mould builder NodeMould* pMould = DoCreateMould(pShape, m_space, pNodeList, &Bounds, FitSelection, LockAspect); ok = (pMould!=NULL); if (ok) { // Recalculate the mould's bounding box pMould->InvalidateBoundingRect(); GetApplication()->UpdateSelection(); ok = DoInvalidateNodeRegion(pMould, TRUE); } } // delete all elements in our compiled list while (!pNodeList->IsEmpty()) delete (NodeListItem*)(pNodeList->RemoveHead()); delete pNodeList; return ok; } /******************************************************************************************** > BOOL OpMouldLibSel::AllMouldObjects(List* pNodeList) Author: Mike_Kenny (Xara Group Ltd) Created: 16/03/95 Inputs: pNodeList = a list of object pointers to objects to mould. Returns: TRUE if the list contains mould objects only, at the top level. FALSE if there are other object types. Purpose: Scans the list of nodes provided. If any objects which are not node moulds are found, false is returned ********************************************************************************************/ BOOL OpMouldLibSel::AllMouldObjects(List* pNodeList) { NodeListItem* CurItem = (NodeListItem*)(pNodeList->GetHead()); if (!CurItem) return FALSE; while (CurItem) { if (CurItem->pNode->GetRuntimeClass() != CC_RUNTIME_CLASS(NodeMould)) return FALSE; CurItem = (NodeListItem*)(pNodeList->GetNext(CurItem)); } return TRUE; } /******************************************************************************************** > NodeMould* OpMouldLibSel::DoCreateMould(Path* pShape, MouldSpace mSpace, List* NodeList, DocRect* pBounds, BOOL ScaleToFit, BOOL LockAspect) Author: Mike_Kenny (Xara Group Ltd) Created: 19/12/94 Inputs: pShape = A path to use as a mould shape mSpace = The mould space to create, currently only envelope and perspective spaces are defined NodeList = A list of tree object nodes to place in the Mould pBounds = The bounding box of the objects in the node list ScaleToFit = If TRUE then the shape will be scaled to fit over the bounds of the objects. LockAspect = If TRUE then the paths current dimensions are scaled so that the minor dimension fits over pBounds Returns: TRUE if DoCreateMould has successfully created the mould object and children FALSE if failed. Purpose: Creates a complete mould object positioned on the next pointer of the last node in the list. All nodes in the list will move inside this new mould object. ********************************************************************************************/ NodeMould* OpMouldLibSel::DoCreateMould(Path* pShape, MouldSpace mSpace, List* NodeList, DocRect* pBounds, BOOL ScaleToFit, BOOL LockAspect) { ERROR2IF(pShape==NULL, NULL, "OpMouldLibSel::DoCreateMould() called with a NULL shape pointer"); ERROR2IF(mSpace==MOULDSPACE_UNDEFINED, NULL, "pMouldLib::DoCreateMould() called with an undefined space type"); ERROR2IF(NodeList==NULL, NULL, "OpMouldLibSel::DoCreateMould() called with a NULL list pointer"); // initialise various locals BOOL ok; NodeListItem* CurItem = (NodeListItem*)(NodeList->GetTail()); if (!CurItem) return NULL; Node* Context = CurItem->pNode; if (!Context) return NULL; // create a mould parent and insert it in the tree NodeMould* pMouldParent; ALLOC_WITH_FAIL( pMouldParent, new NodeMould, this); if (!pMouldParent) return NULL; // give the parent mould object a shape and mould space to work with and stick it // in the tree CALL_WITH_FAIL(pMouldParent->CreateGeometry(mSpace), this, ok); if (ok) CALL_WITH_FAIL(DoInsertNewNode(pMouldParent, Context, NEXT, FALSE, TRUE, TRUE, FALSE), this, ok); if (!ok) { delete pMouldParent; return NULL; } // check the shape we've been given is fine and lovely UINT32 errID; if (!pMouldParent->GetGeometry()->Validate(pShape, errID)) { SetNextMsgHelpContext(errID); ERROR1(NULL, errID); } // build a destination scale rectangle DocRect DRect, *pRect; pRect = FindScaleRect(pBounds, &DRect, ScaleToFit, LockAspect); // Create and add a mould shape object NodeMouldPath* pNodeMPath = pMouldParent->AddNewMouldShape(pShape,pRect,this); if (!pNodeMPath) return NULL; // set the geometry using this new mould shape ok = pMouldParent->GetGeometry()->Define(&pNodeMPath->InkPath, pBounds); if (!ok) return NULL; // now create the mould group object NodeMouldGroup* pMouldGroup = pMouldParent->AddNewMouldGroup(this); if (!pMouldGroup) return NULL; // and fill it with our object list if (!pMouldParent->FillMouldGroup(pMouldGroup, NodeList, this)) return NULL; // Now build the moulder object and its list structure NodeMoulder* pMoulder = pMouldParent->AddNewMoulder(pMouldParent, LASTCHILD, this); if (!pMoulder) return NULL; // create all the moulded objects inside the moulder if (!pMoulder->CreateMouldedObjects(pMouldGroup, pMouldParent->GetGeometry(), this)) return NULL; return pMouldParent; } /******************************************************************************************** DocRect* OpMouldLibSel::FindScaleRect( DocRect* pSRect, DocRect* pDRect, BOOL ScaleToFit, BOOL KeepSquare) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pSRect = a pointer to a source rectangle pDRect = a pointer to a destination rectangle ScaleToFit = If TRUE then pDRect will contain pSRect KeepSquare = If TRUE then pDRect is converted to have equal width and height using its max dimension Returns: a pointer to pDRect or null if pDRect has not been affected Purpose: Internal scaling rectangle calculator. Using the flags scaletofit, lock a destination rectangle is build from the souce. If both flags are false a null pointer will be returned else a pointer to pDRect. ********************************************************************************************/ DocRect* OpMouldLibSel::FindScaleRect(DocRect* pSRect, DocRect* pDRect, BOOL ScaleToFit, BOOL KeepSquare) { if (ScaleToFit) { (*pDRect)=(*pSRect); // form a square rectangle if lock aspect is on. if (KeepSquare) { INT32 wid = pDRect->Width(); INT32 hgt = pDRect->Height(); INT32 max = (hgt>wid) ? hgt : wid; INT32 cx = (pDRect->lo.x + pDRect->hi.x)/2; INT32 cy = (pDRect->lo.y + pDRect->hi.y)/2; pDRect->lo.x = cx-max/2; pDRect->lo.y = cy-max/2; pDRect->hi.x = cx+max/2; pDRect->hi.y = cy+max/2; } return pDRect; } return NULL; } /******************************************************************************************** > static BOOL OpMouldLibSel::IsAMouldSelected(NodeMould** pNodeMould) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pNodeMould, a return pointer (can be null) Outputs: pNodeMould points to the selected mould object if one exists Returns: TRUE if there is a selected mould object FALSE if not Purpose: Scans the selection hunting for a selected mould object. It will return a pointer to the object if found and if the return pointer is none null. Hopefully this will be replaced at some point by Selection->FindObject(type) ********************************************************************************************/ BOOL OpMouldLibSel::IsAMouldSelected(NodeMould** pNodeMould) { BOOL Sel = FALSE; Node* pNode =NULL; if (Document::GetCurrent() != NULL) { SelRange* Selected = GetApplication()->FindSelection(); pNode = Selected->FindFirst(); while ((pNode!=NULL) && (!Sel)) { if (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodeMould)) Sel=TRUE; else pNode = Selected->FindNext(pNode); } } if (Sel && pNodeMould) *pNodeMould = (NodeMould*)pNode; return Sel; } /******************************************************************************************** > BOOL OpMouldLibSel::DoRemoveAllMould(List* pNodeList) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pNodeList = pointer to a list of selected nodes to process Returns: TRUE if successfully removed all the mould objects FALSE if failed. Purpose: Removes the mould from around an object. To do this it ********************************************************************************************/ BOOL OpMouldLibSel::DoRemoveAllMoulds(List* pNodeList) { BOOL ok, constrain, putonclip; INT32 count = 0; NodeListItem* CurItem = (NodeListItem*)(pNodeList->GetHead()); if (!CurItem) return FALSE; constrain=KeyPress::IsConstrainPressed(); while (CurItem) { if (CurItem->pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodeMould)) { NodeMould* pMould = (NodeMould*)(CurItem->pNode); count++; putonclip = (constrain && (count==1)); CALL_WITH_FAIL(DoRemoveMould(pMould, putonclip),this,ok); if (!ok) return FALSE; } CurItem = (NodeListItem*)(pNodeList->GetNext(CurItem)); } return TRUE; } /******************************************************************************************** > BOOL OpMouldLibSel::DoRemoveMould(NodeMould* pMould, BOOL PutOnClip) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pMould = pointer to a mould object Returns: TRUE if successfully removed the mould object FALSE if failed. Purpose: Removes the mould from around an object. To do this it ********************************************************************************************/ BOOL OpMouldLibSel::DoRemoveMould(NodeMould* pMould, BOOL PutOnClip) { ERROR2IF(pMould==NULL, FALSE, "OpMouldBuild::DoCreateMould() called with a NULL shape pointer"); // Ask the mould if it's OK to do the op. // If not, pretend that everything went OK, because we wouldn't have got called unless at least // one selected mould could be removed. This is ensured by OpRemoveMould::GetState ObjChangeFlags cFlags; cFlags.MultiReplaceNode = TRUE; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,pMould,this); if (!pMould->AllowOp(&ObjChange)) return TRUE; // Make sure each child contains the correct attributes. //if (!DoLocaliseCommonAttributes(pMould)) // return FALSE; if (PutOnClip) { NodeMouldPath* pMouldPath = pMould->GetPathShape(); if (pMouldPath!=NULL) { // RangeControl Cntrl = Selected + Unselected + don't cross layers Range CopyRange(pMouldPath,pMouldPath,RangeControl(TRUE,TRUE,FALSE)); if (!DoCopyNodesToClipboard(CopyRange)) return FALSE; } } NodeMouldGroup* pNodeMouldGroup = pMould->FindMouldGroup(); if (!pNodeMouldGroup) return FALSE; // now make sure the nodemouldgroups kids have localised attributes if (!DoLocaliseCommonAttributes(pNodeMouldGroup)) return FALSE; // Now localise all those unsafe attributes at the top. if (!LocaliseMouldAttributes(pMould)) return FALSE; if (!HideAllMouldAttrs(pMould)) return FALSE; Spread* pSpread = pMould->FindParentSpread(); Node* pNode = pNodeMouldGroup->FindLastChild(); while (pNode!=NULL) { Node* pPrevNode = pNode->FindPrevious(); if (pNode->IS_KIND_OF(NodeRenderableInk)) { if (!DoSelectNode((NodeRenderableInk*)pNode,pSpread)) return FALSE; if (!DoInvalidateNodeRegion((NodeRenderableInk*)pNode,TRUE)) return FALSE; } if (pNode->IsAnObject()) { if (!DoMoveNode(pNode, pMould, NEXT)) return FALSE; } pNode = pPrevNode; } if (!DoDeselectNode(pMould, pSpread)) return FALSE; if (!DoInvalidateNodeRegion(pMould, TRUE)) return FALSE; // Hide the mould node if (!DoHideNode(pMould, FALSE, NULL)) return FALSE; // inform the selection something has changed? GetApplication()->UpdateSelection(); return TRUE; } /******************************************************************************************** BOOL OpMouldLibSel::LocaliseMouldAttributes(NodeMould* pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pMould = pointer to a mould object Returns: TRUE if successfully localised the mould attributes FALSE if failed. Purpose: ********************************************************************************************/ BOOL OpMouldLibSel::LocaliseMouldAttributes(NodeMould* pMould) { ERROR2IF(pMould==NULL, FALSE, "LocaliseMouldAttributes() given a null mould pointer"); NodeMouldGroup* pNodeMouldGroup = pMould->FindMouldGroup(); if (!pNodeMouldGroup) return FALSE; Node* pNode = pNodeMouldGroup->FindFirstChild(); while (pNode) { if (pNode->IsAnObject()) { NodeRenderableInk* pInkNode = (NodeRenderableInk*)pNode; if (!LocaliseObjectAttrs(pMould, pInkNode)) return FALSE; } pNode=pNode->FindNext(); } return TRUE; } /******************************************************************************************** BOOL OpMouldLibSel::LocaliseObjectAttrs(NodeRenderableInk* pParent, NodeRenderableInk* pInkNode) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pMould = pointer to a mould object Returns: TRUE if successfully localised the mould attributes FALSE if failed. Purpose: ********************************************************************************************/ BOOL OpMouldLibSel::LocaliseObjectAttrs(NodeRenderableInk* pParent, NodeRenderableInk* pInkNode) { CCRuntimeClass* AttrType; BOOL Exists; NodeAttribute* pCurAttr; Node* pCloneAttr; Node* Current; ERROR2IF(pInkNode==NULL, FALSE, "LocaliseObjectAttrs() given a null object pointer"); Current = pParent->FindFirstChild(); while (Current) { if (Current->IsAnAttribute()) { pCurAttr = (NodeAttribute*)Current; // Some attributes cannot be applied to objects if (pCurAttr->CanBeAppliedToObject()) { AttrType = pCurAttr->GetAttributeType(); if (pInkNode->RequiresAttrib(pCurAttr)) { // Does the node already have this child attribute? Exists = (pInkNode->GetChildAttrOfType(AttrType) != NULL); ERROR3IF(Exists, "LocaliseMouldAttributes: Duplicate attr found !"); if (!Exists) { pCloneAttr = pCurAttr->SimpleCopy(); if (pCloneAttr) { pCloneAttr->AttachNode(pInkNode, FIRSTCHILD, TRUE, FALSE); HideNodeAction* UndoHideNodeAction; if (HideNodeAction::Init(this, GetUndoActionList(), pCloneAttr, TRUE, (Action**)(&UndoHideNodeAction) ) == AC_FAIL) { pCloneAttr->UnlinkNodeFromTree(); delete pCloneAttr; pCloneAttr=NULL; return FALSE; } } } } } } Current = Current->FindNext(); } return TRUE; } /******************************************************************************************** BOOL OpMouldLibSel::HideAllMouldAttrs(NodeMould* pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pMould = pointer to a mould object Returns: TRUE if successfully hidden all the mould attributes FALSE if failed. Purpose: ********************************************************************************************/ BOOL OpMouldLibSel::HideAllMouldAttrs(NodeMould* pMould) { ERROR2IF(pMould==NULL, FALSE, "HideAllMouldAttrs() given a null mould pointer"); Node* pNode = pMould->FindFirstChild(); while (pNode) { if (pNode->IsAnAttribute()) { NodeAttribute* pAttr = (NodeAttribute*)pNode; if (!DoHideNode(pAttr, TRUE)) return FALSE; } pNode=pNode->FindNext(); } return TRUE; } /******************************************************************************************** > BOOL OpMouldLibSel::DoReplaceAllMoulds( List* pNodeList, Path* pShape, MouldSpace mSpace, BOOL ScaleToFit, BOOL LockAspect) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pNodeList = pointer to list of nodes to process pNewShape = A path to use as a mould shape mSpace = The mould space to create, currently only envelope and perspective spaces are defined Returns: TRUE if successfully replaced all mould shapes with the new shape. FALSE if failed. Purpose: Scans all nodes in the node list. If moulds are found, their shapes will be replaced with pShape. ********************************************************************************************/ BOOL OpMouldLibSel::DoReplaceAllMoulds( List* pNodeList, Path* pShape, MouldSpace mSpace, BOOL ScaleToFit, BOOL LockAspect) { ERROR2IF(pNodeList==NULL, FALSE, "DoReplaceAllMoulds() given a null nodelist pointer"); NodeListItem* CurItem = (NodeListItem*)(pNodeList->GetHead()); if (!CurItem) return FALSE; while (CurItem) { if (CurItem->pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodeMould)) { NodeMould* pMould = (NodeMould*)(CurItem->pNode); if (!DoInvalidateNodeRegion(pMould, TRUE)) return FALSE; if (!DoReplaceMould(pMould, pShape, mSpace, ScaleToFit, LockAspect)) return FALSE; // Recalculate the mould's bounding box pMould->InvalidateBoundingRect(); if (!DoInvalidateNodeRegion(pMould, TRUE)) return FALSE; } CurItem = (NodeListItem*)(pNodeList->GetNext(CurItem)); } GetApplication()->UpdateSelection(); return TRUE; } /******************************************************************************************** > BOOL OpMouldLibSel::DoReplaceMould( NodeMould* pMould, Path* pShape, MouldSpace mSpace, BOOL ScaleToFit, BOOL LockAspect) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pMould = pointer to a mould object pNewShape = A path to use as a mould shape mSpace = The mould space to create, currently only envelope and perspective spaces are defined Returns: TRUE if successfully removed the mould object FALSE if failed. Purpose: Replaces one mould shape with another definition ********************************************************************************************/ BOOL OpMouldLibSel::DoReplaceMould( NodeMould* pMould, Path* pNewShape, MouldSpace mSpace, BOOL ScaleToFit, BOOL LockAspect ) { ERROR2IF(pMould==NULL, FALSE, "DoReplaceMould() called with no mould pointer"); ERROR2IF(pNewShape==NULL, FALSE, "DoReplaceMould() called with a NULL shape pointer"); // cache the old geometry MouldGeometry* pGeometry=pMould->GetGeometry(); if (!pGeometry) return FALSE; DocRect Bounds, DRect, *pRect; Bounds = pGeometry->GetSourceRect(); if ((pMould->StartSaveContext(this, REC_REBUILD | REC_GEOMETRY)) == CC_FAIL) return FALSE; // create a new one (will automatically delete the old) if (!pMould->CreateGeometry(mSpace)) return FALSE; // cache a pointer to the new geometry pGeometry=pMould->GetGeometry(); if (!pGeometry) return FALSE; // check the shape we've been given is fine and lovely UINT32 errID; if (!pGeometry->Validate(pNewShape,errID)) { ERROR1(FALSE, errID); } // build a destination scale rectangle pRect = FindScaleRect(&Bounds, &DRect, ScaleToFit, LockAspect); // Find the current mould path object NodeMouldPath* pNodeMPath = pMould->GetPathShape(); if (!pNodeMPath) return FALSE; // Hide the mould path node if (!DoHideNode(pNodeMPath, FALSE, NULL)) return FALSE; // Create and add a mould shape object pNodeMPath = pMould->AddNewMouldShape(pNewShape,pRect,this); if (!pNodeMPath) return FALSE; // set the geometry using this new mould shape if (!pGeometry->Define(&(pNodeMPath->InkPath), &Bounds)) return FALSE; // finally generate some new mould shapes. if (pMould->RemouldAll(this)==CC_FAIL) return FALSE; // Ilan 7/5/00 // Inform geom linked attrs of the change. Nb outside the normal AllowOp mechanism NodeAttribute* pNA = pMould->FindFirstGeometryLinkedAttr(); while(pNA) { pNA->LinkedNodeGeometryHasChanged(this); pNA = pNA->FindNextGeometryLinkedAttr(); } if ((pMould->EndSaveContext(this, REC_REBUILD | REC_GEOMETRY)) == CC_FAIL) return FALSE; return TRUE; } /******************************************************************************************** > BOOL OpMouldLibSel::DoRecordGeometry(NodeMould* pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pMould = pointer to a mould object Returns: TRUE if successfully recorded the mould geometry FALSE if failed. Purpose: Asks the geometry to record its own internal data and then records the type of geometry on the undo, ready to be replaced. ********************************************************************************************/ BOOL OpMouldLibSel::DoRecordGeometry(NodeMould* pMould) { ActionCode Act; // call the actions static init function to get the action going. Act = RecordGeometryAction::DoRecord(this,pMould); if (Act==AC_FAIL) return FALSE; return TRUE; } /******************************************************************************************** > OpMouldLib::OpMouldLib() Author: Mike_Kenny (Xara Group Ltd) Created: 18/01/95 Purpose: OpMouldLib() constructor SeeAlso: - ********************************************************************************************/ OpMouldLib::OpMouldLib() { // Dummy constructor } /******************************************************************************************** > BOOL OpMouldLib::DoRotateMould(NodeMould* pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 19/01/95 Inputs: pMould = pointer to a mould object Returns: TRUE if successfully rotated the geometry around by one path element Purpose: Simply rotates the orientation of all objects within the geometry. ********************************************************************************************/ /* BOOL OpMouldLib::DoRotateMould(NodeMould* pMould) { ERROR2IF(pMould==NULL, FALSE, "DoRotateMould() called with no mould pointer"); MouldGeometry* pGeometry=pMould->GetGeometry(); NodeMouldPath* pMouldPath=pMould->GetPathShape(); if (!pMouldPath || !pGeometry) return FALSE; // cache the actual path shape Path* pEditPath = &(pMouldPath->InkPath); // record the current path data, ie its flags,verbs & coords if (SavePathArraysAction::DoRecord(this, &UndoActions, pEditPath) == AC_FAIL) return FALSE; // record the current mould shape on the undo and any blob rectangles if (pMould->SaveContext(this)==CC_FAIL) return FALSE; // rotate the path around INT32 index=0; if (!pEditPath->FindNextEndPoint(&index)) return FALSE; if (!pEditPath->ChangeStartElement(index)) return FALSE; // set the geometry using this new mould shape if (!pGeometry->Define(pEditPath, NULL)) return FALSE; // finally generate some new mould shapes. if (pMould->RemouldAll(this)==CC_FAIL) return FALSE; return TRUE; } */ BOOL OpMouldLib::DoRotateMould(NodeMould* pMould) { ERROR2IF(pMould==NULL, FALSE, "DoRotateMould() called with no mould pointer"); MouldGeometry* pGeometry=pMould->GetGeometry(); NodeMouldPath* pMouldPath=pMould->GetPathShape(); if (!pMouldPath || !pGeometry) return FALSE; // save the bits we need on the undo if ((pMould->StartSaveContext(this, REC_REBUILD | REC_GEOMCONTEXT | REC_PATHARRAYS)) == CC_FAIL) return FALSE; Path* pEditPath = &(pMouldPath->InkPath); // rotate the path around INT32 index=0; if (!pEditPath->FindNextEndPoint(&index)) return FALSE; if (!pEditPath->ChangeStartElement(index)) return FALSE; // set the geometry using this new mould shape if (!pGeometry->Define(pEditPath, NULL)) return FALSE; // finally generate some new mould shapes. if (pMould->RemouldAll(this)==CC_FAIL) return FALSE; // Ilan 7/5/00 // Inform geom linked attrs of the change. Nb outside the normal AllowOp mechanism NodeAttribute* pNA = pMould->FindFirstGeometryLinkedAttr(); while(pNA) { pNA->LinkedNodeGeometryHasChanged(this); pNA = pNA->FindNextGeometryLinkedAttr(); } if ((pMould->EndSaveContext(this, REC_REBUILD | REC_GEOMCONTEXT | REC_PATHARRAYS)) == CC_FAIL) return FALSE; return TRUE; } /******************************************************************************************** > RecordGeometryAction::RecordGeometryAction() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Purpose: RecordGeometryAction constructor ********************************************************************************************/ RecordGeometryAction::RecordGeometryAction() { pSavedMould=NULL; pSavedGeometry=NULL; } RecordGeometryAction::~RecordGeometryAction() { if (pSavedGeometry!=NULL) { delete pSavedGeometry; pSavedGeometry=NULL; } } /******************************************************************************************** > static ActionCode RecordGeometryAction::DoRecord(Operation* const pOp, NodeMould* const pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 16/03/95 Inputs: pOp = The operation to which the action should be added pMould = a pointer to a mould object we wish to record the geometry of Outputs: - Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: This simple action saves away the current mould space (geometry) defined by this mould object. The mould space may then be deleted externally and replaced with another. During undo the new space will be recorded and the old space rebuilt. ********************************************************************************************/ ActionCode RecordGeometryAction::DoRecord(Operation* const pOp, NodeMould* const pMould) { ERROR2IF(pMould==NULL,AC_FAIL,"Mould pointer is NULL in RecordGeometryAction()"); RecordGeometryAction* GeomAction; // Attempt to initialise the next action return RecordGeometryAction::Init(pOp, pOp->GetUndoActionList(), pMould, (Action**)(&GeomAction)); } /******************************************************************************************** > static ActionCode RecordGeometryAction::Init(Operation* const pOp, ActionList* pActionList, MouldGeometry* pGeometry, Action** NewAction) Author: Mike_Kenny (Xara Group Ltd) Created: 16/03/95 Inputs: pOp: The operation to which the action should be added pActionList: The action list in the operation object pMould: A pointer to a mould object whose geometry we will record. Outputs: NewAction: A pointer to the action if it could be allocated. Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: This simple action saves away the current mould space (geometry) defined by this mould object. The mould space may then be deleted externally and replaced with another. During undo the new space will be recorded and the old space rebuilt. ********************************************************************************************/ ActionCode RecordGeometryAction::Init(Operation* const pOp, ActionList* pActionList, NodeMould* const pMould, Action** NewAction) { ERROR2IF(pMould==NULL,AC_FAIL,"Mould pointer is NULL in RecordGeometryAction()"); MouldGeometry* pGeometry = pMould->GetGeometry(); ERROR2IF(pGeometry==NULL, AC_FAIL, "There was no defined geometry in RecordGeometryAction"); // find the size of the geometry we need to save ActionCode Ac = (Action::Init(pOp, pActionList, sizeof(RecordGeometryAction), CC_RUNTIME_CLASS(RecordGeometryAction), NewAction)); if (*NewAction != NULL) { ((RecordGeometryAction*)(*NewAction))->pSavedMould = pMould; ((RecordGeometryAction*)(*NewAction))->pSavedGeometry = pGeometry; } return (Ac); } /******************************************************************************************** > ActionCode RecordGeometryAction::Execute() Author: Mike_Kenny (Xara Group Ltd) Created: 16/03/95 Inputs: - Returns: ActionCode indicating if the action was successfully executed or not Purpose: Executes the InvalidateRectAction which invalidates a rectangular region of the current document. ********************************************************************************************/ ActionCode RecordGeometryAction::Execute() { RecordGeometryAction* GeomAction; ActionCode Ac; // Attempt to initialise the next action Ac = RecordGeometryAction::Init(pOperation, pOppositeActLst, pSavedMould, (Action**)(&GeomAction)); if (Ac!=AC_FAIL) { // set the geometry using this new mould shape if (!pSavedMould->SetGeometry(pSavedGeometry)) return AC_FAIL; // Make sure the destructor doesn't vape it pSavedGeometry=NULL; } return Ac; } /******************************************************************************************** > InvalidateRectAction::InvalidateRectAction() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Purpose: InvalidateRectAction constructor ********************************************************************************************/ InvalidateRectAction::InvalidateRectAction() { InvalidSpread=NULL; } /******************************************************************************************** > static ActionCode InvalidateRectAction::DoRecord(Operation* const pOp, const DocRect& Rect, Spread* pSpread) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp = The operation to which the action should be added Rect = The rectangle to save pSpread = The rectangles spread Outputs: - Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: This simple action saves away a rectangle on the undo, which will be invalidated during execution of undo and redo. ********************************************************************************************/ ActionCode InvalidateRectAction::DoRecord( Operation* const pOp, const DocRect& Rect, Spread* pSpread) { InvalidateRectAction* InvRctAct; // Attempt to initialise the next action return InvalidateRectAction::Init(pOp, pOp->GetUndoActionList(), Rect, pSpread, (Action**)(&InvRctAct)); } /******************************************************************************************** > static ActionCode InvalidateRectAction::Init(Operation* const pOp, ActionList* pActionList, const DocRect& Rect, Spread* pSpread, Action** NewAction) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp: The operation to which the action should be added pActionList: The action list in the operation object pSpread: The rectangles spread Outputs: NewAction: A pointer to the action if it could be allocated. Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: This simple action invalidates a rectangular region. ********************************************************************************************/ ActionCode InvalidateRectAction::Init(Operation* const pOp, ActionList* pActionList, const DocRect& Rect, Spread* pSpread, Action** NewAction) { ActionCode Ac = (Action::Init(pOp, pActionList, sizeof(InvalidateRectAction), CC_RUNTIME_CLASS(InvalidateRectAction), NewAction)); if (*NewAction != NULL) { ((InvalidateRectAction*)(*NewAction))->InvalidRect = Rect; ((InvalidateRectAction*)(*NewAction))->InvalidSpread = pSpread; } return (Ac); } /******************************************************************************************** > ActionCode InvalidateRectAction::Execute() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: - Returns: ActionCode indicating if the action was successfully executed or not Purpose: Executes the InvalidateRectAction which invalidates a rectangular region of the current document. ********************************************************************************************/ ActionCode InvalidateRectAction::Execute() { InvalidateRectAction* InvRctAct; ActionCode Ac; // Attempt to initialise the next action Ac = InvalidateRectAction::Init(pOperation, pOppositeActLst, InvalidRect, InvalidSpread, (Action**)(&InvRctAct)); if (Ac!=AC_FAIL) { // The action was successfully initialised Document* pDocument = Document::GetCurrent(); ENSURE(pDocument!=NULL, "There was no current document in InvalidateRectAction" ); if (pDocument!=NULL) pDocument->ForceRedraw(InvalidSpread, InvalidRect); } return Ac; } /******************************************************************************************** > RedrawBoundsAction::RedrawBoundsAction() Author: Mike_Kenny (Xara Group Ltd) Created: 11/7/94 ********************************************************************************************/ RedrawBoundsAction::RedrawBoundsAction() { ChangedNode = NULL; } ActionCode RedrawBoundsAction::DoRecord(Operation* pOp, NodeMould* WhichNode, BOOL Redraw) { RedrawBoundsAction* RecAction; ActionCode Act = RedrawBoundsAction::Init(pOp, pOp->GetUndoActionList(), WhichNode, (Action**)&RecAction); if (Act != AC_FAIL && Redraw) RedrawNode(pOp, WhichNode); return Act; } ActionCode RedrawBoundsAction::Init(Operation* pOp, ActionList* pActionList, NodeMould* WhichNode, Action** NewAction) { UINT32 ActSize = sizeof(RedrawBoundsAction); ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(RedrawBoundsAction), NewAction); if ((Ac == AC_OK) && (*NewAction != NULL)) ((RedrawBoundsAction*)*NewAction)->ChangedNode = WhichNode; return Ac; } ActionCode RedrawBoundsAction::Execute() { RedrawBoundsAction* ReAction; ActionCode Act; Act = RedrawBoundsAction::Init(pOperation, pOppositeActLst, ChangedNode, (Action**)(&ReAction)); if (Act != AC_FAIL) RedrawNode(pOperation, ChangedNode); return Act; } void RedrawBoundsAction::RedrawNode(Operation* pOp, NodeMould* pNode) { Document* pDoc = pOp->GetWorkingDoc(); ERROR3IF( pDoc == NULL, "There was no current document when undoing RecordMouldBounds" ); Spread* pSpread = pNode->FindParentSpread(); DocRect Invalid = pNode->GetChildrensBounds(); pDoc->ForceRedraw( pSpread, Invalid, FALSE, pNode); } /******************************************************************************************** > SaveDetachAction::SaveDetachAction() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Purpose: SaveDetachAction constructor ********************************************************************************************/ SaveDetachAction::SaveDetachAction() { pCurrMould=NULL; CurrDetached=FALSE; } /******************************************************************************************** > static ActionCode SaveDetachAction::Save(Operation* const pOp, NodeMould* const pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp = The operation to which the action should be added pMould = The mould object whose data we are saving Outputs: - Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: This simple action saves away a rectangle on the undo, which will be invalidated during execution of undo and redo. ********************************************************************************************/ ActionCode SaveDetachAction::Save(Operation* const pOp, NodeMould* const pMould) { SaveDetachAction* SvDetAct; // Attempt to initialise the next action return SaveDetachAction::Init(pOp, pOp->GetUndoActionList(), pMould, (Action**)(&SvDetAct)); } /******************************************************************************************** > static ActionCode SaveDetachAction::Init(Operation* const pOp, ActionList* pActionList, Action** NewAction) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp: The operation to which the action should be added pActionList: The action list in the operation object pSpread: The rectangles spread Outputs: NewAction: A pointer to the action if it could be allocated. Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: An action to save away the state of the detach flag. ********************************************************************************************/ ActionCode SaveDetachAction::Init( Operation* const pOp, ActionList* pActionList, NodeMould* const pMould, Action** NewAction) { ActionCode Ac = (Action::Init(pOp, pActionList, sizeof(SaveDetachAction), CC_RUNTIME_CLASS(SaveDetachAction), NewAction)); if (*NewAction != NULL) { ((SaveDetachAction*)(*NewAction))->pCurrMould = pMould; ((SaveDetachAction*)(*NewAction))->CurrDetached = pMould->IsDetached(); } return (Ac); } /******************************************************************************************** > ActionCode SaveDetachAction::Execute() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: - Returns: ActionCode indicating if the action was successfully executed or not Purpose: Executes the SaveDetachAction ********************************************************************************************/ ActionCode SaveDetachAction::Execute() { SaveDetachAction* MyAct; ActionCode Ac = SaveDetachAction::Init( pOperation, pOppositeActLst, pCurrMould, (Action**)(&MyAct)); if (Ac!=AC_FAIL) { if ((pCurrMould->IsDetached())!=CurrDetached) pCurrMould->ToggleDetachFlag(); } return Ac; } /******************************************************************************************** > OpDragOrigin::OpDragOrigin() Author: Mike_Kenny (Xara Group Ltd) Created: 25/01/95 Purpose: Constructs an Operation that allows the user to drag vanishing points of a perspective mould ********************************************************************************************/ OpDragOrigin::OpDragOrigin() { StartSpread = NULL; pEditMould = NULL; pEditMouldPath = NULL; pEditPath = NULL; pEditGeometry = NULL; } /******************************************************************************************** > BOOL OpDragOrigin::Declare() Author: Mike_Kenny (Xara Group Ltd) Created: 25/01/95 Inputs: - Outputs: - Returns: TRUE if the op is correctly registered. Purpose: Registers the rotation centre drag Operation. Errors: - SeeAlso: - ********************************************************************************************/ BOOL OpDragOrigin::Init() { return RegisterOpDescriptor(0, /* _R(IDS_DRAGOriginOP), */ _R(IDS_DRAGORIGINOP), CC_RUNTIME_CLASS(OpDragOrigin), OPTOKEN_DRAGVANISHPOINT, OpDragOrigin::GetState, 0, _R(IDBBL_DRAGORIGINOP)); } /******************************************************************************************** > OpState OpDragOrigin::GetState(String_256*, OpDescriptor*) Author: Mike_Kenny (Xara Group Ltd) Created: 27/01/95 Inputs: - Outputs: - Returns: Returns a default OpState. Purpose: Controls whether the rotation-centre mouse drag operation is available or not. Currently, is always available (the internal logic of the selector tool & info-bar handle this really). Errors: - SeeAlso: - ********************************************************************************************/ OpState OpDragOrigin::GetState(String_256*, OpDescriptor*) { OpState os; return os; } /******************************************************************************************** > virtual void OpDragOrigin::GetOpName(String_256* OpName) Author: Mike_Kenny (Xara Group Ltd) Created: 27/01/95 Inputs: - Outputs: The undo string for the operation Returns: Purpose: The GetOpName fn is overridden so that we return back a description appropriate to this type of operation. ********************************************************************************************/ void OpDragOrigin::GetOpName(String_256* OpName) { *OpName = String_256(_R(IDS_UNDO_DRAGVPOINT)); } /******************************************************************************************** > void OpDragOrigin::DoDragVanishPoint(Spread* pSpread, const DocCoord& dcPos, ClickModifiers mods, NodeMould* pMould, BOOL WhichBlob) Author: Mike_Kenny (Xara Group Ltd) Created: 25/01/95 Inputs: pSpread = the spread containing the rotation centre blob dcPos = the initial position of the blob mods = the mouse button click modifiers (eg ctrl click etc) pMould = a pointer to the mould we will drag Outputs: - Returns: - Purpose: Starts a drag of one of the perspective moulds vanishing points. Note, a lot of effort has been put into the redraw of perspectives when dragging vanishing points. We must make sure the vanishing point rectangles are redrawn separately to the actual mould contents. If not it is v likely that you will get a huge rectangle as a result of the union. Redrawing this area is very bad. Also note, we do not drag a copy of the envelope or perspective control polygon but the control polygon itself. This means we must make sure when we scroll we prevent the mould object from attempting to eor its blobs on, this has the tendancy to leave large amounts of unsightly eor all over the place. ********************************************************************************************/ void OpDragOrigin::DoDragVanishPoint(Spread* pSpread, const DocCoord& dcPos, ClickModifiers mods, NodeMould* pMould, BOOL WhichBlob) { ERROR3IF(pMould==NULL,"DoDragVanishPoint() given a null mould object"); DocCoord v0,v1; // Remember the starting positions etc and initiate a drag. StartSpread = pSpread; pEditMould = pMould; // try to find the geometry MouldGeometry* pGeometry = pMould->GetGeometry(); if (!pGeometry->IsKindOf(CC_RUNTIME_CLASS(MouldPerspective))) { ERROR3("DoDragVanishPoint() called on an illegal geometry"); goto Failed; } pEditGeometry = (MouldPerspective*)pGeometry; // read the current vanishing point positions for this mould. pEditGeometry->GetVanishingPoints(v0,v1); WhichBlob ? (Blob=v1) : (Blob=v0); // get hold of the moulds nodepath object pEditMouldPath = pMould->GetPathShape(); if (!pEditMouldPath) goto Failed; // cache the actual path shape pEditPath = &(pEditMouldPath->InkPath); // record an action to redraw the node in its current state during undo/redo // we use our own special action to do this RecordBoundsAction uses the nasty // GetUnionBlobBounds() which unions the vanishing points in there too. // PS.. Pass FALSE into this function to avoid redraw at this stage if (RedrawBoundsAction::DoRecord(this,pEditMould,FALSE)==AC_FAIL) goto Failed; // Save the current rectangular bounds to redraw after the dragop has finished OpDragOrigin::EditRect = pMould->GetChildrensBounds(); // record the current path data, ie its flags,verbs & coords if ((pEditMould->StartSaveContext(this, REC_REBUILD | REC_BLOBS | REC_GEOMCONTEXT | REC_PATHARRAYS)) == CC_FAIL) goto Failed; /* if (SavePathArraysAction::DoRecord(this, &UndoActions, pEditPath) == AC_FAIL) goto Failed; // record the current mould shape on the undo and any blob rectangles if (pEditMould->SaveContext(this)==CC_FAIL) goto Failed; */ // turn the display blobs off to prevent the mould from drawing them on // during a scroll. pEditMould->DisableBlobs(); // start off the drag and get ready StartDrag(DRAGTYPE_AUTOSCROLL); return; Failed: FailAndExecute(); End(); } /******************************************************************************************** > void OpDragOrigin::DragPointerMove(DocCoord dcPos, ClickModifiers, Spread* pSpread, BOOL bSolidDrag) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: dcPos = the current mouse position pSpread = the spread containing the mouse Outputs: - Returns: - Purpose: Called while the vanishing point is being dragged around. Erases it from its old position and redraws it at its new position. ********************************************************************************************/ void OpDragOrigin::DragPointerMove(DocCoord dcPos, ClickModifiers mods, Spread* pSpread, BOOL bSolidDrag) { // If the mouse has moved outside the spread the drag was started on then we must // account for this. if (pSpread != StartSpread) dcPos = MakeRelativeToSpread(StartSpread, pSpread, dcPos); // Erase the old blobs. RenderDragBlobs(CalcBlobClipRect(), StartSpread, bSolidDrag); if (pEditGeometry->MoveVanishingPoint(Blob,dcPos)) { Blob=dcPos; // Update the new shape in the path. DocCoord* pCoords = pEditPath->GetCoordArray(); pEditGeometry->CopyShape(pCoords); } // Draw on the new blobs RenderDragBlobs(CalcBlobClipRect(), StartSpread, bSolidDrag); } /******************************************************************************************** > void OpDragOrigin::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL success, BOOL bSolidDrag) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: PointerPos = final position of the mouse ClickMods = the click modifiers. pSpread = spread containing the mouse success = whether the drag was cancelled or not Outputs: - Returns: - Purpose: Update the perspective mould object with the final resting place of this mould vanishing point. ********************************************************************************************/ void OpDragOrigin::DragFinished(DocCoord dcPos, ClickModifiers mods, Spread* pSpread, BOOL success, BOOL bSolidDrag) { EndDrag(); Document* pDoc = Document::GetCurrent(); NodeAttribute* pNA = NULL; // Make sure the nodes blob rendering is enabled again pEditMould->EnableBlobs(); // Act on the termination status of the drag operation. if (!success) { // Get rid of what ever new stuff we have, the unwind handler will // cause our original stuff to be replaced. RenderDragBlobs(CalcBlobClipRect(), StartSpread, bSolidDrag); goto Failed; } // Turn that hourglass on BeginSlowJob(); // Make sure we invalidate the edit object pEditMouldPath->InvalidateBoundingRect(); // Force a redraw on the original position. // Note, we cannot do this before we start dragging as EORing goes mad! if (pDoc) pDoc->ForceRedraw( StartSpread, OpDragOrigin::EditRect, FALSE, pEditMould); if (pEditMould->RemouldAll(this)==CC_FAIL) goto Failed; // Ilan 7/5/00 // Inform geom linked attrs of the change. Nb outside the normal AllowOp mechanism pNA = pEditMould->FindFirstGeometryLinkedAttr(); while(pNA) { pNA->LinkedNodeGeometryHasChanged(this); pNA = pNA->FindNextGeometryLinkedAttr(); } if ((pEditMould->EndSaveContext(this, REC_REBUILD | REC_BLOBS | REC_GEOMCONTEXT | REC_PATHARRAYS)) == CC_FAIL) goto Failed; // record an action to redraw the state of the mould (but not vanishpoints) if (RedrawBoundsAction::DoRecord(this,pEditMould,TRUE)==AC_FAIL) goto Failed; // record the vanish points separately (important to avoid unionblobbounds()!) if (pEditGeometry->RecordBlobs(this,StartSpread)==CC_FAIL) goto Failed; End(); return; Failed: FailAndExecute(); End(); return; } /******************************************************************************************** > void OpDragOrigin::RenderDragBlobs(DocRect Clip, Spread* pSpread, BOOL bSolidDrag) Author: Mike_Kenny (Xara Group Ltd) Created: 25/01/95 Inputs: Clip = clipping rectangle for output pSpread = the spread to render into Outputs: - Returns: - Purpose: Draws the drag path and new vanishing point positions ********************************************************************************************/ void OpDragOrigin::RenderDragBlobs(DocRect Clip, Spread* pSpread, BOOL bSolidDrag) { RenderRegion* pRegion = DocView::RenderOnTop(&Clip, pSpread, ClippedEOR); while (pRegion != NULL) { // Get the edit path to render itself pEditMouldPath->RenderObjectBlobs(pRegion); pEditGeometry->RenderControlBlobs(pRegion); pRegion = DocView::GetNextOnTop(&Clip); } } /******************************************************************************************** > DocRect OpDragOrigin::CalcBlobClipRect() Author: Mike_Kenny (Xara Group Ltd) Created: 27/01/95 Inputs: - Outputs: - Returns: A clipping rectangle for use by RenderDragBlobs. Purpose: Constructs a rectangle that surrounds the objects we are eoring ********************************************************************************************/ DocRect OpDragOrigin::CalcBlobClipRect() { // get the current vanish point clip rects DocRect Rect0 = pEditGeometry->GetBlobBoundingRect(); DocRect Rect1 = pEditMouldPath->GetBlobBoundingRect(); return Rect0.Union(Rect1); } /******************************************************************************************** > StartRebuildMouldAction::StartRebuildMouldAction() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Purpose: RebuildMouldAction constructor ********************************************************************************************/ StartRebuildMouldAction::StartRebuildMouldAction() { pRebuildMould=NULL; } /******************************************************************************************** > static ActionCode StartRebuildMouldAction::DoRecord(Operation* const pOp, NodeMould* pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp = The operation to which the action should be added pMould = A pointer to a mould to rebuild Outputs: - Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: ********************************************************************************************/ ActionCode StartRebuildMouldAction::DoRecord(Operation* const pOp, NodeMould* pMould) { StartRebuildMouldAction* Act; // Attempt to initialise the next action return StartRebuildMouldAction::Init(pOp, pOp->GetUndoActionList(), pMould, (Action**)(&Act)); } /******************************************************************************************** > static ActionCode StartRebuildMouldAction::Init( Operation* const pOp, ActionList* pActionList, NodeMould* pMould, Action** NewAction) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp: The operation to which the action should be added pActionList: The action list in the operation object pMould: A pointer to a mould to rebuild Outputs: NewAction: A pointer to the action if it could be allocated. Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: This simple action invalidates a rectangular region. ********************************************************************************************/ ActionCode StartRebuildMouldAction::Init(Operation* const pOp, ActionList* pActionList, NodeMould* pMould, Action** NewAction) { ActionCode Ac = (Action::Init(pOp, pActionList, sizeof(StartRebuildMouldAction), CC_RUNTIME_CLASS(StartRebuildMouldAction), NewAction)); if (*NewAction != NULL) { ((StartRebuildMouldAction*)(*NewAction))->pRebuildMould = pMould; } return (Ac); } /******************************************************************************************** > ActionCode RebuildMouldAction::Execute() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: - Returns: ActionCode indicating if the action was successfully executed or not Purpose: Executes the RebuildMouldAction which invalidates a rectangular region of the current document. ********************************************************************************************/ ActionCode StartRebuildMouldAction::Execute() { EndRebuildMouldAction* Act; ActionCode Ac; // Attempt to initialise the next action Ac = EndRebuildMouldAction::Init(pOperation, pOppositeActLst, pRebuildMould, (Action**)(&Act)); if (Ac!=AC_FAIL) { if (!pRebuildMould->IsDetached()) { // The action was successfully initialised NodeMoulder* pMoulder = pRebuildMould->FindFirstMoulder(); NodeMouldGroup* pMouldGroup = pRebuildMould->FindMouldGroup(); if (pMoulder!=NULL && pMouldGroup!=NULL) { pMoulder->DestroyMouldedObjects(); if (!pMoulder->CreateMouldedObjects(pMouldGroup, pRebuildMould->GetGeometry(), NULL)) { pMoulder->DestroyMouldedObjects(); return AC_FAIL; } } } } return Ac; } /******************************************************************************************** > EndRebuildMouldAction::EndRebuildMouldAction() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Purpose: RebuildMouldAction constructor ********************************************************************************************/ EndRebuildMouldAction::EndRebuildMouldAction() { pRebuildMould=NULL; } /******************************************************************************************** > static ActionCode EndRebuildMouldAction::DoRecord(Operation* const pOp, NodeMould* pMould) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp = The operation to which the action should be added pMould = A pointer to a mould to rebuild Outputs: - Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: ********************************************************************************************/ ActionCode EndRebuildMouldAction::DoRecord(Operation* const pOp, NodeMould* pMould) { EndRebuildMouldAction* Act; // Attempt to initialise the next action return EndRebuildMouldAction::Init(pOp, pOp->GetUndoActionList(), pMould, (Action**)(&Act)); } /******************************************************************************************** > static ActionCode EndRebuildMouldAction::Init( Operation* const pOp, ActionList* pActionList, NodeMould* pMould, Action** NewAction) Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: pOp: The operation to which the action should be added pActionList: The action list in the operation object pMould: A pointer to a mould to rebuild Outputs: NewAction: A pointer to the action if it could be allocated. Returns: ActionCode: one of AC_OK, AC_NORESTORE, AC_FAIL Purpose: ********************************************************************************************/ ActionCode EndRebuildMouldAction::Init(Operation* const pOp, ActionList* pActionList, NodeMould* pMould, Action** NewAction) { ActionCode Ac = (Action::Init(pOp, pActionList, sizeof(EndRebuildMouldAction), CC_RUNTIME_CLASS(EndRebuildMouldAction), NewAction)); if (*NewAction != NULL) { ((EndRebuildMouldAction*)(*NewAction))->pRebuildMould = pMould; } return (Ac); } /******************************************************************************************** > ActionCode EndRebuildMouldAction::Execute() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Inputs: - Returns: ActionCode indicating if the action was successfully executed or not Purpose: Executes the RebuildMouldAction which invalidates a rectangular region of the current document. ********************************************************************************************/ ActionCode EndRebuildMouldAction::Execute() { StartRebuildMouldAction* Act; // Attempt to initialise the next action return StartRebuildMouldAction::Init(pOperation, pOppositeActLst, pRebuildMould, (Action**)(&Act)); } /******************************************************************************************** > BuildMouldAction::BuildMouldAction() Author: Mike_Kenny (Xara Group Ltd) Created: 26/01/95 Purpose: BuildMouldAction constructor ********************************************************************************************/ BuildMouldAction::BuildMouldAction() { pBuildMould=NULL; } /******************************************************************************************** ********************************************************************************************/ ActionCode BuildMouldAction::DoRecord(Operation* const pOp, NodeMould* pMould) { EndRebuildMouldAction* Act; // Attempt to initialise the next action return BuildMouldAction::Init(pOp, pOp->GetUndoActionList(), pMould, (Action**)(&Act)); } /******************************************************************************************** ********************************************************************************************/ ActionCode BuildMouldAction::Init(Operation* const pOp, ActionList* pActionList, NodeMould* pMould, Action** NewAction) { ActionCode Ac = (Action::Init(pOp, pActionList, sizeof(BuildMouldAction), CC_RUNTIME_CLASS(BuildMouldAction), NewAction)); if (*NewAction != NULL) { ((BuildMouldAction*)(*NewAction))->pBuildMould = pMould; } return (Ac); } /******************************************************************************************** ********************************************************************************************/ ActionCode BuildMouldAction::Execute() { ActionCode Ac = AC_OK; OperationStatus OpStat = pOperation->OpStatus; BuildMouldAction Act; Ac = BuildMouldAction::Init(pOperation, pOppositeActLst, pBuildMould, (Action**)(&Act)); if (Ac != AC_FAIL) { NodeMoulder* pMoulder = pBuildMould->FindFirstMoulder(); NodeMouldGroup* pMouldGroup = pBuildMould->FindMouldGroup(); if (pMoulder!=NULL && pMouldGroup!=NULL) { // DO, UNDO, REDO destroy all moulder objects pMoulder->DestroyMouldedObjects(); // for DO and REDO we need to recreate them if (OpStat==DO || OpStat==REDO) { if (!pMoulder->CreateMouldedObjects(pMouldGroup, pBuildMould->GetGeometry(), NULL)) { // failed to recreate so toast em. pMoulder->DestroyMouldedObjects(); Ac = AC_FAIL; } } } } return Ac; }