{

  Copyright (c) 1997-2003 by CoolDev.Com. Part of CoolControls package.

}

{$I versions.inc}
unit CoolLists;

interface

uses
  Classes, Windows, Graphics;

type
  // TIntegerList comes with the same methods and properties as TList but
  // works with the Integer type values.
  TIntegerList = class(TPersistent)
  private
    FList: TList;
    FSorted: Boolean;
    FOnChange: TNotifyEvent;
    procedure WriteIntegers(Writer: TWriter);
    procedure ReadIntegers(Reader: TReader);
    procedure SetSorted(Value: Boolean);
  protected
    procedure DefineProperties(Filer: TFiler); override;
    function GetCount: Integer;
    function GetItem(Index: Integer): Integer;
    procedure SetItem(Index: Integer; Value: Integer);
    procedure Changed; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(Value: Integer); virtual;
    procedure AddIntegers(Value: TIntegerList); virtual;
    procedure Assign(Source: TPersistent); override;
    procedure Clear; virtual;
    procedure Delete(Index: Integer); virtual;
    procedure Exchange(Index1, Index2: Integer); virtual;
    function IndexOf(Value: Integer): Integer; virtual;
    procedure Insert(Index: Integer; Value: Integer); virtual;
    procedure Move(CurIndex, NewIndex: Integer); virtual;
    procedure Sort(Compare: TListSortCompare); virtual;
    property Count: Integer read GetCount;
    property Items[Index: Integer]: Integer read GetItem write SetItem; default;
    property Sorted: Boolean read FSorted write SetSorted;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  // TPointList comes with the same methods and properties as TList but
  // works with the TPoint type values.
  TPointList = class(TPersistent)
  private
    FList: TList;
    FOnChange: TNotifyEvent;
    procedure WritePoints(Writer: TWriter);
    procedure ReadPoints(Reader: TReader);
  protected
    procedure DefineProperties(Filer: TFiler); override;
    function GetCount: Integer;
    function GetItem(Index: Integer): TPoint;
    procedure SetItem(Index: Integer; Value: TPoint);
    procedure Changed; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(Value: TPoint); virtual;
    procedure AddPoints(Value: TPointList); virtual;
    procedure Assign(Source: TPersistent); override;
    procedure Clear; virtual;
    procedure Delete(Index: Integer); virtual;
    procedure Exchange(Index1, Index2: Integer); virtual;
    function IndexOf(Value: TPoint): Integer; virtual;
    procedure Insert(Index: Integer; Value: TPoint); virtual;
    procedure Move(CurIndex, NewIndex: Integer); virtual;
    property Count: Integer read GetCount;
    property Items[Index: Integer]: TPoint read GetItem write SetItem; default;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  // TRectList comes with the same methods and properties as TList but
  // works with the TRect type values.
  TRectList = class(TPersistent)
  private
    FList: TList;
    FOnChange: TNotifyEvent;
    procedure WriteRects(Writer: TWriter);
    procedure ReadRects(Reader: TReader);
  protected
    procedure DefineProperties(Filer: TFiler); override;
    function GetCount: Integer;
    function GetItem(Index: Integer): TRect;
    procedure SetItem(Index: Integer; Value: TRect);
    procedure Changed; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(Value: TRect); virtual;
    procedure AddRects(Value: TRectList); virtual;
    procedure Assign(Source: TPersistent); override;
    procedure Clear; virtual;
    procedure Delete(Index: Integer); virtual;
    procedure Exchange(Index1, Index2: Integer); virtual;
    function IndexOf(Value: TRect): Integer; virtual;
    procedure Insert(Index: Integer; Value: TRect); virtual;
    procedure Move(CurIndex, NewIndex: Integer); virtual;
    property Count: Integer read GetCount;
    property Items[Index: Integer]: TRect read GetItem write SetItem; default;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  {$IFNDEF DELPHI5_OR_ABOVE}
  // Pointer to TColor type.
  PColor = ^TColor;
  {$ENDIF}

  // TColorList comes with the same methods and properties as TList but
  // works with the TColor type values.
  TColorList = class(TPersistent)
  private
    FList: TList;
    FOnChange: TNotifyEvent;
    procedure WriteColors(Writer: TWriter);
    procedure ReadColors(Reader: TReader);
  protected
    procedure DefineProperties(Filer: TFiler); override;
    function GetCount: Integer;
    function GetItem(Index: Integer): TColor;
    procedure SetItem(Index: Integer; Value: TColor);
    procedure Changed; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(Value: TColor); virtual;
    procedure AddColors(Value: TColorList); virtual;
    procedure Assign(Source: TPersistent); override;
    procedure Clear; virtual;
    procedure Delete(Index: Integer); virtual;
    procedure Exchange(Index1, Index2: Integer); virtual;
    function IndexOf(Value: TColor): Integer; virtual;
    procedure Insert(Index: Integer; Value: TColor); virtual;
    procedure Move(CurIndex, NewIndex: Integer); virtual;
    property Count: Integer read GetCount;
    property Items[Index: Integer]: TColor read GetItem write SetItem; default;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  // Points to the TPixelData type.
  PPixelData = ^TPixelData;
  // Used to describe the particular pixel.
  TPixelData = packed record
    // color of the pixel.
    Color: TColor;
    // X coordinate of the pixel.
    X: Integer;
    // Y coordinate of the pixel.
    Y: Integer;
  end;

  // TPixelDataList comes with the same methods and properties as TList but
  // works with the TPixelData type values.
  TPixelDataList = class(TPersistent)
  private
    FList: TList;
    FOnChange: TNotifyEvent;
    procedure WritePixelDatas(Writer: TWriter);
    procedure ReadPixelDatas(Reader: TReader);
  protected
    procedure DefineProperties(Filer: TFiler); override;
    function GetCount: Integer;
    function GetItem(Index: Integer): TPixelData;
    procedure SetItem(Index: Integer; Value: TPixelData);
    procedure Changed; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(Value: TPixelData); virtual;
    procedure AddPixelDatas(Value: TPixelDataList); virtual;
    procedure Assign(Source: TPersistent); override;
    procedure Clear; virtual;
    procedure Delete(Index: Integer); virtual;
    procedure Exchange(Index1, Index2: Integer); virtual;
    function IndexOf(Value: TPixelData): Integer; virtual;
    procedure Insert(Index: Integer; Value: TPixelData); virtual;
    procedure Move(CurIndex, NewIndex: Integer); virtual;
    property Count: Integer read GetCount;
    property Items[Index: Integer]: TPixelData read GetItem write SetItem; default;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

implementation

{TIntegerList}
constructor TIntegerList.Create;
begin
  inherited Create;
  FList := TList.Create;
end;

destructor TIntegerList.Destroy;
begin
  Clear;
  FList.Free;
  inherited Destroy;
end;

procedure TIntegerList.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TIntegerList.Assign(Source: TPersistent);
begin
  if Source is TIntegerList then
  begin
    Clear;
    AddIntegers(TIntegerList(Source));
  end else inherited Assign(Source);
end;

procedure TIntegerList.DefineProperties(Filer: TFiler);
begin
  Filer.DefineProperty('Points', ReadIntegers, WriteIntegers, Count > 0);
end;

procedure TIntegerList.WriteIntegers(Writer: TWriter);
var
  I: Integer;
begin
  Writer.WriteListBegin;

  for I := 0 to Count-1 do
    Writer.WriteInteger(GetItem(I));

  Writer.WriteListEnd;
end;

procedure TIntegerList.ReadIntegers(Reader: TReader);
var
  I: Integer;
begin
  Reader.ReadListBegin;
  Clear;
  while not Reader.EndOfList do
  begin
    I := Reader.ReadInteger;
    Add(I);
  end;
  Reader.ReadListEnd;
end;

function TIntegerList.GetCount: Integer;
begin
  Result := FList.Count;
end;

function TIntegerList.GetItem(Index: Integer): Integer;
begin
  Result := PInteger(FList.Items[Index])^;
end;

procedure TIntegerList.SetItem(Index: Integer; Value: Integer);
begin
  PInteger(FList.Items[Index])^ := Value;
  Changed;
end;

procedure TIntegerList.Add(Value: Integer);
begin
  Insert(Count, Value);
end;

procedure TIntegerList.AddIntegers(Value: TIntegerList);
var
  I: Integer;
begin
  for I := 0 to Pred(Value.Count) do
   Add(Value[I]);

  Changed;
end;

procedure TIntegerList.Clear;
var
  I: Integer;
begin
  for I := 0 to Pred(FList.Count) do
    Dispose(PInteger(FList.Items[I]));
  FList.Clear;

  Changed;
end;

procedure TIntegerList.Delete(Index: Integer);
begin
  Dispose(PInteger(FList.Items[Index]));
  FList.Delete(Index);
  Changed;
end;

procedure TIntegerList.Exchange(Index1, Index2: Integer);
begin
  FList.Exchange(Index1, Index2);
  Changed;
end;

function TIntegerList.IndexOf(Value: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Pred(GetCount) do
    if GetItem(I) = Value then
    begin
      Result := I;
      Break;
    end;
end;

procedure TIntegerList.Insert(Index: Integer; Value: Integer);
var
  P: PInteger;
begin
  New(P);
  P^ := Value;
  FList.Insert(Index, P);
  Changed;
end;

procedure TIntegerList.Move(CurIndex, NewIndex: Integer);
begin
  FList.Move(CurIndex, NewIndex);
  Changed;
end;

procedure TIntegerList.Sort(Compare: TListSortCompare);
begin
  FList.Sort(Compare);
end;

function Compare(Item1, Item2: Pointer): Integer;
begin
  if Integer(Item1^) = Integer(Item2^) then
    Result := 0 
  else
    if Integer(Item1^) < Integer(Item2^) then
      Result := -1
    else
      Result := 1;
end;

procedure TIntegerList.SetSorted(Value: Boolean);
begin  
  if FSorted <> Value then
    FSorted := Value;

  if FSorted then
    Sort(Compare);
end;

{TPointList}
constructor TPointList.Create;
begin
  inherited Create;
  FList := TList.Create;
end;

destructor TPointList.Destroy;
begin
  Clear;
  FList.Free;
  inherited Destroy;
end;

procedure TPointList.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TPointList.Assign(Source: TPersistent);
begin
  if Source is TPointList then
  begin
    Clear;
    AddPoints(TPointList(Source));
  end else inherited Assign(Source);
end;

procedure TPointList.DefineProperties(Filer: TFiler);
begin
  Filer.DefineProperty('Points', ReadPoints, WritePoints, Count > 0);
end;

procedure TPointList.WritePoints(Writer: TWriter);
var
  I: Integer;
begin
  Writer.WriteListBegin;

  for I := 0 to Count-1 do
    with GetItem(I) do
    begin
      Writer.WriteInteger(X);
      Writer.WriteInteger(Y);
    end;

  Writer.WriteListEnd;
end;

procedure TPointList.ReadPoints(Reader: TReader);
var
  X, Y: Integer;
begin
  Reader.ReadListBegin;
  Clear;
  while not Reader.EndOfList do
  begin
    X := Reader.ReadInteger;
    Y := Reader.ReadInteger;
    Add(Point(X, Y));
  end;
  Reader.ReadListEnd;
end;

function TPointList.GetCount: Integer;
begin
  Result := FList.Count;
end;

function TPointList.GetItem(Index: Integer): TPoint;
begin
  Result := PPoint(FList.Items[Index])^;
end;

procedure TPointList.SetItem(Index: Integer; Value: TPoint);
begin
  PPoint(FList.Items[Index])^ := Value;
  Changed;
end;

procedure TPointList.Add(Value: TPoint);
begin
  Insert(Count, Value);
end;

procedure TPointList.AddPoints(Value: TPointList);
var
  I: Integer;
begin
  for I := 0 to Pred(Value.Count) do
   Add(Value[I]);

  Changed;
end;

procedure TPointList.Clear;
var
  I: Integer;
begin
  for I := 0 to Pred(FList.Count) do
    Dispose(PPoint(FList.Items[I]));
  FList.Clear;

  Changed;
end;

procedure TPointList.Delete(Index: Integer);
begin
  Dispose(PPoint(FList.Items[Index]));
  FList.Delete(Index);
  Changed;
end;

procedure TPointList.Exchange(Index1, Index2: Integer);
begin
  FList.Exchange(Index1, Index2);
  Changed;
end;

function TPointList.IndexOf(Value: TPoint): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Pred(GetCount) do
    if (GetItem(I).X = Value.X) and (GetItem(I).Y = Value.Y) then
    begin
      Result := I;
      Break;
    end;
end;

procedure TPointList.Insert(Index: Integer; Value: TPoint);
var
  P: PPoint;
begin
  New(P);
  P^ := Value;
  FList.Insert(Index, P);
  Changed;
end;

procedure TPointList.Move(CurIndex, NewIndex: Integer);
begin
  FList.Move(CurIndex, NewIndex);
  Changed;
end;

{TRectList}
constructor TRectList.Create;
begin
  inherited Create;
  FList := TList.Create;
end;

destructor TRectList.Destroy;
begin
  Clear;
  FList.Free;
  inherited Destroy;
end;

procedure TRectList.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TRectList.Assign(Source: TPersistent);
begin
  if Source is TRectList then
  begin
    Clear;
    AddRects(TRectList(Source));
  end else inherited Assign(Source);
end;

procedure TRectList.DefineProperties(Filer: TFiler);
begin
  Filer.DefineProperty('Points', ReadRects, WriteRects, Count > 0);
end;

procedure TRectList.WriteRects(Writer: TWriter);
var
  I: Integer;
begin
  Writer.WriteListBegin;

  for I := 0 to Count-1 do
    with GetItem(I) do
    begin
      Writer.WriteInteger(Left);
      Writer.WriteInteger(Top);
      Writer.WriteInteger(Right);
      Writer.WriteInteger(Bottom);
    end;

  Writer.WriteListEnd;
end;

procedure TRectList.ReadRects(Reader: TReader);
var
  Left, Top, Right, Bottom: Integer;
begin
  Reader.ReadListBegin;
  Clear;
  while not Reader.EndOfList do
  begin
    Left := Reader.ReadInteger;
    Top := Reader.ReadInteger;
    Right := Reader.ReadInteger;
    Bottom := Reader.ReadInteger;
    Add(Rect(Left, Top, Right, Bottom));
  end;
  Reader.ReadListEnd;
end;

function TRectList.GetCount: Integer;
begin
  Result := FList.Count;
end;

function TRectList.GetItem(Index: Integer): TRect;
begin
  Result := PRect(FList.Items[Index])^;
end;

procedure TRectList.SetItem(Index: Integer; Value: TRect);
begin
  PRect(FList.Items[Index])^ := Value;
  Changed;
end;

procedure TRectList.Add(Value: TRect);
begin
  Insert(Count, Value);
end;

procedure TRectList.AddRects(Value: TRectList);
var
  I: Integer;
begin
  for I := 0 to Pred(Value.Count) do
   Add(Value[I]);

  Changed;
end;

procedure TRectList.Clear;
var
  I: Integer;
begin
  for I := 0 to Pred(FList.Count) do
    Dispose(PRect(FList.Items[I]));
  FList.Clear;

  Changed;
end;

procedure TRectList.Delete(Index: Integer);
begin
  Dispose(PRect(FList.Items[Index]));
  FList.Delete(Index);
  Changed;
end;

procedure TRectList.Exchange(Index1, Index2: Integer);
begin
  FList.Exchange(Index1, Index2);
  Changed;
end;

function TRectList.IndexOf(Value: TRect): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Pred(GetCount) do
    if (GetItem(I).Left = Value.Left) and (GetItem(I).Top = Value.Top) and (GetItem(I).Right = Value.Right) and (GetItem(I).Bottom = Value.Bottom) then
    begin
      Result := I;
      Break;
    end;
end;

procedure TRectList.Insert(Index: Integer; Value: TRect);
var
  P: PRect;
begin
  New(P);
  P^ := Value;
  FList.Insert(Index, P);
  Changed;
end;

procedure TRectList.Move(CurIndex, NewIndex: Integer);
begin
  FList.Move(CurIndex, NewIndex);
  Changed;
end;

{TColorList}
constructor TColorList.Create;
begin
  inherited Create;
  FList := TList.Create;
end;

destructor TColorList.Destroy;
begin
  Clear;
  FList.Free;
  inherited Destroy;
end;

procedure TColorList.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TColorList.Assign(Source: TPersistent);
begin
  if Source is TColorList then
  begin
    Clear;
    AddColors(TColorList(Source));
  end else inherited Assign(Source);
end;

procedure TColorList.DefineProperties(Filer: TFiler);
begin
  Filer.DefineProperty('Points', ReadColors, WriteColors, Count > 0);
end;

procedure TColorList.WriteColors(Writer: TWriter);
var
  I: Integer;
  S: string;
begin
  Writer.WriteListBegin;

  for I := 0 to Count-1 do
  begin
    S := ColorToString(GetItem(I));
    Writer.WriteString(S);
  end;

  Writer.WriteListEnd;
end;

procedure TColorList.ReadColors(Reader: TReader);
var
  S: string;
begin
  Reader.ReadListBegin;
  Clear;
  while not Reader.EndOfList do
  begin
    S := Reader.ReadString;
    Add(StringToColor(S));
  end;
  Reader.ReadListEnd;
end;

function TColorList.GetCount: Integer;
begin
  Result := FList.Count;
end;

function TColorList.GetItem(Index: Integer): TColor;
begin
  Result := PColor(FList.Items[Index])^;
end;

procedure TColorList.SetItem(Index: Integer; Value: TColor);
begin
  PColor(FList.Items[Index])^ := Value;
  Changed;
end;

procedure TColorList.Add(Value: TColor);
begin
  Insert(Count, Value);
end;

procedure TColorList.AddColors(Value: TColorList);
var
  I: Integer;
begin
  for I := 0 to Pred(Value.Count) do
   Add(Value[I]);

  Changed;
end;

procedure TColorList.Clear;
var
  I: Integer;
begin
  for I := 0 to Pred(FList.Count) do
    Dispose(PColor(FList.Items[I]));
  FList.Clear;

  Changed;
end;

procedure TColorList.Delete(Index: Integer);
begin
  Dispose(PColor(FList.Items[Index]));
  FList.Delete(Index);
  Changed;
end;

procedure TColorList.Exchange(Index1, Index2: Integer);
begin
  FList.Exchange(Index1, Index2);
  Changed;
end;

function TColorList.IndexOf(Value: TColor): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Pred(GetCount) do
    if GetItem(I) = Value then
    begin
      Result := I;
      Break;
    end;
end;

procedure TColorList.Insert(Index: Integer; Value: TColor);
var
  P: PColor;
begin
  New(P);
  P^ := Value;
  FList.Insert(Index, P);
  Changed;
end;

procedure TColorList.Move(CurIndex, NewIndex: Integer);
begin
  FList.Move(CurIndex, NewIndex);
  Changed;
end;

{TPixelDataList}
constructor TPixelDataList.Create;
begin
  inherited Create;
  FList := TList.Create;
end;

destructor TPixelDataList.Destroy;
begin
  Clear;
  FList.Free;
  inherited Destroy;
end;

procedure TPixelDataList.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TPixelDataList.Assign(Source: TPersistent);
begin
  if Source is TPixelDataList then
  begin
    Clear;
    AddPixelDatas(TPixelDataList(Source));
  end else inherited Assign(Source);
end;

procedure TPixelDataList.DefineProperties(Filer: TFiler);
begin
  Filer.DefineProperty('Points', ReadPixelDatas, WritePixelDatas, Count > 0);
end;

procedure TPixelDataList.WritePixelDatas(Writer: TWriter);
var
  I: Integer;
  PixelData: TPixelData;
begin
  Writer.WriteListBegin;

  for I := 0 to Count-1 do
  begin
    PixelData := GetItem(I);
    Writer.WriteString(ColorToString(PixelData.Color));
    Writer.WriteInteger(PixelData.X);
    Writer.WriteInteger(PixelData.Y);
  end;

  Writer.WriteListEnd;
end;

procedure TPixelDataList.ReadPixelDatas(Reader: TReader);
var
  S: String;
  PixelData: TPixelData;
begin
  Reader.ReadListBegin;
  Clear;
  while not Reader.EndOfList do
  begin
    S := Reader.ReadString;
    PixelData.Color := StringToColor(S);
    PixelData.X := Reader.ReadInteger;
    PixelData.Y := Reader.ReadInteger;
    Add(PixelData);
  end;
  Reader.ReadListEnd;
end;

function TPixelDataList.GetCount: Integer;
begin
  Result := FList.Count;
end;

function TPixelDataList.GetItem(Index: Integer): TPixelData;
begin
  Result := PPixelData(FList.Items[Index])^;
end;

procedure TPixelDataList.SetItem(Index: Integer; Value: TPixelData);
begin
  PPixelData(FList.Items[Index])^ := Value;
  Changed;
end;

procedure TPixelDataList.Add(Value: TPixelData);
begin
  Insert(Count, Value);
end;

procedure TPixelDataList.AddPixelDatas(Value: TPixelDataList);
var
  I: Integer;
begin
  for I := 0 to Pred(Value.Count) do
   Add(Value[I]);

  Changed;
end;

procedure TPixelDataList.Clear;
var
  I: Integer;
begin
  for I := 0 to Pred(FList.Count) do
    Dispose(PPixelData(FList.Items[I]));
  FList.Clear;

  Changed;
end;

procedure TPixelDataList.Delete(Index: Integer);
begin
  Dispose(PPixelData(FList.Items[Index]));
  FList.Delete(Index);
  Changed;
end;

procedure TPixelDataList.Exchange(Index1, Index2: Integer);
begin
  FList.Exchange(Index1, Index2);
  Changed;
end;

function TPixelDataList.IndexOf(Value: TPixelData): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Pred(GetCount) do
    if (GetItem(I).Color = Value.Color) and (GetItem(I).X = Value.X) and (GetItem(I).Y = Value.Y) then
    begin
      Result := I;
      Break;
    end;
end;

procedure TPixelDataList.Insert(Index: Integer; Value: TPixelData);
var
  P: PPixelData;
begin
  New(P);
  P^ := Value;
  FList.Insert(Index, P);
  Changed;
end;

procedure TPixelDataList.Move(CurIndex, NewIndex: Integer);
begin
  FList.Move(CurIndex, NewIndex);
  Changed;
end;

end.

