slicehelper.cpp

Go to the documentation of this file.
00001 // $Id: slicehelper.cpp 1282 2006-06-09 09:46:49Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // helper functions that relate to things slice like
00099 // such as image slicing, bar creation, bar duplication and text synchronising
00100 
00101 #include "camtypes.h"
00102 
00103 #include "layer.h"      // knowing about layers
00104 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 
00106 // for the use of wix temple attribs
00107 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 #include "userattr.h"
00109 #include "tmpltatr.h"
00110 
00111 // named set stuff
00112 #include "ngcore.h"
00113 #include "ngitem.h"
00114 
00115 //#include "simon.h" // for the _R(IDS_ROLLOVER_DEFAULT) etc
00116 //#include "sliceres.h"     // more resources
00117 
00118 #include "slicehelper.h"
00119 #include "ngprop.h"
00120 #include "ngsentry.h"
00121 
00122 #include <algorithm> // for "find" of lists
00123 
00124 #include "nodetxts.h"
00125 #include "nodetxtl.h"
00126 //#include "nodetext.h"
00127 #include "nodecntr.h"
00128 
00129 #include "layergal.h" // for the vis layer action
00130 
00131 // need to know about shadows and bevels since they size funny for the GetNodeBounding()
00132 #include "nodeshad.h"
00133 #include "nbevcont.h"
00134 #include "ncntrcnt.h"
00135 
00136 #include "lineattr.h" // for the line width inclusion part of the GetNodeBounding()
00137 
00138 #include "ophist.h"
00139 #include "nodebev.h"
00140 #include "nodetext.h"
00141 
00142 // global that is set when a bar property tag is imported to say how many bars there were beforehand
00143 // used by SliceHelper::MeshImportedLayersWithExistingButtonBars() and is defined in rechdoc.cpp
00144 extern INT32 g_NoOfBarsBeforeImport;
00145 
00146 #ifdef _DEBUG
00147 #undef THIS_FILE
00148 static char BASED_CODE THIS_FILE[] = __FILE__;
00149 #endif
00150 
00151 DECLARE_SOURCE("$Revision: 1282 $");
00152 
00153 // Declare smart memory handling in Debug builds
00154 #define new CAM_DEBUG_NEW
00155 
00156 #define MAX_IMPORTED_BARS   255
00157 
00158 List * SliceHelper::m_pSelNodeList = 0;
00159 
00160 /********************************************************************************************
00161 
00162 >   static Layer * SliceHelper::FindLayerCalled(const StringBase & LayerName)
00163 
00164     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00165     Created:    30/9/99
00166     Params:     LayerName   -   The name of the layer to find
00167     Returns:    returns the layer node for the Layer name passed in.
00168                 Returns NULL if there is no layer of that name
00169     Purpose:    -
00170     Errors:     -
00171 
00172 ********************************************************************************************/
00173 Layer * SliceHelper::FindLayerCalled(const StringBase & LayerName)
00174 {
00175     // find a spread?
00176     Spread* pSpread = Document::GetSelectedSpread();
00177     if (pSpread == NULL)
00178         return FALSE;
00179 
00180     // scan for the layer to delete
00181     Layer * pLayer = pSpread->FindFirstLayer();
00182 
00183     while (pLayer)
00184     {
00185         if (!pLayer->IsNodeHidden() && pLayer->GetLayerID().CompareTo(LayerName) == 0)
00186             return pLayer;
00187 
00188         pLayer = pLayer->FindNextLayer();
00189     }
00190 
00191     return NULL;
00192 }
00193 
00194 /********************************************************************************************
00195 
00196 >   static INT32 SliceHelper::CountButtonsInBar(const StringBase & BarName)
00197 
00198     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00199     Created:    30/9/99
00200     Params:     BarName -   The name of the bar we refer to
00201     Returns:    The number of buttons in the bar.
00202                 Zero if it doesn't think the bar exists.
00203     Purpose:    -
00204     Errors:     -
00205 
00206 ********************************************************************************************/
00207 INT32 SliceHelper::CountButtonsInBar(const StringBase & BarName)
00208 {
00209     String_256 DefaultLayerName;
00210     DefaultLayerName.Load(_R(IDS_ROLLOVER_DEFAULT));
00211 
00212     // count the buttons on the default layer
00213     // every button bar requires a member to be on the default layer
00214     // so there is no point in counting any other layer
00215     Layer * pDef = FindLayerCalled(DefaultLayerName);
00216 
00217     INT32 NumberOfButtons = 0;
00218 
00219     if (pDef)
00220     {
00221         // store of example attribs of each button in this bar
00222         // but we don't need this data so we can throw it away afterwards
00223         TemplateAttribute ** ppFoundButton[MAX_BUTTONS_IN_A_BAR];
00224 
00225         // scan down from the default layer counting the different button names
00226         // that all have the same bar name
00227         CountButtonsInBarScan(pDef, (TemplateAttribute **) ppFoundButton, &NumberOfButtons, BarName);
00228     }
00229 
00230     return NumberOfButtons;
00231 }
00232 
00233 
00234 /********************************************************************************************
00235 
00236 >   static void SliceHelper::CountButtonsInBarScan(Node * pNode, TemplateAttribute ** ppFoundButton,
00237                                  INT32 * pNumberOfButtons, const StringBase &BarName)
00238 
00239     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00240     Created:    30/9/99
00241     Params:     pNode           -   The node to scan from.
00242                                     Looks at everything down from here.
00243                 ppFoundButton   -   Pointer to array of Template Attribute nodes.
00244                                     Each one holds the name of the bar 
00245                                     and the name of the button it is.
00246                 pNumberOfButtons-   The number of buttons found so far
00247                 BarName         -   The bar we are looking for
00248     Returns:    -
00249     Purpose:    Finds the number of buttons in the bar and the names of each button,
00250                 by filling in the ppFoundButton array and pNumberOfButtons.
00251     Errors:     Limitation of 20 buttons in a bar, which is all the program will let you have.
00252                 Caution this function is RECURSIVE.
00253 
00254 ********************************************************************************************/
00255 void SliceHelper::CountButtonsInBarScan(Node * pNode, TemplateAttribute ** ppFoundButton, INT32 * pNumberOfButtons, const StringBase & BarName)
00256 {
00257     // is this a node we are looking for?
00258     if (pNode->IsAnAttribute()) // is an attrib
00259     {
00260         if (IS_A(pNode,TemplateAttribute)) // is a wix attrib
00261             {
00262                 // does it have the correct bar name hidden in the question?
00263                 if (BarName.CompareTo(((TemplateAttribute *)pNode)->GetQuestion()) == 0)
00264                 {
00265                     BOOL ok = TRUE;
00266                     // get this buttons name
00267                     String_256 ThisButton = ((TemplateAttribute *)pNode)->GetParam();
00268 
00269                     for (INT32 i = 0; ok && i < *pNumberOfButtons; i++)
00270                     {
00271                         // has this button name been seen before?
00272                         if (ThisButton.CompareTo(ppFoundButton[i]->GetParam()) == 0)
00273                             ok = FALSE;
00274                     }
00275 
00276                     // found a new button name
00277                     if (ok && *pNumberOfButtons < MAX_BUTTONS_IN_A_BAR) 
00278                     {
00279                         // add this button to the array
00280                         ppFoundButton[*pNumberOfButtons] = (TemplateAttribute *)pNode;
00281                         TRACE( _T("Found new button called "));
00282                         TRACE(ppFoundButton[*pNumberOfButtons]->GetParam());
00283                         TRACE( _T("\n"));
00284                         // increase the number of buttons
00285                         *pNumberOfButtons = *pNumberOfButtons + 1;
00286                     }
00287 
00288                 }
00289             }
00290     }
00291     else // look at this nodes children
00292     {
00293         Node * pChildNode = pNode->FindFirstChild();
00294 
00295         while (pChildNode)
00296         {
00297             // ***recursive call***
00298             if (!pChildNode->IsNodeHidden())
00299                 CountButtonsInBarScan(pChildNode, ppFoundButton, pNumberOfButtons, BarName);
00300 
00301             pChildNode = pChildNode->FindNext();
00302         }
00303     }
00304 }
00305 
00306 
00307 /********************************************************************************************
00308 
00309 >   static void SliceHelper::BuildListOfNodesInBar(List * pList, Node * pNode,
00310                                                      const StringBase & BarName)
00311 
00312     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00313     Created:    30/9/99
00314     Params:     pList           -   List that is filled in of pointers to all the nodes
00315                 pNode           -   The node to scan from.
00316                                     Looks at everything down from here.
00317                 BarName         -   The bar we are looking for
00318     Returns:    -
00319     Purpose:    Builds a list (or extends an existing list) of ALL the wix attribute nodes
00320                 that have this bar name hidden in the question.
00321     Errors:     Caution is RECURSIVE
00322 
00323 ********************************************************************************************/
00324 void SliceHelper::BuildListOfNodesInBar(List * pList, Node * pNode, const StringBase & BarName)
00325 {
00326     if (pNode->IsAnAttribute()) // looking for an attrib
00327     {
00328         if (IS_A(pNode,TemplateAttribute)) // that is a wix attrib
00329             {
00330                 // that has the bar name in the question
00331                 // the same as the one we are looking for
00332                 if (BarName.CompareTo(((TemplateAttribute *)pNode)->GetQuestion()) == 0)
00333                 {
00334                     //add it to the list then
00335                     NodeListItem * pItem = new NodeListItem(pNode);
00336                     pList->AddTail(pItem);
00337                 }
00338             }
00339     }
00340     else // find anything else interesting?
00341     {
00342         Node * pChildNode = pNode->FindFirstChild();
00343 
00344         while (pChildNode)
00345         {
00346             // ***recursive call***
00347             if (!pChildNode->IsNodeHidden())
00348                 BuildListOfNodesInBar(pList, pChildNode, BarName);
00349             pChildNode = pChildNode->FindNext();
00350         }
00351     }
00352 }
00353 
00354 /********************************************************************************************
00355 
00356 >   static SGNameItem* SliceHelper::LookupNameGalleryItem(const StringBase& strName)
00357 
00358     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00359     Created:    30/9/99
00360     Params:     strName -   Name of the set from the name gallery
00361                             that we are looking for
00362     Returns:    a ptr to the name gallery item if it exists
00363                 or NULL if it doesn't exist.
00364     Purpose:    Used to find out if a button name has been used before.
00365                 Or find the dimensions of a button.
00366     Errors:     -
00367 
00368 ********************************************************************************************/
00369 SGNameItem* SliceHelper::LookupNameGalleryItem(const StringBase & strName)
00370 {
00371     NameGallery * pNameGallery = NameGallery::Instance();
00372     SGUsedNames* pNames = pNameGallery ? pNameGallery->GetUsedNames() : NULL;
00373     SGNameItem* pNameGalleryItem = pNames ? (SGNameItem*) pNames->GetChild() : NULL;
00374 
00375     String_256 str;
00376 
00377     // check all the name gallery items
00378     while (pNameGalleryItem)
00379     {
00380         pNameGalleryItem->GetNameText(&str);
00381         // if the name matches its your man
00382         if (strName.CompareTo(str) == 0)
00383             return pNameGalleryItem;
00384 
00385         // no then try the next one?
00386         pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
00387     }
00388     return NULL;
00389 }
00390 
00391 /********************************************************************************************
00392 
00393 >   static void SliceHelper::GetNextFreeButtonName(INT32 &butno, StringBase * pStr)
00394 
00395     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00396     Created:    15/10/99
00397     Params:     butno   -   The number of the button to start looking from
00398                             use zero to look for button1 first etc..
00399                             Gets filled in to the number of the free button found.
00400                 pStr    -   This is set to the string of the button name if you
00401                             bother to pass it a buffer in.
00402     Returns:    Doesn't bother
00403     Purpose:    Used to find the next button name that can be reused or created,
00404                 that isn't going to interfere with what we have.
00405     Errors:     -
00406 
00407 ********************************************************************************************/
00408 void SliceHelper::GetNextFreeButtonName(INT32 &butno, StringBase * pStr)
00409 {
00410     String_256 TempButtonName;
00411     SGNameItem* pNGItem = NULL;
00412     SGNameItem* pNGItemExtender = NULL;
00413 
00414     // Matt 20/12/2000
00415     // We can't just assume that because "Button1" doesn't exist that "Button1 Extender" doesn't
00416     // because we might have renamed either one !!!
00417     String_256 Postfix; // holds the "Extender" postfix
00418     Postfix.Load(_R(IDS_EXTENDER_POSTFIX));
00419     String_256 TempButtonExtender;
00420 
00421     do
00422     {
00423         butno++;
00424         TempButtonName.MakeMsg(_R(IDS_BUTTONNAME), butno);
00425         pNGItem = SliceHelper::LookupNameGalleryItem(TempButtonName);
00426 
00427         // Construct the appropriate default extender name for this button
00428         TempButtonExtender = TempButtonName;
00429         TempButtonExtender += Postfix;
00430 
00431         pNGItemExtender = SliceHelper::LookupNameGalleryItem(TempButtonExtender);
00432     } while ((pNGItem && !pNGItem->IsEmpty()) || (pNGItemExtender && !pNGItemExtender->IsEmpty()));
00433 
00434     if (pStr)
00435         *pStr = TempButtonName;
00436 }
00437 
00438 /********************************************************************************************
00439 
00440 >   static Node * SliceHelper::FindNextOfClass( Node *pNode,
00441                                                 Node * pLidNode, 
00442                                                 const class CCRuntimeClass * pClass,
00443                                                 BOOL CheckThis = FALSE)
00444 
00445     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00446     Created:    30/9/99
00447     Params:     pNode   -   The node to continue the search from
00448                 pLidNode-   Marks the end of the search, doesn't search beyond
00449                             this node
00450                 pClass  -   The class we are looking for e.g. CC_RUNTIME_CLASS(TextLine)
00451                 CheckThis-  Include or exclude the pNode in the search.
00452                             Useful if it has just returned this node and you then
00453                             want to find the next, not be presented with the same one
00454                             again!
00455     Returns:    a ptr to the next node of the desired type in the tree as it traverses it
00456                 depth first.
00457                 NULL if the search is complete.
00458     Purpose:    Used to walk a tree repeatedly from any given branch,
00459                 searching for nodes of a particular type.
00460 
00461     Use:        To find all textlines in a layer pLayer and process them
00462                 Node * pNode = SliceHelper::FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(TextLine));
00463                 while (pNode)
00464                     {
00465                     pNode-> whatever you like;
00466                     pNode = SliceHelper::FindNextOfClass(pNode, pLayer, CC_RUNTIME_CLASS(TextLine));
00467                     }
00468                 
00469     Errors:     -
00470 
00471 ********************************************************************************************/
00472 Node * SliceHelper::FindNextOfClass(Node *pNode, Node * pLidNode, const class CCRuntimeClass * pClass, BOOL CheckThis)
00473 {
00474     Node * pNextNode = NULL;
00475     BOOL tested = FALSE;
00476     Node * pPassedNode = CheckThis ? NULL : pNode;
00477 
00478     BOOL scanAttributes = FALSE;    // Attribute nodes do NOT ever return CC_RUNTIME_CLASS(NodeAttribute).
00479                                     // if were scanning for these, then we need to do things slightly differently ....
00480 
00481     if (pClass == CC_RUNTIME_CLASS(NodeAttribute))
00482     {
00483         scanAttributes = TRUE;
00484     }
00485 
00486     while (pNode != pLidNode || !tested)
00487     {
00488         tested = TRUE;
00489 
00490         // check this node as we move across
00491         if (!scanAttributes)
00492         {
00493             if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->GetRuntimeClass() == pClass)
00494                 return pNode;
00495         }
00496         else
00497         {
00498             if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->IsAnAttribute ())
00499                 return pNode;
00500         }
00501 
00502         // while can go down do so
00503         pNextNode = pNode->FindFirstChild();
00504 
00505         if (pNextNode && pNextNode->IsNodeHidden())
00506             pNextNode = pNextNode->FindNextNonHidden();
00507         
00508         while(pNextNode)
00509         {
00510             pNode = pNextNode;
00511             
00512             if (!scanAttributes)
00513             {
00514                 // check this node as we move down
00515                 if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->GetRuntimeClass() == pClass)
00516                     return pNode;
00517             }
00518             else
00519             {
00520                 // check this node as we move down
00521                 if (!pNode->IsNodeHidden() && pNode != pPassedNode && pNode->IsAnAttribute ())
00522                     return pNode;
00523             }
00524 
00525             pNextNode = pNode->FindFirstChild();
00526 
00527             if (pNextNode && pNextNode->IsNodeHidden())
00528                 pNextNode = pNextNode->FindNextNonHidden();
00529         }
00530 
00531         // cant go down and we are already at the top
00532         if (pNode == pLidNode)
00533             return NULL;
00534 
00535         // find next none hidden brother
00536         pNextNode = pNode->FindNextNonHidden();
00537 
00538         if (pNextNode)
00539             pNode = pNextNode;
00540         else // no brothers so find an uncle
00541         {
00542             BOOL FoundUncle = FALSE;
00543 
00544             while (!FoundUncle)
00545             {
00546                 pNextNode = pNode->FindParent();
00547                 if (pNextNode == pLidNode || !pNextNode)
00548                     return NULL;
00549 
00550                 pNode = pNextNode;
00551                 pNextNode = pNode->FindNextNonHidden();
00552 
00553                 // found uncle
00554                 if (pNextNode)
00555                 {
00556                     pNode = pNextNode;
00557                     FoundUncle = TRUE;
00558                 }
00559             }
00560         }
00561     }
00562 
00563     return NULL;
00564 }
00565 
00566 
00567 /********************************************************************************************
00568 
00569 >   BOOL SliceHelper::SelectObjectsInSet(const StringBase& strName,
00570                                             SelectScan::Change eNewState)
00571 
00572     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00573     Created:    4/10/99
00574     Returns:    TRUE if it succeeded
00575     Params:     strName     -   The set name to select
00576                 nNewState   -   1 selects, 0 deselects, -1 toggles
00577     Purpose:    Wraps the scan functionality.
00578                 Selects all items from the named set.
00579 ********************************************************************************************/
00580 BOOL SliceHelper::SelectObjectsInSet(const StringBase& strName,
00581                                         SelectScan::Change eNewState)
00582 {
00583     SGNameItem* pItem = SliceHelper::LookupNameGalleryItem(strName);
00584     if (pItem == 0) return FALSE;
00585     SelectScan scanner(pItem, eNewState);
00586     scanner.Scan();
00587     return TRUE;
00588 }
00589 
00590 
00591 
00592 /********************************************************************************************
00593 
00594 >   void SliceHelper::SelectAllSetsOfThisNode(  NodeRenderableInk* pInk,
00595                                                 String_256 & SetName, BOOL bShift )
00596 
00597     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00598     Created:    14/12/99
00599     Returns:    
00600     Params:     pInk        -   The node to check
00601                 SetName     -   The name of the set we are looking for.
00602                 bShift      -   TRUE if the shift key is down (so it toggles)
00603                                 FALSE to shift -> select
00604     Purpose:    Calls RecurseSelectAllSetsOfThisNode after looking up the tree for any
00605                 non-selectable nodes. These are tested for by looking for a FALSE return from
00606                 PromoteHitTestOnChildrenToMe. We do this because the user may try to select
00607                 eg a NodeBevelController with a name on it, the selection slips through to
00608                 its children who don't have the name directly applied. We must therefore find
00609                 the node the user thought they were clicking on, and work on from there.
00610 
00611                 See RecurseSelectAllSetsOfThisNode for more.
00612 
00613 ********************************************************************************************/
00614 void SliceHelper::SelectAllSetsOfThisNode(  NodeRenderableInk* pInk,
00615                                             String_256 & SetName, BOOL bShift )
00616 {
00617     // go up the tree to find the highest non-hittest node above us.
00618     Node* pSelect = pInk;
00619     Node* pNext = pInk->FindParent();
00620     while ( pNext != NULL && !pNext->IsLayer() &&
00621             !pNext->PromoteHitTestOnChildrenToMe() )
00622     {
00623         pSelect = pNext;
00624         pNext = pNext->FindParent();
00625     }
00626 
00627     // ok, now make the *recursive* call to RecurseSelectAllSetsOfThisNode :->
00628     RecurseSelectAllSetsOfThisNode(pSelect, SetName, bShift);
00629 }
00630 
00631 
00632 
00633 /********************************************************************************************
00634 
00635 >   void SliceHelper::RecurseSelectAllSetsOfThisNode(Node * pAttrs, String_256 & SetName, BOOL bShift)
00636 
00637     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> (renamed by Karim from SelectAllSetsOfThisNode, 14/12/1999)
00638     Created:    4/10/99
00639     Returns:    TRUE if this bar name is used from this node down
00640     Params:     pAttrs      -   The node to check from
00641                 SetName     -   The name of the set we are looking for.
00642                 bShift      -   TRUE if the shift key is down (so it toggles)
00643                                 FALSE to shift -> select
00644     Purpose:    Looks at all template attributes applied to this node and its children.
00645                 Sets the selection state of the any referred name sets according to the
00646                 shift setting.
00647                 ie. you pick a member of a set and it selects all members of that set.
00648                 ie2. If it is a member of more than one set all those sets are selected.
00649 ********************************************************************************************/
00650 void SliceHelper::RecurseSelectAllSetsOfThisNode(Node * pAttrs, String_256 & SetName, BOOL bShift)
00651 {
00652     if (pAttrs->IsAnAttribute() && IS_A(pAttrs,TemplateAttribute))
00653     {
00654         if (SetName.CompareTo(((TemplateAttribute *)pAttrs)->GetParam()) != 0)
00655         {
00656             // this line tests if the name is a bar name rather than any old named thing that
00657             // the user may have added
00658             if (!((TemplateAttribute *)pAttrs)->GetQuestion().IsEmpty())
00659             {
00660                 SetName = ((TemplateAttribute *)pAttrs)->GetParam();
00661                 SliceHelper::SelectObjectsInSet(SetName, bShift ? SelectScan::TOGGLE
00662                                                : SelectScan::SELECT/*_EXCLUSIVE*/);
00663             }
00664         }
00665     }
00666     else
00667     {
00668         pAttrs = pAttrs->FindFirstChild();
00669 
00670         while (pAttrs)
00671         {
00672             // recursive call!!!
00673             if (!pAttrs->IsNodeHidden())
00674                 RecurseSelectAllSetsOfThisNode(pAttrs, SetName, bShift);
00675             pAttrs = pAttrs->FindNext();
00676         }
00677     }
00678 }
00679 
00680 
00681 
00682 /********************************************************************************************
00683 >   void SliceHelper::SelectAllSetsInRect(DocRect Rect, Spread* pSpread,
00684                                         SliceHelper::SelStateAction st = SelStateAction::SET)
00685 
00686     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>, from Simon / JustinF's code
00687     Created:    12/10/1999
00688     Inputs:     Rect    the Rectangle to act as a bounding box for the selection.
00689                 Spread  the spread on which to select the objects.
00690                 st      either CLEAR, SET, or TOGGLE, which is what should be done to
00691                         the selection state of each object within the rectangle. 
00692     Purpose:    For each object wholly inside the supplied rectangle, selects that object
00693                 and all other objects sharing any names belonging to that object.
00694                 It will draw in all the EORed blobs of the objects that it selects/deselects.
00695                 rewritten by Simon so that objects on locked layers do not get selected
00696                 also more optimal.
00697                 (st parameter etc added by JCF 1.11.94)
00698     SeeAlso:    NodeRenderableInk::SelectAllInRect
00699 ********************************************************************************************/
00700 void SliceHelper::SelectAllSetsInRect(const DocRect Rect, Spread* pSpread,
00701                                         SliceHelper::SelStateAction st)
00702 {
00703     // Make sure we are not being told garbage
00704     ERROR3IF(pSpread == NULL, "SliceHelper::SelectAllSetsInRect- NULL pSpread");
00705     ERROR3IF(pSpread->FindFirstLayer() == NULL, "SliceHelper::SelectAllSetsInRect- no layer");
00706 
00707     // process all objects on modifiable layers within the given bounding box.
00708     String_256 SetName;
00709     Layer* pLayer = pSpread->FindFirstLayer();
00710     while (pLayer != NULL)
00711     {
00712         // proceed if the current layer is unlocked, visible and overlaps the given rect.
00713         if (!pLayer->IsLocked() && pLayer->IsVisible() &&
00714             pLayer->GetBoundingRect().IsIntersectedWith(Rect)) 
00715         {
00716             Range rng(pLayer->FindFirstChild(), NULL, RangeControl(TRUE, TRUE));
00717             Node* pNode = rng.FindFirst();
00718             while (pNode != NULL)
00719             {
00720                 // make sure it is a Renderable Ink Node
00721                 if (pNode->IsAnObject())
00722                 {
00723                     // Ok, this object is unselected and renderable,  
00724                     // so now we can check if it is in the rect
00725                     NodeRenderableInk* pInk = (NodeRenderableInk*) pNode;
00726                     if (Rect.ContainsRect(pInk->GetBoundingRect()))
00727                     {
00728                         switch (st)
00729                         {
00730                         case CLEAR:
00731                             // Karim MacDonald 22/10/1999
00732                             // TODO: replace this with code for Named-Set deselection.
00733                             //       the only current reason for this is completeness.
00734                             pInk->DeSelect(TRUE);
00735                             break;
00736 
00737                         case SET:
00738                             // although our Range object should only return unselected nodes,
00739                             // we're busy selecting sets of nodes within the loop => what was
00740                             // unselected at the start of the loop will often be selected by
00741                             // this point - hence the double-check for selection.
00742                             if (!pInk->IsSelected())
00743                             {
00744                                 SetName.Empty();
00745                                 SliceHelper::SelectAllSetsOfThisNode(pInk, SetName, FALSE);
00746                                 if (SetName.IsEmpty())
00747                                     pInk->Select(TRUE);
00748                             }
00749                             break;
00750 
00751                         case TOGGLE:
00752                             if (pInk->IsSelected())
00753                             {
00754                                 // Karim MacDonald 22/10/1999
00755                                 // TODO: replace this with code for Named-Set deselection.
00756                                 //       the only current reason for this is completeness.
00757                                 pInk->DeSelect(TRUE);
00758                             }
00759                             else
00760                             {
00761                                 SetName.Empty();
00762                                 SliceHelper::SelectAllSetsOfThisNode(pInk, SetName, FALSE);
00763                                 if (SetName.IsEmpty())
00764                                     pInk->Select(TRUE);
00765                             }
00766                             break;
00767 
00768                         default:
00769                             ERROR3("SliceHelper::SelectAllSetsInRect- unknown SelStateAction");
00770                             return;
00771                         }
00772                     }
00773                 }
00774 
00775                 // get the next Node to consider.
00776                 pNode = rng.FindNext(pNode);
00777             }
00778         }
00779 
00780         // check the next layer.
00781         pLayer = pLayer->FindNextLayer();
00782     }
00783 
00784     // Update the selection cache
00785     GetApplication()->UpdateSelection();
00786 }
00787 
00788 /********************************************************************************************
00789 
00790 >   String_256 SliceHelper::GetBarName(TemplateAttribute * pTemplAttrib)
00791 
00792     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> JK
00793     Created:    4/10/99
00794     Returns:    The name of the bar
00795     Params:     pTemplAttrib-   The attrib node.
00796     Purpose:    The bar name is stored as the question in the wix attrib.
00797                 This fn extracts the bar name from the attrib, checking that it
00798                 is a bar name and not any old string in there.
00799                 If it isn't a bar identifier it returns an empty string.
00800 ********************************************************************************************/
00801 String_256 SliceHelper::GetBarName(TemplateAttribute * pTemplAttrib)
00802 {
00803     String_256 BarName = pTemplAttrib->GetQuestion();
00804 
00805     // must start with the word "Bar"
00806     if (BarName[0] != 'B' || BarName[1] != 'a' || BarName[2] != 'r')
00807         BarName.Empty();
00808 
00809     return BarName;
00810 }
00811 
00812 /********************************************************************************************
00813 
00814 >   Node * SliceHelper::ReplaceAttrsInTree(UndoableOperation * pOp,
00815                                        List * pImportedAttrList,
00816                                        const String_256 & OldButtonName,
00817                                        const String_256 & OldBarName,
00818                                        const String_256 & NewButtonName,
00819                                        const String_256 & NewBarName,
00820                                        Node ** ppNodeFound)
00821 
00822     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> JK
00823     Created:    21/9/00
00824     Purpose:    Scans the pImportedAttrList looking for OldButtonName and OldBarName.
00825                 Replaces it with new versions of the template attribute.
00826                 Returns a ptr to an example of a changed attr node
00827                 Fills in an example of an old node into ppNodeFound if required
00828 
00829 ********************************************************************************************/
00830 Node * SliceHelper::ReplaceAttrsInTree(UndoableOperation * pOp,
00831                                        List * pImportedAttrList,
00832                                        const String_256 & OldButtonName,
00833                                        const String_256 & OldBarName,
00834                                        const String_256 & NewButtonName,
00835                                        const String_256 & NewBarName,
00836                                        Node ** ppNodeFound)
00837 {
00838 
00839     Node * pExampleNode = NULL;
00840     NodeListItem * pItem = (NodeListItem *)pImportedAttrList->GetHead();
00841     NodeListItem * pNextItem = NULL;
00842 
00843     while (pItem)
00844     {
00845         pNextItem = (NodeListItem *)pImportedAttrList->GetNext(pItem);
00846 
00847         if (OldBarName.CompareTo(((TemplateAttribute *)(pItem->pNode))->GetQuestion()) == 0
00848             && OldButtonName.CompareTo(((TemplateAttribute *)(pItem->pNode))->GetParam()) == 0)
00849         {
00850             // found a matching attr
00851 
00852             // set the found node if required
00853             if (ppNodeFound && !*ppNodeFound)
00854                 *ppNodeFound = pItem->pNode;
00855 
00856             if (NewBarName != OldBarName || NewButtonName != OldButtonName)
00857             {
00858 
00859                 // add an attrib of the new type next to this
00860 
00861                 if (!NewButtonName.IsEmpty()) // dont add empty named names
00862                 {
00863                     // define the new attrib with the required button/extender name and bar name if relevent
00864                     TemplateAttribute* pAttr = new TemplateAttribute(String_256(TEXT("ObjectName")),
00865                                                         NewBarName,
00866                                                         NewButtonName);
00867 
00868                     if (!pExampleNode)
00869                         pExampleNode = pAttr;
00870 
00871                     // add the new attrib to the tree
00872                     if (pAttr) 
00873                     {
00874                         pAttr->AttachNode(pItem->pNode, NEXT);
00875                         // add undo info to hide this node?
00876                     }   
00877                 }
00878 
00879                 // remove the old attrib
00880                 // it pointed to the out of date information
00881                 pOp->DoHideNode(pItem->pNode, FALSE);
00882             }
00883             else
00884                 if (!pExampleNode)
00885                     pExampleNode = pItem->pNode;
00886 
00887 
00888             // remove this entry from this list
00889             pImportedAttrList->RemoveItem((NodeListItem *)pItem);
00890             delete pItem;
00891         }
00892 
00893         // look at the next item in the list
00894         pItem = pNextItem;
00895     }
00896 
00897     return pExampleNode;
00898 }
00899 
00900 // scans the list to see if name is used by a template attrib in the list
00901 BOOL SliceHelper::IsUniqueName(const String_256 & Name, List * pList)
00902 {
00903     NodeListItem * pExistingListItem = (NodeListItem *)pList->GetHead();
00904     while (pExistingListItem)
00905     {
00906         if (Name.CompareTo(((TemplateAttribute *)(pExistingListItem->pNode))->GetParam()) == 0)
00907             return TRUE;
00908 
00909         pExistingListItem = (NodeListItem *)pList->GetNext(pExistingListItem);
00910     }
00911 
00912     return FALSE;
00913 }
00914 
00915 
00916 // extension to the NodeListItem to store other stuff needed in this MeshImportedLayersWithExistingButtonBars() fn
00917 class MeshNodeListItem : public NodeListItem
00918 {
00919 public:
00920     MeshNodeListItem(   Node* WhichNode,
00921                         Node* pNode2,
00922                         INT32 type,
00923                         INT32 level,
00924                         BYTE flags,
00925                         DocRect target,
00926                         DocRect extender)
00927     {
00928         pNode = WhichNode;
00929         pNewNode = pNode2;
00930         ButtonClassification = type;
00931         StretchLevel = level;
00932         ExtendFlags = flags;
00933         rTarget = target;
00934         rExtender = extender;
00935         pExtender = NULL;
00936         pOldPropNode = NULL;
00937     };// initialise vars
00938 
00939     Node * pNewNode;
00940     Node * pExtender;
00941     Node * pOldPropNode;
00942     INT32 ButtonClassification;
00943     INT32 StretchLevel;
00944     BYTE ExtendFlags;
00945     DocRect rTarget;
00946     DocRect rExtender;
00947 };
00948 
00949 /********************************************************************************************
00950 
00951 >   void SliceHelper::MeshImportedLayersWithExistingButtonBars(Node * pImportedLayer[5], UndoableOperation * pUndoableOp, BOOL Imported)
00952 
00953     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> JK
00954     Created:    28/10/99
00955     Params:     pImportedLayer[5]   -   ptrs to the 5 (or less) nodes just imported
00956                                         in that represent the DEFAULT, MOUSE, CLICKED
00957                                         and SELECTED states (and now BACKBAR) in the imported document.
00958                                         These have to be merged with whatever is already
00959                                         in these layers proper. Imported nodes are added
00960                                         AFTER the existing nodes and each ptr points to
00961                                         the first node on the tree in each layer.
00962                                         All 5 ptrs must be supplied, they can individually
00963                                         be left NULL.
00964                 pUndoableOp     -   This function must hide and move nodes so it must be part
00965                                     of an undoable op.
00966                 Imported        -   Bool to tell if the bar is coming from an external file (TRUE)
00967                                     or copied/cloned from within this document (FALSE) so that the bar data
00968                                     can be scavinged.
00969                                     
00970     Purpose:    Used when a drawing (.XAR, .WEB) is imported or dragged into an existing
00971                 drawing, or cloned. Button names and bar names are rationalised and made unique, after
00972                 the nodes themselves have already been merged onto the existing special layers.
00973                 The function also has to mesh the stretching properties of any buttons that it
00974                 tampers with providing new extenders to go with each new button defined. The 
00975                 extenders have to be uniquely named too and match with the correct button and
00976                 the correct nodes of that button.
00977     Errors:     This function Only works with a single item stretching in a button as it stores
00978                 it as a string "Extender" which it recieves from the SliceHelper function
00979                 FindTargetAndExtender().
00980 ********************************************************************************************/
00981 void SliceHelper::MeshImportedLayersWithExistingButtonBars(Node * pImportedLayer[5], UndoableOperation * pUndoableOp, BOOL Imported)
00982 {
00983     INT32 BarReplacedWithBar[MAX_IMPORTED_BARS]; // 255 bars the max to import into a document
00984     memset (BarReplacedWithBar, -1, sizeof(BarReplacedWithBar));
00985 
00986     NameGallery* pNameGallery = NameGallery::Instance();
00987     if (!pNameGallery)
00988         return;
00989     pNameGallery->FastUpdateNamedSetSizes(); // update the existing names in the gallery
00990 
00991     String_256 Postfix;                         // holds the "Extender" postfix
00992     Postfix.Load(_R(IDS_EXTENDER_POSTFIX));
00993 
00994     NodeSetSentinel * pNodeSetSentinel = Document::GetSelected()->GetSetSentinel();
00995 
00996     INT32 i; // general perpose use
00997 
00998     // the all important lists of Template attribute nodes in the tree
00999     List ExistingAttrList;
01000     List ImportedAttrList;
01001     List MeshList; // this one add lots more data too to rebuild the properties
01002 
01003     Node * pNode;
01004     Node * pParent;
01005     INT32 BarNo = 0; // used to store the unique bar number we have generated up to so far
01006     INT32 ButtonNo = 0; // used to store the unique button number we have generated up to so far
01007 
01008     //*** fill in the ExistingAttrList & the ImportedAttrList ***
01009 
01010     // fill the list of attrs that were in the tree before the import
01011     // and those that were in the tree after the import
01012     // look on each possible special rollover layers
01013     for (i = 0; i < 5; i++)
01014         if (pImportedLayer[i])
01015         {
01016             // test to see if the first item imported is in fact the first item in the layer
01017             // implying that the whole lot has been imported
01018             pParent = pImportedLayer[i]->FindParent();
01019             Node * pFirstNodeOnLayer = pParent->FindFirstChild(); 
01020             if ( pFirstNodeOnLayer && pFirstNodeOnLayer != pImportedLayer[i])
01021             {
01022                 // there was stuff on this layer before
01023                 pNode = SliceHelper::FindNextNameNode(pFirstNodeOnLayer, pImportedLayer[i]);
01024                 while (pNode)
01025                 {
01026                     // add this node to the list
01027                     NodeListItem * pItem = new NodeListItem(pNode);
01028                     ExistingAttrList.AddTail(pItem);
01029                     pNode = SliceHelper::FindNextNameNode(pNode, pImportedLayer[i]);
01030                 }
01031             }
01032 
01033             // Add the attrs that have just been added to this layer
01034             pNode = SliceHelper::FindNextNameNode(pImportedLayer[i], pParent);
01035             while (pNode)
01036             {
01037                 // add this node to the list
01038                 NodeListItem * pItem = new NodeListItem(pNode);
01039                 ImportedAttrList.AddTail(pItem);
01040                 pNode = SliceHelper::FindNextNameNode(pNode, pParent);
01041             }
01042         }
01043 
01044     //*** Scan the imported attrs list ***
01045 
01046     NodeListItem * pNodeListItem = (NodeListItem *)ImportedAttrList.GetHead();
01047 
01048     // walk the imported list to 
01049     while (pNodeListItem)
01050     {
01051         String_256 OldButtonName = ((TemplateAttribute *)(pNodeListItem->pNode))->GetParam();
01052         String_256 OldBarName = ((TemplateAttribute *)(pNodeListItem->pNode))->GetQuestion();
01053         String_256 NewButtonName = OldButtonName;
01054         String_256 NewBarName = OldBarName;
01055         String_256 OldExtender = "";
01056         String_256 NewExtender = "";
01057 
01058         TRACEUSER( "Matt", _T("Found "));
01059         TRACEUSER("Matt", OldButtonName);
01060         TRACEUSER( "Matt", _T("\n"));
01061 
01062 
01063         // is it a button or a backbar or an extender?
01064 
01065         INT32 type = 0; // a regular button / target
01066 
01067         if (OldBarName.IsEmpty())
01068             type = 1; // an extender
01069 
01070         // look to the layer it is on is it layer[4]
01071         if (pImportedLayer[4])
01072         {
01073             pParent = pImportedLayer[4]->FindParent();
01074             pNode = pNodeListItem->pNode;
01075             while (!pNode->IsLayer())
01076                 pNode = pNode->FindParent();
01077 
01078             if (pNode == pParent)
01079                 type = 2; // a back bar
01080         }
01081 
01082         //*** generate a unique name for the button and the bar ***
01083 
01084         // make the bar name unique
01085         if (type != 1) // it is not an extender
01086         {
01087             INT32 ExistingBarno = SliceHelper::GetBarNumberFromBarName(OldBarName);
01088             ASSERT(ExistingBarno >=0);
01089 
01090             if (BarReplacedWithBar[ExistingBarno] == -1)
01091             {
01092                 if (Imported) // if imported it has a known number to become!
01093                 {
01094                     BarNo = ExistingBarno + g_NoOfBarsBeforeImport + 1;
01095 
01096                     NewBarName.MakeMsg(_R(IDS_BARNAME), BarNo);
01097                 }
01098                 else // find a bar name that hasn't been used in the existing drawing
01099                 {
01100                     BOOL AlreadyUsed = TRUE;
01101                     do
01102                     {
01103                         BarNo++;
01104                         NewBarName.MakeMsg(_R(IDS_BARNAME), BarNo);
01105 
01106                         AlreadyUsed = FALSE;
01107                         NodeListItem * pExistingListItem = (NodeListItem *)ExistingAttrList.GetHead();
01108                         while (pExistingListItem && !AlreadyUsed)
01109                         {
01110                             if (NewBarName.CompareTo(((TemplateAttribute *)(pExistingListItem->pNode))->GetQuestion()) == 0)
01111                                 AlreadyUsed = TRUE;
01112 
01113                             pExistingListItem = (NodeListItem *)ExistingAttrList.GetNext(pExistingListItem);
01114                         }
01115                     } while (AlreadyUsed);
01116                 }
01117 
01118                 BarReplacedWithBar[ExistingBarno] = BarNo - 1; // remember this bar decision next time we find a bar of that bar number
01119                                                               // the -1 is so we dont waste index zero of the array
01120                                                               // Bar1 = location zero etc. like in the bar properties node
01121             }
01122             else
01123                 NewBarName.MakeMsg(_R(IDS_BARNAME), BarReplacedWithBar[ExistingBarno] + 1);
01124 
01125             // scan the items selected in the name gallery
01126             // if we have selected only part of a button do not try to duplicate the whole thing
01127             BOOL PartOfButtonSelected = FALSE;
01128             if (!Imported)
01129             {
01130                 SGNameItem* pNameGalleryItem = (SGNameItem*) pNameGallery->GetUsedNames()->GetChild();
01131                 while (pNameGalleryItem && !PartOfButtonSelected)
01132                 {
01133                     INT32 count = pNameGalleryItem->GetSelectedCount();
01134                     if ( count > 0 && count < pNameGalleryItem->GetObjectCount())
01135                     {
01136                         // this named object is only partly selected
01137                         String_256 TempExtenderName = "";
01138                         pNameGalleryItem->GetNameText(&TempExtenderName);
01139                         if (OldButtonName.CompareTo(TempExtenderName) == 0)
01140                             PartOfButtonSelected = TRUE; // has part of this set
01141                     }
01142 
01143                     pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
01144                 }
01145             }
01146 
01147             if (PartOfButtonSelected) // only if we are not importing 
01148             {
01149 
01150                 String_256 Target = "";
01151                 if ( SliceHelper::FindTargetAndExtender(    pNodeListItem->pNode->FindParent(),
01152                                                     Target,
01153                                                     OldExtender,
01154                                                     0) == 2) // ie we found an extneder
01155                 {
01156                     // delete these attrs from the tree
01157                     ReplaceAttrsInTree(pUndoableOp,
01158                                         &ImportedAttrList,
01159                                         OldExtender,
01160                                         "",
01161                                         "",
01162                                         "");
01163                 }
01164 
01165                 // delete these attrs from the tree
01166                 ReplaceAttrsInTree(pUndoableOp,
01167                                     &ImportedAttrList,
01168                                     OldButtonName,
01169                                     OldBarName,
01170                                     "",
01171                                     "");
01172             }
01173             else
01174             {
01175                 // create a unique button name
01176                 DocRect r;
01177                 r.MakeEmpty();
01178 
01179                 MeshNodeListItem * pMeshData = new MeshNodeListItem(pNodeListItem->pNode,
01180                             NULL,
01181                             type,
01182                             0,
01183                             0,
01184                             r,
01185                             r);
01186 
01187                 if (type == 2)
01188                 {
01189                     // back bars are just called BackBarX where X is the bar number
01190                     NewButtonName.MakeMsg(_R(IDS_BACKBARNAME), BarReplacedWithBar[ExistingBarno]);
01191 
01192                     BOOL AlreadyUsed = TRUE;
01193                     do
01194                     {
01195                         AlreadyUsed = IsUniqueName(NewButtonName, &ExistingAttrList);
01196 
01197                         if (AlreadyUsed)
01198                             NewButtonName += _T("x");
01199 
01200                     }while (AlreadyUsed);
01201                 }
01202                 else
01203                 {
01204                     // buttons / targets need to find unique names not used before in the existing button list
01205                     BOOL AlreadyUsed = TRUE;
01206                     do
01207                     {
01208                         AlreadyUsed = IsUniqueName(NewButtonName, &ExistingAttrList);
01209 
01210                         if (AlreadyUsed)
01211                         {
01212                             ButtonNo++;
01213                             NewButtonName.MakeMsg(_R(IDS_BUTTONNAME), ButtonNo);
01214                         }
01215 
01216                     } while (AlreadyUsed);
01217                 }
01218 
01219                 //*** Read the property data for the button and store this data in the Mesh List ***
01220 
01221                 // find out what the extender for this used to be
01222 
01223                 String_256 Target = "";
01224                 pMeshData->StretchLevel = SliceHelper::FindTargetAndExtender(   pNodeListItem->pNode->FindParent(),
01225                                                                     Target,
01226                                                                     OldExtender,
01227                                                                     0,
01228                                                                     &(pMeshData->ExtendFlags),
01229                                                                     &(pMeshData->rTarget),
01230                                                                     &(pMeshData->rExtender) );
01231 
01232                 pMeshData->pOldPropNode = pNodeSetSentinel->FindPropertyNode(OldButtonName);
01233 
01234                 // replace all the instances of attrs marking the old button in the tree with
01235                 // attrs of the new button and bar.
01236                 // Returns an example node into pMeshData->NewNode
01237                 pMeshData->pNewNode = ReplaceAttrsInTree(pUndoableOp, &ImportedAttrList, OldButtonName, OldBarName, NewButtonName, NewBarName);
01238 
01239                 // add the data about the button to the mesh list
01240                 MeshList.AddTail(pMeshData);
01241 
01242                 // add this new node data to the "existing" now that it has been placed in the tree
01243                 // so that any new items will not think they can reuse this name
01244                 NodeListItem * pNewItem = new NodeListItem(pMeshData->pNewNode);
01245                 ExistingAttrList.AddTail(pNewItem);
01246 
01247 
01248                 // found an extender for the button?
01249                 if (type == 0 && !OldExtender.IsEmpty())
01250                 {
01251                     Node ** ppButtonExtenderNode = &(pMeshData->pExtender);
01252                     pMeshData = new MeshNodeListItem(   NULL,
01253                                                         NULL,
01254                                                         1, // its an extender
01255                                                         0,
01256                                                         0,
01257                                                         r,
01258                                                         r);
01259 
01260                     // generate a unique extender name
01261                     NewExtender = NewButtonName;
01262                     NewExtender += Postfix;
01263 
01264                     BOOL AlreadyUsed = TRUE;
01265                     do
01266                     {
01267                         AlreadyUsed = IsUniqueName(NewExtender, &ExistingAttrList);
01268 
01269                         if (AlreadyUsed)
01270                             NewExtender += _T("x");
01271 
01272                     }while (AlreadyUsed);
01273 
01274                     pMeshData->pOldPropNode = pNodeSetSentinel->FindPropertyNode(OldExtender);
01275 
01276                     pMeshData->pNewNode = ReplaceAttrsInTree(pUndoableOp,
01277                                                             &ImportedAttrList,
01278                                                             OldExtender,
01279                                                             "",
01280                                                             NewExtender,
01281                                                             "",
01282                                                             &(pMeshData->pNode) );
01283 
01284                     if (pMeshData->pNewNode)
01285                     {
01286                         // add the data about the extender to the mesh list
01287                         MeshList.AddTail(pMeshData);
01288 
01289                         // let the button have a ptr to an example of an extender for it
01290                         *ppButtonExtenderNode = pMeshData->pNewNode; 
01291 
01292                         // add this new node data to the "existing" now that it has been placed in the tree
01293                         // so that any new items will not think they can reuse this name
01294                         pNewItem = new NodeListItem(pMeshData->pNewNode);
01295                         ExistingAttrList.AddTail(pNewItem);
01296                     }
01297                     else
01298                     {
01299                         // failled to find the extenders attr in the tree
01300                         // we could be copying just part of a button so treat the last as a bit of a button
01301                         // and treat this as though it had no extender
01302                         delete pMeshData;
01303                     }
01304                 }
01305             }
01306 
01307             // we have deleted items from the list so start at the begining again
01308             pNodeListItem = (NodeListItem *)ImportedAttrList.GetHead();
01309         }
01310         else // skipped an extender node so look at the next in the list
01311             pNodeListItem = (NodeListItem *)ImportedAttrList.GetNext(pNodeListItem);
01312     }
01313 
01314     
01315     //*** add the button properties to the sentinel from the data stored in the MeshDataList ***
01316 
01317     MeshNodeListItem * pMeshData = (MeshNodeListItem *)MeshList.GetHead();
01318     while (pMeshData)
01319     {
01320         // delete the property it is based on as this is now longer used
01321         // since we created an updated version above
01322         if (pMeshData->pOldPropNode && ((NodeSetProperty*) pMeshData->pOldPropNode)->m_Imported)
01323         {
01324             // delete the attr in the sentinel that comes with the property
01325             Node * pAttr = pNodeSetSentinel->FindLastChild(); 
01326             while (pAttr)
01327             {
01328                 if (IS_A(pAttr, TemplateAttribute) && 
01329                     ((TemplateAttribute *)(pMeshData->pNode))->GetParam() == ((TemplateAttribute *)pAttr)->GetParam())
01330                 {
01331                     pUndoableOp->DoHideNode(pAttr, FALSE);
01332                     break;
01333                 }
01334 
01335                 pAttr = pAttr->FindPrevious();
01336             }
01337 
01338             // delete the unused imported property
01339             pUndoableOp->DoHideNode(pMeshData->pOldPropNode, FALSE);
01340         }
01341 
01342         // set the new property
01343         switch (pMeshData->ButtonClassification)
01344         {
01345         case 0: // a normal button
01346             if (pMeshData->pExtender) // the level that says there is a extender for this
01347             {
01348                 CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(),
01349                                         ((TemplateAttribute *)(pMeshData->pNewNode))->GetQuestion(),
01350                                         TRUE, TRUE, FALSE,
01351                                         pMeshData->ExtendFlags,
01352                                         ((TemplateAttribute *)(pMeshData->pExtender))->GetParam(),
01353                                         FALSE,
01354                                         &(pMeshData->rTarget),
01355                                         &(pMeshData->rExtender),
01356                                         pUndoableOp,
01357                                         ((NodeSetProperty *)pMeshData->pOldPropNode));
01358             }
01359             else // create none stretching attribs for the button
01360                 CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(),
01361                                         ((TemplateAttribute *)(pMeshData->pNewNode))->GetQuestion(),
01362                                         TRUE, FALSE, FALSE, 0,
01363                                         TEXT(""), FALSE, NULL, NULL, pUndoableOp,
01364                                         ((NodeSetProperty *)pMeshData->pOldPropNode));
01365 
01366             break;
01367 
01368         case 1: // an extender
01369             CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(),
01370                                     TEXT(""),
01371                                     FALSE, FALSE, FALSE, 0,
01372                                     TEXT(""), FALSE, NULL, NULL, pUndoableOp, 
01373                                     ((NodeSetProperty *)pMeshData->pOldPropNode));
01374             break;
01375 
01376         case 2: // a back bar
01377             CreatePropertiesForSet( ((TemplateAttribute *)(pMeshData->pNewNode))->GetParam(),
01378                                     ((TemplateAttribute *)(pMeshData->pNewNode))->GetQuestion(),
01379                                      FALSE,
01380                                      pMeshData->ExtendFlags != 0,
01381                                      TRUE,
01382                                      pMeshData->ExtendFlags,
01383                                      TEXT(""), FALSE, NULL, NULL, pUndoableOp, 
01384                                      ((NodeSetProperty *)pMeshData->pOldPropNode));
01385             break;
01386 
01387         }
01388 
01389         pMeshData = (MeshNodeListItem *)MeshList.GetNext(pMeshData);
01390     }
01391 
01392     //*** Sort out the sentinel node which contains node properties and template attribs of any buttons ***
01393     
01394     // remove the import tags from the properties that the import added to the tree
01395     // we should have already taken all the data we need from them
01396     // this should only be stuff that doesn't extend left in this set
01397 
01398     if (Imported)
01399     {
01400         pNode = pNodeSetSentinel->FindLastChild();
01401         Node * pNextNode = NULL;
01402         while (pNode)
01403         {
01404             // scan the sentinel for imported properties that are marked "imported"
01405             // NB scans are done from bottom to top to find the last added items
01406             // which will be where the import left them
01407             pNextNode = pNode->FindPrevious();
01408             if (IS_A(pNode, NodeSetProperty) && ((NodeSetProperty*) pNode)->m_Imported)
01409             {
01410                 TRACE(((NodeSetProperty*) pNode)->GetName());
01411                 TRACE( _T(" imported and not deleted\n"));
01412 
01413                 ((NodeSetProperty*) pNode)->m_Imported = FALSE;
01414 
01415             }
01416 
01417             pNode = pNextNode;
01418         }
01419     }
01420 
01421     //*** Sort out the bar properties ***
01422 
01423     // fill up the bar property data in the sentinel
01424     // we dont have to do this on an import as the import pushed this data in for us
01425     // and we made sure that the bar names / numbers used by the import matched the 
01426     // unique new ones we generated.
01427     // however if we are cloning or duplicating we need to expand the bar props
01428     if (!Imported)
01429     {
01430         // get the ptr to the bar properties node
01431         NodeBarProperty * pNodeBarProperty = (NodeBarProperty*) ((NodeSetSentinel *)pNodeSetSentinel)->FindBarProperty();
01432         NodeBarProperty* pBarPropertyCopy = NULL; // our local copy of the props
01433 
01434         for (i = 0; i < MAX_IMPORTED_BARS; i++)
01435         {
01436             if (BarReplacedWithBar[i] != -1)
01437             {
01438                 // found a new bar that we have put in
01439 
01440                 // copy the old bar data into the new bar data as the new is a copy of the old
01441                 if (pBarPropertyCopy == NULL)
01442                     ALLOC_WITH_FAIL(pBarPropertyCopy, ((NodeBarProperty*) pNodeBarProperty->SimpleCopy()), pUndoableOp);
01443 
01444                 if (pBarPropertyCopy)
01445                 {
01446                     if ((INT32)(pBarPropertyCopy->HowMany()) <= BarReplacedWithBar[i])
01447                     {
01448                         UINT32 tempuint = 0;
01449                         while (((INT32)(pBarPropertyCopy->HowMany()) <= BarReplacedWithBar[i]) && tempuint != UINT_MAX)
01450                             tempuint = pBarPropertyCopy->Add(pBarPropertyCopy->Bar(i));
01451                     }
01452                     else
01453                         pBarPropertyCopy->Bar(BarReplacedWithBar[i]) = pBarPropertyCopy->Bar(i);
01454                 }
01455             }
01456         }
01457 
01458         // tidy up the bar properties hiding the old properties and replacing with the new
01459         // if the new exists
01460         if (pBarPropertyCopy)
01461         {
01462             pBarPropertyCopy->AttachNode(pNodeSetSentinel, LASTCHILD);
01463             pUndoableOp->DoHideNode(pNodeBarProperty, FALSE);
01464         }
01465     }
01466 
01467     //*** Tidy up and exit ***
01468 
01469     // the work is done tidy up the three lists
01470     NodeListItem * pListItem = (NodeListItem *)ExistingAttrList.GetHead();
01471     NodeListItem * pNextListItem = NULL;
01472     while (pListItem)
01473     {
01474         pNextListItem = (NodeListItem *)ExistingAttrList.GetNext(pListItem);
01475 
01476         // remove this entry from this list
01477         ExistingAttrList.RemoveItem((NodeListItem *)pListItem);
01478         delete pListItem;
01479 
01480         pListItem = pNextListItem;
01481     }
01482 
01483     pListItem = (NodeListItem *)ImportedAttrList.GetHead();
01484     pNextListItem = NULL;
01485     while (pListItem)
01486     {
01487         pNextListItem = (NodeListItem *)ImportedAttrList.GetNext(pListItem);
01488 
01489         // remove this entry from this list
01490         ImportedAttrList.RemoveItem((NodeListItem *)pListItem);
01491         delete pListItem;
01492 
01493         pListItem = pNextListItem;
01494     }
01495 
01496     pListItem = (NodeListItem *)MeshList.GetHead();
01497     pNextListItem = NULL;
01498     while (pListItem)
01499     {
01500         pNextListItem = (NodeListItem *)MeshList.GetNext(pListItem);
01501 
01502         // remove this entry from this list
01503         MeshList.RemoveItem((NodeListItem *)pListItem);
01504         delete pListItem;
01505 
01506         pListItem = pNextListItem;
01507     }
01508 
01509     SliceHelper::ValidateNodeSetSentinel();
01510 
01511     // update the name gallery when we are finished
01512     pNameGallery->FastUpdateNamedSetSizes();
01513 
01514     // Ensure that the Trigger sets are correctly flagged...
01515     SliceHelper::EnsureTriggerInfo();
01516 
01517 }
01518 
01519 
01520 /********************************************************************************************
01521 
01522 >   BOOL SliceHelper::BarNameExists(Node * pNode, String_256 &BarName, Node * pLid = NULL)
01523 
01524     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> JK
01525     Created:    4/10/99
01526     Returns:    TRUE if this bar name is used from this node down
01527     Params:     pNode       -   The node to check from
01528                 BarName     -   The name of the bar we are looking for.
01529                 pButtonName -   If provided fills in the button name found in a bar.
01530     Purpose:    Just a quick check to see if this bar name is used anywhere here.
01531                 Used to find unused bar names. 
01532 
01533 // Modified non recursive version written 17/9/00 (sjk)
01534 // pnode is the node to start looking from and pLid is the node to stop looking when we reach it
01535 // if pnode == plid or plid == 0 then it scans the entire branch defined by pnode
01536 ********************************************************************************************/
01537 BOOL SliceHelper::BarNameExists(Node * pNode, String_256 &BarName, Node * pLid)
01538 {
01539     if (pLid == NULL)
01540         pLid = pNode;
01541 
01542     Node * pNextNode = FindNextNameNode(pNode, pLid);
01543 
01544     while (pNextNode)
01545     {
01546         if (BarName.CompareTo(((TemplateAttribute *)pNextNode)->GetQuestion()) == 0)
01547             return TRUE;
01548 
01549         pNextNode = FindNextNameNode(pNextNode, pLid);
01550     }
01551 
01552     return FALSE;
01553 }
01554 /********************************************************************************************
01555 
01556 >   INT32 SliceHelper::FindTargetAndExtender(Node * pStartNode, String_256 & Target, String_256 & Extender, INT32 RequiredLevel, BYTE *pExtenderFlags)
01557 
01558     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> JK
01559     Created:    11/11/99
01560     Returns:    The level of compliance found of target and extender so far
01561     Params:     pStartNode  -   The node to check from
01562                 Target      -   Fills this in with the button that gets moved ( levels 1 & 2)
01563                 Extender    -   The set that triggers the target to extend  (level 2)
01564                 RequiredLevel-  Don't return for levels less than this
01565                 pExtenderFlags- If passed in and an extender is found this is filled in with the
01566                                 way the button extends.
01567 
01568     Purpose:    When you want to find out what extends what from a button level (ie you have a node).
01569 ********************************************************************************************/
01570 INT32 SliceHelper::FindTargetAndExtender(Node * pStartNode,
01571                                         String_256 & Target,
01572                                         String_256 & Extender, 
01573                                         INT32 RequiredLevel, 
01574                                         BYTE *pExtenderFlags,
01575                                         DocRect *pTargetRect,
01576                                         DocRect *pExtenderRect)
01577 {
01578     DocRect rTarget, rExtender;
01579     INT32 ret = 0;
01580     Node * pNode = SliceHelper::FindNextOfClass(pStartNode, pStartNode, CC_RUNTIME_CLASS(TemplateAttribute));
01581     while (pNode && ret < 2)
01582         {
01583         INT32 FoundLevel = 0;
01584         // if pNode extends
01585         NodeSetProperty* pSetProp =
01586                 Document::GetSelected()->GetSetSentinel()->FindPropertyNode(((TemplateAttribute *)pNode)->GetParam());
01587         NamedStretchProp* pProp = NULL;
01588         if (pSetProp) 
01589             pProp = (NamedStretchProp*) pSetProp->GetProperty(NamedStretchProp::nIndex);
01590 
01591         if (pProp && !pProp->GetTriggers().empty())
01592         {
01593             FoundLevel = 2;
01594             // get this extender
01595             //Extender = first name in the list of sets that extends target;
01596             Extender = pProp->GetTriggers().front().m_strSet;
01597 
01598             // record the exteding flags
01599             if (pExtenderFlags)
01600                 *pExtenderFlags = pProp->GetStretchType();
01601                 
01602             rExtender = pProp->GetRefUnionTriggerBounds();
01603         }
01604         else if (!GetBarName((TemplateAttribute *)pNode).IsEmpty())
01605         {
01606             // found a bar entry
01607             FoundLevel = 1;
01608         }
01609 
01610         if (FoundLevel > RequiredLevel && FoundLevel > ret)
01611         {
01612             Target = ((TemplateAttribute *)pNode)->GetParam();
01613             ret = FoundLevel;
01614             if (pProp)
01615                 rTarget = pProp->GetRefTargetBounds();
01616         }
01617 
01618         pNode = SliceHelper::FindNextOfClass(pNode, pStartNode, CC_RUNTIME_CLASS(TemplateAttribute));
01619         }
01620         
01621     if (pTargetRect)
01622         *pTargetRect = rTarget;
01623     if (pExtenderRect)
01624         *pExtenderRect = rExtender;
01625 
01626     return ret;
01627 }
01628 
01629 
01630 /********************************************************************************************
01631 
01632 >   BOOL SliceHelper::CreatePropertiesForSet(   const String_256& SetName,
01633                                             const String_256& BarName,
01634                                             BOOL isSlice,
01635                                             BOOL Stretches,
01636                                             BOOL IsBackBar,
01637                                             BYTE StretchFlags,
01638                                             const String_256& StretchedBy,
01639                                             BOOL DontOverWrite,
01640                                             const String_256& strRefTriggerSet,
01641                                             UndoableOperation* pOp)
01642 
01643     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
01644     Created:    11/11/99
01645     Params:     SetName     -   The name of the set that you wish to create properties for
01646                 BarName     -   The name of the bar if it is to belong to one, every thing but a button
01647                                 should ahve this empty
01648                 isSlice     -   Is it a slice
01649                 Stretches   -   Does it stretch
01650                 IsBackBar   -   Is it a back bar - if so it will find its own buttons and set its triggers
01651                 StretchFlags-   If it stretches how does it stretch
01652                 StretchedBy -   What stretches it. This is currently limited to just one set name for
01653                                 the purposes of this function. It could be extended to be a list later in the day.
01654                 DontOverWrite-  If this is TRUE it cuts out if the property already exists
01655                 strRefBoundsSet - the set, if not empty, whose reference bounds for triggers/targets
01656                                    should be copied into the newly created property.
01657                 pOp         -   Ptr to the undoable op
01658     Returns:    TRUE if it succeeded
01659     Purpose:    If you create a named set these days you need to create properties in the node
01660                 set sentinel too so that they are stored somewhere that can be saved out, undone, etc.
01661                 So if you have made any in your code this is a useful little number.
01662 
01663 ********************************************************************************************/   
01664 BOOL SliceHelper::CreatePropertiesForSet(   const String_256& SetName,
01665                                             const String_256& BarName,
01666                                             BOOL isSlice,
01667                                             BOOL Stretches,
01668                                             BOOL IsBackBar,
01669                                             BYTE StretchFlags,
01670                                             const String_256& StretchedBy,
01671                                             BOOL DontOverWrite,
01672                                             DocRect *pTargetRect,
01673                                             DocRect *pExtenderRect,
01674                                             UndoableOperation* pOp,
01675                                             NodeSetProperty * pExampleProp)
01676 {
01677     NodeSetSentinel * pSentinel = Document::GetSelected()->GetSetSentinel();
01678 
01679     if (!pSentinel)
01680         return FALSE;
01681 
01682     // find the property node for the setname
01683     NodeSetProperty* pOldSetProp =pSentinel->FindPropertyNode(SetName);
01684 
01685     // hide the old properties if they existed
01686     if (pOldSetProp)
01687     {
01688         if (DontOverWrite)
01689             return TRUE; // it exists and that is good enough for us
01690 
01691         // need a ptr to an op to hide a node
01692         ASSERT(pOp);
01693         pOp->DoHideNode(pOldSetProp, FALSE);
01694         TRACE( _T("Deleting property set of "));
01695         TRACE(SetName);
01696         TRACE( _T("\n"));
01697     }
01698 
01699     // create the new properties
01700     NodeSetProperty* pNewSetProp = pSentinel->CreatePropertyNode(SetName);
01701     if (pNewSetProp == 0 || !pNewSetProp->CreateDefaults()) return FALSE;
01702 
01703     // debug info
01704     TRACE( _T("Creating property set of "));
01705     TRACE(SetName);
01706     TRACE( _T(" "));
01707 
01708     NamedSliceProp* pNamedSliceProp = !isSlice ? new NamedSliceProp(SetName, isSlice) : 0;
01709     if (!pNamedSliceProp && !isSlice) return FALSE;
01710 
01711     NamedStretchProp* pNamedStretchProp = new NamedStretchProp(SetName, StretchFlags, Stretches);
01712     if (!pNamedStretchProp)
01713     {
01714         if (pNamedSliceProp) delete pNamedSliceProp;
01715         return FALSE;
01716     }
01717 
01718     // add the export props from the example if passed one
01719     // this will remember that the user wanted it as a jpeg and saved as whatever
01720     if (pExampleProp)
01721     {
01722         NamedExportProp * pExampleExportProp = (NamedExportProp *) pExampleProp->GetProperty(NamedExportProp::nIndex);
01723         if (pExampleExportProp)
01724         {
01725             NamedExportProp * pNamedExportProp = new NamedExportProp(*pExampleExportProp);
01726             if (pNamedExportProp)
01727             {
01728                 SGNameProp* pOld = pNewSetProp->SetProperty(pNamedExportProp);
01729                 if (pOld)
01730                     delete pOld;
01731             }
01732         }
01733     }
01734 
01735     // debugging info
01736     if (pNamedSliceProp) TRACE( _T("with slices "));
01737     if (Stretches) TRACE( _T("with stretching "));
01738 
01739     // add an extend trigger
01740     if (pNamedStretchProp)
01741     {
01742         if (!StretchedBy.IsEmpty() && !IsBackBar)
01743         {
01744             pNamedStretchProp->AddTrigger(StretchedBy);
01745 
01746             // Fix up the trigger and target reference rectangles.
01747             if (pTargetRect && pExtenderRect)
01748             {
01749                 TRACE( _T("button"));
01750                 pNamedStretchProp->SetRefTargetBounds(*pTargetRect);
01751                 pNamedStretchProp->SetRefUnionTriggerBounds(*pExtenderRect);
01752             }
01753         }
01754         else
01755         if (IsBackBar) // build a list of all the members of the bar to make triggers for the back bar
01756         {
01757             NameGallery * pNameGallery = NameGallery::Instance();
01758             SGUsedNames* pNames = pNameGallery ? pNameGallery->GetUsedNames() : NULL;
01759 
01760             if (pNameGallery && pNames)
01761             {
01762                 TRACE( _T("BackBar"));
01763                 // scan to get data about everything
01764                 pNameGallery->FastUpdateNamedSetSizes(FALSE);
01765 
01766                 INT32 BarNo = GetBarNumberFromBarName(BarName);
01767                 DocRect RefUnionTriggers;
01768                 DocRect BackBarBounds;
01769 
01770                 RefUnionTriggers.MakeEmpty();
01771                 BackBarBounds.MakeEmpty();
01772 
01773                 String_256 FoundSetName;
01774 
01775                 // reset all the names
01776                 SGNameItem * pNameGalleryItem = (SGNameItem*) pNames->GetChild();
01777 
01778                 while (pNameGalleryItem)
01779                 {
01780                     if (pNameGalleryItem->m_BarNumber == BarNo )//&& !pNameGalleryItem->IsABackBar())
01781                     {
01782                         pNameGalleryItem->GetNameText(&FoundSetName); 
01783 
01784                         if (FoundSetName.CompareTo(SetName) == 0)
01785                         {
01786                             // found yourself the back bar
01787                             BackBarBounds = pNameGalleryItem->GetSetBounds();
01788                         }
01789                         else
01790                         {
01791                             // found a button of this bar
01792                             RefUnionTriggers = RefUnionTriggers.Union(pNameGalleryItem->GetSetBounds());
01793                             pNamedStretchProp->AddTrigger(FoundSetName);
01794                         }
01795                     }
01796 
01797                     pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
01798                 }
01799 
01800                 pNamedStretchProp->SetRefTargetBounds(pTargetRect ? *pTargetRect : BackBarBounds);
01801                 pNamedStretchProp->SetRefUnionTriggerBounds(pExtenderRect ? *pExtenderRect : RefUnionTriggers);
01802             }
01803         }
01804     }
01805 
01806     // replace the slice and stretch properties
01807     if (pNamedSliceProp)
01808     {
01809         SGNameProp* pOld = pNewSetProp->SetProperty(pNamedSliceProp);
01810         if (pOld)
01811             delete pOld;
01812     }
01813 
01814     if (pNamedStretchProp)
01815     {
01816         SGNameProp* pOld = pNewSetProp->SetProperty(pNamedStretchProp);
01817         if (pOld)
01818             delete pOld;
01819     }
01820 
01821     TRACE( _T("\n"));
01822 
01823     // Create a hide node action to hide the node when we undo
01824     // this node was attached by the fn "NodeSetProperty* pNewSetProp = pSentinel->CreatePropertyNode(SetName);"
01825     // which is 107 lines above
01826     HideNodeAction* UndoHideNodeAction;
01827     HideNodeAction::Init(pOp,                    
01828                          pOp->GetUndoActions(), //&UndoActions,
01829                          pNewSetProp, 
01830                          TRUE,       // Include subtree size 
01831                          ( Action**)(&UndoHideNodeAction));
01832 
01833     // now we have added the property we should also add the standard attrib
01834     // to the node set sentinel too
01835     Node * pSentinelChild = pSentinel->FindFirstChild();
01836 
01837     BOOL Found = FALSE;
01838     while (pSentinelChild && !Found)
01839     {
01840         if (pSentinelChild->IsAnAttribute())
01841         {
01842             if (SetName.CompareTo(((TemplateAttribute *)pSentinelChild)->GetParam()) == 0)
01843             {
01844                 if ((BarName.CompareTo(((TemplateAttribute *)pSentinelChild)->GetQuestion()) == 0))
01845                     Found = TRUE;
01846                 else
01847                 {
01848                     pOp->DoHideNode(pSentinelChild, TRUE); // hide the near duplicate
01849                     pSentinelChild = NULL; // end the search
01850                 }
01851             }
01852         }
01853 
01854         if (pSentinelChild)
01855             pSentinelChild = pSentinelChild->FindNext();
01856     }
01857 
01858     if (!Found)
01859     {
01860         TemplateAttribute* pExtraAttr = new TemplateAttribute(String_256(TEXT("ObjectName")),
01861                                                             BarName,
01862                                                             SetName);
01863 
01864         if (!pExtraAttr) return TRUE;
01865 
01866         // add the attrib into the tree
01867         pExtraAttr->AttachNode(pSentinel, FIRSTCHILD);
01868 
01869         // Create a hide node action to hide the node when we undo 
01870         HideNodeAction* UndoHideNodeAction;
01871         HideNodeAction::Init(pOp,                    
01872                              pOp->GetUndoActions(), //&UndoActions,
01873                              pExtraAttr, 
01874                              TRUE,       // Include subtree size 
01875                              ( Action**)(&UndoHideNodeAction));
01876 
01877     }
01878     return TRUE;
01879 }
01880 
01881 
01882 /********************************************************************************************
01883 
01884 >   BOOL SliceHelper::MakeTriggerLikeExample(const String_256& NewButtonName,
01885                                              const String_256& ButtonName,
01886                                              const String_256* pExclude)
01887 
01888     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
01889     Created:    22/11/99
01890     Params:     NewButtonName   -   The button name that you wish to extend the things
01891                 ButtonName      -   The name of button as an example of what you wish this would
01892                                     stretch things like
01893                 pExclude        -   ptr to button to exclude looking at
01894     Returns:    TRUE if it succeeded, FALSE if we had a problem
01895     Purpose:    Adds NewButtonName to the lists of triggers that ButtonName belongs to.
01896                 This is used by the bars thatwant all the buttons to stretch the same background bar.
01897 
01898 ********************************************************************************************/   
01899 BOOL SliceHelper::MakeTriggerLikeExample(const String_256& NewButtonName,
01900                                          const String_256& ButtonName,
01901                                          const String_256* pExclude)
01902 {
01903     NodeSetSentinel* pSentinel = Document::GetSelected()->GetSetSentinel();
01904     if (!pSentinel) return FALSE;
01905 
01906     // find the property node for the setname
01907     NodeSetProperty* pSetProp =
01908         (NodeSetProperty*) FindNextOfClass(pSentinel, pSentinel, CC_RUNTIME_CLASS(NodeSetProperty));
01909     NamedStretchProp* pProp = NULL;
01910 
01911     // walk the list of node set properties
01912     while (pSetProp)
01913     {
01914         pProp = (NamedStretchProp*) pSetProp->GetProperty(NamedStretchProp::nIndex);
01915 
01916         BOOL ok = TRUE;
01917 
01918         if (pExclude)
01919         {
01920             // exclude a particular name
01921             ok = pSetProp->GetName().CompareTo(*pExclude);
01922         }
01923 
01924         if (ok && pProp && !pProp->GetTriggers().empty())
01925         {           
01926             // walk the list of triggers looking for the button name
01927             // if we find it add the new trigger to the list
01928             std::list<TriggerSet>::iterator p =
01929                 std::find(pProp->GetTriggers().begin(), pProp->GetTriggers().end(), ButtonName);
01930             // found ButtonName?
01931             if (p != pProp->GetTriggers().end())
01932             {
01933                 // make sure we haven't added this before
01934                 p = std::find(pProp->GetTriggers().begin(), pProp->GetTriggers().end(), NewButtonName);
01935                 if (p == pProp->GetTriggers().end())
01936                     pProp->AddTrigger(NewButtonName); // add this new trigger
01937             }
01938         }
01939         // find the next node set property
01940         pSetProp = (NodeSetProperty*) FindNextOfClass(pSetProp, pSentinel,
01941                                                       CC_RUNTIME_CLASS(NodeSetProperty));
01942     }
01943 
01944     return TRUE;
01945 
01946 }
01947 
01948 /********************************************************************************************
01949 
01950 >   DocRect SliceHelper::BoundingNodeSize(Node * pNode)
01951 
01952     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
01953     Created:    17/3/00
01954     Params:     pNode   - the node that you want to know how big it is
01955     Returns:    its size
01956     Purpose:    Get bounds was returning the acturate "how big is it"
01957                 This fn treats text that is of a size and the same font as being
01958                 the same size ignoring decsenders and ascenders.
01959 
01960   * WARNING *   It does this by examining the char size of the first char in each line
01961                 and getting a min and max height. It uses the width from getbounds().
01962                 So if fonts change mid way then descenders become a problem again.
01963                 But this is faster than checking each char.
01964 
01965 ********************************************************************************************/   
01966 DocRect SliceHelper::BoundingNodeSize(Node * pNode)
01967 {
01968     if (!pNode->IsBounded())
01969         return DocRect(0,0,0,0);
01970 
01971     DocRect rBounds = ((NodeRenderableBounded*) pNode)->GetBoundingRect();
01972 
01973     if (IS_A(pNode,TextStory))
01974     {
01975         DocRect rTextBounds;
01976         Node * pLid = pNode; 
01977         pNode = SliceHelper::FindNextOfClass(pLid, pLid, CC_RUNTIME_CLASS(TextChar));
01978 
01979         if (pNode)
01980         {
01981             if (((TextChar*) pNode)->GetMetricsRectBounds(&rTextBounds))
01982             {
01983                 rBounds.hi.y = rTextBounds.hi.y;
01984                 rBounds.lo.y = rTextBounds.lo.y;
01985             }
01986         }
01987         else
01988             return DocRect(0,0,0,0); // no text chars should return empty
01989 
01990         while (pNode)
01991         {
01992             pNode = SliceHelper::FindNextOfClass(pNode, pLid, CC_RUNTIME_CLASS(TextLine));
01993 
01994             if (pNode)
01995             {
01996                 pNode = SliceHelper::FindNextOfClass(pNode, pLid, CC_RUNTIME_CLASS(TextChar));
01997                 if (pNode)
01998                 {
01999                     if (((TextChar*) pNode)->GetMetricsRectBounds(&rTextBounds))
02000                     {
02001                         rBounds.hi.y = max(rTextBounds.hi.y, rBounds.hi.y);
02002                         rBounds.lo.y = min(rTextBounds.lo.y, rBounds.lo.y);
02003                     }
02004                 }
02005             }
02006         }
02007     }
02008     else
02009     if (IS_A(pNode,NodeShadowController)) // ignore the size of the shadow and just get me the size of the other bits
02010     {
02011         rBounds.MakeEmpty();
02012 
02013         pNode = pNode->FindFirstChild();
02014 
02015         while (pNode)
02016         {
02017             if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden() && !IS_A(pNode, NodeShadow))
02018                 rBounds = rBounds.Union(BoundingNodeSize(pNode));
02019 
02020             pNode = pNode->FindNext();
02021         }
02022         
02023     }
02024     else
02025     if (IS_A(pNode,NodeBevelController)) // ignore the size of the bevel and just get me the size of the other bits
02026     {
02027         rBounds.MakeEmpty();
02028 
02029         INT32 BevWidth = ((NodeBevelController *)pNode)->m_bOuter ? ((NodeBevelController *)pNode)->m_Indent : 0;
02030         pNode = pNode->FindFirstChild();
02031 
02032         while (pNode)
02033         {
02034             if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden()
02035                 && !IS_A(pNode, NodeBevel) && !IS_A(pNode, NodeBevelBegin))
02036             {
02037                 rBounds = rBounds.Union(BoundingNodeSize(pNode));
02038             }
02039 
02040             pNode = pNode->FindNext();
02041         }
02042 
02043         if (!rBounds.IsEmpty())
02044             rBounds.Inflate(BevWidth); // inflate the other bits' size by the bevel width
02045         
02046     }
02047     else
02048     if (IS_A(pNode,NodeContourController)) // ignore the size of the contour and just get me the size of the other bits
02049     {
02050         rBounds.MakeEmpty();
02051 
02052         // -ve width means outer contour!!?!
02053         // this is crazy but ignore inners so negate it to get the actual width increase
02054         INT32 Width = -((NodeContourController *)pNode)->GetWidth(); 
02055         pNode = pNode->FindFirstChild();
02056 
02057         while (pNode)
02058         {
02059             if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden()
02060                 && !IS_A(pNode, NodeContour))
02061             {
02062                 rBounds = rBounds.Union(BoundingNodeSize(pNode));
02063             }
02064 
02065             pNode = pNode->FindNext();
02066         }
02067 
02068         if (Width > 0)
02069             rBounds.Inflate(Width); // inflate the size of the other bits' by the width of the contour
02070     }
02071 
02072     return rBounds;
02073 }
02074 
02075 
02076 /********************************************************************************************
02077 
02078 >   INT32 SliceHelper::GetBarNumberFromBarName(const String_256 & BarName)
02079 
02080     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02081     Created:    6/4/00
02082     Returns:    the bar number (-1 for not in a bar)
02083     Purpose:    Converts the bar name into a bar number
02084 
02085 ********************************************************************************************/   
02086 INT32 SliceHelper::GetBarNumberFromBarName(const String_256 & BarName)
02087 {
02088     PTSTR pszMark = NULL;
02089     if (BarName.Length() > 3)
02090         return camStrtol( ((const TCHAR*)BarName) + 3, &pszMark, 10 ) - 1;
02091 
02092     return -1;
02093 }
02094 
02095 /***************************************/
02096 
02097 
02098 
02099 
02100 /********************************************************************************************
02101 
02102 >   TemplateAttribute * SliceHelper::FindFirstSetNodeBelongsTo(Node * pNode)
02103 
02104     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02105     Created:    12/5/00
02106     Returns:    
02107     Purpose:    returns the fist attrib that may apply to a node
02108 
02109 ********************************************************************************************/   
02110 TemplateAttribute * SliceHelper::FindFirstSetNodeBelongsTo(Node * pNode)
02111 {
02112     while (!pNode->IsLayer())
02113     {
02114         // test children for wix attrib
02115         Node * pChild = pNode->FindFirstChild();
02116 
02117         while (pChild)
02118         {
02119             if (pChild->IsAnAttribute() && IS_A(pChild,TemplateAttribute))
02120                 return (TemplateAttribute *) pChild;
02121 
02122             pChild = pChild->FindNext();
02123         }
02124 
02125         pNode = pNode->FindParent();
02126     }
02127 
02128     return NULL;
02129 }
02130 
02131 /********************************************************************************************
02132 
02133 >   String_256 SliceHelper::GetSetNameFromAttrib(Node * pNode)
02134 
02135     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02136     Created:    12/5/00
02137     Returns:    The set name from the attrib empty if it is not an attrib
02138     Purpose:    returns the fist attrib that may apply to a node
02139 
02140 ********************************************************************************************/   
02141 String_256 SliceHelper::GetSetNameFromAttrib(Node * pNode)
02142 {
02143     if (IS_A(pNode,TemplateAttribute))
02144         return ((TemplateAttribute *)pNode)->GetParam();
02145     else
02146         return "";
02147 }
02148 
02149 /********************************************************************************************
02150 
02151 >   TextStory * SliceHelper::FindNextTextStoryToSync( TextStory * pLastStory, Node * pLid, TextStory * pMasterStory, String_256 & ButtonName)
02152 
02153     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02154     Created:    12/5/00
02155     Returns:    The next text story that should be edited if the master has been edited
02156     Purpose:    scans for text stories that are of the named set and match the criteria to edit
02157                 when the master is edited.
02158 
02159 ********************************************************************************************/   
02160 TextStory * SliceHelper::FindNextTextStoryToSync( TextStory * pLastStory, Node * pLid, TextStory * pMasterStory,
02161                                                  const String_256 & ButtonName, const String_256 & MasterText,
02162                                                  BOOL AnyText)
02163 {
02164     Node * pStart = pLastStory;
02165 
02166     if (!pStart)
02167         pStart = pLid;
02168 
02169     Node * pNode = NULL;
02170 
02171     BOOL ok;
02172     TemplateAttribute * pAttrib = NULL;
02173 
02174     do
02175     {
02176         // find the next text story
02177         pNode = FindNextOfClass(pStart, pLid, CC_RUNTIME_CLASS(TextStory));
02178 
02179         ok = TRUE;
02180 
02181         if (!pNode)
02182             ok = FALSE;
02183 
02184         if (ok && pNode == pMasterStory)
02185             ok = FALSE;
02186 
02187 
02188         if (ok)
02189         {
02190             // is it in the right set?
02191             pAttrib = FindFirstSetNodeBelongsTo(pNode);
02192             if (!pAttrib)
02193                 ok = FALSE; // no set
02194         }
02195 
02196         if (ok)
02197         {
02198             // the name of the set must match and the question should have a string that implies it is a bar
02199             // not just any old user named set
02200             if (ButtonName.CompareTo(pAttrib->GetParam()) != 0 || pAttrib->GetQuestion().IsEmpty())
02201             {
02202                 ok = FALSE; // wrong set
02203 
02204                 // check other set names for this text story
02205                 while (!ok && pAttrib)
02206                 {
02207                     // look at brothers of this template attrib
02208                     pAttrib = (TemplateAttribute *) pAttrib->FindNext(CC_RUNTIME_CLASS(TemplateAttribute));
02209                     if (pAttrib && ButtonName.CompareTo(pAttrib->GetParam()) == 0 && !pAttrib->GetQuestion().IsEmpty())
02210                         ok = TRUE;
02211                 }
02212             }
02213         }
02214 
02215         // does its text match?
02216         if (ok && !AnyText)
02217         {
02218             if (MasterText != ((TextStory *)pNode)->GetStoryAsString())
02219                 ok = FALSE;
02220         }
02221 
02222         pStart = pNode;
02223 
02224     } while (!ok && pNode);
02225 
02226     return (TextStory *) pNode;
02227 }
02228 
02229 
02230 BOOL SliceHelper::TextStoriesHaveSameText (TextStory * pStory1, TextStory * pStory2)
02231 {
02232     Node * pTextLine1 = pStory1->FindFirstChild(CC_RUNTIME_CLASS(TextLine));
02233     Node * pTextLine2 = pStory2->FindFirstChild(CC_RUNTIME_CLASS(TextLine));
02234 
02235     while (pTextLine1 && pTextLine2)
02236     {
02237         if (!TextLinesHaveSameText((TextLine *)pTextLine1, (TextLine *)pTextLine2))
02238             return FALSE;
02239 
02240         pTextLine1 = pTextLine1->FindNext(CC_RUNTIME_CLASS(TextLine));
02241         pTextLine2 = pTextLine2->FindNext(CC_RUNTIME_CLASS(TextLine));
02242     }
02243 
02244     if (!pTextLine1 && !pTextLine2)
02245         return TRUE; // both empty!
02246 
02247     return FALSE; // one empty
02248 }
02249 
02250 BOOL SliceHelper::TextLinesHaveSameText (TextLine * pLine1, TextLine * pLine2)
02251 {
02252 //  TextNode * pVTN1 = pLine1->FindFirstVTN();
02253 //  TextNode * pVTN2 = pLine2->FindFirstVTN();
02254     Node * pChar1 = pLine1->FindFirstChild(CC_RUNTIME_CLASS(TextChar));
02255     Node * pChar2 = pLine2->FindFirstChild(CC_RUNTIME_CLASS(TextChar));
02256 
02257     while (pChar1 && pChar2)
02258     {
02259         if (((TextChar *)pChar1)->GetUnicodeValue() != ((TextChar *)pChar2)->GetUnicodeValue())
02260             return FALSE;
02261 
02262         pChar1 = pChar1->FindNext(CC_RUNTIME_CLASS(TextChar));
02263         pChar2 = pChar2->FindNext(CC_RUNTIME_CLASS(TextChar));
02264     }
02265 
02266     if (!pChar1 && !pChar2)
02267         return TRUE; // both empty!
02268 
02269     return FALSE; // one empty
02270 }
02271 
02272 
02273 /********************************************************************************************
02274 
02275 >   BOOL SliceHelper::SyncTextStories (TextStory * pStory, TextStory * pMaster, UndoableOperation * pOp)
02276 
02277     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02278     Created:    12/5/00
02279     Returns:    TRUE if it changes pStory
02280     Purpose:    Changes the characters in the story pStory to match those in pMaster.
02281                 In this process it attempts to minimise the amount of undo information required.
02282                 Calls the fn bellow SyncTextLines()
02283 
02284 ********************************************************************************************/   
02285 BOOL SliceHelper::SyncTextStories (TextStory * pStory, TextStory * pMaster, UndoableOperation * pOp)
02286 {
02287     Node * pTextLine = pStory->FindFirstChild(CC_RUNTIME_CLASS(TextLine));
02288     Node * pMasterTextLine = pMaster->FindFirstChild(CC_RUNTIME_CLASS(TextLine));
02289 
02290     BOOL ret = FALSE;
02291 
02292     while (pTextLine || pMasterTextLine)
02293     {
02294         pStory->ReleaseCached();
02295         pOp->DoInvalidateRegion(Document::GetSelectedSpread(), pStory->GetBoundingRect());
02296 
02297         // only existing line
02298         if (pTextLine && pMasterTextLine)
02299         {
02300             SyncTextLines((TextLine*)pTextLine, (TextLine*)pMasterTextLine, pOp);
02301             ret = TRUE;
02302             pTextLine = pTextLine->FindNext(CC_RUNTIME_CLASS(TextLine));
02303             pMasterTextLine = pMasterTextLine->FindNext(CC_RUNTIME_CLASS(TextLine));
02304         }
02305         else
02306         if (!pTextLine)
02307         {
02308             // master has added a line do so and then sync
02309             pTextLine = TextLine::CreateEmptyTextLine(pStory, LASTCHILD);
02310 
02311             SyncTextLines((TextLine*)pTextLine, (TextLine*)pMasterTextLine, pOp);
02312             ret = TRUE;
02313 
02314             pTextLine = NULL;
02315             pMasterTextLine = pMasterTextLine->FindNext(CC_RUNTIME_CLASS(TextLine));
02316         }
02317         else
02318         if (!pMasterTextLine)
02319         {
02320             // master has deleted a line
02321 
02322             // get the caret onto the top story to make sure it is not deleted out of the story
02323             if (pStory->GetCaret()->FindParent() == pTextLine)
02324                 pStory->MoveCaretToCharacter(pStory->FindFirstVTN(), PREV);
02325 
02326             TextLine * pTemp = (TextLine *)pTextLine;
02327             pTextLine = pTextLine->FindNext(CC_RUNTIME_CLASS(TextLine));
02328             pOp->DoHideNode(pTemp, TRUE);
02329             ret = TRUE;
02330         }
02331 
02332     }
02333 
02334     if (ret)
02335     {
02336         pStory->FormatAndChildren(pOp);
02337         // We also need to invalidate the region of the story
02338         //pOp->DoInvalidateNodeRegion((NodeRenderableBounded*) pStory, TRUE, FALSE);
02339         pStory->ReleaseCached();
02340         pOp->DoInvalidateRegion(Document::GetSelectedSpread(), pStory->GetBoundingRect());
02341     }
02342 
02343     return ret;
02344 }
02345 
02346 
02347 
02348 BOOL SliceHelper::SyncTextLines (TextLine * pLine1, TextLine * pLine2, UndoableOperation * pOp)
02349 {
02350     Node * pChar1 = pLine1->FindFirstChild(CC_RUNTIME_CLASS(TextChar));
02351     Node * pChar2 = pLine2->FindFirstChild(CC_RUNTIME_CLASS(TextChar));
02352 
02353     BOOL ok = TRUE;
02354 
02355     TRACEUSER( "Matt", _T("\n#############################################\nStart: "));
02356 
02357     // find the first 2 different chars
02358     while (ok && pChar1 && pChar2)
02359     {
02360         if (((TextChar *)pChar1)->GetUnicodeValue() != ((TextChar *)pChar2)->GetUnicodeValue())
02361             ok = FALSE;
02362 
02363         if (ok)
02364         {
02365             TRACEUSER( "Matt", _T("%c="), ((TextChar *)pChar1)->GetUnicodeValue());
02366             pChar1 = pChar1->FindNext(CC_RUNTIME_CLASS(TextChar));
02367             pChar2 = pChar2->FindNext(CC_RUNTIME_CLASS(TextChar));
02368         }
02369     }
02370 
02371     // Check if the two strings were the same - no point continuing!
02372     if (ok && !pChar1 && !pChar2)
02373         return TRUE; // they were the same!
02374 
02375 
02376     // Matt 15/12/2000
02377     // 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
02378     if (ok && pChar1 && !pChar2)
02379     {
02380         while (pChar1)
02381         {
02382             Node * pNodeNext = pChar1->FindNext(CC_RUNTIME_CLASS(TextChar));
02383             TRACEUSER( "Matt", _T("Deleting From Slave %c\n"), ((TextChar *)pChar1)->GetUnicodeValue());
02384             pOp->DoHideNode(pChar1, TRUE);
02385             pChar1 = pNodeNext;
02386         }
02387 
02388         return TRUE;
02389     }
02390 
02391 
02392     Node * pChar1e = pLine1->FindLastChild(CC_RUNTIME_CLASS(TextChar));
02393     Node * pChar2e = pLine2->FindLastChild(CC_RUNTIME_CLASS(TextChar));
02394 
02395     ok = TRUE;
02396 
02397     BOOL RequireDel = FALSE;
02398     BOOL RequireAdd = FALSE;
02399 
02400 
02401     TRACEUSER( "Matt", _T("\nEnd:   "));
02402 
02403     // find the last 2 different chars
02404     while (ok && pChar1e && pChar2e)
02405     {
02406         if (((TextChar *)pChar1e)->GetUnicodeValue() != ((TextChar *)pChar2e)->GetUnicodeValue())
02407             ok = FALSE;
02408 
02409         if (ok && pChar1e == pChar1)
02410         {
02411             ok = FALSE;
02412             RequireAdd = TRUE;
02413         }
02414 
02415         if (ok && pChar2e == pChar2)
02416         {
02417             ok = FALSE;
02418             RequireDel = TRUE;
02419         }
02420 
02421         if (ok)
02422         {
02423             TRACEUSER( "Matt", _T("%c="), ((TextChar *)pChar1e)->GetUnicodeValue());
02424             pChar1e = pChar1e->FindPrevious(CC_RUNTIME_CLASS(TextChar));
02425             pChar2e = pChar2e->FindPrevious(CC_RUNTIME_CLASS(TextChar));
02426         }
02427     }
02428     TRACEUSER( "Matt", _T("\n"));
02429 
02430 
02431     BOOL AddChar2e = FALSE;
02432 
02433     // Matt 15/12/2000
02434     // Bodge-Case Flagging to make it add the correct no. of chars after concatenating two lines
02435     if (!ok && !pChar1 && pChar1e)
02436     {
02437         if (pChar1e->FindNext(CC_RUNTIME_CLASS(TextChar)))
02438         {
02439             TRACEUSER( "Matt", _T("\nFlagging AddChar2e\n"));
02440             AddChar2e = TRUE;
02441         }
02442     }
02443 
02444     // add chars just before pchar1 of the range of text chars between pchar2 and pchar2e
02445     Node * pNode = pChar2;
02446     Node * pDest = pChar1;
02447     AttachNodeDirection TailAttachDirection = PREV;
02448 
02449     if (!pDest)
02450     {
02451         // add the chars onto the end
02452 
02453         // put it before the EOFL node
02454         pDest = pLine1->FindLastChild(CC_RUNTIME_CLASS(EOLNode));
02455         if (!pDest)
02456         {
02457             // failling that slap it on the end
02458             pDest = pLine1;
02459             TailAttachDirection = LASTCHILD;
02460         }
02461     }
02462 
02463     if (!RequireDel) RequireDel = pChar1e && pChar1 && pChar1e->FindNext(CC_RUNTIME_CLASS(TextChar)) != pChar1;
02464     if (!RequireAdd)  RequireAdd = pChar2e && pChar2e->FindNext(CC_RUNTIME_CLASS(TextChar)) != pChar2;
02465 
02466     if (RequireDel) TRACEUSER( "Matt", _T("Requires a del\n"));
02467     if (RequireAdd) TRACEUSER( "Matt", _T("Requires an add\n"));
02468 
02469     ok = TRUE;
02470 
02471     while (RequireAdd && pNode && ok)
02472     {
02473         // this it the last time?
02474         ok =  pNode != pChar2e;
02475 
02476         // Make a new text char node
02477         TextChar * pTheCopy = NULL;
02478 
02479         ALLOC_WITH_FAIL(pTheCopy, (new TextChar()) , pOp);
02480         pTheCopy->SetUnicodeValue(((TextChar *)pNode)->GetUnicodeValue());
02481 
02482         TRACEUSER( "Matt", _T("Adding %c\n"), ((TextChar *)pNode)->GetUnicodeValue());
02483 
02484         // insert the node under the destination
02485         pOp->DoInsertNewNode((NodeRenderableBounded *)pTheCopy, pDest, TailAttachDirection,
02486                      TRUE,      // Do Invalidate region 
02487                      FALSE,     // Dont Clear the selections
02488                      FALSE,     // Dont select this object
02489                      FALSE);    // Dont normalise attribs
02490 
02491         // attach the next node after this one
02492         TailAttachDirection = NEXT;
02493         pDest = pTheCopy;
02494 
02495         pNode = pNode->FindNext(CC_RUNTIME_CLASS(TextChar));
02496     }
02497 
02498     // delete the text chars between pchar1 & pchar1e
02499     pNode = pChar1;
02500     BOOL hiddenall = FALSE;
02501 
02502     if (RequireDel)
02503         while (!hiddenall)
02504         {
02505             if (pNode == pChar1e)
02506                 hiddenall = TRUE;
02507 
02508             if (pNode)
02509             {
02510                 Node * pNodeNext = pNode->FindNext(CC_RUNTIME_CLASS(TextChar));
02511                 TRACEUSER( "Matt", _T("Deleting %c\n"), ((TextChar *)pNode)->GetUnicodeValue());
02512                 pOp->DoHideNode(pNode, TRUE);
02513                 pNode = pNodeNext;
02514             }
02515             else
02516                 hiddenall = TRUE;
02517         }
02518 
02519     // Matt 15/12/2000
02520     // If it was necessary to flag above that pChar2e and all following chars should be added, then do so...
02521     if (AddChar2e && pChar2e)
02522     {
02523         AttachNodeDirection TailAttachDirection = PREV;
02524         Node * pDestination = pLine1->FindLastChild(CC_RUNTIME_CLASS(EOLNode));
02525         Node * pNode = pChar2e->FindNext(CC_RUNTIME_CLASS(TextChar));
02526 
02527         if (!pDestination)
02528         {
02529             pDestination = pLine1;
02530             TailAttachDirection = LASTCHILD;
02531         }
02532 
02533         while (pNode)
02534         {
02535             // Make a new text char node
02536             TextChar * pTheCopy = NULL;
02537 
02538             ALLOC_WITH_FAIL(pTheCopy, (new TextChar()) , pOp);
02539             pTheCopy->SetUnicodeValue(((TextChar *)pNode)->GetUnicodeValue());
02540 
02541             TRACEUSER( "Matt", _T("Extra-Adding %c\n"), ((TextChar *)pTheCopy)->GetUnicodeValue());
02542 
02543             // insert the node under the destination
02544             pOp->DoInsertNewNode((NodeRenderableBounded *)pTheCopy, pDestination, TailAttachDirection,
02545                          TRUE,      // Do Invalidate region 
02546                          FALSE,     // Dont Clear the selections
02547                          FALSE,     // Dont select this object
02548                          FALSE);    // Dont normalise attribs
02549 
02550             // attach the next node after this one
02551             TailAttachDirection = NEXT;
02552             pDestination = pTheCopy;
02553 
02554             pNode = pNode->FindNext(CC_RUNTIME_CLASS(TextChar));
02555         }
02556     }
02557 
02558     TRACEUSER( "Matt", _T("Done\n"));
02559     return TRUE;
02560 }
02561 
02562 /********************************************************************************************
02563 
02564 >   BOOL SliceHelper::OnTextStoryChanged(TextStory * pMasterStory, UndoableOperation * pOp, ObjChangeParam * pObjChange)
02565 
02566     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02567     Created:    12/5/00
02568     Returns:    TRUE if any changes were made
02569     Purpose:    Call this if a master text may have been edited.
02570                 It tests to see if the text is a master text.
02571                 Finds slave text stories and syncs these with the master text.
02572 
02573 ********************************************************************************************/   
02574 BOOL SliceHelper::OnTextStoryChanged(TextStory * pMasterStory, UndoableOperation * pOp, ObjChangeParam * pObjChange, const String_256 & MasterText)
02575 {
02576     TemplateAttribute * pAttrib = SliceHelper::FindFirstSetNodeBelongsTo(pMasterStory);
02577 
02578     // the attrib is not a bar attrib? 
02579     while (pAttrib && pAttrib->GetQuestion().IsEmpty())
02580     {
02581         // look at brothers of this template attrib
02582         pAttrib = (TemplateAttribute *) pAttrib->FindNext(CC_RUNTIME_CLASS(TemplateAttribute));
02583     }
02584     
02585     if (!pAttrib)
02586         return FALSE;
02587 
02588     String_256 MasterSetName = SliceHelper::GetSetNameFromAttrib((Node *)pAttrib);
02589 
02590     if (MasterSetName.IsEmpty())
02591         return FALSE;
02592 
02593     Spread * pSpread =  Document::GetSelectedSpread();
02594 
02595     CaretNode * pCaret = pMasterStory->GetCaret();
02596     BOOL CaretSelected = pCaret->IsSelected();
02597 
02598     Node * pStory = SliceHelper::FindNextTextStoryToSync(NULL, pSpread, pMasterStory, MasterSetName, MasterText);
02599     
02600     BOOL ret = FALSE;
02601 
02602     while (pStory)
02603     {
02604         if (pStory != pMasterStory)
02605         {
02606             if (!pObjChange || ((TextStory *)pStory)->GetCaret()->AllowOp(pObjChange))
02607             {
02608                 SliceHelper::SyncTextStories((TextStory *)pStory, pMasterStory, pOp);
02609                 ret = TRUE;
02610             }
02611         }
02612 
02613         pStory = SliceHelper::FindNextTextStoryToSync((TextStory *)pStory, pSpread, pMasterStory, MasterSetName, MasterText);
02614     }
02615 
02616     pCaret->SetSelected(CaretSelected);
02617 
02618     return ret;
02619 }
02620 
02621 
02622 /********************************************************************************************
02623 
02624 >   BOOL PurgeUseOfSetName(const StringBase & SetName, UndoableOperation* pOp)
02625 
02626     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02627     Created:    21/5/00
02628     Returns:    TRUE if any changes were made
02629     Purpose:    "SetName" should be a new clean set
02630                 there should be no old references to this "SetName" in any extend lists
02631                 remove them if there are any.
02632                 find the property node for the setname
02633 
02634 ********************************************************************************************/   
02635 BOOL SliceHelper::PurgeUseOfSetName(const StringBase & SetName, UndoableOperation* pOp, const String_256 * pReplacementName)
02636 {
02637     NodeSetSentinel* pSentinel = Document::GetSelected()->GetSetSentinel();
02638     if (!pSentinel) return FALSE;
02639 
02640     NodeSetProperty* pSetProp =
02641         (NodeSetProperty*) FindNextOfClass(pSentinel, pSentinel, CC_RUNTIME_CLASS(NodeSetProperty));
02642     NamedStretchProp* pProp = NULL;
02643 
02644     BOOL ret = FALSE;
02645 
02646     // walk the list of node set properties
02647     while (pSetProp)
02648     {
02649         pProp = (NamedStretchProp*) pSetProp->GetProperty(NamedStretchProp::nIndex);
02650 
02651         BOOL ok = TRUE;
02652 
02653         if (ok && pProp && !pProp->GetTriggers().empty())
02654         {           
02655             // walk the list of triggers looking for the set name
02656             // if we find it remove this trigger from the list
02657             std::list<TriggerSet>::iterator p =
02658                 std::find(pProp->GetTriggers().begin(), pProp->GetTriggers().end(), SetName);
02659             // found SetName?
02660             if (p != pProp->GetTriggers().end())
02661             {
02662                 NodeSetProperty* pNewSetProp = (NodeSetProperty*) pSetProp->SimpleCopy();
02663                 if (pNewSetProp)
02664                 {
02665                     ((NamedStretchProp*) pNewSetProp->GetProperty(NamedStretchProp::nIndex))->RemoveTrigger(SetName);
02666                     if (pReplacementName)
02667                         ((NamedStretchProp*) pNewSetProp->GetProperty(NamedStretchProp::nIndex))->AddTrigger(*pReplacementName);
02668 
02669                     // add the prop into the tree
02670                     pNewSetProp->AttachNode(pSetProp, NEXT);
02671 
02672                     // Create a hide node action to hide the node when we undo 
02673                     HideNodeAction* UndoHideNodeAction;
02674                     HideNodeAction::Init(pOp,                    
02675                                          pOp->GetUndoActions(), //&UndoActions,
02676                                          pNewSetProp, 
02677                                          TRUE,       // Include subtree size 
02678                                          ( Action**)(&UndoHideNodeAction));
02679 
02680                     // delete the original
02681                     pOp->DoHideNode(pSetProp, TRUE); // hide the near duplicate
02682 
02683                     // carry on the search from here
02684                     pSetProp = pNewSetProp;
02685 
02686                     ret = TRUE;
02687                 }
02688             }
02689         }
02690         // find the next node set property
02691         pSetProp = (NodeSetProperty*) FindNextOfClass(pSetProp, pSentinel,
02692                                                       CC_RUNTIME_CLASS(NodeSetProperty));
02693     }
02694 
02695     return ret;
02696 }
02697 
02698 
02699 /********************************************************************************************
02700 
02701 >   DocRect SliceHelper::ScanForSetSizeExcluding (const String_256 & IncludeSet, const String_256 & ExcludeSet)
02702 
02703     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02704     Created:    22/6/00
02705     Returns:    The bounding rect of the set "IncludeSet" taking all members of "IncludeSet"
02706                 that do NOT belong in "ExcludeSet" aswell.
02707     Purpose:    Caculating clean set sizes for extending buttons
02708 
02709 ********************************************************************************************/   
02710 DocRect SliceHelper::ScanForSetSizeExcluding (const String_256 & IncludeSet, const String_256 & ExcludeSet)
02711 {
02712     DocRect r;
02713     r.MakeEmpty();
02714 
02715     // scan the tree looking for name attribs
02716     // find one and the size of the parent should be added to the bounds of the set
02717     Spread * pSpread =  Document::GetSelectedSpread();
02718     if (!pSpread)
02719         return r;
02720 
02721     Node * pNode = NULL;
02722     Node * pTop = pSpread;
02723     Node * pNewNode = NULL;
02724 
02725     DocRect TempRect;
02726     TempRect.MakeEmpty();
02727     BOOL CleanNode = TRUE;
02728 
02729     // scan from the first layer all through the layers since they are brothers of this layer
02730     pNode = SliceHelper::FindNextOfClass(pTop, pTop, CC_RUNTIME_CLASS(TemplateAttribute));
02731 
02732     // for each marker in the tree
02733     while (pNode)
02734     {
02735         BOOL Siblings = TRUE;
02736         do
02737         {
02738             if (IncludeSet.CompareTo(((TemplateAttribute *)pNode)->GetParam()) == 0)
02739                 TempRect = SliceHelper::BoundingNodeSize(pNode->FindParent());
02740 
02741             if (ExcludeSet.CompareTo(((TemplateAttribute *)pNode)->GetParam()) == 0)
02742                 CleanNode = FALSE;
02743 
02744             pNewNode = SliceHelper::FindNextOfClass(pNode, pTop, CC_RUNTIME_CLASS(TemplateAttribute));
02745 
02746             Siblings = pNewNode && pNewNode->FindParent() == pNode->FindParent();
02747 
02748             pNode = pNewNode;
02749 
02750         } while (Siblings);
02751 
02752         if (CleanNode && !TempRect.IsEmpty())
02753             r = r.Union(TempRect);
02754 
02755         CleanNode = TRUE;
02756         TempRect.MakeEmpty();
02757     }
02758 
02759     return r;
02760 }
02761 
02762 
02763 /********************************************************************************************
02764 
02765 >   BOOL SliceHelper::BarExistsOnLayer(const String_256 &BarName, const String_256 &Layer)
02766 
02767     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
02768     Created:    26/6/00
02769     Returns:    TRUE if an element of the bar is found on the layer, FALSE if not found
02770     Purpose:    Fast scan for existance
02771 
02772 ********************************************************************************************/   
02773 BOOL SliceHelper::BarExistsOnLayer(const String_256 &BarName, const String_256 &Layer)
02774 {
02775     Node * pLayer = SliceHelper::FindLayerCalled(Layer);
02776 
02777     if (!pLayer) return FALSE;
02778 
02779     Node * pNode = pLayer;
02780     do
02781     {
02782         pNode = FindNextOfClass(pNode, pLayer, CC_RUNTIME_CLASS(TemplateAttribute));
02783         if (pNode && BarName.CompareTo(((TemplateAttribute *)pNode)->GetQuestion()) == 0)
02784             return TRUE;
02785     } while (pNode);
02786 
02787     return FALSE;
02788 }
02789 
02790 // find out if the rect r intersects with any bars (sjk 27/6/00)
02791 void SliceHelper::BarNameInRect(DocRect r, String_256 *pBarName)
02792 {
02793     NameGallery * pNameGallery = NameGallery::Instance();
02794     if (!pNameGallery)
02795         return;
02796 
02797     SGUsedNames* pNames = pNameGallery->GetUsedNames();
02798     if (!pNames)
02799         return;
02800 
02801     SGNameItem * pNameGalleryItem = (SGNameItem*) pNames->GetChild();
02802 
02803     while (pNameGalleryItem)
02804     {
02805         if (pNameGalleryItem->m_BarNumber >= 0 && r.IsIntersectedWith(pNameGalleryItem->GetSetBounds()))
02806         {
02807             pBarName->MakeMsg(_R(IDS_BARNAME), pNameGalleryItem->m_BarNumber + 1);
02808             return;
02809         }
02810         pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
02811     }
02812 }
02813 
02814 // add an action to show this Layer undoably (sjk 6/7/00)
02815 void SliceHelper::ShowLayer(BOOL Visible, Layer * pLayer, Spread * pSpread, UndoableOperation * pUndoOp)
02816 {
02817     // dont put in the undo action if we have done nothing
02818     if (pLayer->IsVisible() == Visible)
02819         return;
02820 
02821     // Make layer visible/invisible undoably
02822     OpLayerGalParam Param(LAYER_VISIBLE, pSpread);
02823     Param.NewState = Visible;
02824     Param.pLayer = pLayer;
02825     LayerStateAction::Init(pUndoOp, pUndoOp->GetUndoActions(), Param);
02826 }
02827 
02828 // uses the name gallery data to work out if the selection
02829 // consists of whole sets or not. We need to know if we have a partial set in the selection
02830 // returns  0 = contains a parts of a set
02831 //          1 = contains all of the set
02832 //          2 = selection not part of a set at all
02833 // sjk 7/7/00
02834 INT32 SliceHelper::DoesSelectionOnlyContainCompleteSets()
02835 {
02836     NameGallery * pNameGallery = NameGallery::Instance();
02837     if (!pNameGallery)
02838         return 2;
02839 
02840     pNameGallery->FastUpdateNamedSetSizes(); // Ensure timely info in NameGallery
02841     SGUsedNames* pNames = pNameGallery->GetUsedNames();
02842     if (!pNames)
02843         return 2;
02844 
02845     SGNameItem * pNameGalleryItem = (SGNameItem*) pNames->GetChild();
02846 
02847     INT32 ret = 2; // default as none of any set
02848     INT32 count = 0;
02849 
02850     while (pNameGalleryItem)
02851     {
02852         count = pNameGalleryItem->GetSelectedCount();
02853         if ( count > 0 && count < pNameGalleryItem->GetObjectCount())
02854         {
02855             return 0; // has part of this set
02856         }
02857         else if (count > 0)
02858             ret = 1; // has all of this set then
02859 
02860         pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
02861     }
02862 
02863     return ret;
02864 }
02865 
02866 // Small function to scan the tree looking for a named object
02867 // pNode -  the start of the scan
02868 // pLid -   the node that defines the branch to look down
02869 // SetName - the name of the set we are looking for
02870 // return TRUE if it is found, false otherwise
02871 // sjk (18/9/00 or there abouts)
02872 BOOL SliceHelper::SetUsedInTree(Node * pNode, const String_256 &SetName, Node * pLid)
02873 {
02874     if (pLid == NULL)
02875         pLid = pNode;
02876 
02877     pNode = SliceHelper::FindNextNameNode(pNode, pLid);
02878     while (pNode)
02879     {
02880         if (SetName.CompareTo(((TemplateAttribute *)pNode)->GetParam()) == 0)
02881             return TRUE;
02882         pNode = SliceHelper::FindNextNameNode(pNode, pLid);
02883     }
02884 
02885     return FALSE;
02886 }
02887 
02888 // deletes the attribute and the properties in the node set sentinel
02889 void SliceHelper::DeleteUnusedReferences(const String_256 &SetName, UndoableOperation * pOp)
02890 {
02891     NodeSetSentinel * pNodeSetSentinel = Document::GetSelected()->GetSetSentinel();
02892     if (!pNodeSetSentinel)
02893         return;
02894 
02895     // remove all properties of the deleted node
02896     Node * pNode = pNodeSetSentinel->FindPropertyNode(SetName);
02897     if (pNode)
02898         pOp->DoHideNode(pNode, FALSE);
02899 
02900     // deleted bar members should be got rid of completely
02901     // remove the attribs from the sentinel
02902     pNode = pNodeSetSentinel->GetNameAttr(SetName);
02903     if (pNode)
02904         pOp->DoHideNode(pNode, FALSE);
02905 }
02906 
02907 // a controller has been added to the tree (such as a bevel controller)
02908 // move any names from normal nodes onto the controller
02909 BOOL SliceHelper::AddNamesToController (UndoableOperation * pOp, Node * pCtrlr)
02910 {
02911     Node * pNode = pCtrlr->FindFirstChild();
02912 
02913     while (pNode)
02914     {
02915         if (!pNode->IsAnAttribute() && !pNode->IsNodeHidden())
02916         {
02917             Node * pAttr = NULL;
02918 
02919             do
02920             {
02921                 // find any name attrs on this node
02922                 pAttr = SliceHelper::FindNextNameNode(pNode, pNode);
02923 
02924                 // does this already exist on the controller?
02925                 Node * pExistingAttr = NULL;
02926                 Node * pTemp = NULL;
02927                 if (pAttr)
02928                 {
02929                     for (pTemp = pCtrlr->FindFirstChild();
02930                         pTemp != NULL && pExistingAttr == NULL;
02931                         pTemp = pTemp->FindNext() )
02932                         {
02933                             if (pTemp->IsAnObjectName())
02934                             {
02935                                 if (((TemplateAttribute *) pTemp)->GetParam() == ((TemplateAttribute *) pAttr)->GetParam())
02936                                     pExistingAttr = pTemp;
02937                             }
02938                         }
02939 
02940                     pTemp = SliceHelper::FindNextNameNode(pAttr, pNode);
02941                 
02942                     // didn't exist on controller so add it
02943                     if (!pExistingAttr)
02944                     {
02945                         TemplateAttribute* pNewAttr = new TemplateAttribute(String_256(TEXT("ObjectName")),
02946                                                             ((TemplateAttribute *) pAttr)->GetQuestion(),
02947                                                             ((TemplateAttribute *) pAttr)->GetParam());
02948 
02949                         // add the new attrib to the tree
02950                         if (pNewAttr) 
02951                         {
02952                             pNewAttr->AttachNode(pCtrlr, FIRSTCHILD);
02953                             // Create a hide node action to hide the node when we undo 
02954                             HideNodeAction* UndoHideNodeAction;
02955                             HideNodeAction::Init(pOp,                    
02956                                                  pOp->GetUndoActions(), //&UndoActions,
02957                                                  pNewAttr, 
02958                                                  TRUE,       // Include subtree size 
02959                                                  ( Action**)(&UndoHideNodeAction));
02960                         }   
02961                     }
02962 
02963                     // did exist so just delete the duplicate
02964                     pOp->DoHideNode(pAttr, FALSE);
02965 
02966                     pAttr = pTemp;
02967                 }
02968 
02969             }while (pAttr != NULL);
02970             
02971         }
02972 
02973         pNode = pNode->FindNext();
02974     }
02975 
02976     return TRUE;
02977 }
02978 
02979 // reverse of the above operation
02980 // factorises the name attribs down from the controller
02981 // controller MUST be deleted after this function is called
02982 BOOL SliceHelper::RemoveNamesFromController (UndoableOperation * pOp, Node * pCtrlr)
02983 {
02984     Node * pNode = pCtrlr->FindFirstChild();
02985 
02986     while (pNode)
02987     {
02988         // found a name attribute
02989         if (pNode->IsAnObjectName())
02990         {
02991             // add this to all proper nodes that are children of the controller
02992             Node * pTemp = NULL;
02993             for (pTemp = pCtrlr->FindFirstChild();
02994                 pTemp != NULL;
02995                 pTemp = pTemp->FindNext() )
02996                 {
02997                     // add it to all nodes excluding attributes, bevels and contours
02998                     if (!pTemp->IsNodeHidden() && !pTemp->IsAnAttribute() && !pTemp->IsABevel() && !pTemp->IsAContour()
02999                         && !IS_A(pTemp, NodeBevelBegin))
03000                     {
03001                         // add the attr node to this child
03002                         TemplateAttribute* pAttr = new TemplateAttribute(String_256(TEXT("ObjectName")),
03003                                                             ((TemplateAttribute *) pNode)->GetQuestion(),
03004                                                             ((TemplateAttribute *) pNode)->GetParam());
03005 
03006                         // add the new attrib to the tree
03007                         if (pAttr) 
03008                         {
03009                             pAttr->AttachNode(pTemp, FIRSTCHILD);
03010                             // Create a hide node action to hide the node when we undo 
03011                             HideNodeAction* UndoHideNodeAction;
03012                             HideNodeAction::Init(pOp,                    
03013                                                  pOp->GetUndoActions(), //&UndoActions,
03014                                                  pAttr, 
03015                                                  TRUE,       // Include subtree size 
03016                                                  ( Action**)(&UndoHideNodeAction));
03017 
03018                         }   
03019                     }
03020                 }
03021 
03022             // remove this from the controller
03023             pTemp = pNode;
03024             pNode = pNode->FindNext();
03025             pOp->DoHideNode(pTemp, FALSE);
03026         }
03027         else
03028             pNode = pNode->FindNext();
03029     }
03030 
03031     return TRUE;
03032 }
03033 
03034 
03035 
03036 /********************************************************************************************
03037 
03038 >   BOOL SliceHelper::ModifySelectionToContainWholeButtonElements()
03039 
03040     Author:     Priestley (Xara Group Ltd) <camelotdev@xara.com>
03041     Created:    6/11/00
03042     Returns:    TRUE all the time...
03043 
03044 ********************************************************************************************/   
03045 
03046 BOOL SliceHelper::ModifySelectionToContainWholeButtonElements()
03047 {
03048     // Get a pointer to the NameGallery, and make sure it is up-to-date
03049     NameGallery *pNameGallery = NameGallery::Instance();
03050     if (!pNameGallery)
03051         return TRUE;
03052     pNameGallery->FastUpdateNamedSetSizes();
03053 
03054     //Get the first entry in the 'UsedNames' field of the NameGallery
03055     SGNameItem *pNameGalleryItem = (SGNameItem*) pNameGallery->GetUsedNames()->GetChild();
03056 
03057     // Build up a list of all the nodes used in the current selection. We will prune this list at every opportunity
03058     // in order to minimise the search space...
03059 
03060     SelRange* pSelRng = GetApplication()->FindSelection();
03061     RangeControl rangecontrol = pSelRng->GetRangeControlFlags();
03062     rangecontrol.PromoteToParent = TRUE;
03063     pSelRng->Range::SetRangeControl(rangecontrol);
03064     pSelRng->Update();
03065 
03066     List *pSelList = pSelRng->MakeListOfNodes(FALSE);
03067     bool unchanged = true; // Assume that no changes will be made...
03068 
03069     while (pNameGalleryItem)
03070     {
03071         INT32 CurrentSelection = pNameGalleryItem->GetSelectedCount();
03072 
03073         // If the named set is fully selected or not selected at all - there's no need to consider it further, otherwise...
03074         if ((CurrentSelection > 0) && (CurrentSelection != pNameGalleryItem->GetObjectCount()))
03075         {
03076             // Then we are interested in this named set - it is not fully selected, but it does appear
03077             // in our current selection (obviously) so we need to determine:
03078             // a) Is this named set a button bar element?
03079             // b) If so, are all elements on one layer selected?
03080             // c) What is the GDP of Bolivia, given that carrots are 50p/lb?
03081             // d) How is an oxbow lake formed? (see, you thought you'd never need to know that)
03082             String_256 StrName;
03083             StrName.Empty();
03084             pNameGalleryItem->GetNameText(&StrName);
03085             TemplateAttribute* pAttr;
03086 
03087             NodeListItem *pItem;
03088             Node *pNode;
03089             
03090             // For each node in the list, check if it is a member of the current named set...
03091             for (pItem = (NodeListItem *)pSelList->GetHead(); pItem; pItem = (NodeListItem *)pSelList->GetNext(pItem))
03092             {
03093                 // Retrieve the current node from the NodeListItem...
03094                 pNode = pItem->pNode;
03095 
03096                 for (pAttr = (TemplateAttribute*)pNode->FindFirstAttr(&Node::IsAnObjectName); pAttr; pAttr = (TemplateAttribute*)pAttr->FindNextAttr(&Node::IsAnObjectName))
03097                 {
03098                     // This may seem a redundant check - but it's not! When we find that all of one layer has been selected
03099                     // we select all of the layer and update the NameGallery - if we check within this 'for' loop, then
03100                     // we no longer need to do computation when all of the named set is already selected...
03101                     if (!pNameGalleryItem->IsAllSelected())
03102                     {
03103                         if ((StrName == pAttr->GetParam()) && (!(pAttr->GetQuestion().IsEmpty())))
03104                         {
03105                             // Then this node is a member of this named set AND it is a button bar element...
03106                             // Now, we want to know if there are any other nodes which are NOT in the current selection
03107                             // which form part of this named set and are on this node's layer. If not, select all of
03108                             // this named set as that is what the user (probably) meant to do...
03109                         
03110                             Layer *pLayer = (Layer *)pNode->FindParent(CC_RUNTIME_CLASS(Layer));    // What layer is the current node on?
03111                             bool AllLayerSelected = true;   // Assume all the layer is selected
03112 
03113                             // - NOW THE (POTENTIALLY) SLOW BIT -
03114                             // We need to go through every node in the current node's layer, checking if it is NOT SELECTED
03115                             // AND if it is a member of the current name gallery item. As soon as both cases are true, then
03116                             // we know we should not auto-select all of the named set and can exit quickly...
03117                             Node * pTempNode = FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(TemplateAttribute));
03118                             while (pTempNode && AllLayerSelected)
03119                             {
03120                                 // 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...
03121                                 if (((TemplateAttribute *)pTempNode)->GetQuestion() && ((TemplateAttribute *)pTempNode)->GetParam() == StrName)
03122                                 {
03123                                     // Check if we are dealing with a controller node...
03124                                     if (IS_A(pTempNode->FindParent(), NodeShadowController) || IS_A(pTempNode->FindParent(), NodeContourController) || IS_A(pTempNode->FindParent(),
03125                                         NodeBevelController))
03126                                     {
03127                                         // If so, check its children...
03128                                         TRACEUSER( "Matt", _T("\nController Node found - looking at its children"));
03129                                         Node * pChild = pTempNode->FindParent()->FindFirstChild();
03130                                         while (pChild && AllLayerSelected)
03131                                         {
03132                                             if (!pChild->IsAnAttribute() && !pChild->IsNodeHidden() && !IS_A(pChild, NodeShadow) && !IS_A(pChild, NodeContour) && !IS_A(pChild,
03133                                                 NodeBevel) && !IS_A(pChild, NodeBevelBegin) && !IS_A(pChild, NodeContourController) && !IS_A(pChild, NodeBevelController))
03134                                             {
03135                                                 // Then consider this child... Is it unselected - if so then we have found a matching node which is unselected when it should have been!
03136                                                 if (!pChild->IsSelected())
03137                                                 {
03138                                                     AllLayerSelected = false;
03139                                                 }
03140                                             }
03141                                             pChild = pChild->FindNext();
03142                                         }
03143                                     }
03144                                     else
03145                                     {
03146                                         if (!pTempNode->FindParent()->IsSelected())
03147                                         {
03148                                             // Then we should check if this node's parent is selected or not...
03149                                             AllLayerSelected = false;
03150                                         }
03151                                     }
03152                                 }
03153 
03154                                 pTempNode = FindNextOfClass(pTempNode, pLayer, CC_RUNTIME_CLASS(TemplateAttribute));
03155                             }
03156 
03157 
03158                             if (AllLayerSelected)
03159                             {
03160                                 // Save the original selection (if it hasn't already been saved)
03161                                 if (unchanged)
03162                                 {
03163                                     SliceHelper::SaveSelection();
03164                                     unchanged = false;
03165                                 }
03166 
03167                                 // Select ALL LAYERS of this named set...
03168                                 TRACEUSER( "Matt", _T("ALL of %s on layer selected\n"),(TCHAR *)StrName);
03169 
03170                                 SelectScan scanner(pNameGalleryItem, SelectScan::SELECT);
03171                                 scanner.Scan();
03172                                 pNameGallery->FastUpdateNamedSetSizes();
03173                             }
03174 
03175                             // END of slow bit...
03176                         }
03177                     }
03178                 }
03179             }
03180         }
03181 
03182         // Get the next 'UsedName' from the NameGallery
03183         pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
03184     }
03185 
03186     // Karim 04/12/2000 - unset PromoteToParent, so we don't corrupt the app's sel-range.
03187     rangecontrol.PromoteToParent = FALSE;
03188     pSelRng->Range::SetRangeControl(rangecontrol);
03189     pSelRng->Update();
03190 
03191     // Tidy up after myself... (Now there's a first - I won't make a habit of it)
03192     pSelList->DeleteAll();
03193     delete pSelList;
03194     pSelList = 0;
03195 
03196     if (!unchanged)
03197     {
03198         // Then our procedure has altered the selection, so we'd better update it!
03199         GetApplication()->FindSelection()->Update();        
03200     }
03201     return TRUE;
03202 }
03203 
03204 
03205 
03206 
03207 /********************************************************************************************
03208 
03209 >   static void SliceHelper::SaveSelection()
03210 
03211     Author:     Priestley (Xara Group Ltd) <camelotdev@xara.com>
03212     Created:    9/11/2000
03213     Purpose:    Save the current selection to m_pSelNodeList
03214     
03215 ********************************************************************************************/
03216 
03217 void SliceHelper::SaveSelection()
03218 {
03219     if (m_pSelNodeList)
03220     {
03221         m_pSelNodeList->DeleteAll();
03222         delete m_pSelNodeList;
03223         m_pSelNodeList = 0;
03224     }
03225 
03226     // Find the current selection and store it as a list of NodeListItems for later use...
03227     SelRange* pSelRng = GetApplication()->FindSelection();
03228     m_pSelNodeList = pSelRng->MakeListOfNodes(FALSE);
03229 }
03230 
03231 
03232 
03233 
03234 /********************************************************************************************
03235 
03236 >   static void OpMenuExport::RestoreSelection()
03237 
03238     Author:     Priestley (Xara Group Ltd) <camelotdev@xara.com>
03239     Created:    9/11/2000
03240     Purpose:    Restores the current selection to that given in m_pSelNodeList
03241     
03242 ********************************************************************************************/
03243 void SliceHelper::RestoreSelection()
03244 {
03245     if (m_pSelNodeList)
03246     {
03247         SelRange* pSelRng = GetApplication()->FindSelection();
03248         NodeRenderableInk::DeselectAll(FALSE, FALSE);
03249 
03250         for (NodeListItem* pItem = (NodeListItem*) m_pSelNodeList->GetHead(); pItem != 0; pItem = (NodeListItem*) m_pSelNodeList->GetNext(pItem))
03251         {
03252             if (pItem->pNode != 0)
03253             {
03254                 pItem->pNode->SetSelected(TRUE);
03255             }
03256         }
03257 
03258         m_pSelNodeList->DeleteAll();
03259         delete m_pSelNodeList;
03260         m_pSelNodeList = 0;
03261 
03262         RangeControl rangecontrol = pSelRng->GetRangeControlFlags();
03263         rangecontrol.PromoteToParent = FALSE;
03264         pSelRng->Range::SetRangeControl(rangecontrol);
03265         pSelRng->Update();
03266     }
03267 }
03268 
03269 
03270 // Matt 18/12/2000
03271 // Quick routine to ensure that the NodeSetSentinel is correct - if it is not then this routine will
03272 // attempt to correct it as much as possible...
03273 void SliceHelper::ValidateNodeSetSentinel()
03274 {
03275     // Get the NodeSetSentinel for this document
03276     NodeSetSentinel * pNodeSetSentinel = Document::GetSelected()->GetSetSentinel();
03277 
03278     // Ensure that each NodeSetProperty which is NOT hidden has a corresponding UNHIDDEN TemplateAttribute...
03279     NodeSetProperty * pNodeSetProperty = (NodeSetProperty *) pNodeSetSentinel->FindFirstChild(CC_RUNTIME_CLASS(NodeSetProperty));
03280 
03281     while (pNodeSetProperty)
03282     {
03283         if (!pNodeSetProperty->IsNodeHidden())
03284         {
03285             BOOL ok = TRUE;
03286             TemplateAttribute * pTemplateAttribute = (TemplateAttribute *) pNodeSetSentinel->FindFirstChild(CC_RUNTIME_CLASS(TemplateAttribute));
03287             while (ok && pTemplateAttribute)
03288             {
03289                 String_256 templateattributename = pTemplateAttribute->GetParam();
03290                 String_256 nodesetpropertyname = pNodeSetProperty->GetName();
03291                 // If the templateattribute is both unhidden and is for the current nodesetproperty then flag it!
03292                 if (!pTemplateAttribute->IsNodeHidden() && (templateattributename == nodesetpropertyname))
03293                 {
03294                     ok = FALSE;
03295                 }
03296 
03297                 pTemplateAttribute = (TemplateAttribute *) pTemplateAttribute->FindNext(CC_RUNTIME_CLASS(TemplateAttribute));
03298             }
03299 
03300             // 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 !!!
03301             if (ok)
03302             {
03303                 TRACEUSER( "Matt", _T("\nNodeSetProperty  "));
03304                 TRACEUSER("Matt", pNodeSetProperty->GetName());
03305                 TRACEUSER( "Matt", _T(" doesn't have a matching TemplateAttribute!!"));
03306 
03307                 String_256 ButtonName = pNodeSetProperty->GetName();
03308                 String_256 BarName = "";
03309 
03310                 // 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...
03311                 // If there is no MouseOff layer then we don't care...
03312                 String_256 mouseoffstring(_R(IDS_ROLLOVER_DEFAULT));
03313 
03314                 Spread * tempspread = Document::GetSelectedSpread();
03315 
03316                 TemplateAttribute * temptemplate = (TemplateAttribute *) SliceHelper::FindNextNameNode(tempspread, tempspread);
03317                 BOOL templatefound = FALSE;
03318                 while (temptemplate && !templatefound)
03319                 {
03320                     if (temptemplate->GetParam() == ButtonName)
03321                     {
03322                         BarName = temptemplate->GetQuestion();
03323                         templatefound = TRUE;
03324                     }
03325 
03326                     temptemplate = (TemplateAttribute *) SliceHelper::FindNextNameNode(temptemplate, tempspread);
03327                 }
03328 
03329                 // Make a new TemplateAttribute and add it into the tree...
03330                 TemplateAttribute* pAttr = new TemplateAttribute(String_256(TEXT("ObjectName")), BarName, ButtonName);
03331                 if (pAttr) 
03332                 {
03333                     pAttr->AttachNode(pNodeSetProperty, NEXT);
03334                 }
03335 
03336             }
03337         }
03338 
03339         pNodeSetProperty = (NodeSetProperty *) pNodeSetProperty->FindNext(CC_RUNTIME_CLASS(NodeSetProperty));
03340     }
03341 }
03342 
03343 
03344 /********************************************************************************************
03345 >   static void SliceHelper::BuildListOfNodesInButton(List * pList, Layer * pLayer, const StringBase & ButtonName)
03346 
03347     Author:     Priestley (Xara Group Ltd) <camelotdev@xara.com>
03348     Created:    20/12/2000
03349     Params:     pList   -   List that gets filled with pointers to all the nodes
03350                     pLayer  -   The layer we are interested in
03351                     ButtonName  -   The button we are looking for
03352     Returns:    -
03353     Purpose:    Scans all of pLayer, building a list of all nodes which are part of ButonName - if a NodeShadowController
03354                         is encountered, we add its children instead in order to get the correct bounding box info...
03355 ********************************************************************************************/
03356 void SliceHelper::BuildListOfNodesInButton(List * pList, Layer * pLayer, const StringBase & ButtonName)
03357 {
03358     ERROR3IF(!pLayer || !ButtonName || !pList, "Please don't call SliceHelper::BuildListOfNodesInButton with null params!");
03359 
03360     // Starting at pLayer, look for all TemplateAttributes with the correct name attached...
03361     Node * pNode = FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(TemplateAttribute));
03362     while (pNode)
03363     {
03364         // If this Node is part of a bar AND has the correct name, then add it to the list...
03365         if (((TemplateAttribute *)pNode)->GetQuestion() && (((TemplateAttribute *)pNode)->GetParam() == ButtonName))
03366         {
03367             // Add this node to the list unless it is a nodeshadowcontroller - if it is, add all of its children individually (except the nodeshadow child)
03368             if (IS_A(pNode->FindParent(), NodeShadowController))
03369             {
03370                 TRACEUSER( "Matt", _T("\nNodeShadowController found in BuildListOfNodesInButton - replacing list entries with its children"));
03371                 Node * pChild = pNode->FindParent()->FindFirstChild();
03372                 while (pChild)
03373                 {
03374                     if (!pChild->IsAnAttribute() && !pChild->IsNodeHidden() && !IS_A(pChild, NodeShadow))
03375                     {
03376                         // Then add this child to the list
03377                         NodeListItem * pItem = new NodeListItem(pChild);
03378                         pList->AddTail(pItem);
03379                     }
03380                     pChild = pChild->FindNext();
03381                 }
03382             }
03383             else
03384             {
03385                 // Then we were safe to add it to the list...
03386                 NodeListItem * pItem = new NodeListItem(pNode->FindParent());
03387                 pList->AddTail(pItem);
03388             }
03389         }
03390 
03391         pNode = FindNextOfClass(pNode, pLayer, CC_RUNTIME_CLASS(TemplateAttribute));
03392     }
03393 }
03394 
03395 
03396 /********************************************************************************************
03397 >   static void SliceHelper::EnsureTriggerInfo()
03398 
03399     Author:     Priestley (Xara Group Ltd) <camelotdev@xara.com>
03400     Created:    22/01/2001
03401     Purpose:    Scans the NameGallery ensuring that the triggers are in the right places...
03402 ********************************************************************************************/
03403 void SliceHelper::EnsureTriggerInfo()
03404 {
03405     NameGallery *pNameGallery = NameGallery::Instance();
03406     SGUsedNames* pNames = pNameGallery->GetUsedNames();
03407 
03408     if (!pNames) { return; }
03409 
03410     SGNameItem* pNameGalleryItem = (SGNameItem*) pNames->GetChild();
03411 
03412     // First we need to loop around and set all the triggers to false...
03413     while (pNameGalleryItem)
03414     {
03415         pNameGalleryItem->m_IsATrigger = FALSE;
03416         pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
03417     }
03418 
03419     // Now go back to the beginning and work out which named sets have triggers - mark these triggers as being such...
03420     pNameGalleryItem = (SGNameItem*) pNames->GetChild();
03421     while (pNameGalleryItem)
03422     {
03423         NodeSetProperty* pPropNode = NULL;
03424         NamedStretchProp* pProp = NULL;
03425 
03426         // Check if the named set has 'Stretch' property information...
03427         pPropNode = pNameGalleryItem->GetPropertyNode();
03428         if (pPropNode)
03429         {
03430             pProp = (NamedStretchProp*) pPropNode->GetProperty(NamedStretchProp::nIndex);
03431         }
03432 
03433         // 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...
03434         if (pProp && !pProp->GetTriggers().empty())
03435         {
03436             for (std::list<TriggerSet>::iterator pt = pProp->GetTriggers().begin(); pt != pProp->GetTriggers().end(); pt++)
03437             {
03438                 SGNameItem* pTempTriggerSet = SliceHelper::LookupNameGalleryItem(pt->m_strSet);
03439 
03440                 if (pTempTriggerSet)
03441                 {
03442                     pTempTriggerSet->m_IsATrigger = TRUE;
03443                 }
03444             }
03445         }
03446 
03447         // Get the next item and try again...
03448         pNameGalleryItem = (SGNameItem *) pNameGalleryItem->GetNext();
03449     }
03450 }

Generated on Sat Nov 10 03:47:02 2007 for Camelot by  doxygen 1.4.4