{ Double Commander ------------------------------------------------------------------------- Implementation of configuration file in XML. Based on XmlConf from fcl-xml package. Copyright (C) 2010 Przemyslaw Nagay (cobines@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } unit DCXmlConfig; {$mode objfpc}{$H+} interface uses Classes, SysUtils, DOM, XMLRead, XMLWrite; type // Define type aliases so we don't have to include DOM if we want to use config. TXmlNode = TDOMNode; TXmlPath = DOMString; { TXmlConfig } TXmlConfig = class private FFileName: UTF8String; FDoc: TXMLDocument; function GetRootNode: TXmlNode; procedure SplitPathToNodeAndAttr(const Path: DOMString; out NodePath: DOMString; out AttrName: DOMString); public constructor Create; virtual; constructor Create(const AFileName: UTF8String; AutoLoad: Boolean = False); virtual; destructor Destroy; override; procedure Clear; function AddNode(const RootNode: TDOMNode; const ValueName: DOMString): TDOMNode; procedure DeleteNode(const RootNode: TDOMNode; const Path: DOMString); procedure DeleteNode(const Node: TDOMNode); procedure ClearNode(const Node: TDOMNode); function FindNode(const RootNode: TDOMNode; const Path: DOMString; bCreate: Boolean = False): TDOMNode; function GetContent(const Node: TDOMNode): UTF8String; function IsEmpty: Boolean; procedure SetContent(const Node: TDOMNode; const AValue: UTF8String); // ------------------------------------------------------------------------ function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: UTF8String): UTF8String; function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean; function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer; function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64; function GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double; function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: UTF8String): UTF8String; function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean; function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer; function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64; function GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double; // The Try... functions return True if the attribute/node was found and only then set AValue. function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: UTF8String): Boolean; function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean; function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean; function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean; function TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean; function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: UTF8String): Boolean; function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean; function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean; function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean; function TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean; // ------------------------------------------------------------------------ // AddValue functions always add a new node. procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: String); procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Boolean); procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Integer); procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Int64); procedure AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Double); procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: String); procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Boolean); procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Integer); procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Int64); procedure AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Double); // SetValue functions can only set values for unique paths. procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: UTF8String); procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean); procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer); procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64); procedure SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double); procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: String); procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean); procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer); procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64); procedure SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double); // ------------------------------------------------------------------------ procedure GetFont(const aNode: TXmlNode; Path: TXmlPath; out Name: UTF8String; out Size: Integer; out Style: Integer; const DefName: UTF8String; const DefSize: Integer; const DefStyle: Integer); procedure SetFont(const aNode: TXmlNode; Path: TXmlPath; const Name: UTF8String; const Size: Integer; const Style: Integer); // ------------------------------------------------------------------------ procedure ReadFromFile(const AFilename: UTF8String); procedure ReadFromStream(AStream: TStream); procedure WriteToFile(const AFilename: UTF8String); procedure WriteToStream(AStream: TStream); function Load: Boolean; function LoadBypassingErrors: Boolean; function Save: Boolean; {en Get path of form "//...". } function GetPathFromNode(aNode: TDOMNode): String; property FileName: UTF8String read FFileName write FFileName; property RootNode: TXmlNode read GetRootNode; end; EXmlConfigEmpty = class(EFilerError); EXmlConfigNotFound = class(EFilerError); implementation uses LazUTF8, LazLogger, DCOSUtils, DCClassesUtf8, URIParser; const BoolStrings: array[Boolean] of DOMString = ('False', 'True'); constructor TXmlConfig.Create; begin Clear; end; constructor TXmlConfig.Create(const AFileName: UTF8String; AutoLoad: Boolean); begin FFileName := AFileName; if not (AutoLoad and LoadBypassingErrors) then Clear; end; destructor TXmlConfig.Destroy; begin FreeAndNil(FDoc); inherited Destroy; end; procedure TXmlConfig.Clear; begin FreeAndNil(FDoc); FDoc := TXMLDocument.Create; FDoc.AppendChild(FDoc.CreateElement(ApplicationName)); end; function TXmlConfig.GetRootNode: TXmlNode; begin Result := FDoc.DocumentElement; end; // ------------------------------------------------------------------------ function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: UTF8String): UTF8String; begin if not TryGetAttr(RootNode, Path, Result) then Result := ADefault; end; function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean; begin if not TryGetAttr(RootNode, Path, Result) then Result := ADefault; end; function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer; begin if not TryGetAttr(RootNode, Path, Result) then Result := ADefault; end; function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64; begin if not TryGetAttr(RootNode, Path, Result) then Result := ADefault; end; function TXmlConfig.GetAttr(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double; begin if not TryGetAttr(RootNode, Path, Result) then Result := ADefault; end; function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: UTF8String): Boolean; var Node: TDOMNode; Attr: TDOMAttr; NodePath, AttrName: DOMString; begin SplitPathToNodeAndAttr(Path, NodePath, AttrName); if NodePath <> EmptyWideStr then begin Node := FindNode(RootNode, NodePath, False); if not Assigned(Node) then Exit(False); end else Node := RootNode; Attr := TDOMElement(Node).GetAttributeNode(AttrName); Result := Assigned(Attr); if Result then AValue := UTF16ToUTF8(Attr.Value); end; function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean; var sValue: UTF8String; begin Result := TryGetAttr(RootNode, Path, sValue); if Result then begin if SameText(sValue, 'TRUE') then AValue := True else if SameText(sValue, 'FALSE') then AValue := False else Result := False; // If other text then return not found. end; end; function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean; var sValue: UTF8String; begin Result := TryGetAttr(RootNode, Path, sValue) and TryStrToInt(sValue, AValue); end; function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean; var sValue: UTF8String; begin Result := TryGetAttr(RootNode, Path, sValue) and TryStrToInt64(sValue, AValue); end; function TXmlConfig.TryGetAttr(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean; var sValue: UTF8String; begin Result := TryGetAttr(RootNode, Path, sValue) and TryStrToFloat(sValue, AValue); end; function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: UTF8String): UTF8String; var Node: TDOMNode; begin Node := FindNode(RootNode, Path, False); if Assigned(Node) then Result := UTF16ToUTF8(Node.TextContent) else Result := ADefault; end; function TXmlConfig.IsEmpty: Boolean; begin Result := RootNode.ChildNodes.Count = 0; end; function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Boolean): Boolean; var sValue: UTF8String; begin sValue := GetValue(RootNode, Path, ''); if SameText(sValue, 'TRUE') then Result := True else if SameText(sValue, 'FALSE') then Result := False else Result := ADefault; end; function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Integer): Integer; begin Result := StrToIntDef(GetValue(RootNode, Path, ''), ADefault); end; function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Int64): Int64; begin Result := StrToInt64Def(GetValue(RootNode, Path, ''), ADefault); end; function TXmlConfig.GetValue(const RootNode: TDOMNode; const Path: DOMString; const ADefault: Double): Double; begin Result := StrToFloatDef(GetValue(RootNode, Path, ''), ADefault); end; function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: UTF8String): Boolean; var Node: TDOMNode; begin Node := FindNode(RootNode, Path, False); Result := Assigned(Node); if Result then AValue := UTF16ToUTF8(Node.TextContent); end; function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Boolean): Boolean; var sValue: UTF8String; begin Result := TryGetValue(RootNode, Path, sValue); if Result then begin if SameText(sValue, 'TRUE') then AValue := True else if SameText(sValue, 'FALSE') then AValue := False else Result := False; // If other text then return not found. end; end; function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Integer): Boolean; var sValue: UTF8String; begin Result := TryGetValue(RootNode, Path, sValue) and TryStrToInt(sValue, AValue); end; function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Int64): Boolean; var sValue: UTF8String; begin Result := TryGetValue(RootNode, Path, sValue) and TryStrToInt64(sValue, AValue); end; function TXmlConfig.TryGetValue(const RootNode: TDOMNode; const Path: DOMString; out AValue: Double): Boolean; var sValue: UTF8String; begin Result := TryGetValue(RootNode, Path, sValue) and TryStrToFloat(sValue, AValue); end; // ---------------------------------------------------------------------------- procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: String); var Node: TDOMNode; begin Node := RootNode.AppendChild(FDoc.CreateElement(ValueName)); Node.TextContent := UTF8ToUTF16(AValue); end; procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Boolean); begin if AValue <> DefaultValue then AddValue(RootNode, ValueName, AValue); end; procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Double); begin if AValue <> DefaultValue then AddValue(RootNode, ValueName, AValue); end; procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Int64); begin if AValue <> DefaultValue then AddValue(RootNode, ValueName, AValue); end; procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: Integer); begin if AValue <> DefaultValue then AddValue(RootNode, ValueName, AValue); end; procedure TXmlConfig.AddValueDef(const RootNode: TDOMNode; const ValueName: DOMString; const AValue, DefaultValue: String); begin if AValue <> DefaultValue then AddValue(RootNode, ValueName, AValue); end; procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Boolean); begin AddValue(RootNode, ValueName, BoolStrings[AValue]); end; procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Integer); begin AddValue(RootNode, ValueName, IntToStr(AValue)); end; procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Int64); begin AddValue(RootNode, ValueName, IntToStr(AValue)); end; procedure TXmlConfig.AddValue(const RootNode: TDOMNode; const ValueName: DOMString; const AValue: Double); begin AddValue(RootNode, ValueName, FloatToStr(AValue)); end; procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: UTF8String); var Node: TDOMNode; NodePath, AttrName: DOMString; begin SplitPathToNodeAndAttr(Path, NodePath, AttrName); if NodePath <> EmptyWideStr then begin Node := FindNode(RootNode, NodePath, True); TDOMElement(Node)[AttrName] := UTF8ToUTF16(AValue); end else TDOMElement(RootNode)[AttrName] := UTF8ToUTF16(AValue); end; procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean); begin SetAttr(RootNode, Path, BoolStrings[AValue]); end; procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer); begin SetAttr(RootNode, Path, IntToStr(AValue)); end; procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64); begin SetAttr(RootNode, Path, IntToStr(AValue)); end; procedure TXmlConfig.SetAttr(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double); begin SetAttr(RootNode, Path, FloatToStr(AValue)); end; procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: String); var Node: TDOMNode; begin Node := FindNode(RootNode, Path, True); Node.TextContent := UTF8ToUTF16(AValue); end; procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Boolean); begin SetValue(RootNode, Path, BoolStrings[AValue]); end; procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Integer); begin SetValue(RootNode, Path, IntToStr(AValue)); end; procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Int64); begin SetValue(RootNode, Path, IntToStr(AValue)); end; procedure TXmlConfig.SetValue(const RootNode: TDOMNode; const Path: DOMString; const AValue: Double); begin SetValue(RootNode, Path, FloatToStr(AValue)); end; // ---------------------------------------------------------------------------- procedure TXmlConfig.ReadFromFile(const AFilename: UTF8String); var FileStream: TStream; TmpDoc: TXMLDocument; begin FileStream := TFileStreamEx.Create(AFilename, fmOpenRead or fmShareDenyWrite); try if FileStream.Size = 0 then raise EXmlConfigEmpty.Create(''); ReadXMLFile(TmpDoc, FileStream, FilenameToURI(AFilename)); if TmpDoc.DocumentElement.NodeName <> ApplicationName then raise EXMLReadError.Create('Root element is not <' + ApplicationName + '>.'); FDoc.Free; FDoc := TmpDoc; finally FileStream.Free; end; end; procedure TXmlConfig.ReadFromStream(AStream: TStream); var TmpDoc: TXMLDocument; begin if AStream.Size = 0 then raise EXmlConfigEmpty.Create(''); ReadXMLFile(TmpDoc, AStream); FDoc.Free; FDoc := TmpDoc; end; procedure TXmlConfig.WriteToFile(const AFilename: UTF8String); var FileStream: TStream; begin FileStream := TFileStreamEx.Create(AFilename, fmCreate or fmShareDenyWrite); try WriteXMLFile(FDoc, FileStream); finally FileStream.Free; end; end; procedure TXmlConfig.WriteToStream(AStream: TStream); begin WriteXMLFile(FDoc, AStream); end; function TXmlConfig.Load: Boolean; begin Result := False; if FFileName = '' then Exit; if not mbFileExists(FileName) then raise EXmlConfigNotFound.Create(''); if not mbFileAccess(FileName, fmOpenRead or fmShareDenyWrite) then raise EFOpenError.Create(SysErrorMessage(GetLastOSError)); ReadFromFile(FileName); Result := True; end; function TXmlConfig.LoadBypassingErrors: Boolean; var ErrMsg: String; begin try Result := Load; except on e: Exception do begin ErrMsg := 'Error loading configuration file ' + FileName; if e.Message <> EmptyStr then ErrMsg := ErrMsg + ': ' + e.Message; DebugLogger.DebugLn(ErrMsg); Result := False; end; end; end; function TXmlConfig.Save: Boolean; var sTmpConfigFileName: String; begin Result := False; if FFileName = '' then Exit; // Write to temporary file and if successfully written rename to proper name. if (not mbFileExists(FileName)) or mbFileAccess(FileName, fmOpenWrite or fmShareDenyWrite) then begin sTmpConfigFileName := GetTempName(FileName); try WriteToFile(sTmpConfigFileName); if not mbRenameFile(sTmpConfigFileName, FileName) then begin mbDeleteFile(sTmpConfigFileName); DebugLogger.Debugln('Cannot save configuration file ', FileName); end else Result := True; except on e: EStreamError do begin mbDeleteFile(sTmpConfigFileName); DebugLogger.Debugln('Error saving configuration file ', FileName, ': ' + e.Message); end; end; end else begin DebugLogger.Debugln('Cannot save configuration file ', FileName, ' - check permissions'); end; end; procedure TXmlConfig.SplitPathToNodeAndAttr(const Path: DOMString; out NodePath: DOMString; out AttrName: DOMString); var AttrSepPos: Integer; begin // Last part of the path is the attr name. AttrSepPos := Length(Path); while (AttrSepPos > 0) and (Path[AttrSepPos] <> '/') do Dec(AttrSepPos); if (AttrSepPos = 0) or (AttrSepPos = Length(Path)) then begin NodePath := EmptyWideStr; AttrName := Path; end else begin NodePath := Copy(Path, 1, AttrSepPos - 1); AttrName := Copy(Path, AttrSepPos + 1, Length(Path) - AttrSepPos); end; end; function TXmlConfig.AddNode(const RootNode: TDOMNode; const ValueName: DOMString): TDOMNode; begin Result := RootNode.AppendChild(FDoc.CreateElement(ValueName)); end; procedure TXmlConfig.DeleteNode(const RootNode: TDOMNode; const Path: DOMString); begin DeleteNode(FindNode(RootNode, Path, False)); end; procedure TXmlConfig.DeleteNode(const Node: TDOMNode); begin if Assigned(Node) and Assigned(Node.ParentNode) then Node.ParentNode.DetachChild(Node); end; procedure TXmlConfig.ClearNode(const Node: TDOMNode); var Attr: TDOMAttr; begin while Assigned(Node.FirstChild) do Node.RemoveChild(Node.FirstChild); if Node.HasAttributes then begin Attr := TDOMAttr(Node.Attributes[0]); while Assigned(Attr) do begin TDOMElement(Node).RemoveAttributeNode(Attr); Attr := TDOMAttr(Attr.NextSibling); end; end; end; function TXmlConfig.FindNode(const RootNode: TDOMNode; const Path: DOMString; bCreate: Boolean = False): TDOMNode; var StartPos, EndPos: Integer; PathLen: Integer; Child: TDOMNode; function CompareDOMStrings(const s1, s2: DOMPChar; l1, l2: integer): integer; var i: integer; begin Result:=l1-l2; i:=0; while (i '/') do Inc(EndPos); Child := Result.FirstChild; while Assigned(Child) and not ((Child.NodeType = ELEMENT_NODE) and (0 = CompareDOMStrings(DOMPChar(Child.NodeName), @Path[StartPos], Length(Child.NodeName), EndPos-StartPos))) do Child := Child.NextSibling; if not Assigned(Child) and bCreate then begin Child := FDoc.CreateElementBuf(@Path[StartPos], EndPos-StartPos); Result.AppendChild(Child); end; Result := Child; StartPos := EndPos + 1; if StartPos > PathLen then Break; end; end; function TXmlConfig.GetContent(const Node: TDOMNode): UTF8String; begin Result := UTF16ToUTF8(Node.TextContent); end; procedure TXmlConfig.SetContent(const Node: TDOMNode; const AValue: UTF8String); begin Node.TextContent := UTF8ToUTF16(AValue); end; function TXmlConfig.GetPathFromNode(aNode: TDOMNode): String; begin Result := aNode.NodeName; aNode := aNode.ParentNode; while Assigned(aNode) and (aNode <> RootNode) do begin Result := aNode.NodeName + '/' + Result; aNode := aNode.ParentNode; end; end; procedure TXmlConfig.GetFont(const aNode: TXmlNode; Path: TXmlPath; out Name: UTF8String; out Size: Integer; out Style: Integer; const DefName: UTF8String; const DefSize: Integer; const DefStyle: Integer); begin if Path <> '' then Path := Path + '/'; Name := GetValue(aNode, Path + 'Name', DefName); Size := GetValue(aNode, Path + 'Size', DefSize); Style := GetValue(aNode, Path + 'Style', DefStyle); end; procedure TXmlConfig.SetFont(const aNode: TXmlNode; Path: TXmlPath; const Name: UTF8String; const Size: Integer; const Style: Integer); begin if Path <> '' then Path := Path + '/'; SetValue(aNode, Path + 'Name', Name); SetValue(aNode, Path + 'Size', Size); SetValue(aNode, Path + 'Style', Style); end; end.