// $Id: collist.cpp 1282 2006-06-09 09:46:49Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // collist.cpp - Implementation fo the ColourList class /* */ // If you get any unexplainable problems with this file - Try changing the order of the includes // It's due to a curse put on Jason's code. #include "camtypes.h" //#include "basedoc.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "colcontx.h" #include "collist.h" //#include "colormgr.h" #include "colourix.h" //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "jason.h" CC_IMPLEMENT_MEMDUMP(ColourList, List) // Declare smart memory handling in Debug builds #define new CAM_DEBUG_NEW /********************************************************************************************** > ColourList::ColourList() Author: Jason_Williams (Xara Group Ltd) Created: 25/5/94 Inputs: - Outputs: - Returns: - Purpose: ColourList constructor **********************************************************************************************/ ColourList::ColourList() { ParentDoc = NULL; // Document containing this list ArrayParent = NULL; // Current parent of the cached array InheritanceArray = NULL; // Cached array of all offspring of ArrayParent LastUnnamedColour = 2; // Memory to optimise unnamed-colour name generation } /********************************************************************************************** > ColourList::~ColourList() Author: Jason_Williams (Xara Group Ltd) Created: 17/11/94 Inputs: - Outputs: - Returns: - Purpose: ColourList destructor **********************************************************************************************/ ColourList::~ColourList() { InvalidateInheritanceCache(); // Release our memory claims UnnamedColours.DeleteAll(); // Delete any remaining unnamed colours // This will probably cause IxCol in use errors // as by now all colours should have been deleted // automatically when their usage reached zero. } /********************************************************************************************** > void ColourList::Init(BaseDocument *ParentDocument) Author: Jason_Williams (Xara Group Ltd) Created: 7/11/94 Inputs: ParentDocument - the parent document of this colour list Purpose: ColourList initialiser. MUST be called after constructing a ColourList in order to correctly initialise it. Note that if ParentDocument is not set to a useful value, some operations on colour lists (eg. the colour editor) will fail. **********************************************************************************************/ void ColourList::Init(BaseDocument *ParentDocument) { ERROR3IF(ParentDocument == NULL, "NULL ParentDoc in ColourList::Init()"); ParentDoc = ParentDocument; } /******************************************************************************************** > void ColourList::InvalidateInheritanceCache(void) Author: Jason_Williams (Xara Group Ltd) Created: 17/11/94 Purpose: Deallocates any memory used for the array of IndexedColours compiled by ColourList::CompileInheritanceArray. The next request for such an array will be regenerated from the ColourList, rather than from cache. This is automatically called by the ColourManager immediately prior to a ColourChangingMsg COLOURUPDATED message broadcast. SeeAlso: ColourList::CompileInheritanceArray ********************************************************************************************/ void ColourList::InvalidateInheritanceCache(void) { if (InheritanceArray != NULL) CCFree(InheritanceArray); InheritanceArray = NULL; ArrayParent = NULL; } /********************************************************************************************** > void ColourList::AddHead(ListItem *ItemToAdd) Author: Jason_Williams (Xara Group Ltd) Created: 16/12/94 Purpose: Overrides the List::AddHead method to ensure that any added item has a name which is unique within this colour list ERROR3's if the item is not a NAMED IndexedColour SeeAlso: ColourList::AddItem **********************************************************************************************/ void ColourList::AddHead(ListItem *ItemToAdd) { ERROR3IF(!IS_A(ItemToAdd, IndexedColour), "You can only insert IndexedColours into ColourLists!"); IndexedColour *NewColour = (IndexedColour *) ItemToAdd; // Ensure the colour has a name which is unique within this colour list ERROR3IF(!NewColour->IsNamed(), "You can't Insert an unnamed colour before a named one!"); // It's a named colour, so make sure its name is unique String_64 NewName; if (GenerateUniqueColourName(NewColour->GetName(), &NewName)) NewColour->SetName(NewName); // And call the base class to add the item List::AddHead(ItemToAdd); } /********************************************************************************************** > void ColourList::AddTail(ListItem *ItemToAdd) Author: Jason_Williams (Xara Group Ltd) Created: 16/12/94 Purpose: Overrides the List::AddTail method to ensure that any added item has a name which is unique within this colour list ERROR3's if the item is not a NAMED IndexedColour SeeAlso: ColourList::AddItem **********************************************************************************************/ void ColourList::AddTail(ListItem *ItemToAdd) { ERROR3IF(!IS_A(ItemToAdd, IndexedColour), "You can only insert IndexedColours into ColourLists!"); IndexedColour *NewColour = (IndexedColour *) ItemToAdd; // Ensure the colour has a name which is unique within this colour list ERROR3IF(!NewColour->IsNamed(), "You can't Insert an unnamed colour before a named one!"); // It's a named colour, so make sure its name is unique String_64 NewName; if (GenerateUniqueColourName(NewColour->GetName(), &NewName)) NewColour->SetName(NewName); // And call the base class to add the item List::AddTail(ItemToAdd); } /********************************************************************************************** > BOOL ColourList::AddItem(IndexedColour *NewColour) Author: Jason_Williams (Xara Group Ltd) Created: 25/5/94 Returns: TRUE Purpose: Adds an IndexedColour item to the tail of the ColourList The name of the colour is checked, and may be changed to ensure that it is a unique identifier within the scope of this colour list. This can also be used to add unnamed colours - in this case, they are just added to the list of unnamed colours held by this object. SeeAlso: ColourList::GenerateUniqueColourName **********************************************************************************************/ BOOL ColourList::AddItem(IndexedColour *NewColour) { // Ensure the colour has a name which is unique within this colour list if (!NewColour->IsNamed()) { UnnamedColours.AddTail(NewColour); return(TRUE); } // It's a named colour, then... String_64 NewName; if (GenerateUniqueColourName(NewColour->GetName(), &NewName)) NewColour->SetName(NewName); // And add it to the tail of the list - using the base class function, not our overrides! List::AddTail(NewColour); return(TRUE); } /********************************************************************************************** > LISTPOS ColourList::InsertBefore(LISTPOS here, ListItem* item) > ListItem *ColourList::InsertBefore(ListItem* here, ListItem* item) > LISTPOS ColourList::InsertAfter(LISTPOS here, ListItem* item) > ListItem *ColourList::InsertAfter(ListItem* here, ListItem* item) Author: Jason_Williams (Xara Group Ltd) Created: 2/8/95 Inputs: here - the list item to insert before. NULL is not allowed (List is unhelpful) item - the item to insert before/after the 'here' item Returns: The position/item which was inserted Purpose: Inserts a NAMED IndexedColour at a particular list position (See List base class for a further description of the actual insertion process) These methods are a veneer onto the normal list calls, which ensure that all inserted colours are given unique names within this list. Note: IMPORTANT: NOTE that at present, these are NOT VIRTUAL, so you MUST call them using a ColourList pointer rather than a generic List pointer! SeeAlso: List; List::InsertBefore; ColourList::GenerateUniqueColourName **********************************************************************************************/ LISTPOS ColourList::InsertBefore(LISTPOS here, ListItem* item) { ERROR3IF(!IS_A(item, IndexedColour), "You can only insert IndexedColours into ColourLists!"); IndexedColour *NewColour = (IndexedColour *) item; // Ensure the colour has a name which is unique within this colour list ERROR3IF(!NewColour->IsNamed(), "You can't Insert an unnamed colour before a named one!"); // It's a named colour, so make sure its name is unique String_64 NewName; if (GenerateUniqueColourName(NewColour->GetName(), &NewName)) NewColour->SetName(NewName); // And add it to the tail of the list - using the base class function, not our overrides! return(List::InsertBefore(here, item)); } ListItem *ColourList::InsertBefore(ListItem* here, ListItem* item) { ERROR3IF(!IS_A(item, IndexedColour), "You can only insert IndexedColours into ColourLists!"); IndexedColour *NewColour = (IndexedColour *) item; // Ensure the colour has a name which is unique within this colour list ERROR3IF(!NewColour->IsNamed(), "You can't Insert an unnamed colour before a named one!"); // It's a named colour, so make sure its name is unique String_64 NewName; if (GenerateUniqueColourName(NewColour->GetName(), &NewName)) NewColour->SetName(NewName); // And add it to the tail of the list - using the base class function, not our overrides! return(List::InsertBefore(here, item)); } LISTPOS ColourList::InsertAfter(LISTPOS here, ListItem* item) { ERROR3IF(!IS_A(item, IndexedColour), "You can only insert IndexedColours into ColourLists!"); IndexedColour *NewColour = (IndexedColour *) item; // Ensure the colour has a name which is unique within this colour list ERROR3IF(!NewColour->IsNamed(), "You can't Insert an unnamed colour before a named one!"); // It's a named colour, so make sure its name is unique String_64 NewName; if (GenerateUniqueColourName(NewColour->GetName(), &NewName)) NewColour->SetName(NewName); // And add it to the tail of the list - using the base class function, not our overrides! return(List::InsertAfter(here, item)); } ListItem *ColourList::InsertAfter(ListItem* here, ListItem* item) { ERROR3IF(!IS_A(item, IndexedColour), "You can only insert IndexedColours into ColourLists!"); IndexedColour *NewColour = (IndexedColour *) item; // Ensure the colour has a name which is unique within this colour list ERROR3IF(!NewColour->IsNamed(), "You can't Insert an unnamed colour before a named one!"); // It's a named colour, so make sure its name is unique String_64 NewName; if (GenerateUniqueColourName(NewColour->GetName(), &NewName)) NewColour->SetName(NewName); // And add it to the tail of the list - using the base class function, not our overrides! return(List::InsertAfter(here, item)); } /******************************************************************************************** > virtual void ColourList::DeleteAll() Author: Jason_Williams (Xara Group Ltd) Created: 1/6/96 Purpose: Deletes the list, calling the destructors of all ListItems Notes: Colours are deleted carefully to be tidy with child links (As we're deleting them all this shouldn't matter, but when children of a deleted colour are deleted, they poke its ChildUsage, and Smart Heap chokes itself in dismay. The old smartheap didn't care) We thus vape all colour linking before deleting the colours. We don't need to be particularly gentle about this because we are deleting them all anyway. SeeAlso: List::DeleteAll ********************************************************************************************/ void ColourList::DeleteAll() { // Vape all colour linkages in the named colour list IndexedColour *Ptr = (IndexedColour *) GetHead(); while (Ptr != NULL) { Ptr->SetLinkedParent(NULL, COLOURTYPE_NORMAL); Ptr = (IndexedColour *) GetNext(Ptr); } // And vape all colour linkages in the UNnamed colour list Ptr = (IndexedColour *) UnnamedColours.GetHead(); while (Ptr != NULL) { Ptr->SetLinkedParent(NULL, COLOURTYPE_NORMAL); Ptr = (IndexedColour *) UnnamedColours.GetNext(Ptr); } // Delete all the unnamed colours now too, just to be tidy UnnamedColours.DeleteAll(); // Finally, call the base class to do the actual deletions List::DeleteAll(); } /********************************************************************************************** > UINT32 ColourList::GetUndeletedCount(void) Author: Jason_Williams (Xara Group Ltd) Created: 3/6/94 Inputs: - Outputs: - Returns: The number of IndexedColours in this ColourList which are not 'deleted' so as to be hidden from view by the undo system. Purpose: Count the number of non-'deleted' items in a ColourList **********************************************************************************************/ UINT32 ColourList::GetUndeletedCount(void) { ListItem *Ptr = GetHead(); UINT32 Count = 0; while (Ptr != NULL) { if ( !((IndexedColour *)Ptr)->IsDeleted() ) Count++; Ptr = GetNext(Ptr); } return(Count); } /******************************************************************************************** > IndexedColour *ColourList::FindUndeletedItem(INT32 Index) Author: Jason_Williams (Xara Group Ltd) Created: 3/6/94 Inputs: ColourIndex - essentially a LISTPOS of the item in the Palette list Returns: NULL if the index is past the end of the list else a pointer to the referenced IndexedColour ListItem Purpose: The same as the conventional List FindItem() function, except that it treats 'deleted' colours as if they are not in the list. ********************************************************************************************/ IndexedColour *ColourList::FindUndeletedItem(INT32 Index) { IndexedColour *Ptr = (IndexedColour *) GetHead(); while (Ptr != NULL) { if (!Ptr->IsDeleted() && --Index < 0) return(Ptr); Ptr = (IndexedColour *) GetNext(Ptr); } return(NULL); } /******************************************************************************************** > IndexedColour *ColourList::GetUndeletedHead(void) Author: Jason_Williams (Xara Group Ltd) Created: 6/3/95 Returns: NULL if the list contains no 'live' (non-deleted) colours else a pointer to the first IndexedColour ListItem in the list Purpose: The same as the conventional List GetHead() function, except that it treats 'deleted' colours as if they are not in the list. ********************************************************************************************/ IndexedColour *ColourList::GetUndeletedHead(void) { IndexedColour *Ptr = (IndexedColour *) GetHead(); while (Ptr != NULL && Ptr->IsDeleted()) Ptr = (IndexedColour *) GetNext(Ptr); return(Ptr); } /******************************************************************************************** > IndexedColour *ColourList::GetUndeletedNext(IndexedColour *ThisItem) Author: Jason_Williams (Xara Group Ltd) Created: 6/3/95 Returns: NULL if the list contains no more 'live' (non-deleted) colours else a pointer to the next 'live' IndexedColour in the list Purpose: The same as the conventional List GetNext() function, except that it treats 'deleted' colours as if they are not in the list. ********************************************************************************************/ IndexedColour *ColourList::GetUndeletedNext(IndexedColour *ThisItem) { ERROR2IF(ThisItem == NULL, NULL, "ColourList::GetUndeletedNext - NULL parameter is illegal"); IndexedColour *Ptr = (IndexedColour *) GetNext(ThisItem); while (Ptr != NULL && Ptr->IsDeleted()) Ptr = (IndexedColour *) GetNext(Ptr); return(Ptr); } /******************************************************************************************** > IndexedColour **ColourList::CompileInheritanceArray(IndexedColour *Parent) Author: Jason_Williams (Xara Group Ltd) Created: 17/11/94 Inputs: Parent - The colour for which you want an inheritance array Returns: If NO error occurred, this returns a pointer to a NULL terminated array of IndexedColours, with Array[0] == Parent. If an error did occur, the error is reported, and NULL is returned Purpose: Compiles a NULL-terminated array of pointers to ALL colours which are linked to this, both directly and indirectly (i.e. an array listing the entire inheritance 'subtree' from 'Parent', *including* 'Parent', and including named, unnamed, and 'deleted' colours). This array is cached so that the cost of generating it several times in a row (as may be necessary for different clients of the ColourChangingMsg) is minimised. Notes: The array is NULL terminated The array INCLUDES Parent as its first member (Array[0]) The array is owned (allocated and deallocated) by the ColourList. It MUST be treated as a read-only shared array. The array includes ALL affected indexed colours. Use IsDeleted() and IsNamed() to sort out named/unnamed colours, and deleted (hidden for undo) colours. This is expected to be called mainly as a result of COLOURUPDATED ColourChangingMsg broadcasts. The cache is flushed automatically by the colour manager just prior to these broadcasts. If you want to use this function outside these message broadcasts, then you must invalidate the cache at an appropriate time. SeeAlso: ColourList::InvalidateInheritanceCache; IndexedColour::IsADescendantOf; ColourManager::ColourHasChanged ********************************************************************************************/ IndexedColour **ColourList::CompileInheritanceArray(IndexedColour *Parent) { if (Parent != ArrayParent) { InvalidateInheritanceCache(); // Throw away any existing array // Generate the array INT32 ColourArraySize = 32; InheritanceArray = (IndexedColour**) CCMalloc(ColourArraySize * sizeof(IndexedColour *)); if (InheritanceArray == NULL) { InformError(); // Out of memory - inform the user return(NULL); } INT32 ListEnd = 1; // Initialise array to just the Parent InheritanceArray[0] = Parent; InheritanceArray[1] = NULL; // Do all the Named colours IndexedColour *Ptr = (IndexedColour *) GetHead(); while (Ptr != NULL) { if (Ptr != Parent && Ptr->IsADescendantOf(Parent)) { // Is a descendant: add this item to the end of the list if (ListEnd >= ColourArraySize - 1) { // We've filled the array - must increase the size ColourArraySize += 32; IndexedColour **TempArray = (IndexedColour **) CCRealloc(InheritanceArray, ColourArraySize * sizeof(IndexedColour *)); if (TempArray == NULL) { InvalidateInheritanceCache(); // Free our existing memory claim InformError(); return(NULL); } InheritanceArray = TempArray; // It worked- set our ptr to the new block } InheritanceArray[ListEnd++] = Ptr; // Add the item and a new NULL terminator InheritanceArray[ListEnd] = 0; // to the end of the list } Ptr = (IndexedColour *) GetNext(Ptr); } // And likewise with all unnamed colours IndexedColour *Next; Ptr = (IndexedColour *) UnnamedColours.GetHead(); while (Ptr != NULL) { // Allow for deleting this colour from underneath ourselves Next = (IndexedColour *) UnnamedColours.GetNext(Ptr); if (Ptr->IsInUse()) { if (Ptr->IsADescendantOf(Parent)) { // Is a descendant: add this item to the end of the list if (ListEnd >= ColourArraySize - 1) { // We've filled the array - must increase the size ColourArraySize += 32; IndexedColour **TempArray = (IndexedColour **) CCRealloc(InheritanceArray, ColourArraySize * sizeof(IndexedColour *)); if (TempArray == NULL) { InvalidateInheritanceCache(); // Free our existing memory claim InformError(); return(NULL); } InheritanceArray = TempArray; // It worked- set our ptr to the new block } InheritanceArray[ListEnd++] = Ptr; // Add the item and a new NULL terminator InheritanceArray[ListEnd] = 0; // to the end of the list } } else { // We've found an unnamed IndexedColour which is not in use, so we can vape it TRACEUSER( "Jason", _T("CIA: Deleting unnamed colour which is not in use [%p]\n"), Ptr ); // UnnamedColours.RemoveItem(Ptr); // delete Ptr; } Ptr = Next; } ArrayParent = Parent; } TRACEUSER( "Jason", _T("\n")); return(InheritanceArray); } /******************************************************************************************** > void ColourList::RemoveLinksToColour(IndexedColour *DeadColour) Author: Jason_Williams (Xara Group Ltd) Created: 18/11/94 Inputs: DeadColour - The colour for which all ParentColour references are to be removed. Purpose: Scans the entire ColourList for any colours which reference the given IndexedColour. All these colours are converted into standalone representation of their former definition, so that all child-links of this colour are removed. It is then safe to delete the colour. This is done automatically for colours deleted by the HideColours method of the colour manager; however, deletion only occurs for real when the undo record containing the deleted colours is deleted. NOTE that this does NOT remove the colour from the ColourList It also does NOT delete the colour. SeeAlso: ColourManager::HideColours ********************************************************************************************/ void ColourList::RemoveLinksToColour(IndexedColour *DeadColour) { // Search through the named colours IndexedColour *Ptr = (IndexedColour *) GetHead(); while (Ptr != NULL) // For all colours in the list... { if (Ptr->FindLastLinkedParent() == DeadColour) // If they reference DeadColour... { IndexedColourType TheType = COLOURTYPE_NORMAL; if (Ptr->GetType() == COLOURTYPE_SPOT) TheType = COLOURTYPE_SPOT; Ptr->SetLinkedParent(NULL, TheType); // Convert to unlinked (normal/spot) colour } Ptr = (IndexedColour *) GetNext(Ptr); } // And drag through the unnamed colours as well Ptr = (IndexedColour *) UnnamedColours.GetHead(); IndexedColour *Next; while (Ptr != NULL) // For all colours in the list... { Next = (IndexedColour *) UnnamedColours.GetNext(Ptr); if (Ptr->FindLastLinkedParent() == DeadColour) // If they reference DeadColour... { IndexedColourType TheType = COLOURTYPE_NORMAL; if (Ptr->GetType() == COLOURTYPE_SPOT) TheType = COLOURTYPE_SPOT; Ptr->SetLinkedParent(NULL, TheType); // Convert to unlinked (normal/spot) colour } // This could be dangerous - what if the deleted colour is in the inheritance array? // if (!Ptr->IsInUse()) // { // // We've found an unnamed colour which is not in use - we can vape it //TRACEUSER( "Jason", _T("RLTC: Deleting unnamed colour which is not in use [%x]\n"), (INT32)Ptr); // UnnamedColours.RemoveItem(Ptr); // delete Ptr; // } Ptr = Next; } } /******************************************************************************************** > BOOL ColourList::IsColourInUseInDoc(IndexedColour *TheColour, BOOL IgnoreColourGallery = FALSE) Author: Jason_Williams (Xara Group Ltd) Created: 27/6/95 Inputs: TheColour - Points to the colour to check IgnoreColourGallery - TRUE to ignore usage in the colour gallery. DO NOT USE this parameter. It is a special-case for the colour gallery to use to ignore the usage that that gallery has of the colour. Returns: TRUE if the colour is in use in the document Purpose: Determine if a colour is used in the parent document. This is similar to IndexedColour::IsInUse(), but it also traverses the linking hierarchy to see if a colour is in use indirectly (e.g. A parent of a colour that is in use is needed for the document even if it is not used directly in\the document. SeeAlso: IndexedColour::IsInUse ********************************************************************************************/ BOOL ColourList::IsColourInUseInDoc(IndexedColour *TheColour, BOOL IgnoreColourGallery) { ERROR3IF(TheColour == NULL, "Illegal NULL param"); // First, is it directly in-use? if (TheColour->IsInUse(IgnoreColourGallery)) return(TRUE); // Is it a parent of other colours? If not, then it is not in use if (!TheColour->IsNamed() || !TheColour->HasLinkedChildren()) return(FALSE); // It has descendants, so scan the inheritance tree to determine if any of them are in use // Scan through the named colours (NOTE: We also scan 'deleted' colours, as a parent colour // may be indirectly in use in the undo record) IndexedColour *Ptr = (IndexedColour *) GetHead(); while (Ptr != NULL) // For all colours in the list... { if (Ptr != TheColour && Ptr->IsADescendantOf(TheColour)) // If this one is a descendant... { if (Ptr->IsInUse(IgnoreColourGallery)) // and it's in use, then we are return(TRUE); // in use, and can return now. } Ptr = (IndexedColour *) GetNext(Ptr); } // And scan through the unnamed colours as well Ptr = (IndexedColour *) UnnamedColours.GetHead(); while (Ptr != NULL) // For all colours in the list... { if (Ptr != TheColour && Ptr->IsADescendantOf(TheColour)) // If this one is a descendant... { if (Ptr->IsInUse()) // and it's in use, then we are return(TRUE); // in use, and can return now. } Ptr = (IndexedColour *) UnnamedColours.GetNext(Ptr); } // OK, we're obviously not in use then return(FALSE); } /******************************************************************************************** > BOOL ColourList::NamedColourExists(const char *pName) Author: Tim_Browse (Xara Group Ltd) (Jason copied this code here from colcomp.cpp) Created: 08/08/94 (19/12/94) Inputs: pName - the name to look for. Returns: TRUE if the name is already in use; FALSE otherwise. Purpose: Determine if a colour name is already in use by any colours stored in this colour list SeeAlso: ColourList::GenerateUniqueColourName ********************************************************************************************/ BOOL ColourList::NamedColourExists( PCTSTR pName ) { IndexedColour *pItem = (IndexedColour *) GetHead(); while (pItem != NULL) { if (!pItem->IsDeleted() && pItem->IsNamed() && (pItem->GetName()->IsIdentical(pName)) ) { // The name is the same - it's a match return(TRUE); } // Try the next colour pItem = (IndexedColour *) GetNext(pItem); } // No match for this name return(FALSE); } /******************************************************************************************** > BOOL ColourList::NamedColourExists(const StringBase *pName) Author: Jason_Williams (Xara Group Ltd) Created: 19/12/94 Inputs: pName - the name to look for. Returns: TRUE if the name is already in use; FALSE otherwise. Purpose: Determine if a colour name is already in use by any colours stored in this colour list SeeAlso: ColourList::GenerateUniqueColourName ********************************************************************************************/ BOOL ColourList::NamedColourExists(const StringBase *pName) { return(NamedColourExists((const TCHAR *)(*pName))); } /******************************************************************************************** > BOOL ColourList::GenerateUniqueColourName(const StringBase *pPrefix, String_64 *pResult) Author: Jason_Williams (Xara Group Ltd) (From code by Tim) Created: 16/12/94 (8/8/94) Inputs: pPrefix - Points to the suggested name for this colour. This may be NULL, in which case a name based on the string "Unnamed" will be generated Outputs: pResult - is filled in with a unique name NOTE that this is a String_64, the maximum length for a colour name. If the input pPrefix string is longer than will fit, it will be truncated. If truncation was necessary, this will have no effect on the return value. (IndexedColour::SetName will truncate strings longer than 64 chars as well) Returns: TRUE if the pResult contains a different string from that supplied in pPrefix. (pResult will always contain a valid result, but checking this flag may save you the bother of copying the string over) Purpose: Scans the entire ColourList to determine if the suggested name is unique within this document. If it is not, the name is altered (currently by truncating to 50 characters and appending a decimal number) to provide a unique name, which is returned in the supplied string. This method can be called whenever you wish to generate a unique name for a colour. However, note that it is called automatically for every colour which is added to the colour list with the AddItem() method Notes: Name matching is done using StringBase::IsIdentical, so see that method to determine the rules by which strings are matched If the colour is unnamed (pPrefix == NULL, *pPrefix is a NULL string, or the name is "Unnamed" (_R(IDS_UNNAMEDCOLOUR)), then the code is optimised to provide a new name for it extra fast. SeeAlso: ColourList::AddItem; IndexedColour::SetName; StringBase::IsIdentical ********************************************************************************************/ BOOL ColourList::GenerateUniqueColourName(const StringBase *pPrefix, String_64 *pResult) { if (pResult == NULL) { ERROR3("ColourList::GenerateUniqueColourName: pResult is NULL!"); return(FALSE); } if (pPrefix != NULL && !NamedColourExists(pPrefix)) { // Either the List is empty, or this name is unique, so return it (ensuring < 64 chars) pPrefix->Left(pResult, 63); return(FALSE); } // Copy the prefix into this string. String_64 NewName; BOOL Unnamed = FALSE; // Remember if it is 'unnamed' if (pPrefix != NULL) pPrefix->Left(&NewName, 50); // Copy into NewName, truncating String_64 NoName(_R(IDS_UNNAMEDCOLOUR)); if ((pPrefix == NULL) || (NewName.Length() == 0)) // Has no name, so call it "Unnamed" { Unnamed = TRUE; NewName = NoName; } else if (NewName.IsIdentical(NoName)) // Is called 'Unnamed', so remember that { Unnamed = TRUE; } INT32 PrefixLen = NewName.Length(); if (PrefixLen > 50) { // Limit prefix length to 50 chars - this means we can add numbers up to // 10-odd digits long in order to make it unique - should be enough! PrefixLen = 50; } TCHAR *pPrefixEnd = (TCHAR *) NewName; pPrefixEnd += PrefixLen; // Is this name ok? if (!NamedColourExists(NewName)) { (*pResult) = NewName; // Yes - return it. return(TRUE); } // Start adding numbers to make it unique INT32 i = (Unnamed) ? LastUnnamedColour : 2; while (TRUE) { // If this doesn't terminate [Tim] will eat [his] Archimedes! camSnprintf( pPrefixEnd, 64, _T(" %ld"), i++ ); // Is this name ok? if (!NamedColourExists(NewName)) { (*pResult) = NewName; // Yes - return it if (Unnamed) // Update unnamed colour generation number LastUnnamedColour = i; return(TRUE); } } *pResult = NewName; // Ensure stable return, though this should never happen return(TRUE); }