home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 March / Chip_1998-03_cd.bin / zkuste / delphi / WhiteAnts / CONTSTRM.ZIP / Containr.pas next >
Encoding:
Pascal/Delphi Source File  |  1997-01-10  |  24.2 KB  |  770 lines

  1. {
  2. +----------------------------------------------------------------------------+
  3. |                                      ⌐  ⌐                                  |
  4. |                                    ⌐⌐ ⌐ ⌐ ⌐                                |
  5. |                                 ⌐⌐⌐ ⌐   ⌐  ⌐                               |
  6. |                                 ⌐⌐    ⌐ ⌐   ⌐                              |
  7. |                  ⌐             ⌐⌐     ⌐  ⌐                                 |
  8. |                 ⌐ ⌐            ⌐⌐⌐    ⌐⌐  ⌐                                |
  9. |             ⌐⌐  ⌐  ⌐      ⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐  ⌐                                    |
  10. |            ⌐  ⌐⌐  ⌐⌐      ⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐                                  |
  11. |            ⌐ ⌐⌐⌐ ⌐⌐⌐⌐⌐⌐ ⌐⌐⌐ ⌐   ⌐⌐⌐⌐⌐⌐⌐⌐                                   |
  12. |           ⌐ ⌐⌐ ⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐ ⌐   ⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐      Copyright ⌐ 1996-1997 by:  |
  13. |           ⌐ ⌐⌐⌐⌐⌐⌐ ⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐⌐  ⌐ ⌐⌐⌐⌐⌐ ⌐⌐                                 |
  14. |          ⌐ ⌐⌐⌐⌐⌐⌐⌐   ⌐⌐⌐⌐⌐ ⌐⌐⌐⌐    ⌐⌐ ⌐⌐ ⌐      WHITE ANTS SYSTEMHOUSE BV  |
  15. |         ⌐  ⌐⌐⌐⌐⌐⌐⌐ ⌐⌐⌐ ⌐⌐⌐ ⌐⌐ ⌐       ⌐⌐⌐⌐      Geleen 12                  |
  16. |         ⌐ ⌐⌐⌐⌐⌐⌐⌐    ⌐   ⌐⌐   ⌐⌐⌐       ⌐       8032 GB Zwolle             |
  17. |           ⌐⌐⌐⌐⌐⌐     ⌐            ⌐ ⌐           Netherlands                |
  18. |      ⌐⌐⌐  ⌐⌐⌐⌐⌐      ⌐     ⌐⌐     ⌐  ⌐                                     |
  19. |            ⌐⌐       ⌐              ⌐  ⌐⌐⌐ ⌐     Tel. +31 38 453 86 31      |
  20. |      ⌐              ⌐              ⌐            Fax. +31 38 453 41 22      |
  21. |      ⌐             ⌐               ⌐⌐                                      |
  22. |    ⌐              ⌐                  ⌐⌐         www.whiteants.com          |
  23. |  ⌐⌐              ⌐                     ⌐ ⌐      support@whiteants.com      |
  24. |                 ⌐                                                          |
  25. +----------------------------------------------------------------------------+
  26.   file     : CONTAINR
  27.   version  : 1.0
  28.   comment  : Replaces and extends BP 7.0 collections
  29.   author   : G. Beuze
  30.   compiler : Delphi 1.0
  31. +----------------------------------------------------------------------------+
  32. | DISCLAIMER:                                                                |
  33. | THIS SOURCE IS FREEWARE. YOU ARE ALLOWED TO USE IT IN YOUR OWN PROJECTS    |
  34. | WITHOUT ANY RESTRICTIONS. YOU ARE NOT ALLOWED TO SELL THE SOURCE CODE.     |
  35. | THERE IS NO WARRANTY AT ALL - YOU USE IT ON YOUR OWN RISC. WHITE ANTS DOES |
  36. | NOT ASSUME ANY RESPONSIBILITY FOR ANY DAMAGE OR ANY LOSS OF TIME OR MONEY  |
  37. | DUE THE USE OF ANY PART OF THIS SOURCE CODE.                               |
  38. +----------------------------------------------------------------------------+
  39. }
  40. unit Containr;
  41.  
  42. interface
  43.  
  44. uses Classes, SysUtils, Filters;
  45.  
  46. type
  47.   EContainerError = class(Exception);
  48.  
  49.   TContainerDuplicates = (dupIgnore, dupAccept, dupDestroy, dupError);
  50.  
  51.   TCheckNewKeyEvent = function(CurKey, NewKey: Pointer): Boolean of object;
  52.  
  53.   TContainer = class(TStreamable)
  54.   private
  55.     FCanSort: Boolean;
  56.     FDuplicates: TContainerDuplicates;
  57.     FOnChange: TNotifyEvent;
  58.     FOnStyleChange: TNotifyEvent;
  59.     FOwnesItems: Boolean;
  60.     FSorted: Boolean;
  61.     FUpdateCount: Integer;
  62.     function CheckDuplicates(Item: Pointer): Pointer;
  63.     procedure DeleteDuplicates;
  64.     procedure QuickSort(L, R: Integer);
  65.     procedure SetSorted(Value: Boolean);
  66.     procedure SetCanSort(Value: Boolean);
  67.     procedure SetDuplicates(Value: TContainerDuplicates);
  68.   protected
  69.     procedure Changed; virtual;
  70.       { Called whenever the list is changed. Calls FOnChanged if it is assigned
  71.         Change mechanism is locked when FUpdateCount > 0. Note: Changed does NOT
  72.         check for changes in the contents of the list. Changes in items should
  73.         be reflected by replacing them in the Container using the Put method.
  74.         This will also take care of correct sorting if a key field was changed }
  75.     function CheckNewKey(CurKey, NewKey: Pointer): Boolean; virtual;
  76.       { Could be called by items in a duplicate restricted list in order to
  77.         validate a change of their key. }
  78.     function Compare(Key1, Key2: Pointer): Integer; virtual;
  79.       { Returns 0 if Key1 = Key2. Descendants should override this method
  80.         in order to create some sorting order if required }
  81.     procedure FreeItem(Item: Pointer); virtual;
  82.       { Called whenever an owned item has to be disposed of. Assumes Item to be
  83.         a TObject descendnat and calls it's Free method }
  84.     procedure FreeDataStructure; virtual;
  85.       { Called form Destroy ONLY. Calls Clear if FOwnesItems is True else RemoveAll.
  86.         Descendants should free their data structure and also free their items
  87.         if FOwnesItems = True }
  88.     procedure FreeDuplicate(Item: Pointer); virtual;
  89.       { Called if an attempt was made to insert a duplicate item, in dupDestroy
  90.         mode. Implemented as calling FreeItem. Descendants could generate an
  91.         appropriate error / warning }
  92.     function Get(Index: Integer): Pointer; virtual; abstract;
  93.       { Should give acces to indexed property Items }
  94.     function GetCount: Integer; virtual; abstract;
  95.       { Should returns number of items in container }
  96.     function GetItem(S: TFilter): Pointer; virtual;
  97.       { Used to retrieve stored items from S. Assumes TStreamable descendants
  98.         to be stored on S and calls S.Get to retrive it }
  99.     function GetString(Index: Integer): String; virtual; abstract;
  100.       { Should give acces to string representation of Items[Index], this
  101.         could e.g. be used in displaying the container in a TListBox object }
  102.     function KeyOf(Item: Pointer): Pointer; virtual;
  103.       { Returns Key for Comparison. Duplicate handling and sorting is based on
  104.         the KeyOf / Compare methods. Implemented as returning Item }
  105.     procedure ListInsert(Index: Integer; Item: Pointer); virtual; abstract;
  106.       { Performs actual insertion in container called by Add and Insert after
  107.         duplicates and sorting order have been checked }
  108.     procedure ListPut(Index: Integer; Item: Pointer); virtual; abstract;
  109.       { Should put Item at Index. All checking has been done }
  110.     procedure ListRemove(Index: Integer); virtual; abstract;
  111.       { Should remove item from container without destroying it. Called by
  112.         Delete and Remove methods }
  113.     procedure Put(Index: Integer; Item: Pointer); virtual;
  114.       { Gives acces to indexed property Items. Calls Remove and Insert to
  115.         implement behaviour. Also safe for sorted containers }
  116.     procedure PutItem(Item: Pointer; S: TFilter); virtual;
  117.       { Used to store items to stream. assumes Item to be a TStreamable
  118.         descendant and Put's it on S. Descendants could override this method in
  119.         order to store items different, e.g. when items are strings }
  120.     procedure SetOwnesItems(Value: Boolean); virtual;
  121.       { Simply sets FOwnesItems to Value }
  122.     procedure SetUpdateState(Updating: Boolean); virtual;
  123.       { Calls Changed if Updating is False }
  124.     procedure StyleChanged;
  125.       { Calls FOnStyleChanged if it is assigned }
  126.     property CanSort: Boolean read FCanSort write SetCanSort;
  127.       { Stores wheter container is able to sort or not. To be set by Create }
  128.   public
  129.     constructor Load(S: TFilter); override;
  130.       { Loads properties from S and then reads items calling GetItem. Add is
  131.         used to add items to container. Note: Descendants should therefore make
  132.         sure that the data structure in which items are stored is initialised
  133.         before inherited load is called, See TCollection.Load. Inherited Load
  134.         should ALWAYS be called before any loading from s is done however. }
  135.     destructor Destroy; override;
  136.       { Sets FOnChange to nil, preventing change message's to
  137.         be send during destruction, then calls FreeDataStructire and inherited Destroy }
  138.     function Add(Item: Pointer): Integer; virtual;
  139.       { Checkes for duplicates and sorting order, then calls ListInsert to
  140.         insert item at correct position. Add returns the position at which the
  141.         item was added or -1 if it was not added at all }
  142.     procedure Assign(Container: TContainer); virtual;
  143.       { List is cleared. Adds all items in Container by repeatedly calling Add.
  144.         If Container = nil, list is cleared anyway }
  145.     procedure BeginUpdate;
  146.       { Increments UpdateCount and calls SetupdateState(True). Locking
  147.         mechanism. Always lock and unlock updates with pairs of BeginUpdate and
  148.         EndUpdate method calls }
  149.     procedure Clear; virtual;
  150.       { Clears all items in container. Disposes of items if OwnedItems is True.
  151.         Clear is identical to 'DeleteAll' }
  152.     procedure Delete(Index: Integer); virtual;
  153.       { Deletes item at Index from container, calls FreeItem if OwnesItem is
  154.         True }
  155.     procedure DeleteItem(Item: Pointer); virtual;
  156.       { Deletes item from container, uses Find to locate it, then calls Delete }
  157.     procedure Exchange(Index1, Index2: Integer); virtual;
  158.       { Exchages items at Index1 and Index2. Safe when container is Sorted. }
  159.     procedure EndUpdate;
  160.       { Decrements the UpdateCounter. Update locking mechanism.
  161.         If EndUpdate hits UpdateCount = 0, SetUpdateState(False) is called }
  162.     function First: Pointer;
  163.       { returns pointer to first item or nil if no such item exists }
  164.     function Find(Item: Pointer; var Index: Integer): Boolean; virtual;
  165.       { Returns FindKey(KeyOf(Item) }
  166.     function FindKey(Key: Pointer; var Index: Integer): Boolean; virtual;
  167.       { Returns True if Item is found. Index then contains the index at which
  168.         Key was found. If False is returned Index containes the value at which
  169.         Key would be inserted }
  170.     function IndexOf(Item: Pointer): Integer; virtual;
  171.       { Calls Find and returns index of Item, or -1 if it was not found }
  172.     function Insert(Index: Integer; Item: Pointer): Integer; virtual;
  173.       { Checkes for sorting order and duplicates, then inserts item calling
  174.         ListInsert. Note: if Sorted is True, calling Insert has the same result
  175.         as calling Add. Insert always returns the position at which the item was
  176.         inserted or -1 if it was not inserted at all }
  177.     function Last: Pointer;
  178.       { returns pointer to last item or nil if no such item exists }
  179.     procedure Move(CurIndex, NewIndex: Integer); virtual;
  180.       { Moves the item at CurIndex to NewIndex. Implemented with Remove and
  181.         Insert methods. Also safe for sorted containers in which case nothing
  182.         will happen (except when duplicates are accepted in which case the
  183.         order of the duplicate items might be changed }
  184.     procedure Remove(Index: Integer); virtual;
  185.       { Removes the item from the container without destroying it even if
  186.         OwnesItems is true. Implemented as calling ListRemove. }
  187.     procedure RemoveAll; virtual;
  188.       { Removes all items by iterating over container and calling Remove }
  189.     procedure RemoveItem(Item: Pointer);
  190.       { Removes Item by calling Find and Remove, does not destroy the item }
  191.     procedure Store(S: TFilter); override;
  192.       { Stores properties to S. Then stores each item to S calling PutItem }
  193.     procedure Sort; virtual;
  194.       { Calls quicksort if nessecary }
  195.     function ValidIndex(Index: Integer): Boolean;
  196.       { Returns True if Index in range of [0.. Count> }
  197.     property Count: Integer read GetCount;
  198.       { returns number of items }
  199.     property Duplicates: TContainerDuplicates read FDuplicates write SetDuplicates;
  200.       { Controls duplicates behavior }
  201.     property Items[Index: Integer]: Pointer read Get write Put; default;
  202.       { Items in containr , default property }
  203.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  204.       { Dispatched each time the list container changes }
  205.     property OnStyleChange: TNotifyEvent read FOnChange write FOnChange;
  206.       { Dispatched each time the list style (sorted etc) changes }
  207.     property OwnesItems: Boolean read FOwnesItems write SetOwnesItems;
  208.       { Controls ownership of items in list. If set True FreeItem is called
  209.         whenever an item is Deleted from the list }
  210.     property Sorted: Boolean read FSorted write SetSorted;
  211.       { Controls the sorting behavior. If Sorted is set from False to True
  212.         the container will (re-) sort it's contents }
  213.     property Strings[Index: Integer]: String read GetString;
  214.       { Returns a string for each item in the container. Depends on getStrings
  215.         which is abstract }
  216.   end;
  217.  
  218.  
  219.     { TCollection is a BP7.0 TCollection like class with extended features.
  220.       It is also streamable. Main behaviour defined by TContainer, TCollection
  221.       just implements a (Sorted) collection stored in a TList }
  222.   TCollection = class(TContainer)
  223.   private
  224.     FList: TList;
  225.   protected
  226.     procedure FreeDataStructure; override;
  227.       { Calls inherited, then disposes of FList }
  228.     function Get(Index: Integer): Pointer; override;
  229.       { Gets item from FList }
  230.     function GetCount: Integer; override;
  231.       { Returns FList.Count }
  232.     procedure ListInsert(Index: Integer; Item: Pointer); override;
  233.       { Insert Item at position Index in FList }
  234.     procedure ListPut(Index: Integer; Item: Pointer); override;
  235.       { Puts Item at Index in FList }
  236.     procedure ListRemove(Index: Integer); override;
  237.       { Removes item at Index from FList }
  238.   public
  239.     constructor Create;
  240.       { Calls inherited Create then instantiates FList }
  241.     constructor Load(S: TFilter); override;
  242.       { Creates FList then calls inherited load which will fill it }
  243.     procedure Assign(Container: TContainer); override;
  244.       { Overrides the inherited Assign to improve performance }
  245.     procedure RemoveAll; override;
  246.       { Overrides inherited method to implement more economic algoritm }
  247.   end;
  248.  
  249.  
  250. implementation
  251.  
  252. uses NumUtils;
  253.  
  254. {$B-}
  255.  
  256. const
  257.   SDuplicateItem = 0;
  258.   SContainerSortError = 1;
  259.  
  260. procedure ContainerError(Ident: Word);
  261. begin
  262.   raise EContainerError.Create('Container error: ' + IntToStr(Ident));
  263. end;
  264.  
  265. constructor TContainer.Load(S: TFilter);
  266. var I, C: Integer;
  267. begin
  268.   inherited Load(S);
  269.   BeginUpdate;
  270.   S.Read(FCanSort, SizeOf(FCanSort));
  271.   S.Read(FDuplicates, SizeOf(FDuplicates));
  272.   S.Read(FOwnesItems, SizeOf(FOwnesItems));
  273.   S.Read(FSorted, SizeOf(FSorted));
  274.   S.Read(C, SizeOf(C));
  275.   for I := 0 to C - 1 do
  276.     Add(GetItem(S));
  277.   EndUpdate;
  278. end;
  279.  
  280. destructor TContainer.Destroy;
  281. begin
  282.   FOnChange := nil;
  283.   FreeDataStructure;
  284.   inherited Destroy;
  285. end;
  286.  
  287. function TContainer.Add(Item: Pointer): Integer;
  288. begin
  289.   Item := CheckDuplicates(Item);
  290.   if Assigned(Item) then
  291.   begin
  292.     if Sorted then
  293.       Find(Item, Result)
  294.     else
  295.       Result := Count;
  296.     ListInsert(Result, Item);
  297.     Changed;
  298.   end
  299.   else
  300.     Result := -1;
  301. end;
  302.  
  303. procedure TContainer.Assign(Container: TContainer);
  304. var I: Integer;
  305. begin
  306.   BeginUpdate;
  307.   Clear;
  308.   if Assigned(Container) then
  309.     for I := 0 to Container.Count - 1 do
  310.       Add(Container[I]);
  311.   EndUpdate;
  312. end;
  313.  
  314. procedure TContainer.BeginUpdate;
  315. begin
  316.   if FUpdateCount = 0 then SetUpdateState(True);
  317.   Inc(FUpdateCount);
  318. end;
  319.  
  320. procedure TContainer.Changed;
  321. begin
  322.   if Assigned(FOnChange) and (FUpdateCount = 0) then FOnChange(Self);
  323. end;
  324.  
  325. function TContainer.CheckDuplicates(Item: Pointer): Pointer;
  326. var I: Integer;
  327. begin
  328.   Result := Item;
  329.   if (Duplicates <> dupAccept) and Find(Item, I) then
  330.   begin
  331.     case Duplicates of
  332.       dupIgnore: ;
  333.       dupDestroy: FreeDuplicate(Item);
  334.       dupError: ContainerError(SDuplicateItem);
  335.     end;
  336.     Result := nil;
  337.   end;
  338. end;
  339.  
  340. function TContainer.CheckNewKey(CurKey, NewKey: Pointer): Boolean;
  341. var I: Integer;
  342. begin
  343.   Result := (Duplicates = dupAccept) or (Compare(CurKey, NewKey) = 0) or
  344.             not FindKey(NewKey, I);
  345. end;
  346.  
  347. procedure TContainer.Clear;
  348. var I: Integer;
  349. begin
  350.   BeginUpdate;
  351.   I := GetCount - 1;
  352.   while I >= 0 do
  353.   begin
  354.     Delete(I);
  355.     I := Min2Int(I, GetCount);
  356.     Dec(I);
  357.   end;
  358.   EndUpdate;
  359. end;
  360.  
  361. function TContainer.Compare(Key1, Key2: Pointer): Integer;
  362. begin
  363.   if Key1 = Key2 then Result := 0 else Result := -1;
  364. end;
  365.  
  366. procedure TContainer.Delete(Index: Integer);
  367. var Item: Pointer;
  368. begin
  369.   Item := Items[Index];
  370.   ListRemove(Index);
  371.   { This items being removed might have caused related items to be removed.
  372.     Therefore do not count on Index here anymore, but instead refer to item }
  373.   if OwnesItems then FreeItem(Item);
  374.   Changed;
  375. end;
  376.  
  377. procedure TContainer.DeleteDuplicates;
  378. var First, Last, I: Integer;
  379.     Key: Pointer;
  380. begin
  381.   if GetCount < 2 then Exit;
  382.   BeginUpdate;
  383.   if Sorted then
  384.   begin { Sorted }
  385.     Last := GetCount - 1;
  386.     while Last > 0 do
  387.     begin
  388.       Key := KeyOf(Items[Last]);
  389.       First := Last - 1;
  390.       while (First >= 0) and (Compare(Key, KeyOf(Items[First])) = 0) do Dec(First);
  391.       for I := Last downto First + 2 do Delete(I);
  392.       Last := First;
  393.     end;
  394.   end { Sorted }
  395.   else
  396.   begin { not sorted }
  397.     First := 0;
  398.     while First < GetCount do
  399.     begin
  400.       Last := GetCount - 1;
  401.       Key := KeyOf(Items[First]);
  402.       while Last > First do
  403.       begin
  404.         if Compare(KeyOf(Items[Last]), Key) = 0 then
  405.           Delete(Last);
  406.         Dec(Last);
  407.       end;
  408.       Inc(First);
  409.     end;
  410.   end; { not sorted }
  411.   EndUpdate;
  412. end;
  413.  
  414. procedure TContainer.DeleteItem(Item: Pointer);
  415. var Index: Integer;
  416. begin
  417.   if Find(Item, Index) then
  418.     Delete(Index);
  419. end;
  420.  
  421. procedure TContainer.EndUpdate;
  422. begin
  423.   Dec(FUpdateCount);
  424.   if FUpdateCount = 0 then SetUpdateState(False);
  425. end;
  426.  
  427. procedure TContainer.Exchange(Index1, Index2: Integer);
  428. var
  429.   Item1, Item2: Pointer;
  430. begin
  431.   Item1 := Items[Index1];
  432.   Item2 := Items[Index2];
  433.   ListPut(Index1, Item2);
  434.   ListPut(Index2, Item1);
  435.   Changed;
  436. end;
  437.  
  438. function TContainer.Find(Item: Pointer; var Index: Integer): Boolean;
  439. begin
  440.   Result := FindKey(KeyOf(Item), Index);
  441. end;
  442.  
  443. function TContainer.FindKey(Key: Pointer; var Index: Integer): Boolean;
  444. var
  445.   L, H, I, C: Integer;
  446. begin
  447.   if Sorted then
  448.   begin
  449.     Result := False;
  450.     L := 0;
  451.     H := Count - 1;
  452.     while L <= H do
  453.     begin
  454.       I := (L + H) shr 1;
  455.       C := Compare(KeyOf(Items[I]), Key);
  456.       if C < 0 then L := I + 1 else
  457.       begin
  458.         H := I - 1;
  459.         if C = 0 then
  460.         begin
  461.           Result := True;
  462.           if Duplicates <> dupAccept then L := I;
  463.         end;
  464.       end;
  465.     end;
  466.     Index := L;
  467.   end
  468.   else
  469.   begin
  470.     Index := -1;
  471.     for I := 0 to Count - 1 do
  472.       if Compare(KeyOf(Items[I]), Key) = 0 then
  473.       begin
  474.         Index := I;
  475.         Break;
  476.       end;
  477.     Result := Index <> -1;
  478.     if not Result then Index := Count;
  479.   end
  480. end;
  481.  
  482. function TContainer.First: Pointer;
  483. begin
  484.   if Count > 0 then
  485.     Result := Items[0]
  486.   else
  487.     Result := nil;
  488. end;
  489.  
  490. procedure TContainer.FreeDataStructure;
  491. begin
  492.   if FOwnesItems then
  493.     Clear
  494.   else
  495.     RemoveAll;
  496. end;
  497.  
  498. procedure TContainer.FreeItem(Item: Pointer);
  499. begin
  500.   TObject(Item).Free;
  501. end;
  502.  
  503. procedure TContainer.FreeDuplicate(Item: Pointer);
  504. begin
  505.   FreeItem(Item);
  506.   ContainerError(SDuplicateItem);
  507. end;
  508.  
  509. function TContainer.GetItem(S: TFilter): Pointer;
  510. begin
  511.   Result := S.Get;
  512. end;
  513.  
  514. function TContainer.IndexOf(Item: Pointer): Integer;
  515. begin
  516.   if not Find(Item, Result) then Result := -1;
  517. end;
  518.  
  519. function TContainer.Insert(Index: Integer; Item: Pointer): Integer;
  520. begin
  521.   Item := CheckDuplicates(Item);
  522.   if Assigned(Item) then
  523.   begin
  524.     if Sorted then
  525.       Find(Item, Index);
  526.     Result := Index;
  527.     ListInsert(Result, Item);
  528.     Changed;
  529.   end
  530.   else
  531.     Result := -1;
  532. end;
  533.  
  534. function TContainer.KeyOf(Item: Pointer): Pointer;
  535. begin
  536.   Result := Item;
  537. end;
  538.  
  539. function TContainer.Last: Pointer;
  540. begin
  541.   if Count > 0 then
  542.     Result := Items[Count - 1]
  543.   else
  544.     Result := nil;
  545. end;
  546.  
  547. procedure TContainer.Move(CurIndex, NewIndex: Integer);
  548. var
  549.   Temp: Pointer;
  550. begin
  551.   if CurIndex <> NewIndex then
  552.   begin
  553.     BeginUpdate;
  554.     Temp := Get(CurIndex);
  555.     Remove(CurIndex);
  556.     Insert(NewIndex, Temp);
  557.     EndUpdate;
  558.   end;
  559. end;
  560.  
  561. procedure TContainer.Put(Index: Integer; Item: Pointer);
  562. begin
  563.   BeginUpdate;
  564.   Remove(Index);
  565.   Insert(Index, Item);
  566.   EndUpdate;
  567. end;
  568.  
  569. procedure TContainer.PutItem(Item: Pointer; S: TFilter);
  570. begin
  571.   S.Put(TStreamable(Item));
  572. end;
  573.  
  574. procedure TContainer.QuickSort(L, R: Integer);
  575. var
  576.   I, J: Integer;
  577.   P: Pointer;
  578. begin
  579.   I := L;
  580.   J := R;
  581.   P := Items[(L + R) shr 1];
  582.   repeat
  583.     while Compare(KeyOf(Items[I]), KeyOf(P)) < 0 do Inc(I);
  584.     while Compare(KeyOf(Items[J]), KeyOf(P)) > 0 do Dec(J);
  585.     if I <= J then
  586.     begin
  587.       Exchange(I, J);
  588.       Inc(I);
  589.       Dec(J);
  590.     end;
  591.   until I > J;
  592.   if L < J then QuickSort(L, J);
  593.   if I < R then QuickSort(I, R);
  594. end;
  595.  
  596. procedure TContainer.Remove(Index: Integer);
  597. begin
  598.   ListRemove(Index);
  599.   Changed;
  600. end;
  601.  
  602. procedure TContainer.RemoveAll;
  603. var I: Integer;
  604. begin
  605.   BeginUpdate;
  606.   for I := Count - 1 downto 0 do Remove(I);
  607.   EndUpdate;
  608. end;
  609.  
  610. procedure TContainer.RemoveItem(Item: Pointer);
  611. var Index: Integer;
  612. begin
  613.   if Find(Item, Index) then
  614.     Remove(Index);
  615. end;
  616.  
  617. procedure TContainer.SetCanSort(Value: Boolean);
  618. begin
  619.   FCanSort := Value;
  620.   if not FCanSort then Sorted := False;
  621. end;
  622.  
  623. procedure TContainer.SetDuplicates(Value: TContainerDuplicates);
  624. begin
  625.   if (FDuplicates <> Value) then
  626.   begin
  627.     if FDuplicates = dupAccept then
  628.       DeleteDuplicates;
  629.     FDuplicates := Value;
  630.     StyleChanged;
  631.   end;
  632. end;
  633.  
  634. procedure TContainer.SetOwnesItems(Value: Boolean);
  635. begin
  636.   FOwnesItems := Value;
  637. end;
  638.  
  639. procedure TContainer.SetSorted(Value: Boolean);
  640. begin
  641.   if (FSorted <> Value) then
  642.   begin
  643.     if Value and CanSort then Sort;
  644.     FSorted := Value and CanSort;
  645.     StyleChanged;
  646.   end;
  647. end;
  648.  
  649. procedure TContainer.SetUpdateState(Updating: Boolean);
  650. begin
  651.   if not Updating then Changed;
  652. end;
  653.  
  654. procedure TContainer.Store(S: TFilter);
  655. var I, C: Integer;
  656. begin
  657.   inherited Store(S);
  658.   S.Write(FCanSort, SizeOf(FCanSort));
  659.   S.Write(FDuplicates, SizeOf(FDuplicates));
  660.   S.Write(FOwnesItems, SizeOf(FOwnesItems));
  661.   S.Write(FSorted, SizeOf(FSorted));
  662.   C := Count;
  663.   S.Write(C, SizeOf(C));
  664.   for I := 0 to Count - 1 do
  665.     PutItem(Items[I], S);
  666. end;
  667.  
  668. procedure TContainer.Sort;
  669. begin
  670.   if not Sorted and CanSort and (Count > 1) then
  671.   begin
  672.     BeginUpdate;
  673.     QuickSort(0, Count - 1);
  674.     EndUpdate;
  675.   end;
  676. end;
  677.  
  678. procedure TContainer.StyleChanged;
  679. begin
  680.   if Assigned(FOnStyleChange) and (FUpdateCount = 0) then FOnChange(Self);
  681. end;
  682.  
  683. function TContainer.ValidIndex(Index: Integer): Boolean;
  684. begin
  685.   Result := (Index >= 0) and (Index < Count);
  686. end;
  687.  
  688. { TCollection }
  689.  
  690. constructor TCollection.Create;
  691. begin
  692.   inherited Create;
  693.   FList := TList.Create;
  694. end;
  695.  
  696. constructor TCollection.Load(S: TFilter);
  697. begin
  698.   FList := TList.Create;
  699.   inherited Load(S);
  700. end;
  701.  
  702. procedure TCollection.Assign(Container: TContainer);
  703. var I: Integer;
  704. begin
  705.   if ((FDuplicates = dupAccept) or (Container.Duplicates <> dupAccept)) and
  706.      (not FSorted) then
  707.   begin
  708.     BeginUpdate;
  709.     try
  710.       FList.Count := Container.Count;
  711.       if Container is TCollection then
  712.         SYSTEM.Move(TCollection(Container).FList.List^, FList.List^,
  713.                     SizeOf(Pointer) * Container.Count)
  714.       else
  715.         for I := 0 to Container.Count - 1 do FList[I] := Container[I];
  716.     finally
  717.       EndUpdate;
  718.     end;
  719.   end
  720.   else
  721.     inherited Assign(Container);
  722. end;
  723.  
  724. procedure TCollection.FreeDataStructure;
  725. begin
  726.   inherited FreeDataStructure;
  727.   FList.Free;
  728. end;
  729.  
  730. function TCollection.Get(Index: Integer): Pointer;
  731. begin
  732.   if Assigned(FList) then
  733.      Result := FList[Index]
  734.   else
  735.     Result := nil;
  736. end;
  737.  
  738. function TCollection.GetCount: Integer;
  739. begin
  740.   if Assigned(FList) then
  741.     Result := FList.Count
  742.   else
  743.     Result := 0;
  744. end;
  745.  
  746. procedure TCollection.ListInsert(Index: Integer; Item: Pointer);
  747. begin
  748.   if Assigned(FList) then FList.Insert(Index, Item);
  749. end;
  750.  
  751. procedure TCollection.ListPut(Index: Integer; Item: Pointer);
  752. begin
  753.   if Assigned(FList) then FList.Items[Index] := Item;
  754. end;
  755.  
  756. procedure TCollection.ListRemove(Index: Integer);
  757. begin
  758.   if Assigned(FList) then FList.Delete(Index);
  759. end;
  760.  
  761. procedure TCollection.RemoveAll;
  762. begin
  763.   BeginUpdate;
  764.   if Assigned(FList) then FList.Clear;
  765.   EndUpdate;
  766. end;
  767.  
  768.  
  769. end.
  770.