00001 // $Id: list.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 00099 /********************************************************************************************** 00100 LIST.CPP - List class definition file. 00101 00102 This is the Camelot list class. It is an implementation of a doubly linked-list and will be 00103 used rampantly throughout Camelot. 00104 00105 **********************************************************************************************/ 00106 00107 00108 #include "camtypes.h" 00109 00110 //#include "list.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00111 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00112 00113 CC_IMPLEMENT_DYNAMIC( List, CCObject ) 00114 00115 00116 #ifdef _DEBUG 00117 00118 /******************************************************************************************** 00119 00120 Preference: DebugLevel 00121 Section: Debug 00122 Range: 0 to 2 00123 Purpose: Controls how much checking is done in the List class. 00124 0 - No checking 00125 1 - Normal debug checking 00126 2 - Extra debug checking 00127 SeeAlso: - 00128 00129 ********************************************************************************************/ 00130 00131 INT32 List::ListDebugLevel = 0; 00132 #endif 00133 00134 /********************************************************************************************** 00135 00136 > List::List() 00137 00138 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00139 Created: 13/4/93 00140 Inputs: None 00141 Outputs: None 00142 Returns: None 00143 Purpose: List class constructor 00144 Errors: None 00145 00146 **********************************************************************************************/ 00147 00148 List::List() 00149 { 00150 HeadItem = NULL; 00151 TailItem = NULL; 00152 ItemCount = 0; 00153 00154 #ifdef _DEBUG 00155 LastItemRemoved = NULL; 00156 #endif 00157 } 00158 00159 /********************************************************************************************** 00160 00161 > virtual List::~List() 00162 00163 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00164 Created: 13/4/93 00165 Inputs: None 00166 Outputs: None 00167 Returns: None 00168 Purpose: List class destructor. NOTE that this does NOT destroy the list items - 00169 it simply delinks all items in the list and deletes the list header. 00170 00171 When destructing a list, you should really delete all objects from within 00172 it (by deriving a class from List with a special destructor, or calling 00173 List::DeleteAll() on it first, or some other appropriate action) before 00174 calling delete on the List header. 00175 00176 Notes: In DEBUG builds, any non-empty lists are reported to the TRACE output, 00177 in order to encourage people to discard their used lists thoughtfully. 00178 To remove this TRACE, you must either delete (DeleteAll()) or delink 00179 (RemoveItem()) the items from the list before calling the destructor. 00180 00181 In ALL builds, any items still in the list when it is destructed are delinked 00182 (RemoveItem()) from the list, in order to ensure their next/previous 00183 pointers remain valid. 00184 00185 **********************************************************************************************/ 00186 00187 List::~List() 00188 { 00189 #ifdef _DEBUG 00190 if (!IsEmpty()) 00191 { 00192 TRACE( _T("NON EMPTY LIST DELETED! Its items may appear as memory leaks on exit\n") 00193 _T("The %ld items are listed below. I have delinked them from the list\n"), 00194 (INT32) GetCount()); 00195 00196 // Dump the contents of the list 00197 ListItem *Ptr = GetHead(); 00198 while (Ptr != NULL) 00199 { 00200 TRACE( _T(" '%s' at 0x%x\n"), Ptr->GetRuntimeClass()->GetClassName(), Ptr ); 00201 Ptr = GetNext(Ptr); 00202 } 00203 TRACE( _T("\n") ); 00204 } 00205 #endif 00206 00207 // In all builds, if the list was deleted while non-empty, we delink all items from it 00208 // to ensure that their next/previous pointers are all NULL, rather than pointing at 00209 // possibly invalid areas of memory. This will at least mean that the items can be 00210 // safely added to other lists, without causing complete screwups. 00211 // These items will show up as memory leaks if they are not properly deleted by anyone 00212 // so we don't really need to concern ourselves with alerting the programmer too much 00213 // at this point 00214 while (!IsEmpty()) 00215 RemoveHead(); 00216 } 00217 00218 00219 00220 /********************************************************************************************** 00221 00222 > virtual ListItem *List::RemoveHead() 00223 00224 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00225 Created: 13/4/93 00226 Inputs: None 00227 Outputs: None 00228 Returns: ListItem that was head of list 00229 NULL if list is empty 00230 Purpose: 00231 00232 RemoveHead does not as its name may suggest delete the ListItem at the head 00233 of the list. It instead removes pointers to and from the ListItem at the head 00234 of the list, in effect removing it from the list. RemoveHead returns a pointer 00235 to the ListItem 'removed'. It is the responsibility of the caller to delete 00236 the ListItem from memory. 00237 00238 Errors: None. 00239 00240 **********************************************************************************************/ 00241 00242 ListItem *List::RemoveHead() 00243 { 00244 // CC_ASSERT_VALID(this); 00245 00246 if (this->IsEmpty()) // if list is empty return a null pointer 00247 return NULL; 00248 00249 ListItem *oldHead; 00250 00251 oldHead = HeadItem; 00252 HeadItem = HeadItem->Next; // list head pointed to next item in list 00253 00254 if (HeadItem != NULL) // if list is not empty 00255 HeadItem->Previous = NULL; // pointer to old list head must be nullified 00256 else 00257 TailItem = NULL; // tail item must be nullified 00258 00259 ItemCount -= 1; // decrement item counter 00260 00261 #ifdef _DEBUG 00262 LastItemRemoved = oldHead; 00263 #endif 00264 00265 return oldHead; 00266 } 00267 00268 00269 00270 /********************************************************************************************** 00271 00272 > virtual ListItem *List::RemoveTail() 00273 00274 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00275 Created: 13/4/93 00276 Inputs: None 00277 Outputs: None 00278 Returns: ListItem that was tail of list 00279 NULL if it is an empty list 00280 Purpose: 00281 00282 RemoveTail does not as its name may suggest delete the ListItem at the tail 00283 of the list. It instead removes pointers to and from the ListItem at the tail 00284 of the list, in effect removing it from the list. RemoveTail returns a pointer 00285 to the ListItem 'removed'. It is the responsibility of the caller to delete 00286 the ListItem from memory. 00287 00288 Errors: None. 00289 00290 **********************************************************************************************/ 00291 00292 ListItem *List::RemoveTail() 00293 { 00294 // CC_ASSERT_VALID(this); 00295 00296 if (this->IsEmpty()) // if list is empty return a null pointer 00297 return NULL; 00298 00299 ListItem *oldTail; 00300 00301 oldTail = TailItem; 00302 TailItem = TailItem->Previous; // list tail pointed to previous item in list 00303 00304 if (TailItem != NULL) // if list is not empty 00305 TailItem->Next = NULL; // pointer to old list tail must be nullified 00306 else 00307 HeadItem = NULL; // head item must be nullified 00308 00309 ItemCount -= 1; // decrement item counter 00310 00311 #ifdef _DEBUG 00312 LastItemRemoved = oldTail; 00313 #endif 00314 00315 return oldTail; 00316 } 00317 00318 00319 00320 /******************************************************************************************** 00321 00322 > virtual void List::DeleteAll() 00323 00324 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 00325 Created: 11/2/94 00326 Inputs: - 00327 Outputs: - 00328 Returns: - 00329 Purpose: Deletes the list, calling the destructors of all ListItems 00330 Errors: - 00331 SeeAlso: - 00332 00333 ********************************************************************************************/ 00334 00335 void List::DeleteAll() 00336 { 00337 while (!IsEmpty()) 00338 { 00339 delete (RemoveTail()); 00340 } 00341 } 00342 00343 00344 00345 /********************************************************************************************** 00346 00347 > virtual void List::AddHead( ListItem* ) 00348 00349 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00350 Created: 14/4/93 00351 Inputs: ListItem to be inserted 00352 Outputs: None 00353 Returns: void 00354 Purpose: To insert a ListItem at the head of the list. 00355 Errors: None. 00356 00357 **********************************************************************************************/ 00358 /* 00359 Technical notes: 00360 00361 If a new item is added to the list that already exists the list will become corrupted. 00362 In order to trap this error the FindPosition() function can be used to scan the list, 00363 but this will cause a performance overhead. The solution is to perform the scan only in 00364 debug mode so that users of the list are warned during development while release versions 00365 of the software do not suffer from inadequate performance. 00366 00367 **********************************************************************************************/ 00368 00369 void List::AddHead( ListItem* newHead ) 00370 { 00371 // CC_ASSERT_VALID(this); 00372 00373 if (this->IsEmpty()) // if list is empty 00374 { 00375 newHead->Next = NULL; // init next prev pointers 00376 newHead->Previous = NULL; 00377 HeadItem = newHead; // head & tail point to first item 00378 TailItem = newHead; 00379 } 00380 else 00381 { 00382 #ifdef _DEBUG 00383 if (ListDebugLevel > 0) 00384 { 00385 ENSURE( this->FindPosition(newHead) == NOT_IN_LIST, 00386 "AddHead: New head item is already in the list"); 00387 } 00388 #endif 00389 newHead->Next = HeadItem; // new head pointed to old head 00390 newHead->Previous = NULL; // new head previous pointer nullified 00391 HeadItem->Previous = newHead; // old head pointed back to new head 00392 HeadItem = newHead; // head pointer pointed to new head 00393 } 00394 00395 ItemCount += 1; // increment item counter 00396 00397 #ifdef _DEBUG 00398 if (newHead == LastItemRemoved) 00399 LastItemRemoved = NULL; 00400 #endif 00401 } 00402 00403 00404 00405 /********************************************************************************************** 00406 00407 > virtual void List::AddTail( ListItem* ) 00408 00409 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00410 Created: 14/4/93 00411 Inputs: ListItem to be inserted 00412 Outputs: None 00413 Returns: void 00414 Purpose: To insert a ListItem at the tail of the list. 00415 Errors: None. 00416 00417 **********************************************************************************************/ 00418 /* 00419 Technical notes: 00420 00421 If a new item is added to the list that already exists the list will become corrupted. 00422 In order to trap this error the FindPosition() function can be used to scan the list, 00423 but this will cause a performance overhead. The solution is to perform the scan only in 00424 debug mode so that users of the list are warned during development while release versions 00425 of the software do not suffer from inadequate performance. 00426 00427 **********************************************************************************************/ 00428 00429 void List::AddTail( ListItem* newTail ) 00430 { 00431 // CC_ASSERT_VALID(this); 00432 00433 if (this->IsEmpty()) // if list is empty 00434 { 00435 newTail->Next = NULL; // init next prev pointers 00436 newTail->Previous = NULL; 00437 HeadItem = newTail; // head & tail point to first item 00438 TailItem = newTail; 00439 } 00440 else 00441 { 00442 #ifdef _DEBUG 00443 if (ListDebugLevel > 0) 00444 { 00445 ENSURE(this->FindPosition(newTail) == NOT_IN_LIST, 00446 "AddTail: New tail item is already in the list"); 00447 } 00448 #endif 00449 00450 newTail->Previous = TailItem; // previous points old tail 00451 newTail->Next = NULL; // next is nullified 00452 TailItem->Next = newTail; // old tail next points to new tail 00453 TailItem = newTail; // update tail pointer 00454 } 00455 00456 ItemCount += 1; // increment item counter 00457 00458 #ifdef _DEBUG 00459 if (newTail == LastItemRemoved) 00460 LastItemRemoved = NULL; 00461 #endif 00462 } 00463 00464 00465 00466 /********************************************************************************************** 00467 00468 > ListItem *List::GetHead() const 00469 00470 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00471 Created: 14/4/93 00472 Inputs: None 00473 Outputs: None 00474 Returns: ListItem if not empty 00475 NULL if empty list 00476 Purpose: To allow access to the ListItem at the head of the list. 00477 Errors: None. 00478 00479 **********************************************************************************************/ 00480 00481 ListItem *List::GetHead() const 00482 { 00483 // CC_ASSERT_VALID(this); 00484 00485 if (this->IsEmpty()) 00486 return NULL; 00487 else 00488 return HeadItem; 00489 } 00490 00491 00492 00493 /********************************************************************************************** 00494 00495 > ListItem *List::GetTail() const 00496 00497 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00498 Created: 14/4/93 00499 Inputs: None 00500 Outputs: None 00501 Returns: ListItem if not empty 00502 NULL if list empty 00503 Purpose: To allow access to the ListItem at the tail of the list. 00504 Errors: None. 00505 00506 **********************************************************************************************/ 00507 00508 ListItem *List::GetTail( ) const 00509 { 00510 // CC_ASSERT_VALID(this); 00511 00512 if (this->IsEmpty()) 00513 return NULL; 00514 else 00515 return TailItem; 00516 } 00517 00518 00519 00520 /********************************************************************************************** 00521 00522 > LISTPOS List::GetHeadPosition() const 00523 00524 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00525 Created: 14/4/93 00526 Inputs: None 00527 Outputs: None 00528 Returns: ListPosition of head 00529 EMPTY_LIST error flag 00530 Purpose: Gives the user the relative position of the head of the list 00531 Errors: None. 00532 00533 **********************************************************************************************/ 00534 /* 00535 Technical notes: 00536 00537 The first position in the list is always zero. 00538 00539 **********************************************************************************************/ 00540 00541 LISTPOS List::GetHeadPosition() const 00542 { 00543 // CC_ASSERT_VALID(this); 00544 00545 if (this->IsEmpty()) 00546 return EMPTY_LIST; 00547 else 00548 return 0; 00549 } 00550 00551 00552 00553 /********************************************************************************************** 00554 00555 > LISTPOS List::GetTailPosition() const 00556 00557 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00558 Created: 14/4/93 00559 Inputs: None 00560 Outputs: None 00561 Returns: ListPosition of tail item 00562 EMPTY_LIST error flag 00563 Purpose: Gives the user the relative position of the tail of the list 00564 Errors: None. 00565 00566 00567 **********************************************************************************************/ 00568 /* 00569 Technical notes: 00570 00571 The first position in the list is zero therefore the last is: 00572 00573 ( Number of Items - 1 ) 00574 00575 **********************************************************************************************/ 00576 00577 LISTPOS List::GetTailPosition() const 00578 { 00579 // CC_ASSERT_VALID(this); 00580 00581 if (this->IsEmpty()) 00582 return EMPTY_LIST; 00583 else 00584 return (ItemCount - 1); 00585 } 00586 00587 00588 00589 /********************************************************************************************** 00590 00591 > LISTPOS List::FindPosition(ListItem * here) const 00592 00593 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00594 Created: 14/4/93 00595 Inputs: ListItem 00596 Outputs: None 00597 Returns: ListPosition if found 00598 NOT_IN_LIST if not found or input is NULL 00599 EMPTY_LIST if list is empty 00600 Purpose: Gives the user the relative position of the ListItem passed in. 00601 Errors: None. 00602 00603 00604 **********************************************************************************************/ 00605 00606 LISTPOS List::FindPosition(ListItem * here) const 00607 { 00608 // CC_ASSERT_VALID(this); 00609 00610 00611 if (this->IsEmpty()) // if list is empty return a null pointer 00612 return EMPTY_LIST; 00613 00614 if (here == NULL) // if input list item is NULL 00615 return NOT_IN_LIST; 00616 00617 ListItem *listIter; // list iterator 00618 LISTPOS listCount = 0; // list iterator count 00619 00620 listIter = HeadItem; 00621 00622 while (listIter != here && listIter != NULL) // iterate until input ListItem found 00623 { 00624 listIter = listIter->Next; 00625 listCount += 1; 00626 } 00627 00628 if (listIter == NULL) 00629 return NOT_IN_LIST; 00630 else 00631 return listCount; 00632 } 00633 00634 00635 00636 /********************************************************************************************** 00637 00638 > ListItem *List::FindItem(LISTPOS here) const 00639 00640 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00641 Created: 21/4/93 00642 Inputs: ListItem 00643 Outputs: None 00644 Returns: ListItem if found 00645 NULL if not found or input is less than zero or list is empty 00646 Purpose: Gives the user the ListItem at list position passed in. 00647 Errors: None. 00648 00649 00650 **********************************************************************************************/ 00651 00652 ListItem *List::FindItem(LISTPOS here) const 00653 { 00654 // CC_ASSERT_VALID(this); 00655 00656 00657 if ((this->IsEmpty()) || (here < 0)) // if list is empty return a null pointer 00658 return NULL; 00659 00660 ListItem *listIter; // list iterator 00661 LISTPOS listCount = 0; // list iterator count 00662 00663 listIter = HeadItem; 00664 00665 while (listCount != here && listIter != NULL) // iterate until input List position found 00666 { 00667 listIter = listIter->Next; 00668 listCount += 1; 00669 } 00670 00671 return listIter; 00672 } 00673 00674 00675 00676 /********************************************************************************************** 00677 00678 > virtual ListItem *List::RemoveItem( ListItem* ) 00679 00680 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00681 Created: 15/4/93 00682 Inputs: ListItem 00683 Outputs: None 00684 Returns: ListItem removed 00685 NULL if not found or if list empty or input is NULL 00686 Purpose: 00687 00688 Removes the ListItem passed in as a parameter. It is the responsibility of the 00689 caller to delete the removed ListItem from memory. 00690 00691 Errors: In debug builds, an ENSURE is triggered if the item to be removed is not 00692 actually in the list. 00693 00694 00695 **********************************************************************************************/ 00696 /* 00697 Technical notes: 00698 00699 Assert is used to trap the error caused by trying to remove an item not in the list. It 00700 does this by scanning the entire list with the function FindPosition() and if item is not 00701 found assert will stop the program. This will only occur in the debug version of the list 00702 class, the release copy will not include this overhead. 00703 00704 **********************************************************************************************/ 00705 00706 ListItem *List::RemoveItem( ListItem* oldItem ) 00707 { 00708 // CC_ASSERT_VALID(this); 00709 00710 #ifdef _DEBUG 00711 if (ListDebugLevel > 0) 00712 { 00713 ENSURE( this->FindPosition(oldItem) != NOT_IN_LIST, 00714 "RemoveItem: List item to be removed is not in the list"); 00715 } 00716 #endif 00717 00718 if ((this->IsEmpty()) || // if list is empty or 00719 (oldItem == NULL)) // NULL input or 00720 return NULL; 00721 00722 ListItem *adjacentItem; 00723 00724 if ((oldItem->Previous) != NULL) 00725 { 00726 adjacentItem = oldItem->Previous; // point to previous item in list 00727 adjacentItem->Next = oldItem->Next; // by pass old item forwards 00728 if ((oldItem->Next) != NULL) // Remove from middle of ist 00729 { // there are items both sides 00730 adjacentItem = oldItem->Next; // point to next item in list 00731 adjacentItem->Previous = oldItem->Previous; // by pass old item backwards 00732 } 00733 else 00734 { // Removal of tail of list 00735 TailItem = adjacentItem; // new tail item 00736 } 00737 } 00738 else 00739 { // Removal of head of list 00740 if ((oldItem->Next) != NULL) 00741 { 00742 adjacentItem = oldItem->Next; // next item in list 00743 adjacentItem->Previous = NULL; // nullify prev pointer to old item 00744 HeadItem = adjacentItem; // new head item 00745 } 00746 else 00747 { 00748 HeadItem = NULL; // nullify head and tail pointers 00749 TailItem = NULL; 00750 } 00751 } 00752 00753 ItemCount -= 1; // decrement item counter 00754 00755 #ifdef _DEBUG 00756 LastItemRemoved = oldItem; // Check for stupidity - see GetNext/Prev 00757 #endif 00758 00759 oldItem->Previous = NULL; // Er, we should vape the disused links 00760 oldItem->Next = NULL; // shouldn't we? 00761 00762 return oldItem; 00763 } 00764 00765 00766 00767 /********************************************************************************************** 00768 00769 > virtual LISTPOS List::InsertBefore(LISTPOS here, ListItem* newItem) 00770 00771 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00772 Created: 15/4/93 00773 Inputs: ListItem to be inserted 00774 List position 00775 Outputs: None 00776 Returns: List position new item is inserted at. 00777 INVALID_LISTPOS when the list position goes beyond the list size 00778 INVALID_NEWITEM when the new item is NULL 00779 Purpose: Inserts a ListItem in the list position before the one that is passed in. 00780 Notes: This function involves scanning the entire list until 'here' is found, 00781 so is much slower on average than the other flavour of InsertBefore 00782 Errors: This ALWAYS checks that the item you wish to insert 00783 before is actually in the list, and that the item you are inserting 00784 is not already in the list - an ENSURE failure results if not. 00785 00786 **********************************************************************************************/ 00787 /* 00788 Technical notes: 00789 00790 If a new item is added to the list that already exists the list will become corrupted. 00791 In order to trap this error the FindPosition() function can be used to scan the list, 00792 but this will cause a performance overhead. The solution is to perform the scan only in 00793 debug mode so that users of the list are warned during development while release versions 00794 of the software do not suffer from inadequate performance. 00795 00796 **********************************************************************************************/ 00797 00798 LISTPOS List::InsertBefore(LISTPOS here, ListItem* newItem) 00799 { 00800 // CC_ASSERT_VALID(this); 00801 00802 #ifdef _DEBUG 00803 if (ListDebugLevel > 0) 00804 { 00805 if (!this->IsEmpty()) 00806 ENSURE( this->FindPosition(newItem) == NOT_IN_LIST, 00807 "InsertBefore: Item to be inserted is already in the list"); 00808 } 00809 #endif 00810 00811 if ((this->IsEmpty()) && (here > 0)) 00812 return INVALID_LISTPOS; 00813 00814 if (newItem == NULL) // if input parameter is NULL 00815 return INVALID_NEWITEM; 00816 00817 if (here == this->GetHeadPosition()) // if head of list 00818 { 00819 this->AddHead(newItem); // insert before head 00820 return this->GetHeadPosition(); 00821 } 00822 00823 ListItem *listIter; // list iterator 00824 LISTPOS listCount = this->GetHeadPosition(); // list iterator count 00825 00826 listIter = HeadItem; 00827 00828 while (listCount != here && listIter != NULL) // iterate until list position found 00829 { 00830 listIter = listIter->Next; 00831 listCount += 1; 00832 } 00833 00834 if (listIter == NULL) // list position does not exist in list 00835 return INVALID_LISTPOS; 00836 00837 newItem->Next = listIter; // point new item to before one 00838 newItem->Previous = listIter->Previous; // point new item to previous one 00839 listIter->Previous = newItem; // point before item to new one 00840 newItem->Previous->Next = newItem; // point previous item back new one 00841 ItemCount += 1; // increment item counter 00842 00843 #ifdef _DEBUG 00844 if (newItem == LastItemRemoved) 00845 LastItemRemoved = NULL; 00846 #endif 00847 00848 return listCount; 00849 } 00850 00851 00852 00853 /********************************************************************************************** 00854 00855 > virtual ListItem *List::InsertBefore(ListItem* here, ListItem* newItem) 00856 00857 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00858 Created: 15/4/93 00859 Inputs: ListItem to be inserted 00860 ListItem before insertion 00861 Outputs: None 00862 Returns: ListItem inserted 00863 INVALID_NEWITEM when the new item is NULL 00864 NULL if list empty or ListItem not found or parameters are NULL 00865 Purpose: Inserts the 'newItem' ListItem before 'here' ListItem in the list. 00866 Errors: In the debug build, this checks that the item you wish to insert 00867 before is actually in the list, and that the item you are inserting 00868 is not already in the list - an ENSURE failure results if not. 00869 00870 **********************************************************************************************/ 00871 /* 00872 Technical notes: 00873 00874 If a new item is added to the list that already exists the list will become corrupted. 00875 In order to trap this error the FindPosition() function can be used to scan the list, 00876 but this will cause a performance overhead. The solution is to perform the scan only in 00877 debug mode so that users of the list are warned during development while release versions 00878 of the software do not suffer from inadequate performance. 00879 00880 **********************************************************************************************/ 00881 00882 ListItem *List::InsertBefore(ListItem* here, ListItem* newItem) 00883 { 00884 // CC_ASSERT_VALID(this); 00885 00886 #ifdef _DEBUG 00887 if (ListDebugLevel > 0) 00888 { 00889 if (!this->IsEmpty()) 00890 ENSURE( this->FindPosition(newItem) == NOT_IN_LIST, 00891 "InsertBefore: Item to be inserted is already in the list"); 00892 } 00893 #endif 00894 00895 if (this->IsEmpty() || (here == NULL) || (newItem == NULL)) 00896 return NULL; 00897 00898 if (here == HeadItem) // if head of list 00899 { 00900 this->AddHead(newItem); // insert before head 00901 return newItem; 00902 } 00903 00904 #ifdef _DEBUG 00905 if (ListDebugLevel > 0) 00906 { 00907 ListItem *listIter; // list iterator 00908 00909 listIter = HeadItem; 00910 00911 while (listIter != here && listIter != NULL) // iterate until list position found 00912 listIter = listIter->Next; 00913 00914 ENSURE (listIter != NULL, "List::InsertBefore(Here) - 'Here' does not exist!"); 00915 } 00916 #endif 00917 00918 newItem->Next = here; // point new item to before one 00919 newItem->Previous = here->Previous; // point new item to previous one 00920 here->Previous = newItem; // point before item to new one 00921 newItem->Previous->Next = newItem; // point previous item back new one 00922 00923 #ifdef _DEBUG 00924 if (newItem == LastItemRemoved) 00925 LastItemRemoved = NULL; 00926 #endif 00927 00928 ItemCount += 1; // increment item counter 00929 return newItem; 00930 } 00931 00932 00933 00934 /********************************************************************************************** 00935 00936 > virtual LISTPOS List::InsertAfter(LISTPOS here, ListItem* newItem) 00937 00938 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 00939 Created: 14/4/93 00940 Inputs: ListItem to be inserted 00941 List position 00942 Outputs: None 00943 Returns: List position new item is inserted at. 00944 INVALID_LISTPOS when the list position goes beyond the list size 00945 INVALID_NEWITEM when new list item is NULL 00946 Purpose: Inserts a ListItem in the list position after the one that is passed in. 00947 Notes: To find 'here', the list must be scanned, so this is much slower on 00948 average than the other flavour of InsertAfter 00949 Errors: This ALWAYS checks that the item you wish to insert 00950 after is actually in the list, and that the item you are inserting 00951 is not already in the list - an ENSURE failure results if not. 00952 00953 **********************************************************************************************/ 00954 /* 00955 Technical notes: 00956 00957 If a new item is added to the list that already exists the list will become corrupted. 00958 In order to trap this error the FindPosition() function can be used to scan the list, 00959 but this will cause a performance overhead. The solution is to perform the scan only in 00960 debug mode so that users of the list are warned during development while release versions 00961 of the software do not suffer from inadequate performance. 00962 00963 **********************************************************************************************/ 00964 00965 LISTPOS List::InsertAfter(LISTPOS here, ListItem* newItem) 00966 { 00967 // CC_ASSERT_VALID(this); 00968 00969 #ifdef _DEBUG 00970 if (ListDebugLevel > 0) 00971 { 00972 if (!this->IsEmpty()) 00973 ENSURE( this->FindPosition(newItem) == NOT_IN_LIST, 00974 "InsertAfter: Item to be inserted is already in the list"); 00975 } 00976 #endif 00977 00978 if ((this->IsEmpty()) && (here > 0)) 00979 return INVALID_LISTPOS; 00980 00981 if (newItem == NULL) 00982 return INVALID_NEWITEM; 00983 00984 if (here == this->GetTailPosition()) // if tail of list 00985 { 00986 this->AddTail(newItem); // insert after tail 00987 return this->GetTailPosition(); 00988 } 00989 00990 ListItem *listIter; // list iterator 00991 LISTPOS listCount = GetHeadPosition(); // list iterator count 00992 00993 listIter = HeadItem; 00994 00995 while (listCount != here && listIter != NULL) // iterate until list position found 00996 { 00997 listIter = listIter->Next; 00998 listCount += 1; 00999 } 01000 01001 if (listIter == NULL) // list position does not exist in list 01002 return INVALID_LISTPOS; 01003 01004 01005 newItem->Previous = listIter; // point new item to previous one in list 01006 newItem->Next = listIter->Next; // point new item to next one 01007 listIter->Next = newItem; // point after item to new one 01008 newItem->Next->Previous = newItem; // point next item back to new one 01009 ItemCount += 1; // increment item counter 01010 01011 #ifdef _DEBUG 01012 if (newItem == LastItemRemoved) 01013 LastItemRemoved = NULL; 01014 #endif 01015 01016 return (listCount + 1); 01017 } 01018 01019 01020 01021 /********************************************************************************************** 01022 01023 > virtual ListItem *List::InsertAfter(ListItem* here, ListItem* newItem) 01024 01025 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 01026 Created: 15/4/93 01027 Inputs: ListItem to be inserted 01028 ListItem after insertion 01029 Outputs: None 01030 Returns: ListItem inserted 01031 NULL if list empty or ListItem not found or parameters are NULL 01032 Purpose: Inserts the 'newItem' ListItem after 'here' ListItem in the list. 01033 Errors: In the debug build, this checks that the item you wish to insert 01034 after is actually in the list, and that the item you are inserting 01035 is not already in the list - an ENSURE failure results if not. 01036 01037 **********************************************************************************************/ 01038 /* 01039 Technical notes: 01040 01041 If a new item is added to the list that already exists the list will become corrupted. 01042 In order to trap this error the FindPosition() function can be used to scan the list, 01043 but this will cause a performance overhead. The solution is to perform the scan only in 01044 debug mode so that users of the list are warned during development while release versions 01045 of the software do not suffer from inadequate performance. 01046 01047 **********************************************************************************************/ 01048 01049 ListItem *List::InsertAfter(ListItem* here, ListItem* newItem) 01050 { 01051 // CC_ASSERT_VALID(this); 01052 01053 #ifdef _DEBUG 01054 if (ListDebugLevel > 0) 01055 { 01056 if (!this->IsEmpty()) 01057 ENSURE( this->FindPosition(newItem) == NOT_IN_LIST, 01058 "InsertAfter: Item to be inserted is already in the list"); 01059 } 01060 #endif 01061 01062 if ((this->IsEmpty()) || (here == NULL) || (newItem == NULL)) 01063 return NULL; 01064 01065 01066 if (here == TailItem) // if tail of list 01067 { 01068 this->AddTail(newItem); // insert after tail 01069 return newItem; 01070 } 01071 01072 #ifdef _DEBUG 01073 if (ListDebugLevel > 0) 01074 { 01075 ListItem *listIter = HeadItem; 01076 01077 while (listIter != here && listIter != NULL) // iterate until list position found 01078 listIter = listIter->Next; 01079 01080 if (listIter == NULL) // list position does not exist in list 01081 { 01082 return NULL; 01083 } 01084 } 01085 #endif 01086 01087 newItem->Previous = here; // point new item to previous one in list 01088 newItem->Next = here->Next; // point new item to next one 01089 here->Next = newItem; // point after item to new one 01090 newItem->Next->Previous = newItem; // point next item back to new one 01091 01092 #ifdef _DEBUG 01093 if (newItem == LastItemRemoved) 01094 LastItemRemoved = NULL; 01095 #endif 01096 01097 ItemCount += 1; // increment item counter 01098 return newItem; 01099 } 01100 01101 01102 01103 /********************************************************************************************** 01104 01105 > UINT32 List::GetCount() const 01106 01107 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 01108 Created: 14/4/93 01109 Inputs: None 01110 Outputs: None 01111 Returns: Number of items in the list 01112 Purpose: To give user of list an indication of its size in terms of number of items. 01113 Errors: None 01114 01115 **********************************************************************************************/ 01116 01117 UINT32 List::GetCount() const 01118 { 01119 // CC_ASSERT_VALID(this); 01120 01121 return ItemCount; 01122 } 01123 01124 01125 01126 /********************************************************************************************** 01127 01128 > BOOL List::IsEmpty() const 01129 01130 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 01131 Created: 14/4/93 01132 Inputs: None 01133 Outputs: None 01134 Returns: TRUE if list is empty 01135 FALSE if list is not empty 01136 Purpose: Allows user to see if list is empty or not 01137 Errors: None 01138 01139 **********************************************************************************************/ 01140 01141 BOOL List::IsEmpty() const 01142 { 01143 // CC_ASSERT_VALID(this); 01144 01145 if (ItemCount == 0) 01146 return TRUE; 01147 else 01148 return FALSE; 01149 } 01150 01151 /******************************************************************************************** 01152 01153 > BOOL List::CreateIndex(List* ListIndex) 01154 01155 Author: Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com> 01156 Created: 28/9/95 01157 Outputs: A List of in-order pointers into 'this' list. Each item on the list is a 01158 ListItemIdx. 01159 Returns: FALSE if we run out of memory. All items on the output list are deleted 01160 if this occurs. 01161 Purpose: Creates a list of in-order pointers into this list. 01162 01163 This has many uses eg. 01164 01165 You wish to scan a list repeatedly, not wasting time considering items 01166 you are not interested in. This method avoids changing the order of the 01167 items within the list. 01168 01169 Could be used for list sorting etc. 01170 01171 SeeAlso: ListItemIdx 01172 01173 ********************************************************************************************/ 01174 01175 BOOL List::CreateIndex(List* ListIndex) 01176 { 01177 01178 ListItem* pi = GetHead(); // pointer to this lists item 01179 ListItemIdx* ppi; // pointer to pi 01180 while (pi) 01181 { 01182 ppi = new ListItemIdx(); 01183 if (!ppi) 01184 { 01185 ListIndex->DeleteAll(); // Tidyup 01186 return FALSE; 01187 } 01188 ppi->pItem = pi; 01189 ListIndex->AddTail(ppi); 01190 pi = GetNext(pi); 01191 } 01192 return TRUE; 01193 } 01194 01195 01196 01197 /********************************************************************************************** 01198 01199 > void List::AssertValid() const 01200 01201 Author: Mario_Shamtani (Xara Group Ltd) <camelotdev@xara.com> 01202 Created: 14/4/93 01203 Inputs: None 01204 Outputs: None 01205 Returns: void 01206 Purpose: 01207 01208 Validates the internal state of the object. If an inconsistency is detected 01209 an error message is indicated. This function will not be included in the release 01210 version. 01211 01212 Errors: ENSURE failures will occur if the list is found to be inconsistent 01213 01214 **********************************************************************************************/ 01215 /* 01216 Technical notes: 01217 01218 Use of AfxIsValidAddress will cause portability problems if it is not implemented as 01219 part of the memory management functions of the OIL. It is currently being used on a 01220 temporary basis. 01221 01222 **********************************************************************************************/ 01223 01224 #ifdef _DEBUG 01225 void List::AssertValid() const 01226 { 01227 CCObject::AssertValid(); 01228 01229 if (ItemCount == 0) 01230 { 01231 // empty list 01232 ENSURE(HeadItem == NULL, "Head item of a non-empty list should never be null"); 01233 ENSURE(TailItem == NULL, "Tail item of a non-empty list should never be null"); 01234 } 01235 } 01236 #endif 01237 01238 // End of List class