///////////////////////////////////////////////////////////////////////////////
// CM2Tree.pas
// -----------
// This unit contains CML/XML tree used in CoolMon 2
//
// Last Edit: June 8th, 2006
// Certified: NA
//
// This unit is coded in : Delphi 7
//
// Unit is copyright (c) 2003 - 2006, The CoolMon Project
////////////////////////////////////////////////////////////////////////////////
// This unit is specific to CML Version 1.4
////////////////////////////////////////////////////////////////////////////////
// What's new
//
// 080606 - RGB color support added
// 251205 - Dismantleself has been changed to run recursive downward to ensure
//          everything below is freed up
// 240205 - Added Backup coordinates to Form for -NoResMove Option
// 060904 - Added VisualID lookup table
// 160804 - Implemented CMLX
// 260704 - Added Comment tag
//
////////////////////////////////////////////////////////////////////////////////



unit CM2Tree;

interface

Uses
  SysUtils,
  xmldom,
  XMLIntf,
  msxmldom,
  XMLDoc,
  Variants,
  Forms,
  Classes,
  Filter,
  Graphics,
  CM2FormManager,
  CM2FormUnit,
  CM2VariableManager,
  CM2VisPlugTypes,
  CM2GenPlugTypes,
  CM2PlugInTypes,
  CM2CMLXPlugTypes,
  CM2CMLXInterface,
  Dialogs, verbose,
  CM2EventTypes;

Type
 ECMLError = Class(Exception);
 TAttrib = Record
  Name: String;
  Value: String;
 End;
 TAttribList = Class(TObject)
 Private
  fList: Array of TAttrib;
  function GetCount: Integer;
  function GetAttrib(Index: Integer): TAttrib;
  procedure SetAttrib(Index: Integer; const Value: TAttrib);
 public
  Procedure Add(Name, Value: String);
  Procedure Delete(Index: Integer);
  Property Count: Integer read GetCount;
  Property Attrib[Index: Integer]: TAttrib read GetAttrib write SetAttrib; default;
  Procedure Clear;
 end;
 TCM2Tree = Class;
 TNodeType = (ntUndefined, ntForm, ntVisual, ntSensor, ntText, ntParameterBlock,
              ntParameter, ntContent, ntCML, ntComment, ntCMLX, ntEvents,
              ntEventHandler, ntAction, ntExecute, ntReserved, nt_Text);
 TRawNode = Class(TObject)
 private
  fNodes: array of TRawNode;
  fParent: TRawNode;
  fStr: String;
  fName: String;
  fCode: String;
  fNodeType: TNodeType;
  fOwner: TCM2Tree;
  fResult: TResult;
  fChildrenCreated: Boolean;
  function GetCount: Integer;
  Function CreateChild(Node: IXMLNode; CM2Parent: TRawNode): TRawNode;
  procedure DoChildren(Node: IXMLNode; CM2Parent: TRawNode); virtual;
  procedure DoChildrenEx(Node: IXMLNode; CM2Parent: TRawNode); virtual;
  procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); Virtual;
  procedure DismantleSelf; virtual;
 Protected
  function Get_Node(Index: Integer): TRawNode;
  procedure SetNode(Index: Integer; const Value: TRawNode);
 public
  Attribs: TAttribList;
  Constructor Create(Owner: TCM2Tree; Parent: TRawNode);
  Destructor Destroy; Override;
  Function Add(NodeType: TNodeType): TRawNode;
  Procedure Delete(Index: Integer);
  Property Node[Index: Integer]: TRawNode read Get_Node write SetNode; Default;
  Property Count: Integer read GetCount;
  Property Parent: TRawNode read fParent;
  Property Str: String read fStr write fStr;
  Property Name: String read fName write fName;
  Property Code: String read fCode write fCode;
  Property Result: TResult read fResult write fResult;
  Property NodeType: TNodeType read fNodeType write fNodeType;
  Property Owner: TCM2Tree read fOwner;
  Procedure LoadFromFile(Filename: String);
  Procedure LoadFromStrings(Str: String); virtual;
  Procedure Process; virtual;
  Procedure ProcessChildren; virtual;
  Procedure ProcessParameters; virtual;
  Procedure OnEvent(Sender: TObject); virtual;
  Function GetPath: String;
  Function GetNode(Path: String; Var Node: TRawNode): Boolean;
  Function ReInitSafeMode: Boolean; virtual;
  Procedure Clear;
  Property ChildrenCreated: Boolean read fChildrenCreated write fChildrenCreated;
  Function WriteXML: String; virtual;
 end;
 PRawNode = ^TRawNode;
 TCM2Tree = Class(TObject)
 private
  fRoot: TRawNode;
  fFormMgr: PCM2FormMgr;
  fVarMgr: PVarMgr;
  fGenPlugInList: PGenPlugInList;
  fVisPlugInList: PVisPlugInList;
  fCMLXPlugInList: PCMLXPlugInList;
 public
  Constructor Create(FormMgr: PCM2FormMgr; VarMgr: PVarMgr; GenPlugInList: PGenPlugInList; VisPlugInList: PVisPlugInList; CMLXPlugInList: PCMLXPlugInList);
  Destructor Destroy; override;
  Property Root: TRawNode read fRoot write fRoot;
  Property VarMgr: PVarMgr read fVarMgr;
  Property FormMgr: PCM2FormMgr read fFormMgr;
  Property GenPlugInList: PGenPlugInList read fGenPlugInList;
  Property VisPlugInList: PVisPlugInList read fVisPlugInList;
  Property CMLXPlugInList: PCMLXPlugInList read fCMLXPlugInList;
 end;
 TTextNode = Class(TRawNode)
 private
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
 public
  Procedure Process; override;
  Function WriteXML: String; override;
 end;
 TFormNode = Class(TRawNode)
 private
  fForm: PCM2Form;
  fSafeModeName: String;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); Override;
  Procedure DismantleSelf; override;
 public
  Property Form: PCM2Form read fForm write fForm;
  Property SafeModeName: String read fSafeModeName write fSafeModeName;
  Procedure Process; override;
  Procedure OnParameterUpdate(Sender: TObject);
  Function ReInitSafeMode: Boolean; override;
  Function WriteXML: String; override;
 end;
 PFormNode = ^TFormNode;
 TVisualNode = Class(TRawNode)
 private
  fLastFormNode: PFormNode;
  fVisObject: PVisObject;
  fSafeModeName: String;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); Override;
 public
  Property LastFormNode: PFormNode read fLastFormNode write fLastFormNode;
  Property VisObject: PVisObject read fVisObject write fVisObject;
  Property SafeModeName: String read fSafeModeName write fSafeModeName;
  Procedure Process; override;
  Procedure ProcessChildren; override;
  Procedure OnParameterUpdate(Sender: TObject);
  Function ReInitSafeMode: Boolean; override;
  Procedure OnEvent(Sender: TObject); override;
  Function WriteXML: String; override;
 end;
 TParameterNode = Class(TRawNode)
 private
  fCalcMethod: String;
 public
  Procedure Process; override;
  Property CalcMethod: String read fCalcMethod write fCalcMethod;
  Procedure Calc;
  Procedure OnEvent(Sender: TObject); override;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
  Function WriteXML: String; override;
 End;
 TParameterBlockNode = Class(TRawNode)
 private
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
 public
  Procedure Process; override;
  Procedure OnEvent(Sender: TObject); override;
  Procedure ProcessParameters; override;
 end;
 TSensorNode = Class(TRawNode)
 private
  fVarItem: PVarItem;
  fSafeModeName: String;
  fParameterStr: String;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); Override;
  Procedure DismantleSelf; override;
 public
  Property VarItem: PVarItem read fVarItem write fVarItem;
  Property SafeModeName: String read fSafeModeName write fSafeModeName;
  Property ParameterStr: String read fParameterStr write fParameterStr;
  Procedure Process; override;
  Procedure OnEvent(Sender: TObject); override;
  Procedure OnParameterUpdate(Sender: TObject);
  Function ReInitSafeMode: Boolean; override;
  Function WriteXML: String; override;
 End;
 TCMLXNode = Class(TRawNode)
 private
  AttribList: String;
    fTagName: String;
  Parameters: TParameterBlockNode;
  procedure DoChildren(Node: IXMLNode; CM2Parent: TRawNode); override;
  procedure DoChildrenEx(Node: IXMLNode; CM2Parent: TRawNode); override;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); Override;
 public
  Property TagName: String read fTagName;
  Procedure Process; override;
  Procedure ProcessChildren; override;
  Procedure OnEvent(Sender: TObject); override;
  Procedure OnParameterUpdate(Sender: TObject);
  Procedure LoadFromStrings(Str: String); override;
  Function WriteXML: String; override;
 end;
 TContentNode = Class(TRawNode)
 public
  Procedure Process; override;
  Procedure OnEvent(Sender: TObject); override;
 End;
 TCMLNode = Class(TRawNode)
 private
  fVersion: String;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
    procedure SetVersion(const Value: String);
 public
  Property Version: String read fVersion write SetVersion;
 end;
 TCommentNode = Class(TRawNode)
 private
    fCommentStr: String;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
 public
  Property CommentStr: String read fCommentStr;
  Function WriteXML: String; override;
 end;
 TEventsNode = Class(TRawNode)
 public
  Procedure OnEvent(Sender: TObject); override;
 end;
 TEventHandlerNode = Class(TRawNode)
 private
  fEventType: TEventType;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
 public
  Property EventType: TEventType read fEventType;
  Procedure ProcessChildren; override;
  Procedure ProcessParameters; override;
  Procedure OnEvent(Sender: TObject); override;
  Function WriteXML: String; override;
 end;
 TActionNode = Class(TRawNode)
 private
  fVisualTarget: String;
  fModKeys: TModifierKeyState;
  fpTrigger: PTrigger;
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
 public
  Property Trigger: PTrigger read fpTrigger;
  Property ModifierKeys: TModifierKeyState read fModKeys;
  Property VisualTarget: String read fVisualTarget;
  Procedure OnParameterUpdate(Sender: TObject);
  Procedure ProcessChildren; override;
  Procedure OnEvent(Sender: TObject); override;
  Function WriteXML: String; override;
 end;
 TExecuteNode = Class(TRawNode)
 private
  Procedure SetupSelf(Node: IXMLNode; CM2Parent: TRawNode); override;
  Procedure Calc;
 public
  Procedure OnEvent(Sender: TObject); override;
  Function WriteXML: String; override;
  Procedure Process; override;
 end;

Var
 VisObjs: Integer = 0;
 FormObjs: Integer = 0;
 SensorObjs: Integer = 0;
 RelationToDesktop: Boolean = true;

implementation

Function ColorToRGB(Color: TColor): String;
Var
 s: String;
 RS, GS, BS: String;
begin
 Result := '$FFFFFF';
 S := ColorToString(Color);
 If length(S) = 0 then exit;
 If S[1] = '$' Then begin
  BS := Copy(S, 4, 2);
  GS := Copy(S, 6, 2);
  RS := Copy(S, 8, 2);
  Result := '$' + RS + GS + BS;
 End
  else
 begin
  If uppercase(copy(S, 1, 2)) = 'CL' then begin
   Result := copy(S, 3, length(s));
  end;
 end;
end;

Function RGBToColor(RGB: String): TColor;
Begin
 Result := clWhite;
 If length(RGB) = 0 then exit;
 If RGB[1] = '$' then begin
  If length(RGB) = 7 then begin
   // RGB
   RGB := '$00' + Copy(RGB, 6, 2) + Copy(RGB, 4, 2) + Copy(RGB, 2, 2);
  end;
  // ABGR is not checked because it basically should be converted later
 end
  else
 begin
  // Color name
  If uppercase(copy(rgb, 1, 2)) <> 'CL' then begin
   RGB := 'cl' + RGB;
  end;
 end;
 Try
  Result := StringToColor(RGB);
 except
  on EConvertError do begin
   Result := clWhite;
  end;
 end;
End;

{ TAttribList }

procedure TAttribList.Add(Name, Value: String);
begin
 SetLength(fList, Length(fList) + 1);
 fList[Length(fList) - 1].Name := Name;
 fList[Length(fList) - 1].Value := Value;
end;

procedure TAttribList.Clear;
begin
 SetLength(fList, 0);
end;

procedure TAttribList.Delete(Index: Integer);
Var
 I : Integer;
begin
 If Index >= Length(fList) Then exit;
 For I := Index to Length(fList) - 2 do begin
  fList[I] := fList[I + 1];
 End;
 setLength(fList, Length(fList) - 1);
end;

function TAttribList.GetAttrib(Index: Integer): TAttrib;
begin
 Result.Name := '';
 Result.Value := '';
 If Index >= Length(fList) Then exit;
 result := fList[Index];
end;

function TAttribList.GetCount: Integer;
begin
 Result := Length(fList);
end;

procedure TAttribList.SetAttrib(Index: Integer; const Value: TAttrib);
begin
 If Index >= Length(fList) Then exit;
 fList[Index] := Value;
end;

{ TRawNode }

Function NTToStr(NT: TNodeType): String;
// Convert a TNodetype to string
Begin
 // Set default output value to undetermined, this value will remain in effect
 // if the NT isn't triggered in the case statement
 Result := 'Undetermined';
 Case NT of
  ntCML: Result := 'CML';
  ntForm: Result := 'Form';
  ntSensor: Result := 'Sensor';
  ntVisual: Result := 'Visual';
  ntText: Result := 'Text';
  ntParameterBlock: Result := 'Parameter Block';
  ntParameter: Result := 'Parameter';
  ntContent: Result := 'Content';
  ntComment: Result := 'Comment';
  ntCMLX: Result := 'CMLX';
  ntEvents: Result := 'Events';
  ntEventHandler: Result := 'EventHandler';
  ntAction: Result := 'Action';
  ntExecute: Result := 'Execute';
 End;
End;

function TRawNode.Add(NodeType: TNodeType): TRawNode;
// Add a node based on the NodeType passed as parameter
Var
 Sel: Integer;
begin
 If Debug Then Form4.Add('TRawNode.Add [' + NTToStr(Nodetype) + ']', dlcore);
 // Get the node count
 Sel := Length(fNodes);
 // Increase the array length by one
 SetLength(fNodes, Sel + 1);
 // Determine what type of node is desired and create the accordingly
 Case NodeType of
  ntCML: fNodes[Sel] := TCMLNode.Create(Owner, Self);
  ntForm: fNodes[Sel] := TFormNode.Create(Owner, Self);
  ntSensor: fNodes[Sel] := TSensorNode.Create(Owner, Self);
  ntVisual: fNodes[Sel] := TVisualNode.Create(Owner, Self);
  ntText: fNodes[Sel] := TTextNode.Create(Owner, Self);
  ntParameterBlock: fNodes[Sel] := TParameterBlockNode.Create(Owner, Self);
  ntParameter: fNodes[Sel] := TParameterNode.Create(Owner, Self);
  ntContent: fNodes[Sel] := TContentNode.Create(Owner, Self);
  ntComment: fNodes[Sel] := TCommentNode.Create(Owner, self);
  ntCMLX: fNodes[Sel] := TCMLXNode.Create(Owner, self);
  ntEvents: fNodes[Sel] := TEventsNode.Create(Owner, Self);
  ntEventHandler: fNodes[Sel] := TEventHandlerNode.Create(Owner, Self);
  ntAction: fNodes[Sel] := TActionNode.Create(Owner, Self);
  ntExecute: fNodes[Sel] := TExecuteNode.Create(Owner, Self);
 else
  // If not found - Create as Undefined (which creates a raw node)
  fNodes[Sel] := TRawNode.Create(Owner, Self);
 End;
 // Set the result as the node we just created
 Result := fNodes[Sel];
 Result.NodeType := NodeType;
end;

procedure TRawNode.Clear;
// This routine should clear (delete) all subnodes
// IN ALL LIKELYHOOD THIS ROUTINE DOESN'T WORK
Var
 I: Integer;
begin
 If Debug Then Form4.Add('TRawNode.Clear ... ', dlcore);
 // Delete all nodes objects in the array
 For I := 0 to self.Count - 1 do begin
  sElf.fNodes[0].Free;
 End;
 // Resize the array to zero
 Setlength(Self.fNodes, 0);
 If Debug Then Form4.Append('done', dlcore);
end;

constructor TRawNode.Create(Owner: TCM2Tree; Parent: TRawNode);
// Create the node
begin
 If Debug Then Form4.Add('TRawnode.Create ... ', dlcore);
 // Set "parent"
 Self.fParent := Parent;
 // Set "Owner"
 Self.fOwner := Owner;
 // Create the attriblist
 Self.Attribs := TAttribList.Create;
 // Set the nodetype - since it's a raw node it will be defined as "undefined"
 Self.fNodeType := ntUndefined;
 If Debug Then Form4.Append('done', dlcore);
end;

procedure TRawNode.Delete(Index: Integer);
// Delete the node at Index
Var
 I: Integer;
begin
 If Debug Then Form4.Add('TRawNode.Delete (#' + IntToStr(Index) + ') ... ', dlcore);
 // IF index is a larger number than possible then exit
 If Index >= Self.Count Then Exit;
 // Reposition the entire array below "Index"
 For I := index to Self.Count - 2 do begin
  fNodes[I] := fNodes[I + 1];
 End;
 // Resize the array to one less
 SetLength(fNodes, Length(fNodes) - 1);
 If Debug Then Form4.Append('done', dlcore);
end;

destructor TRawNode.Destroy;
// Destroy the node
Var
 I: integer;
begin
 If Debug Then Form4.Add('TRawNode.Destroy ... ', dlcore);
 // Release all memory in related classes
 DismantleSelf;
 // Free all subnodes
 For I := 0 to Count - 1 do begin
  FreeAndNil(fNodes[I]);
 End;
 // Free the attrib list
 FreeAndNil(Attribs);
 If Debug Then Form4.Append('done', dlcore);
 inherited;
end;

function TRawNode.CreateChild(Node: IXMLNode;
  CM2Parent: TRawNode): TRawNode;
// Create a child to the current node
Var
 I: Integer;
 NT: TNodeType;
 PCMLX: PCMLXPlugIn; 
begin
 If Debug Then Form4.Add('TRawNode.CreateChild [' + Node.NodeName + ']', dlcore);
 // Determine Child type
 NT := ntUndefined;
 If Uppercase(Node.NodeName) = 'CML' Then NT := ntCML;
 If uppercase(Node.NodeName) = 'FORM' Then NT := ntForm;
 If Uppercase(Node.NodeName) = 'VISUAL' then NT := ntVisual;
 If Uppercase(Node.NodeName) = 'TEXT' Then NT := ntText;
 If Uppercase(Node.NodeName) = 'CONTENT' then NT := ntContent;
 If Uppercase(Node.NodeName) = 'PARAMETERS' then NT := ntParameterBlock;
 If Uppercase(Node.NodeName) = 'SENSOR' then NT := ntSensor;
 If Uppercase(Node.NodeName) = 'EVENTS' then NT := ntEvents;
 If Uppercase(Node.NodeName) = 'EVENTHANDLER' then NT := ntEventHandler;
 If Uppercase(Node.NodeName) = 'ACTION' then NT := ntAction;
 If Uppercase(Node.NodeName) = 'EXECUTE' then NT := ntExecute;
 If Uppercase(Node.NodeName) = '#TEXT' then NT := nt_Text;
 If CM2Parent.NodeType = ntParameterBlock then begin
  If CM2Parent.Parent.NodeType <> ntCMLx then NT := ntParameter;
  // cmlX is using ParameterBlock as it's parameter tree, but it doesn't have
  // parameter nodes below, it has "real" nodes. The parameterblock is used
  // to give the CMLx node 2 event calls.
 end;
 If Node.NodeType = XMLIntf.ntComment then NT := ntComment;
 If NT = ntUndefined then begin
  // Check if the word is in the reserved word list
  For I := 0 to length(Reservedwordlist) - 1 do begin
   If uppercase(Node.NodeName) = uppercase(ReservedWordList[I]) then begin
    NT := ntReserved;
    break;
   end;
  end;
  // Still not recognized, scan cmlX list
  If NT = ntUndefined then begin
   If Self.fOwner.fCMLXPlugInList.GetPlugInByTag(Node.NodeName, PCMLX) then NT := ntCMLX;
  end;
 end;
// If NT <> nt_Text then begin
  // Create Child Type
  Result := CM2Parent.Add(NT);
  // Set nodetype
  Result.NodeType := nt;
  // set nodename
  Result.Name := Node.NodeName;
  // Copy attributes
  if node.AttributeNodes.Count > 0 then begin
   For I := 0 to node.AttributeNodes.Count - 1 do begin
    Result.Attribs.Add(node.AttributeNodes.Nodes[I].NodeName, vartostr(node.AttributeNodes.Nodes[I].NodeValue));
   end;
  end;
  // Setup child
  Result.SetupSelf(Node, CM2Parent);
// end;
end;

procedure TRawNode.DoChildrenEx(Node: IXMLNode; CM2Parent: TRawNode);
// Will create all children under "Node"
Var
 I: Integer;
begin
 If Debug Then begin
  Form4.Add('TRawNode.DoChildrenEx ->', dlcore);
  form4.IndentAdd;
 end;
 // Set the boolean value used to check that the children has been created
 Self.ChildrenCreated := True;
 // Create all childnodes found under "node"
 For I := 0 to Node.ChildNodes.Count - 1 do begin
  DoChildren(Node.ChildNodes[I], CM2Parent);
 end;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TRawNode.DoChildrenEx', dlcore);
 end;
end;

procedure TRawNode.DoChildren(Node: IXMLNode; CM2Parent: TRawNode);
Var
 Raw: TRawNode;
begin
 If Debug Then begin
  Form4.Add('TRawNode.DoChildren ->', dlcore);
  Form4.IndentAdd;
 end;
 Raw := CreateChild(Node, CM2Parent);
 // If the children hasn't been created, do so
 If Raw <> nil then begin
  If Not Raw.ChildrenCreated Then DoChildrenEx(Node, Raw);
 End;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TRawNode.DoChildren', dlcore);
 end;
end;

function TRawNode.Get_Node(Index: Integer): TRawNode;
begin
 Result := Nil;
 If Index >= Self.Count Then Exit;
 Result := fNodes[Index];
end;

function TRawNode.GetCount: Integer;
// Get the count of children nodes
begin
 Result := Length(Self.fNodes);
end;

function TRawNode.GetNode(Path: String; var Node: TRawNode): Boolean;
// Get a TxxxNode at the path eg. "CML\DISPLAY\FORM"
// NB: Is relative to itself, meaning it will NOT look from the topmost node if you provide Leading backslash
Var
 Level, I: Integer;
 AtNode: TRawNode;
 Res: String;
 Found: Boolean;
begin
 If Debug Then Form4.Add('TRawNode.GetNode (' + Path + ') ... ', dlcore);
 Level := 1;
 AtNode := Self;
 Result := false;
 Repeat
  I := 0;
  Found := False;
  Repeat
   If AtNode[I].Name = '#text' then Res := Self.Str
    else Res := AtNode[I].Name;
   If uppercase(GetFilt(Path, Level, '\')) = uppercase(Res) Then Begin
    Inc(Level);
    Found := True;
    AtNode := AtNode[I];
   End;
   Inc(I);
  until (I >= AtNode.Count) or (Found);
  If Not found then begin
   If Debug Then Form4.append('not found', dlcore);
   exit;
  End;
 until Level = CountFilt(Path, '\')+1;
 Node := AtNode;
 If Debug Then Form4.append('done', dlcore);
 Result := true;
end;

function TRawNode.GetPath: String;
// Will provide the string path to self - eg. "CML\DISPLAY\FORM"
Var
 Res: String;
 Node: TRawNode;
begin
 If Debug Then Form4.add('TRawNode.GetPath ... ', dlcore);
 IF self.name = '#text' then Res := Self.Parent.Str
  else Res := Self.Name;
 Node := Self;
 While Node.Parent <> nil do begin
  Node := Node.Parent;
  If node.Name = '#text' then Res := Node.Parent.Str + '\' + Res
   else Res := Node.Name + '\' + Res;
 End;
 If Debug Then Form4.append('Done [' + Res + ']', dlcore);
 Result := Res;
end;

procedure TRawNode.LoadFromFile(Filename: String);
// Allows you to load a file in a substructure
// For example you can use this in the rootnode to load in a CML file
// then after that you can use it again to load another file in one of the subnodes
var
 XML: TXMLDocument;
 Form: TForm;
 I: Integer;
begin
 If Debug Then begin
  Form4.Add('TRawNode.LoadFromFile [' + Filename + '] ->', dlcore);
  Form4.IndentAdd;
 end;
 // If the file in "Filename" doesn't exists, exit routine
 If Not fileexists(Filename) then exit;
 // Create dummy form, the XML engine will not work without
 Form := TForm.Create(nil);
 // Create the XML engine, with the form as parent
 XML := TXMLDocument.Create(Form);
 // Set the filename
 XML.FileName := Filename;
 // Load
 XML.Active := True;
 For I := 0 to XML.ChildNodes.Count - 1 do begin
  // Create all root nodes
  DoChildren(XML.ChildNodes[i], self);
 End;
 // Unload
 XML.Active := False;
 // Free the XML Engine
 FreeAndNil(XML);
 // Free the dummy form
 FreeAndNil(Form);
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TRawNode.Loadfromfile', dlcore);
 end;
end;

procedure TRawNode.LoadFromStrings(Str: String);
// Same as Load from file, but loads from TStrings instead
var
 XML: TXMLDocument;
 Form: TForm;
 I: Integer;
begin
 If Debug Then begin
  Form4.Add('TRawNode.LoadFromStrings ->', dlcore);
  Form4.IndentAdd;
 End;
 Form := TForm.Create(nil);
 XML := TXMLDocument.Create(Form);
 XML.XML.Text := Str;
 XML.Active := True;
 For I := 0 to XML.ChildNodes.Count - 1 do begin
  DoChildren(XML.ChildNodes[i], self);
 End;
 XML.Active := False;
 XML.Free;
 Form.Free;
 If Debug Then Begin
  Form4.IndentDel;
  Form4.add('Done - TRawNode.LoadFromStrings', dlcore);
 end;
end;

procedure TRawNode.Process;
// Will start a Chain-Reaction providing a combined result for itself or all children depending on the nodetype
begin
 If Debug Then begin
  Form4.Add('TRawNode.Process ->', dlcore);
  Form4.IndentAdd;
 end;
 Self.ProcessChildren;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TRawNode.Process', dlcore);
 end;
end;

procedure TRawNode.ProcessChildren;
// Will create a combined result for all childrens
Var
 Node: TRawNode;
 I: Integer;
 Res: TResult;
 Cnt: Integer;
begin
 If Debug Then Form4.Add('TRawNode.ProcessChildren [' + NTToStr(Self.NodeType) + '] ... ', dlcore);
 // If there is no children exit
 If Length(self.fNodes) = 0 then exit;
 Cnt := 0;
 For I := 0 to self.Count - 1 do begin
  Node := Self.Node[I];
  If Node.NodeType <> ntComment then begin
   Inc(Cnt);
   Node.Process;
   If Cnt = 1 then begin
    // 1st node clause
    Self.Result := Node.Result;
   end
    else
   begin
    // 2nd+ node clause
    if Self.Result.SensorStyle = ssNumeric then begin
     // Block required - otherwise the first numeric data will be discarded
     // because it's only stored in Result.Value not Result.Str
     { --- }
     // Set sensorstyle to NonNumeric
     Res.SensorStyle := ssNonNumeric;
     // Set the string property to a converted float value
     Res.Str := FloatToStr(Self.Result.Value);
     Self.Result := Res;
    end;
    // Convert to NonNumeric
    Res.SensorStyle := ssNonNumeric;
    // Add the result to the previous ones
    Case Node.Result.SensorStyle of
     ssNumeric: begin
                 Res.Str := Self.Result.Str + FloatToStr(Node.Result.Value);
                end;
     ssNonNumeric: Begin
                    Res.str := Self.result.str + Node.Result.Str;
                   end;
    else
     Begin
      if debug then Form4.Add('Node.Result is different that ssNumeric and ssNonNumeric', dlcore, mtwarning);
     end;
    end;
    SElf.Result := Res;
   end;
  end;
 end;
 If Debug Then Form4.Append('done', dlcore);
end;

procedure TRawNode.SetNode(Index: Integer; const Value: TRawNode);
begin
 If Index >= Self.Count Then Exit;
 fNodes[Index] := Value;
end;

procedure TRawNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
// This routine will setup the node to the according nodetype
begin
 // Nothing needs to be setup in the raw version
 Name := Node.NodeName;
end;

procedure TRawNode.OnEvent(Sender: TObject);
// Used when a children has been modified, then it calls the event procedure in
// the parent which will then run it on it's parent etc. until a node that has
// to do with output is triggered. It will then update the output.
begin
 ProcessChildren;
 // Migrate upward
 If assigned(Parent) then Parent.OnEvent(self);
end;

Function TRawNode.ReInitSafeMode: Boolean;
// Due to a pointer bug, which I have been unable to solve for the time being we
// have to reinitialise all pointers before use, so running this procedure will
// reinitialise the pointers for the current node.
// - This system should ofcourse be eliminated ones a correct pointer system has
// been developed. But for now it works with the bypass routine here.
begin
 // - Do nothing -
 // But since the procedure didn't fail (obviously there is nothing that can fail)
 // we need to set true as result, so not to halt program execution
 Result := True;
end;

procedure TRawNode.ProcessParameters;
// Uses to initialise all parameters
Var
 I: Integer;
begin
 // Start chain reaction
 For I := 0 to Self.Count - 1 do begin
  Self.fNodes[I].ProcessParameters;
 End;
end;

function TRawNode.WriteXML: String;
Var
 I: Integer;
begin
 // This routine will write an XML structure of all children, used when passing parameters to CMLX
 Result := '';
 For I := 0 to count - 1 do begin
  Result := Result + fNodes[I].WriteXML;
 end;
 Result := '<' + name + '>' + Result + '</' + name + '>';
end;

procedure TRawNode.DismantleSelf;
Var
 I: Integer;
begin
 // This block will be ran just prior to deleting of the node.
 // You this to free up any links/memory from VarMgr, FormMgr etc.
 // Must run down recursive to ensure full deletion
 For I := 0 to count - 1 do begin
  fNodes[I].DismantleSelf;
 end;
end;

{ TCM2Tree }

constructor TCM2Tree.Create(FormMgr: PCM2FormMgr; VarMgr: PVarMgr;
  GenPlugInList: PGenPlugInList; VisPlugInList: PVisPlugInList; CMLXPlugInList: PCMLXPlugInList);
begin
 If Debug Then begin
  Form4.add('TCM2Tree.Create ->', dlcore);
  Form4.IndentAdd;
 end;
 VisObjs := 0;
 FormObjs := 0;
 SensorObjs := 0;
 Self.fFormMgr := FormMgr;
 Self.fVarMgr := VarMgr;
 Self.fGenPlugInList := GenPlugInList;
 Self.fVisPlugInList := VisPlugInList;
 Self.fCMLXPlugInList := CMLXPlugInList;
 Self.fRoot := TRawNode.Create(Self, nil);
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TCM2Tree.Create', dlcore);
 end;
end;

destructor TCM2Tree.Destroy;
begin
 If Debug Then Form4.Add('TCM2Tree.Destroy ... ', dlcore);
 FreeAndNil(fRoot);
 If Debug Then Form4.Append('done', dlcore);
end;

{ TTextNode }

procedure TTextNode.Process;
// Process node to get a result
Var
 Res: TResult;
begin
 If Debug Then Form4.Add('TTextNode.Process ... ', dlcore);
 // Since the nodetype is text it's always a string
 Res.SensorStyle := ssNonNumeric;
 // Res name is not really needed, but it should be the name of the sensor
 // that provided the result, so we'll set it to ...
 Res.Name := 'CMLEngine12 - TTextNode';
 // Set the result to the string in the node
 Res.Str := Self.fStr;
 Result := Res;
 If Debug Then Form4.Append('done', dlcore);
end;

procedure TTextNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
begin
 If Debug Then Form4.Add('TTextNode.SetupSelf ... ', dlcore);
 // since this node is static load the value right away
 If node.NodeValue = null then Str := ' '
  else Str := VarToStr(Node.NodeValue);
 If Debug Then Form4.Append('done', dlcore);
end;

function TTextNode.WriteXML: String;
begin
 Result := '<text>' + str + '</text>';
end;

{ TFormNode }

procedure TFormNode.DismantleSelf;
begin

end;

procedure TFormNode.OnParameterUpdate(Sender: TObject);
// Triggered when a parameter in the parameter block below has changed
Var
 I: Integer;
 PassOn, N, V: String;
 Handled: Boolean;
begin
 If Debug Then begin
  Form4.Add('TFormNode.OnParameterUpdate ->', dlcore);
  Form4.IndentAdd;
 end;
 // Try to Initialize safemode
 If not ReInitSafeMode then Begin
  If Debug Then begin
   Form4.IndentDel;
   Form4.add('Done - TFormNode.OnParameterUpdate', dlcore);
  end;
  exit;
 End;
 // The entire parameterlist is passed as attributes with sender
 // Set PassOn to be nothing
 PassOn := '';
 For I := 0 to (Sender as TRawNode).Attribs.Count - 1 do begin
  // Define current attrib as not being handled
  Handled := False;
  // Get Attrib name
  N := Uppercase((Sender as TRawNode).Attribs[I].Name);
  // Get Attrib Value
  V := (Sender as TRawNode).Attribs[I].Value;
  If N = 'RIGHT' Then begin
   Self.fForm^.Right := StrToInt(V);
  end;
  If N = 'BOTTOM' Then Begin
   Self.fForm^.Bottom := StrToInt(V);
  End;
  If N = 'LEFT' then Begin
   // Attrib is handled
   Handled := true;
   // Attempt integer conversion of the value
   Try
    Self.fForm^.Left := StrToInt(V);
    Self.fForm^.LeftBackup := StrToInt(V);
   except
    Self.fForm^.Left := 1;
    Self.fForm^.LeftBackup := 1;
   End;
  End;
  If N = 'WIDTH' then begin
   // Attrib is handled
   Handled := true;
   // Attempt integer conversion of the value
   Try
    Self.fForm^.Width := StrToInt(V);
    Self.fForm^.WidthBackup := StrToInt(V);
   except
    Self.fForm^.Width := 50;
    Self.fForm^.WidthBackup := 50;
   End;
  End;
  If N = 'TOP' then begin
   // Attrib is handled
   Handled := true;
   // Attempt integer conversion of the value
   Try
    Self.fForm^.Top := StrToInt(V);
    Self.fForm^.TopBackup := StrToInt(V);
   except
    Self.fForm^.Top := 0;
    Self.fForm^.TopBackup := 0;
   End;
  End;
  If N = 'HEIGHT' then begin
   // Attrib is handled
   Handled := true;
   Try
   // Attempt integer conversion of the value
    Self.fForm^.Height := StrToInt(V);
    Self.fForm^.HeightBackup := StrToInt(V);
   except
    Self.fForm^.Height := 50;
    Self.fForm^.HeightBackup := 50;
   End;
  End;
  If N = 'ALPHA' then begin
   // Attrib is handled
   Handled := true;
   // Attempt integer conversion of the value
   Try
    Self.fForm^.BlendValue := StrToInt(V);
   except
    Self.fForm^.BlendValue := 255;
   End;
  End;
  If N = 'WINDOWMODE' then begin
   // Attrib is handled
   Handled := true;
   If (uppercase(V) = 'NORMAL') or (uppercase(V) = 'UNKNOWN') then Self.fForm^.WindowMode := wmUnknown;
   If (Uppercase(V) = 'PINTODESKTOP') or (Uppercase(V) = 'PTD') Then Self.fForm^.WindowMode := wmPinToDesktop;
   If (Uppercase(V) = 'ALWAYSONTOP') or (Uppercase(V) = 'AOT') then Self.fForm^.WindowMode := wmAOT;
  End;
  If N = 'COLOR' then begin
   // Attrib is handled
   Handled := true;
   // Convert value to a delphi color "ABGR" value
   Try
    Self.fForm^.Color := RGBToColor(V);
   except
    Self.fForm^.Color := clMoneyGreen;
   End;
  End;
  If N = 'TRANSPARENT' then begin
   // Attrib is handled
   Handled := true;
   // If the value is yes set it to true otherwise set it to false
   If uppercase(V) = 'YES' then Self.fForm^.Transparent := True
    else Self.fForm^.Transparent := False;
  End;
  If N = 'TRANSPARENTCOLOR' Then begin
   handled := true;
   Try
    Self.fForm^.TransparentColorValue := RGBToColor(V);
   except
    Self.fForm^.TransparentColorValue := clNone;
   End;
  End;
  If N = 'SHOW' Then Begin
   // Attrib is handled
   Handled := true;
   // If the value is yes set it to true otherwise set it to false
   If (uppercase(V) = 'NO') or (uppercase(V) = 'FALSE') then Self.fForm^.Visible := False
    else Self.fForm^.Visible := True;
   Self.fForm^.ReInitAllDC;
  End;
  If Not Handled Then begin
   // If the attrib hasn't been handled built a list we can pass on
   PassOn := '|' + N + '=' + V;
  End;
 End;
 If Self.fForm^.Right > -1 then Self.fForm^.Right := Self.fForm^.Right;
 If Self.fForm^.Bottom > -1 Then Self.fForm^.Bottom := Self.fForm^.Bottom;
 // Currently there is no way of passing unknown forward
 If Debug Then begin
  Form4.IndentDel;
  Form4.add('Done - TFormNode.OnParameterUpdate', dlcore);
 end;
end;

procedure TFormNode.Process;
begin
 If Debug Then begin
  Form4.Add('TFormNode.Process ->', dlcore);
  Form4.IndentAdd;
 end;
 // Start chain reaction
 Self.ProcessChildren;
 If Debug Then begin
  form4.IndentDel;
  Form4.add('Done - TFormNode.Process', dlcore);
 end;
end;

Function TFormNode.ReInitSafeMode: Boolean;
begin
 // By default function has succeded
 Result := True;
 If Debug Then Form4.Add('TFormNode.ReInitSafeMode ... ', dlcore);
 // Only reload safemode if program is actually running in safemode
 If UseSafeMode Then begin
  // Attempt to reload Form pointer
  If Not Owner.fFormMgr^.GetFormByName(SafeModeName, fForm) Then Begin
//   MessageDlg('Unable to ReInitSafeMode for ' + SafeModeName, mtWarning, [mbOK], 0);
   If Debug Then Form4.Append('failure', dlerror, mtError);
   // Set result to false since we where unable to reload the pointer
   Result := False;
  End
   else
  Begin
   If Debug Then Form4.Append('done', dlcore);
  End;
 End;
end;

procedure TFormNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
// Create the form node
Var
 I: Integer;
 N, V: String;
begin
 If Debug Then begin
  Form4.Add('TFormNode.SetupSelf ->', dlcore);
  form4.IndentAdd;
 end;
 // Create the form
 fForm := Owner.FormMgr^.AddP;
 // Hide it - So we only have the splash screen present
 fForm^.Visible := False;
 // update it so that it does actually hide
 fForm^.Update;
 // If we are running in safemode we need to init the safemode system
 If UseSafeMode Then begin
  If Debug then form4.Add('InitSafeMode ... ', dlcore);
  // Create a unique form name
  SafeModeName := 'Form_' + IntToStr(FormObjs);
  // Increase unique form
  Inc(FormObjs);
  // Safe the name for future use
  fForm^.Name := SafemodeName;
  If Debug then Form4.Append('done', dlcore);
 End;
 // Extract the friendly form name and set it to the caption of the form
 For I := 0 to Attribs.Count - 1 do begin
  N := uppercase(Attribs[I].Name);
  V := Attribs[I].Value;
  If N = 'NAME' Then Begin
   fForm^.Caption := V;
  End;
  If N = 'VISUALID' then begin
   fForm^.VisualID := V;
  end;
 End;
 Self.ProcessChildren;
 If Debug Then begin
  Form4.IndentDel;
  Form4.add('Done - TFormNode.SetupSelf', dlcore);
 end;
end;

function TFormNode.WriteXML: String;
Var
 I: Integer;
begin
 Result := '';
 For I := 0 to Self.Count - 1 do begin
  Result := Result + fNodes[I].WriteXML;
 end;
 Result := '<form name="' + Self.fName + '">' + Result + '</form>';
end;

{ TVisualNode }

procedure TVisualNode.OnEvent(Sender: TObject);
// Triggered when a sensor node in "content" is updated
begin
 Self.ProcessChildren;
end;

procedure TVisualNode.OnParameterUpdate(Sender: TObject);
// Triggered when a sensor node in "Parameters" is updated, for more details on
// how this routine work, look under TFormNode.OnParameterUpdate
Var
 I: Integer;
 PassOn, N, V: String;
 Handled: Boolean;
begin
 If Debug Then begin
  Form4.Add('TVisualNode.OnParameterUpdate ->', dlcore);
  Form4.IndentAdd;
 end;
 PassOn := '';
 For I := 0 to (Sender as TRawNode).Attribs.Count - 1 do begin
  Handled := False;
  N := Uppercase((Sender as TRawNode).Attribs[I].Name);
  V := (Sender as TRawNode).Attribs[I].Value;
  If N = 'LEFT' then Begin
   Handled := true;
   Try
    Self.fVisObject^.Left := StrToInt(V);
   except
    Self.fVisObject^.Left := 1;
   End;
  End;
  If N = 'WIDTH' then begin
   Handled := true;
   Try
    Self.fVisObject^.Width := StrToInt(V);
   except
    Self.fVisObject^.Width := 50;
   End;
  End;
  If N = 'TOP' then begin
   Handled := true;
   Try
    Self.fVisObject^.Top := StrToInt(V);
   except
    Self.fVisObject^.Top := 0;
   End;
  End;
  If N = 'HEIGHT' then begin
   Handled := true;
   Try
    Self.fVisObject^.Height := StrToInt(V);
   except
    Self.fVisObject^.Height := 50;
   End;
  End;
  If Not Handled Then begin
   // if the attrib wasn't handled add it to the unhandled paramters list
   PassOn := PassOn + '|' + N + '=' + V;
  End;
 End;
 If UseSafeMode Then begin
  If not ReInitSafeMode then begin
   If Debug Then begin
    form4.IndentDel;
    Form4.Add('Done - TVisualNode.OnParameterUpdate', dlcore);
   end;
   exit;
  End;
 End;
 // Pass the unhandled parameters forward to the visual plugin
 PassOn := Copy(PassOn, 2, length(PassOn));
 If PassOn <> '' then Self.fVisObject^.ConfigString := PassOn;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TVisualNode.OnParameterUpdate', dlcore);
 end;
end;

procedure TVisualNode.Process;
begin
 If Debug Then begin
  Form4.Add('TVisualNode.Process ->', dlcore);
  Form4.IndentAdd;
 end;
 // Start chain reaction, which creates a result for sensor nodes
 ProcessChildren;
 If Debug Then begin
  Form4.IndentDel;
  Form4.add('Done - TVisualNode.Process', dlcore);
 end;
end;

procedure TVisualNode.ProcessChildren;
Var
 Node: TRawNode;
 I: Integer;
begin
 If Debug Then begin
  Form4.Add('TVisualNode.ProcessChildren [' + NTToStr(Self.NodeType) + '] -> ', dlcore);
  Form4.IndentAdd;
 end;
 If Length(self.fNodes) = 0 then exit;
 For I := 0 to length(self.fNodes) - 1 do begin
  Node := Self.Node[I];
  If Node.NodeType = ntContent then begin
   Node.Process;
   Self.Result := Node.Result;
  End;
 End;
 If UseSafeMode Then begin
  If not ReInitSafeMode then begin
   If Debug Then begin
    Form4.IndentDel;
    Form4.add('Done - TVisualNode.ProcessChildren', dlcore);
   end;
   Exit;
  End;
 End;
 Self.fVisObject^.SetResult(Self.fResult);
 If Debug Then begin
  Form4.IndentDel;
  Form4.add('Done - TVisualNode.ProcessChildren', dlcore);
 end;
end;

Function TVisualNode.ReInitSafeMode: Boolean;
// Reinitialise the pointers used in this node
Var
 R: TRawNode;
begin
 Result := True;
 If Debug Then begin
  Form4.Add('TVisualNode.ReInitSafeMode -> ', dlcore);
  Form4.IndentAdd;
 end;
 // First ensure that we are acutally running is safemode otherwise there is not need to perferm this procedure
 If UseSafeMode then begin
  R := Self;
  // Find parent form node
  While R.Nodetype <> ntForm do begin
   R := R.Parent;
  End;
  // update the pointer location
  fLastFormNode := Addr(R);
  // ReInit safemode on the form and then acquire a pointer to self through the form
  If fLastFormNode^.ReInitSafeMode then fLastFormNode^.fForm^.QueryVisObject(SafeModeName, fVisObject)
   else
  Begin
   If debug then Form4.Add('Unable to ReInitsafe', dlerror, mtError);
   Result := False;
  End;
 End;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TVisualNode.ReInitSafeMode', dlcore);
 end;
end;

procedure TVisualNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
Var
 NNode: TRawNode;
 Continue: Boolean;
 I: Integer;
 N, Na, V: String;
 GUID: TGUID;
 Res: TResult;
 VI: String;
begin
  VI := '';
  If Debug then begin
   Form4.Append('TVisualNode.SetupSelf ->', dlcore);
   Form4.IndentAdd;
  end;
  NNode := CM2Parent;
  If Debug then form4.Add('Find parent form node ... ', dlcore);
  // Find parent Form node
  While (NNode.NodeType <> ntForm) do begin
   If NNode.Parent = nil then break
    else NNode := NNode.Parent;
  end;
  If Debug then form4.append('done', dlcore);
  // If a form node was found set pointer
  If NNode <> nil then fLastFormNode := Addr(NNode);
  If Self.LastFormNode <> nil then begin
   Continue := False;
   // Scan attribs for key values
   For I := 0 to Attribs.Count - 1 do begin
    // Attrib Name
    N := uppercase(Attribs[I].Name);
    // Attrib Value
    V := Attribs[I].Value;
    If N = 'NAME' then begin
     Na := V;
     // The visual name - or rather the name of the visual plugin desired for use
     If Debug then form4.Add('Visual Name = ' +v, dlcore);
     // Continue if a plugin with that name has been found in the visual plugin list
     Continue := Owner.fVisPlugInList^.GetGUID(V, GUID);
    end;
    If N = 'VISUALID' then begin
     VI := V;
    End;
   End;
   // Clean the attribs list, since we use it later for parameter event calling
   For I := 0 to Attribs.Count - 1 do begin
    Attribs.Delete(0);
   End;
   If Continue Then begin
    If Debug then form4.Add('Add visual object to form ... ', dlcore);
    If UseSafeMode Then Begin
     // Acquire pointer to the form using safemodename
     If not Owner.fFormMgr^.GetFormByName(fLastFormNode^.SafeModeName, fLastFormNode^.fForm) then begin
      MessageDlg('Unable to reload pointer in safemode, for ' + fLastFormNode^.SafeModeName, mtWarning, [mbOK], 0);
     End;
    End;
    // Create/Add visual object using the GUID for the Vis plugin desire
    fVisObject := fLastFormNode^.fForm^.AddVisObject(GUID);
    If Debug then form4.Add('Load Attribs', dlcore);
{    For I := 0 to Attribs.Count do begin
     N := uppercase(Attribs[I].Name);
     V := Attribs[I].Value;
     // Property methods, yet
    End;}
    fVisObject.VisualID := VI;
    If Debug then form4.Add('Init visual object', dlcore);
    // Give the visual plugin a text output of "Polling..."
    Res.SensorStyle := ssNonnumeric;
    Res.Name := 'CM2';
    Res.Str := 'Polling ...';
    // Update the result
    fVisObject^.SetResult(Res);
    // Invalidate form ... makes windows redraw the form the next time the program is "IDLE"
    // should be a speed gain, because we don't update everytime we create a VO on the form
    fLastFormNode^.fForm^.Invalidate;
    If UseSafeMode Then begin
     // Initialise Safemode system
     If Debug then form4.Add('Init safemode ... ', dlcore);
     // Create a unique safemode name
     SafeModeName := 'VisObj#' + IntToStr(VisObjs);
     // Store the name for future use
     fVisObject^.Name := Safemodename;
     Inc(VisObjs);
     If Debug then form4.append('done', dlcore);
    End;
   End
    else
   begin
    Showmessage('Could not find the Visual plugin requested:' + #13#10 + Na);
    If Debug then form4.Add('Could not find the visual plugin [' + Na + ']', dlplugins, mtWarning);
    Raise ECMLError.Create('Plugin used in configuration [' + Na + '], was not found in plugin list');
   End;
  End;
  // Questionable place to put this line ... but it should cause the VO to update parameters
  SElf.OnParameterUpdate(self);
  If Debug Then begin
   Form4.IndentDel;
   Form4.Add('Done - TVisualNode.SetupSelf', dlcore);
  end;
end;

function TVisualNode.WriteXML: String;
Var
 I: Integer;
begin
 result := '';
 For I := 0 to count - 1 do begin
  Result := Result + fNodes[I].WriteXML;
 end;
 Result := '<visual name="' + fName + '">' + Result + '</visual>';
end;

{ TSensorNode }

procedure TSensorNode.DismantleSelf;
begin
 Self.fOwner.fVarMgr^.Delete(SafeModeName);
end;

procedure TSensorNode.OnEvent(Sender: TObject);
// Called by the Var Manager, when it has updated the sensor
begin
 If Debug Then begin
  Form4.Add('TSensorNode.OnEvent ->', dlcore);
  Form4.IndentAdd;
 end;
 // Gather a result
 SElf.Process;
 Self.ProcessChildren;
 // Alert parent
 Parent.OnEvent(self);
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TSensorNode.OnEvent', dlcore);
 end;
end;

procedure TSensorNode.OnParameterUpdate(Sender: TObject);
// Triggered when a sensor node in the parameter block is updated
Var
 I: Integer;
 PassOn, N, V: String;
begin
 If Debug Then begin
  Form4.Add('TSensorNode.OnParameterUpdate ->', dlcore);
  Form4.IndentAdd;
 end;
 PassOn := '';
 For I := 0 to (Sender as TRawNode).Attribs.Count - 1 do begin
  N := Uppercase((Sender as TRawNode).Attribs[I].Name);
  V := (Sender as TRawNode).Attribs[I].Value;
  PassOn := PassOn + '|' + N + '=' + V;
 End;
 ParameterStr := PassOn;
 If UseSafeMode Then begin
  If not ReInitSafeMode then begin
   If Debug Then begin
    Form4.IndentDel;
    Form4.Add('Done - TSensorNode.OnParameterUpdate', dlcore);
   end;
   Exit;
  End;
 End;
 // Pass parameter list to the Variable manager
 If ParameterStr <> '' then Self.fVarItem^.Parameters := ParameterStr;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TSensorNode.OnParameterUpdate', dlcore);
 end;
end;

procedure TSensorNode.Process;
begin
 If Debug Then begin
  Form4.Add('TSensorNode.Process ->', dlcore);
  Form4.IndentAdd;
 end;
 If UseSafeMode Then begin
  If not ReInitSafeMode then begin
   If Debug Then begin
    Form4.IndentDel;
    Form4.Add('Done - TSensorNode.Process', dlcore);
   end;
  End;
 End;
 Self.Result := Self.fVarItem^.Result;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TSensorNode.Process', dlcore);
 end;
end;

Function TSensorNode.ReInitSafeMode: Boolean;
begin
 Result := True;
 If Debug Then Form4.Add('TSensorNode.ReInitSafeMode ... ', dlcore);
 If UseSafeMode Then Begin
  If not SElf.ChildrenCreated then begin
   Result := False;
   If Debug then Form4.Append('Children not created yet', dlcore, mtWarning);
   exit;
  End;
  Self.VarItem := Owner.fVarMgr^.GetP(SafeModeName);
  If VarItem = Nil then Begin
   Result := False;
//   Showmessage('"' + Self.SafeModeName + '" does not have a corresponding VarItem in the manager');
   If Debug Then Form4.Append('Failure', dlerror, mtError);
  End
   else
  Begin
   If Debug Then Form4.append('done', dlcore);
  End;
 End;
end;

procedure TSensorNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
Var
 Gen: PGenPlugIn;
 I: Integer;
 Update: String;
 SensorId: Integer;
 ID: String;
 dedicated: Boolean;
 aName, N, V: String;
 Temp: Integer;
 Res: TResult;
begin
 If Debug Then begin
  Form4.Add('TSensorNode.SetupSelf ->', dlcore);
  Form4.IndentAdd;
 end;
 // This node needs to create subs nodes before creating itsel
 Self.ChildrenCreated := False;
 SElf.DoChildrenEx(Node, Self);
 Self.ChildrenCreated := true;
 // Main setup code
 Update := '1';
 Gen := nil;
 SensorId := -1;
 Dedicated := false;
 If Debug then form4.Add('Load Attribs', dlcore);
 For I := 0 to Attribs.Count - 1 do begin
  N := uppercase(Attribs[I].Name);
  V := Attribs[I].Value;
  If N = 'NAME' Then Begin
   If Debug then form4.Add('Name = ' + v, dlcore);
   aName := V;
   Name := aName;
  End;
  If N = 'UPDATE' Then begin
   Update := V;
  End;
  If N = 'DEDICATED' Then begin
   If uppercase(V) = 'YES' then Dedicated := True
    else Dedicated := False;
  End;
  If N = 'ID' then begin
   ID := V;
  end;
 End;
 // Clean the attrib list since we use in for parameter event
 For I := 0 to Attribs.Count - 1 do begin
  Attribs.Delete(0);
 End;
 If (CountFilt(aName, '.') = 2) or (aName[1] = '@') then begin
  If CountFilt(aName, '.') = 2 then begin
   Temp := Owner.fGenPlugInList^.IndexOf(GetFilt(aName, 1, '.'));
   If Debug then form4.Add('Get pointer to Plugin', dlcore);
   If Temp > -1 then begin
    For I := 0 to Owner.fGenPlugInList^.GenPlugIn[Temp].SensorList.Count - 1 do begin
     If uppercase(Owner.fGenPlugInList^.GenPlugIn[Temp].SensorList[I].Name) = uppercase(GetFilt(aName, 2, '.')) then begin
      Gen := Owner.fGenPlugInList^.GetPlugInAddr(Temp);
      SensorID := Owner.fGenPlugInList^.GenPlugIn[Temp].SensorList[I].ID;
     end;
    End;
   End
    else
   Begin
    If Debug then form4.Add('unable to locate requested plugin', dlplugins, mtwarning);
    Showmessage('Unable to locate plugin: ' + GetFilt(aName, 1, '.') + #13#10 + 'Quiting ...');
    Raise ECMLError.Create('Plugin used in configuration [' + GetFilt(aName, 1, '.') + '], was not found in plugin list');
    // Program will still run so app.terminate doesn't work, hence the raise command
   End;
   If gen <> nil then begin
    If Debug then form4.Add('Add Variable to variable manager', dlcore);
    fVarItem := Owner.fVarMgr^.AddGenP(aName + '#' + IntToStr(SensorObjs), Update, gen, sensorId, '', dedicated, ID);
    fVarItem^.AddEvent(Self.OnEvent);
    If UseSafeMode Then begin
     If Debug then form4.Add('Init safemode ... ', dlcore);
     SafeModeName := aName + '#' + IntToStr(SensorObjs);
     If Debug then form4.append('done', dlcore);
    End;
    Inc(SensorObjs);
    If Parent.NodeType = ntParameter then Begin
     // init to make sure it has a value
     Res.SensorStyle := ssNumeric;
     Res.Value := 0;
     Self.Result := Res;
    End;
   end;
  end;
  If aName[1] = '@' then begin
   aName := Copy(aName, 2, length(aName));
   fVarItem := Owner.fVarMgr^.AddViaID(aName, fSafeModeName);
   If (fSafeModeName <> '') or (fVarItem <> nil) then begin
    fVarItem^.AddEvent(Self.OnEvent);
   end
    else
   begin
    If Debug then Form4.Add('Could not find the requested linked sensor: @' + aName, dlPlugins, mtwarning);
    Showmessage('Could not find the requested linked sensor: @' + aName);
   end;
  end;
 end
  else
 begin
  If Debug then form4.Add('could not find the requested sensor in the specified plugin', dlplugins, mtwarning);
  Showmessage('Could not find the requested sensor in the specified');
 End;
 SElf.OnParameterUpdate(self);
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TSensorNode.SetupSelf', dlcore);
 end;
end;

function TSensorNode.WriteXML: String;
Var
 I: Integer;
 d, u, s, v: String;
begin
 U := '0';
 If ReInitSafeMode then begin
  If (VarItem^ is TGenItem) then begin
   if (VarItem^ as TGenItem).RunningDedicated then D := 'Yes';
   u := IntToStr((VarItem^ as TGenItem).UpdateFreq);
  end;
  If (VarItem^ is TAdvTimedGenItem) then begin
   D := 'No';
   u := (VarItem^ as TAdvTimedGenItem).UpdateString;
  end;
 end;
 Result := '<sensor source="' + Name + '" update="' + u + '" dedicated="' + d + '"';
 Case self.fResult.SensorStyle of
  ssNumeric: begin
              s := 'numeric';
              v := floatToStr(self.fResult.Value);
             End;
  ssNonNumeric: begin
                 s := 'nonnumeric';
                 v := fResult.Str;
                end;
 end;
 Result := Result + ' style="' + s + '" value="' + v + '">';
 for I := 0 to length(Self.fNodes) - 1 do begin
  Result := Result + fNodes[I].WriteXML;
 end;
 Result := Result + '</sensor>';
end;

{ TParameterNode }

procedure TParameterNode.Calc;
Var
 Res: TResult;
 I: Integer;
 Found: Boolean;
begin
 If Debug Then Form4.Add('TParameterNode.Calc ... ', dlcore);
 // Method - Not sensor type (needs to please the Node.string into the result
 Found := False;
 For I := 0 to count - 1 do begin
  If fNodes[I].NodeType = ntSensor then Begin
   Found := True;
   With (fNodes[I] as TSensorNode) do begin
    If not ReInitSafeMode then begin
     If Debug Then Form4.append('done', dlcore);
     Exit;
    End;
    Result := VarItem^.Result;
   End;
   Result := fNodes[I].Result;
  End;
 End;
 If not found then begin
  Res.SensorStyle := ssNonNumeric;
  Res.Name := 'CMLEngine 1.3';
  Res.Str := Self.fStr;
  Self.Result := res;
 End;
 If Debug Then Form4.append('done', dlcore);
end;

procedure TParameterNode.OnEvent(Sender: TObject);
begin
 If Debug Then begin
  Form4.Add('TParameterNode.OnEvent ->', dlcore);
  Form4.IndentAdd;
 end;
 Calc;
 Parent.OnEvent(Self);
 If Debug Then Begin
  Form4.IndentDel;
  Form4.Add('Done - TParameterNode.OnEvent', dlcore);
 End;
end;

procedure TParameterNode.Process;
begin
 If Debug Then begin
  Form4.Add('TParameterNode.Process ->', dlcore);
  Form4.IndentAdd;
 end;
 Calc;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TParameterNode.Process', dlcore);
 end;
end;

procedure TParameterNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
begin
 // Create children first
 Self.DoChildrenEx(Node, Self);
 // if the parameter is static
 If Node.ChildNodes.Count = 0 then begin
  MessageDlg('Parameter tag has no value', mtWarning, [mbOK], 0);
 End;
 If Node.ChildNodes[0].NodeName = '#text' then begin
  Str := VarToStr(Node.ChildNodes[0].NodeValue);
  Process;
 End;
end;

function TParameterNode.WriteXML: String;
Var
 I: Integer;
begin
 Result := '';
 If Str <> '' then begin
  Result := Self.Str;
 end
  else
 begin
  For I := 0 to count - 1 do begin
   Result := Result + fNodes[I].WriteXML;
  end;
 end;
 Result := '<' + name + '>' + result + '</' + name + '>';
end;

{ TParameterBlockNode }

procedure TParameterBlockNode.OnEvent(Sender: TObject);
Var
 I: Integer;
begin
 If Debug Then begin
  Form4.Add('TParameterBlockNode.OnEvent ->', dlcore);
  Form4.IndentAdd;
 end;
 // Rebuild Property list
 If Debug Then Form4.Add('Rebuild Attribs ... ', dlcore);
 Self.Attribs.Clear;
 For I := 0 to Self.Count - 1 do begin
  Case Node[I].Result.SensorStyle of
   ssNumeric: Attribs.Add(Self.Node[I].fName, FloatToStr(Self.Node[I].fResult.Value));
   ssNonNumeric: Attribs.Add(Self.Node[I].fName, Self.Node[I].fResult.Str);
  End;
 End;
 If Debug Then Form4.append('done', dlcore);
{ If not Parent.ChildrenCreated then begin
  If Debug Then Form4.Add('Parent has not been created yet', mtWarning);
  If Debug Then Form4.Add('Done - TParameterBlockNode.OnEvent');
  Exit;
 End;}
 If (Parent is TFormNode) Then (Parent as TFormNode).OnParameterUpdate(Self);
 If (Parent is TSensorNode) Then (Parent as TSensorNode).OnParameterUpdate(Self);
 If (Parent is TVisualNode) Then (Parent as TVisualNode).OnParameterUpdate(Self);
 If (Parent is TActionNode) Then (Parent as TActionNode).OnParameterUpdate(Self);
 If (Parent is TCMLxNode) Then (Parent as TCMLxNode).OnParameterUpdate(self);
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TParameterBlockNode.OnEvent', dlcore);
 end;
end;

procedure TParameterBlockNode.Process;
begin
 If Debug Then begin
  Form4.Add('TParameterBlockNode.Process ->', dlcore);
  Form4.IndentAdd;
 end;
 ProcessChildren;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('done', dlcore);
 end;
end;

procedure TParameterBlockNode.ProcessParameters;
begin
 // Scan sub nodes for parameterblocks
 inherited;
 // Report back, by triggering an event
 Self.OnEvent(nil);
end;

procedure TParameterBlockNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
begin
 If Debug Then begin
  Form4.Add('TParameterBlockNode.SetupSelf ->', dlcore);
  Form4.IndentAdd;
 end;
 // This node needs to create subs nodes before creating itsel
 Name := 'parameters';
 SElf.DoChildrenEx(Node, Self);
 Self.ChildrenCreated := True;
 // Trigger event to left parameter owner set up, but only if it has been created
 If Parent.ChildrenCreated then Self.OnEvent(Self);
 // Main setup code
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TParameterBlockNode.SetupSelf', dlcore);
 end;
end;

{ TContentNode }

procedure TContentNode.OnEvent(Sender: TObject);
begin
 If Debug Then begin
  Form4.Add('TContentNode.OnEvent ->', dlcore);
  Form4.IndentAdd;
 end;
 Self.Parent.OnEvent(Self);
 If Debug Then begin
  Form4.IndentDel;
  Form4.add('Done - TContentNode.OnEvent', dlcore);
 end;
end;

procedure TContentNode.Process;
begin
 If Debug Then begin
  Form4.Add('TContentNode.Process ->', dlcore);
  Form4.IndentAdd;
 end;
 Self.ProcessChildren;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TContentNode.Process', dlcore);
 end;
end;

{ TCMLNode }

procedure TCMLNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
Var
 I: Integer;
 N, V: string;
begin
 If Debug Then Form4.Add('TCMLNode.SetupSelf ... ', dlcore);
 For I := 0 to Attribs.Count-1 do begin
  N := uppercase(Attribs[I].Name);
  V := Attribs[I].Value;
  If N = 'VERSION' Then Begin
   SetVersion(V);
  End;
 End;
 If Debug Then Form4.Append('done', dlcore);
end;

procedure TCMLNode.SetVersion(const Value: String);
begin
 fVersion := Value;
 If not((fVersion = '1.2') or (fVersion = '1.3') or (fVersion = '1.4')) then begin
  MessageDlg('The CML file reads a different version' + #13#10 + 'than those supported by this core (Version 1.2 & Version 1.3).' + #13#10 + #13#10 + 'Closing CoolMon 2', mtError, [mbOk], 0);
  Application.Terminate;
 End;
end;

{ TCommentNode }

procedure TCommentNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
begin
 If Debug Then Form4.Add('TCommentNode.SetupSelf ... ', dlcore);
 Self.fCommentStr := Node.NodeValue;
 If Debug Then Form4.Append('done', dlcore);
end;

function TCommentNode.WriteXML: String;
begin
 Result := '<!-- ' + Self.CommentStr + ' -->';
end;

{ TCMLXNode }

procedure TCMLXNode.DoChildren(Node: IXMLNode; CM2Parent: TRawNode);
Var
 Raw: TRawNode;
begin
 If Debug Then begin
  Form4.Add('TCMLxNode.DoChildren ->', dlcore);
  Form4.IndentAdd;
 end;
 Raw := CreateChild(Node, CM2Parent);
 If Raw <> nil then begin
  // If the children hasn't been created, do so
  If Not Raw.ChildrenCreated Then DoChildrenEx(Node, Raw);
 end;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TCMLxNode.DoChildren', dlcore);
 end;
end;

procedure TCMLXNode.DoChildrenEx(Node: IXMLNode; CM2Parent: TRawNode);
Var
 I: Integer;
begin
 If Debug Then begin
  Form4.Add('TCMLxNode.DoChildrenEx ->', dlcore, mtError);
  form4.IndentAdd;
 end;
 // Set the boolean value used to check that the children has been created
 Self.ChildrenCreated := True;
 // Create all childnodes found under "node"
 For I := 0 to Node.ChildNodes.Count - 1 do begin
  DoChildren(Node.ChildNodes[I], CM2Parent);
 end;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TCMLxNode.DoChildrenEx', dlcore);
 end;
end;

procedure TCMLXNode.LoadFromStrings(Str: String);
var
 XML: TXMLDocument;
 Form: TForm;
 I: Integer;
 Node: IXMLNode;
begin
 If Debug Then begin
  Form4.Add('TCMLxNode.LoadFromStrings ->', dlcore);
  Form4.IndentAdd;
 End;
 ChildrenCreated := false;
 Form := TForm.Create(nil);
 XML := TXMLDocument.Create(Form);
 XML.XML.Text := Str;
 XML.Active := True;
 Node := nil;
 For I := 0 to XML.ChildNodes.Count - 1 do begin
  If uppercase(XML.ChildNodes[I].Nodename) = 'CML' then begin
   Node := XML.ChildNodes[I];
  end;
 End;
 If Node <> nil then begin
  For I := 0 to node.ChildNodes.Count - 1 do begin
   Self.Parent.DoChildren(Node.ChildNodes[I], Self);
  end;
 end;
 XML.Active := False;
 XML.Free;
 Form.Free;
 If Debug Then Begin
  Form4.IndentDel;
  Form4.add('Done - TCMLxNode.LoadFromStrings', dlcore);
 end;
end;

procedure TCMLXNode.OnEvent(Sender: TObject);
begin
 ProcessChildren;
 Parent.OnEvent(self);
end;

procedure TCMLXNode.OnParameterUpdate(Sender: TObject);
Var
 I: Integer;
 RS, Params: String;
 RT: Byte;
 CMLX: PCMLXPlugin;
 Res: TResult;
begin
 For I := 0 to Parameters.count - 1 do begin
  Parameters[I].Process;
 end;
 Params := Parameters.WriteXML;
 if not Self.Owner.fCMLXPlugInList.GetPlugInByTag(tagname, CMLX) then begin
  If Debug then form4.Add('TCMLXNode.ProcessChildren cannot find required plugin' + #13#10 + 'Tag Name: ' + Tagname + #13#10 + 'There is no such plugin in list', dlplugins);
  exit;
 end;
 If not CMLX^.Perform(AttribList, Params, RT, RS) then begin
  If Debug then Form4.Add(Name + ' plugin''s Perform block failed', dlPlugins);
 end;
 Case RT of
  rtNone: begin
           // Do nothing
          end;
  rtSensor: Begin
             Try
              Res.SensorStyle := ssNumeric;
              Res.Value := StrToFloat(RS);
             except
              on EConvertError do begin
               Res.Str := RS;
               Res.SensorStyle := ssNonNumeric;
              end;
             end;
             Self.Result := Res;
            end;
  rtCML: Begin
          // CMLX
          // * 1. Clear/Destroy current nodes
          //   * Make sure all node types free up their memory on destroy, also
          //     ensure that things left in VarMgr and FormMgr is freed.
          For i := 0 to length(fNodes) - 1 do begin
           fNodes[I].DismantleSelf;
           FreeAndNil(fNodes[I]);
          end;
          SetLength(fNodes, 0);
          // * 2. Load new nodes
          //   * LoadStrings will not work for this, though the DoProcess system
          //     will created the nodes in the fNodes instead of Parameters, it
          //     will run the SetupSelf function. A solution for this will have
          //     to be written, placing it here will do, but LoadStrings was
          //     designed for this exact purpose so maybe placing it there might
          //     not be a bad idea.
          LoadFromStrings(RS);
          // * 3. Run Process on all subnodes
          //   * Seeing as the result nodes are placed in the fNodes, the parent
          //     notes should automaticly pickup the results. As long as the
          //     subnodes are processed.
          Process;
          Parent.OnEvent(self);
          //
          // I'M NOT AT ALL KEEN ON THIS CMLX SYSTEM. Having to destroy all
          // subsnodes everything something in the parameters update seems like
          // it could render this system unuseable because of the loading times.
          // I RECOMMEND A SYSTEM THAT CAN DO SPECIFIC UPDATES INSTEAD OF
          // CLEARING THE ENTIRE SHIT BE DEVELOPED INSTEAD.
         end;
 end;
end;

procedure TCMLXNode.Process;
begin
 Self.ProcessChildren;
end;

procedure TCMLXNode.ProcessChildren;
Begin
 Inherited;
end;

procedure TCMLXNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
Var
 I: Integer;
 S: String;
begin
 If Debug then begin
  Form4.Add('TcmlXNode.SetupSelf [Node: ' + Node.Nodename + '; CM2Parent: ' + CM2Parent.Name + '] -> ', dlcore);
  Form4.IndentAdd;
 end;
 Parameters := TParameterBlockNode.Create(fOwner, Self);
 Parameters.Name := 'cml';
 S := '';
 For I := 0 to Node.AttributeNodes.Count - 1 do begin
  S := S + '|' + Node.AttributeNodes[I].NodeName + '=' + Node.AttributeNodes[I].NodeValue;
 end;
 Self.AttribList := Copy(S, 2, length(S));
 Self.fTagName := Node.NodeName;
 Self.DoChildrenEx(Node, Parameters);
 OnParameterUpdate(self);
 If Debug then begin
  Form4.IndentDel;
  Form4.Append('done - TCMLXNode.SetupSelf', dlcore);
 end;
end;

function TCMLXNode.WriteXML: String;
Var
 I: Integer;
 S: String;
begin
 Result := '<parameters>';
 For I := 0 to parameters.Count - 1 do begin
  Result := Result + Parameters[I].WriteXML;
 end;
 Result := Result + '</parameters><content>';
 For I := 0 to count - 1 do begin
  Result := Result + fNodes[I].WriteXML;
 end;
 Result := Result + '</content>';
 S := '<' + tagname;
 For I := 1 to countfilt(Self.AttribList, '|') do begin
  S := S + ' ' + GetFilt(GetFilt(AttribList, I), 1, '=') + '="' + GetFilt(GetFilt(AttribList, I), 2, '=') + '"';
 end;
 S := S + '>';
 Result := S + Result + '</' + tagname + '>';
end;

{ TEventsNode }

procedure TEventsNode.OnEvent(Sender: TObject);
begin
 // Do Nothing for now
end;

{ TEventHandlerNode }

procedure TEventHandlerNode.OnEvent(Sender: TObject);
begin
 // Some thing has been altered
end;

procedure TEventHandlerNode.ProcessChildren;
begin
 inherited;
 // Don't think something needs to be done in this routine
end;

procedure TEventHandlerNode.ProcessParameters;
begin
 // Load all paramters
 Inherited;
 // Trigger own event
 Self.OnEvent(self);
end;

procedure TEventHandlerNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
Var
 I: Integer;
begin
 Inherited;
 For I := 0 to Node.AttributeNodes.Count - 1 do begin
  If uppercase(Node.AttributeNodes[I].NodeName) = 'TRIGGER' then begin
   Self.fEventType := StrToEventType(Node.AttributeNodes[I].NodeValue);
  end;
 end;
end;

function TEventHandlerNode.WriteXML: String;
Var
 I: Integer;
begin
 // This routine will write an XML structure of all children, used when passing parameters to CMLX
 Result := '';
 For I := 0 to count - 1 do begin
  Result := Result + fNodes[I].WriteXML;
 end;
 Result := '<' + name + ' trigger="' + EventTypeToStr(Self.fEventType) + '">' + Result + '</' + name + '>';
end;

{ TActionNode }

procedure TActionNode.OnEvent(Sender: TObject);
begin
 Parent.OnEvent(self);
end;

procedure TActionNode.OnParameterUpdate(Sender: TObject);
Var
 I: Integer;
 PassOn, N, V: String;
 Handled: Boolean;
begin
 If Debug Then begin
  Form4.Add('TActionNode.OnParameterUpdate ->', dlcore);
  Form4.IndentAdd;
 end;
 PassOn := '';
 For I := 0 to (Sender as TRawNode).Attribs.Count - 1 do begin
  Handled := False;
  N := Uppercase((Sender as TRawNode).Attribs[I].Name);
  V := (Sender as TRawNode).Attribs[I].Value;
  If Not Handled Then begin
   // if the attrib wasn't handled add it to the unhandled paramters list
   PassOn := PassOn + '|' + N + '=' + V;
  End;
 End;
 // SINCE THE FOLLOWING BLOCK ISN'T VALID THIS STYLE MIGHT CAUSE POINTER ERRORS
 // ---------------------------------------------------------------------------
 { If UseSafeMode Then begin
  If not ReInitSafeMode then begin
   If Debug Then Form4.Add('Done - TVisualNode.OnParameterUpdate', dlcore);
   exit;
  End;
 End;}
 // Pass the unhandled parameters forward to the visual plugin
 PassOn := Copy(PassOn, 2, length(PassOn));
 If PassOn <> '' then Self.fpTrigger^.Parameters := PassOn;
 If Debug Then begin
  Form4.IndentDel;
  Form4.Add('Done - TActionNode.OnParameterUpdate', dlcore);
 end;
end;

procedure TActionNode.ProcessChildren;
begin
  inherited;

end;

procedure TActionNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
Var
 I: Integer;
 aNode: TRawNode;
begin
 inherited;
 fVisualTarget := 'self';
 fModKeys := mksNone;
 For I := 0 to Node.AttributeNodes.Count - 1 do begin
  If uppercase(Node.AttributeNodes[I].NodeName) = 'MODIFIER' then Begin
   fModKeys := StrToModState(Node.AttributeNodes[I].NodeValue);
  End;
  If Uppercase(Node.AttributeNodes[I].NodeName) = 'VISUALTARGET' then fVisualTarget := Node.AttributeNodes[I].NodeValue;
 end;
 // Get Eventhandler node
 aNode := Self.Parent;
 // Get Events Node
 aNode := aNode.Parent;
 // Get Form or Visual node
 aNode := aNode.Parent;
 Case aNode.NodeType of
  ntForm: Begin
           Self.fpTrigger := (aNode as TFormNode).fForm.TriggerList.Add((Self.Parent as TEventHandlerNode).fEventType, fModKeys, '', '', VisualTarget);
          End;
  ntVisual: Begin
             Self.fpTrigger := (aNode as TVisualNode).fVisObject.TriggerList.Add((Self.Parent as TEventHandlerNode).fEventType, fModKeys, '', '', VisualTarget);
            End;
  else MessageDlg('Action node appears misplaced, could not find Visual or Form '+#13+#10+'owner', mtWarning, [mbOK], 0);
 End;
end;

function TActionNode.WriteXML: String;
Var
 I: integer;
begin
 Result := '';
 For I := 0 to Self.Count - 1 do begin
  Result := Result + Self.Node[I].WriteXML;
 End;
 Result := '<action modifier="' + ModStateToStr(Self.fModKeys) + '" visualtarget="' + Self.fVisualTarget + '">' + Result + '</action>';
end;

{ TExecuteNode }

procedure TExecuteNode.Calc;
Var
 Res: TResult;
 I: Integer;
 Found: Boolean;
begin
 If Debug Then Form4.Add('TExecuteNode.Calc ... ', dlcore);
 // Method - Not sensor type (needs to please the Node.string into the result
 Found := False;
 For I := 0 to count - 1 do begin
  If fNodes[I].NodeType = ntSensor then Begin
   Found := True;
   With (fNodes[I] as TSensorNode) do begin
    If not ReInitSafeMode then begin
     If Debug Then Form4.append('done', dlcore);
     Exit;
    End;
    Result := VarItem^.Result;
   End;
   Result := fNodes[I].Result;
  End;
 End;
 If not found then begin
  Res.SensorStyle := ssNonNumeric;
  Res.Name := 'CMLEngine 1.3';
  Res.Str := Self.fStr;
  Self.Result := res;
 End;
 If Debug Then Form4.append('done', dlcore);
end;

procedure TExecuteNode.OnEvent(Sender: TObject);
begin
  inherited;
  Calc;
  If (Parent is TActionNode) then (Parent as TActionNode).Trigger^.Execute := Result.Str;
  Parent.OnEvent(self);
end;

procedure TExecuteNode.Process;
begin
 Calc;
end;

procedure TExecuteNode.SetupSelf(Node: IXMLNode; CM2Parent: TRawNode);
begin
 inherited;
 // Create children first
 Self.DoChildrenEx(Node, Self);
 // if the parameter is static
 If Node.ChildNodes.Count = 0 then begin
  MessageDlg('Execute tag has no value', mtWarning, [mbOK], 0);
 End;
 If Node.ChildNodes[0].NodeName = '#text' then begin
  Str := VarToStr(Node.ChildNodes[0].NodeValue);
  Process;
 End;
 Self.OnEvent(self);
end;

function TExecuteNode.WriteXML: String;
Var
 I: Integer;
begin
 Result := '';
 If Str <> '' then begin
  Result := Self.Str;
 end
  else
 begin
  For I := 0 to count - 1 do begin
   Result := Result + fNodes[I].WriteXML;
  end;
 end;
 Result := '<' + name + '>' + result + '</' + name + '>';
end;

end.
