mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 11:24:40 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			569 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			569 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
unit DbgIntfMiscClasses;
 | 
						|
 | 
						|
{$mode objfpc}{$H+}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  Classes, SysUtils, LazClasses;
 | 
						|
 | 
						|
type
 | 
						|
 | 
						|
  (* TDbgEntityValue are created with a refcount of 0 (zero)
 | 
						|
  *)
 | 
						|
 | 
						|
  TDbgEntityValuesList = class;
 | 
						|
  TDbgEntitiesThreadStackList = class;
 | 
						|
 | 
						|
  { TDbgEntityValue
 | 
						|
    Locals, Watches, Registers
 | 
						|
  }
 | 
						|
 | 
						|
  TDbgEntityValue = class(TRefCountedObject)
 | 
						|
  private
 | 
						|
    FOwner: TDbgEntityValuesList;
 | 
						|
    FFlags: set of (devImmutable);
 | 
						|
    function GetImmutable: Boolean;
 | 
						|
    function GetStackFrame: Integer;
 | 
						|
    function GetThreadId: Integer;
 | 
						|
    procedure SetImmutable(AValue: Boolean);
 | 
						|
  protected
 | 
						|
    procedure DoAssign({%H-}AnOther: TDbgEntityValue); virtual;
 | 
						|
    property Owner: TDbgEntityValuesList read FOwner;
 | 
						|
  public
 | 
						|
    procedure Assign({%H-}AnOther: TDbgEntityValue);
 | 
						|
    property ThreadId: Integer read GetThreadId;
 | 
						|
    property StackFrame: Integer read GetStackFrame;
 | 
						|
    property Immutable: Boolean read GetImmutable write SetImmutable; // mainly used by assert
 | 
						|
  end;
 | 
						|
 | 
						|
  { TDbgEntityValuesList
 | 
						|
    All Values for a specifer Thread/StackFrame
 | 
						|
  }
 | 
						|
 | 
						|
  TDbgEntityValuesList = class(TRefCountedObject)
 | 
						|
  private
 | 
						|
    FStackFrame: Integer;
 | 
						|
    FThreadId: Integer;
 | 
						|
    FFlags: set of (devlImmutable);
 | 
						|
    FList: TRefCntObjList;
 | 
						|
    FOwner: TDbgEntitiesThreadStackList;
 | 
						|
    function GetEntry(AnIndex: Integer): TDbgEntityValue;
 | 
						|
    function GetImmutable: Boolean;
 | 
						|
    procedure SetImmutable(AValue: Boolean);
 | 
						|
  protected
 | 
						|
    function  CreateEntry: TDbgEntityValue; virtual; abstract;
 | 
						|
    procedure DoAssign(AnOther: TDbgEntityValuesList); virtual;      // assert other has same thread/stack
 | 
						|
    procedure DoAssignListContent(AnOther: TDbgEntityValuesList); virtual;      // assert other has same thread/stack
 | 
						|
    procedure DoCleared; virtual;
 | 
						|
    procedure DoAdded({%H-}AnEntry: TDbgEntityValue); virtual;
 | 
						|
    procedure Init; virtual;
 | 
						|
    property Owner: TDbgEntitiesThreadStackList read FOwner;
 | 
						|
  public
 | 
						|
    constructor Create(AThreadId, AStackFrame: Integer);
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure Assign(AnOther: TDbgEntityValuesList);       // assert other has same thread/stack
 | 
						|
    procedure Add(AnEntry: TDbgEntityValue);
 | 
						|
    procedure Clear;
 | 
						|
    function Count: Integer;
 | 
						|
    property Entries[AnIndex: Integer]: TDbgEntityValue read GetEntry;
 | 
						|
 | 
						|
    property ThreadId: Integer read FThreadId;
 | 
						|
    property StackFrame: Integer read FStackFrame;
 | 
						|
    property Immutable: Boolean read GetImmutable write SetImmutable; // mainly used by assert
 | 
						|
  end;
 | 
						|
 | 
						|
  TDbgValuesThreadList = record
 | 
						|
    ThreadId: Integer;
 | 
						|
    List: TRefCntObjList;
 | 
						|
  end;
 | 
						|
 | 
						|
  { TDbgEntitiesThreadStackList }
 | 
						|
 | 
						|
  TDbgEntitiesThreadStackList = class(TRefCountedObject)
 | 
						|
  private
 | 
						|
    FList: array of TDbgValuesThreadList;
 | 
						|
    FFlags: set of (devtsImmutable);
 | 
						|
    function GetEntry(AThreadId, AStackFrame: Integer): TDbgEntityValuesList;
 | 
						|
    function GetEntryByIdx(AnIndex: Integer): TDbgEntityValuesList;
 | 
						|
    function GetHasEntry(AThreadId, AStackFrame: Integer): Boolean;
 | 
						|
    function GetImmutable: Boolean;
 | 
						|
    function IndexOfThread(AThreadId: Integer; ACreateSubList: Boolean = False): Integer;
 | 
						|
    procedure SetImmutable(AValue: Boolean);
 | 
						|
  protected
 | 
						|
    function  CreateEntry(AThreadId, AStackFrame: Integer): TDbgEntityValuesList; virtual; abstract;
 | 
						|
    procedure DoAssign(AnOther: TDbgEntitiesThreadStackList); virtual;
 | 
						|
    procedure DoAssignListContent(AnOther: TDbgEntitiesThreadStackList); virtual;
 | 
						|
    procedure DoCleared; virtual;
 | 
						|
    procedure DoAdded({%H-}AnEntry: TDbgEntityValuesList); virtual;
 | 
						|
  public
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure Assign(AnOther: TDbgEntitiesThreadStackList);
 | 
						|
    procedure Add(AnEntry: TDbgEntityValuesList);
 | 
						|
    procedure Clear;
 | 
						|
    function Count: Integer;
 | 
						|
    property EntriesByIdx[AnIndex: Integer]: TDbgEntityValuesList read GetEntryByIdx;
 | 
						|
    // Entries will automatically be created
 | 
						|
    property Entries[AThreadId, AStackFrame: Integer]: TDbgEntityValuesList read GetEntry; default;
 | 
						|
    property HasEntry[AThreadId, AStackFrame: Integer]: Boolean read GetHasEntry;
 | 
						|
    property Immutable: Boolean read GetImmutable write SetImmutable; // used by assert
 | 
						|
  end;
 | 
						|
 | 
						|
  { TDelayedUdateItem }
 | 
						|
 | 
						|
  TDelayedUdateItem = class(TCollectionItem)
 | 
						|
  private
 | 
						|
    FUpdateCount: Integer;
 | 
						|
    FDoChanged: Boolean;
 | 
						|
  protected
 | 
						|
    procedure Changed;
 | 
						|
    procedure DoChanged; virtual;
 | 
						|
    procedure DoEndUpdate; virtual; // even if not changed
 | 
						|
  public
 | 
						|
    procedure Assign(ASource: TPersistent); override;
 | 
						|
    procedure BeginUpdate;
 | 
						|
    constructor Create(ACollection: TCollection); override;
 | 
						|
    procedure EndUpdate;
 | 
						|
    function IsUpdating: Boolean;
 | 
						|
  end;
 | 
						|
 | 
						|
  { TRefCountedColectionItem }
 | 
						|
 | 
						|
  TRefCountedColectionItem = class(TDelayedUdateItem)
 | 
						|
  public
 | 
						|
    constructor Create(ACollection: TCollection); override;
 | 
						|
    destructor  Destroy; override;
 | 
						|
    procedure AddReference;
 | 
						|
    procedure ReleaseReference;
 | 
						|
  private
 | 
						|
    FRefCount: Integer;
 | 
						|
  protected
 | 
						|
    procedure DoFree; virtual;
 | 
						|
    property  RefCount: Integer read FRefCount;
 | 
						|
  end;
 | 
						|
 | 
						|
procedure ReleaseRefAndNil(var ARefCountedObject);
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
procedure ReleaseRefAndNil(var ARefCountedObject);
 | 
						|
begin
 | 
						|
  Assert( (Pointer(ARefCountedObject) = nil) or
 | 
						|
          (TObject(ARefCountedObject) is TRefCountedObject) or
 | 
						|
          (TObject(ARefCountedObject) is TRefCountedColectionItem),
 | 
						|
         'ReleaseRefAndNil requires TRefCountedObject');
 | 
						|
 | 
						|
  if Pointer(ARefCountedObject) = nil then
 | 
						|
    exit;
 | 
						|
 | 
						|
  if (TObject(ARefCountedObject) is TRefCountedObject) then
 | 
						|
    TRefCountedObject(ARefCountedObject).ReleaseReference
 | 
						|
  else
 | 
						|
  if (TObject(ARefCountedObject) is TRefCountedColectionItem) then
 | 
						|
    TRefCountedColectionItem(ARefCountedObject).ReleaseReference;
 | 
						|
 | 
						|
  Pointer(ARefCountedObject) := nil;
 | 
						|
end;
 | 
						|
 | 
						|
{ TDbgEntityValue }
 | 
						|
 | 
						|
function TDbgEntityValue.GetImmutable: Boolean;
 | 
						|
begin
 | 
						|
  Result := (devImmutable in FFlags) or ((FOwner <> nil) and FOwner.Immutable);
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntityValue.GetStackFrame: Integer;
 | 
						|
begin
 | 
						|
  Result := FOwner.StackFrame;
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntityValue.GetThreadId: Integer;
 | 
						|
begin
 | 
						|
  Result := FOwner.ThreadId;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValue.SetImmutable(AValue: Boolean);
 | 
						|
begin
 | 
						|
  assert((AValue = True) or not(Immutable), 'TDbgEntityValue.SetImmutable Not allowed to set to false');
 | 
						|
  if AValue then Include(FFlags, devImmutable);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValue.DoAssign(AnOther: TDbgEntityValue);
 | 
						|
begin
 | 
						|
  //
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValue.Assign(AnOther: TDbgEntityValue);
 | 
						|
begin
 | 
						|
  Assert(not Immutable, 'TDbgEntityValue.Assign Immutable');
 | 
						|
  DoAssign(AnOther);
 | 
						|
end;
 | 
						|
 | 
						|
{ TDbgEntityValuesList }
 | 
						|
 | 
						|
function TDbgEntityValuesList.GetImmutable: Boolean;
 | 
						|
begin
 | 
						|
  Result := devlImmutable in FFlags;
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntityValuesList.GetEntry(AnIndex: Integer): TDbgEntityValue;
 | 
						|
begin
 | 
						|
  Result := TDbgEntityValue(FList[AnIndex]);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.SetImmutable(AValue: Boolean);
 | 
						|
begin
 | 
						|
  assert((AValue = True) or not(devlImmutable in FFlags), 'TDbgEntityValuesList.SetImmutable Not allowed to set to false');
 | 
						|
  if AValue then Include(FFlags, devlImmutable);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.DoCleared;
 | 
						|
begin
 | 
						|
  //
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.DoAdded(AnEntry: TDbgEntityValue);
 | 
						|
begin
 | 
						|
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.Init;
 | 
						|
begin
 | 
						|
  //
 | 
						|
end;
 | 
						|
 | 
						|
constructor TDbgEntityValuesList.Create(AThreadId, AStackFrame: Integer);
 | 
						|
begin
 | 
						|
  inherited Create;
 | 
						|
  FFlags := [];
 | 
						|
  FThreadId   := AThreadId;
 | 
						|
  FStackFrame := AStackFrame;
 | 
						|
  FList := TRefCntObjList.Create;
 | 
						|
  Init;
 | 
						|
end;
 | 
						|
 | 
						|
destructor TDbgEntityValuesList.Destroy;
 | 
						|
begin
 | 
						|
  Exclude(FFlags, devlImmutable);
 | 
						|
  Clear;
 | 
						|
  FList.Free;
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.DoAssign(AnOther: TDbgEntityValuesList);
 | 
						|
begin
 | 
						|
  DoAssignListContent(AnOther);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.DoAssignListContent(AnOther: TDbgEntityValuesList);
 | 
						|
var
 | 
						|
  e: TDbgEntityValue;
 | 
						|
  i: Integer;
 | 
						|
begin
 | 
						|
  for i := 0 to AnOther.FList.Count - 1 do begin
 | 
						|
    e := CreateEntry;
 | 
						|
    e.FOwner := Self;
 | 
						|
    e.Assign(TDbgEntityValue(AnOther.FList[i]));
 | 
						|
    FList.Add(e);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.Assign(AnOther: TDbgEntityValuesList);
 | 
						|
begin
 | 
						|
  Assert(not Immutable, 'TDbgEntityValuesList.Assign Immutable');
 | 
						|
  Assert((FThreadId = AnOther.FThreadId) and (FStackFrame = AnOther.FStackFrame), 'TDbgEntityValuesList.Assign same thread and stack');
 | 
						|
 | 
						|
  Clear;
 | 
						|
  DoAssign(AnOther);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.Add(AnEntry: TDbgEntityValue);
 | 
						|
begin
 | 
						|
  Assert(not Immutable, 'TDbgEntityValuesList.Add  Immutable');
 | 
						|
  AnEntry.FOwner := Self;
 | 
						|
  FList.Add(AnEntry);
 | 
						|
  DoAdded(AnEntry);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntityValuesList.Clear;
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
begin
 | 
						|
  Assert(not Immutable, 'TDbgEntityValuesList.Clear Immutable');
 | 
						|
  if FList.Count = 0 then
 | 
						|
    exit;
 | 
						|
  for i := 0 to FList.Count - 1 do
 | 
						|
    TDbgEntityValue(FList[i]).FOwner := nil;
 | 
						|
  FList.Clear;
 | 
						|
  DoCleared;
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntityValuesList.Count: Integer;
 | 
						|
begin
 | 
						|
  Result := FList.Count;
 | 
						|
end;
 | 
						|
 | 
						|
{ TDbgEntitiesThreadStackList }
 | 
						|
 | 
						|
function TDbgEntitiesThreadStackList.GetEntry(AThreadId, AStackFrame: Integer): TDbgEntityValuesList;
 | 
						|
var
 | 
						|
  i, j: Integer;
 | 
						|
begin
 | 
						|
  i := IndexOfThread(AThreadId);
 | 
						|
  if i >= 0 then begin
 | 
						|
    // TODO: binary search / need sorted list
 | 
						|
    for j := 0 to FList[i].List.Count - 1 do begin
 | 
						|
      Result := TDbgEntityValuesList(FList[i].List[j]);
 | 
						|
      if Result.StackFrame = AStackFrame then
 | 
						|
        exit;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
 | 
						|
  if Immutable then begin
 | 
						|
    Result := nil;
 | 
						|
    exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  Result := CreateEntry(AThreadId, AStackFrame);
 | 
						|
  Add(Result);
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntitiesThreadStackList.GetEntryByIdx(AnIndex: Integer): TDbgEntityValuesList;
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
begin
 | 
						|
  Result := nil;
 | 
						|
  i := 0;
 | 
						|
  while AnIndex >= FList[i].List.Count do begin
 | 
						|
    dec(AnIndex, FList[i].List.Count);
 | 
						|
    inc(i);
 | 
						|
    if i >= Length(FList) then
 | 
						|
      exit;
 | 
						|
  end;
 | 
						|
  Result := TDbgEntityValuesList(FList[i].List[AnIndex]);
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntitiesThreadStackList.GetHasEntry(AThreadId, AStackFrame: Integer): Boolean;
 | 
						|
var
 | 
						|
  i, j: Integer;
 | 
						|
begin
 | 
						|
  Result := False;
 | 
						|
  i := IndexOfThread(AThreadId);
 | 
						|
  if i < 0 then exit;
 | 
						|
  // TODO: binary search / need sorted list
 | 
						|
  for j := 0 to FList[i].List.Count - 1 do begin
 | 
						|
    if TDbgEntityValuesList(FList[i].List[j]).StackFrame = AStackFrame then begin
 | 
						|
      Result := True;
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntitiesThreadStackList.GetImmutable: Boolean;
 | 
						|
begin
 | 
						|
  Result := devtsImmutable in FFlags;
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntitiesThreadStackList.IndexOfThread(AThreadId: Integer;
 | 
						|
  ACreateSubList: Boolean): Integer;
 | 
						|
begin
 | 
						|
  Result := length(FList) - 1;
 | 
						|
  while (Result >= 0) and (FList[Result].ThreadId <> AThreadId) do
 | 
						|
    dec(Result);
 | 
						|
  if (Result >= 0) or (not ACreateSubList) then
 | 
						|
    exit;
 | 
						|
 | 
						|
  Result := length(FList);
 | 
						|
  SetLength(FList, Result + 1);
 | 
						|
  FList[Result].ThreadId := AThreadId;
 | 
						|
  FList[Result].List := TRefCntObjList.Create;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.SetImmutable(AValue: Boolean);
 | 
						|
begin
 | 
						|
  assert((AValue = True) or not(devtsImmutable in FFlags), 'TDbgEntityValuesList.SetImmutable Not allowed to set to false');
 | 
						|
  if AValue then Include(FFlags, devtsImmutable);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.DoCleared;
 | 
						|
begin
 | 
						|
  //
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.DoAdded(AnEntry: TDbgEntityValuesList);
 | 
						|
begin
 | 
						|
  //
 | 
						|
end;
 | 
						|
 | 
						|
destructor TDbgEntitiesThreadStackList.Destroy;
 | 
						|
begin
 | 
						|
  Exclude(FFlags, devtsImmutable);
 | 
						|
  Clear;
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.DoAssign(AnOther: TDbgEntitiesThreadStackList);
 | 
						|
begin
 | 
						|
  DoAssignListContent(AnOther);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.DoAssignListContent(AnOther: TDbgEntitiesThreadStackList);
 | 
						|
var
 | 
						|
  i, j: Integer;
 | 
						|
  t: Integer;
 | 
						|
  e, o: TDbgEntityValuesList;
 | 
						|
begin
 | 
						|
  SetLength(FList, length(AnOther.FList));
 | 
						|
  for i := 0 to Length(FList) - 1 do begin
 | 
						|
    t := AnOther.FList[i].ThreadId;
 | 
						|
    FList[i].ThreadId := t;
 | 
						|
    FList[i].List := TRefCntObjList.Create;
 | 
						|
    for j := 0 to AnOther.FList[i].List.Count - 1 do begin
 | 
						|
      o := TDbgEntityValuesList(AnOther.FList[i].List[j]);
 | 
						|
      e := CreateEntry(t, o.StackFrame);
 | 
						|
      e.FOwner := Self;
 | 
						|
      e.Assign(o);
 | 
						|
      FList[i].List.Add(e);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.Assign(AnOther: TDbgEntitiesThreadStackList);
 | 
						|
begin
 | 
						|
  Assert(not Immutable, 'TDbgEntitiesThreadStackList.Assign Immutable');
 | 
						|
  Clear;
 | 
						|
  DoAssign(AnOther);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.Add(AnEntry: TDbgEntityValuesList);
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
begin
 | 
						|
  Assert(not Immutable, 'TDbgEntitiesThreadStackList.Add Immutable');
 | 
						|
  Assert((AnEntry.FOwner = nil) or (AnEntry.FOwner = Self), 'TDbgEntitiesThreadStackList.Add Entry.FThreadStackList');
 | 
						|
  AnEntry.FOwner := Self;
 | 
						|
  i := IndexOfThread(AnEntry.ThreadId, True);
 | 
						|
  FList[i].List.Add(AnEntry);
 | 
						|
  DoAdded(AnEntry);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDbgEntitiesThreadStackList.Clear;
 | 
						|
var
 | 
						|
  i, j: Integer;
 | 
						|
begin
 | 
						|
  Assert(not Immutable, 'TDbgEntitiesThreadStackList.Clear Immutable');
 | 
						|
  if Length(FList) = 0 then
 | 
						|
    exit;
 | 
						|
  for i := 0 to Length(FList) - 1 do begin
 | 
						|
    for j := 0 to FList[i].List.Count - 1 do
 | 
						|
      TDbgEntityValuesList(FList[i].List[j]).FOwner := nil;
 | 
						|
    FList[i].List.Free;
 | 
						|
  end;
 | 
						|
  SetLength(FList, 0);
 | 
						|
  DoCleared;
 | 
						|
end;
 | 
						|
 | 
						|
function TDbgEntitiesThreadStackList.Count: Integer;
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
begin
 | 
						|
  Result := 0;
 | 
						|
  for i := 0 to Length(FList) - 1 do
 | 
						|
    Result := Result + FList[i].List.Count;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{ TDelayedUdateItem }
 | 
						|
 | 
						|
procedure TDelayedUdateItem.Assign(ASource: TPersistent);
 | 
						|
begin
 | 
						|
  BeginUpdate;
 | 
						|
  try
 | 
						|
    inherited Assign(ASource);
 | 
						|
  finally
 | 
						|
    EndUpdate;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDelayedUdateItem.BeginUpdate;
 | 
						|
begin
 | 
						|
  Inc(FUpdateCount);
 | 
						|
  if FUpdateCount = 1 then FDoChanged := False;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDelayedUdateItem.Changed;
 | 
						|
begin
 | 
						|
  if FUpdateCount > 0
 | 
						|
  then FDoChanged := True
 | 
						|
  else DoChanged;
 | 
						|
end;
 | 
						|
 | 
						|
constructor TDelayedUdateItem.Create(ACollection: TCollection);
 | 
						|
begin
 | 
						|
  inherited Create(ACollection);
 | 
						|
  FUpdateCount := 0;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDelayedUdateItem.DoChanged;
 | 
						|
begin
 | 
						|
  inherited Changed(False);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDelayedUdateItem.DoEndUpdate;
 | 
						|
begin
 | 
						|
  //
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDelayedUdateItem.EndUpdate;
 | 
						|
begin
 | 
						|
  Dec(FUpdateCount);
 | 
						|
  if FUpdateCount < 0 then raise EInvalidOperation.Create('TDelayedUdateItem.EndUpdate');
 | 
						|
  if (FUpdateCount = 0)
 | 
						|
  then DoEndUpdate;
 | 
						|
  if (FUpdateCount = 0) and FDoChanged
 | 
						|
  then begin
 | 
						|
    DoChanged;
 | 
						|
    FDoChanged := False;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TDelayedUdateItem.IsUpdating: Boolean;
 | 
						|
begin
 | 
						|
  Result := FUpdateCount > 0;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{ TRefCountedColectionItem }
 | 
						|
 | 
						|
constructor TRefCountedColectionItem.Create(ACollection: TCollection);
 | 
						|
begin
 | 
						|
  FRefCount := 0;
 | 
						|
  inherited Create(ACollection);
 | 
						|
end;
 | 
						|
 | 
						|
destructor TRefCountedColectionItem.Destroy;
 | 
						|
begin
 | 
						|
  Assert(FRefcount = 0, 'Destroying referenced object');
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TRefCountedColectionItem.AddReference;
 | 
						|
begin
 | 
						|
  Inc(FRefcount);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TRefCountedColectionItem.ReleaseReference;
 | 
						|
begin
 | 
						|
  Assert(FRefCount > 0, 'TRefCountedObject.ReleaseReference  RefCount > 0');
 | 
						|
  Dec(FRefCount);
 | 
						|
  if FRefCount = 0 then DoFree;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TRefCountedColectionItem.DoFree;
 | 
						|
begin
 | 
						|
  Self.Free;
 | 
						|
end;
 | 
						|
 | 
						|
end.
 | 
						|
 |