1 {
2 MiConfigIni 0.1b
3 =============
4 Por Tito Hinostroza 29/07/2016
5 
6 Descripción
7 ===========
8 Unidad con rutinas de lectura/escritura de propiedades en archivos INI. Permite crear
9 fácilmente, una ventana de configuración, con las opciones: ACEPTAR y CANCELAR.
10 Está basado en la librería ConfigFrame, pero a diferencia de esta, aquí las propiedades
11 no se separan en "frames", sino que todas las propiedades se manejan en un mismo objeto.
12 Para alamacenar las propiedades, se debe crear un objeto TMiConfigINI. Sin embargo,
13 la unidad crea por defecto, una isntancia de TMiConfigINI, llamada "cfgFile", que toma
14 como nombre <nombre del proyecto>.ini
15 Tiene como dependencia a la librería MisUtils.
16 
17 Por Tito Hinostroza 29/07/2016
18 }
19 unit MiConfigINI;
20 {$mode objfpc}{$H+}
21 interface
22 uses
23   Classes, SysUtils, Graphics, Forms, IniFiles, MisUtils, MiConfigBasic;
24 type
25   { TMiConfigINI }
26   {Clase base que es usada para manejar los campos de configuración.}
27   TMiConfigINI = class(TMiConfigBasic)
28   private
29     fileName    : string;   //archivo XML
DefaultFileNamenull30     function DefaultFileName: string;
31     procedure FileProperty(iniCfg: TIniFile; const r: TParElem; FileToProp: boolean);
32   public
33     secINI: string;   //sección donde se guardarán los datos en un archivo INI
34     procedure VerifyFile;
FileToPropertiesnull35     function FileToProperties: boolean; virtual;
PropertiesToFilenull36     function PropertiesToFile: boolean; virtual;
37   public  //Constructor y Destructor
38     constructor Create(INIfile0: string);
39     destructor Destroy; override;
40   end;
41 
42 var
43   cfgFile : TMiConfigINI;   //Default INI Config file
44 
45 implementation
46 //Funciones de uso interno
CodeStrnull47 function CodeStr(s:string): string;
48 {Protege a una cadena para que no pierda los espacios laterales si es que los tiene,
49 porque el el archivo INI se pierden. Además codifica el caracter "=", porque es
50 reservado en el archvio INI}
51 begin
52   Result := '.'+s+'.';
53   Result := StringReplace(Result, '=', #25, [rfReplaceAll]);  //protege caracter
54   Result := StringReplace(Result, LineEnding, #26, [rfReplaceAll]);  //protege caracter
55 end;
DecodeStrnull56 function DecodeStr(s:string): string;
57 {Quita la protección a una cadena que ha sido guardada en un archivo INI}
58 begin
59   Result:=copy(s,2,length(s)-2);
60   Result := StringReplace(Result, #25, '=', [rfReplaceAll]);  //protege caracter
61   Result := StringReplace(Result, #26, LineEnding, [rfReplaceAll]);  //protege caracter
62 end;
63 { TMiConfigINI }
TMiConfigINI.DefaultFileNamenull64 function TMiConfigINI.DefaultFileName: string;
65 {Devuelve el nombre pro defecto del archvio de configuración}
66 begin
67   Result := ChangeFileExt(Application.ExeName,'.ini');
68 end;
69 procedure TMiConfigINI.VerifyFile;
70 //Verifica si el archivo INI "FileName" existe. Si no, muestra un mensaje y lo crea.
71 var
72   F: textfile;
73 begin
74   if not FileExists(fileName) then begin
75     MsgErr('No INI file found: %s', [fileName]);
76     //crea uno vacío para leer las opciones por defecto
77     AssignFile(F, fileName);
78     Rewrite(F);
79     CloseFile(F);
80   end;
81 end;
82 procedure TMiConfigINI.FileProperty(iniCfg: TIniFile; const r: TParElem; FileToProp: boolean);
83 {Permite leer o escribir una propiedad en el archivo XML}
84 var
85   n, j: Integer;
86   list: TStringList;
87   strlst: TStringList;
88   c: TColor;
89 begin
90   if r.pVar = nil then exit;   //se inició con NIL
91   case r.tipPar of
92   tp_Int, tp_Int_TEdit, tp_Int_TSpinEdit, tp_Int_TRadioGroup:
93     if FileToProp then begin  //lee entero
94       r.AsInteger := iniCfg.ReadInteger(secINI, r.etiqVar, r.defInt);
95     end else begin
96       iniCfg.WriteInteger(secINI, r.etiqVar, r.AsInteger);
97     end;
98   //---------------------------------------------------------------------
99   tp_Dbl, tp_Dbl_TEdit, tp_Dbl_TFloatSpinEdit:
100     if FileToProp then begin
101       r.AsDouble := iniCfg.ReadFloat(secINI, r.etiqVar, r.defDbl);
102     end else begin
103       iniCfg.WriteFloat(secINI, r.etiqVar, r.AsDouble);
104     end;
105   //---------------------------------------------------------------------
106   tp_Str, tp_Str_TEdit, tp_Str_TEditButton, tp_Str_TCmbBox:
107     if FileToProp then begin  //lee cadena
108       r.AsString := DecodeStr(iniCfg.ReadString(secINI, r.etiqVar, '.'+r.defStr+'.'));
109     end else begin
110       iniCfg.WriteString(secINI, r.etiqVar, CodeStr(r.AsString));
111     end;
112   //---------------------------------------------------------------------
113   tp_Bol, tp_Bol_TCheckBox, tp_Bol_TRadBut:
114     if FileToProp then begin  //lee booleano
115       r.AsBoolean := iniCfg.ReadBool(secINI, r.etiqVar, r.defBol);
116     end else begin
117       iniCfg.WriteBool(secINI, r.etiqVar, r.AsBoolean);
118     end;
119   //---------------------------------------------------------------------
120   tp_Enum, tp_Enum_TRadBut, tp_Enum_TRadGroup:
121     if FileToProp then begin  //lee enumerado como entero
122        if r.lVar = 4 then begin  //tamaño común de las variable enumeradas
123          r.AsInt32 := iniCfg.ReadInteger(secINI, r.etiqVar, r.defInt);
124        end else begin  //tamaño no implementado
125          msjErr := dic('Enumerated type no handled.');
126          exit;
127        end;
128     end else begin
129       if r.lVar = 4 then begin
130         iniCfg.WriteInteger(secINI, r.etiqVar, r.AsInt32);  //como entero de 4 bytes
131       end else begin  //tamaño no implementado
132         msjErr := dic('Enumerated type no handled.');
133         exit;
134       end;
135     end;
136   //---------------------------------------------------------------------
137   tp_TCol_TColBut, tp_TCol_TColBox:
138     if FileToProp then begin  //lee TColor
139       r.AsTColor := iniCfg.ReadInteger(secINI, r.etiqVar, r.defCol);
140     end else begin
141       c := r.AsTColor;
142       iniCfg.WriteInteger(secINI, r.etiqVar, c);
143     end;
144   tp_StrList, tp_StrList_TListBox:
145     if FileToProp then  begin //lee TStringList
146       list := TStringList(r.Pvar^);
147       iniCfg.ReadSection(secINI+'_'+r.etiqVar, list);
148       //decodifica cadena
149       for n:=0 to list.Count-1 do list[n] := DecodeStr(list[n]);
150     end else begin
151       strlst := TStringList(r.Pvar^);
152       iniCfg.EraseSection(secINI+'_'+r.etiqVar);
153       for j:= 0 to strlst.Count-1 do begin
154         iniCfg.WriteString(secINI+'_'+r.etiqVar,
155                            CodeStr(strlst[j]),'');
156       end;
157     end;
158   else  //no se ha implementado bien
159     msjErr := dic('Design error.');
160     exit;
161   end;
162 end;
TMiConfigINI.FileToPropertiesnull163 function TMiConfigINI.FileToProperties: boolean;
164 {Lee de disco las propiedades registradas
165 Si encuentra error devuelve FALSE, y el mensaje de error en "MsjErr", y el elemento
166 con error en "ctlErr".}
167 var
168   r: TParElem;
169   iniCfg: TIniFile;
170 begin
171   if not FileExists(fileName) then begin
172     ctlErr := nil;
173     MsjErr := dic('INI file does not exist.');  //errro
174     exit(false);  //para que no intente leer
175   end;
176   try
177     iniCfg := TIniFile.Create(fileName);
178   except
179     ctlErr := nil;
180     MsjErr := dic('Error reading INI file: %s', [fileName]);
181     iniCfg.Free;
182     exit(false);
183   end;
184   msjErr := '';
185   for r in listParElem do begin
186     FileProperty(iniCfg, r, true);
187     if msjErr<>'' then begin
188       ctlErr := r;  //elemento que produjo el error
189       iniCfg.Free;  //libera
190       exit(false);   //sale con error
191     end;
192     if r.OnFileToProperty<>nil then r.OnFileToProperty;
193   end;
194   //Terminó con éxito. Actualiza los cambios
195   if OnPropertiesChanges<>nil then OnPropertiesChanges;
196   ctlErr := nil;
197   iniCfg.Free;  //libera
198   exit(true);   //sale sin error
199 end;
TMiConfigINI.PropertiesToFilenull200 function TMiConfigINI.PropertiesToFile: boolean;
201 {Guarda en disco las propiedades registradas
202 Si encuentra error devuelve FALSE, y el mensaje de error en "MsjErr", y el elemento
203 con error en "ctlErr".}
204 var
205   r: TParElem;
206   iniCfg: TIniFile; //
207 begin
208   if FileExists(fileName) then begin  //ve si existe
209      if FileIsReadOnly(fileName) then begin
210        ctlErr := nil;
211        MsjErr := dic('INI file is only read.');
212        exit(false);
213      end;
214   end;
215   try
216     iniCfg := TIniFile.Create(fileName);
217   except
218     ctlErr := nil;
219     MsjErr := dic('Error writing INI file: %s', [fileName]);
220     exit(false);
221   end;
222   msjErr := '';
223   for r in listParElem do begin
224     if r.OnPropertyToFile<>nil then r.OnPropertyToFile;  //se ejecuta antes
225     FileProperty(iniCfg, r, false);
226     if msjErr<>'' then begin
227       ctlErr := r;   //elemento que produjo el error
228       iniCfg.Free;   //libera
229       exit(false);   //sale con error
230     end;
231   end;
232   ctlErr := nil;
233   iniCfg.Free;    //libera
234   exit(true);     //sin error
235 end;
236 //Constructor y Destructor
237 constructor TMiConfigINI.Create(INIfile0: string);
238 begin
239   inherited Create;
240   fileName := INIfile0;
241   secINI := 'config';  //sección por defecto en archivo INI
242 end;
243 destructor TMiConfigINI.Destroy;
244 begin
245   inherited Destroy;
246 end;
247 
248 initialization
249   cfgFile := TMiConfigINI.Create(cfgFile.DefaultFileName);
250 
251 finalization
252   cfgFile.Destroy;
253 end.
254 
255