unit LList2C;
//*******************************************************
// Class implementing linked lists with CurrentItem and
// MoveNext methods.
//*******************************************************
// Copyright (C) 1998 John Wiley & Sons, Inc.
// All rights reserved. See additional copyright
// information in Readme.txt.
//*******************************************************

interface

uses
    Dialogs, SysUtils, Classes;

type
    // The linked list cells.
    String10 = String[10];
    PLinkedListCell = ^TLinkedListCell;
    TLinkedListCell = record
        Value    : String10;        // The data.
        NextCell : PLinkedListCell; // The next cell.
    end;

    // The linked list class.
    TLinkedList = class(TObject)
        private
            Sentinel    : TLinkedListCell; // The list sentinel (not a pointer).
            NumItems    : Longint;         // For convenience.
            CurrentCell : PLinkedListCell; // Currently selected item.

        public
            destructor Destroy; override;
            procedure AddAfter(after_me : Longint; new_value : String10);
            procedure RemoveAfter(after_me : Longint);
            procedure ClearList;
            function Count : Longint;
            function Item(index : Longint) : String10;
            function CurrentItem : String10;
            function EndOfList : Boolean;
            procedure MoveFirst;
            procedure MoveNext;
    end;

implementation

// Free any allocated memory.
destructor TLinkedList.Destroy;
begin
    ClearList;
    inherited Destroy;
end;

// Add an item to the list.
procedure TLinkedList.AddAfter(after_me : Longint; new_value : String10);
var
    cell_ptr, new_cell : PLinkedListCell;
begin
    if ((after_me < 0) or (after_me > NumItems)) then
        raise ERangeError.CreateFmt(
            'Index %d out of bounds %d..%d',
            [after_me, 0, NumItems]);

    // Find the insertion position.
    cell_ptr := @Sentinel;
    while (after_me > 0) do
    begin
        after_me := after_me - 1;
        cell_ptr := cell_ptr^.NextCell;
    end;

    // Create the new cell.
    New(new_cell);
    new_cell^.Value := new_value;

    // Insert the cell in the list.
    new_cell^.NextCell := cell_ptr^.NextCell;
    cell_ptr^.NextCell := new_cell;
    NumItems := NumItems + 1;
end;

// Remove an item from the list.
procedure TLinkedList.RemoveAfter(after_me : Longint);
var
    cell_ptr, target : PLinkedListCell;
begin
    if ((after_me < 0) or (after_me >= NumItems)) then
        raise ERangeError.CreateFmt(
            'Index %d out of bounds %d..%d',
            [after_me, 0, NumItems - 1]);

    // Find the target cell.
    cell_ptr := @Sentinel;
    while (after_me > 0) do
    begin
        after_me := after_me - 1;
        cell_ptr := cell_ptr^.NextCell;
    end;

    // Remove the cell from the list.
    target := cell_ptr^.NextCell;
    cell_ptr^.NextCell := target^.NextCell;

    // Free the target cell's memory.
    Dispose(target);

    NumItems := NumItems - 1;
end;

// Remove all items from the list.
procedure TLinkedList.ClearList;
var
    target : PLinkedListCell;
begin
    while (Sentinel.NextCell <> nil) do
    begin
        target := Sentinel.NextCell;
        Sentinel.NextCell := target^.NextCell;
        Dispose(target);
    end;
    NumItems := 0;
end;

// Return the number of items in the list.
function TLinkedList.Count : Longint;
begin
    Count := NumItems;
end;

// Return the value of an item in the list.
function TLinkedList.Item(index : Longint) : String10;
var
    cell_ptr : PLinkedListCell;
begin
    if ((index < 1) or (index > NumItems)) then
        raise ERangeError.CreateFmt(
            'Index %d out of bounds %d..%d',
            [index, 1, NumItems]);

    // Find the cell.
    cell_ptr := Sentinel.NextCell;
    while (index > 1) do
    begin
        index := index - 1;
        cell_ptr := cell_ptr^.NextCell;
    end;
    Item := cell_ptr^.Value;
end;

// Return the current item's value.
function TLinkedList.CurrentItem : String10;
begin
    if (CurrentCell = nil) then
        raise EInvalidOperation.Create(
            'No current cell selected.')
    else
        CurrentItem := CurrentCell.Value;
end;

// Return true if the current item is beyond
// the end of the list.
function TLinkedList.EndOfList : Boolean;
begin
    EndOfList := (CurrentCell = nil);
end;

// Move CurrentItem to the first cell.
procedure TLinkedList.MoveFirst;
begin
    CurrentCell := Sentinel.NextCell;
end;

// Move CurrentItem to the next cell.
procedure TLinkedList.MoveNext;
begin
    // If there's no current cell, do nothing.
    if (CurrentCell <> nil) then
        CurrentCell := CurrentCell.NextCell;
end;

end.
