2018-11-06 17:59:59 +00:00
|
|
|
{-----------------------------------------------------------------------------
|
|
|
|
The contents of this file are subject to the Mozilla Public License
|
|
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
|
|
with the License. You may obtain a copy of the License at
|
|
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
|
|
|
|
The Original Code is: JvStrHlder.PAS, released on 2002-07-04.
|
|
|
|
|
|
|
|
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
|
|
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
|
|
Copyright (c) 2001,2002 SGB Software
|
|
|
|
Portions created by Marc Geldon are Copyright (C) 2004 Marc Geldon.
|
|
|
|
All Rights Reserved.
|
|
|
|
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
|
|
located at http://jvcl.delphi-jedi.org
|
|
|
|
|
|
|
|
Known Issues:
|
|
|
|
-----------------------------------------------------------------------------}
|
|
|
|
// $Id$
|
|
|
|
|
|
|
|
unit JvStringHolder;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
Variants, RTLConsts, SysUtils, Classes;
|
|
|
|
|
|
|
|
type
|
|
|
|
TJvMacros = class;
|
|
|
|
TMacroTextEvent = procedure(Sender: TObject; Data: Variant; var Text: string) of object;
|
|
|
|
|
|
|
|
TJvMacro = class(TCollectionItem)
|
|
|
|
private
|
|
|
|
FName: string;
|
|
|
|
FData: Variant;
|
|
|
|
FOnGetText: TMacroTextEvent;
|
|
|
|
function IsMacroStored: Boolean;
|
|
|
|
function GetText: string;
|
|
|
|
function GetMacros: TJvMacros;
|
|
|
|
protected
|
|
|
|
function GetDisplayName: string; override;
|
|
|
|
procedure SetDisplayName(const Value: string); override;
|
|
|
|
procedure GetMacroText(var AText: string);
|
|
|
|
function GetAsVariant: Variant;
|
|
|
|
procedure SetAsVariant(Value: Variant);
|
|
|
|
public
|
|
|
|
constructor Create(ACollection: TCollection); override;
|
|
|
|
procedure Assign(Source: TPersistent); override;
|
|
|
|
procedure Clear;
|
|
|
|
function IsEqual(Value: TJvMacro): Boolean;
|
|
|
|
property Macros: TJvMacros read GetMacros;
|
|
|
|
property Text: string read GetText;
|
|
|
|
published
|
|
|
|
property Name: string read FName write SetDisplayName;
|
|
|
|
property Value: Variant read GetAsVariant write SetAsVariant stored IsMacroStored;
|
|
|
|
property OnGetText: TMacroTextEvent read FOnGetText write FOnGetText;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TJvMacros = class(TOwnedCollection)
|
|
|
|
private
|
|
|
|
function GetMacroValue(const MacroName: string): Variant;
|
|
|
|
procedure SetMacroValue(const MacroName: string; const Value: Variant);
|
|
|
|
function GetItem(Index: Integer): TJvMacro;
|
|
|
|
procedure SetItem(Index: Integer; Value: TJvMacro);
|
|
|
|
public
|
|
|
|
constructor Create(AOwner: TPersistent);
|
|
|
|
procedure AssignValues(Value: TJvMacros);
|
|
|
|
procedure AddMacro(Value: TJvMacro);
|
|
|
|
procedure RemoveMacro(Value: TJvMacro);
|
|
|
|
function CreateMacro(const MacroName: string): TJvMacro;
|
|
|
|
procedure GetMacroList(List: TList; const MacroNames: string);
|
|
|
|
function IndexOf(const AName: string): Integer;
|
|
|
|
function IsEqual(Value: TJvMacros): Boolean;
|
|
|
|
function ParseString(const Value: string; DoCreate: Boolean; SpecialChar: Char): string;
|
|
|
|
function MacroByName(const Value: string): TJvMacro;
|
|
|
|
function FindMacro(const Value: string): TJvMacro;
|
|
|
|
property Items[Index: Integer]: TJvMacro read GetItem write SetItem; default;
|
|
|
|
property MacroValues[const MacroName: string]: Variant read GetMacroValue write SetMacroValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TJvStrHolder = class(TComponent)
|
|
|
|
private
|
|
|
|
FStrings: TStringList;
|
|
|
|
FXorKey: string;
|
|
|
|
FReserved: Integer;
|
|
|
|
FMacros: TJvMacros;
|
|
|
|
FMacroChar: Char;
|
|
|
|
FOnExpandMacros: TNotifyEvent;
|
|
|
|
FOnChange: TNotifyEvent;
|
|
|
|
FOnChanging: TNotifyEvent;
|
|
|
|
function GetDuplicates: TDuplicates;
|
|
|
|
procedure SetDuplicates(Value: TDuplicates);
|
|
|
|
function GetSorted: Boolean;
|
|
|
|
procedure SetSorted(Value: Boolean);
|
|
|
|
function GetStrings: TStrings;
|
|
|
|
procedure SetStrings(Value: TStrings);
|
|
|
|
procedure StringsChanged(Sender: TObject);
|
|
|
|
procedure StringsChanging(Sender: TObject);
|
|
|
|
procedure ReadStrings(Reader: TReader);
|
|
|
|
procedure WriteStrings(Writer: TWriter);
|
|
|
|
procedure ReadVersion(Reader: TReader);
|
|
|
|
procedure WriteVersion(Writer: TWriter);
|
|
|
|
function GetCommaText: string;
|
|
|
|
procedure SetCommaText(const Value: string);
|
|
|
|
function GetCapacity: Integer;
|
|
|
|
procedure SetCapacity(NewCapacity: Integer);
|
|
|
|
procedure SetMacros(Value: TJvMacros);
|
|
|
|
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;
|
2023-04-20 13:23:34 +00:00
|
|
|
{ Clears the string list. }
|
2018-11-06 17:59:59 +00:00
|
|
|
procedure Clear;
|
|
|
|
function MacroCount: Integer;
|
|
|
|
function MacroByName(const MacroName: string): TJvMacro;
|
|
|
|
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: TJvMacros read FMacros write SetMacros;
|
|
|
|
property OnExpandMacros: TNotifyEvent read FOnExpandMacros write FOnExpandMacros;
|
|
|
|
property Duplicates: TDuplicates read GetDuplicates write SetDuplicates default dupIgnore;
|
2023-04-20 13:23:34 +00:00
|
|
|
{ String for encoding the contents of string holder.
|
|
|
|
Empty --> no encryption done.
|
|
|
|
Not empty ..> Encoding is performed, and the encrypted contents will be stored in the lfm file.
|
|
|
|
Encryption is a simple XOR-alghoritm with KeyString as the key to decode. }
|
2018-11-06 17:59:59 +00:00
|
|
|
property KeyString: string read FXorKey write FXorKey stored False;
|
2023-04-20 13:23:34 +00:00
|
|
|
{ Ensures the string list is sorted when any changes occur. }
|
2018-11-06 17:59:59 +00:00
|
|
|
property Sorted: Boolean read GetSorted write SetSorted default False;
|
2023-04-20 13:23:34 +00:00
|
|
|
{ Main container to store the strings. }
|
2018-11-06 17:59:59 +00:00
|
|
|
property Strings: TStrings read GetStrings write SetStrings stored False;
|
2023-04-20 13:23:34 +00:00
|
|
|
{ Triggered after any changes have been made to the list. }
|
2018-11-06 17:59:59 +00:00
|
|
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
2023-04-20 13:23:34 +00:00
|
|
|
{ Trigged before changes are made to the list. }
|
2018-11-06 17:59:59 +00:00
|
|
|
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ MultiStringHolder }
|
|
|
|
|
|
|
|
EJvMultiStringHolderException = class(Exception);
|
|
|
|
|
|
|
|
TJvMultiStringHolderCollectionItem = class(TCollectionItem)
|
|
|
|
private
|
|
|
|
FName: string;
|
|
|
|
FStrings: TStrings;
|
|
|
|
procedure SetName(Value: string);
|
|
|
|
procedure SetStrings(const Value: TStrings);
|
|
|
|
protected
|
|
|
|
function GetDisplayName: string; override;
|
|
|
|
public
|
|
|
|
constructor Create(ACollection: TCollection); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
published
|
|
|
|
property Name: string read FName write SetName;
|
|
|
|
property Strings: TStrings read FStrings write SetStrings;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TJvMultiStringHolderCollection = class(TOwnedCollection)
|
|
|
|
protected
|
|
|
|
function GetItem(Index: Integer): TJvMultiStringHolderCollectionItem;
|
|
|
|
procedure SetItem(Index: Integer; Value: TJvMultiStringHolderCollectionItem);
|
|
|
|
public
|
|
|
|
function DoesNameExist(const Name: string): Boolean;
|
|
|
|
property Items[Index: Integer]: TJvMultiStringHolderCollectionItem read GetItem write SetItem;
|
|
|
|
function Add: TJvMultiStringHolderCollectionItem;
|
|
|
|
function Insert(Index: Integer): TJvMultiStringHolderCollectionItem;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TJvMultiStringHolder = class(TComponent)
|
|
|
|
private
|
|
|
|
FMultipleStrings: TJvMultiStringHolderCollection;
|
|
|
|
procedure SetMultipleStrings(Value: TJvMultiStringHolderCollection);
|
|
|
|
function GetItemByName(const AName: string): TJvMultiStringHolderCollectionItem;
|
|
|
|
function GetStringsByName(const AName: string): TStrings;
|
|
|
|
public
|
|
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
property ItemByName[const AName: string]: TJvMultiStringHolderCollectionItem read GetItemByName;
|
|
|
|
property StringsByName[const AName: string]: TStrings read GetStringsByName;
|
|
|
|
published
|
|
|
|
property MultipleStrings: TJvMultiStringHolderCollection read FMultipleStrings write SetMultipleStrings;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
//Consts,
|
|
|
|
(*
|
|
|
|
{$IFDEF SUPPORTS_INLINE}
|
|
|
|
Windows,
|
|
|
|
{$ENDIF SUPPORTS_INLINE}
|
|
|
|
*)
|
|
|
|
JvJCLUtils, JvResources, JvConsts, JvTypes;
|
|
|
|
|
|
|
|
const
|
|
|
|
AnsiXorVersion = 1;
|
|
|
|
XorVersion = 2;
|
|
|
|
|
|
|
|
type
|
|
|
|
TCharSet = TSysCharSet;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
function NameDelimiter(C: Char; Delims: TCharSet): Boolean;
|
|
|
|
begin
|
|
|
|
Result := CharInSet(C, [' ', ',', ';', ')', Cr, Lf]) or CharInSet(C, Delims);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function IsLiteral(C: Char): Boolean;
|
|
|
|
begin
|
|
|
|
case C of
|
|
|
|
'''', '"':
|
|
|
|
Result := True;
|
|
|
|
else
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure CreateMacros(List: TJvMacros; 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
|
|
|
|
BufLen: Integer;
|
|
|
|
TempBuf: PChar;
|
|
|
|
|
|
|
|
procedure StripChar(Value: Char);
|
|
|
|
var
|
|
|
|
Len: Integer;
|
|
|
|
begin
|
|
|
|
if TempBuf^ = Value then
|
|
|
|
StrMove(TempBuf, TempBuf + 1, BufLen - 1);
|
|
|
|
Len := StrLen(TempBuf);
|
|
|
|
if TempBuf[Len - 1] = Value then
|
|
|
|
TempBuf[Len - 1] := #0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
begin
|
|
|
|
TempBuf := StrNew(Buffer);
|
|
|
|
BufLen := StrLen(TempBuf) + 1;
|
|
|
|
Result := '';
|
|
|
|
try
|
|
|
|
StripChar('''');
|
|
|
|
StripChar('"');
|
|
|
|
Result := StrPas(TempBuf);
|
|
|
|
finally
|
|
|
|
StrDispose(TempBuf);
|
|
|
|
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 := not Literal;
|
|
|
|
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
|
|
|
|
if List.FindMacro(Name) = nil then
|
|
|
|
List.CreateMacro(Name);
|
|
|
|
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 := not Literal;
|
|
|
|
Inc(CurPos);
|
|
|
|
until CurChar = #0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
//=== { TJvMacro } ===========================================================
|
|
|
|
|
|
|
|
constructor TJvMacro.Create(ACollection: TCollection);
|
|
|
|
begin
|
|
|
|
inherited Create(ACollection);
|
|
|
|
FData := Unassigned;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacro.Assign(Source: TPersistent);
|
|
|
|
begin
|
|
|
|
if Source is TJvMacro then
|
|
|
|
begin
|
|
|
|
if VarIsEmpty(TJvMacro(Source).FData) then
|
|
|
|
Clear
|
|
|
|
else
|
|
|
|
Value := TJvMacro(Source).FData;
|
|
|
|
Name := TJvMacro(Source).Name;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
inherited Assign(Source);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacro.GetDisplayName: string;
|
|
|
|
begin
|
|
|
|
if FName = '' then
|
|
|
|
Result := inherited GetDisplayName
|
|
|
|
else
|
|
|
|
Result := FName;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacro.SetDisplayName(const Value: string);
|
|
|
|
begin
|
|
|
|
if (Value <> '') and (AnsiCompareText(Value, FName) <> 0) and
|
|
|
|
(Collection is TJvMacros) and (TJvMacros(Collection).IndexOf(Value) >= 0) then
|
|
|
|
raise EJVCLException.CreateRes(@SDuplicateString);
|
|
|
|
FName := Value;
|
|
|
|
inherited SetDisplayName(Value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacro.GetMacroText(var AText: string);
|
|
|
|
begin
|
|
|
|
if Assigned(FOnGetText) then
|
|
|
|
FOnGetText(Self, FData, AText);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacro.GetText: string;
|
|
|
|
begin
|
|
|
|
Result := FData;
|
|
|
|
GetMacroText(Result);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacro.GetMacros: TJvMacros;
|
|
|
|
begin
|
|
|
|
if Collection is TJvMacros then
|
|
|
|
Result := TJvMacros(Collection)
|
|
|
|
else
|
|
|
|
Result := nil;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacro.Clear;
|
|
|
|
begin
|
|
|
|
FData := Unassigned;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacro.IsMacroStored: Boolean;
|
|
|
|
begin
|
|
|
|
Result := not VarIsEmpty(FData);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacro.GetAsVariant: Variant;
|
|
|
|
begin
|
|
|
|
Result := FData;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacro.SetAsVariant(Value: Variant);
|
|
|
|
begin
|
|
|
|
FData := Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacro.IsEqual(Value: TJvMacro): Boolean;
|
|
|
|
begin
|
|
|
|
Result := (VarType(FData) = VarType(Value.FData)) and
|
|
|
|
(VarIsEmpty(FData) or (FData = Value.FData)) and
|
|
|
|
(Name = Value.Name);
|
|
|
|
end;
|
|
|
|
|
|
|
|
//=== { TJvMacros } ==========================================================
|
|
|
|
|
|
|
|
constructor TJvMacros.Create(AOwner: TPersistent);
|
|
|
|
begin
|
|
|
|
inherited Create(AOwner, TJvMacro);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacros.IndexOf(const AName: string): Integer;
|
|
|
|
begin
|
|
|
|
for Result := 0 to Count - 1 do
|
|
|
|
if AnsiSameText(TJvMacro(Items[Result]).Name, AName) then
|
|
|
|
Exit;
|
|
|
|
Result := -1;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacros.GetItem(Index: Integer): TJvMacro;
|
|
|
|
begin
|
|
|
|
Result := TJvMacro(inherited Items[Index]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacros.SetItem(Index: Integer; Value: TJvMacro);
|
|
|
|
begin
|
|
|
|
inherited SetItem(Index, TCollectionItem(Value));
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacros.AddMacro(Value: TJvMacro);
|
|
|
|
begin
|
|
|
|
Value.Collection := Self;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacros.RemoveMacro(Value: TJvMacro);
|
|
|
|
begin
|
|
|
|
if Value.Collection = Self then
|
|
|
|
Value.Collection := nil;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacros.CreateMacro(const MacroName: string): TJvMacro;
|
|
|
|
begin
|
|
|
|
Result := Add as TJvMacro;
|
|
|
|
Result.Name := MacroName;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacros.IsEqual(Value: TJvMacros): 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 TJvMacros.MacroByName(const Value: string): TJvMacro;
|
|
|
|
begin
|
|
|
|
Result := FindMacro(Value);
|
|
|
|
if Result = nil then
|
|
|
|
raise EJVCLException.CreateRes(@SInvalidPropertyValue);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMacros.FindMacro(const Value: string): TJvMacro;
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
begin
|
|
|
|
for I := 0 to Count - 1 do
|
|
|
|
begin
|
|
|
|
Result := TJvMacro(inherited Items[I]);
|
|
|
|
if AnsiSameText(Result.Name, Value) then
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
Result := nil;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacros.AssignValues(Value: TJvMacros);
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
P: TJvMacro;
|
|
|
|
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 TJvMacros.ParseString(const Value: string; DoCreate: Boolean;
|
|
|
|
SpecialChar: Char): string;
|
|
|
|
var
|
|
|
|
Macros: TJvMacros;
|
|
|
|
begin
|
|
|
|
Result := Value;
|
|
|
|
Macros := TJvMacros.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 TJvMacros.GetMacroValue(const MacroName: string): Variant;
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
Macros: TList;
|
|
|
|
begin
|
|
|
|
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] := TJvMacro(Macros[I]).Value;
|
|
|
|
finally
|
|
|
|
Macros.Free;
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Result := MacroByName(MacroName).Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacros.SetMacroValue(const MacroName: string;
|
|
|
|
const Value: Variant);
|
|
|
|
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
|
|
|
|
TJvMacro(Macros[I]).Value := Value[I];
|
|
|
|
finally
|
|
|
|
Macros.Free;
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
MacroByName(MacroName).Value := Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMacros.GetMacroList(List: TList; const MacroNames: string);
|
|
|
|
var
|
|
|
|
Pos, Len: Integer;
|
|
|
|
begin
|
|
|
|
Pos := 1;
|
|
|
|
Len := Length(MacroNames);
|
|
|
|
while Pos <= Len do
|
|
|
|
List.Add(MacroByName(ExtractName(MacroNames, Pos)));
|
|
|
|
end;
|
|
|
|
|
|
|
|
//=== { TJvStrHolder } =======================================================
|
|
|
|
|
|
|
|
constructor TJvStrHolder.Create(AOwner: TComponent);
|
|
|
|
begin
|
|
|
|
inherited Create(AOwner);
|
|
|
|
FStrings := TStringList.Create;
|
|
|
|
FMacros := TJvMacros.Create(Self);
|
|
|
|
FMacroChar := '%';
|
|
|
|
FStrings.OnChange := @StringsChanged;
|
|
|
|
FStrings.OnChanging := @StringsChanging;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TJvStrHolder.Destroy;
|
|
|
|
begin
|
|
|
|
FOnChange := nil;
|
|
|
|
FOnChanging := nil;
|
|
|
|
FMacros.Free;
|
|
|
|
FStrings.OnChange := nil;
|
|
|
|
FStrings.OnChanging := nil;
|
|
|
|
FStrings.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.Assign(Source: TPersistent);
|
|
|
|
begin
|
|
|
|
if Source is TStrings then
|
|
|
|
FStrings.Assign(Source)
|
|
|
|
else
|
|
|
|
if Source is TJvStrHolder then
|
|
|
|
FStrings.Assign(TJvStrHolder(Source).Strings)
|
|
|
|
else
|
|
|
|
inherited Assign(Source);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.AssignTo(Dest: TPersistent);
|
|
|
|
begin
|
|
|
|
if Dest is TStrings then
|
|
|
|
Dest.Assign(Strings)
|
|
|
|
else
|
|
|
|
inherited AssignTo(Dest);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.Changed;
|
|
|
|
begin
|
|
|
|
if Assigned(FOnChange) then
|
|
|
|
FOnChange(Self);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.Changing;
|
|
|
|
begin
|
|
|
|
if Assigned(FOnChanging) then
|
|
|
|
FOnChanging(Self);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.Clear;
|
|
|
|
begin
|
|
|
|
Strings.Clear;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.GetCommaText: string;
|
|
|
|
begin
|
|
|
|
Result := Strings.CommaText;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.SetCommaText(const Value: string);
|
|
|
|
begin
|
|
|
|
Strings.CommaText := Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.GetCapacity: Integer;
|
|
|
|
begin
|
|
|
|
Result := Strings.Capacity;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.SetCapacity(NewCapacity: Integer);
|
|
|
|
begin
|
|
|
|
Strings.Capacity := NewCapacity;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.BeforeExpandMacros;
|
|
|
|
begin
|
|
|
|
if Assigned(FOnExpandMacros) then
|
|
|
|
FOnExpandMacros(Self);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.SetMacros(Value: TJvMacros);
|
|
|
|
begin
|
|
|
|
FMacros.AssignValues(Value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.RecreateMacros;
|
|
|
|
begin
|
|
|
|
if not (csReading in ComponentState) then
|
|
|
|
Macros.ParseString(Strings.Text, True, MacroChar);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.SetMacroChar(Value: Char);
|
|
|
|
begin
|
|
|
|
if Value <> FMacroChar then
|
|
|
|
begin
|
|
|
|
FMacroChar := Value;
|
|
|
|
RecreateMacros;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.MacroCount: Integer;
|
|
|
|
begin
|
|
|
|
Result := Macros.Count;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.MacroByName(const MacroName: string): TJvMacro;
|
|
|
|
begin
|
|
|
|
Result := Macros.MacroByName(MacroName);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.ExpandMacros: string;
|
|
|
|
var
|
|
|
|
I, J, P, LiteralChars: Integer;
|
|
|
|
Macro: TJvMacro;
|
|
|
|
Found: Boolean;
|
|
|
|
begin
|
|
|
|
BeforeExpandMacros;
|
|
|
|
Result := Strings.Text;
|
|
|
|
for I := Macros.Count - 1 downto 0 do
|
|
|
|
begin
|
|
|
|
Macro := Macros[I];
|
|
|
|
if not VarIsEmpty(Macro.FData) then
|
|
|
|
begin
|
|
|
|
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;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.DefineProperties(Filer: TFiler);
|
|
|
|
|
|
|
|
function DoWrite: Boolean;
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
Ancestor: TJvStrHolder;
|
|
|
|
begin
|
|
|
|
Ancestor := TJvStrHolder(Filer.Ancestor);
|
|
|
|
Result := False;
|
|
|
|
if (Ancestor <> nil) and (Ancestor.Strings.Count = Strings.Count) and
|
|
|
|
(KeyString = Ancestor.KeyString) and (Strings.Count > 0) then
|
|
|
|
for I := 0 to Strings.Count - 1 do
|
|
|
|
begin
|
|
|
|
Result := CompareText(Strings[I], Ancestor.Strings[I]) <> 0;
|
|
|
|
if Result then
|
|
|
|
Break;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Result := (Strings.Count > 0) or (KeyString <> '');
|
|
|
|
end;
|
|
|
|
|
|
|
|
begin
|
|
|
|
inherited DefineProperties(Filer);
|
|
|
|
{ for backward compatibility }
|
|
|
|
Filer.DefineProperty('InternalVer', @ReadVersion, @WriteVersion, Filer.Ancestor = nil);
|
|
|
|
Filer.DefineProperty('StrData', @ReadStrings, @WriteStrings, DoWrite);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.GetSorted: Boolean;
|
|
|
|
begin
|
|
|
|
Result := FStrings.Sorted;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.GetDuplicates: TDuplicates;
|
|
|
|
begin
|
|
|
|
Result := FStrings.Duplicates;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.ReadStrings(Reader: TReader);
|
|
|
|
var
|
|
|
|
Tmp: string;
|
|
|
|
begin
|
|
|
|
Strings.BeginUpdate;
|
|
|
|
try
|
|
|
|
Reader.ReadListBegin;
|
|
|
|
if not Reader.EndOfList then
|
|
|
|
KeyString := Reader.ReadString;
|
|
|
|
Strings.Clear;
|
|
|
|
while not Reader.EndOfList do
|
|
|
|
begin
|
|
|
|
Tmp := Reader.ReadString;
|
|
|
|
if FReserved >= AnsiXorVersion then
|
|
|
|
begin
|
|
|
|
if FReserved >= XorVersion then
|
|
|
|
Strings.Add(XorDecodeString(KeyString, Tmp))
|
|
|
|
else
|
|
|
|
{$WARNINGS OFF} // XorDecode is deprecated, but we need it for backward compatibility, so hide the warning
|
|
|
|
Strings.Add(XorDecode(KeyString, Tmp));
|
|
|
|
{$WARNINGS ON}
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Strings.Add(string(XorString(ShortString(KeyString), ShortString(Tmp))));
|
|
|
|
end;
|
|
|
|
Reader.ReadListEnd;
|
|
|
|
finally
|
|
|
|
Strings.EndUpdate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.SetDuplicates(Value: TDuplicates);
|
|
|
|
begin
|
|
|
|
FStrings.Duplicates := Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.SetSorted(Value: Boolean);
|
|
|
|
begin
|
|
|
|
FStrings.Sorted := Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvStrHolder.GetStrings: TStrings;
|
|
|
|
begin
|
|
|
|
Result := FStrings;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.SetStrings(Value: TStrings);
|
|
|
|
begin
|
2023-04-20 13:23:34 +00:00
|
|
|
//if Value <> FStrings then
|
2018-11-06 17:59:59 +00:00
|
|
|
FStrings.Assign(Value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.StringsChanged(Sender: TObject);
|
|
|
|
begin
|
|
|
|
RecreateMacros;
|
|
|
|
if not (csReading in ComponentState) then
|
|
|
|
Changed;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.StringsChanging(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if not (csReading in ComponentState) then
|
|
|
|
Changing;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.WriteStrings(Writer: TWriter);
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
begin
|
|
|
|
Writer.WriteListBegin;
|
|
|
|
Writer.WriteString(KeyString);
|
|
|
|
for I := 0 to Strings.Count - 1 do
|
|
|
|
Writer.WriteString(XorEncodeString(KeyString, Strings[I]));
|
|
|
|
Writer.WriteListEnd;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.ReadVersion(Reader: TReader);
|
|
|
|
begin
|
|
|
|
FReserved := Reader.ReadInteger;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvStrHolder.WriteVersion(Writer: TWriter);
|
|
|
|
begin
|
|
|
|
Writer.WriteInteger(XorVersion);
|
|
|
|
end;
|
|
|
|
|
|
|
|
//=== { TJvMultiStringHolderCollectionItem } =================================
|
|
|
|
|
|
|
|
procedure TJvMultiStringHolderCollectionItem.SetName(Value: string);
|
|
|
|
begin
|
|
|
|
Value := Trim(Value);
|
|
|
|
if Value = '' then
|
|
|
|
FName := ''
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
if not TJvMultiStringHolderCollection(Collection).DoesNameExist(Value) then
|
|
|
|
FName := Value
|
|
|
|
else
|
|
|
|
raise EJVCLException.CreateRes(@SDuplicateString);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMultiStringHolderCollectionItem.SetStrings(const Value: TStrings);
|
|
|
|
begin
|
|
|
|
if Value <> FStrings then
|
|
|
|
FStrings.Assign(Value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMultiStringHolderCollectionItem.GetDisplayName: string;
|
|
|
|
begin
|
|
|
|
if FName <> '' then
|
|
|
|
Result := FName
|
|
|
|
else
|
|
|
|
Result := RsNoName;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor TJvMultiStringHolderCollectionItem.Create(ACollection: TCollection);
|
|
|
|
begin
|
|
|
|
inherited Create(ACollection);
|
|
|
|
FStrings := TStringList.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TJvMultiStringHolderCollectionItem.Destroy;
|
|
|
|
begin
|
|
|
|
FStrings.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
//=== { TJvMultiStringHolderCollection } =====================================
|
|
|
|
|
|
|
|
function TJvMultiStringHolderCollection.GetItem(Index: Integer): TJvMultiStringHolderCollectionItem;
|
|
|
|
begin
|
|
|
|
Result := TJvMultiStringHolderCollectionItem(inherited GetItem(Index));
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMultiStringHolderCollection.SetItem(Index: Integer; Value: TJvMultiStringHolderCollectionItem);
|
|
|
|
begin
|
|
|
|
inherited SetItem(Index, Value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMultiStringHolderCollection.DoesNameExist(const Name: string): Boolean;
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
for I := 0 to Count - 1 do
|
|
|
|
if CompareText(Items[I].Name, Name) = 0 then
|
|
|
|
Exit;
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMultiStringHolderCollection.Add: TJvMultiStringHolderCollectionItem;
|
|
|
|
begin
|
|
|
|
Result := TJvMultiStringHolderCollectionItem(inherited Add);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMultiStringHolderCollection.Insert(Index: Integer): TJvMultiStringHolderCollectionItem;
|
|
|
|
begin
|
|
|
|
Result := Add;
|
|
|
|
Result.Index := Index;
|
|
|
|
end;
|
|
|
|
|
|
|
|
//=== { TJvMultiStringHolder } ===============================================
|
|
|
|
|
|
|
|
constructor TJvMultiStringHolder.Create(AOwner: TComponent);
|
|
|
|
begin
|
|
|
|
inherited Create(AOwner);
|
|
|
|
FMultipleStrings := TJvMultiStringHolderCollection.Create(Self, TJvMultiStringHolderCollectionItem);
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TJvMultiStringHolder.Destroy;
|
|
|
|
begin
|
|
|
|
FMultipleStrings.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvMultiStringHolder.SetMultipleStrings(Value: TJvMultiStringHolderCollection);
|
|
|
|
begin
|
|
|
|
if Value <> FMultipleStrings then
|
|
|
|
FMultipleStrings.Assign(Value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMultiStringHolder.GetItemByName(const AName: string): TJvMultiStringHolderCollectionItem;
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
begin
|
|
|
|
for I := 0 to MultipleStrings.Count - 1 do
|
|
|
|
if CompareText(MultipleStrings.Items[I].Name, AName) = 0 then
|
|
|
|
begin
|
|
|
|
Result := MultipleStrings.Items[I];
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
raise EJvMultiStringHolderException.CreateResFmt(@RsENoItemFoundWithName, [AName]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvMultiStringHolder.GetStringsByName(const AName: string): TStrings;
|
|
|
|
begin
|
|
|
|
Result := GetItemByName(AName).Strings;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
end.
|