1
0
mirror of https://github.com/moparisthebest/Simba synced 2024-08-13 16:53:59 -04:00
Simba/trunk/Units/MMLAddon/settings.pas
Wizzup? 0d67409787 Default OS, Arch -> Default.
Ben, don't pass -WG to the project. We NEED that terminal on Windows for any writeln. If you add -WG to the project, all the writeln's will fail, because they aren't bound to any terminal, and they will tell us they have no file to write to.
Small "fix" in settings.

Cheers :-) 




git-svn-id: http://www.villavu.com/repositories/merlijn/mufasa@447 3f818213-9676-44b0-a9b4-5e4c4e03d09d
2010-01-21 22:43:48 +00:00

620 lines
13 KiB
ObjectPascal

{
This file is part of the Mufasa Macro Library (MML)
Copyright (c) 2009 by Raymond van Venetië and Merlijn Wajer
MML 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 3 of the License, or
(at your option) any later version.
MML 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 MML. If not, see <http://www.gnu.org/licenses/>.
See the file COPYING, included in this distribution,
for details about the copyright.
Settings class for Mufasa Macro Library
}
unit settings;
{$mode objfpc}{$M+}
interface
uses
Classes, SysUtils, ComCtrls, xmlread, xmlwrite, DOM;
type
// remove later
TStringArray = Array Of String;
TSettingData = class(TObject)
public
Val: String;
constructor Create;
destructor Destroy; override;
end;
{
TMMLSettings; class to manage settings with XML.
Features:
- Loading and Saving to XML.
- Showing the settings as a tree.
- Changing, Creating settings. (No Deleting yet.)
- Bad naming conventions.
Bugs:
- Don't use '/' as a *name* for a node. It WILL fuck up.
It is no problem in values, but in NAMES for nodes, it will
simply not work.
- Poor naming.
}
TMMLSettings = class(TObject)
public
constructor Create(aNodes: TTreeNodes);
destructor Destroy; override;
private
Nodes: TTreeNodes;
function KeyNameToKeys(KeyName: String): TStringArray;
function WalkToNode(KeyName: String): TTreeNode;
procedure InternalLoadFromXML(XMLDoc: TXMLDocument);
procedure WriteXMLData(n: TTreeNode;
XMLNode: TDOMNode; XMLDoc: TXMLDocument;
var XMLChild: TDOMNode; var C: Integer);
procedure WalkTree(Node: TTreeNode; XMLNode: TDOMNode; XMLDoc: TXMLDocument;
var C: Integer);
public
function GetNodePath(Node: TTreeNode): String;
function ListKeys(KeyName: String): TStringArray;
function KeyExists(KeyName: String): Boolean;
function IsKey(KeyName: String): Boolean;
function IsDirectory(KeyName: String): Boolean;
procedure SetKeyValue(KeyName: String; KeyValue: String);
function CreateKey(KeyName: String; CreatePath: Boolean = False): Boolean;
function GetKeyValue(KeyName: String): String;
// Horrible name
function GetSetDefaultKeyValue(KeyName, defVal: String): String;
private
// /facepalm
function GetSetLoadSaveDefaultKeyValue(KeyName, defVal, fileName: String): String;
public
// AAAAAAAAAAAHG??
function GetSetLoadSaveDefaultKeyValueIfNotExists(KeyName, defVal, fileName: String): String;
public
procedure LoadFromXML(fileName: String);
procedure SaveToXML(fileName: String);
end;
implementation
uses
strutils;
constructor TSettingData.Create;
begin
inherited;
Val := '';
end;
destructor TSettingData.Destroy;
begin
Val := '';
inherited;
end;
constructor TMMLSettings.Create(aNodes: TTreeNodes);
begin
Self.Nodes := aNodes;
end;
destructor TMMLSettings.Destroy;
var
i : integer;
begin
for i := 0 to Nodes.Count - 1 do
if Nodes[i].data <> nil then
TSettingData(Nodes[i].Data).Free;
Nodes := nil;
inherited;
end;
procedure TMMLSettings.InternalLoadFromXML(XMLDoc: TXMLDocument);
var
iNode: TDOMNode;
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);
var
cNode: TDOMNode;
s: string;
d: TSettingData;
begin
if Node = nil then Exit; // Stops if reached a leaf
// Adds a node to the tree
s := Node.NodeName;
TreeNode := Nodes.AddChild(TreeNode, s);
if (Node.NodeType = 3) then
begin
d := TSettingData.Create;
D.Val := Node.NodeValue;
TreeNode.Data := D;
TreeNode.Text := Node.NodeValue;
end;
// Goes to the child node
cNode := Node.FirstChild;
// Processes all child nodes
while cNode <> nil do
begin
ProcessNode(cNode, TreeNode);
cNode := cNode.NextSibling;
end;
end;
begin
iNode := XMLDoc.DocumentElement;
while iNode <> nil do
begin
ProcessNode(iNode, nil); // Recursive
iNode := iNode.NextSibling;
end;
end;
function TMMLSettings.KeyNameToKeys(KeyName: String): TStringArray;
// yay for SRL!
function srl_Explode(str, del: string): TStringArray;
var
i, l, dL: Integer;
begin
i := 0;
l := -1;
SetLength(Result, 0);
if (str = '') then
Exit;
dL := Length(del) - 1;
repeat
Inc(l);
SetLength(Result, l + 1);
i := Pos(del, str);
if i <= 0 then
Break;
Result[l] := Copy(str, 1, i - 1);
Delete(str, 1, i + dL);
until false;
Result[l] := Copy(str, 1, Length(str));
end;
begin
Result := srl_Explode(KeyName, '/');
end;
function TMMLSettings.WalkToNode(KeyName: String): TTreeNode;
var
N: TTreeNode;
i: Integer;
S: TStringArray;
begin
Result := nil;
if KeyName[length(KeyName)]='/' then setlength(KeyName,length(KeyName)-1);
S := KeyNameToKeys(KeyName);
if not assigned(s) then
Exit(nil);
N := Nodes.GetFirstNode;
i := 0;
while N <> nil do
begin
if N.Text = s[i] then
begin
inc(i);
if i = length(s) then
break;
N := N.GetFirstChild;
end else
N := N.GetNextSibling;
end;
Result := N;
end;
{
Return the "path" of the given Node.
The node should be in Nodes. (TreeView.Items)
}
function TMMLSettings.GetNodePath(Node: TTreeNode): String;
var
N: TTreeNode;
s: TStringArray;
i: Integer;
begin
if Node = nil then
Exit('');
N := Node;
setlength(s, 0);
while N <> nil do
begin
setlength(s,length(s) + 1);
s[high(s)] := N.Text;
N := N.Parent;
end;
result := '';
for i := high(s) downto 0 do
result := result + s[i] + '/';
if length(result) > 1 then
setlength(result,length(result)-1);
end;
{
Equivalent to 'ls' or 'dir'. It lists the keys in a certain key (directory)
}
function TMMLSettings.ListKeys(KeyName: String): TStringArray;
var
N: TTreeNode;
i: Integer;
S: TStringArray;
begin
SetLength(Result, 0);
N := WalkToNode(KeyName);
if N <> nil then
N := N.GetFirstChild;
while N <> nil do
begin
setlength(result,length(result)+1);
result[high(result)] := N.Text;
N := N.GetNextSibling;
end;
end;
{
Return wether the given key exists or not
}
function TMMLSettings.KeyExists(KeyName: String): Boolean;
begin
Result := WalkToNode(KeyName) <> nil;
end;
{
Return wether the given key is a key. (again, bad naming)
What I mean is, a 'key' only has a 'Value', which a 'directory key' has other
keys (or none) as childs.
}
function TMMLSettings.IsKey(KeyName: String): Boolean;
var
N: TTreeNode;
begin
N := WalkToNode(KeyName);
if N = nil then
Exit(False);
n := N.GetFirstChild;
while n <> nil do
begin
if n.HasChildren then
exit(false);
n := N.GetNextSibling;
end;
exit(true);
end;
{
Perhaps this should just do Exit(not IsKey(KeyName))
}
function TMMLSettings.IsDirectory(KeyName: String): Boolean;
var
N: TTreeNode;
begin
N := WalkToNode(KeyName);
if N <> nil then
Exit(N.HasChildren);
Exit(False);
end;
{
Get the value of a Key. (String)
}
function TMMLSettings.GetKeyValue(KeyName: String): String;
var
N: TTreeNode;
begin
if not KeyExists(KeyName) then
Exit('');
N := WalkToNode(KeyName);
if N <> nil then
N := N.GetFirstChild;
while N <> nil do
begin
if assigned(n.Data) then
begin
Exit(TSettingData(n.Data).Val);
end;
N := N.GetNextSibling;
end;
Exit('');
end;
{
If the key exists - return the value.
If it does not exist, create the key - with a possible path, set it to
defVal and return defVal.
}
function TMMLSettings.GetSetDefaultKeyValue(KeyName, defVal: String): String;
var
Res: String;
begin
if not IsKey(KeyName) then
begin
CreateKey(KeyName, True);
SetKeyValue(KeyName, defVal);
exit(defVal);
end;
Res := GetKeyValue(KeyName);
if Res = '' then
begin
SetKeyValue(KeyName, defVal);
exit(defVal);
end;
Exit(Res);
end;
{
Clear the entire tree. Load from fileName. call GetSetDefaultKeyValue.
}
function TMMLSettings.GetSetLoadSaveDefaultKeyValue(KeyName, defVal, fileName: String): String;
begin
Nodes.Clear;
LoadFromXML(fileName);
Result := GetSetDefaultKeyValue(KeyName, defVal);
SaveToXML(fileName);
end;
{
If Key exists, call getSetDefaultKeyValue, else call GetSetLoadSaveDefaultKeyValue
}
function TMMLSettings.GetSetLoadSaveDefaultKeyValueIfNotExists(KeyName, defVal, fileName: String): String;
begin
if KeyExists(KeyName) then
Exit(GetSetDefaultKeyValue(KeyName, defVal))
else
Exit(GetSetLoadSaveDefaultKeyValue(KeyName, defVal, fileName));
end;
{
Create the given key. If CreatePath = true, then create every key that is
required to create the key. (Say KeyName = 'a/b/c/d/e' and only key a exists,
and CreatePath = True, then b,c,d and e are all created.
}
function TMMLSettings.CreateKey(KeyName: String; CreatePath: Boolean = False): Boolean;
var
N, newN, nParent: TTreeNode;
Path: TStringArray;
NewPath: String;
i: Integer;
begin
if KeyExists(KeyName) then
begin
Exit(False);
end;
NewPath := '';
N := nil;
nParent := Nodes.GetFirstNode;
Path := KeyNameToKeys(KeyName);
if length(path) < 2 then
begin
writeln('Path too short!');
exit(false);
end;
if path[0] <> nParent.Text then
begin
writeln('First key doesn''t match. First key should always match');
exit(false);
end;
for i := 0 to length(Path) - 2 do
begin
if Path[i] = '' then
begin
writeln('Invalid Key Path / Name');
exit(false);
end;
NewPath := NewPath + Path[i] + '/';
N := WalkToNode(NewPath);
if (N = nil) and (not CreatePath) then
exit(false);
if (N = nil) and CreatePath then
begin
newN := TTreeNode.Create(Nodes);
newN.Text := Path[i];
if (nParent = nil) then
begin
writeln('This shouldn''t happen...');
newN.MoveTo(Nodes.GetFirstNode, naAddChild);
nParent := newN;
end
else
begin
newN.MoveTo(nParent, naAddChild);
nParent := newN;
end;
end;
if N <> nil then
nParent := N;
end;
if nParent = nil then
exit(false);
newN := TTreeNode.Create(Nodes);
newN.Text := Path[High(Path)];
newN.MoveTo(nParent, naAddChild);
end;
{
Set the value of a key.
}
procedure TMMLSettings.SetKeyValue(KeyName: String; KeyValue: String);
var
N, NN: TTreeNode;
begin
if not KeyExists(KeyName) then
begin
writeln('SetKeyValue - Key does not exist');
Exit;
end;
if not IsKey(KeyName) then
begin
writeln('SetKeyValue - IsKey returned false');
Exit;
end;
N := WalkToNode(KeyName);
if not N.HasChildren then
begin
NN := TTreeNode.Create(Nodes);
NN.Text := KeyValue;
NN.MoveTo(N, naAddChild);
end;
if n <> nil then
N := N.GetFirstChild;
while N <> nil do
begin
if Assigned(N.Data) then
TSettingData(N.Data).Free;
N.Data := TSettingData.Create;
TSettingData(N.Data).Val := KeyValue;
writeln('Setting ' + KeyName + ' to ' + KeyValue);
N := N.GetNextSibling;
end;
end;
{ load from xml }
procedure TMMLSettings.LoadFromXML(fileName: String);
var
Doc: TXMLDocument;
begin
Nodes.Clear;
if not fileExists(fileName) then
begin
writeln('KANKER');
// create file.
SaveToXML(fileName);
end;
ReadXMLFile(Doc, fileName);
InternalLoadFromXML(Doc);
Doc.Free;
end;
procedure TMMLSettings.WriteXMLData(n: TTreeNode;
XMLNode: TDOMNode; XMLDoc: TXMLDocument;
var XMLChild: TDOMNode; var C: Integer);
var
DDataNode, DataNode: TDOMNode;
begin
if assigned(n.data) and (n.HasChildren) then
writeln('Has data and children! Please close simba and remove settings.xml. if problem persists, please report your settings.xml');
if assigned(n.Data) then
begin
XMLChild := XMLDoc.CreateTextNode(TSettingData(N.Data).Val);
end else
begin
XMLChild := XMLDoc.CreateElement(n.Text);
end;
Inc(C);
XMLNode.AppendChild(XMLChild);
end;
procedure TMMLSettings.WalkTree(Node: TTreeNode; XMLNode: TDOMNode; XMLDoc: TXMLDocument;
var C: Integer);
var
N: TTreeNode;
XMLChild: TDOMNode;
begin
N := Node.GetFirstChild;
while assigned(n) do
begin
WriteXMLData(n, XMLNode, XMLDoc, XMLChild, C);
WalkTree(n, XMLChild, XMLDoc, C);
n := n.GetNextSibling;
end;
end;
{ save to xml }
procedure TMMLSettings.SaveToXML(fileName: String);
var
XMLDoc: TXMLDocument;
RootNode: TDOMNode;
C: Integer;
begin
XMLDoc := TXMLDocument.Create;
RootNode := XMLDoc.CreateElement('Settings');
XMLDoc.AppendChild(RootNode);
RootNode := XMLDoc.DocumentElement;
C := 0;
if Nodes.GetFirstNode <> nil then
WalkTree(Nodes.GetFirstNode, RootNode, XMLDoc, C);
try
WriteXMLFile(XMLDoc, fileName);
except
Writeln('Failed to write ' + fileName);
end;
XMLDoc.Free;
end;
end.