unit DblListF;
//*******************************************************
// Example program demonstrating doubly linked lists.
//*******************************************************
// Copyright (C) 1998 John Wiley & Sons, Inc.
// All rights reserved. See additional copyright
// information in Readme.txt.
//*******************************************************

interface

uses
  Windows, Messages, SysUtils, Graphics, Controls, Forms, Dialogs,
  Menus, StdCtrls,
  DblListC, Classes;

type
  TDblListForm = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Help1: TMenuItem;
    mnuExit: TMenuItem;
    mnuAbout: TMenuItem;
    ItemText: TEdit;
    Label1: TLabel;
    CmdAddAfter: TButton;
    CmdRemove: TButton;
    Label2: TLabel;
    CmdClearList: TButton;
    CmdAddBefore: TButton;
    Label3: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure mnuExitClick(Sender: TObject);
    procedure mnuAboutClick(Sender: TObject);
    procedure CmdAddAfterClick(Sender: TObject);
    procedure CmdRemoveClick(Sender: TObject);
    procedure CmdClearListClick(Sender: TObject);
    procedure ItemTextChange(Sender: TObject);
    procedure ShowList;
    procedure LabelsOnClick(Sender: TObject);
    procedure SelectLabel(index : Longint);
    procedure Deselect;
    procedure CmdAddBeforeClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DblListForm: TDblListForm;

implementation

type
    // Note: This array starts at 0.
    TListLabelArray = array[0..100000000] of TLabel;
    PListLabelArray = ^TListLabelArray;

var
    the_list    : TDblLinkedList;
    list_labels : PListLabelArray;
    max_label   : Longint;
    selected    : Longint;

{$R *.DFM}

procedure TDblListForm.FormCreate(Sender: TObject);
begin
    // Make the list.
    the_list := TDblLinkedList.Create;

    // Put the sentinel's labels in the label list.
    GetMem(list_labels,
        (the_list.Count + 2) * SizeOf(TLabel));
    list_labels^[0] := Label2;
    Label2.OnClick := LabelsOnClick; // Event handler.
    list_labels^[1] := Label3;
    Label3.OnClick := LabelsOnClick; // Event handler.
    max_label := 1;

    // Start with no label selected.
    selected := -1;
end;

procedure TDblListForm.mnuExitClick(Sender: TObject);
begin
     Close;
end;

procedure TDblListForm.mnuAboutClick(Sender: TObject);
const
    CRCR = #13#10#13#10;
begin
    MessageDlg(
        'This program demonstrates a doubly linked list.' + CRCR +
        'Enter a value and click on an item or a sentinel. Then click Add Before to add the item before the position you selected. Click Add After to add the item after the position you selected.' + CRCR +
        'Click an item in the list and click Remove to remove the item.'
        , mtInformation, [mbOK], 0);
end;

procedure TDblListForm.CmdAddAfterClick(Sender: TObject);
var
    new_index : Longint;
begin
    the_list.AddAfter(selected, ItemText.Text);
    new_index := selected + 1;
    ShowList;
    SelectLabel(new_index); // Select the new item.
    ItemText.Text := '';
    ItemText.SetFocus;
end;

procedure TDblListForm.CmdRemoveClick(Sender: TObject);
begin
    the_list.Remove(selected);
    Deselect;
    ShowList;
    ItemText.SetFocus;
end;

procedure TDblListForm.CmdClearListClick(Sender: TObject);
begin
    the_list.ClearList;
    Deselect;
    ShowList;
    ItemText.SetFocus;
end;

procedure TDblListForm.ItemTextChange(Sender: TObject);
begin
    CmdAddAfter.Enabled :=
        ((selected >= 0) and
         (selected <= the_list.Count) and
         (ItemText.Text <> ''));
    CmdAddBefore.Enabled :=
        ((selected >= 1) and
         (selected <= the_list.Count + 1) and
         (ItemText.Text <> ''));
end;

procedure TDblListForm.ShowList;
var
    new_labels : PListLabelArray;
    i          : Longint;
    t, l, w, h : Integer;
begin

    // Make enough labels for the list.
    if (the_list.Count >= max_label) then
    begin
        // Allocate space for the labels.
        GetMem(new_labels, (the_list.Count + 2) * SizeOf(TLabel));

        // Copy the old labels.
        for i := 0 to max_label do
            new_labels^[i] := list_labels^[i];

        // Create the new labels.
        l := Label2.Left;
        w := Label2.Width;
        h := Label2.Height;
        t := new_labels^[max_label].Top + h;
        for i := max_label + 1 to the_list.Count + 1 do
        begin
            new_labels^[i] := TLabel.Create(Self);
            with new_labels^[i] do
            begin
                Parent := Self;
                Left := l;
                Top := t;
                Width := w;
                Height := h;
                AutoSize := False;
                Alignment := taCenter;
                OnClick := LabelsOnClick; // Event handler.
            end;
            t := t + h;
        end;

        // Point to the new array.
        list_labels := new_labels;
        max_label := the_list.Count + 1;
    end;

    // Display the list.
    the_list.MoveFirst;
    i := 1;
    while (not the_list.EndOfList) do
    begin
        list_labels^[i].Caption := the_list.CurrentItem;
        list_labels^[i].Visible := True;
        i := i + 1;
        the_list.MoveNext;
    end;

    // Display the bottom sentinel.
    list_labels^[the_list.Count + 1].Caption := '<Sentinel>';
    list_labels^[the_list.Count + 1].Visible := True;

    // Hide the other labels.
    for i := the_list.Count + 2 to max_label do
    begin
        list_labels^[i].Visible := False;
    end;

    Deselect;
    CmdRemove.Enabled := False;
end;

procedure TDblListForm.LabelsOnClick(Sender: TObject);
var
    i : Longint;
begin
    for i := 0 to max_label do
        if (list_labels^[i] = Sender) then
        begin
            SelectLabel(i);
            exit;
        end;
end;

procedure TDblListForm.SelectLabel(index : Longint);
begin
    // Unhighlight the previous selection.
    Deselect;

    // Highlight the label.
    if (index <= the_list.Count + 1) then
    begin
        selected := index;
        list_labels^[selected].Color := Label1.Font.Color;
        list_labels^[selected].Font.Color := Label1.Color;
    end;
    CmdAddAfter.Enabled :=
        ((selected >= 0) and
         (selected <= the_list.Count) and
         (ItemText.Text <> ''));
    CmdAddBefore.Enabled :=
        ((selected >= 1) and
         (selected <= the_list.Count + 1) and
         (ItemText.Text <> ''));
    CmdRemove.Enabled :=
        ((selected >= 1) and
         (selected <= the_list.Count));
end;

procedure TDblListForm.Deselect;
begin
    if (selected >= 0) then
    begin
        list_labels^[selected].Color := Label1.Color;
        list_labels^[selected].Font.Color := Label1.Font.Color;
        selected := -1;
    end;
end;

procedure TDblListForm.CmdAddBeforeClick(Sender: TObject);
var
    new_index : Longint;
begin
    the_list.AddBefore(selected, ItemText.Text);
    new_index := selected;
    ShowList;
    SelectLabel(new_index); // Select the new item.
    ItemText.Text := '';
    ItemText.SetFocus;
end;

end.
