{D+}
unit EditorVariableManager;

interface

Uses
  filter,
  Extctrls,
  SysUtils,
  DateUtils,
  Classes,
  CM2PlugInTypes,
  CM2GenPlugTypes;

type
  TObjectList = class(Tobject)
  private
   fList: Array of TObject;
    function GETOBJ(Index: Integer): TObject;
    procedure SETOBJ(Index: Integer; const Value: TObject);
    function GETCOUNT: Integer;
  public
   Procedure Add(Obj: TObject);
   Procedure Delete(Obj: TObject);
   Property Obj[Index: Integer]: TObject read GETOBJ write SETOBJ; default;
   Property Count: Integer Read GETCOUNT;
  end;
  TVarMgr = class;
  TVarItem = Class(TObject)
  private
   fPolling: Boolean;
   fLastRan: String;
   fResult: TResult;
   fName: String;
   fCountDown: Integer;
   fSensorID: Integer;
   fParameters: String;
   fActive: Boolean;
   fID: String;
   fParent: TVarMgr;
   procedure SetUpdateFreq(const Value: Integer); virtual;
    function GETNode(Index: Integer): TObject;
  public
   Nodes: TObjectList;
   EventList: TOnEventList;
   SaveCount: Integer;
   Constructor Create(Owner: TVarMgr); virtual;
   Destructor Destroy; override;
   Property Active: Boolean read fActive;
   Property Polling: Boolean read fPolling;
   Property LastRan: String read fLastRan;
   Property CountDown: Integer read fCountDown write fCountDown;
   Property Result: TResult read fResult;
   Property Name: String read fName write fName;
   Property SensorID: Integer read fSensorID write fSensorID;
   Property Parameters: String read fParameters write fParameters;
   Property ID: String read fID write fID;
   procedure Poll(Sender: TObject); virtual;
   Procedure AddEvent(Event: TNotifyEvent);
   Function GetUpdateString: String; virtual;
   Property Parent: TVarMgr read fParent;
   Property Node[Index: Integer]: TObject read GETNode;
   Procedure AddNode(Node: TObject);
  end;
  PVarItem = ^TVarItem;
  TGenItem = Class(TVarItem)
  private
   Timer: TTimer;
   fPGenPlugIn: PGenPlugIn;
   fRunningDedicated: Boolean;
   fUpdateFreq: Integer;
   procedure SetUpdateFreq(const Value: Integer); override;
  public
   Constructor Create(Owner: TVarMgr); override;
   Destructor Destroy; override;
   Procedure Initialize(Node: TObject; Name: String; UpdateFreq: Integer; Const GenPlugIn: PGenPlugIn; SensorID: Integer; Parameters: String; Dedicated: Boolean = False; ID: String = '');
   Property GenPlugIn: PGenPlugIn read fPGenPlugIn;
   Procedure Poll(Sender: TObject); override;
   procedure RunDedicated;
   Property RunningDedicated: Boolean read fRunningDedicated;
   Property UpdateFreq: Integer read fUpdateFreq write SetUpdateFreq; // In Sec.
   Function GetUpdateString: String; override;
  end;
  TAdvTimedGenItem = Class(TVarItem)
  private
   fPGenPlugIn: PGenPlugIn;
    fUpdateString: String;
    procedure SetUpdateString(const Value: String);
  public
   Constructor Create(Owner: TVarMgr); override;
   Destructor Destroy; override;
   Procedure Initialize(Node: TObject; Name: String; UpdateString: String; Const GenPlugIn: PGenPlugIn; SensorID: Integer; Parameters: String; ID: String = '');
   Property GenPlugIn: PGenPlugIn read fPGenPlugIn;
   Procedure Poll(Sender: TObject); override;
   Property UpdateString: String read fUpdateString write SetUpdateString; // In Sec.
   Function GetUpdateString: String; override;
  end;
  PGenItem = ^TGenItem;
  TVarMgr = Class(TObject)
  private
   fList: Array of TVarItem;
   Timer: TTimer;
    fSaveToXML: boolean;
    fXMLFilename: String;
   function GetCount: Integer;
   function GetActive: Boolean;
   procedure SetActive(const Value: Boolean);
   Function GetIndex(Node: TObject): Integer; overload;
   Function GetIndex(SafeModeName: String): Integer; Overload;
   function GetVarItem(Index: Integer): TVarItem;
  public
   Constructor Create(CreateSuspended: Boolean = True);
   Destructor Destroy; override;
   Procedure PollAll(Sender: TObject);
   Procedure DoPollAll;
   Property Count: Integer read GetCount;
   Function AddGenP(Node: TObject; Name: String; UpdateFreq: String; Const GenPlugIn: PGenPlugIn; SensorID: Integer; Parameters: String; Dedicated: Boolean = False; ID: String = ''): PVarItem;
   Property VarItem[Index: Integer]: TVarItem read GetVarItem; default;
   Function GetP(SafeModeName: String): PVarItem;
   Procedure Delete(Node: TObject);
   Function GetRes(Name: String): TResult;
   Property Active: Boolean read GetActive write SetActive;
   Procedure Suspend;
   Procedure Resume;
   Function CheckForRun(DT: TDateTime; ExecuteStr: String): Boolean;
   Function AddViaID(ID: String; Var SafeModeName: String; Node: TObject): PVarItem;
   Property SaveToXml: boolean read fSaveToXML write fSaveToXML;
   Property XMLFilename: String read fXMLFilename write fXMLFilename;
   Procedure SaveToXMLProc;
   Procedure ResetSaveCount;
  end;
  PVarMgr = ^TVarMgr;

  function IsFileInUse(FileName: TFileName): Boolean;

implementation

Uses Windows, Engine, Main;

function IsFileInUse(FileName: TFileName): Boolean;
var
  HFileRes: HFILE;
begin
  Result := False;
  if not FileExists(FileName) then Exit;
  HFileRes := CreateFile(PChar(FileName),
                         GENERIC_READ or GENERIC_WRITE,
                         0,
                         nil,
                         OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL,
                         0);
  Result := (HFileRes = INVALID_HANDLE_VALUE);
  if not Result then
    CloseHandle(HFileRes);
end;

{ TVarItem }

procedure TVarItem.AddEvent(Event: TNotifyEvent);
begin
// If Debug Then Form4.Add('TVarItem.AddEvent [$' + IntToStr(Cardinal(@Event)) + '] ... ');
 Self.EventList.Add(Event);
// If Debug Then Form4.Append('done');
end;

procedure TVarItem.AddNode(Node: TObject);
begin
 Nodes.Add(Node);
end;

constructor TVarItem.Create;
begin
end;

destructor TVarItem.Destroy;
begin
 FreeAndNil(EventList);
 inherited;
end;

function TVarItem.GETNode(Index: Integer): TObject;
begin
 Result := Nodes[Index];
end;

function TVarItem.GetUpdateString: String;
begin
 Result := 'You need to typecast the TVarItem';
end;

procedure TVarItem.Poll(Sender: TObject);
begin
end;

procedure TVarItem.SetUpdateFreq(const Value: Integer);
begin
end;

{ TVarMgr }

Function TVarMgr.AddGenP(Node: TObject; Name: String; UpdateFreq: String;
    Const GenPlugIn: PGenPlugIn; SensorID: Integer; Parameters: String; Dedicated: Boolean = False; ID: String = ''): PVarItem;
Var
 Value, Sel: Integer;
begin
 SetLength(fList, Length(fList) + 1);
 Sel := Length(fList) - 1;
 Try
  Value := StrToInt(UpdateFreq);
 except
  Value := -1;
 end;
 If Value = -1 then begin
  fList[Sel] := TAdvTimedGenItem.Create(self);
  (fList[Sel] as TAdvTimedGenItem).Initialize(Node, Name, UpdateFreq, GenPlugIn, SensorID, Parameters, ID);
 end
  else
 begin
  fList[Sel] := TGenItem.Create(self);
  (fList[Sel] as TGenItem).Initialize(Node, Name, Value, GenPlugIn, SensorID, Parameters, Dedicated, ID);
 end;
 Result := Addr(fList[Sel]);
end;

constructor TVarMgr.Create(CreateSuspended: Boolean);
begin
 Timer := TTimer.Create(nil);
 Timer.Interval := 1000; // Update every second
 Timer.OnTimer := PollAll;
 Timer.Enabled := Not CreateSuspended;
end;

procedure TVarMgr.Delete(Node: TObject);
Var
 I: Integer;
 Sel: Integer;
begin
 Sel := GetIndex(Node);
 If Sel = -1 then exit;
 If fList[Sel].Nodes.Count > 1 then begin
  For I := 0 to fList[Sel].Nodes.Count - 1 do begin
   If fList[Sel].Nodes[I] = Node then begin
    If I = 0 then begin
     oeMain.CML.XML.Text := (fList[Sel].Nodes[0] as TEdSensorNode).GetParameterBlockNode.WriteXML('');
     oeMain.CML.Active := true;
     (fList[Sel].Nodes[1] as TEdSensorNode).GetParameterBlockNode.DeleteSelf;
     (fList[Sel].Nodes[1] as TEdSensorNode).LoadChildren(oeMain.CML.Node, (fList[Sel].Nodes[1] as TEdSensorNode));
     (fList[Sel].Nodes[1] as TEdSensorNode).PlugInGUID := (fList[Sel].Nodes[0] as TEdSensorNode).PlugInGUID;
     (fList[Sel].Nodes[1] as TEdSensorNode).SensorIndex := (fList[Sel].Nodes[0] as TEdSensorNode).SensorIndex;
     (fList[Sel].Nodes[1] as TEdSensorNode).LinkedTo := nil;
     (fList[Sel].Nodes[1] as TEdSensorNode).UpdateStr := (fList[Sel].Nodes[0] as TEdSensorNode).UpdateStr;
     (fList[Sel].Nodes[1] as TEdSensorNode).IsDedicated := (fList[Sel].Nodes[0] as TEdSensorNode).IsDedicated;
     (fList[Sel].Nodes[1] as TEdSensorNode).IDName := (fList[Sel].Nodes[0] as TEdSensorNode).IDName;
     (fList[Sel].Nodes[1] as TEdSensorNode).PlugInName := (fList[Sel].Nodes[0] as TEdSensorNode).PlugInName;
     (fList[Sel].Nodes[1] as TEdSensorNode).SensorName := (fList[Sel].Nodes[0] as TEdSensorNode).SensorName;
     fList[Sel].Nodes.Delete(Node);
     fList[Sel].EventList.Delete((Node as TEdSensorNode).OnEvent);
     oeMain.CML.Active := false;
    end
     else
    begin
     fList[Sel].EventList.Delete((Node as TedSensorNode).OnEvent);
    end;
    exit;
   end;
  end;
 end;
 FreeAndNil(fList[Sel]);
 For I := Sel to length(fList) - 2 do begin
  fList[I] := fList[I + 1];
 End;
 SetLength(fList, Length(fList) - 1);
end;

destructor TVarMgr.Destroy;
begin
 FreeAndNil(Timer);
 inherited;
end;

procedure TVarMgr.DoPollAll;
Var
 I: Integer;
begin
 For I := 0 to SElf.Count - 1 do begin
  If Assigned(VarItem[I]) then VarItem[I].Poll(self);
 end;
end;

function TVarMgr.GetActive: Boolean;
begin
 Result := false;
 If Timer = nil Then Exit;
 Result := Timer.Enabled;
end;

function TVarMgr.GetCount: Integer;
begin
 Result := Length(fList);
end;

function TVarMgr.GetIndex(Node: TObject): Integer;
Var
 II, I: Integer;
begin
 Result := -1;
 For I := 0 to Length(fList) - 1 do begin
  For II := 0 to fList[I].Nodes.Count - 1 do begin
   If Node = fList[I].Nodes[II] Then begin
    Result := I;
    Exit; // For speed optimisation, break the loop, when found
   End;
  end;
 End;
end;

function TVarMgr.GetP(SafeModeName: String): PVarItem;
Var
 Sel: Integer;
begin
 Sel := GetIndex(SafeModeName);
 If Sel = -1 then begin
  Result := nil;
  exit;
 End;
 Result := addr(fList[Sel]);
end;

function TVarMgr.GetRes(Name: String): TResult;
Var
 Sel: Integer;
begin
 Sel := GetIndex(Name);
 If Sel = -1 then begin
  Result.SensorStyle := ssUnknown;
  Result.Name := '';
  Result.Str := '';
  Result.Value := -1;
  Exit;
 End;
 Result := fList[Sel].fResult;
end;

function TVarMgr.GetVarItem(Index: Integer): TVarItem;
begin
 Result := fList[Index];
end;

procedure TVarMgr.PollAll(Sender: TObject);
Var
 I: Integer;
 DT: TDateTime;
begin
 // IF YOU ARE PLANNING ON DEBUGGING FROM THIS
 // ROUTINE, I RECOMMEND THAT YOU ACTIVATE THE
 // DEBUG SWITCH. IT WILL KEEP THE TIMER FROM
 // GETTING ACTIVATED WHEN YOU WITH IN THIS
 // ROUTINE.
 DT := Now;
 For I := 0 to Length(fList) - 1 do begin
  If (fList[I] is TGenItem) then begin
   If not(fList[I] as TGenItem).RunningDedicated then begin
    With (fList[I] as TGenItem) do begin
     CountDown := CountDown - 1;
     If CountDown < 0 then begin
      CountDown := UpdateFreq - 1;
      If Active then Poll(Self);
     End;
    end;
   end;
  end;
  If (fList[I] is TAdvTimedGenItem) then begin
   With (fList[I] as TAdvTimedGenItem) do begin
    If Self.CheckForRun(DT, UpdateString) Then Poll(Self);
   end;
  End;
 End;
end;

procedure TVarMgr.Resume;
Var
 I: Integer;
begin
 Self.Timer.Enabled := true;
 For I := 0 to Self.Count - 1 do begin
  If (VarItem[I] is TGenItem) then begin
   If (VarItem[I] as TGenItem).fRunningDedicated then begin
    (VarItem[I] as TGenItem).Timer.Enabled := true;
   end;
  end;
 end;
end;

procedure TVarMgr.SetActive(const Value: Boolean);
begin
 If Timer = nil then exit;
 Timer.Enabled := Value;
end;

procedure TVarMgr.Suspend;
Var
 I: Integer;
begin
 Self.Timer.Enabled := false;
 For I := 0 to Self.Count - 1 do begin
  If (VarItem[I] is TGenItem) then begin
   If (VarItem[I] as TGenItem).fRunningDedicated then begin
    (VarItem[I] as TGenItem).Timer.Enabled := false;
   end;
  end;
 end;
end;

Function TVarMgr.CheckForRun(DT: TDateTime; ExecuteStr: String): Boolean;
Var
 H, M, S, MSec: Word;
 Y, Mo, D: Word;
 dow: Byte;
 S1: String;
 I, II: Integer;
begin
 Result := false;
 DecodeDate(DT, Y, Mo, D);
 DecodeTime(DT, H, M, S, MSec);
 dow := DayOfTheWeek(DT);
 If uppercase(Copy(ExecuteStr,1, length('MIN'))) = 'MIN' then begin
  If uppercase(ExecuteStr) = 'MIN' then ExecuteStr := 'Min00';
  I := StrToInt(Copy(ExecuteStr, 4, length(ExecuteStr)));
  If I = S then Result := true;
  exit;
 end;
 If S <> 0 then exit; // speed optimization
 If uppercase(Copy(ExecuteStr,1, length('HOUR'))) = 'HOUR' then begin
  If uppercase(ExecuteStr) = 'HOUR' then ExecuteStr := 'Hour00';
  I := StrToInt(Copy(ExecuteStr, 5, length(ExecuteSTR)));
  If I = M then Result := true;
  exit;
 end;
 If uppercase(Copy(ExecuteStr,1, length('DAY'))) = 'DAY' then begin
  If Uppercase(ExecuteStr) = 'DAY' then ExecuteStr := 'Day00:00';
  S1 := Copy(ExecuteStr, 4, length(ExecuteStr));
  I := StrToInt(GetFilt(S1, 1, ':'));
  II := StrToInt(GetFilt(S1, 2, ':'));
  If (I = H) and (II = M) then Result := True;
  exit;
 end;
 Case dow of
  1: Begin // monday
      If uppercase(Copy(ExecuteStr, 1, length('MONDAY'))) <> 'MONDAY' then exit;
      executeStr := Copy(ExecuteStr, 7, length(executestr));
     end;
  2: Begin // Tuesday
      If uppercase(Copy(ExecuteStr, 1, length('TUESDAY'))) <> 'TUESDAY' then exit;
      executeStr := Copy(ExecuteStr, 8, length(executestr));
     end;
  3: Begin // Wednesday
      If uppercase(Copy(ExecuteStr, 1, length('WEDNESDAY'))) <> 'WEDNESDAY' then exit;
      executeStr := Copy(ExecuteStr, 10, length(executestr));
     end;
  4: Begin // Thursday
      If uppercase(Copy(ExecuteStr, 1, length('THURSDAY'))) <> 'THURSDAY' then exit;
      executeStr := Copy(ExecuteStr, 9, length(executestr));
     end;
  5: Begin // Friday
      If uppercase(Copy(ExecuteStr, 1, length('FRIDAY'))) <> 'FRIDAY' then exit;
      executeStr := Copy(ExecuteStr, 7, length(executestr));
     end;
  6: Begin // Saturday
      If uppercase(Copy(ExecuteStr, 1, length('SATURDAY'))) <> 'SATURDAY' then exit;
      executeStr := Copy(ExecuteStr, 9, length(executestr));
     end;
  7: Begin // Sunday
      If uppercase(Copy(ExecuteStr, 1, length('SUNDAY'))) <> 'SUNDAY' then exit;
      executeStr := Copy(ExecuteStr, 7, length(executestr));
     end;
 end;
 If executeStr = '' then ExecuteStr := '00:00';
 I := StrToInt(GetFilt(Executestr, 1, ':'));
 II := StrToInt(GetFilt(Executestr, 2, ':'));
 If (I = H) and (II = M) then Result := True;
end;

function TVarMgr.AddViaID(ID: String; var SafeModeName: String; Node: TObject): PVarItem;
Var
 I: Integer;
begin
 SafeModeName := '';
 Result := nil;
 For I := 0 to Self.Count - 1 do begin
  if uppercase(self.VarItem[I].ID) = uppercase(ID) then begin
   SafeModeName := VarItem[I].Name;
   Result := GetP(SafeModeName);
   Result.AddNode(Node);
  end;
 end;
end;

procedure TVarMgr.SaveToXMLProc;
Var
 List: TStrings;
 I: Integer;
 ResS, S: String;
begin
 If not SaveToXml then exit;
 List := TStringlist.Create;
 List.Add('<root>');
 For I := 0 to Length(fList) - 1 do begin
  If fList[I].ID <> '' then begin
   ResS := '';
   S := '  <' + fList[I].ID + ' sensorstyle="' + IntToStr(fList[I].Result.SensorStyle) + '">';
   If fList[I].Result.SensorStyle = ssNumeric then ResS := FloatToStr(fList[I].Result.Value)
    else ResS := fList[I].Result.Str;
   S := s + ResS + '</' + fList[I].ID + '>';
   List.Add(S);
  end;
 end;
 List.Add('</root>');
 If not IsFileInUse(XMLFilename) then begin
  // If the file is in use this will cause the entire system to cease working
  List.SaveToFile(XMLFilename);
 end;
 FreeAndNil(List);
end;

procedure TVarMgr.ResetSaveCount;
Var
 I: Integer;
begin
 For I := 0 to Count - 1 do begin
  fList[I].SaveCount := 0;
 end;
end;

function TVarMgr.GetIndex(SafeModeName: String): Integer;
Var
 I: Integer;
begin
 Result := -1;
 For I := 0 to Count - 1 do begin
  If uppercase(SafemodeName) = uppercase(fList[I].fName) then begin
   Result := I;
   Exit;
  end;
 end;
end;

{ TGenItem }

procedure TGenItem.RunDedicated;
begin
 If Self.fRunningDedicated then exit;
 Self.Timer := TTimer.Create(nil);
 Self.Timer.Interval := UpdateFreq * 1000;
 Self.Timer.OnTimer := Poll;
 Self.fRunningDedicated := True;
end;

constructor TGenItem.Create(Owner: TVarMgr);
begin
 Nodes := TObjectList.Create;
 fParent := Owner;
 // Do Nothing, all happens in the Initialize procedure
end;

destructor TGenItem.Destroy;
begin
  If fRunningDedicated then FreeAndNil(Timer);
  FreeAndNil(EventList);
  inherited;
end;

procedure TGenItem.Initialize(Node: TObject; Name: String; UpdateFreq: Integer;
  const GenPlugIn: PGenPlugIn; SensorID: Integer; Parameters: String;
  Dedicated: Boolean; ID: String);
begin
  Self.EventList := TOnEventList.Create(self);
  AddNode(Node);
  fName := Name;
  If Dedicated then Begin
   Self.Timer := TTimer.Create(nil);
   Self.Timer.Interval := UpdateFreq * 1000;
   Self.Timer.OnTimer := Poll;
  End;
  Self.fRunningDedicated := Dedicated;
  Self.fUpdateFreq := UpdateFreq;
  Self.fPGenPlugIn := GenPlugIn;
  Self.fSensorID := SensorID;
  Self.fParameters := Parameters;
  Self.fActive := true;
  Self.fResult.SensorStyle := ssNumeric;
  Self.fResult.Name := 'Default Value';
  Self.fResult.Value := -1;
  Self.fID := ID;
end;

procedure TGenItem.Poll(Sender: TObject);
begin
 If fPolling Then exit;
 If Not Active then exit;
 fPolling := True;
 Try
  Self.fPGenPlugIn^.Poll(fSensorID, fParameters);
  Self.fResult := Self.fPGenPlugIn^.Result;
 except
  fActive := False;
  Self.fResult.Value := -1;
  Self.fResult.SensorStyle := ssNumeric;
  fPolling := false;
  Exit;
 end;
 DateTimeToString(fLastRan, 'dd mmmm yyyy "@" hh:nn:ss,zzz', now);
 EventList.Trigger; // call all objects that wants to be notified when updated
 If ID <> '' then begin
  Parent.SaveToXMLProc;
 end;
 fPolling := False;
end;

procedure TGenItem.SetUpdateFreq(const Value: Integer);
begin
  inherited;
  If Value = fUpdateFreq Then exit; // Speed optimisation
  fUpdateFreq := Value;
  If fRunningDedicated Then Timer.Interval := fUpdateFreq * 1000;
end;

function TGenItem.GetUpdateString: String;
Var
 Upd: Integer;
begin
 If fRunningDedicated then begin
  Upd := Timer.Interval;
  Upd := Upd div 1000;
 end
  else
 begin
  Upd := fUpdateFreq;
 end;
 Result := IntToStr(Upd) + ' Sec';
 If Upd > 1 then Result := Result + 's';
end;

{ TAdvTimedGenItem }

constructor TAdvTimedGenItem.Create(Owner: TVarMgr);
begin
 fParent := Owner;
 Nodes := TObjectList.Create;
end;

destructor TAdvTimedGenItem.Destroy;
begin
  FreeAndNil(EventList);
  inherited;
end;

function TAdvTimedGenItem.GetUpdateString: String;
begin
 Result := fUpdateString;
end;

procedure TAdvTimedGenItem.Initialize(Node: TObject; Name, UpdateString: String;
  const GenPlugIn: PGenPlugIn; SensorID: Integer; Parameters: String; ID: String);
begin
  Self.EventList := TOnEventList.Create(self);
  AddNode(Node);
  fName := Name;
  Self.fUpdateString := UpdateString;
  Self.fPGenPlugIn := GenPlugIn;
  Self.fSensorID := SensorID;
  Self.fParameters := Parameters;
  Self.fActive := true;
  Self.fResult.SensorStyle := ssNumeric;
  Self.fResult.Name := 'Default Value';
  Self.fResult.Value := -1;
  Self.fID := ID;
end;

procedure TAdvTimedGenItem.Poll(Sender: TObject);
begin
 If fPolling Then exit;
 If Not Active then exit;
 fPolling := True;
 Try
  Self.fPGenPlugIn^.Poll(fSensorID, fParameters);
  Self.fResult := Self.fPGenPlugIn^.Result;
 except
  fActive := False;
  Self.fResult.Value := -1;
  Self.fResult.SensorStyle := ssNumeric;
  fPolling := false;
  Exit;
 end;
 DateTimeToString(fLastRan, 'dd mmmm yyyy "@" hh:nn:ss,zzz', now);
 EventList.Trigger; // call all objects that wants to be notified when updated
 If ID <> '' then begin
  Parent.SaveToXMLProc;
 end;
 fPolling := False;
end;

procedure TAdvTimedGenItem.SetUpdateString(const Value: String);
begin
  fUpdateString := Value;
end;

{ TObjectList }

procedure TObjectList.Add(Obj: TObject);
begin
 setlength(fList, Count + 1);
 fList[Count-1] := Obj;
end;

procedure TObjectList.Delete(Obj: TObject);
Var
 Sel, I: Integer;
begin
 Sel := -1;
 For I := 0 to Count - 1 do begin
  If obj = fList[I] then begin
   Sel := I;
  end;
 end;
 If Sel = -1 then exit;
 For I := sel to count - 2 do begin
  fList[I] := fList[I+1];
 end;
 SetLength(fList, count - 1);
end;

function TObjectList.GETCOUNT: Integer;
begin
 Result := length(fList);
end;

function TObjectList.GETOBJ(Index: Integer): TObject;
begin
 Result := fList[Index];
end;

procedure TObjectList.SETOBJ(Index: Integer; const Value: TObject);
begin
 fList[Index] := Value;
end;

end.
