mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 16:59:33 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			773 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			773 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{*******************************************************}
 | 
						|
{                                                       }
 | 
						|
{         Delphi VCL Extensions (RX)                    }
 | 
						|
{                                                       }
 | 
						|
{         Copyright (c) 1996 AO ROSNO                   }
 | 
						|
{         Copyright (c) 1997, 1998 Master-Bank          }
 | 
						|
{                                                       }
 | 
						|
{*******************************************************}
 | 
						|
 | 
						|
{$mode objfpc}
 | 
						|
{$H+}
 | 
						|
 | 
						|
unit StrHolder;
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses SysUtils, Classes, LResources;
 | 
						|
 | 
						|
type
 | 
						|
 | 
						|
{$ifdef usevariant}
 | 
						|
  TMacroData = Variant;
 | 
						|
{$else}
 | 
						|
  TMacroData = AnsiString;  
 | 
						|
{$endif}  
 | 
						|
 | 
						|
{ TMacro }
 | 
						|
 | 
						|
  TMacros = class;
 | 
						|
  TMacroTextEvent = procedure(Sender: TObject; Data: TMacroData; 
 | 
						|
    var Text: string) of object;
 | 
						|
  
 | 
						|
  TMacro = class(TCollectionItem)
 | 
						|
  private
 | 
						|
    FName: string;
 | 
						|
    FData: TMacroData;
 | 
						|
    FOnGetText: TMacroTextEvent;
 | 
						|
    function IsMacroStored: Boolean;
 | 
						|
    function GetText: string;
 | 
						|
    function GetMacros: TMacros;
 | 
						|
  protected
 | 
						|
    function GetDisplayName: string; override;
 | 
						|
    procedure SetDisplayName(const Value: string); override;
 | 
						|
    procedure GetMacroText(var AText: string);
 | 
						|
    function GetAsTMacroData: TMacroData;
 | 
						|
    procedure SetAsTMacroData(Value: TMacroData);
 | 
						|
  public
 | 
						|
    constructor Create(ACollection: TCollection); override;
 | 
						|
    procedure Assign(Source: TPersistent); override;
 | 
						|
    procedure Clear;
 | 
						|
    function IsEqual(Value: TMacro): Boolean;
 | 
						|
    property Macros: TMacros read GetMacros;
 | 
						|
    property Text: string read GetText;
 | 
						|
  published
 | 
						|
    property Name: string read FName write SetDisplayName;
 | 
						|
    property Value: TMacroData read GetAsTMacroData write SetAsTMacroData stored IsMacroStored;
 | 
						|
    property OnGetText: TMacroTextEvent read FOnGetText write FOnGetText;
 | 
						|
  end;
 | 
						|
 | 
						|
{ TMacros }
 | 
						|
 | 
						|
  TMacros = class(TOwnedCollection)
 | 
						|
  private
 | 
						|
    function GetMacroValue(const MacroName: string): TMacroData;
 | 
						|
    procedure SetMacroValue(const MacroName: string;
 | 
						|
      const Value: TMacroData);
 | 
						|
    function GetItem(Index: Integer): TMacro;
 | 
						|
    procedure SetItem(Index: Integer; Value: TMacro);
 | 
						|
  public
 | 
						|
    constructor Create(AOwner: TPersistent);
 | 
						|
    procedure AssignValues(Value: TMacros);
 | 
						|
    procedure AddMacro(Value: TMacro);
 | 
						|
    procedure RemoveMacro(Value: TMacro);
 | 
						|
    function CreateMacro(const MacroName: string): TMacro;
 | 
						|
    procedure GetMacroList(List: TList; const MacroNames: string);
 | 
						|
    function IndexOf(const AName: string): Integer;
 | 
						|
    function IsEqual(Value: TMacros): Boolean;
 | 
						|
    function ParseString(const Value: string; DoCreate: Boolean; 
 | 
						|
      SpecialChar: Char): string;
 | 
						|
    function MacroByName(const Value: string): TMacro;
 | 
						|
    function FindMacro(const Value: string): TMacro;
 | 
						|
    property Items[Index: Integer]: TMacro read GetItem write SetItem; default;
 | 
						|
    property MacroValues[const MacroName: string]: TMacroData read GetMacroValue write SetMacroValue;
 | 
						|
  end;
 | 
						|
 | 
						|
 | 
						|
{ TStrHolder }
 | 
						|
 | 
						|
  TStrHolder = class(TComponent)
 | 
						|
  private
 | 
						|
    FStrings: TStrings;
 | 
						|
    FXorKey: string;
 | 
						|
    FMacros: TMacros;
 | 
						|
    FMacroChar: Char;
 | 
						|
    FOnExpandMacros: TNotifyEvent;
 | 
						|
    FOnChange: TNotifyEvent;
 | 
						|
    FOnChanging: TNotifyEvent;
 | 
						|
    function GetDuplicates: TDuplicates;
 | 
						|
    procedure SetDuplicates(Value: TDuplicates);
 | 
						|
    function GetSorted: Boolean;
 | 
						|
    procedure SetSorted(Value: Boolean);
 | 
						|
    procedure SetStrings(Value: TStrings);
 | 
						|
    procedure StringsChanged(Sender: TObject);
 | 
						|
    procedure StringsChanging(Sender: TObject);
 | 
						|
    procedure ReadStrings(Reader: TReader);
 | 
						|
    procedure WriteStrings(Writer: TWriter);
 | 
						|
    function GetCommaText: string;
 | 
						|
    procedure SetCommaText(const Value: string);
 | 
						|
    function GetCapacity: Integer;
 | 
						|
    procedure SetCapacity(NewCapacity: Integer);
 | 
						|
    procedure SetMacros(Value: TMacros);
 | 
						|
    procedure RecreateMacros;
 | 
						|
    procedure SetMacroChar(Value: Char);
 | 
						|
  protected
 | 
						|
    procedure AssignTo(Dest: TPersistent); override;
 | 
						|
    procedure DefineProperties(Filer: TFiler); override;
 | 
						|
    procedure Changed; dynamic;
 | 
						|
    procedure Changing; dynamic;
 | 
						|
    procedure BeforeExpandMacros; dynamic;
 | 
						|
  public
 | 
						|
    constructor Create(AOwner: TComponent); override;
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure Assign(Source: TPersistent); override;
 | 
						|
    procedure Clear;
 | 
						|
    function MacroCount: Integer;
 | 
						|
    function MacroByName(const MacroName: string): TMacro;
 | 
						|
    function ExpandMacros: string;
 | 
						|
    property CommaText: string read GetCommaText write SetCommaText;
 | 
						|
  published
 | 
						|
    property Capacity: Integer read GetCapacity write SetCapacity default 0;
 | 
						|
    property MacroChar: Char read FMacroChar write SetMacroChar default '%';
 | 
						|
    property Macros: TMacros read FMacros write SetMacros;
 | 
						|
    property OnExpandMacros: TNotifyEvent read FOnExpandMacros write FOnExpandMacros;
 | 
						|
    property Duplicates: TDuplicates read GetDuplicates write SetDuplicates
 | 
						|
      default dupIgnore;
 | 
						|
    property KeyString: string read FXorKey write FXorKey stored False;
 | 
						|
    property Sorted: Boolean read GetSorted write SetSorted default False;
 | 
						|
    property Strings: TStrings read FStrings write SetStrings stored False;
 | 
						|
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
 | 
						|
    property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
 | 
						|
  end;
 | 
						|
 | 
						|
Procedure Register;
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
{$R strholder.res}
 | 
						|
 | 
						|
uses
 | 
						|
  RTLConsts;
 | 
						|
 | 
						|
Procedure Register;
 | 
						|
begin
 | 
						|
  RegisterComponents('Misc',[TStrHolder])
 | 
						|
end;
 | 
						|
 | 
						|
function XorEncode(const Key, Source: string): string;
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
  C: Byte;
 | 
						|
begin
 | 
						|
  Result := '';
 | 
						|
  for I := 1 to Length(Source) do begin
 | 
						|
    if Length(Key) > 0 then
 | 
						|
      C := Byte(Key[1 + ((I - 1) mod Length(Key))]) xor Byte(Source[I])
 | 
						|
    else
 | 
						|
      C := Byte(Source[I]);
 | 
						|
    Result := Result + AnsiLowerCase(IntToHex(C, 2));
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function XorDecode(const Key, Source: string): string;
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
  C: Char;
 | 
						|
 | 
						|
begin
 | 
						|
  Result := '';
 | 
						|
  for I := 0 to Length(Source) div 2 - 1 do begin
 | 
						|
    C := Chr(StrToIntDef('$' + Copy(Source, (I * 2) + 1, 2), Ord(' ')));
 | 
						|
    if Length(Key) > 0 then
 | 
						|
      C := Chr(Byte(Key[1 + (I mod Length(Key))]) xor Byte(C));
 | 
						|
    Result := Result + C;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function ExtractName(const Items: string; var Pos: Integer): string;
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
begin
 | 
						|
  I := Pos;
 | 
						|
  while (I <= Length(Items)) and (Items[I] <> ';') do Inc(I);
 | 
						|
  Result := Trim(Copy(Items, Pos, I - Pos));
 | 
						|
  if (I <= Length(Items)) and (Items[I] = ';') then Inc(I);
 | 
						|
  Pos := I;
 | 
						|
end;
 | 
						|
 | 
						|
Type
 | 
						|
  TCharSet = Set of char;
 | 
						|
 | 
						|
function NameDelimiter(C: Char; Delims: TCharSet): Boolean;
 | 
						|
begin
 | 
						|
  Result := (C in [' ', ',', ';', ')', #13, #10]) or (C in Delims);
 | 
						|
end;
 | 
						|
 | 
						|
function IsLiteral(C: Char): Boolean;
 | 
						|
begin
 | 
						|
  Result := C in ['''', '"'];
 | 
						|
end;
 | 
						|
 | 
						|
procedure CreateMacros(List: TMacros; const Value: PChar; SpecialChar: Char; Delims: TCharSet);
 | 
						|
var
 | 
						|
  CurPos, StartPos: PChar;
 | 
						|
  CurChar: Char;
 | 
						|
  Literal: Boolean;
 | 
						|
  EmbeddedLiteral: Boolean;
 | 
						|
  Name: string;
 | 
						|
 | 
						|
  function StripLiterals(Buffer: PChar): string;
 | 
						|
  var
 | 
						|
    Len: Word;
 | 
						|
    TempBuf: PChar;
 | 
						|
 | 
						|
    procedure StripChar(Value: Char);
 | 
						|
    begin
 | 
						|
      if TempBuf^ = Value then
 | 
						|
        StrMove(TempBuf, TempBuf + 1, Len - 1);
 | 
						|
      if TempBuf[StrLen(TempBuf) - 1] = Value then
 | 
						|
        TempBuf[StrLen(TempBuf) - 1] := #0;
 | 
						|
    end;
 | 
						|
 | 
						|
  begin
 | 
						|
    Len := StrLen(Buffer) + 1;
 | 
						|
    TempBuf := AllocMem(Len);
 | 
						|
    Result := '';
 | 
						|
    try
 | 
						|
      StrCopy(TempBuf, Buffer);
 | 
						|
      StripChar('''');
 | 
						|
      StripChar('"');
 | 
						|
      Result := StrPas(TempBuf);
 | 
						|
    finally
 | 
						|
      FreeMem(TempBuf, Len);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
 | 
						|
begin
 | 
						|
  if SpecialChar = #0 then Exit;
 | 
						|
  CurPos := Value;
 | 
						|
  Literal := False;
 | 
						|
  EmbeddedLiteral := False;
 | 
						|
  repeat
 | 
						|
    CurChar := CurPos^;
 | 
						|
    if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ <> SpecialChar) then
 | 
						|
    begin
 | 
						|
      StartPos := CurPos;
 | 
						|
      while (CurChar <> #0) and (Literal or not NameDelimiter(CurChar, Delims)) do begin
 | 
						|
        Inc(CurPos);
 | 
						|
        CurChar := CurPos^;
 | 
						|
        if IsLiteral(CurChar) then begin
 | 
						|
          Literal := Literal xor True;
 | 
						|
          if CurPos = StartPos + 1 then EmbeddedLiteral := True;
 | 
						|
        end;
 | 
						|
      end;
 | 
						|
      CurPos^ := #0;
 | 
						|
      if EmbeddedLiteral then begin
 | 
						|
        Name := StripLiterals(StartPos + 1);
 | 
						|
        EmbeddedLiteral := False;
 | 
						|
      end
 | 
						|
      else Name := StrPas(StartPos + 1);
 | 
						|
      if Assigned(List) then begin
 | 
						|
        if List.FindMacro(Name) = nil then
 | 
						|
          List.CreateMacro(Name);
 | 
						|
      end;
 | 
						|
      CurPos^ := CurChar;
 | 
						|
      StartPos^ := '?';
 | 
						|
      Inc(StartPos);
 | 
						|
      StrMove(StartPos, CurPos, StrLen(CurPos) + 1);
 | 
						|
      CurPos := StartPos;
 | 
						|
    end
 | 
						|
    else if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ = SpecialChar) then
 | 
						|
      StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1)
 | 
						|
    else if IsLiteral(CurChar) then Literal := Literal xor True;
 | 
						|
    Inc(CurPos);
 | 
						|
  until CurChar = #0;
 | 
						|
end;
 | 
						|
 | 
						|
{ TMacro }
 | 
						|
 | 
						|
constructor TMacro.Create(ACollection: TCollection);
 | 
						|
begin
 | 
						|
  inherited Create(ACollection);
 | 
						|
{$ifdef usevariant}
 | 
						|
  FData := Unassigned;
 | 
						|
{$else}
 | 
						|
  FData:='';
 | 
						|
{$endif}  
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacro.Assign(Source: TPersistent);
 | 
						|
begin
 | 
						|
  if (Source is TMacro) and (Source <> nil) then
 | 
						|
    begin
 | 
						|
{$ifdef usevariant}
 | 
						|
    if VarIsEmpty(TMacro(Source).FData) then
 | 
						|
      Clear
 | 
						|
    else
 | 
						|
{$endif}
 | 
						|
      Value := TMacro(Source).FData;
 | 
						|
    Name := TMacro(Source).Name;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacro.GetDisplayName: string;
 | 
						|
begin
 | 
						|
  if FName = '' then 
 | 
						|
    Result := inherited GetDisplayName 
 | 
						|
  else 
 | 
						|
    Result := FName;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacro.SetDisplayName(const Value: string);
 | 
						|
begin
 | 
						|
  if (Value <> '') and (AnsiCompareText(Value, FName) <> 0) and
 | 
						|
    (Collection is TMacros) and (TMacros(Collection).IndexOf(Value) >= 0) then
 | 
						|
    raise Exception.Create(SDuplicateString);    
 | 
						|
  FName := Value;
 | 
						|
  inherited;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacro.GetMacroText(var AText: string);
 | 
						|
begin
 | 
						|
  if Assigned(FOnGetText) then FOnGetText(Self, FData, AText);
 | 
						|
end;
 | 
						|
 | 
						|
function TMacro.GetText: string;
 | 
						|
begin
 | 
						|
  Result := FData;
 | 
						|
  GetMacroText(Result);
 | 
						|
end;
 | 
						|
 | 
						|
function TMacro.GetMacros: TMacros;
 | 
						|
begin
 | 
						|
  if Collection is TMacros then 
 | 
						|
    Result := TMacros(Collection)
 | 
						|
  else 
 | 
						|
    Result := nil;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacro.Clear;
 | 
						|
begin
 | 
						|
{$ifdef usevariant} 
 | 
						|
  FData := Unassigned;
 | 
						|
{$else}  
 | 
						|
  FData := '';
 | 
						|
{$endif}  
 | 
						|
end;
 | 
						|
 | 
						|
function TMacro.IsMacroStored: Boolean;
 | 
						|
begin
 | 
						|
{$ifdef usevariant}
 | 
						|
  Result := not VarIsEmpty(FData);
 | 
						|
{$else}
 | 
						|
  Result := (FData<>'');
 | 
						|
{$endif}
 | 
						|
end;
 | 
						|
 | 
						|
function TMacro.GetAsTMacroData: TMacroData;
 | 
						|
begin
 | 
						|
  Result := FData;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacro.SetAsTMacroData(Value: TMacroData);
 | 
						|
begin
 | 
						|
  FData := Value;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacro.IsEqual(Value: TMacro): Boolean;
 | 
						|
begin
 | 
						|
{$ifdef usevariant}
 | 
						|
  Result := (VarType(FData) = VarType(Value.FData)) and
 | 
						|
    (VarIsEmpty(FData) or (FData = Value.FData)) and
 | 
						|
    (Name = Value.Name);
 | 
						|
{$else}
 | 
						|
  Result := (FData=Value.FData) and
 | 
						|
            (Name = Value.Name);
 | 
						|
{$endif}
 | 
						|
end;
 | 
						|
 | 
						|
{ TMacros }
 | 
						|
 | 
						|
constructor TMacros.Create(AOwner: TPersistent);
 | 
						|
begin
 | 
						|
  inherited Create(AOwner, TMacro);
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.IndexOf(const AName: string): Integer;
 | 
						|
begin
 | 
						|
  for Result := 0 to Count - 1 do
 | 
						|
    if AnsiCompareText(TMacro(Items[Result]).Name, AName) = 0 then Exit;
 | 
						|
  Result := -1;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.GetItem(Index: Integer): TMacro;
 | 
						|
begin
 | 
						|
  Result := TMacro(inherited Items[Index]);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacros.SetItem(Index: Integer; Value: TMacro);
 | 
						|
begin
 | 
						|
  inherited SetItem(Index, TCollectionItem(Value));
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacros.AddMacro(Value: TMacro);
 | 
						|
begin
 | 
						|
  Value.Collection := Self;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacros.RemoveMacro(Value: TMacro);
 | 
						|
begin
 | 
						|
  if Value.Collection = Self then
 | 
						|
    Value.Collection := nil;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.CreateMacro(const MacroName: string): TMacro;
 | 
						|
begin
 | 
						|
  Result := Add as TMacro;
 | 
						|
  Result.Name := MacroName;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.IsEqual(Value: TMacros): Boolean;
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
begin
 | 
						|
  Result := Count = Value.Count;
 | 
						|
  if Result then
 | 
						|
    for I := 0 to Count - 1 do begin
 | 
						|
      Result := Items[I].IsEqual(Value.Items[I]);
 | 
						|
      if not Result then Break;
 | 
						|
    end;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.MacroByName(const Value: string): TMacro;
 | 
						|
begin
 | 
						|
  Result := FindMacro(Value);
 | 
						|
  if Result = nil then
 | 
						|
    raise Exception.Create(SInvalidPropertyValue);
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.FindMacro(const Value: string): TMacro;
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
begin
 | 
						|
  for I := 0 to Count - 1 do begin
 | 
						|
    Result := TMacro(inherited Items[I]);
 | 
						|
    if AnsiCompareText(Result.Name, Value) = 0 then Exit;
 | 
						|
  end;
 | 
						|
  Result := nil;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacros.AssignValues(Value: TMacros);
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
  P: TMacro;
 | 
						|
begin
 | 
						|
  BeginUpdate;
 | 
						|
  try
 | 
						|
    for I := 0 to Value.Count - 1 do begin
 | 
						|
      P := FindMacro(Value[I].Name);
 | 
						|
      if P <> nil then P.Assign(Value[I]);
 | 
						|
    end;
 | 
						|
  finally
 | 
						|
    EndUpdate;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.ParseString(const Value: string; DoCreate: Boolean; 
 | 
						|
  SpecialChar: Char): string;
 | 
						|
var
 | 
						|
  Macros: TMacros;
 | 
						|
begin
 | 
						|
  Result := Value;
 | 
						|
  Macros := TMacros.Create(Self.GetOwner);
 | 
						|
  try
 | 
						|
    CreateMacros(Macros, PChar(Result), SpecialChar, ['.']);
 | 
						|
    if DoCreate then begin
 | 
						|
      Macros.AssignValues(Self);
 | 
						|
      Self.Assign(Macros);
 | 
						|
    end;
 | 
						|
  finally
 | 
						|
    Macros.Free;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TMacros.GetMacroValue(const MacroName: string): TMacroData;
 | 
						|
{$ifdef usevariant}
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
  Macros: TList;
 | 
						|
{$ENDIF}
 | 
						|
begin
 | 
						|
{$ifdef usevariant}
 | 
						|
  if Pos(';', MacroName) <> 0 then
 | 
						|
    begin
 | 
						|
    Macros := TList.Create;
 | 
						|
    try
 | 
						|
      GetMacroList(Macros, MacroName);
 | 
						|
      Result := VarArrayCreate([0, Macros.Count - 1], varVariant);
 | 
						|
      for I := 0 to Macros.Count - 1 do
 | 
						|
        Result[I] := TMacro(Macros[I]).Value;
 | 
						|
    finally
 | 
						|
      Macros.Free;
 | 
						|
    end;
 | 
						|
    end
 | 
						|
  else
 | 
						|
{$else}
 | 
						|
   Result := MacroByName(MacroName).Value;
 | 
						|
{$endif}
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacros.SetMacroValue(const MacroName: string;
 | 
						|
  const Value: TMacroData);
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
  Macros: TList;
 | 
						|
begin
 | 
						|
  if Pos(';', MacroName) <> 0 then begin
 | 
						|
    Macros := TList.Create;
 | 
						|
    try
 | 
						|
      GetMacroList(Macros, MacroName);
 | 
						|
      for I := 0 to Macros.Count - 1 do
 | 
						|
        TMacro(Macros[I]).Value := Value[I];
 | 
						|
    finally
 | 
						|
      Macros.Free;
 | 
						|
    end;
 | 
						|
  end 
 | 
						|
  else MacroByName(MacroName).Value := Value;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TMacros.GetMacroList(List: TList; const MacroNames: string);
 | 
						|
var
 | 
						|
  Pos: Integer;
 | 
						|
begin
 | 
						|
  Pos := 1;
 | 
						|
  while Pos <= Length(MacroNames) do
 | 
						|
    List.Add(MacroByName(ExtractName(MacroNames, Pos)));
 | 
						|
end;
 | 
						|
 | 
						|
{ TStrHolder }
 | 
						|
 | 
						|
constructor TStrHolder.Create(AOwner: TComponent);
 | 
						|
begin
 | 
						|
  inherited Create(AOwner);
 | 
						|
  FStrings := TStringList.Create;
 | 
						|
  FMacros := TMacros.Create(Self);
 | 
						|
  FMacroChar := '%';
 | 
						|
  TStringList(FStrings).OnChange := @StringsChanged;
 | 
						|
  TStringList(FStrings).OnChanging := @StringsChanging;
 | 
						|
end;
 | 
						|
 | 
						|
destructor TStrHolder.Destroy;
 | 
						|
begin
 | 
						|
  FOnChange := nil;
 | 
						|
  FOnChanging := nil;
 | 
						|
  FMacros.Free;
 | 
						|
  FStrings.Free;
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.Assign(Source: TPersistent);
 | 
						|
begin
 | 
						|
  if Source is TStrings then
 | 
						|
    FStrings.Assign(Source)
 | 
						|
  else if Source is TStrHolder then
 | 
						|
    FStrings.Assign(TStrHolder(Source).Strings)
 | 
						|
  else
 | 
						|
    inherited Assign(Source);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.AssignTo(Dest: TPersistent);
 | 
						|
begin
 | 
						|
  if Dest is TStrings then
 | 
						|
    Dest.Assign(Strings)
 | 
						|
  else
 | 
						|
    inherited AssignTo(Dest);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.Changed;
 | 
						|
begin
 | 
						|
  if Assigned(FOnChange) then FOnChange(Self);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.Changing;
 | 
						|
begin
 | 
						|
  if Assigned(FOnChanging) then FOnChanging(Self);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.Clear;
 | 
						|
begin
 | 
						|
  FStrings.Clear;
 | 
						|
end;
 | 
						|
 | 
						|
function TStrHolder.GetCommaText: string;
 | 
						|
begin
 | 
						|
  Result := FStrings.CommaText;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.SetCommaText(const Value: string);
 | 
						|
begin
 | 
						|
  FStrings.CommaText := Value;
 | 
						|
end;
 | 
						|
 | 
						|
function TStrHolder.GetCapacity: Integer;
 | 
						|
begin
 | 
						|
  Result := FStrings.Capacity;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.SetCapacity(NewCapacity: Integer);
 | 
						|
begin
 | 
						|
  FStrings.Capacity := NewCapacity;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.BeforeExpandMacros;
 | 
						|
begin
 | 
						|
  if Assigned(FOnExpandMacros) then FOnExpandMacros(Self);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.SetMacros(Value: TMacros);
 | 
						|
begin
 | 
						|
  FMacros.AssignValues(Value);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.RecreateMacros;
 | 
						|
begin
 | 
						|
  if not (csReading in ComponentState) then
 | 
						|
    Macros.ParseString(FStrings.Text, True, MacroChar);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.SetMacroChar(Value: Char); 
 | 
						|
begin
 | 
						|
  if Value <> FMacroChar then begin
 | 
						|
    FMacroChar := Value;
 | 
						|
    RecreateMacros;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TStrHolder.MacroCount: Integer;
 | 
						|
begin
 | 
						|
  Result := Macros.Count;
 | 
						|
end;
 | 
						|
 | 
						|
function TStrHolder.MacroByName(const MacroName: string): TMacro;
 | 
						|
begin
 | 
						|
  Result := Macros.MacroByName(MacroName);
 | 
						|
end;
 | 
						|
 | 
						|
function TStrHolder.ExpandMacros: string;
 | 
						|
var
 | 
						|
  I, J, P, LiteralChars: Integer;
 | 
						|
  Macro: TMacro;
 | 
						|
  Found: Boolean;
 | 
						|
begin
 | 
						|
  BeforeExpandMacros;
 | 
						|
  Result := FStrings.Text;
 | 
						|
  for I := Macros.Count - 1 downto 0 do
 | 
						|
    begin
 | 
						|
    Macro := Macros[I];
 | 
						|
{$ifdef usevariant}
 | 
						|
    if VarIsEmpty(Macro.FData) then
 | 
						|
      Continue;
 | 
						|
{$endif}
 | 
						|
    repeat
 | 
						|
      P := Pos(MacroChar + Macro.Name, Result);
 | 
						|
      Found := (P > 0) and ((Length(Result) = P + Length(Macro.Name)) or
 | 
						|
        NameDelimiter(Result[P + Length(Macro.Name) + 1], ['.']));
 | 
						|
      if Found then begin
 | 
						|
        LiteralChars := 0;
 | 
						|
        for J := 1 to P - 1 do
 | 
						|
          if IsLiteral(Result[J]) then Inc(LiteralChars);
 | 
						|
        Found := LiteralChars mod 2 = 0;
 | 
						|
        if Found then begin
 | 
						|
          Result := Copy(Result, 1, P - 1) + Macro.Text + Copy(Result,
 | 
						|
            P + Length(Macro.Name) + 1, MaxInt);
 | 
						|
        end;
 | 
						|
      end;
 | 
						|
    until not Found;
 | 
						|
    end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.DefineProperties(Filer: TFiler);
 | 
						|
 | 
						|
  function DoWrite: Boolean;
 | 
						|
  var
 | 
						|
    I: Integer;
 | 
						|
    Ancestor: TStrHolder;
 | 
						|
  begin
 | 
						|
    Ancestor := TStrHolder(Filer.Ancestor);
 | 
						|
    Result := False;
 | 
						|
    if (Ancestor <> nil) and (Ancestor.FStrings.Count = FStrings.Count) and
 | 
						|
      (KeyString = Ancestor.KeyString) and (FStrings.Count > 0) then
 | 
						|
      for I := 0 to FStrings.Count - 1 do begin
 | 
						|
        Result := CompareText(FStrings[I], Ancestor.FStrings[I]) <> 0;
 | 
						|
        if Result then Break;
 | 
						|
      end
 | 
						|
    else Result := (FStrings.Count > 0) or (Length(KeyString) > 0);
 | 
						|
  end;
 | 
						|
 | 
						|
begin
 | 
						|
  inherited DefineProperties(Filer);
 | 
						|
  Filer.DefineProperty('StrData', @ReadStrings, @WriteStrings, DoWrite);
 | 
						|
end;
 | 
						|
 | 
						|
function TStrHolder.GetSorted: Boolean;
 | 
						|
begin
 | 
						|
  Result := TStringList(FStrings).Sorted;
 | 
						|
end;
 | 
						|
 | 
						|
function TStrHolder.GetDuplicates: TDuplicates;
 | 
						|
begin
 | 
						|
  Result := TStringList(FStrings).Duplicates;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.ReadStrings(Reader: TReader);
 | 
						|
begin
 | 
						|
  Reader.ReadListBegin;
 | 
						|
  if not Reader.EndOfList then KeyString := Reader.ReadString;
 | 
						|
  FStrings.Clear;
 | 
						|
  while not Reader.EndOfList do
 | 
						|
    FStrings.Add(XorDecode(KeyString, Reader.ReadString));
 | 
						|
  Reader.ReadListEnd;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.SetDuplicates(Value: TDuplicates);
 | 
						|
begin
 | 
						|
  TStringList(FStrings).Duplicates := Value;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.SetSorted(Value: Boolean);
 | 
						|
begin
 | 
						|
  TStringList(FStrings).Sorted := Value;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.SetStrings(Value: TStrings);
 | 
						|
begin
 | 
						|
  FStrings.Assign(Value);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.StringsChanged(Sender: TObject);
 | 
						|
begin
 | 
						|
  if Sender=nil then ;
 | 
						|
  RecreateMacros;
 | 
						|
  if not (csReading in ComponentState) then Changed;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.StringsChanging(Sender: TObject);
 | 
						|
begin
 | 
						|
  if Sender=nil then ;
 | 
						|
  if not (csReading in ComponentState) then Changing;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TStrHolder.WriteStrings(Writer: TWriter);
 | 
						|
var
 | 
						|
  I: Integer;
 | 
						|
begin
 | 
						|
  Writer.WriteListBegin;
 | 
						|
  Writer.WriteString(KeyString);
 | 
						|
  for I := 0 to FStrings.Count - 1 do
 | 
						|
    Writer.WriteString(XorEncode(KeyString, FStrings[I]));
 | 
						|
  Writer.WriteListEnd;
 | 
						|
end;
 | 
						|
 | 
						|
end.
 |