Menu

Home

Victor

Welcome to your wiki!

This is the default page, edit it as you see fit. To add a new page simply reference it within brackets, e.g.: [SamplePage].

The wiki uses Markdown syntax.

Project Admins:


Discussion

  • Victor

    Victor - 2012-10-18

    XMLFile

    XMLFile is a Lazarus/Free Pascal unit, which being added to an application allows creating and maintaining configuration data. It works similar to INI files with richer set of features provided by underlying XML format.

    Note: this module is not compatible with Delphi because DOM implementations in Delphi and Lazarus/Free Pascal are different.

    In order to use XMLFile in the project the file "XMLFile.pas" should be copied into location, where the Pascal compiler could find it (along with project's other .pas or .pp files, for example) and included in units "uses" clause, where it required.
    It provides a class called "TXMLFile" with properties and methods discussed below.

    The main difference between XMLFile and XMLConfig, found in Lazarus, is that XMLFile keeps values in text nodes while XMLConfig saves them as attributes. Using separate nodes for values allows saving complex structures inside configuration files, examples include icons, multi-line lists and texts, SQL statements and even complete XML or HTML documents.

    Public properties

    FileName: string;
    - full path + file name of the XML configuration file on the disk.

    ReadOnly: boolean;
    - a flag which prevents modification of the external file on disk. The in-memory copy is still changeable, regardless of the state of this flag.

    ReadError: boolean;
    - internal flag indicating status of the last read or write operation. For "read" operations it indicates if data where obtained from the file or substituted by the default value. For writing it is set to true if disk error occurred.

    SectionDelimiter: char;
    (default is ".") - a character, which separates tag (section) names, when accessing nested tags. obviously, it must not be found in tag names.

    Public methods

    constructor Create(const FileName: string; const topNodeName : string; readOnly : boolean = true);
    

    Creates an instance of TXMLFile class and reads the content of the file from disk. Parameter "FileName" is a full path to the existing file or the file you are about to create. If the file does not exist, the empty XML structure with a root node named as specified by "topNodeName" is created in memory and with "ReadOnly = false" the attempt to write it to the disk is made, otherwise the parameter "topNodeName" is just ignored. If a new file cannot be written onto disk or provided path is not accessible, then "ReadOnly" is set to "true" automatically.

    Example (for MS Windows)

    var
      xConf : TXMLFile;
    ....
     // read or create local per-user config file
    xConf := TXMLFile.Create(GetEnvironmentVariable('LOCALAPPDATA') +
                ChangeFileExt(ExtractFileName(ParamStr(0)), '.cfg'),
            'Config', false);
    

    Example (for Linux)

    var
      xConf : TXMLFile;
    ....
     // read or create local per-user config file
    xConf := TXMLFile.Create(GetAppConfigFile(False), 'Config', false);
    
    function ReadString(aSection, aKey : string; aDefault : string = emptyString) : string;
    

    This is the main method to read values, as XML is stored in a text file. In order to read the value "Ctrl+O" from the structure shown below

    <?xml version="1.0"?> 
    <Configuration> 
      <ShortCuts> 
        <frmMain> 
          <amActionList> 
            <!--menu File --> 
            <actOpen>Ctrl+O</actOpen> 
          </amActionList> 
        </frmMain> 
      </ShortCuts> 
    </Configuration>
    

    the parameters "aSection" and "aKey" must be as follows:

    aSection := 'ShortCuts.frmMain.amActionlist'; 
    aKey := 'actOpen';
    

    assuming that SectionDelimiter is ".".
    Note that root node "Configuration" is not included.

    The full code for reading action shortcuts is show below:

    procedure LoadActionShortCuts(amList: TActionList); 
    var 
      secName : string; 
      i : integer; 
      scText : string; 
    begin 
      secName := 'ShortCuts' + gConf.SectionsDelimiter + 
                 amList.Owner.Name + 
                 gConf.SectionsDelimiter + amList.Name; 
      if gConf.SectionExists(secName) then 
        begin 
          for i := 0 to amList.ActionCount - 1 do 
            begin 
              scText := gConf.ReadString(secName, 
                        amList.Actions[i].Name, emptyString); 
              if not Empty(scText) then 
                try // trying to prevent crash if shortcut text is invalid 
                  TAction(amList.Actions[i]).ShortCut := 
                     TextToShortCut(scText); 
                except 
      { // debugging, for production don't do anything  
                  on E : exception do 
                    ShowMessage('Invalid Shortcut for ' + 
                                amList.Actions[i].Name + 
                                ': ' + scText); 
      }
                end; 
            end; 
        end; 
    end;
    

    The following methods use ReadString internally and just convert the output value to the appropriate format:

    function ReadInteger(aSection, aKey : string; iDefault : integer = 0) : integer;
    
    function ReadFloat(aSection, aKey : string; rDefault : double = 0) : double;
    
    function ReadBoolean(aSection, aKey : string; bDefault : boolean = false; yesValue : string = '1') : boolean;
    

    Function ReadBoolen has an extra parameter to define what text is considered as "Yes" or "True" value (the default is "1"), any other text value will return "False".

    function ReadBinaryStream(aSection, aKey : string; aStream : TStream) : integer;
    

    Function ReadBinaryStream allows reading any data into memory buffer. It employs internal Text-to-Hex conversion. The variable of TStream descendant class must be created before this function called.

    The similar set for writing data into XML config file shown below.

    procedure WriteString(aSection, aKey, aValue : string; suspend : boolean = false);
    

    WriteString is the main method, used internally by most of the others. The extra parameter "suspend" governs flushing to the disk behaviour. If it is set to "False" (default) the data is written to the physical file immediately (unless the file is read-only), otherwise changes remained in memory buffer. Note, that any "write" operation re-writes the whole XML document on the disk, this ensures that no changes lost, but may cause quite high disk load, if the program saves many changes at once.

    procedure WriteInteger(aSection, aKey : string; aValue : integer; suspend : boolean = false);
    
    procedure WriteFloat(aSection, aKey : string; aValue : double; suspend : boolean = false);
    
    procedure WriteBoolean(aSection, aKey : string; aValue : boolean;
                           yesValue : string = '1'; noValue : string = '0'; suspend : boolean = false);
    

    WriteBoolean has two extra parameters to define text representing both "True" and "False" values.

    procedure WriteBinaryStream(aSection, aKey : string; aStream : TStream);
    

    Below is the very simplified example of using WriteBinaryStream to save an icon from TImage into XML configuration file

    procedure SaveImage(img : TImage);
    var
      aStream : TMemoryStream;
    begin
      aStream := TMemoryStream.Create;
      try
        img..Picture.Icon.SaveToStream(aStream);
        xConf.WriteBinaryStream('Image', 'icon', aStream);
       finally
         aStream.Free;
        end;
     end;
    

    Method DeleteKey removes the key from XML configuration file. Note, if the "key" has any child nodes, they will be removed as well.

    procedure DeleteKey(aSection, aKey : string; suspend : boolean = false);
    

    The following is the set of methods to deal with attributes, they are self-explanatory.

    function ReadAttribute(aSection, aKey, aAttribute : string; aDefault : string = emptyString) : string;
    
    procedure WriteAttribute(aSection, aKey, aAttribute : string; aValue : string; suspend : boolean = false);
    
    procedure DeleteAttribute(aSection, aKey, aAttribute : string; suspend : boolean = false);
    

    Some extra methods to utilize XML capabilities

    procedure ListChildren(aSection : string; sChildren : TStrings);
    

    Lists all child nodes in a TSrings descendant.

    function SectionExists(aSection : string) : boolean;
    

    Below is two methods to implement "frequently used" or "recently opened" lists.

    procedure AddToList(aSection, aKey : string; aValue : string; capacity : integer = 0);
    
    procedure ReadList(aSection, aKey : string; aList : TStrings);
    

    Procedure AddToList creates, if necessary, and adds aValue on top of the list. Then it checks for and removes duplicate values, and trims the list to a given capacity (default is 9 elements). It also saves the capacity as an attribute of the node. The resulting list has a LIFO structure, last added element has 0 index.

     

    Last edit: Victor 2012-11-28
  • sfexplorer

    sfexplorer - 2021-04-10

    The 1034 and 1035 setups won't install, in XP.

    I was able to install one of the prior versions, 1028, in XP. I don't know about 32 or any of the others.

    Very impressive. Love it. Is that the entire source, in xmlfile, and uxmlconfig for delphi?

    Please tell me how to donate.

     
  • Victor

    Victor - 2021-04-10

    Hi,
    Due to the fact, that Windows XP is not supported for several years already, I cannot guaranty that later versions can work on it. It can even be the installer (Inno Setup) failing to run on XP. Also, I have stopped making 32 bit setups since version 28. The whole project source is provided in xxx_source.tar.gz files (can be open with 7 Zip, for example). It is not a Delphi project, but Lazarus-Free Pascal one; its internal implementation of XML support is different from Delphi, hence the two different units to play with XML for Delphi (uXMLConfig.pas) and Lazarus (XMLFile.pas). The project itself cannot be easily translated into Delphi, but can compile for both Windows and Linux with Lazarus without any source code modifications; I think 32 bit would compile too.
    Or you can try installing a later version on another PC and copying XMLTreeEdit.exe to XP machine: I suppose it can run OK on its own without installation.

    I don't expect any donations for this project and I don't have any facilities for that. Thank you for the offer. This project is my small contribution to the world of Open Source software.

     
  • sfexplorer

    sfexplorer - 2021-04-11

    Yep, I understand. Thanks, Victor.

     
  • Jim Wilson

    Jim Wilson - 2022-09-11

    Shortcut key "Ctrl-End" does not Expand All and shortcut key "Ctrl-Home" does not Collapse All. The menu function works but not the indicated shortcut keys. I have installed on Win10 and Win11 with same results.

     
  • Victor

    Victor - 2022-09-12

    Hi Jim,
    Yes, these shortcuts don't work on Windows 10 anymore; it looks like Windows blocks Ctrl or any other modifier key completely. They work on Linux and I also found, that Ctrl++ and Ctrl+- (plus and minus on the numeric pad) work as well. Maybe these combinations would work on Windows, I will check tomorrow.

     
  • Victor

    Victor - 2022-09-12

    Hi Jim,
    Apparently, all keyboard shortcuts are stored in XMLTreeEdit.cfg file, located in the same folder where the executable file is (for Windows). You can open it with XMLTreeEdit and edit to you taste. The Help file contains more details.

     
  • Jim Wilson

    Jim Wilson - 2022-09-13

    Thank you for that quick reply. It was very helpful.

    I tested several Ctrl-key combinations but instead settled on F11/F12 to get it done. I most strongly recommend this as a solution for future releases.

    <actCollapse>F11</actCollapse>
    <actExpand>F12</actExpand>
    
     

Log in to post a comment.