MiConfigINI.pas 9.3 KB

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