unit DblListC;
//*******************************************************
// Doubly linked list class.
//*******************************************************
// 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 = ^TDblLinkedListCell;
    TDblLinkedListCell = record
        Value    : String10;        // The data.
        NextCell : PLinkedListCell; // The next cell.
        PrevCell : PLinkedListCell; // The previous cell.
    end;

    // The linked list class.
    TDblLinkedList = class(TObject)
        private
            TopSentinel    : TDblLinkedListCell; // The top list sentinel (not a pointer).
            BottomSentinel : TDblLinkedListCell; // The bottom list sentinel (not a pointer).
            NumItems       : Longint;            // For convenience.
            CurrentCell    : PLinkedListCell;    // Currently selected item.

        public
            constructor Create;
            destructor Destroy; override;
            procedure AddAfter(after_me : Longint; new_value : String10);
            procedure AddBefore(before_me : Longint; new_value : String10);
            procedure Remove(target : Longint);
            procedure ClearList;
            function Count : Longint;
            function CurrentItem : String10;
            function EndOfList : Boolean;
            procedure MoveFirst;
            procedure MoveLast;
            procedure MoveNext;
            procedure MovePrev;
    end;

implementation

// Initialize the sentinels so they point to each other.
constructor TDblLinkedList.Create;
begin
    // Allocate memory and perform inherited initialization.
    inherited Create;

    TopSentinel.NextCell := @BottomSentinel;
    BottomSentinel.PrevCell := @TopSentinel;
end;

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

// Add an item after the indicated item.
procedure TDblLinkedList.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 := @TopSentinel;
    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 after cell_ptr.
    new_cell^.PrevCell := cell_ptr;
    new_cell^.NextCell := cell_ptr^.NextCell;
    new_cell^.NextCell^.PrevCell := new_cell;
    cell_ptr^.NextCell := new_cell;
    NumItems := NumItems + 1;
end;

// Add an item before the indicated item.
procedure TDblLinkedList.AddBefore(before_me : Longint; new_value : String10);
var
    cell_ptr, new_cell : PLinkedListCell;
begin
    if ((before_me < 1) or (before_me > NumItems + 1)) then
        raise ERangeError.CreateFmt(
            'Index %d out of bounds %d..%d',
            [before_me, 1, NumItems + 1]);

    // Find the insertion position.
    cell_ptr := TopSentinel.NextCell;
    while (before_me > 1) do
    begin
        before_me := before_me - 1;
        cell_ptr := cell_ptr^.NextCell;
    end;

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

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

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

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

    // If we are removing the currently selected item,
    // select no item.
    if (cell_ptr = CurrentCell) then
        CurrentCell := @TopSentinel;

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

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

    NumItems := NumItems - 1;
end;

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

    // Select no item.
    CurrentCell := @TopSentinel;
end;

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

// Return the current item's value.
function TDblLinkedList.CurrentItem : String10;
begin
    if (EndOfList) 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 TDblLinkedList.EndOfList : Boolean;
begin
    EndOfList :=
        ((CurrentCell = @TopSentinel) or
         (CurrentCell = @BottomSentinel));
end;

// Move CurrentCell to the first cell.
procedure TDblLinkedList.MoveFirst;
begin
    CurrentCell := TopSentinel.NextCell;
end;

// Move CurrentCell to the last cell.
procedure TDblLinkedList.MoveLast;
begin
    CurrentCell := BottomSentinel.PrevCell;
end;

// Move CurrentCell to the next cell.
procedure TDblLinkedList.MoveNext;
begin
    // If there's no current cell, do nothing.
    if (not EndOfList) then
        CurrentCell := CurrentCell^.NextCell;
end;

// Move CurrentCell to the previous cell.
procedure TDblLinkedList.MovePrev;
begin
    // If there's no current cell, do nothing.
    if (not EndOfList) then
        CurrentCell := CurrentCell^.PrevCell;
end;

end.
