// $Id: slicehelper.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============================ */ // helper functions that relate to things slice like // such as image slicing, bar creation, bar duplication and text synchronising #include "camtypes.h" #include "layer.h" // knowing about layers //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] // for the use of wix temple attribs //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "userattr.h" #include "tmpltatr.h" // named set stuff #include "ngcore.h" #include "ngitem.h" //#include "simon.h" // for the _R(IDS_ROLLOVER_DEFAULT) etc //#include "sliceres.h" // more resources #include "slicehelper.h" #include "ngprop.h" #include "ngsentry.h" #include // for "find" of lists #include "nodetxts.h" #include "nodetxtl.h" //#include "nodetext.h" #include "nodecntr.h" #include "layergal.h" // for the vis layer action // need to know about shadows and bevels since they size funny for the GetNodeBounding() #include "nodeshad.h" #include "nbevcont.h" #include "ncntrcnt.h" #include "lineattr.h" // for the line width inclusion part of the GetNodeBounding() #include "ophist.h" #include "nodebev.h" #include "nodetext.h" // global that is set when a bar property tag is imported to say how many bars there were beforehand // used by SliceHelper::MeshImportedLayersWithExistingButtonBars() and is defined in rechdoc.cpp extern INT32 g_NoOfBarsBeforeImport; #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif DECLARE_SOURCE("$Revision: 1282 $"); // Declare smart memory handling in Debug builds #define new CAM_DEBUG_NEW #define MAX_IMPORTED_BARS 255 List * SliceHelper::m_pSelNodeList = 0; /******************************************************************************************** > static Layer * SliceHelper::FindLayerCalled(const StringBase & LayerName) Author: Simon_Knight (Xara Group Ltd) Created: 30/9/99 Params: LayerName - The name of the layer to find Returns: returns the layer node for the Layer name passed in. Returns NULL if there is no layer of that name Purpose: - Errors: - ********************************************************************************************/ Layer * SliceHelper::FindLayerCalled(const StringBase & LayerName) { // find a spread? Spread* pSpread = Document::GetSelectedSpread(); if (pSpread == NULL) return FALSE; // scan for the layer to delete Layer * pLayer = pSpread->FindFirstLayer(); while (pLayer) { if (!pLayer->IsNodeHidden() && pLayer->GetLayerID().CompareTo(LayerName) == 0) return pLayer; pLayer = pLayer->FindNextLayer(); } return NULL; } /******************************************************************************************** > static INT32 SliceHelper::CountButtonsInBar(const StringBase & BarName) Author: Simon_Knight (Xara Group Ltd) Created: 30/9/99 Params: BarName - The name of the bar we refer to Returns: The number of buttons in the bar. Zero if it doesn't think the bar exists. Purpose: - Errors: - ********************************************************************************************/ INT32 SliceHelper::CountButtonsInBar(const StringBase & BarName) { String_256 DefaultLayerName; DefaultLayerName.Load(_R(IDS_ROLLOVER_DEFAULT)); // count the buttons on the default layer // every button bar requires a member to be on the default layer // so there is no point in counting any other layer Layer * pDef = FindLayerCalled(DefaultLayerName); INT32 NumberOfButtons = 0; if (pDef) { // store of example attribs of each button in this bar // but we don't need this data so we can throw it away afterwards TemplateAttribute ** ppFoundButton[MAX_BUTTONS_IN_A_BAR]; // scan down from the default layer counting the different button names // that all have the same bar name CountButtonsInBarScan(pDef, (TemplateAttribute **) ppFoundButton, &NumberOfButtons, BarName); } return NumberOfButtons; } /******************************************************************************************** > static void SliceHelper::CountButtonsInBarScan(Node * pNode, TemplateAttribute ** ppFoundButton, INT32 * pNumberOfButtons, const StringBase &BarName) Author: Simon_Knight (Xara Group Ltd) Created: 30/9/99 Params: pNode - The node to scan from. Looks at everything down from here. ppFoundButton - Pointer to array of Template Attribute nodes. Each one holds the name of the bar and the name of the button it is. pNumberOfButtons- The number of buttons found so far BarName - The bar we are looking for Returns: - Purpose: Finds the number of buttons in the bar and the names of each button, by filling in the ppFoundButton array and pNumberOfButtons. Errors: Limitation of 20 buttons in a bar, which is all the program will let you have. Caution this function is RECURSIVE. ********************************************************************************************/ void SliceHelper::CountButtonsInBarScan(Node * pNode, TemplateAttribute ** ppFoundButton, INT32 * pNumberOfButtons, const StringBase & BarName) { // is this a node we are looking for? if (pNode->IsAnAttribute()) // is an attrib { if (IS_A(pNode,TemplateAttribute)) // is a wix attrib { // does it have the correct bar name hidden in the question? if (BarName.CompareTo(((TemplateAttribute *)pNode)->GetQuestion()) == 0) { BOOL ok = TRUE; // get this buttons name String_256 ThisButton = ((TemplateAttribute *)pNode)->GetParam(); for (INT32 i = 0; ok && i < *pNumberOfButtons; i++) { // has this button name been seen before? if (ThisButton.CompareTo(ppFoundButton[i]->GetParam()) == 0) ok = FALSE; } // found a new button name if (ok && *pNumberOfButtons < MAX_BUTTONS_IN_A_BAR) { // add this button to the array ppFoundButton[*pNumberOfButtons] = (TemplateAttribute *)pNode; TRACE( _T("Found new button called ")); TRACE(ppFoundButton[*pNumberOfButtons]->GetParam()); TRACE( _T("\n")); // increase the number of buttons *pNumberOfButtons = *pNumberOfButtons + 1; } } } } else // look at this nodes children { Node * pChildNode = pNode->FindFirstChild(); while (pChildNode) { // ***recursive call*** if (!pChildNode->IsNodeHidden()) CountButtonsInBarScan(pChildNode, ppFoundButton, pNumberOfButtons, BarName); pChildNode = pChildNode->FindNext(); } } } /******************************************************************************************** > static void SliceHelper::BuildListOfNodesInBar(List * pList, Node * pNode, const StringBase & BarName) Author: Simon_Knight (Xara Group Ltd) Created: 30/9/99 Params: pList - List that is filled in of pointers to all the nodes pNode - The node to scan from. Looks at everything down from here. BarName - The bar we are looking for Returns: - Purpose: Builds a list (or extends an existing list) of ALL the wix attribute nodes that have this bar name hidden in the question. Errors: Caution is RECURSIVE ********************************************************************************************/ void SliceHelper::BuildListOfNodesInBar(List * pList, Node * pNode, const StringBase & BarName) { if (pNode->IsAnAttribute()) // looking for an attrib { if (IS_A(pNode,TemplateAttribute)) // that is a wix attrib { // that has the bar name in the question // the same as the one we are looking for if (BarName.CompareTo(((TemplateAttribute *)pNode)->GetQuestion()) == 0) { //add it to the list then NodeListItem * pItem = new NodeListItem(pNode); pList->AddTail(pItem); } } } else // find anything else interesting? { Node * pChildNode = pNode->FindFirstChild(); while (pChildNode) { // ***recursive call*** if (!pChildNode->IsNodeHidden()) BuildListOfNodesInBar(pList, pChildNode, BarName); pChildNode = pChildNode->FindNext(); } } } /******************************************************************************************** > static SGNameItem* SliceHelper::LookupNameGalleryItem(const StringBase& strName) Author: Simon_Knight (Xara Group Ltd) Created: 30/9/99 Params: strName - Name of the set from the name gallery that we are looking for Returns: a ptr to the name gallery item if it exists or NULL if it doesn't exist. Purpose: Used to find out if a button name has been used before. Or find the dimensions of a button. Errors: - ********************************************************************************************/ SGNameItem* SliceHelper::LookupNameGalleryItem(const StringBase & strName) { NameGallery * pNameGallery = NameGallery::Instance(); SGUsedNames* pNames = pNameGallery ? pNameGallery->GetUsedNames() : NULL; SGNameItem* pNameGalleryItem = pNames ? (SGNameItem*) pNames->GetChild() : NULL; String_256 str; // check all the name gallery items while (pNameGalleryItem) { pNameGalleryItem->GetNameText(&str); // if the name matches its your man if (strName.CompareTo(str) == 0) return pNameGalleryItem; // no then try the next one? pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } return NULL; } /******************************************************************************************** > static void SliceHelper::GetNextFreeButtonName(INT32 &butno, StringBase * pStr) Author: Simon_Knight (Xara Group Ltd) Created: 15/10/99 Params: butno - The number of the button to start looking from use zero to look for button1 first etc.. Gets filled in to the number of the free button found. pStr - This is set to the string of the button name if you bother to pass it a buffer in. Returns: Doesn't bother Purpose: Used to find the next button name that can be reused or created, that isn't going to interfere with what we have. Errors: - ********************************************************************************************/ void SliceHelper::GetNextFreeButtonName(INT32 &butno, StringBase * pStr) { String_256 TempButtonName; SGNameItem* pNGItem = NULL; SGNameItem* pNGItemExtender = NULL; // Matt 20/12/2000 // We can't just assume that because "Button1" doesn't exist that "Button1 Extender" doesn't // because we might have renamed either one !!! String_256 Postfix; // holds the "Extender" postfix Postfix.Load(_R(IDS_EXTENDER_POSTFIX)); String_256 TempButtonExtender; do { butno++; TempButtonName.MakeMsg(_R(IDS_BUTTONNAME), butno); pNGItem = SliceHelper::LookupNameGalleryItem(TempButtonName); // Construct the appropriate default extender name for this button TempButtonExtender = TempButtonName; TempButtonExtender += Postfix; pNGItemExtender = SliceHelper::LookupNameGalleryItem(TempButtonExtender); } while ((pNGItem && !pNGItem->IsEmpty()) || (pNGItemExtender && !pNGItemExtender->IsEmpty())); if (pStr) *pStr = TempButtonName; } /******************************************************************************************** > static Node * SliceHelper::FindNextOfClass( Node *pNode, Node * pLidNode, const class CCRuntimeClass * pClass, BOOL CheckThis = FALSE) Author: Simon_Knight (Xara Group Ltd) Created: 30/9/99 Params: pNode - The node to continue the search from pLidNode- Marks the end of the search, doesn't search beyond this node pClass - The class we are looking for e.g. CC_RUNTIME_CLASS(TextLine) CheckThis- Include or exclude the pNode in the search. Useful if it has just returned this node and you then want to find the next, not be presented with the same one again! Returns: a ptr to the next node of the desired type in the tree as it traverses it depth first. NULL if the search is complete. Purpose: Used to walk a tree repeatedly from any given branch, searching for nodes of a particular type. Use: To find all textlines in a layer pLayer and process them Node * pNode = SliceHelper::FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(TextLine)); while (pNode) { pNode-> whatever you like; pNode = SliceHelper::FindNextOfClass(pNode, pLayer, CC_RUNTIME_CLASS(TextLine)); } Errors: - ********************************************************************************************/ Node * SliceHelper::FindNextOfClass(Node *pNode, Node * pLidNode, const class CCRuntimeClass * pClass, BOOL CheckThis) { Node * pNextNode = NULL; BOOL tested = FALSE; Node * pPassedNode = CheckThis ? NULL : pNode; BOOL scanAttributes = FALSE; // Attribute nodes do NOT ever return CC_RUNTIME_CLASS(NodeAttribute). // if were scanning for these, then we need to do things slightly differently .... if (pClass == CC_RUNTIME_CLASS(NodeAttribute)) { scanAttributes = TRUE; } while (pNode != pLidNode || !tested) { tested = TRUE; // check this node as we move across if (!scanAttributes) { if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->GetRuntimeClass() == pClass) return pNode; } else { if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->IsAnAttribute ()) return pNode; } // while can go down do so pNextNode = pNode->FindFirstChild(); if (pNextNode && pNextNode->IsNodeHidden()) pNextNode = pNextNode->FindNextNonHidden(); while(pNextNode) { pNode = pNextNode; if (!scanAttributes) { // check this node as we move down if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->GetRuntimeClass() == pClass) return pNode; } else { // check this node as we move down if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->IsAnAttribute ()) return pNode; } pNextNode = pNode->FindFirstChild(); if (pNextNode && pNextNode->IsNodeHidden()) pNextNode = pNextNode->FindNextNonHidden(); } // cant go down and we are already at the top if (pNode == pLidNode) return NULL; // find next none hidden brother pNextNode = pNode->FindNextNonHidden(); if (pNextNode) pNode = pNextNode; else // no brothers so find an uncle { BOOL FoundUncle = FALSE; while (!FoundUncle) { pNextNode = pNode->FindParent(); if (pNextNode == pLidNode || !pNextNode) return NULL; pNode = pNextNode; pNextNode = pNode->FindNextNonHidden(); // found uncle if (pNextNode) { pNode = pNextNode; FoundUncle = TRUE; } } } } return NULL; } /******************************************************************************************** > BOOL SliceHelper::SelectObjectsInSet(const StringBase& strName, SelectScan::Change eNewState) Author: Simon_Maneggio (Xara Group Ltd) Created: 4/10/99 Returns: TRUE if it succeeded Params: strName - The set name to select nNewState - 1 selects, 0 deselects, -1 toggles Purpose: Wraps the scan functionality. Selects all items from the named set. ********************************************************************************************/ BOOL SliceHelper::SelectObjectsInSet(const StringBase& strName, SelectScan::Change eNewState) { SGNameItem* pItem = SliceHelper::LookupNameGalleryItem(strName); if (pItem == 0) return FALSE; SelectScan scanner(pItem, eNewState); scanner.Scan(); return TRUE; } /******************************************************************************************** > void SliceHelper::SelectAllSetsOfThisNode( NodeRenderableInk* pInk, String_256 & SetName, BOOL bShift ) Author: Karim_MacDonald (Xara Group Ltd) Created: 14/12/99 Returns: Params: pInk - The node to check SetName - The name of the set we are looking for. bShift - TRUE if the shift key is down (so it toggles) FALSE to shift -> select Purpose: Calls RecurseSelectAllSetsOfThisNode after looking up the tree for any non-selectable nodes. These are tested for by looking for a FALSE return from PromoteHitTestOnChildrenToMe. We do this because the user may try to select eg a NodeBevelController with a name on it, the selection slips through to its children who don't have the name directly applied. We must therefore find the node the user thought they were clicking on, and work on from there. See RecurseSelectAllSetsOfThisNode for more. ********************************************************************************************/ void SliceHelper::SelectAllSetsOfThisNode( NodeRenderableInk* pInk, String_256 & SetName, BOOL bShift ) { // go up the tree to find the highest non-hittest node above us. Node* pSelect = pInk; Node* pNext = pInk->FindParent(); while ( pNext != NULL && !pNext->IsLayer() && !pNext->PromoteHitTestOnChildrenToMe() ) { pSelect = pNext; pNext = pNext->FindParent(); } // ok, now make the *recursive* call to RecurseSelectAllSetsOfThisNode :-> RecurseSelectAllSetsOfThisNode(pSelect, SetName, bShift); } /******************************************************************************************** > void SliceHelper::RecurseSelectAllSetsOfThisNode(Node * pAttrs, String_256 & SetName, BOOL bShift) Author: Simon_Maneggio (Xara Group Ltd) (renamed by Karim from SelectAllSetsOfThisNode, 14/12/1999) Created: 4/10/99 Returns: TRUE if this bar name is used from this node down Params: pAttrs - The node to check from SetName - The name of the set we are looking for. bShift - TRUE if the shift key is down (so it toggles) FALSE to shift -> select Purpose: Looks at all template attributes applied to this node and its children. Sets the selection state of the any referred name sets according to the shift setting. ie. you pick a member of a set and it selects all members of that set. ie2. If it is a member of more than one set all those sets are selected. ********************************************************************************************/ void SliceHelper::RecurseSelectAllSetsOfThisNode(Node * pAttrs, String_256 & SetName, BOOL bShift) { if (pAttrs->IsAnAttribute() && IS_A(pAttrs,TemplateAttribute)) { if (SetName.CompareTo(((TemplateAttribute *)pAttrs)->GetParam()) != 0) { // this line tests if the name is a bar name rather than any old named thing that // the user may have added if (!((TemplateAttribute *)pAttrs)->GetQuestion().IsEmpty()) { SetName = ((TemplateAttribute *)pAttrs)->GetParam(); SliceHelper::SelectObjectsInSet(SetName, bShift ? SelectScan::TOGGLE : SelectScan::SELECT/*_EXCLUSIVE*/); } } } else { pAttrs = pAttrs->FindFirstChild(); while (pAttrs) { // recursive call!!! if (!pAttrs->IsNodeHidden()) RecurseSelectAllSetsOfThisNode(pAttrs, SetName, bShift); pAttrs = pAttrs->FindNext(); } } } /******************************************************************************************** > void SliceHelper::SelectAllSetsInRect(DocRect Rect, Spread* pSpread, SliceHelper::SelStateAction st = SelStateAction::SET) Author: Karim_MacDonald (Xara Group Ltd) , from Simon / JustinF's code Created: 12/10/1999 Inputs: Rect the Rectangle to act as a bounding box for the selection. Spread the spread on which to select the objects. st either CLEAR, SET, or TOGGLE, which is what should be done to the selection state of each object within the rectangle. Purpose: For each object wholly inside the supplied rectangle, selects that object and all other objects sharing any names belonging to that object. It will draw in all the EORed blobs of the objects that it selects/deselects. rewritten by Simon so that objects on locked layers do not get selected also more optimal. (st parameter etc added by JCF 1.11.94) SeeAlso: NodeRenderableInk::SelectAllInRect ********************************************************************************************/ void SliceHelper::SelectAllSetsInRect(const DocRect Rect, Spread* pSpread, SliceHelper::SelStateAction st) { // Make sure we are not being told garbage ERROR3IF(pSpread == NULL, "SliceHelper::SelectAllSetsInRect- NULL pSpread"); ERROR3IF(pSpread->FindFirstLayer() == NULL, "SliceHelper::SelectAllSetsInRect- no layer"); // process all objects on modifiable layers within the given bounding box. String_256 SetName; Layer* pLayer = pSpread->FindFirstLayer(); while (pLayer != NULL) { // proceed if the current layer is unlocked, visible and overlaps the given rect. if (!pLayer->IsLocked() && pLayer->IsVisible() && pLayer->GetBoundingRect().IsIntersectedWith(Rect)) { Range rng(pLayer->FindFirstChild(), NULL, RangeControl(TRUE, TRUE)); Node* pNode = rng.FindFirst(); while (pNode != NULL) { // make sure it is a Renderable Ink Node if (pNode->IsAnObject()) { // Ok, this object is unselected and renderable, // so now we can check if it is in the rect NodeRenderableInk* pInk = (NodeRenderableInk*) pNode; if (Rect.ContainsRect(pInk->GetBoundingRect())) { switch (st) { case CLEAR: // Karim MacDonald 22/10/1999 // TODO: replace this with code for Named-Set deselection. // the only current reason for this is completeness. pInk->DeSelect(TRUE); break; case SET: // although our Range object should only return unselected nodes, // we're busy selecting sets of nodes within the loop => what was // unselected at the start of the loop will often be selected by // this point - hence the double-check for selection. if (!pInk->IsSelected()) { SetName.Empty(); SliceHelper::SelectAllSetsOfThisNode(pInk, SetName, FALSE); if (SetName.IsEmpty()) pInk->Select(TRUE); } break; case TOGGLE: if (pInk->IsSelected()) { // Karim MacDonald 22/10/1999 // TODO: replace this with code for Named-Set deselection. // the only current reason for this is completeness. pInk->DeSelect(TRUE); } else { SetName.Empty(); SliceHelper::SelectAllSetsOfThisNode(pInk, SetName, FALSE); if (SetName.IsEmpty()) pInk->Select(TRUE); } break; default: ERROR3("SliceHelper::SelectAllSetsInRect- unknown SelStateAction"); return; } } } // get the next Node to consider. pNode = rng.FindNext(pNode); } } // check the next layer. pLayer = pLayer->FindNextLayer(); } // Update the selection cache GetApplication()->UpdateSelection(); } /******************************************************************************************** > String_256 SliceHelper::GetBarName(TemplateAttribute * pTemplAttrib) Author: Simon_Maneggio (Xara Group Ltd) JK Created: 4/10/99 Returns: The name of the bar Params: pTemplAttrib- The attrib node. Purpose: The bar name is stored as the question in the wix attrib. This fn extracts the bar name from the attrib, checking that it is a bar name and not any old string in there. If it isn't a bar identifier it returns an empty string. ********************************************************************************************/ String_256 SliceHelper::GetBarName(TemplateAttribute * pTemplAttrib) { String_256 BarName = pTemplAttrib->GetQuestion(); // must start with the word "Bar" if (BarName[0] != 'B' || BarName[1] != 'a' || BarName[2] != 'r') BarName.Empty(); return BarName; } /******************************************************************************************** > Node * SliceHelper::ReplaceAttrsInTree(UndoableOperation * pOp, List * pImportedAttrList, const String_256 & OldButtonName, const String_256 & OldBarName, const String_256 & NewButtonName, const String_256 & NewBarName, Node ** ppNodeFound) Author: Simon_Maneggio (Xara Group Ltd) JK Created: 21/9/00 Purpose: Scans the pImportedAttrList looking for OldButtonName and OldBarName. Replaces it with new versions of the template attribute. Returns a ptr to an example of a changed attr node Fills in an example of an old node into ppNodeFound if required ********************************************************************************************/ Node * SliceHelper::ReplaceAttrsInTree(UndoableOperation * pOp, List * pImportedAttrList, const String_256 & OldButtonName, const String_256 & OldBarName, const String_256 & NewButtonName, const String_256 & NewBarName, Node ** ppNodeFound) { Node * pExampleNode = NULL; NodeListItem * pItem = (NodeListItem *)pImportedAttrList->GetHead(); NodeListItem * pNextItem = NULL; while (pItem) { pNextItem = (NodeListItem *)pImportedAttrList->GetNext(pItem); if (OldBarName.CompareTo(((TemplateAttribute *)(pItem->pNode))->GetQuestion()) == 0 && OldButtonName.CompareTo(((TemplateAttribute *)(pItem->pNode))->GetParam()) == 0) { // found a matching attr // set the found node if required if (ppNodeFound && !*ppNodeFound) *ppNodeFound = pItem->pNode; if (NewBarName != OldBarName || NewButtonName != OldButtonName) { // add an attrib of the new type next to this if (!NewButtonName.IsEmpty()) // dont add empty named names { // define the new attrib with the required button/extender name and bar name if relevent TemplateAttribute* pAttr = new TemplateAttribute(String_256(TEXT("ObjectName")), NewBarName, NewButtonName); if (!pExampleNode) pExampleNode = pAttr; // add the new attrib to the tree if (pAttr) { pAttr->AttachNode(pItem->pNode, NEXT); // add undo info to hide this node? } } // remove the old attrib // it pointed to the out of date information pOp->DoHideNode(pItem->pNode, FALSE); } else if (!pExampleNode) pExampleNode = pItem->pNode; // remove this entry from this list pImportedAttrList->RemoveItem((NodeListItem *)pItem); delete pItem; } // look at the next item in the list pItem = pNextItem; } return pExampleNode; } // scans the list to see if name is used by a template attrib in the list BOOL SliceHelper::IsUniqueName(const String_256 & Name, List * pList) { NodeListItem * pExistingListItem = (NodeListItem *)pList->GetHead(); while (pExistingListItem) { if (Name.CompareTo(((TemplateAttribute *)(pExistingListItem->pNode))->GetParam()) == 0) return TRUE; pExistingListItem = (NodeListItem *)pList->GetNext(pExistingListItem); } return FALSE; } // extension to the NodeListItem to store other stuff needed in this MeshImportedLayersWithExistingButtonBars() fn class MeshNodeListItem : public NodeListItem { public: MeshNodeListItem( Node* WhichNode, Node* pNode2, INT32 type, INT32 level, BYTE flags, DocRect target, DocRect extender) { pNode = WhichNode; pNewNode = pNode2; ButtonClassification = type; StretchLevel = level; ExtendFlags = flags; rTarget = target; rExtender = extender; pExtender = NULL; pOldPropNode = NULL; };// initialise vars Node * pNewNode; Node * pExtender; Node * pOldPropNode; INT32 ButtonClassification; INT32 StretchLevel; BYTE ExtendFlags; DocRect rTarget; DocRect rExtender; }; /******************************************************************************************** > void SliceHelper::MeshImportedLayersWithExistingButtonBars(Node * pImportedLayer[5], UndoableOperation * pUndoableOp, BOOL Imported) Author: Simon_Maneggio (Xara Group Ltd) JK Created: 28/10/99 Params: pImportedLayer[5] - ptrs to the 5 (or less) nodes just imported in that represent the DEFAULT, MOUSE, CLICKED and SELECTED states (and now BACKBAR) in the imported document. These have to be merged with whatever is already in these layers proper. Imported nodes are added AFTER the existing nodes and each ptr points to the first node on the tree in each layer. All 5 ptrs must be supplied, they can individually be left NULL. pUndoableOp - This function must hide and move nodes so it must be part of an undoable op. Imported - Bool to tell if the bar is coming from an external file (TRUE) or copied/cloned from within this document (FALSE) so that the bar data can be scavinged. Purpose: Used when a drawing (.XAR, .WEB) is imported or dragged into an existing drawing, or cloned. Button names and bar names are rationalised and made unique, after the nodes themselves have already been merged onto the existing special layers. The function also has to mesh the stretching properties of any buttons that it tampers with providing new extenders to go with each new button defined. The extenders have to be uniquely named too and match with the correct button and the correct nodes of that button. Errors: This function Only works with a single item stretching in a button as it stores it as a string "Extender" which it recieves from the SliceHelper function FindTargetAndExtender(). ********************************************************************************************/ void SliceHelper::MeshImportedLayersWithExistingButtonBars(Node * pImportedLayer[5], UndoableOperation * pUndoableOp, BOOL Imported) { INT32 BarReplacedWithBar[MAX_IMPORTED_BARS]; // 255 bars the max to import into a document memset (BarReplacedWithBar, -1, sizeof(BarReplacedWithBar)); NameGallery* pNameGallery = NameGallery::Instance(); if (!pNameGallery) return; pNameGallery->FastUpdateNamedSetSizes(); // update the existing names in the gallery String_256 Postfix; // holds the "Extender" postfix Postfix.Load(_R(IDS_EXTENDER_POSTFIX)); NodeSetSentinel * pNodeSetSentinel = Document::GetSelected()->GetSetSentinel(); INT32 i; // general perpose use // the all important lists of Template attribute nodes in the tree List ExistingAttrList; List ImportedAttrList; List MeshList; // this one add lots more data too to rebuild the properties Node * pNode; Node * pParent; INT32 BarNo = 0; // used to store the unique bar number we have generated up to so far INT32 ButtonNo = 0; // used to store the unique button number we have generated up to so far //*** fill in the ExistingAttrList & the ImportedAttrList *** // fill the list of attrs that were in the tree before the import // and those that were in the tree after the import // look on each possible special rollover layers for (i = 0; i < 5; i++) if (pImportedLayer[i]) { // test to see if the first item imported is in fact the first item in the layer // implying that the whole lot has been imported pParent = pImportedLayer[i]->FindParent(); Node * pFirstNodeOnLayer = pParent->FindFirstChild(); if ( pFirstNodeOnLayer && pFirstNodeOnLayer != pImportedLayer[i]) { // there was stuff on this layer before pNode = SliceHelper::FindNextNameNode(pFirstNodeOnLayer, pImportedLayer[i]); while (pNode) { // add this node to the list NodeListItem * pItem = new NodeListItem(pNode); ExistingAttrList.AddTail(pItem); pNode = SliceHelper::FindNextNameNode(pNode, pImportedLayer[i]); } } // Add the attrs that have just been added to this layer pNode = SliceHelper::FindNextNameNode(pImportedLayer[i], pParent); while (pNode) { // add this node to the list NodeListItem * pItem = new NodeListItem(pNode); ImportedAttrList.AddTail(pItem); pNode = SliceHelper::FindNextNameNode(pNode, pParent); } } //*** Scan the imported attrs list *** NodeListItem * pNodeListItem = (NodeListItem *)ImportedAttrList.GetHead(); // walk the imported list to while (pNodeListItem) { String_256 OldButtonName = ((TemplateAttribute *)(pNodeListItem->pNode))->GetParam(); String_256 OldBarName = ((TemplateAttribute *)(pNodeListItem->pNode))->GetQuestion(); String_256 NewButtonName = OldButtonName; String_256 NewBarName = OldBarName; String_256 OldExtender = ""; String_256 NewExtender = ""; TRACEUSER( "Matt", _T("Found ")); TRACEUSER("Matt", OldButtonName); TRACEUSER( "Matt", _T("\n")); // is it a button or a backbar or an extender? INT32 type = 0; // a regular button / target if (OldBarName.IsEmpty()) type = 1; // an extender // look to the layer it is on is it layer[4] if (pImportedLayer[4]) { pParent = pImportedLayer[4]->FindParent(); pNode = pNodeListItem->pNode; while (!pNode->IsLayer()) pNode = pNode->FindParent(); if (pNode == pParent) type = 2; // a back bar } //*** generate a unique name for the button and the bar *** // make the bar name unique if (type != 1) // it is not an extender { INT32 ExistingBarno = SliceHelper::GetBarNumberFromBarName(OldBarName); ASSERT(ExistingBarno >=0); if (BarReplacedWithBar[ExistingBarno] == -1) { if (Imported) // if imported it has a known number to become! { BarNo = ExistingBarno + g_NoOfBarsBeforeImport + 1; NewBarName.MakeMsg(_R(IDS_BARNAME), BarNo); } else // find a bar name that hasn't been used in the existing drawing { BOOL AlreadyUsed = TRUE; do { BarNo++; NewBarName.MakeMsg(_R(IDS_BARNAME), BarNo); AlreadyUsed = FALSE; NodeListItem * pExistingListItem = (NodeListItem *)ExistingAttrList.GetHead(); while (pExistingListItem && !AlreadyUsed) { if (NewBarName.CompareTo(((TemplateAttribute *)(pExistingListItem->pNode))->GetQuestion()) == 0) AlreadyUsed = TRUE; pExistingListItem = (NodeListItem *)ExistingAttrList.GetNext(pExistingListItem); } } while (AlreadyUsed); } BarReplacedWithBar[ExistingBarno] = BarNo - 1; // remember this bar decision next time we find a bar of that bar number // the -1 is so we dont waste index zero of the array // Bar1 = location zero etc. like in the bar properties node } else NewBarName.MakeMsg(_R(IDS_BARNAME), BarReplacedWithBar[ExistingBarno] + 1); // scan the items selected in the name gallery // if we have selected only part of a button do not try to duplicate the whole thing BOOL PartOfButtonSelected = FALSE; if (!Imported) { SGNameItem* pNameGalleryItem = (SGNameItem*) pNameGallery->GetUsedNames()->GetChild(); while (pNameGalleryItem && !PartOfButtonSelected) { INT32 count = pNameGalleryItem->GetSelectedCount(); if ( count > 0 && count < pNameGalleryItem->GetObjectCount()) { // this named object is only partly selected String_256 TempExtenderName = ""; pNameGalleryItem->GetNameText(&TempExtenderName); if (OldButtonName.CompareTo(TempExtenderName) == 0) PartOfButtonSelected = TRUE; // has part of this set } pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } } if (PartOfButtonSelected) // only if we are not importing { String_256 Target = ""; if ( SliceHelper::FindTargetAndExtender( pNodeListItem->pNode->FindParent(), Target, OldExtender, 0) == 2) // ie we found an extneder { // delete these attrs from the tree ReplaceAttrsInTree(pUndoableOp, &ImportedAttrList, OldExtender, "", "", ""); } // delete these attrs from the tree ReplaceAttrsInTree(pUndoableOp, &ImportedAttrList, OldButtonName, OldBarName, "", ""); } else { // create a unique button name DocRect r; r.MakeEmpty(); MeshNodeListItem * pMeshData = new MeshNodeListItem(pNodeListItem->pNode, NULL, type, 0, 0, r, r); if (type == 2) { // back bars are just called BackBarX where X is the bar number NewButtonName.MakeMsg(_R(IDS_BACKBARNAME), BarReplacedWithBar[ExistingBarno]); BOOL AlreadyUsed = TRUE; do { AlreadyUsed = IsUniqueName(NewButtonName, &ExistingAttrList); if (AlreadyUsed) NewButtonName += _T("x"); }while (AlreadyUsed); } else { // buttons / targets need to find unique names not used before in the existing button list BOOL AlreadyUsed = TRUE; do { AlreadyUsed = IsUniqueName(NewButtonName, &ExistingAttrList); if (AlreadyUsed) { ButtonNo++; NewButtonName.MakeMsg(_R(IDS_BUTTONNAME), ButtonNo); } } while (AlreadyUsed); } //*** Read the property data for the button and store this data in the Mesh List *** // find out what the extender for this used to be String_256 Target = ""; pMeshData->StretchLevel = SliceHelper::FindTargetAndExtender( pNodeListItem->pNode->FindParent(), Target, OldExtender, 0, &(pMeshData->ExtendFlags), &(pMeshData->rTarget), &(pMeshData->rExtender) ); pMeshData->pOldPropNode = pNodeSetSentinel->FindPropertyNode(OldButtonName); // replace all the instances of attrs marking the old button in the tree with // attrs of the new button and bar. // Returns an example node into pMeshData->NewNode pMeshData->pNewNode = ReplaceAttrsInTree(pUndoableOp, &ImportedAttrList, OldButtonName, OldBarName, NewButtonName, NewBarName); // add the data about the button to the mesh list MeshList.AddTail(pMeshData); // add this new node data to the "existing" now that it has been placed in the tree // so that any new items will not think they can reuse this name NodeListItem * pNewItem = new NodeListItem(pMeshData->pNewNode); ExistingAttrList.AddTail(pNewItem); // found an extender for the button? if (type == 0 && !OldExtender.IsEmpty()) { Node ** ppButtonExtenderNode = &(pMeshData->pExtender); pMeshData = new MeshNodeListItem( NULL, NULL, 1, // its an extender 0, 0, r, r); // generate a unique extender name NewExtender = NewButtonName; NewExtender += Postfix; BOOL AlreadyUsed = TRUE; do { AlreadyUsed = IsUniqueName(NewExtender, &ExistingAttrList); if (AlreadyUsed) NewExtender += _T("x"); }while (AlreadyUsed); pMeshData->pOldPropNode = pNodeSetSentinel->FindPropertyNode(OldExtender); pMeshData->pNewNode = ReplaceAttrsInTree(pUndoableOp, &ImportedAttrList, OldExtender, "", NewExtender, "", &(pMeshData->pNode) ); if (pMeshData->pNewNode) { // add the data about the extender to the mesh list MeshList.AddTail(pMeshData); // let the button have a ptr to an example of an extender for it *ppButtonExtenderNode = pMeshData->pNewNode; // add this new node data to the "existing" now that it has been placed in the tree // so that any new items will not think they can reuse this name pNewItem = new NodeListItem(pMeshData->pNewNode); ExistingAttrList.AddTail(pNewItem); } else { // failled to find the extenders attr in the tree // we could be copying just part of a button so treat the last as a bit of a button // and treat this as though it had no extender delete pMeshData; } } } // we have deleted items from the list so start at the begining again pNodeListItem = (NodeListItem *)ImportedAttrList.GetHead(); } else // skipped an extender node so look at the next in the list pNodeListItem = (NodeListItem *)ImportedAttrList.GetNext(pNodeListItem); } //*** add the button properties to the sentinel from the data stored in the MeshDataList *** MeshNodeListItem * pMeshData = (MeshNodeListItem *)MeshList.GetHead(); while (pMeshData) { // delete the property it is based on as this is now longer used // since we created an updated version above if (pMeshData->pOldPropNode && ((NodeSetProperty*) pMeshData->pOldPropNode)->m_Imported) { // delete the attr in the sentinel that comes with the property Node * pAttr = pNodeSetSentinel->FindLastChild(); while (pAttr) { if (IS_A(pAttr, TemplateAttribute) && ((TemplateAttribute *)(pMeshData->pNode))->GetParam() == ((TemplateAttribute *)pAttr)->GetParam()) { pUndoableOp->DoHideNode(pAttr, FALSE); break; } pAttr = pAttr->FindPrevious(); } // delete the unused imported property pUndoableOp->DoHideNode(pMeshData->pOldPropNode, FALSE); } // set the new property switch (pMeshData->ButtonClassification) { case 0: // a normal button if (pMeshData->pExtender) // the level that says there is a extender for this { CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(), ((TemplateAttribute *)(pMeshData->pNewNode))->GetQuestion(), TRUE, TRUE, FALSE, pMeshData->ExtendFlags, ((TemplateAttribute *)(pMeshData->pExtender))->GetParam(), FALSE, &(pMeshData->rTarget), &(pMeshData->rExtender), pUndoableOp, ((NodeSetProperty *)pMeshData->pOldPropNode)); } else // create none stretching attribs for the button CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(), ((TemplateAttribute *)(pMeshData->pNewNode))->GetQuestion(), TRUE, FALSE, FALSE, 0, TEXT(""), FALSE, NULL, NULL, pUndoableOp, ((NodeSetProperty *)pMeshData->pOldPropNode)); break; case 1: // an extender CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(), TEXT(""), FALSE, FALSE, FALSE, 0, TEXT(""), FALSE, NULL, NULL, pUndoableOp, ((NodeSetProperty *)pMeshData->pOldPropNode)); break; case 2: // a back bar CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(), ((TemplateAttribute *)(pMeshData->pNewNode))->GetQuestion(), FALSE, pMeshData->ExtendFlags != 0, TRUE, pMeshData->ExtendFlags, TEXT(""), FALSE, NULL, NULL, pUndoableOp, ((NodeSetProperty *)pMeshData->pOldPropNode)); break; } pMeshData = (MeshNodeListItem *)MeshList.GetNext(pMeshData); } //*** Sort out the sentinel node which contains node properties and template attribs of any buttons *** // remove the import tags from the properties that the import added to the tree // we should have already taken all the data we need from them // this should only be stuff that doesn't extend left in this set if (Imported) { pNode = pNodeSetSentinel->FindLastChild(); Node * pNextNode = NULL; while (pNode) { // scan the sentinel for imported properties that are marked "imported" // NB scans are done from bottom to top to find the last added items // which will be where the import left them pNextNode = pNode->FindPrevious(); if (IS_A(pNode, NodeSetProperty) && ((NodeSetProperty*) pNode)->m_Imported) { TRACE(((NodeSetProperty*) pNode)->GetName()); TRACE( _T(" imported and not deleted\n")); ((NodeSetProperty*) pNode)->m_Imported = FALSE; } pNode = pNextNode; } } //*** Sort out the bar properties *** // fill up the bar property data in the sentinel // we dont have to do this on an import as the import pushed this data in for us // and we made sure that the bar names / numbers used by the import matched the // unique new ones we generated. // however if we are cloning or duplicating we need to expand the bar props if (!Imported) { // get the ptr to the bar properties node NodeBarProperty * pNodeBarProperty = (NodeBarProperty*) ((NodeSetSentinel *)pNodeSetSentinel)->FindBarProperty(); NodeBarProperty* pBarPropertyCopy = NULL; // our local copy of the props for (i = 0; i < MAX_IMPORTED_BARS; i++) { if (BarReplacedWithBar[i] != -1) { // found a new bar that we have put in // copy the old bar data into the new bar data as the new is a copy of the old if (pBarPropertyCopy == NULL) ALLOC_WITH_FAIL(pBarPropertyCopy, ((NodeBarProperty*) pNodeBarProperty->SimpleCopy()), pUndoableOp); if (pBarPropertyCopy) { if ((INT32)(pBarPropertyCopy->HowMany()) <= BarReplacedWithBar[i]) { UINT32 tempuint = 0; while (((INT32)(pBarPropertyCopy->HowMany()) <= BarReplacedWithBar[i]) && tempuint != UINT_MAX) tempuint = pBarPropertyCopy->Add(pBarPropertyCopy->Bar(i)); } else pBarPropertyCopy->Bar(BarReplacedWithBar[i]) = pBarPropertyCopy->Bar(i); } } } // tidy up the bar properties hiding the old properties and replacing with the new // if the new exists if (pBarPropertyCopy) { pBarPropertyCopy->AttachNode(pNodeSetSentinel, LASTCHILD); pUndoableOp->DoHideNode(pNodeBarProperty, FALSE); } } //*** Tidy up and exit *** // the work is done tidy up the three lists NodeListItem * pListItem = (NodeListItem *)ExistingAttrList.GetHead(); NodeListItem * pNextListItem = NULL; while (pListItem) { pNextListItem = (NodeListItem *)ExistingAttrList.GetNext(pListItem); // remove this entry from this list ExistingAttrList.RemoveItem((NodeListItem *)pListItem); delete pListItem; pListItem = pNextListItem; } pListItem = (NodeListItem *)ImportedAttrList.GetHead(); pNextListItem = NULL; while (pListItem) { pNextListItem = (NodeListItem *)ImportedAttrList.GetNext(pListItem); // remove this entry from this list ImportedAttrList.RemoveItem((NodeListItem *)pListItem); delete pListItem; pListItem = pNextListItem; } pListItem = (NodeListItem *)MeshList.GetHead(); pNextListItem = NULL; while (pListItem) { pNextListItem = (NodeListItem *)MeshList.GetNext(pListItem); // remove this entry from this list MeshList.RemoveItem((NodeListItem *)pListItem); delete pListItem; pListItem = pNextListItem; } SliceHelper::ValidateNodeSetSentinel(); // update the name gallery when we are finished pNameGallery->FastUpdateNamedSetSizes(); // Ensure that the Trigger sets are correctly flagged... SliceHelper::EnsureTriggerInfo(); } /******************************************************************************************** > BOOL SliceHelper::BarNameExists(Node * pNode, String_256 &BarName, Node * pLid = NULL) Author: Simon_Maneggio (Xara Group Ltd) JK Created: 4/10/99 Returns: TRUE if this bar name is used from this node down Params: pNode - The node to check from BarName - The name of the bar we are looking for. pButtonName - If provided fills in the button name found in a bar. Purpose: Just a quick check to see if this bar name is used anywhere here. Used to find unused bar names. // Modified non recursive version written 17/9/00 (sjk) // pnode is the node to start looking from and pLid is the node to stop looking when we reach it // if pnode == plid or plid == 0 then it scans the entire branch defined by pnode ********************************************************************************************/ BOOL SliceHelper::BarNameExists(Node * pNode, String_256 &BarName, Node * pLid) { if (pLid == NULL) pLid = pNode; Node * pNextNode = FindNextNameNode(pNode, pLid); while (pNextNode) { if (BarName.CompareTo(((TemplateAttribute *)pNextNode)->GetQuestion()) == 0) return TRUE; pNextNode = FindNextNameNode(pNextNode, pLid); } return FALSE; } /******************************************************************************************** > INT32 SliceHelper::FindTargetAndExtender(Node * pStartNode, String_256 & Target, String_256 & Extender, INT32 RequiredLevel, BYTE *pExtenderFlags) Author: Simon_Maneggio (Xara Group Ltd) JK Created: 11/11/99 Returns: The level of compliance found of target and extender so far Params: pStartNode - The node to check from Target - Fills this in with the button that gets moved ( levels 1 & 2) Extender - The set that triggers the target to extend (level 2) RequiredLevel- Don't return for levels less than this pExtenderFlags- If passed in and an extender is found this is filled in with the way the button extends. Purpose: When you want to find out what extends what from a button level (ie you have a node). ********************************************************************************************/ INT32 SliceHelper::FindTargetAndExtender(Node * pStartNode, String_256 & Target, String_256 & Extender, INT32 RequiredLevel, BYTE *pExtenderFlags, DocRect *pTargetRect, DocRect *pExtenderRect) { DocRect rTarget, rExtender; INT32 ret = 0; Node * pNode = SliceHelper::FindNextOfClass(pStartNode, pStartNode, CC_RUNTIME_CLASS(TemplateAttribute)); while (pNode && ret < 2) { INT32 FoundLevel = 0; // if pNode extends NodeSetProperty* pSetProp = Document::GetSelected()->GetSetSentinel()->FindPropertyNode(((TemplateAttribute *)pNode)->GetParam()); NamedStretchProp* pProp = NULL; if (pSetProp) pProp = (NamedStretchProp*) pSetProp->GetProperty(NamedStretchProp::nIndex); if (pProp && !pProp->GetTriggers().empty()) { FoundLevel = 2; // get this extender //Extender = first name in the list of sets that extends target; Extender = pProp->GetTriggers().front().m_strSet; // record the exteding flags if (pExtenderFlags) *pExtenderFlags = pProp->GetStretchType(); rExtender = pProp->GetRefUnionTriggerBounds(); } else if (!GetBarName((TemplateAttribute *)pNode).IsEmpty()) { // found a bar entry FoundLevel = 1; } if (FoundLevel > RequiredLevel && FoundLevel > ret) { Target = ((TemplateAttribute *)pNode)->GetParam(); ret = FoundLevel; if (pProp) rTarget = pProp->GetRefTargetBounds(); } pNode = SliceHelper::FindNextOfClass(pNode, pStartNode, CC_RUNTIME_CLASS(TemplateAttribute)); } if (pTargetRect) *pTargetRect = rTarget; if (pExtenderRect) *pExtenderRect = rExtender; return ret; } /******************************************************************************************** > BOOL SliceHelper::CreatePropertiesForSet( const String_256& SetName, const String_256& BarName, BOOL isSlice, BOOL Stretches, BOOL IsBackBar, BYTE StretchFlags, const String_256& StretchedBy, BOOL DontOverWrite, const String_256& strRefTriggerSet, UndoableOperation* pOp) Author: Simon_Knight (Xara Group Ltd) Created: 11/11/99 Params: SetName - The name of the set that you wish to create properties for BarName - The name of the bar if it is to belong to one, every thing but a button should ahve this empty isSlice - Is it a slice Stretches - Does it stretch IsBackBar - Is it a back bar - if so it will find its own buttons and set its triggers StretchFlags- If it stretches how does it stretch StretchedBy - What stretches it. This is currently limited to just one set name for the purposes of this function. It could be extended to be a list later in the day. DontOverWrite- If this is TRUE it cuts out if the property already exists strRefBoundsSet - the set, if not empty, whose reference bounds for triggers/targets should be copied into the newly created property. pOp - Ptr to the undoable op Returns: TRUE if it succeeded Purpose: If you create a named set these days you need to create properties in the node set sentinel too so that they are stored somewhere that can be saved out, undone, etc. So if you have made any in your code this is a useful little number. ********************************************************************************************/ BOOL SliceHelper::CreatePropertiesForSet( const String_256& SetName, const String_256& BarName, BOOL isSlice, BOOL Stretches, BOOL IsBackBar, BYTE StretchFlags, const String_256& StretchedBy, BOOL DontOverWrite, DocRect *pTargetRect, DocRect *pExtenderRect, UndoableOperation* pOp, NodeSetProperty * pExampleProp) { NodeSetSentinel * pSentinel = Document::GetSelected()->GetSetSentinel(); if (!pSentinel) return FALSE; // find the property node for the setname NodeSetProperty* pOldSetProp =pSentinel->FindPropertyNode(SetName); // hide the old properties if they existed if (pOldSetProp) { if (DontOverWrite) return TRUE; // it exists and that is good enough for us // need a ptr to an op to hide a node ASSERT(pOp); pOp->DoHideNode(pOldSetProp, FALSE); TRACE( _T("Deleting property set of ")); TRACE(SetName); TRACE( _T("\n")); } // create the new properties NodeSetProperty* pNewSetProp = pSentinel->CreatePropertyNode(SetName); if (pNewSetProp == 0 || !pNewSetProp->CreateDefaults()) return FALSE; // debug info TRACE( _T("Creating property set of ")); TRACE(SetName); TRACE( _T(" ")); NamedSliceProp* pNamedSliceProp = !isSlice ? new NamedSliceProp(SetName, isSlice) : 0; if (!pNamedSliceProp && !isSlice) return FALSE; NamedStretchProp* pNamedStretchProp = new NamedStretchProp(SetName, StretchFlags, Stretches); if (!pNamedStretchProp) { if (pNamedSliceProp) delete pNamedSliceProp; return FALSE; } // add the export props from the example if passed one // this will remember that the user wanted it as a jpeg and saved as whatever if (pExampleProp) { NamedExportProp * pExampleExportProp = (NamedExportProp *) pExampleProp->GetProperty(NamedExportProp::nIndex); if (pExampleExportProp) { NamedExportProp * pNamedExportProp = new NamedExportProp(*pExampleExportProp); if (pNamedExportProp) { SGNameProp* pOld = pNewSetProp->SetProperty(pNamedExportProp); if (pOld) delete pOld; } } } // debugging info if (pNamedSliceProp) TRACE( _T("with slices ")); if (Stretches) TRACE( _T("with stretching ")); // add an extend trigger if (pNamedStretchProp) { if (!StretchedBy.IsEmpty() && !IsBackBar) { pNamedStretchProp->AddTrigger(StretchedBy); // Fix up the trigger and target reference rectangles. if (pTargetRect && pExtenderRect) { TRACE( _T("button")); pNamedStretchProp->SetRefTargetBounds(*pTargetRect); pNamedStretchProp->SetRefUnionTriggerBounds(*pExtenderRect); } } else if (IsBackBar) // build a list of all the members of the bar to make triggers for the back bar { NameGallery * pNameGallery = NameGallery::Instance(); SGUsedNames* pNames = pNameGallery ? pNameGallery->GetUsedNames() : NULL; if (pNameGallery && pNames) { TRACE( _T("BackBar")); // scan to get data about everything pNameGallery->FastUpdateNamedSetSizes(FALSE); INT32 BarNo = GetBarNumberFromBarName(BarName); DocRect RefUnionTriggers; DocRect BackBarBounds; RefUnionTriggers.MakeEmpty(); BackBarBounds.MakeEmpty(); String_256 FoundSetName; // reset all the names SGNameItem * pNameGalleryItem = (SGNameItem*) pNames->GetChild(); while (pNameGalleryItem) { if (pNameGalleryItem->m_BarNumber == BarNo )//&& !pNameGalleryItem->IsABackBar()) { pNameGalleryItem->GetNameText(&FoundSetName); if (FoundSetName.CompareTo(SetName) == 0) { // found yourself the back bar BackBarBounds = pNameGalleryItem->GetSetBounds(); } else { // found a button of this bar RefUnionTriggers = RefUnionTriggers.Union(pNameGalleryItem->GetSetBounds()); pNamedStretchProp->AddTrigger(FoundSetName); } } pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } pNamedStretchProp->SetRefTargetBounds(pTargetRect ? *pTargetRect : BackBarBounds); pNamedStretchProp->SetRefUnionTriggerBounds(pExtenderRect ? *pExtenderRect : RefUnionTriggers); } } } // replace the slice and stretch properties if (pNamedSliceProp) { SGNameProp* pOld = pNewSetProp->SetProperty(pNamedSliceProp); if (pOld) delete pOld; } if (pNamedStretchProp) { SGNameProp* pOld = pNewSetProp->SetProperty(pNamedStretchProp); if (pOld) delete pOld; } TRACE( _T("\n")); // Create a hide node action to hide the node when we undo // this node was attached by the fn "NodeSetProperty* pNewSetProp = pSentinel->CreatePropertyNode(SetName);" // which is 107 lines above HideNodeAction* UndoHideNodeAction; HideNodeAction::Init(pOp, pOp->GetUndoActions(), //&UndoActions, pNewSetProp, TRUE, // Include subtree size ( Action**)(&UndoHideNodeAction)); // now we have added the property we should also add the standard attrib // to the node set sentinel too Node * pSentinelChild = pSentinel->FindFirstChild(); BOOL Found = FALSE; while (pSentinelChild && !Found) { if (pSentinelChild->IsAnAttribute()) { if (SetName.CompareTo(((TemplateAttribute *)pSentinelChild)->GetParam()) == 0) { if ((BarName.CompareTo(((TemplateAttribute *)pSentinelChild)->GetQuestion()) == 0)) Found = TRUE; else { pOp->DoHideNode(pSentinelChild, TRUE); // hide the near duplicate pSentinelChild = NULL; // end the search } } } if (pSentinelChild) pSentinelChild = pSentinelChild->FindNext(); } if (!Found) { TemplateAttribute* pExtraAttr = new TemplateAttribute(String_256(TEXT("ObjectName")), BarName, SetName); if (!pExtraAttr) return TRUE; // add the attrib into the tree pExtraAttr->AttachNode(pSentinel, FIRSTCHILD); // Create a hide node action to hide the node when we undo HideNodeAction* UndoHideNodeAction; HideNodeAction::Init(pOp, pOp->GetUndoActions(), //&UndoActions, pExtraAttr, TRUE, // Include subtree size ( Action**)(&UndoHideNodeAction)); } return TRUE; } /******************************************************************************************** > BOOL SliceHelper::MakeTriggerLikeExample(const String_256& NewButtonName, const String_256& ButtonName, const String_256* pExclude) Author: Simon_Knight (Xara Group Ltd) Created: 22/11/99 Params: NewButtonName - The button name that you wish to extend the things ButtonName - The name of button as an example of what you wish this would stretch things like pExclude - ptr to button to exclude looking at Returns: TRUE if it succeeded, FALSE if we had a problem Purpose: Adds NewButtonName to the lists of triggers that ButtonName belongs to. This is used by the bars thatwant all the buttons to stretch the same background bar. ********************************************************************************************/ BOOL SliceHelper::MakeTriggerLikeExample(const String_256& NewButtonName, const String_256& ButtonName, const String_256* pExclude) { NodeSetSentinel* pSentinel = Document::GetSelected()->GetSetSentinel(); if (!pSentinel) return FALSE; // find the property node for the setname NodeSetProperty* pSetProp = (NodeSetProperty*) FindNextOfClass(pSentinel, pSentinel, CC_RUNTIME_CLASS(NodeSetProperty)); NamedStretchProp* pProp = NULL; // walk the list of node set properties while (pSetProp) { pProp = (NamedStretchProp*) pSetProp->GetProperty(NamedStretchProp::nIndex); BOOL ok = TRUE; if (pExclude) { // exclude a particular name ok = pSetProp->GetName().CompareTo(*pExclude); } if (ok && pProp && !pProp->GetTriggers().empty()) { // walk the list of triggers looking for the button name // if we find it add the new trigger to the list std::list::iterator p = std::find(pProp->GetTriggers().begin(), pProp->GetTriggers().end(), ButtonName); // found ButtonName? if (p != pProp->GetTriggers().end()) { // make sure we haven't added this before p = std::find(pProp->GetTriggers().begin(), pProp->GetTriggers().end(), NewButtonName); if (p == pProp->GetTriggers().end()) pProp->AddTrigger(NewButtonName); // add this new trigger } } // find the next node set property pSetProp = (NodeSetProperty*) FindNextOfClass(pSetProp, pSentinel, CC_RUNTIME_CLASS(NodeSetProperty)); } return TRUE; } /******************************************************************************************** > DocRect SliceHelper::BoundingNodeSize(Node * pNode) Author: Simon_Knight (Xara Group Ltd) Created: 17/3/00 Params: pNode - the node that you want to know how big it is Returns: its size Purpose: Get bounds was returning the acturate "how big is it" This fn treats text that is of a size and the same font as being the same size ignoring decsenders and ascenders. * WARNING * It does this by examining the char size of the first char in each line and getting a min and max height. It uses the width from getbounds(). So if fonts change mid way then descenders become a problem again. But this is faster than checking each char. ********************************************************************************************/ DocRect SliceHelper::BoundingNodeSize(Node * pNode) { if (!pNode->IsBounded()) return DocRect(0,0,0,0); DocRect rBounds = ((NodeRenderableBounded*) pNode)->GetBoundingRect(); if (IS_A(pNode,TextStory)) { DocRect rTextBounds; Node * pLid = pNode; pNode = SliceHelper::FindNextOfClass(pLid, pLid, CC_RUNTIME_CLASS(TextChar)); if (pNode) { if (((TextChar*) pNode)->GetMetricsRectBounds(&rTextBounds)) { rBounds.hi.y = rTextBounds.hi.y; rBounds.lo.y = rTextBounds.lo.y; } } else return DocRect(0,0,0,0); // no text chars should return empty while (pNode) { pNode = SliceHelper::FindNextOfClass(pNode, pLid, CC_RUNTIME_CLASS(TextLine)); if (pNode) { pNode = SliceHelper::FindNextOfClass(pNode, pLid, CC_RUNTIME_CLASS(TextChar)); if (pNode) { if (((TextChar*) pNode)->GetMetricsRectBounds(&rTextBounds)) { rBounds.hi.y = max(rTextBounds.hi.y, rBounds.hi.y); rBounds.lo.y = min(rTextBounds.lo.y, rBounds.lo.y); } } } } } else if (IS_A(pNode,NodeShadowController)) // ignore the size of the shadow and just get me the size of the other bits { rBounds.MakeEmpty(); pNode = pNode->FindFirstChild(); while (pNode) { if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden() && !IS_A(pNode, NodeShadow)) rBounds = rBounds.Union(BoundingNodeSize(pNode)); pNode = pNode->FindNext(); } } else if (IS_A(pNode,NodeBevelController)) // ignore the size of the bevel and just get me the size of the other bits { rBounds.MakeEmpty(); INT32 BevWidth = ((NodeBevelController *)pNode)->m_bOuter ? ((NodeBevelController *)pNode)->m_Indent : 0; pNode = pNode->FindFirstChild(); while (pNode) { if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden() && !IS_A(pNode, NodeBevel) && !IS_A(pNode, NodeBevelBegin)) { rBounds = rBounds.Union(BoundingNodeSize(pNode)); } pNode = pNode->FindNext(); } if (!rBounds.IsEmpty()) rBounds.Inflate(BevWidth); // inflate the other bits' size by the bevel width } else if (IS_A(pNode,NodeContourController)) // ignore the size of the contour and just get me the size of the other bits { rBounds.MakeEmpty(); // -ve width means outer contour!!?! // this is crazy but ignore inners so negate it to get the actual width increase INT32 Width = -((NodeContourController *)pNode)->GetWidth(); pNode = pNode->FindFirstChild(); while (pNode) { if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden() && !IS_A(pNode, NodeContour)) { rBounds = rBounds.Union(BoundingNodeSize(pNode)); } pNode = pNode->FindNext(); } if (Width > 0) rBounds.Inflate(Width); // inflate the size of the other bits' by the width of the contour } return rBounds; } /******************************************************************************************** > INT32 SliceHelper::GetBarNumberFromBarName(const String_256 & BarName) Author: Simon_Knight (Xara Group Ltd) Created: 6/4/00 Returns: the bar number (-1 for not in a bar) Purpose: Converts the bar name into a bar number ********************************************************************************************/ INT32 SliceHelper::GetBarNumberFromBarName(const String_256 & BarName) { PTSTR pszMark = NULL; if (BarName.Length() > 3) return camStrtol( ((const TCHAR*)BarName) + 3, &pszMark, 10 ) - 1; return -1; } /***************************************/ /******************************************************************************************** > TemplateAttribute * SliceHelper::FindFirstSetNodeBelongsTo(Node * pNode) Author: Simon_Knight (Xara Group Ltd) Created: 12/5/00 Returns: Purpose: returns the fist attrib that may apply to a node ********************************************************************************************/ TemplateAttribute * SliceHelper::FindFirstSetNodeBelongsTo(Node * pNode) { while (!pNode->IsLayer()) { // test children for wix attrib Node * pChild = pNode->FindFirstChild(); while (pChild) { if (pChild->IsAnAttribute() && IS_A(pChild,TemplateAttribute)) return (TemplateAttribute *) pChild; pChild = pChild->FindNext(); } pNode = pNode->FindParent(); } return NULL; } /******************************************************************************************** > String_256 SliceHelper::GetSetNameFromAttrib(Node * pNode) Author: Simon_Knight (Xara Group Ltd) Created: 12/5/00 Returns: The set name from the attrib empty if it is not an attrib Purpose: returns the fist attrib that may apply to a node ********************************************************************************************/ String_256 SliceHelper::GetSetNameFromAttrib(Node * pNode) { if (IS_A(pNode,TemplateAttribute)) return ((TemplateAttribute *)pNode)->GetParam(); else return ""; } /******************************************************************************************** > TextStory * SliceHelper::FindNextTextStoryToSync( TextStory * pLastStory, Node * pLid, TextStory * pMasterStory, String_256 & ButtonName) Author: Simon_Knight (Xara Group Ltd) Created: 12/5/00 Returns: The next text story that should be edited if the master has been edited Purpose: scans for text stories that are of the named set and match the criteria to edit when the master is edited. ********************************************************************************************/ TextStory * SliceHelper::FindNextTextStoryToSync( TextStory * pLastStory, Node * pLid, TextStory * pMasterStory, const String_256 & ButtonName, const String_256 & MasterText, BOOL AnyText) { Node * pStart = pLastStory; if (!pStart) pStart = pLid; Node * pNode = NULL; BOOL ok; TemplateAttribute * pAttrib = NULL; do { // find the next text story pNode = FindNextOfClass(pStart, pLid, CC_RUNTIME_CLASS(TextStory)); ok = TRUE; if (!pNode) ok = FALSE; if (ok && pNode == pMasterStory) ok = FALSE; if (ok) { // is it in the right set? pAttrib = FindFirstSetNodeBelongsTo(pNode); if (!pAttrib) ok = FALSE; // no set } if (ok) { // the name of the set must match and the question should have a string that implies it is a bar // not just any old user named set if (ButtonName.CompareTo(pAttrib->GetParam()) != 0 || pAttrib->GetQuestion().IsEmpty()) { ok = FALSE; // wrong set // check other set names for this text story while (!ok && pAttrib) { // look at brothers of this template attrib pAttrib = (TemplateAttribute *) pAttrib->FindNext(CC_RUNTIME_CLASS(TemplateAttribute)); if (pAttrib && ButtonName.CompareTo(pAttrib->GetParam()) == 0 && !pAttrib->GetQuestion().IsEmpty()) ok = TRUE; } } } // does its text match? if (ok && !AnyText) { if (MasterText != ((TextStory *)pNode)->GetStoryAsString()) ok = FALSE; } pStart = pNode; } while (!ok && pNode); return (TextStory *) pNode; } BOOL SliceHelper::TextStoriesHaveSameText (TextStory * pStory1, TextStory * pStory2) { Node * pTextLine1 = pStory1->FindFirstChild(CC_RUNTIME_CLASS(TextLine)); Node * pTextLine2 = pStory2->FindFirstChild(CC_RUNTIME_CLASS(TextLine)); while (pTextLine1 && pTextLine2) { if (!TextLinesHaveSameText((TextLine *)pTextLine1, (TextLine *)pTextLine2)) return FALSE; pTextLine1 = pTextLine1->FindNext(CC_RUNTIME_CLASS(TextLine)); pTextLine2 = pTextLine2->FindNext(CC_RUNTIME_CLASS(TextLine)); } if (!pTextLine1 && !pTextLine2) return TRUE; // both empty! return FALSE; // one empty } BOOL SliceHelper::TextLinesHaveSameText (TextLine * pLine1, TextLine * pLine2) { // TextNode * pVTN1 = pLine1->FindFirstVTN(); // TextNode * pVTN2 = pLine2->FindFirstVTN(); Node * pChar1 = pLine1->FindFirstChild(CC_RUNTIME_CLASS(TextChar)); Node * pChar2 = pLine2->FindFirstChild(CC_RUNTIME_CLASS(TextChar)); while (pChar1 && pChar2) { if (((TextChar *)pChar1)->GetUnicodeValue() != ((TextChar *)pChar2)->GetUnicodeValue()) return FALSE; pChar1 = pChar1->FindNext(CC_RUNTIME_CLASS(TextChar)); pChar2 = pChar2->FindNext(CC_RUNTIME_CLASS(TextChar)); } if (!pChar1 && !pChar2) return TRUE; // both empty! return FALSE; // one empty } /******************************************************************************************** > BOOL SliceHelper::SyncTextStories (TextStory * pStory, TextStory * pMaster, UndoableOperation * pOp) Author: Simon_Knight (Xara Group Ltd) Created: 12/5/00 Returns: TRUE if it changes pStory Purpose: Changes the characters in the story pStory to match those in pMaster. In this process it attempts to minimise the amount of undo information required. Calls the fn bellow SyncTextLines() ********************************************************************************************/ BOOL SliceHelper::SyncTextStories (TextStory * pStory, TextStory * pMaster, UndoableOperation * pOp) { Node * pTextLine = pStory->FindFirstChild(CC_RUNTIME_CLASS(TextLine)); Node * pMasterTextLine = pMaster->FindFirstChild(CC_RUNTIME_CLASS(TextLine)); BOOL ret = FALSE; while (pTextLine || pMasterTextLine) { pStory->ReleaseCached(); pOp->DoInvalidateRegion(Document::GetSelectedSpread(), pStory->GetBoundingRect()); // only existing line if (pTextLine && pMasterTextLine) { SyncTextLines((TextLine*)pTextLine, (TextLine*)pMasterTextLine, pOp); ret = TRUE; pTextLine = pTextLine->FindNext(CC_RUNTIME_CLASS(TextLine)); pMasterTextLine = pMasterTextLine->FindNext(CC_RUNTIME_CLASS(TextLine)); } else if (!pTextLine) { // master has added a line do so and then sync pTextLine = TextLine::CreateEmptyTextLine(pStory, LASTCHILD); SyncTextLines((TextLine*)pTextLine, (TextLine*)pMasterTextLine, pOp); ret = TRUE; pTextLine = NULL; pMasterTextLine = pMasterTextLine->FindNext(CC_RUNTIME_CLASS(TextLine)); } else if (!pMasterTextLine) { // master has deleted a line // get the caret onto the top story to make sure it is not deleted out of the story if (pStory->GetCaret()->FindParent() == pTextLine) pStory->MoveCaretToCharacter(pStory->FindFirstVTN(), PREV); TextLine * pTemp = (TextLine *)pTextLine; pTextLine = pTextLine->FindNext(CC_RUNTIME_CLASS(TextLine)); pOp->DoHideNode(pTemp, TRUE); ret = TRUE; } } if (ret) { pStory->FormatAndChildren(pOp); // We also need to invalidate the region of the story //pOp->DoInvalidateNodeRegion((NodeRenderableBounded*) pStory, TRUE, FALSE); pStory->ReleaseCached(); pOp->DoInvalidateRegion(Document::GetSelectedSpread(), pStory->GetBoundingRect()); } return ret; } BOOL SliceHelper::SyncTextLines (TextLine * pLine1, TextLine * pLine2, UndoableOperation * pOp) { Node * pChar1 = pLine1->FindFirstChild(CC_RUNTIME_CLASS(TextChar)); Node * pChar2 = pLine2->FindFirstChild(CC_RUNTIME_CLASS(TextChar)); BOOL ok = TRUE; TRACEUSER( "Matt", _T("\n#############################################\nStart: ")); // find the first 2 different chars while (ok && pChar1 && pChar2) { if (((TextChar *)pChar1)->GetUnicodeValue() != ((TextChar *)pChar2)->GetUnicodeValue()) ok = FALSE; if (ok) { TRACEUSER( "Matt", _T("%c="), ((TextChar *)pChar1)->GetUnicodeValue()); pChar1 = pChar1->FindNext(CC_RUNTIME_CLASS(TextChar)); pChar2 = pChar2->FindNext(CC_RUNTIME_CLASS(TextChar)); } } // Check if the two strings were the same - no point continuing! if (ok && !pChar1 && !pChar2) return TRUE; // they were the same! // Matt 15/12/2000 // If the master has run out of next chars before the slave, then we should just remove everything from the end of the slave line and return if (ok && pChar1 && !pChar2) { while (pChar1) { Node * pNodeNext = pChar1->FindNext(CC_RUNTIME_CLASS(TextChar)); TRACEUSER( "Matt", _T("Deleting From Slave %c\n"), ((TextChar *)pChar1)->GetUnicodeValue()); pOp->DoHideNode(pChar1, TRUE); pChar1 = pNodeNext; } return TRUE; } Node * pChar1e = pLine1->FindLastChild(CC_RUNTIME_CLASS(TextChar)); Node * pChar2e = pLine2->FindLastChild(CC_RUNTIME_CLASS(TextChar)); ok = TRUE; BOOL RequireDel = FALSE; BOOL RequireAdd = FALSE; TRACEUSER( "Matt", _T("\nEnd: ")); // find the last 2 different chars while (ok && pChar1e && pChar2e) { if (((TextChar *)pChar1e)->GetUnicodeValue() != ((TextChar *)pChar2e)->GetUnicodeValue()) ok = FALSE; if (ok && pChar1e == pChar1) { ok = FALSE; RequireAdd = TRUE; } if (ok && pChar2e == pChar2) { ok = FALSE; RequireDel = TRUE; } if (ok) { TRACEUSER( "Matt", _T("%c="), ((TextChar *)pChar1e)->GetUnicodeValue()); pChar1e = pChar1e->FindPrevious(CC_RUNTIME_CLASS(TextChar)); pChar2e = pChar2e->FindPrevious(CC_RUNTIME_CLASS(TextChar)); } } TRACEUSER( "Matt", _T("\n")); BOOL AddChar2e = FALSE; // Matt 15/12/2000 // Bodge-Case Flagging to make it add the correct no. of chars after concatenating two lines if (!ok && !pChar1 && pChar1e) { if (pChar1e->FindNext(CC_RUNTIME_CLASS(TextChar))) { TRACEUSER( "Matt", _T("\nFlagging AddChar2e\n")); AddChar2e = TRUE; } } // add chars just before pchar1 of the range of text chars between pchar2 and pchar2e Node * pNode = pChar2; Node * pDest = pChar1; AttachNodeDirection TailAttachDirection = PREV; if (!pDest) { // add the chars onto the end // put it before the EOFL node pDest = pLine1->FindLastChild(CC_RUNTIME_CLASS(EOLNode)); if (!pDest) { // failling that slap it on the end pDest = pLine1; TailAttachDirection = LASTCHILD; } } if (!RequireDel) RequireDel = pChar1e && pChar1 && pChar1e->FindNext(CC_RUNTIME_CLASS(TextChar)) != pChar1; if (!RequireAdd) RequireAdd = pChar2e && pChar2e->FindNext(CC_RUNTIME_CLASS(TextChar)) != pChar2; if (RequireDel) TRACEUSER( "Matt", _T("Requires a del\n")); if (RequireAdd) TRACEUSER( "Matt", _T("Requires an add\n")); ok = TRUE; while (RequireAdd && pNode && ok) { // this it the last time? ok = pNode != pChar2e; // Make a new text char node TextChar * pTheCopy = NULL; ALLOC_WITH_FAIL(pTheCopy, (new TextChar()) , pOp); pTheCopy->SetUnicodeValue(((TextChar *)pNode)->GetUnicodeValue()); TRACEUSER( "Matt", _T("Adding %c\n"), ((TextChar *)pNode)->GetUnicodeValue()); // insert the node under the destination pOp->DoInsertNewNode((NodeRenderableBounded *)pTheCopy, pDest, TailAttachDirection, TRUE, // Do Invalidate region FALSE, // Dont Clear the selections FALSE, // Dont select this object FALSE); // Dont normalise attribs // attach the next node after this one TailAttachDirection = NEXT; pDest = pTheCopy; pNode = pNode->FindNext(CC_RUNTIME_CLASS(TextChar)); } // delete the text chars between pchar1 & pchar1e pNode = pChar1; BOOL hiddenall = FALSE; if (RequireDel) while (!hiddenall) { if (pNode == pChar1e) hiddenall = TRUE; if (pNode) { Node * pNodeNext = pNode->FindNext(CC_RUNTIME_CLASS(TextChar)); TRACEUSER( "Matt", _T("Deleting %c\n"), ((TextChar *)pNode)->GetUnicodeValue()); pOp->DoHideNode(pNode, TRUE); pNode = pNodeNext; } else hiddenall = TRUE; } // Matt 15/12/2000 // If it was necessary to flag above that pChar2e and all following chars should be added, then do so... if (AddChar2e && pChar2e) { AttachNodeDirection TailAttachDirection = PREV; Node * pDestination = pLine1->FindLastChild(CC_RUNTIME_CLASS(EOLNode)); Node * pNode = pChar2e->FindNext(CC_RUNTIME_CLASS(TextChar)); if (!pDestination) { pDestination = pLine1; TailAttachDirection = LASTCHILD; } while (pNode) { // Make a new text char node TextChar * pTheCopy = NULL; ALLOC_WITH_FAIL(pTheCopy, (new TextChar()) , pOp); pTheCopy->SetUnicodeValue(((TextChar *)pNode)->GetUnicodeValue()); TRACEUSER( "Matt", _T("Extra-Adding %c\n"), ((TextChar *)pTheCopy)->GetUnicodeValue()); // insert the node under the destination pOp->DoInsertNewNode((NodeRenderableBounded *)pTheCopy, pDestination, TailAttachDirection, TRUE, // Do Invalidate region FALSE, // Dont Clear the selections FALSE, // Dont select this object FALSE); // Dont normalise attribs // attach the next node after this one TailAttachDirection = NEXT; pDestination = pTheCopy; pNode = pNode->FindNext(CC_RUNTIME_CLASS(TextChar)); } } TRACEUSER( "Matt", _T("Done\n")); return TRUE; } /******************************************************************************************** > BOOL SliceHelper::OnTextStoryChanged(TextStory * pMasterStory, UndoableOperation * pOp, ObjChangeParam * pObjChange) Author: Simon_Knight (Xara Group Ltd) Created: 12/5/00 Returns: TRUE if any changes were made Purpose: Call this if a master text may have been edited. It tests to see if the text is a master text. Finds slave text stories and syncs these with the master text. ********************************************************************************************/ BOOL SliceHelper::OnTextStoryChanged(TextStory * pMasterStory, UndoableOperation * pOp, ObjChangeParam * pObjChange, const String_256 & MasterText) { TemplateAttribute * pAttrib = SliceHelper::FindFirstSetNodeBelongsTo(pMasterStory); // the attrib is not a bar attrib? while (pAttrib && pAttrib->GetQuestion().IsEmpty()) { // look at brothers of this template attrib pAttrib = (TemplateAttribute *) pAttrib->FindNext(CC_RUNTIME_CLASS(TemplateAttribute)); } if (!pAttrib) return FALSE; String_256 MasterSetName = SliceHelper::GetSetNameFromAttrib((Node *)pAttrib); if (MasterSetName.IsEmpty()) return FALSE; Spread * pSpread = Document::GetSelectedSpread(); CaretNode * pCaret = pMasterStory->GetCaret(); BOOL CaretSelected = pCaret->IsSelected(); Node * pStory = SliceHelper::FindNextTextStoryToSync(NULL, pSpread, pMasterStory, MasterSetName, MasterText); BOOL ret = FALSE; while (pStory) { if (pStory != pMasterStory) { if (!pObjChange || ((TextStory *)pStory)->GetCaret()->AllowOp(pObjChange)) { SliceHelper::SyncTextStories((TextStory *)pStory, pMasterStory, pOp); ret = TRUE; } } pStory = SliceHelper::FindNextTextStoryToSync((TextStory *)pStory, pSpread, pMasterStory, MasterSetName, MasterText); } pCaret->SetSelected(CaretSelected); return ret; } /******************************************************************************************** > BOOL PurgeUseOfSetName(const StringBase & SetName, UndoableOperation* pOp) Author: Simon_Knight (Xara Group Ltd) Created: 21/5/00 Returns: TRUE if any changes were made Purpose: "SetName" should be a new clean set there should be no old references to this "SetName" in any extend lists remove them if there are any. find the property node for the setname ********************************************************************************************/ BOOL SliceHelper::PurgeUseOfSetName(const StringBase & SetName, UndoableOperation* pOp, const String_256 * pReplacementName) { NodeSetSentinel* pSentinel = Document::GetSelected()->GetSetSentinel(); if (!pSentinel) return FALSE; NodeSetProperty* pSetProp = (NodeSetProperty*) FindNextOfClass(pSentinel, pSentinel, CC_RUNTIME_CLASS(NodeSetProperty)); NamedStretchProp* pProp = NULL; BOOL ret = FALSE; // walk the list of node set properties while (pSetProp) { pProp = (NamedStretchProp*) pSetProp->GetProperty(NamedStretchProp::nIndex); BOOL ok = TRUE; if (ok && pProp && !pProp->GetTriggers().empty()) { // walk the list of triggers looking for the set name // if we find it remove this trigger from the list std::list::iterator p = std::find(pProp->GetTriggers().begin(), pProp->GetTriggers().end(), SetName); // found SetName? if (p != pProp->GetTriggers().end()) { NodeSetProperty* pNewSetProp = (NodeSetProperty*) pSetProp->SimpleCopy(); if (pNewSetProp) { ((NamedStretchProp*) pNewSetProp->GetProperty(NamedStretchProp::nIndex))->RemoveTrigger(SetName); if (pReplacementName) ((NamedStretchProp*) pNewSetProp->GetProperty(NamedStretchProp::nIndex))->AddTrigger(*pReplacementName); // add the prop into the tree pNewSetProp->AttachNode(pSetProp, NEXT); // Create a hide node action to hide the node when we undo HideNodeAction* UndoHideNodeAction; HideNodeAction::Init(pOp, pOp->GetUndoActions(), //&UndoActions, pNewSetProp, TRUE, // Include subtree size ( Action**)(&UndoHideNodeAction)); // delete the original pOp->DoHideNode(pSetProp, TRUE); // hide the near duplicate // carry on the search from here pSetProp = pNewSetProp; ret = TRUE; } } } // find the next node set property pSetProp = (NodeSetProperty*) FindNextOfClass(pSetProp, pSentinel, CC_RUNTIME_CLASS(NodeSetProperty)); } return ret; } /******************************************************************************************** > DocRect SliceHelper::ScanForSetSizeExcluding (const String_256 & IncludeSet, const String_256 & ExcludeSet) Author: Simon_Knight (Xara Group Ltd) Created: 22/6/00 Returns: The bounding rect of the set "IncludeSet" taking all members of "IncludeSet" that do NOT belong in "ExcludeSet" aswell. Purpose: Caculating clean set sizes for extending buttons ********************************************************************************************/ DocRect SliceHelper::ScanForSetSizeExcluding (const String_256 & IncludeSet, const String_256 & ExcludeSet) { DocRect r; r.MakeEmpty(); // scan the tree looking for name attribs // find one and the size of the parent should be added to the bounds of the set Spread * pSpread = Document::GetSelectedSpread(); if (!pSpread) return r; Node * pNode = NULL; Node * pTop = pSpread; Node * pNewNode = NULL; DocRect TempRect; TempRect.MakeEmpty(); BOOL CleanNode = TRUE; // scan from the first layer all through the layers since they are brothers of this layer pNode = SliceHelper::FindNextOfClass(pTop, pTop, CC_RUNTIME_CLASS(TemplateAttribute)); // for each marker in the tree while (pNode) { BOOL Siblings = TRUE; do { if (IncludeSet.CompareTo(((TemplateAttribute *)pNode)->GetParam()) == 0) TempRect = SliceHelper::BoundingNodeSize(pNode->FindParent()); if (ExcludeSet.CompareTo(((TemplateAttribute *)pNode)->GetParam()) == 0) CleanNode = FALSE; pNewNode = SliceHelper::FindNextOfClass(pNode, pTop, CC_RUNTIME_CLASS(TemplateAttribute)); Siblings = pNewNode && pNewNode->FindParent() == pNode->FindParent(); pNode = pNewNode; } while (Siblings); if (CleanNode && !TempRect.IsEmpty()) r = r.Union(TempRect); CleanNode = TRUE; TempRect.MakeEmpty(); } return r; } /******************************************************************************************** > BOOL SliceHelper::BarExistsOnLayer(const String_256 &BarName, const String_256 &Layer) Author: Simon_Knight (Xara Group Ltd) Created: 26/6/00 Returns: TRUE if an element of the bar is found on the layer, FALSE if not found Purpose: Fast scan for existance ********************************************************************************************/ BOOL SliceHelper::BarExistsOnLayer(const String_256 &BarName, const String_256 &Layer) { Node * pLayer = SliceHelper::FindLayerCalled(Layer); if (!pLayer) return FALSE; Node * pNode = pLayer; do { pNode = FindNextOfClass(pNode, pLayer, CC_RUNTIME_CLASS(TemplateAttribute)); if (pNode && BarName.CompareTo(((TemplateAttribute *)pNode)->GetQuestion()) == 0) return TRUE; } while (pNode); return FALSE; } // find out if the rect r intersects with any bars (sjk 27/6/00) void SliceHelper::BarNameInRect(DocRect r, String_256 *pBarName) { NameGallery * pNameGallery = NameGallery::Instance(); if (!pNameGallery) return; SGUsedNames* pNames = pNameGallery->GetUsedNames(); if (!pNames) return; SGNameItem * pNameGalleryItem = (SGNameItem*) pNames->GetChild(); while (pNameGalleryItem) { if (pNameGalleryItem->m_BarNumber >= 0 && r.IsIntersectedWith(pNameGalleryItem->GetSetBounds())) { pBarName->MakeMsg(_R(IDS_BARNAME), pNameGalleryItem->m_BarNumber + 1); return; } pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } } // add an action to show this Layer undoably (sjk 6/7/00) void SliceHelper::ShowLayer(BOOL Visible, Layer * pLayer, Spread * pSpread, UndoableOperation * pUndoOp) { // dont put in the undo action if we have done nothing if (pLayer->IsVisible() == Visible) return; // Make layer visible/invisible undoably OpLayerGalParam Param(LAYER_VISIBLE, pSpread); Param.NewState = Visible; Param.pLayer = pLayer; LayerStateAction::Init(pUndoOp, pUndoOp->GetUndoActions(), Param); } // uses the name gallery data to work out if the selection // consists of whole sets or not. We need to know if we have a partial set in the selection // returns 0 = contains a parts of a set // 1 = contains all of the set // 2 = selection not part of a set at all // sjk 7/7/00 INT32 SliceHelper::DoesSelectionOnlyContainCompleteSets() { NameGallery * pNameGallery = NameGallery::Instance(); if (!pNameGallery) return 2; pNameGallery->FastUpdateNamedSetSizes(); // Ensure timely info in NameGallery SGUsedNames* pNames = pNameGallery->GetUsedNames(); if (!pNames) return 2; SGNameItem * pNameGalleryItem = (SGNameItem*) pNames->GetChild(); INT32 ret = 2; // default as none of any set INT32 count = 0; while (pNameGalleryItem) { count = pNameGalleryItem->GetSelectedCount(); if ( count > 0 && count < pNameGalleryItem->GetObjectCount()) { return 0; // has part of this set } else if (count > 0) ret = 1; // has all of this set then pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } return ret; } // Small function to scan the tree looking for a named object // pNode - the start of the scan // pLid - the node that defines the branch to look down // SetName - the name of the set we are looking for // return TRUE if it is found, false otherwise // sjk (18/9/00 or there abouts) BOOL SliceHelper::SetUsedInTree(Node * pNode, const String_256 &SetName, Node * pLid) { if (pLid == NULL) pLid = pNode; pNode = SliceHelper::FindNextNameNode(pNode, pLid); while (pNode) { if (SetName.CompareTo(((TemplateAttribute *)pNode)->GetParam()) == 0) return TRUE; pNode = SliceHelper::FindNextNameNode(pNode, pLid); } return FALSE; } // deletes the attribute and the properties in the node set sentinel void SliceHelper::DeleteUnusedReferences(const String_256 &SetName, UndoableOperation * pOp) { NodeSetSentinel * pNodeSetSentinel = Document::GetSelected()->GetSetSentinel(); if (!pNodeSetSentinel) return; // remove all properties of the deleted node Node * pNode = pNodeSetSentinel->FindPropertyNode(SetName); if (pNode) pOp->DoHideNode(pNode, FALSE); // deleted bar members should be got rid of completely // remove the attribs from the sentinel pNode = pNodeSetSentinel->GetNameAttr(SetName); if (pNode) pOp->DoHideNode(pNode, FALSE); } // a controller has been added to the tree (such as a bevel controller) // move any names from normal nodes onto the controller BOOL SliceHelper::AddNamesToController (UndoableOperation * pOp, Node * pCtrlr) { Node * pNode = pCtrlr->FindFirstChild(); while (pNode) { if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden()) { Node * pAttr = NULL; do { // find any name attrs on this node pAttr = SliceHelper::FindNextNameNode(pNode, pNode); // does this already exist on the controller? Node * pExistingAttr = NULL; Node * pTemp = NULL; if (pAttr) { for (pTemp = pCtrlr->FindFirstChild(); pTemp != NULL && pExistingAttr == NULL; pTemp = pTemp->FindNext() ) { if (pTemp->IsAnObjectName()) { if (((TemplateAttribute *) pTemp)->GetParam() == ((TemplateAttribute *) pAttr)->GetParam()) pExistingAttr = pTemp; } } pTemp = SliceHelper::FindNextNameNode(pAttr, pNode); // didn't exist on controller so add it if (!pExistingAttr) { TemplateAttribute* pNewAttr = new TemplateAttribute(String_256(TEXT("ObjectName")), ((TemplateAttribute *) pAttr)->GetQuestion(), ((TemplateAttribute *) pAttr)->GetParam()); // add the new attrib to the tree if (pNewAttr) { pNewAttr->AttachNode(pCtrlr, FIRSTCHILD); // Create a hide node action to hide the node when we undo HideNodeAction* UndoHideNodeAction; HideNodeAction::Init(pOp, pOp->GetUndoActions(), //&UndoActions, pNewAttr, TRUE, // Include subtree size ( Action**)(&UndoHideNodeAction)); } } // did exist so just delete the duplicate pOp->DoHideNode(pAttr, FALSE); pAttr = pTemp; } }while (pAttr != NULL); } pNode = pNode->FindNext(); } return TRUE; } // reverse of the above operation // factorises the name attribs down from the controller // controller MUST be deleted after this function is called BOOL SliceHelper::RemoveNamesFromController (UndoableOperation * pOp, Node * pCtrlr) { Node * pNode = pCtrlr->FindFirstChild(); while (pNode) { // found a name attribute if (pNode->IsAnObjectName()) { // add this to all proper nodes that are children of the controller Node * pTemp = NULL; for (pTemp = pCtrlr->FindFirstChild(); pTemp != NULL; pTemp = pTemp->FindNext() ) { // add it to all nodes excluding attributes, bevels and contours if (!pTemp->IsNodeHidden() && !pTemp->IsAnAttribute() && !pTemp->IsABevel() && !pTemp->IsAContour() && !IS_A(pTemp, NodeBevelBegin)) { // add the attr node to this child TemplateAttribute* pAttr = new TemplateAttribute(String_256(TEXT("ObjectName")), ((TemplateAttribute *) pNode)->GetQuestion(), ((TemplateAttribute *) pNode)->GetParam()); // add the new attrib to the tree if (pAttr) { pAttr->AttachNode(pTemp, FIRSTCHILD); // Create a hide node action to hide the node when we undo HideNodeAction* UndoHideNodeAction; HideNodeAction::Init(pOp, pOp->GetUndoActions(), //&UndoActions, pAttr, TRUE, // Include subtree size ( Action**)(&UndoHideNodeAction)); } } } // remove this from the controller pTemp = pNode; pNode = pNode->FindNext(); pOp->DoHideNode(pTemp, FALSE); } else pNode = pNode->FindNext(); } return TRUE; } /******************************************************************************************** > BOOL SliceHelper::ModifySelectionToContainWholeButtonElements() Author: Priestley (Xara Group Ltd) Created: 6/11/00 Returns: TRUE all the time... ********************************************************************************************/ BOOL SliceHelper::ModifySelectionToContainWholeButtonElements() { // Get a pointer to the NameGallery, and make sure it is up-to-date NameGallery *pNameGallery = NameGallery::Instance(); if (!pNameGallery) return TRUE; pNameGallery->FastUpdateNamedSetSizes(); //Get the first entry in the 'UsedNames' field of the NameGallery SGNameItem *pNameGalleryItem = (SGNameItem*) pNameGallery->GetUsedNames()->GetChild(); // Build up a list of all the nodes used in the current selection. We will prune this list at every opportunity // in order to minimise the search space... SelRange* pSelRng = GetApplication()->FindSelection(); RangeControl rangecontrol = pSelRng->GetRangeControlFlags(); rangecontrol.PromoteToParent = TRUE; pSelRng->Range::SetRangeControl(rangecontrol); pSelRng->Update(); List *pSelList = pSelRng->MakeListOfNodes(FALSE); bool unchanged = true; // Assume that no changes will be made... while (pNameGalleryItem) { INT32 CurrentSelection = pNameGalleryItem->GetSelectedCount(); // If the named set is fully selected or not selected at all - there's no need to consider it further, otherwise... if ((CurrentSelection > 0) && (CurrentSelection != pNameGalleryItem->GetObjectCount())) { // Then we are interested in this named set - it is not fully selected, but it does appear // in our current selection (obviously) so we need to determine: // a) Is this named set a button bar element? // b) If so, are all elements on one layer selected? // c) What is the GDP of Bolivia, given that carrots are 50p/lb? // d) How is an oxbow lake formed? (see, you thought you'd never need to know that) String_256 StrName; StrName.Empty(); pNameGalleryItem->GetNameText(&StrName); TemplateAttribute* pAttr; NodeListItem *pItem; Node *pNode; // For each node in the list, check if it is a member of the current named set... for (pItem = (NodeListItem *)pSelList->GetHead(); pItem; pItem = (NodeListItem *)pSelList->GetNext(pItem)) { // Retrieve the current node from the NodeListItem... pNode = pItem->pNode; for (pAttr = (TemplateAttribute*)pNode->FindFirstAttr(&Node::IsAnObjectName); pAttr; pAttr = (TemplateAttribute*)pAttr->FindNextAttr(&Node::IsAnObjectName)) { // This may seem a redundant check - but it's not! When we find that all of one layer has been selected // we select all of the layer and update the NameGallery - if we check within this 'for' loop, then // we no longer need to do computation when all of the named set is already selected... if (!pNameGalleryItem->IsAllSelected()) { if ((StrName == pAttr->GetParam()) && (!(pAttr->GetQuestion().IsEmpty()))) { // Then this node is a member of this named set AND it is a button bar element... // Now, we want to know if there are any other nodes which are NOT in the current selection // which form part of this named set and are on this node's layer. If not, select all of // this named set as that is what the user (probably) meant to do... Layer *pLayer = (Layer *)pNode->FindParent(CC_RUNTIME_CLASS(Layer)); // What layer is the current node on? bool AllLayerSelected = true; // Assume all the layer is selected // - NOW THE (POTENTIALLY) SLOW BIT - // We need to go through every node in the current node's layer, checking if it is NOT SELECTED // AND if it is a member of the current name gallery item. As soon as both cases are true, then // we know we should not auto-select all of the named set and can exit quickly... Node * pTempNode = FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(TemplateAttribute)); while (pTempNode && AllLayerSelected) { // If this Node is part of a bar and has the same name as our name gallery item, then we need to consider it further... if (((TemplateAttribute *)pTempNode)->GetQuestion() && ((TemplateAttribute *)pTempNode)->GetParam() == StrName) { // Check if we are dealing with a controller node... if (IS_A(pTempNode->FindParent(), NodeShadowController) || IS_A(pTempNode->FindParent(), NodeContourController) || IS_A(pTempNode->FindParent(), NodeBevelController)) { // If so, check its children... TRACEUSER( "Matt", _T("\nController Node found - looking at its children")); Node * pChild = pTempNode->FindParent()->FindFirstChild(); while (pChild && AllLayerSelected) { if (!pChild->IsAnAttribute() && !pChild->IsNodeHidden() && !IS_A(pChild, NodeShadow) && !IS_A(pChild, NodeContour) && !IS_A(pChild, NodeBevel) && !IS_A(pChild, NodeBevelBegin) && !IS_A(pChild, NodeContourController) && !IS_A(pChild, NodeBevelController)) { // Then consider this child... Is it unselected - if so then we have found a matching node which is unselected when it should have been! if (!pChild->IsSelected()) { AllLayerSelected = false; } } pChild = pChild->FindNext(); } } else { if (!pTempNode->FindParent()->IsSelected()) { // Then we should check if this node's parent is selected or not... AllLayerSelected = false; } } } pTempNode = FindNextOfClass(pTempNode, pLayer, CC_RUNTIME_CLASS(TemplateAttribute)); } if (AllLayerSelected) { // Save the original selection (if it hasn't already been saved) if (unchanged) { SliceHelper::SaveSelection(); unchanged = false; } // Select ALL LAYERS of this named set... TRACEUSER( "Matt", _T("ALL of %s on layer selected\n"),(TCHAR *)StrName); SelectScan scanner(pNameGalleryItem, SelectScan::SELECT); scanner.Scan(); pNameGallery->FastUpdateNamedSetSizes(); } // END of slow bit... } } } } } // Get the next 'UsedName' from the NameGallery pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } // Karim 04/12/2000 - unset PromoteToParent, so we don't corrupt the app's sel-range. rangecontrol.PromoteToParent = FALSE; pSelRng->Range::SetRangeControl(rangecontrol); pSelRng->Update(); // Tidy up after myself... (Now there's a first - I won't make a habit of it) pSelList->DeleteAll(); delete pSelList; pSelList = 0; if (!unchanged) { // Then our procedure has altered the selection, so we'd better update it! GetApplication()->FindSelection()->Update(); } return TRUE; } /******************************************************************************************** > static void SliceHelper::SaveSelection() Author: Priestley (Xara Group Ltd) Created: 9/11/2000 Purpose: Save the current selection to m_pSelNodeList ********************************************************************************************/ void SliceHelper::SaveSelection() { if (m_pSelNodeList) { m_pSelNodeList->DeleteAll(); delete m_pSelNodeList; m_pSelNodeList = 0; } // Find the current selection and store it as a list of NodeListItems for later use... SelRange* pSelRng = GetApplication()->FindSelection(); m_pSelNodeList = pSelRng->MakeListOfNodes(FALSE); } /******************************************************************************************** > static void OpMenuExport::RestoreSelection() Author: Priestley (Xara Group Ltd) Created: 9/11/2000 Purpose: Restores the current selection to that given in m_pSelNodeList ********************************************************************************************/ void SliceHelper::RestoreSelection() { if (m_pSelNodeList) { SelRange* pSelRng = GetApplication()->FindSelection(); NodeRenderableInk::DeselectAll(FALSE, FALSE); for (NodeListItem* pItem = (NodeListItem*) m_pSelNodeList->GetHead(); pItem != 0; pItem = (NodeListItem*) m_pSelNodeList->GetNext(pItem)) { if (pItem->pNode != 0) { pItem->pNode->SetSelected(TRUE); } } m_pSelNodeList->DeleteAll(); delete m_pSelNodeList; m_pSelNodeList = 0; RangeControl rangecontrol = pSelRng->GetRangeControlFlags(); rangecontrol.PromoteToParent = FALSE; pSelRng->Range::SetRangeControl(rangecontrol); pSelRng->Update(); } } // Matt 18/12/2000 // Quick routine to ensure that the NodeSetSentinel is correct - if it is not then this routine will // attempt to correct it as much as possible... void SliceHelper::ValidateNodeSetSentinel() { // Get the NodeSetSentinel for this document NodeSetSentinel * pNodeSetSentinel = Document::GetSelected()->GetSetSentinel(); // Ensure that each NodeSetProperty which is NOT hidden has a corresponding UNHIDDEN TemplateAttribute... NodeSetProperty * pNodeSetProperty = (NodeSetProperty *) pNodeSetSentinel->FindFirstChild(CC_RUNTIME_CLASS(NodeSetProperty)); while (pNodeSetProperty) { if (!pNodeSetProperty->IsNodeHidden()) { BOOL ok = TRUE; TemplateAttribute * pTemplateAttribute = (TemplateAttribute *) pNodeSetSentinel->FindFirstChild(CC_RUNTIME_CLASS(TemplateAttribute)); while (ok && pTemplateAttribute) { String_256 templateattributename = pTemplateAttribute->GetParam(); String_256 nodesetpropertyname = pNodeSetProperty->GetName(); // If the templateattribute is both unhidden and is for the current nodesetproperty then flag it! if (!pTemplateAttribute->IsNodeHidden() && (templateattributename == nodesetpropertyname)) { ok = FALSE; } pTemplateAttribute = (TemplateAttribute *) pTemplateAttribute->FindNext(CC_RUNTIME_CLASS(TemplateAttribute)); } // At this point, if ok is TRUE then we have scanned all templateattributes and not found a matching one for our nodesetproperty - so make one !!! if (ok) { TRACEUSER( "Matt", _T("\nNodeSetProperty ")); TRACEUSER("Matt", pNodeSetProperty->GetName()); TRACEUSER( "Matt", _T(" doesn't have a matching TemplateAttribute!!")); String_256 ButtonName = pNodeSetProperty->GetName(); String_256 BarName = ""; // Right, now we know all we need to make a new TemplateAttribute to repair the NodeSetSentinel except which bar it belongs to - let's find out... // If there is no MouseOff layer then we don't care... String_256 mouseoffstring(_R(IDS_ROLLOVER_DEFAULT)); Spread * tempspread = Document::GetSelectedSpread(); TemplateAttribute * temptemplate = (TemplateAttribute *) SliceHelper::FindNextNameNode(tempspread, tempspread); BOOL templatefound = FALSE; while (temptemplate && !templatefound) { if (temptemplate->GetParam() == ButtonName) { BarName = temptemplate->GetQuestion(); templatefound = TRUE; } temptemplate = (TemplateAttribute *) SliceHelper::FindNextNameNode(temptemplate, tempspread); } // Make a new TemplateAttribute and add it into the tree... TemplateAttribute* pAttr = new TemplateAttribute(String_256(TEXT("ObjectName")), BarName, ButtonName); if (pAttr) { pAttr->AttachNode(pNodeSetProperty, NEXT); } } } pNodeSetProperty = (NodeSetProperty *) pNodeSetProperty->FindNext(CC_RUNTIME_CLASS(NodeSetProperty)); } } /******************************************************************************************** > static void SliceHelper::BuildListOfNodesInButton(List * pList, Layer * pLayer, const StringBase & ButtonName) Author: Priestley (Xara Group Ltd) Created: 20/12/2000 Params: pList - List that gets filled with pointers to all the nodes pLayer - The layer we are interested in ButtonName - The button we are looking for Returns: - Purpose: Scans all of pLayer, building a list of all nodes which are part of ButonName - if a NodeShadowController is encountered, we add its children instead in order to get the correct bounding box info... ********************************************************************************************/ void SliceHelper::BuildListOfNodesInButton(List * pList, Layer * pLayer, const StringBase & ButtonName) { ERROR3IF(!pLayer || !ButtonName || !pList, "Please don't call SliceHelper::BuildListOfNodesInButton with null params!"); // Starting at pLayer, look for all TemplateAttributes with the correct name attached... Node * pNode = FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(TemplateAttribute)); while (pNode) { // If this Node is part of a bar AND has the correct name, then add it to the list... if (((TemplateAttribute *)pNode)->GetQuestion() && (((TemplateAttribute *)pNode)->GetParam() == ButtonName)) { // Add this node to the list unless it is a nodeshadowcontroller - if it is, add all of its children individually (except the nodeshadow child) if (IS_A(pNode->FindParent(), NodeShadowController)) { TRACEUSER( "Matt", _T("\nNodeShadowController found in BuildListOfNodesInButton - replacing list entries with its children")); Node * pChild = pNode->FindParent()->FindFirstChild(); while (pChild) { if (!pChild->IsAnAttribute() && !pChild->IsNodeHidden() && !IS_A(pChild, NodeShadow)) { // Then add this child to the list NodeListItem * pItem = new NodeListItem(pChild); pList->AddTail(pItem); } pChild = pChild->FindNext(); } } else { // Then we were safe to add it to the list... NodeListItem * pItem = new NodeListItem(pNode->FindParent()); pList->AddTail(pItem); } } pNode = FindNextOfClass(pNode, pLayer, CC_RUNTIME_CLASS(TemplateAttribute)); } } /******************************************************************************************** > static void SliceHelper::EnsureTriggerInfo() Author: Priestley (Xara Group Ltd) Created: 22/01/2001 Purpose: Scans the NameGallery ensuring that the triggers are in the right places... ********************************************************************************************/ void SliceHelper::EnsureTriggerInfo() { NameGallery *pNameGallery = NameGallery::Instance(); SGUsedNames* pNames = pNameGallery->GetUsedNames(); if (!pNames) { return; } SGNameItem* pNameGalleryItem = (SGNameItem*) pNames->GetChild(); // First we need to loop around and set all the triggers to false... while (pNameGalleryItem) { pNameGalleryItem->m_IsATrigger = FALSE; pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } // Now go back to the beginning and work out which named sets have triggers - mark these triggers as being such... pNameGalleryItem = (SGNameItem*) pNames->GetChild(); while (pNameGalleryItem) { NodeSetProperty* pPropNode = NULL; NamedStretchProp* pProp = NULL; // Check if the named set has 'Stretch' property information... pPropNode = pNameGalleryItem->GetPropertyNode(); if (pPropNode) { pProp = (NamedStretchProp*) pPropNode->GetProperty(NamedStretchProp::nIndex); } // If so, and its list of triggers is not empty, loop around ensuring that all of the triggers of this set have their 'is trigger' flag set... if (pProp && !pProp->GetTriggers().empty()) { for (std::list::iterator pt = pProp->GetTriggers().begin(); pt != pProp->GetTriggers().end(); pt++) { SGNameItem* pTempTriggerSet = SliceHelper::LookupNameGalleryItem(pt->m_strSet); if (pTempTriggerSet) { pTempTriggerSet->m_IsATrigger = TRUE; } } } // Get the next item and try again... pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext(); } }