unit Unit_UserConfig;
//////////////////////////////////////////////////////////////////////
///
/// Unit zur vereinfachten Speicherung von User-Daten
///
/// Mittels Save werden die Eigenschaften ALLER Child-Komponenten einer
/// Komponente gespeichert.
/// Load holt diese dann wieder
///
/// In Datei speichern
/// UserConfig:=TUserConfig.Create(0); //Nur auf Festplatte speichern
/// aufruf z.B. SaveToFile (GroupBox1,'configuratio.cfg');
/// LoadFromFile(GroupBox1,'configuratio.cfg');
///
/// In Speicher ablegen
/// (Damit kann z.B eine Undo-Funktion für Optionen realisiert werden)
/// UserConfig:=TUserConfig.Create(10); //Plätze zur Speicherung bereithalten
/// aufruf z.B. SaveToFile (Form1,5); //Auf Platz 5 speichern
/// LoadFromFile(Form1,5); //von Platz 4 laden
///
///
///(c) 2005 Borg@Sven-of-Nine.de
///
///Beispielprogramm unter www.Sven-of-Nine.de
///
//////////////////////////////////////////////////////////////////////
interface
uses Classes;
type
TUserConfig = class(TObject)
private
{ Private-Deklarationen }
//Direkter Zugriff auf Eigenschaften
//set properties using winapi
function IsProperty(Obj: TObject; sProp: string): Boolean;
function SetProperty(Obj: TObject; sProp: string; vValue: Variant): Boolean;
function HasAncestor(Child: TComponent; Name: string): Boolean;
public
{ Public-Deklarationen }
constructor Create(MaxMemory: Integer = 10);
destructor Destroy(); override;
//Komponenten in Datei schreiben
//save/load components to/from file
function SaveToFile(Component: TComponent; sFilename: string): Boolean;
function LoadFromFile(Component: TComponent; sFilename: string): Boolean;
//Komponenten in Speicher schreiben (UNDO-Funktion)
//save/load components to/from mem
function SaveToMemory(Component: TComponent; Index: Integer): Boolean;
function LoadFromMemory(Component: TComponent; Index: Integer): Boolean;
end;
implementation
uses Windows, SysUtils, Controls, Forms, TypInfo;
var
aMemStream: array of TMemoryStream;
//////////////////////////////////////////////////////////////////////
/// Konstruktor und Destruktor
//////////////////////////////////////////////////////////////////////
constructor TUserConfig.Create(MaxMemory: Integer = 10);
var
iIndex: Integer;
begin
//Alle angeforderten Speicherstreams initialisieren
//initialize memorystreams
if (MaxMemory > 255) then MaxMemory := 255;
try
SetLength(aMemStream, MaxMemory);
for iIndex := 0 to MaxMemory - 1 do
begin
aMemStream[iIndex] := TMemoryStream.Create;
end;
finally
end;
end;
destructor TUserConfig.Destroy();
var
iIndex: Integer;
begin
//Alle angeforderten Speicherstreams freimachen
//free all
for iIndex := 0 to Length(aMemStream) - 1 do
begin
aMemStream[iIndex].Free;
end;
SetLength(aMemStream, 0);
end;
//////////////////////////////////////////////////////////////////////
/// Prüfen, ob ein Object die gewünschte Eigenschaft hat
/// Check for properties
//////////////////////////////////////////////////////////////////////
function TUserConfig.IsProperty(Obj: TObject; sProp: string): Boolean;
var
plList: tPropList;
iIndex1: Integer;
iIndex2: Integer;
begin
Result := False;
//Alle verfügbaren Properties holen
//get properties
iIndex2 := GetPropList(PTypeInfo(Obj.ClassInfo),
[tkUnknown, tkVariant, tkInteger, tkInt64, tkFloat,
tkString, tkWString, tkLString, tkChar, tkWChar,
tkEnumeration, tkSet, tkClass, tkMethod, tkArray,
tkDynArray, tkRecord, tkInterface], @plList);
//nach der gewünschten suchen
//search for the wanted
iIndex1 := 0;
while (iIndex1 < iIndex2) do
begin
if plList[iIndex1].Name = sProp then
begin
Result := True;
iIndex1 := iIndex2;
end;
Inc(iIndex1);
end;
end;
//////////////////////////////////////////////////////////////////////
/// Eine Egenschaft direkt setzen
/// set properties
//////////////////////////////////////////////////////////////////////
function TUserConfig.SetProperty(Obj: TObject; sProp: string; vValue: Variant): Boolean;
begin
if (IsProperty(Obj, sProp)) then
begin
SetPropValue(Obj, sProp, vValue);
Result := True;
end
else
begin
Result := False;
end;
end;
//////////////////////////////////////////////////////////////////////
/// Nach einem Vorfahr mit dem Namen "Name" suchen
/// check for ancestor named "Name"
//////////////////////////////////////////////////////////////////////
function TUserConfig.HasAncestor(Child: TComponent; Name: string): Boolean;
var
cWork: TComponent;
begin
Result := False;
cWork := Child;
while (cWork.HasParent) do
begin
//Eltern holen
cWork := cWork.GetParentComponent;
//Sind die Eltern die gesuchten ?
if (cWork.Name = Name) then
begin
//Dann Suche beenden
Result := True;
break;
end;
end;
cWork := nil;
end;
//////////////////////////////////////////////////////////////////////
/// Save all components to disk
/// alle komponenten in datei speichern
//////////////////////////////////////////////////////////////////////
function TUserConfig.SaveToFile(Component: TComponent; sFilename: string): Boolean;
var
hFile: THandle;
Stream: THandleStream;
iIndex: Integer;
sName: string[255];
cWork: TComponent;
begin
Result := False;
//Datei auf jeden Fall immer neu erzeugen
//Create File
hFile := FileCreate(sFilename);
if (hFile > 0) then
begin
//Die Hauptkomponente finden (das Formular)
//Find parent
cWork := Component;
while (cWork.HasParent) do
begin
cWork := cWork.GetParentComponent;
end;
//Stream erzeugen
//Create stream
Stream := THandleStream.Create(hFile);
try
//Und los
//enumerate all
for iIndex := 0 to cWork.ComponentCount - 1 do
begin
//Ist es ein Win-Control und eine Nachfahre der gewünschten Componente?
//save only TWinControls and childs of Component
if (cWork.Components[iIndex] is TWinControl) and
(HasAncestor(cWork.Components[iIndex], Component.Name)) then
begin
//Hier ein paar Ausnahmen
//some exceptions
if (cWork.Components[iIndex].ClassName <> 'TFlatTitlebar')
and
(cWork.Components[iIndex].ClassName <>
'TFlatSpinEd1itInteger') then
begin
//Erst den Namen
//save name first
sName := cWork.Components[iIndex].Name;
Stream.Write(sName, Length(sName) + 1);
//Und dann die Komponente hinterher
//and component
Stream.WriteComponent(cWork.Components[iIndex]);
end;
end;
end;
Result := True;
finally
//Fertig
//done
Stream.Free;
end;
//close handle
FileClose(hFile);
end;
cWork := nil;
end;
//////////////////////////////////////////////////////////////////////
/// load all components from disk
/// alle komponenten aus datei laden
//////////////////////////////////////////////////////////////////////
function TUserConfig.LoadFromFile(Component: TComponent; sFilename: string): Boolean;
var
hFile: THandle;
Stream: THandleStream;
iIndex: Integer;
sName: string[255];
iName: Integer;
cWork: TComponent;
begin
Result := False;
//Date öffnen
//open read
hFile := FileOpen(sFilename, fmOPENREAD);
if (hFile > 0) then
begin
//Das die Hauptkomponente finden (das Formular)
cWork := Component;
while (cWork.HasParent) do
begin
cWork := cWork.GetParentComponent;
end;
//Stream erzeugen
//create stream
Stream := THandleStream.Create(hFile);
try
//Vorne anfangen
//from the beginning
Stream.Position := 0;
//Und kpl. durchwurstem
//the whole file
while (Stream.Position < Stream.Size) do
begin
//erstes byte des namens
//first byte of Name
Stream.read(sName[0], 1);
//Größe rausholen
//get size
iName := Byte(sName[0]);
//Und den ganzen Namen lesen
//Read the whole name
Stream.read(sName[1], iName);
//Object holen
//get object
try
//Nach dem namen suchens
//search for the name
for iIndex := 0 to cWork.ComponentCount - 1 do
begin
if (cWork.Components[iIndex].Name = sName) then
begin
//Bei allem, was Checked hat, dies erst auf FALSE
// setzen
//Uncheck all "checkables"
SetProperty(cWork.Components[iIndex],
'Checked', False);
//Und dann erst laden
//load
Stream.ReadComponent(cWork.Components[iIndex]);
end;
end;
except
end;
end;
finally
//done
Stream.Free;
end;
FileClose(hFile);
end;
cWork := nil;
end;
//////////////////////////////////////////////////////////////////////
/// Save all components to memory
/// alle komponenten in speicher schreiben
//////////////////////////////////////////////////////////////////////
function TUserConfig.SaveToMemory(Component: TComponent; Index: Integer): Boolean;
var
iIndex: Integer;
sName: string[255];
cWork: TComponent;
begin
Result := False;
if (Index < 0) or (Index >= Length(aMemStream)) then Exit;
try
//Die Hauptkomponente finden (das Formular)
cWork := Component;
while (cWork.HasParent) do
begin
cWork := cWork.GetParentComponent;
end;
for iIndex := 0 to cWork.ComponentCount - 1 do
begin
if (cWork.Components[iIndex] is TWinControl) and
(HasAncestor(cWork.Components[iIndex], Component.Name)) then
begin
if (cWork.Components[iIndex].ClassName <> 'TFlatTitlebar') and
(cWork.Components[iIndex].ClassName <> 'TFlatSpinEd1itInteger') then
begin
sName := Component.Components[iIndex].Name;
aMemStream[Index].Write(sName, Length(sName) + 1);
aMemStream[Index].WriteComponent(cWork.Components[iIndex]);
end;
end;
end;
Result := True;
finally
cWork := nil;
end;
end;
//////////////////////////////////////////////////////////////////////
/// load components[index] from memory
/// komponenten[index] aus speicher lesen
//////////////////////////////////////////////////////////////////////
function TUserConfig.LoadFromMemory(Component: TComponent; Index: Integer): Boolean;
var
iIndex: Integer;
sName: string[255];
iName: Integer;
cWork: TComponent;
begin
Result := False;
if (Index < 0) or (Index >= Length(aMemStream)) then Exit;
try
cWork := Component;
while (cWork.HasParent) do
begin
cWork := cWork.GetParentComponent;
end;
aMemStream[Index].Position := 0;
while (aMemStream[Index].Position < aMemStream[Index].Size) do
begin
aMemStream[Index].read(sName[0], 1);
iName := Byte(sName[0]);
aMemStream[Index].read(sName[1], iName);
try
for iIndex := 0 to cWork.ComponentCount - 1 do
begin
if (cWork.Components[iIndex].Name = sName) then
begin
SetProperty(cWork.Components[iIndex],
'Checked', False);
aMemStream[Index].ReadComponent
(cWork.Components[iIndex]);
end;
end;
except
end;
end;
Result := True;
finally
cWork := nil;
end;
end;
end.
|