(Bluefox) make windows install run.

(Bluefox) make windows install run.

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "ioBroker automation platfrom on NodeJS"
#define MyAppShortName "ioBroker"
#define MyAppLCShortName "iobroker"
#define MyAppVersion "@@version"
#define MyAppPublisher "ioBroker.org"
#define MyAppURL "http://ioBroker.org/"
#define MyAppIcon "ioBroker.ico"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{97DA02F5-2E8C-4B96-BB42-61ED2BBF34DF}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\ioBroker
;DisableDirPage=yes
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
OutputDir=..\..\delivery
OutputBaseFilename={#MyAppShortName}Installer.{#MyAppVersion}
SetupIconFile={#MyAppIcon}
Compression=lzma
SolidCompression=yes
UsePreviousAppDir=yes
ArchitecturesInstallIn64BitMode=x64
UninstallDisplayIcon={app}\{#MyAppIcon}
CloseApplications=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "german";  MessagesFile: "compiler:Languages\German.isl"
Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl"

[Files]
Source: "nodejs-v0.10.29\node.exe"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "nodejs-v0.10.29\nodex64.exe"; DestDir: "{app}"; DestName: "node.exe"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "redis-v2.4.6\redis-2.4.6-setup-32-bit.exe"; DestDir: "{app}"; DestName: "redisSetup.exe"; Flags: ignoreversion deleteafterinstall; Check: not Is64BitInstallMode
Source: "redis-v2.4.6\redis-2.4.6-setup-64-bit.exe"; DestDir: "{app}"; DestName: "redisSetup.exe"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
Source: "couchDB\couchDBsetup.exe"; DestDir: "{app}"; Flags: ignoreversion deleteafterinstall;
Source: "install.js"; DestDir: "{app}"; Flags: ignoreversion
Source: "uninstall.js"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppIcon}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\.windows-ready\data\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\{#MyAppName} Settings"; Filename: "http://localhost:8080"; IconFilename: "{app}\{#MyAppIcon}"
Name: "{group}\{#MyAppName} Uninstall"; Filename: "{uninstallexe}"
Name: "{group}\Start {#MyAppName} Service"; Filename: "{app}\service_ioBroker.bat"; Parameters: "start"
Name: "{group}\Stop {#MyAppName} Service"; Filename: "{app}\service_ioBroker.bat"; Parameters: "stop"
Name: "{group}\Restart {#MyAppName} Service"; Filename: "{app}\service_ioBroker.bat"; Parameters: "restart"

[Code]
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  ResultCode: integer;
begin
  Result := '';
  if FileExists(ExpandConstant('{app}\restart_ccu_io.bat')) then begin
     Exec(ExpandConstant('{app}\restart_ccu_io.bat'), 'stop', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
  end;
end;

function FileReplaceString(const FileName, SearchString, ReplaceString: string):boolean;
var
  MyFile : TStrings;
  MyText : string;
begin
  MyFile := TStringList.Create;

  try
    result := true;

    try
      MyFile.LoadFromFile(FileName);
      MyText := MyFile.Text;

      if StringChangeEx(MyText, SearchString, ReplaceString, True) > 0 then //Only save if text has been changed.
      begin;
        MyFile.Text := MyText;
        MyFile.SaveToFile(FileName);
      end;
    except
      result := false;
    end;
  finally
    MyFile.Free;
  end;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  fileName : string;
  lang : string;
begin
  if  CurStep=ssPostInstall then
    begin
      lang := ActiveLanguage();
      if lang = 'russian' then begin
        lang := 'ru';
      end
      else begin
          if lang = 'german' then begin
            lang := 'de';
          end
          else begin
            lang := '';
          end;
      end;
      if lang <> '' then begin
          fileName := ExpandConstant('{app}\settings-dist.json');
          //FileReplaceString(fileName, '"language": "en"', '"language": "' + lang + '"');
      end;
    end;
end;

function RedisNeedsInstall():boolean;
begin
   result := not FileExists(ExpandConstant('{pf}\Redis\redis-service.exe'));
end;
function CouchNeedsInstall():boolean;
begin
   result := not FileExists(ExpandConstant('{pf32}\Apache Software Foundation\CouchDB\Install.exe'));
end;

[Run]
; postinstall launch
Filename: "{app}\redisSetup.exe"; Check: RedisNeedsInstall
Filename: "{app}\couchDBsetup.exe"; Parameters: "/SILENT"; Check: CouchNeedsInstall
Filename: "{sys}\net.exe"; Parameters: "start redis"
Filename: "{app}\install.bat"; Flags: runhidden;
Filename: "{app}\node.exe"; Parameters: "install.js"; Flags: runhidden;
Filename: "{app}\service_ioBroker.bat"; Parameters: "start"; Flags: runhidden;
; Add Firewall Rules
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall add rule name=""Node In"" program=""{app}\node.exe"" dir=in action=allow enable=yes"; Flags: runhidden;
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall add rule name=""Node Out"" program=""{app}\node.exe"" dir=out action=allow enable=yes"; Flags: runhidden;
Filename: http://localhost:8080/; Description: "Control page"; Flags: postinstall shellexec

[UninstallRun]
; Removes System Service
Filename: "{app}\node.exe"; Parameters: "uninstall.js"; Flags: runhidden;
Filename: "{sys}\net"; Parameters: "stop redis"; Flags: runhidden;
Filename: "{pf}\Redis\unins000.exe"
Filename: "{pf32}\Apache Software Foundation\CouchDB\unins000.exe"

; Remove Firewall Rules
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall delete rule name=""Node In"" program=""{app}\node.exe"""; Flags: runhidden;
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall delete rule name=""Node Out"" program=""{app}\node.exe"""; Flags: runhidden;

; Remove all leftovers
Filename: "{sys}\del"; Parameters: """{app}\daemon\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\daemon""";
Filename: "{sys}\del"; Parameters: """{app}\node_modules\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\node_modules""";
Filename: "{sys}\del"; Parameters: """{app}\conf\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\conf""";
Filename: "{sys}\del"; Parameters: """{app}\log\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\log""";
Filename: "{sys}\del"; Parameters: """{app}\adapter\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\adapter""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}""";

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "ioBroker automation platfrom on NodeJS"
#define MyAppShortName "ioBroker"
#define MyAppLCShortName "iobroker"
#define MyAppVersion "@@version"
#define MyAppPublisher "ioBroker.org"
#define MyAppURL "http://ioBroker.org/"
#define MyAppIcon "ioBroker.ico"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{97DA02F5-2E8C-4B96-BB42-61ED2BBF34DF}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\ioBroker
;DisableDirPage=yes
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
OutputDir=..\..\delivery
OutputBaseFilename={#MyAppShortName}Installer.{#MyAppVersion}
SetupIconFile={#MyAppIcon}
Compression=lzma
SolidCompression=yes
UsePreviousAppDir=yes
ArchitecturesInstallIn64BitMode=x64
UninstallDisplayIcon={app}\{#MyAppIcon}
CloseApplications=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "german";  MessagesFile: "compiler:Languages\German.isl"
Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl"

[Files]
Source: "nodejs-v0.10.29\node.exe"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "nodejs-v0.10.29\nodex64.exe"; DestDir: "{app}"; DestName: "node.exe"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "redis-v2.4.6\redis-2.4.6-setup-32-bit.exe"; DestDir: "{app}"; DestName: "redisSetup.exe"; Flags: ignoreversion deleteafterinstall; Check: not Is64BitInstallMode
Source: "redis-v2.4.6\redis-2.4.6-setup-64-bit.exe"; DestDir: "{app}"; DestName: "redisSetup.exe"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
Source: "couchDB\couchDBsetup.exe"; DestDir: "{app}"; Flags: ignoreversion deleteafterinstall;
Source: "install.js"; DestDir: "{app}"; Flags: ignoreversion
Source: "uninstall.js"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppIcon}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\.windows-ready\data\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\{#MyAppName} Settings"; Filename: "http://localhost:8080"; IconFilename: "{app}\{#MyAppIcon}"
Name: "{group}\{#MyAppName} Uninstall"; Filename: "{uninstallexe}"
Name: "{group}\Start {#MyAppName} Service"; Filename: "{app}\service_ioBroker.bat"; Parameters: "start"
Name: "{group}\Stop {#MyAppName} Service"; Filename: "{app}\service_ioBroker.bat"; Parameters: "stop"
Name: "{group}\Restart {#MyAppName} Service"; Filename: "{app}\service_ioBroker.bat"; Parameters: "restart"

[Code]
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  ResultCode: integer;
begin
  Result := '';
  if FileExists(ExpandConstant('{app}\restart_ccu_io.bat')) then begin
     Exec(ExpandConstant('{app}\restart_ccu_io.bat'), 'stop', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
  end;
end;

function FileReplaceString(const FileName, SearchString, ReplaceString: string):boolean;
var
  MyFile : TStrings;
  MyText : string;
begin
  MyFile := TStringList.Create;

  try
    result := true;

    try
      MyFile.LoadFromFile(FileName);
      MyText := MyFile.Text;

      if StringChangeEx(MyText, SearchString, ReplaceString, True) > 0 then //Only save if text has been changed.
      begin;
        MyFile.Text := MyText;
        MyFile.SaveToFile(FileName);
      end;
    except
      result := false;
    end;
  finally
    MyFile.Free;
  end;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  fileName : string;
  lang : string;
begin
  if  CurStep=ssPostInstall then
    begin
      lang := ActiveLanguage();
      if lang = 'russian' then begin
        lang := 'ru';
      end
      else begin
          if lang = 'german' then begin
            lang := 'de';
          end
          else begin
            lang := '';
          end;
      end;
      if lang <> '' then begin
          fileName := ExpandConstant('{app}\settings-dist.json');
          //FileReplaceString(fileName, '"language": "en"', '"language": "' + lang + '"');
      end;
    end;
end;

function RedisNeedsInstall():boolean;
begin
   result := not FileExists(ExpandConstant('{pf}\Redis\redis-service.exe'));
end;
function CouchNeedsInstall():boolean;
begin
   result := not FileExists(ExpandConstant('{pf32}\Apache Software Foundation\CouchDB\Install.exe'));
end;

[Run]
; postinstall launch
Filename: "{app}\redisSetup.exe"; Check: RedisNeedsInstall
Filename: "{app}\couchDBsetup.exe"; Parameters: "/SILENT"; Check: CouchNeedsInstall
Filename: "{sys}\net.exe"; Parameters: "start redis"
Filename: "{app}\node.exe"; Parameters: "install.js"; Flags: runhidden;
Filename: "{app}\install.bat"; Flags: runhidden;
Filename: "{app}\service_ioBroker.bat"; Parameters: "start"; Flags: runhidden;
; Add Firewall Rules
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall add rule name=""Node In"" program=""{app}\node.exe"" dir=in action=allow enable=yes"; Flags: runhidden;
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall add rule name=""Node Out"" program=""{app}\node.exe"" dir=out action=allow enable=yes"; Flags: runhidden;
Filename: http://localhost:8080/; Description: "Control page"; Flags: postinstall shellexec

[UninstallRun]
; Removes System Service
Filename: "{app}\node.exe"; Parameters: "uninstall.js"; Flags: runhidden;
Filename: "{sys}\net"; Parameters: "stop redis"; Flags: runhidden;
Filename: "{pf}\Redis\unins000.exe"
Filename: "{pf32}\Apache Software Foundation\CouchDB\unins000.exe"

; Remove Firewall Rules
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall delete rule name=""Node In"" program=""{app}\node.exe"""; Flags: runhidden;
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall delete rule name=""Node Out"" program=""{app}\node.exe"""; Flags: runhidden;

; Remove all leftovers
Filename: "{sys}\del"; Parameters: """{app}\daemon\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\daemon""";
Filename: "{sys}\del"; Parameters: """{app}\node_modules\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\node_modules""";
Filename: "{sys}\del"; Parameters: """{app}\conf\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\conf""";
Filename: "{sys}\del"; Parameters: """{app}\log\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\log""";
Filename: "{sys}\del"; Parameters: """{app}\adapter\*""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}\adapter""";
Filename: "{sys}\rmdir"; Parameters: "-r ""{app}""";


installers.windows: Set installer file version to plug-in version

; To successfully compile the installer, run `compile_installer.bat` instead
; with specified parameters.
;
; Code dealing with re-running installer with elevated privileges taken from:
; https://stackoverflow.com/a/35435534
;
; Custom parameters for installer:
; pluginpath - plug-in installation directory

#define PLUGIN_TITLE "Export Layers"
#define INSTALLER_NAME PLUGIN_TITLE + " for GIMP"

[Setup]
AppName={#INSTALLER_NAME}
AppVersion={#PLUGIN_VERSION}
AppVerName={#INSTALLER_NAME} {#PLUGIN_VERSION}
AppPublisher={#AUTHOR_NAME}
VersionInfoVersion={#PLUGIN_VERSION}
DefaultDirName={code:GetPluginsDirpath}
DefaultGroupName=GIMP
DisableProgramGroupPage=Yes
DisableDirPage=Yes
UninstallFilesDir={app}\{#PLUGIN_NAME}
PrivilegesRequired=lowest
DirExistsWarning=no
OutputDir={#OUTPUT_DIRPATH}
OutputBaseFilename={#OUTPUT_FILENAME_PREFIX}
WizardSmallImageFile=logo_wizard.bmp
WizardImageFile=menu_path.bmp
SetupIconFile=icon_installer.ico
UninstallDisplayIcon=logo.ico

[Files]
Source: {#INPUT_DIRPATH}\*; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Source: logo_wizard.bmp; Flags: dontcopy
Source: menu_path.bmp; Flags: dontcopy
Source: icon_installer.ico; Flags: dontcopy
Source: logo.ico; Flags: dontcopy

[Messages]
ReadyLabel2b=Click Install to continue with the installation of {#PLUGIN_TITLE}, or click Customize to modify the installation.
FinishedLabelNoIcons={#PLUGIN_TITLE} successfully installed. To run the plug-in, start GIMP (or restart if already running) and go to File → Export Layers.

[UninstallDelete]
Type: filesandordirs; Name: "{app}\{#PLUGIN_NAME}"

[Code]

#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

const
  GIMP_REG_PATH = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\GIMP-2_is1';
  GIMP_NOT_FOUND_MESSAGE = (
    'Could not find GIMP installation path.'
    + ' Please specify the path to GIMP and GIMP plug-ins manually.'
    + ' If GIMP is not installed, abort this installation and install GIMP first.');
  PYTHON_NOT_FOUND_IN_GIMP_MESSAGE = (
    'It appears that your GIMP installation does not support Python scripting.'
    + ' Please install GIMP with enabled support for Python scripting before proceeding.');
  NO_WRITE_PERMISSION_MESSAGE_PROMPT = (
    'It appears that you do not have permissions to install to directory "%s". '
    + 'Would you like to re-run the installer with administrator privileges?'
  );
  NO_WRITE_PERMISSION_MESSAGE = (
    'It appears that you do not have permissions to install to directory "%s". '
    + 'Please select a different directory.'
  );
  FAILED_TO_RUN_ELEVATED = (
    'Failed to run with administrator privileges. '
    + 'Try again or install to a directory with write permissions.'
  );

  PLUGIN_PATH_PARAM_NAME = 'pluginpath';

  MIN_REQUIRED_GIMP_VERSION_MAJOR = 2;
  MIN_REQUIRED_GIMP_VERSION_MINOR = 8;
  MIN_REQUIRED_GIMP_VERSION = '2.8';

  NUM_CUSTOM_PAGES = 2;

type
  TVersionArray = array [0..1] of Integer;

var
  PluginsDirpath: String;
  GimpDirpath: String;
  GimpVersionMajorMinor: TVersionArray;

  CustomPathsPage: TInputDirWizardPage;
  PluginsDirpathEdit: TEdit;
  GimpDirpathEdit: TEdit;

  SelectPluginInstallPathPage: TInputOptionWizardPage;

  IsGimpDetected: Boolean;
  InstallerState: (Initialized, ReadyToInstall);

  CustomizeButton: TNewButton;

procedure AddCustomizeToInstallPage; forward;
procedure OnCustomizeClicked(sender: TObject); forward;
function GetButtonWidthFitToCaption(const caption: String; const xSpacing: Integer) : Integer; forward;

procedure CreateSelectPluginInstallPathPage(const afterID: Integer); forward;
procedure CreateCustomPathsPage(const afterID: Integer); forward;

function ShouldSpecifyCustomPluginsDirpath : Boolean; forward;
procedure CheckPythonScriptingEnabled; forward;

function GetLocalPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String; forward;
function GetSystemPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String; forward;
function GetGimpVersionMajorMinor(const gimpVersion: String) : TVersionArray; forward;
function GetGimpVersionStr(const gimpVersionArray: array of Integer) : String; forward;

function HasElevatedPrivileges : Boolean; forward;
function HasWritePermission(const dirpath: String) : Boolean; forward;
function CheckWritePermission(const dirpath: String; out shouldElevate: Boolean) : Boolean; forward;
function CreateDirRecursive(const dirpath: String; out nonexistentDirpaths: array of String) : Boolean; forward;
function RemoveDirs(const dirpaths: array of String) : Boolean; forward;
function RunWithElevatedPrivileges : Boolean; forward;

procedure ExitProcess(uExitCode: UINT); external 'ExitProcess@kernel32.dll stdcall';
function ShellExecute(hwnd: HWND; lpOperation, lpFile, lpParameters, lpDirectory: String; nShowCmd: Integer) : THandle; external 'ShellExecute{#AW}@shell32.dll stdcall';

function GetPluginsDirpath(value: String) : String;
begin
  Result := PluginsDirpath;
end;

function InitializeSetup() : Boolean;
var
  gimpVersion: String;
begin
  Result := True;

  InstallerState := Initialized;

  IsGimpDetected := (
    RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'DisplayVersion', gimpVersion)
    or RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'DisplayVersion', gimpVersion));

  if not IsGimpDetected then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    Exit;
  end;

  GimpVersionMajorMinor := GetGimpVersionMajorMinor(gimpVersion);

  if ((GimpVersionMajorMinor[0] <= MIN_REQUIRED_GIMP_VERSION_MAJOR)
      and (GimpVersionMajorMinor[1] < MIN_REQUIRED_GIMP_VERSION_MINOR)) then begin
    MsgBox(
      'GIMP version ' + GetGimpVersionStr(GimpVersionMajorMinor) + ' detected.'
      + ' To use {#PLUGIN_TITLE}, install GIMP ' + MIN_REQUIRED_GIMP_VERSION + ' or later.'
      + ' If you do have GIMP ' + MIN_REQUIRED_GIMP_VERSION + ' or later installed, '
      + 'specify the path to GIMP and GIMP plug-ins manually.'
      + ' Otherwise, abort this installation and install GIMP with a sufficient version first.',
      mbInformation,
      MB_OK);

      IsGimpDetected := False;
      Exit;
  end;

  PluginsDirpath := ExpandConstant(
    '{param:' + PLUGIN_PATH_PARAM_NAME + '|' + GetLocalPluginsDirpath(GimpVersionMajorMinor) + '}');

  IsGimpDetected := (
    RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'InstallLocation', GimpDirpath)
    or RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'InstallLocation', GimpDirpath));

  if not IsGimpDetected then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    Exit;
  end
  else begin
    if GimpDirpath[Length(GimpDirpath)] = '\' then begin
      GimpDirpath := Copy(GimpDirpath, 1, Length(GimpDirpath) - 1);
    end;
  end;

  CheckPythonScriptingEnabled();
end;

procedure InitializeWizard;
begin
  AddCustomizeToInstallPage();

  CreateSelectPluginInstallPathPage(wpWelcome);
  CreateCustomPathsPage(SelectPluginInstallPathPage.ID);
end;

function NextButtonClick(curPageID: Integer) : Boolean;
var
  shouldElevate: Boolean;
begin
  Result := True;
  shouldElevate := False;

  if curPageID = CustomPathsPage.ID then begin
    GimpDirpath := GimpDirpathEdit.Text;
    PluginsDirpath := PluginsDirpathEdit.Text;

    CheckPythonScriptingEnabled();
    Result := CheckWritePermission(PluginsDirpath, shouldElevate);
  end
  else if curPageID = SelectPluginInstallPathPage.ID then begin
    if SelectPluginInstallPathPage.values[0] then begin
      PluginsDirpath := GetLocalPluginsDirpath(GimpVersionMajorMinor);
      Result := CheckWritePermission(PluginsDirpath, shouldElevate);
    end
    else if SelectPluginInstallPathPage.values[1] then begin
      PluginsDirpath := GetSystemPluginsDirpath(GimpVersionMajorMinor);
      Result := CheckWritePermission(PluginsDirpath, shouldElevate);
    end;

    PluginsDirpathEdit.Text := PluginsDirpath;
  end;

  if shouldElevate then begin
    RunWithElevatedPrivileges();
  end;
end;

function ShouldSkipPage(pageID: Integer) : Boolean;
var
  isInstallerStarted: Boolean;
  isInstallerStartedWithGimpNotDetected: Boolean;
  shouldSkipCustomPathsPage: Boolean;
begin
  isInstallerStarted := (
    (pageID <> wpReady)
    and (InstallerState = Initialized)
    and IsGimpDetected);

  isInstallerStartedWithGimpNotDetected := (
    (pageID = SelectPluginInstallPathPage.ID)
    and (InstallerState = Initialized)
    and not IsGimpDetected);

  shouldSkipCustomPathsPage := (
    (pageID = CustomPathsPage.ID)
    and (InstallerState <> Initialized)
    and not ShouldSpecifyCustomPluginsDirpath());

  Result := (
    isInstallerStarted
    or isInstallerStartedWithGimpNotDetected
    or shouldSkipCustomPathsPage);
end;

procedure CurPageChanged(curPageID: Integer);
begin
  CustomizeButton.visible := (curPageID = wpReady) and IsGimpDetected;

  if (curPageID = CustomPathsPage.ID) and not IsGimpDetected then begin
    WizardForm.BackButton.visible := False;
  end;

  if curPageID = wpReady then begin
    InstallerState := ReadyToInstall;

    // Assign correct value to `DefaultDirName` (it may be empty or incorrect at this point).
    WizardForm.DirEdit.Text := PluginsDirpath;
  end;
end;

procedure AddCustomizeToInstallPage;
begin
  CustomizeButton := TNewButton.Create(WizardForm);

  with CustomizeButton do begin
    Caption := 'Customize';
    Parent := WizardForm;
    Width := GetButtonWidthFitToCaption(Caption, 12);
    Height := WizardForm.NextButton.Height;
    Left := WizardForm.ClientWidth - (WizardForm.CancelButton.Left + WizardForm.CancelButton.Width);
    Top := WizardForm.NextButton.Top;

    OnClick := @OnCustomizeClicked;
  end;
end;

procedure OnCustomizeClicked(sender: TObject);
var
  i: Integer;
begin
  for i := 0 to NUM_CUSTOM_PAGES - 1 do begin
    WizardForm.BackButton.OnClick(TNewButton(sender).Parent);
  end;
end;

function GetButtonWidthFitToCaption(const caption: String; const xSpacing: Integer) : Integer;
var
  dummyLabel: TNewStaticText;
  defaultWidth: Integer;
begin
  dummyLabel := TNewStaticText.Create(WizardForm);

  dummyLabel.Autosize := True;
  dummyLabel.Caption := caption;

  defaultWidth := WizardForm.NextButton.Width;

  if defaultWidth >= dummyLabel.Width + ScaleX(xSpacing) then begin
    Result := defaultWidth;
  end
  else begin
    Result := dummyLabel.Width + ScaleX(xSpacing);
  end;

  dummyLabel.Free;
end;

procedure CreateSelectPluginInstallPathPage(const afterID: Integer);
begin
  SelectPluginInstallPathPage := CreateInputOptionPage(
    afterID,
    'Select Installation Path',
    'Where should {#PLUGIN_TITLE} be installed?',
    'Select one of the options below:',
    True,
    False
  );

  SelectPluginInstallPathPage.Add('Install for just me');
  SelectPluginInstallPathPage.Add('Install for all users');
  SelectPluginInstallPathPage.Add('Choose custom installation path');

  SelectPluginInstallPathPage.Values[0] := True;
end;

procedure CreateCustomPathsPage(const afterID: Integer);
var
  lastAddedDirIndex: Integer;
begin
  CustomPathsPage := CreateInputDirPage(
    afterID,
    'Select Location for GIMP and GIMP Plug-ins',
    'Where is GIMP located and where should {#PLUGIN_TITLE} be installed?',
    'Specify custom directory path to a working GIMP installation and to GIMP plug-ins.',
    False,
    'New Folder');

  lastAddedDirIndex := CustomPathsPage.Add('Path to GIMP installation');
  GimpDirpathEdit := CustomPathsPage.Edits[lastAddedDirIndex];
  CustomPathsPage.Values[0] := GimpDirpath;

  lastAddedDirIndex := CustomPathsPage.Add('Path to GIMP plug-ins');
  PluginsDirpathEdit := CustomPathsPage.Edits[lastAddedDirIndex];
  CustomPathsPage.Values[1] := PluginsDirpath;
end;

function ShouldSpecifyCustomPluginsDirpath : Boolean;
begin
  Result := SelectPluginInstallPathPage.values[2];
end;

procedure CheckPythonScriptingEnabled;
var
  possiblePythonDirpaths: TStringList;
  i: Integer;
  pythonPathFound: Boolean;
begin
  pythonPathFound := False;

  possiblePythonDirpaths := TStringList.Create;
  possiblePythonDirpaths.Add(GimpDirpath + '\Python');
  possiblePythonDirpaths.Add(GimpDirpath + '\32\lib\python2.7');
  possiblePythonDirpaths.Add(GimpDirpath + '\lib\python2.7');

  for i := 0 to possiblePythonDirpaths.Count - 1 do begin
    if DirExists(possiblePythonDirpaths[i]) then begin
      pythonPathFound := True;
      break;
    end;
  end;

  possiblePythonDirpaths.Free;

  if not pythonPathFound then begin
    MsgBox(PYTHON_NOT_FOUND_IN_GIMP_MESSAGE, mbInformation, MB_OK);
    Abort();
  end;
end;

function GetLocalPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String;
var
  gimpVersionMajorMinorStr: String;
begin
  gimpVersionMajorMinorStr := GetGimpVersionStr(gimpVersionMajorMinor);

  if (gimpVersionMajorMinor[0] <= 2) and (gimpVersionMajorMinor[1] < 9) then begin
    Result := ExpandConstant('{%USERPROFILE}') + '\.gimp-' + gimpVersionMajorMinorStr + '\plug-ins';
  end
  else begin
    Result := ExpandConstant('{userappdata}') + '\GIMP\' + gimpVersionMajorMinorStr + '\plug-ins';
  end;
end;

function GetSystemPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String;
begin
  Result := GimpDirpath + '\lib\gimp\' + IntToStr(gimpVersionMajorMinor[0]) + '.0\plug-ins';
end;

function GetGimpVersionMajorMinor(const gimpVersion: String) : TVersionArray;
var
  versionNumberMajorMinor: TVersionArray;
  i: Integer;
  versionNumberFields: array of Integer;
  versionNumberFieldCurrentArrayIndex: Integer;
  versionNumberFieldStartIndex: Integer;
begin
  versionNumberFieldCurrentArrayIndex := 0;
  versionNumberFieldStartIndex := 1;

  for i := 1 to Length(gimpVersion) do begin
    if gimpVersion[i] = '.' then begin
      SetArrayLength(versionNumberFields, GetArrayLength(versionNumberFields) + 1);
      versionNumberFields[versionNumberFieldCurrentArrayIndex] := (
        StrToIntDef(
          Copy(
            gimpVersion, versionNumberFieldStartIndex, i - versionNumberFieldStartIndex), -1));
      Inc(versionNumberFieldCurrentArrayIndex);
      versionNumberFieldStartIndex := i + 1;
    end;
  end;

  if versionNumberFieldStartIndex <= Length(gimpVersion) then begin
    SetArrayLength(versionNumberFields, GetArrayLength(versionNumberFields) + 1);
    versionNumberFields[versionNumberFieldCurrentArrayIndex] := (
      StrToIntDef(
        Copy(
          gimpVersion, versionNumberFieldStartIndex, Length(gimpVersion) + 1 - versionNumberFieldStartIndex), -1));
  end;

  versionNumberMajorMinor[0] := versionNumberFields[0];
  versionNumberMajorMinor[1] := versionNumberFields[1];

  Result := versionNumberMajorMinor;
end;

function GetGimpVersionStr(const gimpVersionArray: array of Integer) : String;
var
  i: Integer;
  gimpVersionStr: String;
begin
  gimpVersionStr := '';

  for i := 0 to Length(gimpVersionArray) - 1 do begin
    gimpVersionStr := gimpVersionStr + IntToStr(gimpVersionArray[i]) + '.';
  end;

  Result := Copy(gimpVersionStr, 1, Length(gimpVersionStr) - 1);
end;

function HasElevatedPrivileges : Boolean;
begin
  Result := IsAdminLoggedOn or IsPowerUserLoggedOn;
end;

function HasWritePermission(const dirpath: String) : Boolean;
var
  tempFilepath: String;
  directoryCreated: Boolean;
  nonexistentDirpaths: array of String;
begin
  repeat
    tempFilepath := dirpath + '\' + 'temp_' + IntToStr(Random(MAXINT));
  until not FileExists(tempFilepath);

  if not DirExists(dirpath) then begin
    directoryCreated := CreateDirRecursive(dirpath, nonexistentDirpaths);
  end
  else begin
    directoryCreated := False;
  end;

  Result := SaveStringToFile(tempFilepath, 'test', False);

  if Result then begin
    DeleteFile(tempFilepath);
  end;

  if directoryCreated then begin
    RemoveDirs(nonexistentDirpaths);
  end;
end;

function CheckWritePermission(const dirpath: String; out shouldElevate: Boolean) : Boolean;
var
  noWritePermissionPromptResult: Integer;
begin
  Result := HasWritePermission(dirpath);
  shouldElevate := False;

  if not Result then begin
    if not HasElevatedPrivileges() then begin
      noWritePermissionPromptResult := MsgBox(
        Format(NO_WRITE_PERMISSION_MESSAGE_PROMPT, [dirpath]), mbConfirmation, MB_YESNO);
      shouldElevate := noWritePermissionPromptResult = IDYES;
    end
    else begin
      MsgBox(
        Format(NO_WRITE_PERMISSION_MESSAGE, [dirpath]), mbInformation, MB_OK);
    end;
  end;
end;

function CreateDirRecursive(const dirpath: String; out nonexistentDirpaths: array of String) : Boolean;
var
  currentDirectory: String;
  nonexistentDirpathsIndex: Integer;
begin
  nonexistentDirpathsIndex := 0;
  currentDirectory := dirpath;

  while not DirExists(currentDirectory) do begin
    SetArrayLength(nonexistentDirpaths, GetArrayLength(nonexistentDirpaths) + 1);
    nonexistentDirpaths[nonexistentDirpathsIndex] := currentDirectory;
    currentDirectory := ExtractFileDir(currentDirectory);
    Inc(nonexistentDirpathsIndex);
  end;

  Result := True;

  for nonexistentDirpathsIndex := GetArrayLength(nonexistentDirpaths) - 1 downto 0 do begin
    Result := Result and CreateDir(nonexistentDirpaths[nonexistentDirpathsIndex]);
  end;
end;

function RemoveDirs(const dirpaths: array of String) : Boolean;
var
  dirpathsIndex: Integer;
begin
  Result := True;

  for dirpathsIndex := 0 to GetArrayLength(dirpaths) - 1 do begin
    Result := Result and RemoveDir(dirpaths[dirpathsIndex]);
  end;
end;

function RunWithElevatedPrivileges : Boolean;
var
  shellExecuteRetVal: Integer;
  params: String;
  paramIndex: Integer;
begin
  for paramIndex := 1 to ParamCount do begin
    params := params + AddQuotes(ParamStr(paramIndex)) + ' ';
  end;

  params := params + AddQuotes('/LANG=' + ActiveLanguage) + ' ';
  params := params + AddQuotes('/' + PLUGIN_PATH_PARAM_NAME + '=' + PluginsDirpath);

  shellExecuteRetVal := ShellExecute(0, 'runas', ExpandConstant('{srcexe}'), params, '', SW_SHOW);

  Result := shellExecuteRetVal > 32;

  if Result then begin
    ExitProcess(0);
  end
  else begin
    MsgBox(FAILED_TO_RUN_ELEVATED, mbInformation, MB_OK);
  end;
end;

; To successfully compile the installer, run `compile_installer.bat` instead
; with specified parameters.
;
; Code dealing with re-running installer with elevated privileges taken from:
; https://stackoverflow.com/a/35435534
;
; Custom parameters for installer:
; pluginpath - plug-in installation directory

#define PLUGIN_TITLE "Export Layers"
#define INSTALLER_NAME PLUGIN_TITLE + " for GIMP"

[Setup]
AppName={#INSTALLER_NAME}
AppVersion={#PLUGIN_VERSION}
AppVerName={#INSTALLER_NAME} {#PLUGIN_VERSION}
AppPublisher={#AUTHOR_NAME}
DefaultDirName={code:GetPluginsDirpath}
DefaultGroupName=GIMP
DisableProgramGroupPage=Yes
DisableDirPage=Yes
UninstallFilesDir={app}\{#PLUGIN_NAME}
PrivilegesRequired=lowest
DirExistsWarning=no
OutputDir={#OUTPUT_DIRPATH}
OutputBaseFilename={#OUTPUT_FILENAME_PREFIX}
WizardSmallImageFile=logo_wizard.bmp
WizardImageFile=menu_path.bmp
SetupIconFile=icon_installer.ico
UninstallDisplayIcon=logo.ico

[Files]
Source: {#INPUT_DIRPATH}\*; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Source: logo_wizard.bmp; Flags: dontcopy
Source: menu_path.bmp; Flags: dontcopy
Source: icon_installer.ico; Flags: dontcopy
Source: logo.ico; Flags: dontcopy

[Messages]
ReadyLabel2b=Click Install to continue with the installation of {#PLUGIN_TITLE}, or click Customize to modify the installation.
FinishedLabelNoIcons={#PLUGIN_TITLE} successfully installed. To run the plug-in, start GIMP (or restart if already running) and go to File → Export Layers.

[UninstallDelete]
Type: filesandordirs; Name: "{app}\{#PLUGIN_NAME}"

[Code]

#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

const
  GIMP_REG_PATH = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\GIMP-2_is1';
  GIMP_NOT_FOUND_MESSAGE = (
    'Could not find GIMP installation path.'
    + ' Please specify the path to GIMP and GIMP plug-ins manually.'
    + ' If GIMP is not installed, abort this installation and install GIMP first.');
  PYTHON_NOT_FOUND_IN_GIMP_MESSAGE = (
    'It appears that your GIMP installation does not support Python scripting.'
    + ' Please install GIMP with enabled support for Python scripting before proceeding.');
  NO_WRITE_PERMISSION_MESSAGE_PROMPT = (
    'It appears that you do not have permissions to install to directory "%s". '
    + 'Would you like to re-run the installer with administrator privileges?'
  );
  NO_WRITE_PERMISSION_MESSAGE = (
    'It appears that you do not have permissions to install to directory "%s". '
    + 'Please select a different directory.'
  );
  FAILED_TO_RUN_ELEVATED = (
    'Failed to run with administrator privileges. '
    + 'Try again or install to a directory with write permissions.'
  );

  PLUGIN_PATH_PARAM_NAME = 'pluginpath';

  MIN_REQUIRED_GIMP_VERSION_MAJOR = 2;
  MIN_REQUIRED_GIMP_VERSION_MINOR = 8;
  MIN_REQUIRED_GIMP_VERSION = '2.8';

  NUM_CUSTOM_PAGES = 2;

type
  TVersionArray = array [0..1] of Integer;

var
  PluginsDirpath: String;
  GimpDirpath: String;
  GimpVersionMajorMinor: TVersionArray;

  CustomPathsPage: TInputDirWizardPage;
  PluginsDirpathEdit: TEdit;
  GimpDirpathEdit: TEdit;

  SelectPluginInstallPathPage: TInputOptionWizardPage;

  IsGimpDetected: Boolean;
  InstallerState: (Initialized, ReadyToInstall);

  CustomizeButton: TNewButton;

procedure AddCustomizeToInstallPage; forward;
procedure OnCustomizeClicked(sender: TObject); forward;
function GetButtonWidthFitToCaption(const caption: String; const xSpacing: Integer) : Integer; forward;

procedure CreateSelectPluginInstallPathPage(const afterID: Integer); forward;
procedure CreateCustomPathsPage(const afterID: Integer); forward;

function ShouldSpecifyCustomPluginsDirpath : Boolean; forward;
procedure CheckPythonScriptingEnabled; forward;

function GetLocalPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String; forward;
function GetSystemPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String; forward;
function GetGimpVersionMajorMinor(const gimpVersion: String) : TVersionArray; forward;
function GetGimpVersionStr(const gimpVersionArray: array of Integer) : String; forward;

function HasElevatedPrivileges : Boolean; forward;
function HasWritePermission(const dirpath: String) : Boolean; forward;
function CheckWritePermission(const dirpath: String; out shouldElevate: Boolean) : Boolean; forward;
function CreateDirRecursive(const dirpath: String; out nonexistentDirpaths: array of String) : Boolean; forward;
function RemoveDirs(const dirpaths: array of String) : Boolean; forward;
function RunWithElevatedPrivileges : Boolean; forward;

procedure ExitProcess(uExitCode: UINT); external 'ExitProcess@kernel32.dll stdcall';
function ShellExecute(hwnd: HWND; lpOperation, lpFile, lpParameters, lpDirectory: String; nShowCmd: Integer) : THandle; external 'ShellExecute{#AW}@shell32.dll stdcall';

function GetPluginsDirpath(value: String) : String;
begin
  Result := PluginsDirpath;
end;

function InitializeSetup() : Boolean;
var
  gimpVersion: String;
begin
  Result := True;

  InstallerState := Initialized;

  IsGimpDetected := (
    RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'DisplayVersion', gimpVersion)
    or RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'DisplayVersion', gimpVersion));

  if not IsGimpDetected then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    Exit;
  end;

  GimpVersionMajorMinor := GetGimpVersionMajorMinor(gimpVersion);

  if ((GimpVersionMajorMinor[0] <= MIN_REQUIRED_GIMP_VERSION_MAJOR)
      and (GimpVersionMajorMinor[1] < MIN_REQUIRED_GIMP_VERSION_MINOR)) then begin
    MsgBox(
      'GIMP version ' + GetGimpVersionStr(GimpVersionMajorMinor) + ' detected.'
      + ' To use {#PLUGIN_TITLE}, install GIMP ' + MIN_REQUIRED_GIMP_VERSION + ' or later.'
      + ' If you do have GIMP ' + MIN_REQUIRED_GIMP_VERSION + ' or later installed, '
      + 'specify the path to GIMP and GIMP plug-ins manually.'
      + ' Otherwise, abort this installation and install GIMP with a sufficient version first.',
      mbInformation,
      MB_OK);

      IsGimpDetected := False;
      Exit;
  end;

  PluginsDirpath := ExpandConstant(
    '{param:' + PLUGIN_PATH_PARAM_NAME + '|' + GetLocalPluginsDirpath(GimpVersionMajorMinor) + '}');

  IsGimpDetected := (
    RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'InstallLocation', GimpDirpath)
    or RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'InstallLocation', GimpDirpath));

  if not IsGimpDetected then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    Exit;
  end
  else begin
    if GimpDirpath[Length(GimpDirpath)] = '\' then begin
      GimpDirpath := Copy(GimpDirpath, 1, Length(GimpDirpath) - 1);
    end;
  end;

  CheckPythonScriptingEnabled();
end;

procedure InitializeWizard;
begin
  AddCustomizeToInstallPage();

  CreateSelectPluginInstallPathPage(wpWelcome);
  CreateCustomPathsPage(SelectPluginInstallPathPage.ID);
end;

function NextButtonClick(curPageID: Integer) : Boolean;
var
  shouldElevate: Boolean;
begin
  Result := True;
  shouldElevate := False;

  if curPageID = CustomPathsPage.ID then begin
    GimpDirpath := GimpDirpathEdit.Text;
    PluginsDirpath := PluginsDirpathEdit.Text;

    CheckPythonScriptingEnabled();
    Result := CheckWritePermission(PluginsDirpath, shouldElevate);
  end
  else if curPageID = SelectPluginInstallPathPage.ID then begin
    if SelectPluginInstallPathPage.values[0] then begin
      PluginsDirpath := GetLocalPluginsDirpath(GimpVersionMajorMinor);
      Result := CheckWritePermission(PluginsDirpath, shouldElevate);
    end
    else if SelectPluginInstallPathPage.values[1] then begin
      PluginsDirpath := GetSystemPluginsDirpath(GimpVersionMajorMinor);
      Result := CheckWritePermission(PluginsDirpath, shouldElevate);
    end;

    PluginsDirpathEdit.Text := PluginsDirpath;
  end;

  if shouldElevate then begin
    RunWithElevatedPrivileges();
  end;
end;

function ShouldSkipPage(pageID: Integer) : Boolean;
var
  isInstallerStarted: Boolean;
  isInstallerStartedWithGimpNotDetected: Boolean;
  shouldSkipCustomPathsPage: Boolean;
begin
  isInstallerStarted := (
    (pageID <> wpReady)
    and (InstallerState = Initialized)
    and IsGimpDetected);

  isInstallerStartedWithGimpNotDetected := (
    (pageID = SelectPluginInstallPathPage.ID)
    and (InstallerState = Initialized)
    and not IsGimpDetected);

  shouldSkipCustomPathsPage := (
    (pageID = CustomPathsPage.ID)
    and (InstallerState <> Initialized)
    and not ShouldSpecifyCustomPluginsDirpath());

  Result := (
    isInstallerStarted
    or isInstallerStartedWithGimpNotDetected
    or shouldSkipCustomPathsPage);
end;

procedure CurPageChanged(curPageID: Integer);
begin
  CustomizeButton.visible := (curPageID = wpReady) and IsGimpDetected;

  if (curPageID = CustomPathsPage.ID) and not IsGimpDetected then begin
    WizardForm.BackButton.visible := False;
  end;

  if curPageID = wpReady then begin
    InstallerState := ReadyToInstall;

    // Assign correct value to `DefaultDirName` (it may be empty or incorrect at this point).
    WizardForm.DirEdit.Text := PluginsDirpath;
  end;
end;

procedure AddCustomizeToInstallPage;
begin
  CustomizeButton := TNewButton.Create(WizardForm);

  with CustomizeButton do begin
    Caption := 'Customize';
    Parent := WizardForm;
    Width := GetButtonWidthFitToCaption(Caption, 12);
    Height := WizardForm.NextButton.Height;
    Left := WizardForm.ClientWidth - (WizardForm.CancelButton.Left + WizardForm.CancelButton.Width);
    Top := WizardForm.NextButton.Top;

    OnClick := @OnCustomizeClicked;
  end;
end;

procedure OnCustomizeClicked(sender: TObject);
var
  i: Integer;
begin
  for i := 0 to NUM_CUSTOM_PAGES - 1 do begin
    WizardForm.BackButton.OnClick(TNewButton(sender).Parent);
  end;
end;

function GetButtonWidthFitToCaption(const caption: String; const xSpacing: Integer) : Integer;
var
  dummyLabel: TNewStaticText;
  defaultWidth: Integer;
begin
  dummyLabel := TNewStaticText.Create(WizardForm);

  dummyLabel.Autosize := True;
  dummyLabel.Caption := caption;

  defaultWidth := WizardForm.NextButton.Width;

  if defaultWidth >= dummyLabel.Width + ScaleX(xSpacing) then begin
    Result := defaultWidth;
  end
  else begin
    Result := dummyLabel.Width + ScaleX(xSpacing);
  end;

  dummyLabel.Free;
end;

procedure CreateSelectPluginInstallPathPage(const afterID: Integer);
begin
  SelectPluginInstallPathPage := CreateInputOptionPage(
    afterID,
    'Select Installation Path',
    'Where should {#PLUGIN_TITLE} be installed?',
    'Select one of the options below:',
    True,
    False
  );

  SelectPluginInstallPathPage.Add('Install for just me');
  SelectPluginInstallPathPage.Add('Install for all users');
  SelectPluginInstallPathPage.Add('Choose custom installation path');

  SelectPluginInstallPathPage.Values[0] := True;
end;

procedure CreateCustomPathsPage(const afterID: Integer);
var
  lastAddedDirIndex: Integer;
begin
  CustomPathsPage := CreateInputDirPage(
    afterID,
    'Select Location for GIMP and GIMP Plug-ins',
    'Where is GIMP located and where should {#PLUGIN_TITLE} be installed?',
    'Specify custom directory path to a working GIMP installation and to GIMP plug-ins.',
    False,
    'New Folder');

  lastAddedDirIndex := CustomPathsPage.Add('Path to GIMP installation');
  GimpDirpathEdit := CustomPathsPage.Edits[lastAddedDirIndex];
  CustomPathsPage.Values[0] := GimpDirpath;

  lastAddedDirIndex := CustomPathsPage.Add('Path to GIMP plug-ins');
  PluginsDirpathEdit := CustomPathsPage.Edits[lastAddedDirIndex];
  CustomPathsPage.Values[1] := PluginsDirpath;
end;

function ShouldSpecifyCustomPluginsDirpath : Boolean;
begin
  Result := SelectPluginInstallPathPage.values[2];
end;

procedure CheckPythonScriptingEnabled;
var
  possiblePythonDirpaths: TStringList;
  i: Integer;
  pythonPathFound: Boolean;
begin
  pythonPathFound := False;

  possiblePythonDirpaths := TStringList.Create;
  possiblePythonDirpaths.Add(GimpDirpath + '\Python');
  possiblePythonDirpaths.Add(GimpDirpath + '\32\lib\python2.7');
  possiblePythonDirpaths.Add(GimpDirpath + '\lib\python2.7');

  for i := 0 to possiblePythonDirpaths.Count - 1 do begin
    if DirExists(possiblePythonDirpaths[i]) then begin
      pythonPathFound := True;
      break;
    end;
  end;

  possiblePythonDirpaths.Free;

  if not pythonPathFound then begin
    MsgBox(PYTHON_NOT_FOUND_IN_GIMP_MESSAGE, mbInformation, MB_OK);
    Abort();
  end;
end;

function GetLocalPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String;
var
  gimpVersionMajorMinorStr: String;
begin
  gimpVersionMajorMinorStr := GetGimpVersionStr(gimpVersionMajorMinor);

  if (gimpVersionMajorMinor[0] <= 2) and (gimpVersionMajorMinor[1] < 9) then begin
    Result := ExpandConstant('{%USERPROFILE}') + '\.gimp-' + gimpVersionMajorMinorStr + '\plug-ins';
  end
  else begin
    Result := ExpandConstant('{userappdata}') + '\GIMP\' + gimpVersionMajorMinorStr + '\plug-ins';
  end;
end;

function GetSystemPluginsDirpath(const gimpVersionMajorMinor: TVersionArray) : String;
begin
  Result := GimpDirpath + '\lib\gimp\' + IntToStr(gimpVersionMajorMinor[0]) + '.0\plug-ins';
end;

function GetGimpVersionMajorMinor(const gimpVersion: String) : TVersionArray;
var
  versionNumberMajorMinor: TVersionArray;
  i: Integer;
  versionNumberFields: array of Integer;
  versionNumberFieldCurrentArrayIndex: Integer;
  versionNumberFieldStartIndex: Integer;
begin
  versionNumberFieldCurrentArrayIndex := 0;
  versionNumberFieldStartIndex := 1;

  for i := 1 to Length(gimpVersion) do begin
    if gimpVersion[i] = '.' then begin
      SetArrayLength(versionNumberFields, GetArrayLength(versionNumberFields) + 1);
      versionNumberFields[versionNumberFieldCurrentArrayIndex] := (
        StrToIntDef(
          Copy(
            gimpVersion, versionNumberFieldStartIndex, i - versionNumberFieldStartIndex), -1));
      Inc(versionNumberFieldCurrentArrayIndex);
      versionNumberFieldStartIndex := i + 1;
    end;
  end;

  if versionNumberFieldStartIndex <= Length(gimpVersion) then begin
    SetArrayLength(versionNumberFields, GetArrayLength(versionNumberFields) + 1);
    versionNumberFields[versionNumberFieldCurrentArrayIndex] := (
      StrToIntDef(
        Copy(
          gimpVersion, versionNumberFieldStartIndex, Length(gimpVersion) + 1 - versionNumberFieldStartIndex), -1));
  end;

  versionNumberMajorMinor[0] := versionNumberFields[0];
  versionNumberMajorMinor[1] := versionNumberFields[1];

  Result := versionNumberMajorMinor;
end;

function GetGimpVersionStr(const gimpVersionArray: array of Integer) : String;
var
  i: Integer;
  gimpVersionStr: String;
begin
  gimpVersionStr := '';

  for i := 0 to Length(gimpVersionArray) - 1 do begin
    gimpVersionStr := gimpVersionStr + IntToStr(gimpVersionArray[i]) + '.';
  end;

  Result := Copy(gimpVersionStr, 1, Length(gimpVersionStr) - 1);
end;

function HasElevatedPrivileges : Boolean;
begin
  Result := IsAdminLoggedOn or IsPowerUserLoggedOn;
end;

function HasWritePermission(const dirpath: String) : Boolean;
var
  tempFilepath: String;
  directoryCreated: Boolean;
  nonexistentDirpaths: array of String;
begin
  repeat
    tempFilepath := dirpath + '\' + 'temp_' + IntToStr(Random(MAXINT));
  until not FileExists(tempFilepath);

  if not DirExists(dirpath) then begin
    directoryCreated := CreateDirRecursive(dirpath, nonexistentDirpaths);
  end
  else begin
    directoryCreated := False;
  end;

  Result := SaveStringToFile(tempFilepath, 'test', False);

  if Result then begin
    DeleteFile(tempFilepath);
  end;

  if directoryCreated then begin
    RemoveDirs(nonexistentDirpaths);
  end;
end;

function CheckWritePermission(const dirpath: String; out shouldElevate: Boolean) : Boolean;
var
  noWritePermissionPromptResult: Integer;
begin
  Result := HasWritePermission(dirpath);
  shouldElevate := False;

  if not Result then begin
    if not HasElevatedPrivileges() then begin
      noWritePermissionPromptResult := MsgBox(
        Format(NO_WRITE_PERMISSION_MESSAGE_PROMPT, [dirpath]), mbConfirmation, MB_YESNO);
      shouldElevate := noWritePermissionPromptResult = IDYES;
    end
    else begin
      MsgBox(
        Format(NO_WRITE_PERMISSION_MESSAGE, [dirpath]), mbInformation, MB_OK);
    end;
  end;
end;

function CreateDirRecursive(const dirpath: String; out nonexistentDirpaths: array of String) : Boolean;
var
  currentDirectory: String;
  nonexistentDirpathsIndex: Integer;
begin
  nonexistentDirpathsIndex := 0;
  currentDirectory := dirpath;

  while not DirExists(currentDirectory) do begin
    SetArrayLength(nonexistentDirpaths, GetArrayLength(nonexistentDirpaths) + 1);
    nonexistentDirpaths[nonexistentDirpathsIndex] := currentDirectory;
    currentDirectory := ExtractFileDir(currentDirectory);
    Inc(nonexistentDirpathsIndex);
  end;

  Result := True;

  for nonexistentDirpathsIndex := GetArrayLength(nonexistentDirpaths) - 1 downto 0 do begin
    Result := Result and CreateDir(nonexistentDirpaths[nonexistentDirpathsIndex]);
  end;
end;

function RemoveDirs(const dirpaths: array of String) : Boolean;
var
  dirpathsIndex: Integer;
begin
  Result := True;

  for dirpathsIndex := 0 to GetArrayLength(dirpaths) - 1 do begin
    Result := Result and RemoveDir(dirpaths[dirpathsIndex]);
  end;
end;

function RunWithElevatedPrivileges : Boolean;
var
  shellExecuteRetVal: Integer;
  params: String;
  paramIndex: Integer;
begin
  for paramIndex := 1 to ParamCount do begin
    params := params + AddQuotes(ParamStr(paramIndex)) + ' ';
  end;

  params := params + AddQuotes('/LANG=' + ActiveLanguage) + ' ';
  params := params + AddQuotes('/' + PLUGIN_PATH_PARAM_NAME + '=' + PluginsDirpath);

  shellExecuteRetVal := ShellExecute(0, 'runas', ExpandConstant('{srcexe}'), params, '', SW_SHOW);

  Result := shellExecuteRetVal > 32;

  if Result then begin
    ExitProcess(0);
  end
  else begin
    MsgBox(FAILED_TO_RUN_ELEVATED, mbInformation, MB_OK);
  end;
end;


Corrected typo in filename.

Corrected typo in filename.

git-svn-id: 4c7b768bc2e2a56aa1c56b1b5a9454ba68342430@2935 ac9c905b-388a-4ddf-9b6a-4063a022bc57

; Script generated by the Inno Setup Script Wizard.

#define MyAppName "Logtalk"
#define MyAppVerName "Logtalk 2.28.0"
#define MyAppPublisher "Logtalk.org"
#define MyAppURL "http://logtalk.org"
#define MyAppUrlName "Logtalk Web Site.url"
#define MyAppRegURL "http://logtalk.org/regform.html"
#define MyAppRegUrlName "Logtalk Registration.url"

#define LOGTALKHOME "{reg:HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment,LOGTALKHOME|{pf}\Logtalk}"
#define LOGTALKUSER "{reg:HKCU\Environment,LOGTALKUSER|{userdocs}\Logtalk}"

[Setup]
AppName={#MyAppName}
AppVerName={#MyAppVerName}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
ChangesEnvironment=yes
DefaultDirName={#LOGTALKHOME}
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
LicenseFile=C:\logtalk\LICENSE.txt
InfoBeforeFile=C:\logtalk\README.txt
OutputBaseFilename=lgt2280
Compression=lzma
SolidCompression=yes

VersionInfoVersion=2.28.0
VersionInfoCopyright= Paulo Moura, 1998-2006

AllowRootDirectory=yes
UninstallFilesDir="{userdocs}\Logtalk uninstaller"

MinVersion=0,5.0

[Types]
Name: "full"; Description: "Full installation (recommended)"
Name: "base"; Description: "Base system installation"
Name: "user"; Description: "User data files installation (must be run by all end-users)"
Name: "prolog"; Description: "Prolog integration (see documentation for compatibility details)"
Name: "custom"; Description: "Custom installation"; Flags: iscustom

[Components]
Name: "base"; Description: "Base system"; Types: full base custom; Flags: disablenouninstallwarning
Name: "user"; Description: "User data files"; Types: full user custom; Flags: checkablealone disablenouninstallwarning
Name: "user\backup"; Description: "Backup current user data files"; Types: full user custom; Flags: disablenouninstallwarning
Name: "prolog"; Description: "Prolog integration"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\bp"; Description: "B-Prolog integration (version 6.9 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\ciao"; Description: "Ciao Prolog integration (version 1.10#5 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\eclipse"; Description: "ECLiPSe integration (versions 5.8 or 5.9)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\gprolog"; Description: "GNU Prolog integration (version 1.2.16 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\plc"; Description: "K-Prolog integration (version 5.1.4 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\sicstus"; Description: "SICStus Prolog integration (versions 3.12.x, 4.0)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\swi"; Description: "SWI-Prolog integration (version 5.6.16 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\xsb"; Description: "XSB integration (version 3.0 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\yap"; Description: "YAP integration (version 5.1.1 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning

[Tasks]
Name: registration; Description: "&Register {#MyAppName} (opens a web page; requires Internet connection)"; Components: base user
Name: shortcut; Description: "&Create a desktop shortcut to the Logtalk user folder"; Components: user

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Messages]
BeveledLabel=Logtalk 2.28.0  Paulo Moura, 1998-2006

[Dirs]
Name: {code:GetLgtUserDir}; Components: user; Flags: uninsneveruninstall
Name: "{userdocs}\Logtalk uninstaller"

[Files]
Source: "C:\logtalk\*"; Excludes: ".*,CVS"; DestDir: "{app}"; Components: base; Flags: ignoreversion recursesubdirs createallsubdirs

Source: "C:\logtalk\configs\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\configs"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\contributions\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\contributions"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\examples\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\examples"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\libpaths\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\libpaths"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\library\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\library"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\xml\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\xml"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall

Source: "C:\logtalk\scripts\*.bat"; DestDir: "{win}"; Components: base; Flags: ignoreversion
Source: "C:\logtalk\xml\*.bat"; DestDir: "{win}"; Components: base; Flags: ignoreversion

[INI]
Filename: "{app}\{#MyAppUrlName}"; Section: "InternetShortcut"; Key: "URL"; String: "{#MyAppURL}"; Components: base
Filename: "{app}\{#MyAppRegUrlName}"; Section: "InternetShortcut"; Key: "URL"; String: "{#MyAppRegURL}"; Components: base

[Icons]
Name: "{group}\{#MyAppName} Bibliography"; Filename: "{app}\BIBLIOGRAPHY.bib"; Components: base
Name: "{group}\{#MyAppName} Documentation"; Filename: "{app}\manuals\index.html"; Components: base
Name: "{group}\{#MyAppName} License"; Filename: "{app}\LICENSE.txt"; Components: base
Name: "{group}\{#MyAppName} Release Notes"; Filename: "{app}\RELEASE_NOTES.txt"; Components: base
Name: "{group}\{#MyAppName} Read Me"; Filename: "{app}\README.txt"; Components: base
Name: "{group}\{#MyAppName} Customization instructions"; Filename: "{app}\CUSTOMIZE.txt"; Components: base

Name: "{group}\{#MyAppName} Web Site"; Filename: "{app}\{#MyAppUrlName}"; Components: base
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Components: base

Name: "{code:GetLgtUserDir}\manuals"; Filename: "{app}\manuals"; Components: user
Name: "{code:GetLgtUserDir}\wenv"; Filename: "{app}\wenv"; Components: user

Name: "{userdesktop}\Logtalk user files"; Filename: "{code:GetLgtUserDir}"; Components: user; Tasks: shortcut

[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "LOGTALKHOME"; ValueData: "{app}"; Components: base; Flags: deletevalue uninsdeletevalue
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "LOGTALKUSER"; ValueData: "{code:GetLgtUserDir}"; Flags: deletevalue uninsdeletevalue

[Run]
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_bplgt.js"""; Description: "B-Prolog integration"; Components: prolog\bp
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_ciaolgt.js"""; Description: "Ciao Prolog integration"; Components: prolog\ciao
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_eclipselgt.js"""; Description: "ECLiPSe integration"; Components: prolog\eclipse
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_gplgt.js"""; Description: "GNU Prolog integration"; Components: prolog\gprolog
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_plclgt.js"""; Description: "K-Prolog integration"; Components: prolog\plc
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_sicstuslgt.js"""; Description: "SICStus Prolog integration"; Components: prolog\sicstus
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_swilgt.js"""; Description: "SWI-Prolog integration"; Components: prolog\swi
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_xsblgt.js"""; Description: "XSB integration"; Components: prolog\xsb
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_yaplgt.js"""; Description: "YAP integration"; Components: prolog\yap

Filename: "{app}\RELEASE_NOTES.txt"; Description: "View the release notes"; Components: base user; Flags: postinstall shellexec skipifsilent
Filename: "{app}\CUSTOMIZE.txt"; Description: "Read the customization instructions for completing your setup"; Components: base user; Flags: postinstall shellexec skipifsilent

Filename: "{app}\{#MyAppRegUrlName}"; Flags: shellexec nowait; Tasks: registration

[UninstallDelete]
Type: filesandordirs; Name: "{app}"; Components: base
Type: filesandordirs; Name: "{group}"; Components: base

[Code]
var
  LgtUserDirPage: TInputDirWizardPage;
  WarningPage: TOutputMsgWizardPage;
  Explanation, Warning, Scripts: String;

procedure InitializeWizard;
begin
  Explanation := 'Select the folder in which Setup should install Logtalk user data files, then click Next.'
                 + Chr(13) + Chr(13)
                 + 'These files allows each user to independently customize Logtalk and to freely modify the provided programming examples.'
                 + Chr(13) + Chr(13)
                 + 'A copy of these files must exist in the user home folder in order to use the Logtalk-Prolog integration scripts available from the Start Menu.'
                 + Chr(13) + Chr(13)
                 + 'Addtional end-users may use this installer to make a copy of these files on their home folders after a full installation of Logtalk.';
  LgtUserDirPage := CreateInputDirPage(wpSelectDir,
    'Select folder for Logtalk user data files', 'Where should Logtalk user data files be installed?',
    Explanation,
    False, 'New Folder');
  LgtUserDirPage.Add('');
  LgtUserDirPage.Values[0] := ExpandConstant('{#LOGTALKUSER}');
  if not IsAdminLoggedOn then
  begin
    Warning := 'Full installation of Logtalk requires an administrative user.'
               + Chr(13) + Chr(13)
               + 'If the base Logtalk system is already installed, you may proceed in order to setup Logtalk for you as an end-user.'
               + Chr(13) + Chr(13)
               + 'If Logtalk is already set for you, this installer will make a backup copy of your current files (if you choose the same installation folder) and will restore all user data files to their default, pristine state.';
    WarningPage := CreateOutputMsgPage(wpWelcome,
  'Information', 'Please read the following important information before continuing.', Warning);
  end
end;

function GetLgtUserDir(Param: String): String;
begin
  Result := LgtUserDirPage.Values[0]
end;

function GetCurrentDate(Param: String): String;
begin
  Result := GetDateTimeString('dddddd tt', '-', '-')
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  if (PageID = wpSelectDir) and not IsAdminLoggedOn then
    Result := True
  else if (PageID = wpSelectComponents) and not IsAdminLoggedOn then
  begin
    WizardForm.TypesCombo.ItemIndex := 2;
    WizardForm.TypesCombo.OnChange(nil);
    Result := True
  end
  else if (PageID = wpReady) and not IsAdminLoggedOn then
    Result := True
  else
    Result := False;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if (CurStep = ssInstall) and DirExists(LgtUserDirPage.Values[0]) and (pos('backup', WizardSelectedComponents(False)) > 0) then
    RenameFile(LgtUserDirPage.Values[0], LgtUserDirPage.Values[0] + ' backup ' + GetDateTimeString('dddddd tt', '-', '-'));
  if (CurStep = ssInstall) and (pos('prolog', WizardSelectedComponents(False)) > 0) then
  begin
    Scripts := 'The installer needs to run a set of JScripts scripts within command shells' + Chr(13) +
               'in order to integrate Logtalk with your installed Prolog compilers.' + Chr(13) + Chr(13) +
               'These scripts require WSH 5.6 (or later version) to be installed.' + Chr(13) + Chr(13) +
               'Make sure that any security software you may have installed do not block' + Chr(13) +
               'the execution of the scripts.';
    MsgBox(Scripts, mbInformation, MB_OK)
  end
end;

; Script generated by the Inno Setup Script Wizard.

#define MyAppName "Logtalk"
#define MyAppVerName "Logtalk 2.28.0"
#define MyAppPublisher "Logtalk.org"
#define MyAppURL "http://logtalk.org"
#define MyAppUrlName "Logtalk Web Site.url"
#define MyAppRegURL "http://logtalk.org/regform.html"
#define MyAppRegUrlName "Logtalk Registration.url"

#define LOGTALKHOME "{reg:HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment,LOGTALKHOME|{pf}\Logtalk}"
#define LOGTALKUSER "{reg:HKCU\Environment,LOGTALKUSER|{userdocs}\Logtalk}"

[Setup]
AppName={#MyAppName}
AppVerName={#MyAppVerName}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
ChangesEnvironment=yes
DefaultDirName={#LOGTALKHOME}
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
LicenseFile=C:\logtalk\LICENSE.txt
InfoBeforeFile=C:\logtalk\README.txt
OutputBaseFilename=lgt2280
Compression=lzma
SolidCompression=yes

VersionInfoVersion=2.28.0
VersionInfoCopyright= Paulo Moura, 1998-2006

AllowRootDirectory=yes
UninstallFilesDir="{userdocs}\Logtalk uninstaller"

MinVersion=0,5.0

[Types]
Name: "full"; Description: "Full installation (recommended)"
Name: "base"; Description: "Base system installation"
Name: "user"; Description: "User data files installation (must be run by all end-users)"
Name: "prolog"; Description: "Prolog integration (see documentation for compatibility details)"
Name: "custom"; Description: "Custom installation"; Flags: iscustom

[Components]
Name: "base"; Description: "Base system"; Types: full base custom; Flags: disablenouninstallwarning
Name: "user"; Description: "User data files"; Types: full user custom; Flags: checkablealone disablenouninstallwarning
Name: "user\backup"; Description: "Backup current user data files"; Types: full user custom; Flags: disablenouninstallwarning
Name: "prolog"; Description: "Prolog integration"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\bp"; Description: "B-Prolog integration (version 6.9 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\ciao"; Description: "Ciao Prolog integration (version 1.10#5 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\eclipse"; Description: "ECLiPSe integration (versions 5.8 or 5.9)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\gprolog"; Description: "GNU Prolog integration (version 1.2.16 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\plc"; Description: "K-Prolog integration (version 5.1.4 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\sicstus"; Description: "SICStus Prolog integration (versions 3.12.x, 4.0)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\swi"; Description: "SWI-Prolog integration (version 5.6.16 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\xsb"; Description: "XSB integration (version 3.0 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning
Name: "prolog\yap"; Description: "YAP integration (version 5.1.1 or later)"; Types: full prolog custom; Flags: disablenouninstallwarning

[Tasks]
Name: registration; Description: "&Register {#MyAppName} (opens a web page; requires Internet connection)"; Components: base user
Name: shortcut; Description: "&Create a desktop shortcut to the Logtalk user folder"; Components: user

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Messages]
BeveledLabel=Logtalk 2.28.0  Paulo Moura, 1998-2006

[Dirs]
Name: {code:GetLgtUserDir}; Components: user; Flags: uninsneveruninstall
Name: "{userdocs}\Logtalk uninstaller"

[Files]
Source: "C:\logtalk\*"; Excludes: ".*,CVS"; DestDir: "{app}"; Components: base; Flags: ignoreversion recursesubdirs createallsubdirs

Source: "C:\logtalk\configs\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\configs"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\contributions\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\contributions"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\examples\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\examples"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\libpaths\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\libpaths"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\library\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\library"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "C:\logtalk\xml\*"; Excludes: ".*,CVS"; DestDir: "{code:GetLgtUserDir}\xml"; Components: user; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall

Source: "C:\logtalk\scripts\*.bat"; DestDir: "{win}"; Components: base; Flags: ignoreversion
Source: "C:\logtalk\xml\*.bat"; DestDir: "{win}"; Components: base; Flags: ignoreversion

[INI]
Filename: "{app}\{#MyAppUrlName}"; Section: "InternetShortcut"; Key: "URL"; String: "{#MyAppURL}"; Components: base
Filename: "{app}\{#MyAppRegUrlName}"; Section: "InternetShortcut"; Key: "URL"; String: "{#MyAppRegURL}"; Components: base

[Icons]
Name: "{group}\{#MyAppName} Bibliography"; Filename: "{app}\BIBLIOGRAPHY.txt"; Components: base
Name: "{group}\{#MyAppName} Documentation"; Filename: "{app}\manuals\index.html"; Components: base
Name: "{group}\{#MyAppName} License"; Filename: "{app}\LICENSE.txt"; Components: base
Name: "{group}\{#MyAppName} Release Notes"; Filename: "{app}\RELEASE_NOTES.txt"; Components: base
Name: "{group}\{#MyAppName} Read Me"; Filename: "{app}\README.txt"; Components: base
Name: "{group}\{#MyAppName} Customization instructions"; Filename: "{app}\CUSTOMIZE.txt"; Components: base

Name: "{group}\{#MyAppName} Web Site"; Filename: "{app}\{#MyAppUrlName}"; Components: base
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Components: base

Name: "{code:GetLgtUserDir}\manuals"; Filename: "{app}\manuals"; Components: user
Name: "{code:GetLgtUserDir}\wenv"; Filename: "{app}\wenv"; Components: user

Name: "{userdesktop}\Logtalk user files"; Filename: "{code:GetLgtUserDir}"; Components: user; Tasks: shortcut

[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "LOGTALKHOME"; ValueData: "{app}"; Components: base; Flags: deletevalue uninsdeletevalue
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "LOGTALKUSER"; ValueData: "{code:GetLgtUserDir}"; Flags: deletevalue uninsdeletevalue

[Run]
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_bplgt.js"""; Description: "B-Prolog integration"; Components: prolog\bp
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_ciaolgt.js"""; Description: "Ciao Prolog integration"; Components: prolog\ciao
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_eclipselgt.js"""; Description: "ECLiPSe integration"; Components: prolog\eclipse
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_gplgt.js"""; Description: "GNU Prolog integration"; Components: prolog\gprolog
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_plclgt.js"""; Description: "K-Prolog integration"; Components: prolog\plc
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_sicstuslgt.js"""; Description: "SICStus Prolog integration"; Components: prolog\sicstus
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_swilgt.js"""; Description: "SWI-Prolog integration"; Components: prolog\swi
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_xsblgt.js"""; Description: "XSB integration"; Components: prolog\xsb
Filename: "{cmd}"; Parameters: "/C set LOGTALKHOME={app} & cscript ""{app}\scripts\make_yaplgt.js"""; Description: "YAP integration"; Components: prolog\yap

Filename: "{app}\RELEASE_NOTES.txt"; Description: "View the release notes"; Components: base user; Flags: postinstall shellexec skipifsilent
Filename: "{app}\CUSTOMIZE.txt"; Description: "Read the customization instructions for completing your setup"; Components: base user; Flags: postinstall shellexec skipifsilent

Filename: "{app}\{#MyAppRegUrlName}"; Flags: shellexec nowait; Tasks: registration

[UninstallDelete]
Type: filesandordirs; Name: "{app}"; Components: base
Type: filesandordirs; Name: "{group}"; Components: base

[Code]
var
  LgtUserDirPage: TInputDirWizardPage;
  WarningPage: TOutputMsgWizardPage;
  Explanation, Warning, Scripts: String;

procedure InitializeWizard;
begin
  Explanation := 'Select the folder in which Setup should install Logtalk user data files, then click Next.'
                 + Chr(13) + Chr(13)
                 + 'These files allows each user to independently customize Logtalk and to freely modify the provided programming examples.'
                 + Chr(13) + Chr(13)
                 + 'A copy of these files must exist in the user home folder in order to use the Logtalk-Prolog integration scripts available from the Start Menu.'
                 + Chr(13) + Chr(13)
                 + 'Addtional end-users may use this installer to make a copy of these files on their home folders after a full installation of Logtalk.';
  LgtUserDirPage := CreateInputDirPage(wpSelectDir,
    'Select folder for Logtalk user data files', 'Where should Logtalk user data files be installed?',
    Explanation,
    False, 'New Folder');
  LgtUserDirPage.Add('');
  LgtUserDirPage.Values[0] := ExpandConstant('{#LOGTALKUSER}');
  if not IsAdminLoggedOn then
  begin
    Warning := 'Full installation of Logtalk requires an administrative user.'
               + Chr(13) + Chr(13)
               + 'If the base Logtalk system is already installed, you may proceed in order to setup Logtalk for you as an end-user.'
               + Chr(13) + Chr(13)
               + 'If Logtalk is already set for you, this installer will make a backup copy of your current files (if you choose the same installation folder) and will restore all user data files to their default, pristine state.';
    WarningPage := CreateOutputMsgPage(wpWelcome,
  'Information', 'Please read the following important information before continuing.', Warning);
  end
end;

function GetLgtUserDir(Param: String): String;
begin
  Result := LgtUserDirPage.Values[0]
end;

function GetCurrentDate(Param: String): String;
begin
  Result := GetDateTimeString('dddddd tt', '-', '-')
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  if (PageID = wpSelectDir) and not IsAdminLoggedOn then
    Result := True
  else if (PageID = wpSelectComponents) and not IsAdminLoggedOn then
  begin
    WizardForm.TypesCombo.ItemIndex := 2;
    WizardForm.TypesCombo.OnChange(nil);
    Result := True
  end
  else if (PageID = wpReady) and not IsAdminLoggedOn then
    Result := True
  else
    Result := False;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if (CurStep = ssInstall) and DirExists(LgtUserDirPage.Values[0]) and (pos('backup', WizardSelectedComponents(False)) > 0) then
    RenameFile(LgtUserDirPage.Values[0], LgtUserDirPage.Values[0] + ' backup ' + GetDateTimeString('dddddd tt', '-', '-'));
  if (CurStep = ssInstall) and (pos('prolog', WizardSelectedComponents(False)) > 0) then
  begin
    Scripts := 'The installer needs to run a set of JScripts scripts within command shells' + Chr(13) +
               'in order to integrate Logtalk with your installed Prolog compilers.' + Chr(13) + Chr(13) +
               'These scripts require WSH 5.6 (or later version) to be installed.' + Chr(13) + Chr(13) +
               'Make sure that any security software you may have installed do not block' + Chr(13) +
               'the execution of the scripts.';
    MsgBox(Scripts, mbInformation, MB_OK)
  end
end;