////////////////////////////////////////////////////////////////////////////////
// CM2VisPlugTypes.pas
// -------------------
// This unit contains visual plugin system routines for CoolMon 2.
//
// Last Edit: June, 12th, 2006
// Certified: -
//
// This unit is coded in : Delphi 7
//
// Unit is copyright (c) 2003 - 2006, The CoolMon Project
////////////////////////////////////////////////////////////////////////////////
{$D+}
{$M+}
unit CM2VisPlugTypes;

interface

Uses
 CM2PlugInTypes, // Declares everything general used by both plugin types
 CM2VISInterface, // Declares the interface used for the Visual System
 ComObj, // Com object support unit
 Windows,
 SysUtils,
 dialogs,
 Classes,
 Registry,
 Forms,
 Controls,
 Verbose,
 CM2EditorSupportInterface;

Type
 TDimension = Record // Drawing Dimensions
  X: Integer; // X axis min
  Y: Integer; // Y axis min
  Width: Integer; // x axis Max
  Height: Integer; // Y axis Max
 End;
 TNotifyOfEvent = Record
  DblClick: boolean;
  MouseDownLeft: Boolean;
  MouseDownRight: Boolean;
  MouseDownMiddle: Boolean;
  MouseUpLeft: Boolean;
  MouseUpRight: Boolean;
  MouseUpMiddle: boolean;
  MouseWheelAny: Boolean;
  MouseWheelUp: Boolean;
  MouseWheelDown: Boolean;
  MouseMove: Boolean;
 end;
 TVISObjectList = Class;
 TVISObject = Class(TObject) // Emulates a Visual PlugIn, since we can only load the dll one time, we need an Interface for the Plugin
  private
   fx: Integer; // DC Paint Rect X
   fy: Integer; // DC Paint Rect Y
   fw: word; // DC Paint Rect Width
   fh: word; // DC Paint Rect Height
   fDC: THandle; // DC handle for the paint command (Access via property DC)
   fConfigString: String; // Settings string, used to config the VISObject. (Format is all up to the plugin)
   fName: String;
   fGUID: TGUID;
   fResult: TResult;
   fParent: TVISObjectList;
   fFormHandle: THandle;
   fActive: Boolean;
   FVisualID: String;
   FInterfaceVersion: Byte;
   fNotifyOfEvent: TNotifyOfEvent;
   procedure SetHeight(const Value: Word);
   procedure SetLeft(const Value: Integer);
   procedure SetTop(const Value: Integer);
   procedure SetWidth(const Value: Word);
   procedure SetConfigString(const Value: String);
   Procedure SetDimensions;
   procedure SetVisualID(const Value: String);
   procedure SetInterfaceVersion(const Value: Byte);
   procedure SetNotifyOfEvent(NOE: Int64);
  public
   PlugIn: ICM2VisPlug; // Lies here to allow for hardcoding of Required and Optional routines
   PlugIn2: ICM2VisPlug2;
   PlugIn3: ICM2VisPlug3;
   PlugIn4: ICM2VisPlug4;
   TriggerList: TTriggerList;
   Constructor Create(GUID: TGUID; FormDC: THandle; Parent: TVisObjectList);
   Destructor Destroy; override;
   Property Active: Boolean read fActive;
   Property ConfigString: String read fConfigString write SetConfigString;
   Property DC: THandle read fDC;
   Property FormHandle: THandle read fFormHandle;
   Property GUID: TGUID read fGUID;
   Property Height: Word read fh Write SetHeight;
   Property InterfaceVersion: Byte read FInterfaceVersion write SetInterfaceVersion;
   Property Left: Integer read fx write SetLeft;
   Property Name: String read fName write fName;
   Property NotifyOfEvent: TNotifyOfEvent read fNotifyOfEvent;
   Property Parent: TVISObjectList read fParent;
   Property Result: TResult read fResult;
   Property Top: Integer read fy write SetTop;
   Property VisualID: String read FVisualID write SetVisualID;
   Property Width: Word read fw write SetWidth;
   procedure SetResult(const Value: TResult);
   Procedure Paint;
   Procedure SetDC(FormDC: THandle);
   Function GetLastError: String;
   Function OnEvent(Sender: String; X, Y: Integer; Trigger: TTrigger): Boolean;
   Function DoNotifyOfEvent(EventType: TEventType): Boolean;
   Function APICallBack(APIStr: String): Boolean;
 End;
 PVISObject = ^TVISObject;
 TVISObjectList = Class(TObject) // A list of the DLLObject type
 private
  fVISObjectList: Array of TVISObject;
  fFormHandle: THandle;
  function GetCount: Integer;
  function GetVISObject(Index: Integer): TVISObject;
  procedure SetVISObject(Index: Integer; const Value: TVISObject);
  function GetPVISObject(Index: Integer): PVISObject; // Returns the length of the list
 public
  fParent: TWinControl;
 Constructor Create(FormHandle: THandle; Parent: TWinControl);
  Destructor Destroy; override;
  Function Add(GUID: TGUID): TVISObject; // Add a VISObject to the list
  Function AddP(GUID: TGUID): PVISObject;
  Procedure Delete(Index: Integer); // Delete a VISObject from the list
  Property VISObject[Index: Integer]: TVISObject read GetVISObject write SetVISObject; Default;
  Function GetVOByVI(VisualID: String): PVISObject;
  Property Count: Integer read GetCount; // Get the length of the list
  Property FormHandle: THandle read fFormHandle;
  Property PVISObject[Index: Integer]: PVISObject read GetPVISObject;
  Property Parent: TWinControl read fParent;
  Procedure ReInitDC;
 End;
 TpVOList = Class(TObject)
 private
  fList: array of PVISObject;
    function GET_VO(Index: Integer): PVisObject;
    function GetCOUNT: Integer;
    procedure SET_VO(Index: Integer; const Value: PVisObject);
 public
  Procedure Add(VO: PVISObject);
  Procedure Delete(Index: Integer);
  Property Count: Integer read GetCOUNT;
  Property VO[Index: Integer]: PVisObject read GET_VO write SET_VO; default;
 end;

 TVISPlugIn = Class(TObject) // Use by TVisualController to represent a Visual Plugin
  private
   fName: String;
   fGuid: TGUID;
   fRCV: word;
   fVersion: Word;
    FInterfaceVersion: Byte;
   Procedure InitInfoList;
    procedure SetInterfaceVersion(const Value: Byte);
  public
   Constructor create(GUID: TGUID);
   Destructor Destroy; override;
  published
   PlugIn: ICM2VisPlug;
   PlugIn2: ICM2VisPlug2;
   PlugIn3: ICM2VisPlug3;
   PlugIn4: ICM2VisPlug4;
   InfoList: TInfoList;
   Property Name: String read fName;
   Property Version: Word read fVersion;
   Property RequiredCoreVersion: word read fRCV;
   Property GUID: TGUID read fGuid;
   Property InterfaceVersion: Byte read FInterfaceVersion write SetInterfaceVersion;
   Procedure Setup;
   Function GetLastError: String;
 End;

 TVISPlugInList = Class(TObject) // The type used in TVisualController to represent a list
 Private
  fVISPlugInList: Array of TVISPlugIn;
  function GetCount: Integer; // Returns the length of the list (Access via Property Count)
  procedure UpdateIndex;
  function GetVISPlugIn(Index: Integer): TVISPlugIn;
  procedure SetVISPlugIn(Index: Integer; const Value: TVISPlugIn); // Update all Index properties in the list
 public
  Progress: TPlugInListProgress;
  Destructor Destroy; override;
  Function Add(GUID: TGUID): TVISPlugIn; // Add a VISPlugIn to the list
  Procedure Delete(Index: Integer); // Delete a VISPlugIn in the List
  Property VISPlugIn[Index: Integer]: TVISPlugIn read GetVISPlugIn write SetVISPlugIn; default;
  Property Count: Integer read GetCount; // Get the length of the list
  Function GetGUID(Name: String; Var GUID: TGUID): Boolean;
  Function GetName(GUID: TGUID; Var TheName: String): Boolean;
  Function GetIndexOf(Name: String): Integer;
  Procedure LoadAll;
  Procedure UnLoadAll;
  Function LoadPlugIn(Name: String): Boolean;
 End;
 PVISPlugInList = ^TVISPlugInList;

 TVisExList = Class(TExInfoList)
 private
 public
  Constructor Create(CreateFrom: ICM2VisPlug); overload;
  Constructor Create(CreateFrom: ICM2VisPlug2); overload;
  Constructor Create(CreateFrom: ICM2VisPlug3); overload;
  Constructor Create(CreateFrom: ICM2VisPlug4); overload;
 End;

 TEditorSupportVis = Class;
 TMItem = Record
  Name: String;
  Hint: String;
 End;
 TESVisMenu = Class(TObject)
 private
  fList: Array of TMItem;
    fParent: TEditorSupportVis;
    function GETCOUNT: cardinal;
    function GETMITEM(Index: Integer): TMItem;
    procedure SETMITEM(Index: Integer; const Value: TMItem);
 public
  Constructor Create(aOwner: TEditorSupportVis);
  Procedure Add(Name, Hint: String);
  Property Count: cardinal Read GETCOUNT;
  Property MItem[Index: Integer]: TMItem read GETMITEM write SETMITEM; default;
  Property Parent: TEditorSupportVis read fParent;
 end;
 TESVis = Record
  ParameterName: String;
  ValueType: Byte;
  ValueRange: String;
 end;
 TEditorSupportVis = Class(Tobject)
 private
  fList: Array of TESVis;
  fSupportsEditor: Boolean;
  ESVisMenu: TESVisMenu;
  function GET_ESVIS(Index: Integer): TESVis;
  function GET_COUNT: Integer;
    function GET_MENUITEM(Index: Integer): TMItem;
    function GET_MENUITEMCOUNT: Cardinal;
 public
  Procedure LoadFrom(PlugIn: TVisPlugIn);
  Property SupportsEditor: Boolean read fSupportsEditor;
  Property ESVis[Index: Integer]: TESVis read GET_ESVis; default;
  Function GetByName(Name: String): TESVis;
  Property Count: Integer read GET_COUNT;
  Property MenuItemCount: Cardinal read GET_MENUITEMCOUNT;
  Property MenuItem[Index: Integer]: TMItem read GET_MENUITEM;
 end;

implementation

uses CM2EventTypes, CM2API;

{ TVISObject }

function TVISObject.APICallBack(APIStr: String): Boolean;
Var
 Res: Hresult;
 CallBack: Pchar;
begin
 Result := False;
 If InterfaceVersion < 4 then exit; // API callback not supported prior to VisPlug4
 Result := True;
 While (Length(APIStr) > 0) do begin
  Res := E_FAIL;
  Case InterfaceVersion of
   4: Res := PlugIn4.APICallBack(Pchar(APIStr), CallBack);
  end;
  If Res <> S_OK then begin
   raise EDLLError.Create('Failed to run APICallBack properly');
  end;
  If not API(Addr(Self), CallBack, APIStr) then APIStr := ''; // don't loop
 end;
end;

constructor TVISObject.Create(GUID: TGUID; FormDC: THandle; Parent: TVisObjectList);
Var
 N: PChar;
 Res: HResult;
 NOE: Cardinal;
begin
  // Set default values
  Self.TriggerList := TTriggerList.Create;
  Self.fx := 0;
  Self.fy := 0;
  Self.fw := 0;
  Self.fh := 0;
  Self.fFormHandle := FormDC;
  Self.fDC := GetDC(FormDC);
  Self.fGUID := GUID;
  Self.fParent := Parent;
  Self.fActive := False;
  If Debug then Form4.Add('VisObject.Create [' + GUIDToString(GUID) + '] ... ', dlplugins);
  InterfaceVersion := 0;
  Try
   PlugIn4 := CreateComObject (GUID) as ICM2VisPlug4;
   InterfaceVersion := 4;
  except
   On EIntfCastError do begin
    Try
     PlugIn3 := CreateComObject (GUID) as ICM2VisPlug3;
     InterfaceVersion := 3;
    except
     On EIntfCastError do begin
      Try
       PlugIn2 := CreateComObject (GUID) as ICM2VisPlug2;
       InterfaceVersion := 2;
      except
       On EIntfCastError do begin
        Try
         PlugIn := CreateComObject (GUID) as ICM2VisPlug;
         InterfaceVersion := 1;
        except
         On EIntFCastError do begin
          PlugIn := nil;
          PlugIn2 := Nil;
          PlugIn3 := Nil;
          PlugIn4 := Nil;
          InterfaceVersion := 0;
         end;
        end;
       end;
      end;
     end;
    end;
   end;
  end;
  If Debug then begin
   Case InterfaceVersion of
    0: Form4.Append('Unknown Interface', dlplugins);
    1: Form4.Append('ICM2VisPlug', dlplugins);
    2: Form4.Append('ICM2VisPlug2', dlplugins);
    3: Form4.Append('ICM2VisPlug3', dlPlugins);
    4: Form4.Append('ICM2VisPlug4', dlplugins);
   end;
  end;
  If Debug then Form4.Add('Get PlugIn Name ... ', dlplugins);
  Res := E_FAIL;
  Case InterfaceVersion of
   1: Res := PlugIn.Name(N);
   2: Res := PlugIn2.Name(N);
   3: Res := PlugIn3.Name(N);
   4: Res := PlugIn4.Name(N);
  end;
  If Res <> S_OK then raise EDLLError.Create('Failed To Return Plugin Name');
  If Debug then form4.append('done', dlplugins);
  SElf.fName := N;
  If Debug then Form4.Add('Run Init Block ... ', dlplugins);
  Res := E_FAIL;
  Case InterfaceVersion of
   1: Res := PlugIn.Init(fDC);
   2: Res := PlugIn2.Init(fDC);
   3: Res := PlugIn3.Init(fDC);
   4: Res := PlugIn4.Init(fDC);
  end;
  If Res = E_FAIL then raise EDLLError.Create('Failed to run init procedure properly');
  If InterfaceVersion > 2 then begin
   // VisPlug3 contains advanded Event system
   NOE := 0;
   Res := E_FAIL;
   Case InterfaceVersion of
    3: res := PlugIn3.NotifyOnEvent(NOE);
    4: Res := PlugIn4.NotifyOnEvent(NOE);
   end;
   If Res = S_OK Then SetNotifyOfEvent(NOE) // Set Event Flags
    else raise EDLLError.Create('NotifyOnEvent Routine failed');
  end;
  If Debug then Form4.append('done', dlplugins);
  Self.fActive := true;
end;

destructor TVISObject.Destroy;
begin
  FreeAndNil(TriggerList);
  Try
   Case InterfaceVersion of
    1: PlugIn.Terminating;
    2: PlugIn2.Terminating;
    3: PlugIn3.Terminating;
    4: PlugIn4.Terminating;
   end;
  Except
   // Do Nothing for now
  end;
  inherited;
end;

function TVISObject.DoNotifyOfEvent(EventType: TEventType): Boolean;
begin
 Result := false;
 Case EventType of
  etDblClick: Result := NotifyOfEvent.DblClick;
  etMouseDownLeft: Result := NotifyOfEvent.MouseDownLeft;
  etMouseDownMiddle: Result := NotifyOfEvent.MouseDownMiddle;
  etMouseDownRight: Result := NotifyOfEvent.MouseDownRight;
  etMouseUpLeft: Result := NotifyOfEvent.MouseUpLeft;
  etMouseUpMiddle: Result := NotifyOfEvent.MouseUpMiddle;
  etMouseUpRight: Result := NotifyOfEvent.MouseUpRight;
  etMouseWheelAny: Result := NotifyOfEvent.MouseWheelAny;
  etMouseWheelUp: Result := NotifyOfEvent.MouseWheelUp;
  etMouseWheelDown: Result := NotifyOfEvent.MouseWheelDown;
  etMouseMove: Result := NotifyOfEvent.MouseMove;
 End;
end;

function TVISObject.GetLastError: String;
Var
 Msg: Pchar;
 Res: HResult;
begin
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.GetLastErrorMsg(Msg);
  2: Res := PlugIn2.GetLastErrorMsg(Msg);
  3: Res := PlugIn3.GetLastErrorMsg(Msg);
  4: Res := PlugIn4.GetLastErrorMsg(Msg);
 end;
 If Res = S_OK then begin
  If Debug then Form4.add('LastError: ' + Msg, dlerror, mtwarning);
 end
  else
 begin
  If Debug then form4.add('Unable to load lasterror', dlerror, mterror);
 End;
end;

function TVISObject.OnEvent(Sender: String; X, Y: Integer;
  Trigger: TTrigger): Boolean;
Var
 APIStr: Pchar;
 CallBack: String;
begin
 Result := InterfaceVersion > 1;
 If Result then begin
  Case InterfaceVersion of
   2: PlugIn2.OnEvent(Pchar(Sender), ord(Trigger.EventType), ord(Trigger.ModKeys), x, y, APIStr);
   3: PlugIn3.OnEvent(PChar(Sender), ord(Trigger.EventType), ord(Trigger.ModKeys), x, y, APIStr);
   4: PlugIn4.OnEvent(PChar(Sender), ord(Trigger.EventType), ord(Trigger.ModKeys), x, y, APIStr);
  end;
  If APIStr <> '' then begin
   If API(Addr(self), APIStr, CallBack) then begin
    APICallBack(Callback);
   end;
  end;
 end;
end;

procedure TVISObject.Paint;
Var
 Res: HResult;
 APIStr: Pchar;
 CallBack: String;
begin
 If Debug then Form4.add('VisPlugIn: ' + Self.fName + '.Paint ... ', dlPlugins);
 If Not Active then exit;
 CallBack := '';
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.Redraw;
  2: Res := PlugIn2.Redraw;
  3: Res := PlugIn3.Redraw;
  4: Res := PlugIn4.Redraw(APIStr);
 end;
 If Res = E_FAIL then begin
  fActive := False;
  raise EDLLError.Create('Unable to perform redraw for ' + fName + #13#10 + #13#10 + 'Last Error:' + #13#10 + GetLastError);
 End;
 If Length(APIStr) > 0 then begin
  If API(Addr(self), APIStr, CallBack) then begin
   APICallBack(Callback);
  end;
 end;
 If Debug then Form4.append('done', dlplugins);
end;

procedure TVISObject.SetConfigString(const Value: String);
Var
 Res: HResult;
begin
  fConfigString := Value;
  If Debug then Form4.add('Set config String ... ', dlplugins);
  Res := E_FAIL;
  Case InterfaceVersion of
   1: Res := PlugIn.SetConfigString(PChar(fConfigString));
   2: Res := PlugIn2.SetConfigString(PChar(fConfigString));
   3: Res := PlugIn3.SetConfigString(PChar(fConfigString));
   4: Res := PlugIn4.SetConfigString(PChar(fConfigString));
  end;
  If Res <> S_OK then begin
   fActive := False;
   raise EDLLError.Create('Unable to set config string for ' + fName);
  end;
  If Debug then Form4.append('done', dlplugins);
end;

procedure TVISObject.SetDC(FormDC: THandle);
Var
 Res: HResult;
begin
 ReleaseDC(fFormHandle, fDC);
 fFormHandle := FormDC;
 fDc := GetDC(FormDC);
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Begin
      Res := PlugIn.SetDC(fDC);
     End;
  2: Begin
      Res := PlugIn2.SetDC(fDC);
     End;
  3: Begin
      Res := PlugIn3.SetDC(fDC);
     end;
  4: Begin
      Res := PlugIn4.SetDC(fDC);
     end; 
 end;
 If Res <> S_OK Then begin
  fActive := False;
  raise EDLLError.Create('Unable to set new DC for ' + fName);
 End;
end;

procedure TVISObject.SetDimensions;
Var
 Res: HResult;
begin
 If Debug then Form4.add('Set Dimensions [' +IntToStr(fx) + ', ' +inttostr(fy) + ', ' + inttostr(fw) + ', ' + InttoStr(fh) + '] ... ', dlplugins);
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.SetDimensions(fx, fy, fw, fh);
  2: Res := PlugIn2.SetDimensions(fx, fy, fw, fh);
  3: Res := PlugIn3.SetDimensions(fx, fy, fw, fh);
  4: Res := PlugIn4.SetDimensions(fx, fy, fw, fh);
 end;
 If Res <> S_OK Then Begin
  fActive := False;
  raise EDLLError.Create('Unable to set Drawing dimensions for ' + fName);
 End;
 If Debug then Form4.append('done', dlplugins);
end;

procedure TVISObject.SetHeight(const Value: Word);
begin
  fh := Value;
  SetDimensions;
end;

procedure TVISObject.SetInterfaceVersion(const Value: Byte);
begin
  FInterfaceVersion := Value;
end;

procedure TVISObject.SetLeft(const Value: Integer);
begin
  fx := Value;
  SetDimensions;
end;

procedure TVISObject.SetNotifyOfEvent(NOE: Int64);
begin
 With fNotifyOfEvent do begin
  DblClick := false;
  MouseDownLeft := false;
  MouseDownRight := false;
  MouseDownMiddle := False;
  MouseUpLeft := false;
  MouseUpRight := false;
  MouseUpMiddle := false;
  MouseWheelAny := false;
  MouseWheelUp := false;
  MouseWheelDown := false;
  MouseMove := false;
  If NOE - noeMouseMove >= 0 Then begin
   MouseMove := True;
   Dec(Noe, noeMouseMove);
  end;
  If NOE - noeMouseWheelDown >= 0 Then begin
   MouseWheelDown := true;
   Dec(Noe, noeMouseWheelDown);
  end;
  If NOE - noeMouseWheelUp >= 0 Then begin
   MouseWheelUp := true;
   Dec(Noe, noeMouseWheelUp);
  end;
  If NOE - noeMouseWheelAny >= 0 Then begin
   MouseWheelAny := true;
   Dec(Noe, noeMouseWheelAny);
  end;
  If NOE - noeMouseUpMiddle >= 0 Then Begin
   MouseUpMiddle := true;
   Dec(Noe, noeMouseUpMiddle);
  end;
  If NOE - noeMouseUpRight >= 0 Then Begin
   MouseUpRight := true;
   Dec(Noe, noeMouseUpRight);
  end;
  If NOE - noeMouseUpLeft >= 0 Then Begin
   MouseUpLeft := true;
   Dec(Noe, noeMouseUpleft);
  end;
  If NOE - noeMouseDownMiddle >= 0 Then Begin
   MouseDownMiddle := true;
   Dec(Noe, noeMouseDownMiddle);
  end;
  If NOE - noeMouseDownRight >= 0 Then begin
   MouseDownRight := True;
   Dec(Noe, noeMouseDownRight);
  end;
  If NOE - noeMouseDownLeft >= 0 Then begin
   MouseDownLeft := True;
   Dec(Noe, noeMouseDownLeft);
  end;
  If NOE - noeDblClick >= 0 Then Begin
   DblClick := True;
  End;
 end;
end;

procedure TVISObject.SetResult(const Value: TResult);
Var
 Res: HResult;
 APIStr: Pchar;
 CallBack: String;
begin
  fResult := Value;
  If Debug then Form4.add('Set Result ... ', dlcore);
  Res := E_FAIL;
  Case InterfaceVersion of
   1: Res := PlugIn.Update(byte(fResult.SensorStyle), Pchar(fResult.Str), fResult.Value);
   2: Res := PlugIn2.Update(byte(fResult.SensorStyle), Pchar(fResult.Str), fResult.Value);
   3: Res := PlugIn3.Update(byte(fResult.SensorStyle), Pchar(fResult.Str), fResult.Value);
   4: Res := PlugIn4.Update(byte(fResult.SensorStyle), Pchar(fResult.Str), fResult.Value, APIStr);
  end;
  If Res <> S_OK then begin
   fActive := false;
   Raise EDLLError.Create('Unable to set result for ' + fName);
  End;
  If Debug then Form4.append('done', dlcore);
  If Length(APIStr) > 0 then begin
   If API(Addr(self), APIStr, CallBack) then begin
    APICallBack(Callback);
   end;
  end;
  If Debug Then Form4.Add(Self.Name + '.Repaint ... ', dlplugins);
  Self.Paint;
  If Debug Then Form4.Append('done', dlplugins);
end;

procedure TVISObject.SetTop(const Value: Integer);
begin
  fy := Value;
  SetDimensions;
end;

procedure TVISObject.SetVisualID(const Value: String);
begin
  FVisualID := Value;
end;

procedure TVISObject.SetWidth(const Value: Word);
begin
  fw := Value;
  SetDimensions;
end;

{ TVISObjectList }

function TVISObjectList.Add(GUID: TGUID): TVISObject;
Var
 Index: Integer;
begin
 If Debug then Form4.add('VisObjectList.Add [' + GUIDToString(GUID) + ']', dlcore);
 Setlength(Self.fVISObjectList, Length(Self.fVISObjectList) + 1); // Resize the List by adding one slot
 Index := Length(Self.fVISObjectList) - 1; // Set Index to be the newly added slots position
 fVISObjectList[Index] := TVISObject.Create(GUID, fFormHandle, self); // Add The Object to the list
 Result := fVISObjectList[Index]; // Return the new slots position (for easy access)
end;

function TVISObjectList.AddP(GUID: TGUID): PVISObject;
Var
 Index: Integer;
begin
 If Debug then Form4.add('VisObjectList.AddP [' + GUIDToString(GUID) + ']', dlcore);
 Index := Length(Self.fVISObjectList); // Set Index to be the newly added slots position
 Setlength(Self.fVISObjectList, Index + 1); // Resize the List by adding one slot
 fVISObjectList[Index] := TVISObject.Create(GUID, fFormHandle, self); // Add The Object to the list
 Result := addr(fVISObjectList[Index]); // Return the new slots position (for easy access)
end;

constructor TVISObjectList.Create(FormHandle: THandle; Parent: TWinControl);
begin
 Self.fFormHandle := FormHandle;
 Self.fParent := Parent;
end;

procedure TVISObjectList.Delete(Index: Integer);
begin
 If Debug then Form4.add('VisObjectList.Delete [#' + IntToStr(Index) + '] ... ', dlcore);
 If (Index < 0) or (Index > Length(Self.fVISObjectList)-1) then Begin // verify that index isn't out of bounds
  fVISObjectList[Index].Free; // Delete the request VISObject
  If (Index < Length(Self.fVISObjectList) - 1) Then begin // If the selected wasn't the last one ...
   fVISObjectList[Index] := fVISObjectList[Length(Self.fVISObjectList)-1]; // Move the last one of the list to the newly open slot
   fVISObjectList[Length(Self.fVISObjectList)-1].Free; // Delete the last VISObject from the list
  End;
  SetLength(fVISObjectList, Length(fVISObjectList) - 1); // Resize the lsit
 End;
 If Debug then Form4.append('done', dlcore);
end;

destructor TVISObjectList.Destroy;
Var
 I: Integer;
begin
  If Length(Self.fVISObjectList) > 0 Then begin // If there is VISObject in the list ...
   For I := 0 to Length(Self.fVISObjectList)-1 do begin // Free them all
    Self.fVISObjectList[I].Free;
   End;
   SetLength(fVISObjectList, 0); // Set new array length to nothing
  End;
  inherited;
end;

function TVISObjectList.GetCount: Integer;
begin
 Result := Length(Self.fVISObjectList); // Return the length of the VISObjectlist
end;

function TVISObjectList.GetPVISObject(Index: Integer): PVISObject;
begin
 Result := Addr(fVISObjectList[Index]);
end;

function TVISObjectList.GetVISObject(Index: Integer): TVISObject;
begin
 Result := fVISObjectList[Index];
end;

function TVISObjectList.GetVOByVI(VisualID: String): PVISObject;
Var
 I: Integer;
begin
 Result := nil;
 For I := 0 to count - 1 do begin
  If uppercase(VisualID) = uppercase(VISObject[I].VisualID) then begin
   Result := Self.GetPVISObject(I);
   Exit;
  end;
 end;
end;

procedure TVISObjectList.ReInitDC;
Var
 I: Integer;
begin
 For I := 0 to count - 1 do begin
  Self.fVISObjectList[I].SetDC(Self.Parent.Handle);
 End;
end;

procedure TVISObjectList.SetVISObject(Index: Integer;
  const Value: TVISObject);
begin
 fVISObjectList[Index] := Value;
end;

{ TVISPlugIn }

constructor TVISPlugIn.create(GUID: TGUID);
Var
 Name: PChar;
 V, RCV: Word;
 Res: HResult;
begin
 If Debug then Form4.add('VISPlugIn.Create [' + GUIDTOString(GUID) + ']', dlplugins);
 If Debug then Form4.add('InfoList.Create ... ', dlplugins);
 Self.InfoList := TInfoList.Create;
 If Debug then Form4.Append('done', dlplugins);
 If Debug then Form4.add('CreateComObject [' + GUIDToString(GUID) + '] ... ', dlplugins);
 Try
  PlugIn4 := CreateComObject (GUID) as ICM2VisPlug4;
  InterfaceVersion := 4;
 except
  On EIntfCastError do begin
   Try
    PlugIn3 := CreateComObject (GUID) as ICM2VisPlug3;
    InterfaceVersion := 3;
   except
    On EIntfCastError do begin
     Try
      PlugIn2 := CreateComObject (GUID) as ICM2VisPlug2;
      InterfaceVersion := 2;
     except
      On EIntfCastError do begin
       Try
        PlugIn := CreateComObject (GUID) as ICM2VisPlug;
        InterfaceVersion := 1;
       except
        On EIntfCastError do begin
         PlugIn := Nil;
         PlugIn2 := Nil;
         PlugIn3 := Nil;
         PlugIn4 := Nil;
         Self.InterfaceVersion := 0;
        end;
       end;
      end;
     end;
    end;
   end;
  end;
 end;
 If Debug then begin
  case InterfaceVersion of
   0: Form4.Append('Unknown Interface', dlplugins);
   1: Form4.Append('ICM2VisPlug', dlPlugins);
   2: Form4.Append('ICM2VisPlug2', dlPlugins);
   3: Form4.Append('ICM2VisPlug3', dlPlugins);
   4: Form4.Append('ICM2VisPlug4', dlPlugins);
  end;
 End;
 If Debug then Form4.Append('done', dlplugins);
 If Debug then Form4.add('Load PlugIn Name ... ', dlplugins);
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.Name(Name);
  2: Res := PlugIn2.Name(Name);
  3: Res := PlugIn3.Name(Name);
  4: Res := PlugIn4.Name(Name);
 end;
 If Res <> S_OK Then Raise EDLLError.Create('Failed To Return Plugin Name');
 If Debug then Form4.Append(name, dlplugins);
 If Debug then Form4.add('Load PlugIn Version ... ', dlplugins);
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.Version(V);
  2: Res := PlugIn2.Version(V);
  3: Res := PlugIn3.Version(V);
  4: Res := PlugIn4.Version(V);
 end;
 If Res <> S_OK then raise EDLLError.Create('Failed To Return Version Number');
 If Debug then Form4.Append(IntToStr(V), dlplugins);
 If Debug then Form4.add('Load PlugIn RCV ... ', dlplugins);
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.RequiredCoreVersion(RCV);
  2: Res := PlugIn2.RequiredCoreVersion(RCV);
  3: Res := PlugIn3.RequiredCoreVersion(RCV);
  4: Res := PlugIn4.RequiredCoreVersion(RCV);
 end;
 If Res <> S_OK then begin
  raise EDLLError.Create('Failed to return RequiredCoreVersion Number');
 end;
 If Debug then Form4.append(IntToStr(RCV), dlplugins);
 fNAme := Name;
 fVersion := V;
 fRCV := RCV;
 fGUID := GUID;
 InitInfoList;
end;

destructor TVISPlugIn.Destroy;
begin
  try
   case InterfaceVersion of
    1: PlugIn.Terminating;
    2: PlugIn2.Terminating;
    3: PlugIn3.Terminating;
    4: PlugIn4.Terminating;
   end;
  except
   // Do nothing for now
  end;
  inherited;
end;

function TVISPlugIn.GetLastError: String;
Var
 Msg: PChar;
begin
 Msg := '';
 Case InterfaceVersion of
  1: PlugIn.GetLastErrorMsg(MSG);
  2: PlugIn2.GetLastErrorMsg(MSG);
  3: PlugIn3.GetLastErrorMsg(MSG);
  4: PlugIn4.GetLastErrorMsg(MSG);
 end;
 Result := MSG;
 If Debug then Form4.add('Unable to perform GetLastError command', dlerror, mterror);
end;

procedure TVISPlugIn.InitInfoList;
Var
 I, Cnt: Integer;
 S1, S2: PChar;
 Res: HResult;
begin
 If Debug then Form4.add('Initialize Info List', dlplugins);
 If Debug then Form4.add('Get Info count ... ', dlplugins);
 Cnt := -1;
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.GetInfoCount(Cnt);
  2: Res := PlugIn2.GetInfoCount(Cnt);
  3: Res := PlugIn3.GetInfoCount(Cnt);
  4: Res := PlugIn4.GetInfoCount(Cnt);
 end;
 If Res <> S_OK Then Raise EDLLError.Create('Failed to return Info count');
 If Debug then Form4.Append('done', dlplugins);
 For I := 0 to cnt - 1 do begin
  If Debug then Form4.add('Get Info [#' + IntToStr(I) + ']', dlplugins);
  Res := E_FAIL;
  Case InterfaceVersion of
   1: Res := PlugIn.GetInfo(I, S1, S2);
   2: Res := PlugIn2.GetInfo(I, S1, S2);
   3: Res := PlugIn3.GetInfo(I, S1, S2);
   4: Res := PlugIn4.GetInfo(I, S1, S2);
  end;
  If Res <> S_OK Then Raise EDLLError.Create('Failed to return Info data');
  SElf.InfoList.Add(S1, S2);
 End;
end;

procedure TVISPlugIn.SetInterfaceVersion(const Value: Byte);
begin
  FInterfaceVersion := Value;
end;

procedure TVISPlugIn.Setup;
Var
 Res: HResult;
begin
 If Debug then Form4.add('VisPlugIn: ' + SElf.fName + '.setup ... ', dlplugins);
 Res := E_FAIL;
 Case InterfaceVersion of
  1: Res := PlugIn.Setup;
  2: Res := PlugIn2.Setup;
  3: Res := PlugIn3.Setup;
  4: Res := PlugIn4.Setup;
 end;
 Case Res of
  E_FAIL: begin
           raise EDLLError.Create('Unable to run setup for ' + fName);
          end;
  E_NOTIMPL: begin
              MessageDlg('Nothing to setup', mtInformation, [mbOK], 0);
             end;
 end;
 If Debug then Form4.append('done', dlplugins);
end;

{ TVISPlugInList }

function TVISPlugInList.Add(GUID: TGUID): TVISPlugIn;
Var
 Index: Integer;
begin
 If Debug then Form4.add('VisPlugInList.Add [' + GUIDToString(GUID) + ']', dlCore);
 Setlength(Self.fVISPlugInList, Length(Self.fVISPlugInList) + 1); // Resize the List by adding one slot
 Index := Length(Self.fVISPlugInList) - 1; // Set Index to be the newly added slots position
 fVISPlugInList[Index] := TVISPlugIn.Create(GUID); // Add the VISPlugIn to the list
 Self.UpdateIndex;
 Result := fVISPlugInList[Index]; // Return the Index of the newly added plugin
end;

procedure TVISPlugInList.Delete(Index: Integer);
begin
 If Debug then Form4.add('VisPlugInList.Delete [#' + IntTOStr(Index) + '] ... ', dlCore);
 If (Index < 0) or (Index > Length(Self.fVISPlugInList)-1) then Begin // verify that index isn't out of bounds
  fVISPlugInList[Index].Free; // Delete the request VISPlugIn
  If (Index < Length(Self.fVISPlugInList) - 1) Then begin // If the selected wasn't the last one ...
   fVISPlugInList[Index] := fVISPlugInList[Length(Self.fVISPlugInList)-1]; // Move the last one of the list to the newly open slot
   fVISPlugInList[Length(Self.fVISPlugInList)-1].Free; // Delete the last VISPlugIn from the list
  End;
  SetLength(fVISPlugInList, Length(fVISPlugInList) - 1); // Resize the list
  Self.UpdateIndex;
 End;
 If Debug then Form4.append('done', dlCore);
end;

destructor TVISPlugInList.Destroy;
Var
 I: Integer;
begin
  If Length(Self.fVISPlugInList) > 0 Then begin // If there is VISPlugIn in the list ...
   For I := 0 to Length(Self.fVISPlugInList)-1 do begin // Free them all
    Self.fVISPlugInList[I].Free;
   End;
   SetLength(fVISPlugInList, 0); // Set new array length to nothing
  End;
  inherited;
end;

function TVISPlugInList.GetCount: Integer;
begin
 Result := Length(Self.fVISPlugInList); // Return the length of the VISPlugInlist
end;

function TVISPlugInList.GetGUID(Name: String; Var GUID: TGUID): Boolean;
Var
 I: Integer;
begin
 Result := False;
 For I := 0 to Self.Count-1 do begin
  if uppercase(Self.VISPlugIn[I].Name) = uppercase(name) then Begin
   Result := True;
   GUID := Self.VISPlugIn[I].GUID;
   exit;
  end;
 End;
 If LoadPlugIn(Name) then begin
  For I := 0 to Self.Count-1 do begin
   if uppercase(Self.VISPlugIn[I].Name) = uppercase(name) then Begin
    Result := True;
    GUID := Self.VISPlugIn[I].GUID;
    exit;
   end;
  End;
 end;
end;

function TVISPlugInList.GetIndexOf(Name: String): Integer;
Var
 I: Integer;
begin
 Result := -1;
 For I := 0 to length(self.fVISPlugInList) - 1 do begin
  If uppercase(fVisPlugInList[I].Name) = uppercase(name) then begin
   result := I;
   exit;
  end;
 End;
 If LoadPlugIn(Name) then begin
  For I := 0 to length(self.fVISPlugInList) - 1 do begin
   If uppercase(fVisPlugInList[I].Name) = uppercase(name) then begin
    result := I;
    exit;
   end;
  End;
 end;
end;

function TVISPlugInList.GetName(GUID: TGUID; var TheName: String): Boolean;
Var
 I: Integer;
begin
 Result := False;
 For I := 0 to count - 1 do begin
  If GUIDToString(fVisPlugInList[I].fGuid) = GUIDToString(GUID) then begin
   TheName := fVisPlugInList[I].Name;
   Result := true;
   break; // Speed optimization
  end;
 end;
end;

function TVISPlugInList.GetVISPlugIn(Index: Integer): TVISPlugIn;
begin
 Result := fVISPlugInList[Index];
end;

procedure TVISPlugInList.LoadAll;
Var
 Reg: TRegistry;
 I: Integer;
 Strings: TStrings;
 S: String;
 RCV: Word;
 N: String;
 GoForLaunch: Boolean;
begin
 If Debug then Form4.add('VisPlugInList.LoadAll', dlCore);
 If Self.Count > 0 Then Unloadall;
 If Debug then Form4.add('Create Registry ... ', dlCore);
 Reg := Tregistry.Create;
 If Debug then Form4.append('done', dlCore);
 If Debug then Form4.add('Create StringList ... ', dlCore);
 Strings := TStringlist.Create;
 If Debug then Form4.Append('done', dlCore);
 If Reg.OpenKey(CertifiedVISPluginsLocation, False) Then Begin
  If Debug then Form4.add('Load PlugIns ... ', dlPlugins);
  Reg.GetKeyNames(Strings);
  For I := 0 to Strings.Count - 1 do begin
    S := Strings[I];
    RCV := 0;
    if Reg.OpenKey(CertifiedVISPluginsLocation + '\' + S, false) then begin
     RCV := Reg.ReadInteger('RequiredCoreVersion');
     N := Reg.ReadString('');
    end;
    If Assigned(Progress) then Progress(N, I - 1, strings.count); 
    GoForLaunch := True;
    If RCV > CoreVersion Then begin
     GoForLaunch := False;
     messagedlg('Core outdated' + #13#10 + 'Visual PlugIn: ' + N + #13#10 + 'requires: ' + Inttostr(RCV) + #13#10 + 'current: ' + IntToStr(CoreVersion), mtWarning, [mbOk], 0);
     If Debug then Form4.add('PlugIn: ' + n + ' [Core Outdated]', dlplugins, mtwarning);
    end;
    If RCV <= OutDatedCoreVersion Then begin
     GoForLaunch := False;
     messagedlg('PlugIn outdated' + #13#10 + 'Visual PlugIn: ' + N + #13#10 + 'requires: ' + Inttostr(RCV) + #13#10 + 'current: ' + IntToStr(CoreVersion), mtWarning, [mbOk], 0);
     If Debug then Form4.add('PlugIn: ' + n + ' [PlugIn Outdated]', dlplugins, mtwarning);
    end;
    If GoForLaunch then Self.Add(StringToGUID(S));
    If Assigned(Progress) then Progress(N, I, strings.count); 
  End;
 End;
 If Debug then Form4.add('StringList free ... ', dlcore);
 Strings.Free;
 If Debug then Form4.append('done', dlcore);
 If Debug then Form4.add('Registry Free ... ', dlcore);
 REg.Free;
 If Debug then Form4.append('done', dlcore);
end;

function TVISPlugInList.LoadPlugIn(Name: String): Boolean;
Var
 I: Integer;
 Reg: TRegistry;
 SL: TStrings;
 RCV: Integer;
 GoForLaunch: Boolean;
 N: String;
begin
 Result := false;
 Reg := TRegistry.Create;
 Reg.RootKey := HKEY_CURRENT_USER;
 SL := TStringList.Create;
 If Reg.OpenKey(CertifiedVISPluginsLocation, false) then begin
  Reg.GetKeyNames(SL);
  For I := 0 to SL.Count - 1 do begin
   Reg.OpenKey(CertifiedVISPluginsLocation+ '\' + SL[I], false);
   N := Reg.ReadString('');
   If uppercase(N) = uppercase(Name) then begin
    RCV := Reg.ReadInteger('RequiredCoreVersion');
    GoForLaunch := True;
    If RCV > CoreVersion Then begin
     GoForLaunch := False;
     messagedlg('Core outdated' + #13#10 + 'Visual PlugIn: ' + N + #13#10 + 'requires: ' + Inttostr(RCV) + #13#10 + 'current: ' + IntToStr(CoreVersion), mtWarning, [mbOk], 0);
    end;
    If RCV <= OutDatedCoreVersion Then begin
     GoForLaunch := False;
     messagedlg('PlugIn outdated' + #13#10 + 'Visual PlugIn: ' + N + #13#10 + 'requires: ' + Inttostr(RCV) + #13#10 + 'current: ' + IntToStr(CoreVersion), mtWarning, [mbOk], 0);
    end;
    If GoForLaunch then begin
     Self.Add(StringToGUID(SL[I]));
     Result := true;
    end;
   end;
  end;
 end;
 FreeAndNil(SL);
 freeAndNil(Reg);
end;

procedure TVISPlugInList.SetVISPlugIn(Index: Integer;
  const Value: TVISPlugIn);
begin
 fVISPlugInList[Index] := Value;
end;

procedure TVISPlugInList.UnLoadAll;
Var
 I: Integer;
begin
 If Debug then Form4.add('VisPlugInList.UnloadAll ... ', dlcore);
 For I := 0 to SElf.Count- 1 do begin
  fVISPlugInList[I].Free;
 End;
 SetLength(fVISPlugInList, 0);
 If Debug then Form4.append('done', dlcore);
end;

procedure TVISPlugInList.UpdateIndex;
begin
end;

{ TVisExList }

constructor TVisExList.Create(CreateFrom: ICM2VisPlug);
Var
 Cnt, I: Integer;
 N,V,D: PChar;
begin
 CreateFrom.GetDefaultCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetDefault(I, N, V, D);
  Self.Add(N, V, D, eitRequired);
 End;
 CreateFrom.GetOptionalCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetOptional(I, N, V, D);
  Self.Add(N, V, D, eitOptional);
 End;
end;

constructor TVisExList.Create(CreateFrom: ICM2VisPlug2);
Var
 Cnt, I: Integer;
 N,V,D: PChar;
begin
 CreateFrom.GetDefaultCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetDefault(I, N, V, D);
  Self.Add(N, V, D, eitRequired);
 End;
 CreateFrom.GetOptionalCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetOptional(I, N, V, D);
  Self.Add(N, V, D, eitOptional);
 End;
end;

constructor TVisExList.Create(CreateFrom: ICM2VisPlug3);
Var
 Cnt, I: Integer;
 N,V,D: PChar;
begin
 CreateFrom.GetDefaultCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetDefault(I, N, V, D);
  Self.Add(N, V, D, eitRequired);
 End;
 CreateFrom.GetOptionalCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetOptional(I, N, V, D);
  Self.Add(N, V, D, eitOptional);
 End;
end;

constructor TVisExList.Create(CreateFrom: ICM2VisPlug4);
Var
 Cnt, I: Integer;
 N,V,D: PChar;
begin
 CreateFrom.GetDefaultCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetDefault(I, N, V, D);
  Self.Add(N, V, D, eitRequired);
 End;
 CreateFrom.GetOptionalCount(Cnt);
 For I := 0 to Cnt-1 do begin
  CreateFrom.GetOptional(I, N, V, D);
  Self.Add(N, V, D, eitOptional);
 End;
end;

{ TEditorSupportVis }

function TEditorSupportVis.GET_COUNT: Integer;
begin
 Result := length(fList);
end;

function TEditorSupportVis.GET_ESVIS(Index: Integer): TESVis;
begin
 Result := fList[Index];
end;

function TEditorSupportVis.GetByName(Name: String): TESVis;
Var
 I: Integer;
begin
 Result.ValueType := vtUnknown;
 Result.ValueRange := '';
 For I := 0 to count - 1 do begin
  If uppercase(name) = uppercase(fList[I].ParameterName) then begin
   Result := fList[I];
   exit;
  end;
 End;
end;

procedure TEditorSupportVis.LoadFrom(PlugIn: TVisPlugIn);
Var
 EditSupport10: ICM2EditorSupportVis10;
 EditSupport20: ICM2EditorSupportVis20;
 InterfaceVersion: Byte;
 VisExList: TVisExlist;
 I: Integer;
 ValueRange: Pchar;
 Valuetype: Byte;
 Res: HResult;
 MC: Byte;
 MN, MH: PChar;
begin
 SetLength(fList, 0);
 Self.fSupportsEditor := True;
 InterfaceVersion := 0;
 Try
  EditSupport20 := CreateComObject (PlugIn.GUID) as ICM2EditorSupportVis20;
  InterfaceVersion := 2;
 Except
  Try
   EditSupport10 := CreateComObject (PlugIn.GUID) as ICM2EditorSupportVis10;
   InterfaceVersion := 1;
  except
   fSupportsEditor := False;
  end;
 end;
 If Not SupportsEditor then exit;
 Case PlugIn.InterfaceVersion of
  1: VisExList := TVisExList.Create(PlugIn.PlugIn);
  2: VisExList := TVisExList.Create(PlugIn.Plugin2);
  3: VisExList := TVisExList.Create(PlugIn.PlugIn3);
  4: VisExList := TVisExList.Create(PlugIn.PlugIn4);
 end;
 For I := 0 to VisExList.Count - 1 do begin
  SetLength(fList, Length(fList) + 1);
  fList[length(fList)-1].ParameterName := VisExList[I].Name;
  fList[Length(fList)-1].ValueType := vtUnknown;
  fList[Length(fList)-1].ValueRange := '';
  Res := E_FAIL;
  Case InterfaceVersion of
   1: Res := EditSupport10.ObjIns(PChar(fList[length(fList)-1].ParameterName), ValueType, ValueRange);
   2: Res := EditSupport20.ObjIns(Pchar(fList[Length(fList)-1].ParameterName), ValueType, ValueRange);
  End;
  If Res = S_OK then begin
   fList[Length(fList)-1].ValueType := ValueType;
   fList[Length(fList)-1].ValueRange := ValueRange;
  end;
 end;
 If InterfaceVersion > 1 then begin
  Res := EditSupport20.GetMenuCount(MC);
  If Res = S_OK then begin
   ESVisMenu := TEsVisMenu.Create(self);
   For I := 0 to MC - 1 do begin
    Res := EditSupport20.GetMenuItem(I, MN, MH);
    If Res = S_OK then begin
     ESVisMenu.Add(MN, MH);
    end;
   end;
  end;
 end;
 FreeAndNil(VisExList);
end;

function TEditorSupportVis.GET_MENUITEM(Index: Integer): TMItem;
begin
 Result.Name := '';
 Result.Hint := '';
 If not assigned(ESVisMenu) then exit;
 Result := ESVisMenu[Index];
end;

function TEditorSupportVis.GET_MENUITEMCOUNT: Cardinal;
begin
 Result := 0;
 If not Assigned(ESVisMenu) then begin
  Exit;
 end;
 Result := ESVisMenu.Count;
end;

{ TpVOList }

procedure TpVOList.Add(VO: PVISObject);
begin
 SetLength(fList, Length(fList) + 1);
 fList[Length(fList) - 1] := VO;
end;

procedure TpVOList.Delete(Index: Integer);
Var
 I: Integer;
begin
 If (Index < 0) or (Index >= count) then exit;
 For I := Index to Count - 2 do begin
  fList[I] := fList[I + 1];
 end;
 SetLength(fList, length(fList) - 1);
end;

function TpVOList.GET_VO(Index: Integer): PVisObject;
begin
 Result := fList[Index];
end;

function TpVOList.GetCOUNT: Integer;
begin
 Result := Length(flist);
end;

procedure TpVOList.SET_VO(Index: Integer; const Value: PVisObject);
begin
 fList[Index] := Value;
end;

{ TESVisMenu }

procedure TESVisMenu.Add(Name, Hint: String);
begin
 Setlength(fList, count+1);
 fList[Count-1].Name := NAme;
 fList[Count-1].Hint := Hint;
end;

constructor TESVisMenu.Create(aOwner: TEditorSupportVis);
begin
 fParent := aOwner;
end;

function TESVisMenu.GETCOUNT: cardinal;
begin
 Result := length(fList);
end;

function TESVisMenu.GETMITEM(Index: Integer): TMItem;
begin
 Result := fList[Index];
end;

procedure TESVisMenu.SETMITEM(Index: Integer; const Value: TMItem);
begin
 fList[Index] := Value;
end;

end.
