unit IniObj;
{ If component placed on the form, all published properties (enum, int, float,
  string) of
  all components (and the form itself) will be stored to .ini file }

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  IniFiles, TypInfo;

type
  TIniObject = class(TComponent)
  private
    { Private declarations }
    FFileName: shortstring;
    FIniExt: shortstring;
    FUseSectionPrefix: boolean;
    FFormNameIsIniName: boolean;
    FDisabled:boolean;
    FAutoSaved:boolean;
  protected
    { Protected declarations }
    procedure ReadWrite(read:boolean);
    procedure Loaded;override;
  public
    { Public declarations }
    procedure Read;
    { published properties of all Owner components from
      ini file }
    procedure Write;
    { properties of all Owner components to ini file }
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  published
    { Published declarations }
    property FileName:shortstring read FFileName write FFileName;
    { if not assigned than ExeName.Ini used in ExeDir;
      if assigned but no dir specified, then ExeDir used }
    property IniExt:shortstring read FIniExt write FIniExt;
    { extension for ini file; if not specified, then '.INI' used }
    property UseSectionPrefix:boolean read FUseSectionPrefix write FUseSectionPrefix default true;
    { if true, then section name consists from form name and
      component name separated with dot, if false, then
      just component name is used for the section name }
    property FormNameIsIniName:boolean read FFormNameIsIniName
      write FFormNameIsIniName;
    { Considered only if FileName not specified.
      if true then Owner form name is used as FileName
      (IniExt appended); if false then ExeName with IniExt
      used }
    property Disabled:boolean read FDisabled write FDisabled;
    { if set true, then read/write methods have no effect }

  end;

procedure Register;

implementation

function GetEnumPropStr(c: TObject; pi: PPropInfo): shortstring;
var
  i: integer;
begin
  i := GetOrdProp(c, pi);
  GetEnumPropStr := GetEnumName(pi^.PropType^, i);
end;

procedure SetEnumPropStr(c: TObject; pi: PPropInfo; const AValue: shortstring);
var i: integer;
begin
  i := GetEnumValue(pi^.PropType^, AValue);
  SetOrdProp(c, pi, i);
end;

procedure TIniObject.Read;
    { published properties of all Owner components from
      ini file }
begin
  ReadWrite(true);
end;

procedure TIniObject.Write;
begin
  ReadWrite(false);
end;

procedure TIniObject.ReadWrite(read:boolean);
var
  f: TIniFile;
  fn: string;
  i, { component index }
  j  { property index }
  : integer;
  c: TComponent;
  ti: PTypeInfo;
  td: PTypeData;
  pl: TPropList;
  pi: PPropInfo;
  vs: shortstring;
  ext: shortstring;
  secpref:shortstring;
begin
  if Owner = nil then
    exit;
  if FDisabled then
    exit;
  fn := FFileName;
  ext := FIniExt;
  if ext = '' then
    ext := '.INI';
  if fn = '' then begin
    if FFormNameIsIniName then begin
      fn := Owner.Name + ext;
    end else begin
      fn := ChangeFileExt(Paramstr(0), ext)
    end;
  end else begin
    if ExtractFileExt(fn) = '' then
      fn := ChangeFileExt(fn, ext);
  end;
  if ExtractFilePath(fn) = '' then begin
    fn := ExtractFilePath(Paramstr(0)) + fn;
  end;

  f := TIniFile.Create(fn);
  try
    if FUseSectionPrefix then
      secpref := Owner.Name + '.'
    else
      secpref := '';

    for i := 0 to Owner.ComponentCount - 1 do begin
      c := Owner.Components[i];
      if c = Self then
        continue;
      {if pos('_', c.Name) <> 0 then
        continue;}
      ti := PTypeInfo(c.ClassInfo);
      td := PTypeData(GetTypeData(ti));
      GetPropInfos(ti, @pl);

      for j := 0 to td^.PropCount - 1 do begin
        pi := pl[j];

        if read then begin
          vs := f.ReadString(secpref + c.Name, pi^.Name, '');
          if vs <> '' then begin
            case pi^.PropType^^.Kind of
              tkInteger: SetOrdProp(c, pi, StrToInt(vs));
              tkFloat: SetFloatProp(c, pi, StrToFloat(vs));
              tkString: SetStrProp(c, pi, vs);
              tkEnumeration: SetEnumPropStr(c, pi, vs);
            end;//proputl
          end;
        end else begin
          vs := '';
          case pi^.PropType^^.Kind of
            tkInteger: vs := IntToStr(GetOrdProp(c, pi));
            tkFloat: vs := FloatToStr(GetFloatProp(c, pi));
            tkString: vs := GetStrProp(c, pi);
            tkEnumeration: vs := GetEnumPropStr(c, pi);
          end;
          if vs <> '' then
            f.WriteString(secpref + c.Name, pi^.Name, vs);
        end;
      end;

    end;
  finally
    f.Free;
  end;
end;

procedure TIniObject.Loaded;
begin
  Read;
end;

procedure TIniObject.Notification(AComponent: TComponent; Operation: TOperation);
begin
  if (Operation = opRemove) and (not FAutoSaved) then begin
    FAutoSaved := true;
    Write;
  end;
end;

procedure Register;
begin
  RegisterComponents('Samples', [TIniObject]);
end;

end.
