Remove ISS install file

Remove ISS install file

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

#define AppName "Red Hat Developer Platform"
#define AppVersion "1.0"
#define AppPublisher "Red Hat"
#define AppURL "http://www.redhat.com/"

#include <idp.iss>

[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={{C72362A0-26F3-49D6-8ED1-8214644255DD}
AppName={#AppName}
AppVersion={#AppVersion}
;AppVerName={#AppName} {#AppVersion}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
CreateAppDir=yes
DefaultDirName={pf}\{#AppName}
DefaultGroupName={#AppName}
OutputBaseFilename=developer_platform
Compression=lzma
SolidCompression=yes
WizardSmallImageFile=redhat.bmp
BackColor=clWhite
BackSolid=yes

;[Files]

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

[Registry]
Root: HKLM; Subkey: "Software\Red Hat";             Flags: uninsdeletekeyifempty
Root: HKLM; Subkey: "Software\Red Hat\{#AppName}"; Flags: uninsdeletekey
Root: HKLM; Subkey: "Software\Red Hat\{#AppName}"; ValueType: string; ValueName: "InstallDir"; ValueData: "{app}"

[Code]
procedure CreateWizardPages;
var
  Page: TWizardPage;
  Button, FormButton: TNewButton;
  Panel: TPanel;
  CheckBox: TNewCheckBox;
  Edit: TNewEdit;
  PasswordEdit: TPasswordEdit;
  Memo: TNewMemo;
  ComboBox: TNewComboBox;
  ListBox: TNewListBox;
  StaticText, ProgressBarLabel: TNewStaticText;
  ProgressBar, ProgressBar2, ProgressBar3: TNewProgressBar;
  CheckListBox, CheckListBox2: TNewCheckListBox;
  FolderTreeView: TFolderTreeView;
  BitmapImage, BitmapImage2, BitmapImage3: TBitmapImage;
  BitmapFileName: String;
  RichEditViewer: TRichEditViewer;
begin
  { TButton and others }

  Page := CreateCustomPage(wpWelcome, 'Log in to your Red Hat account', 'Foo');

  Edit := TNewEdit.Create(Page);
  Edit.Top := ScaleY(8);
  Edit.Width := Page.SurfaceWidth div 2 - ScaleX(8);
  Edit.Text := 'Red Hat Login';
  Edit.Parent := Page.Surface;

  PasswordEdit := TPasswordEdit.Create(Page);
  PasswordEdit.Top := Edit.Top + Edit.Height + ScaleY(8);
  PasswordEdit.Width := Edit.Width;
  PasswordEdit.Text := 'Password';
  PasswordEdit.Parent := Page.Surface;

  Button := TNewButton.Create(Page);
  Button.Width := ScaleX(75);
  Button.Height := ScaleY(23);
  Button.Caption := 'LOG IN';
  Button.Top := PasswordEdit.Top + PasswordEdit.Height + ScaleY(8);
  //Button.OnClick := @ButtonOnClick;
  Button.Parent := Page.Surface;

  {Panel := TPanel.Create(Page);
  Panel.Width := Page.SurfaceWidth div 2 - ScaleX(8);
  Panel.Left :=  Page.SurfaceWidth - Panel.Width;
  Panel.Height := Button.Height * 2;
  Panel.Caption := 'TPanel';
  Panel.Color := clWindow;
  Panel.ParentBackground := False;
  Panel.Parent := Page.Surface;

  CheckBox := TNewCheckBox.Create(Page);
  CheckBox.Top := Button.Top + Button.Height + ScaleY(8);
  CheckBox.Width := Page.SurfaceWidth div 2;
  CheckBox.Height := ScaleY(17);
  CheckBox.Caption := 'TNewCheckBox';
  CheckBox.Checked := True;
  CheckBox.Parent := Page.Surface;

  Memo := TNewMemo.Create(Page);
  Memo.Top := Edit.Top + Edit.Height + ScaleY(8);
  Memo.Width := Page.SurfaceWidth;
  Memo.Height := ScaleY(89);
  Memo.ScrollBars := ssVertical;
  Memo.Text := 'TNewMemo';
  Memo.Parent := Page.Surface;

  FormButton := TNewButton.Create(Page);
  FormButton.Top := Memo.Top + Memo.Height + ScaleY(8);
  FormButton.Width := ScaleX(75);
  FormButton.Height := ScaleY(23);
  FormButton.Caption := 'TSetupForm';
  //FormButton.OnClick := @FormButtonOnClick;
  FormButton.Parent := Page.Surface;  {

  { TComboBox and others }

  {Page := CreateCustomPage(Page.ID, 'Custom wizard page controls', 'TComboBox and others');

  ComboBox := TNewComboBox.Create(Page);
  ComboBox.Width := Page.SurfaceWidth;
  ComboBox.Parent := Page.Surface;
  ComboBox.Style := csDropDownList;
  ComboBox.Items.Add('TComboBox');
  ComboBox.ItemIndex := 0;

  ListBox := TNewListBox.Create(Page);
  ListBox.Top := ComboBox.Top + ComboBox.Height + ScaleY(8);
  ListBox.Width := Page.SurfaceWidth;
  ListBox.Height := ScaleY(97);
  ListBox.Parent := Page.Surface;
  ListBox.Items.Add('TListBox');
  ListBox.ItemIndex := 0;

  StaticText := TNewStaticText.Create(Page);
  StaticText.Top := ListBox.Top + ListBox.Height + ScaleY(8);
  StaticText.Caption := 'TNewStaticText';
  StaticText.AutoSize := True;
  StaticText.Parent := Page.Surface;

  ProgressBarLabel := TNewStaticText.Create(Page);
  ProgressBarLabel.Top := StaticText.Top + StaticText.Height + ScaleY(8);
  ProgressBarLabel.Caption := 'TNewProgressBar';
  ProgressBarLabel.AutoSize := True;
  ProgressBarLabel.Parent := Page.Surface;

  ProgressBar := TNewProgressBar.Create(Page);
  ProgressBar.Left := ProgressBarLabel.Width + ScaleX(8);
  ProgressBar.Top := ProgressBarLabel.Top;
  ProgressBar.Width := Page.SurfaceWidth - ProgressBar.Left;
  ProgressBar.Height := ProgressBarLabel.Height + ScaleY(8);
  ProgressBar.Parent := Page.Surface;
  ProgressBar.Position := 25;

  ProgressBar2 := TNewProgressBar.Create(Page);
  ProgressBar2.Left := ProgressBarLabel.Width + ScaleX(8);
  ProgressBar2.Top := ProgressBar.Top + ProgressBar.Height + ScaleY(4);
  ProgressBar2.Width := Page.SurfaceWidth - ProgressBar.Left;
  ProgressBar2.Height := ProgressBarLabel.Height + ScaleY(8);
  ProgressBar2.Parent := Page.Surface;
  ProgressBar2.Position := 50;     }
  { Note: TNewProgressBar.State property only has an effect on Windows Vista and newer }
  {ProgressBar2.State := npbsError;

  ProgressBar3 := TNewProgressBar.Create(Page);
  ProgressBar3.Left := ProgressBarLabel.Width + ScaleX(8);
  ProgressBar3.Top := ProgressBar2.Top + ProgressBar2.Height + ScaleY(4);
  ProgressBar3.Width := Page.SurfaceWidth - ProgressBar.Left;
  ProgressBar3.Height := ProgressBarLabel.Height + ScaleY(8);
  ProgressBar3.Parent := Page.Surface;  }
  { Note: TNewProgressBar.Style property only has an effect on Windows XP and newer }
  {ProgressBar3.Style := npbstMarquee;    }

  { TNewCheckListBox }

  {Page := CreateCustomPage(Page.ID, 'Custom wizard page controls', 'TNewCheckListBox');

  CheckListBox := TNewCheckListBox.Create(Page);
  CheckListBox.Width := Page.SurfaceWidth;
  CheckListBox.Height := ScaleY(97);
  CheckListBox.Flat := True;
  CheckListBox.Parent := Page.Surface;
  CheckListBox.AddCheckBox('TNewCheckListBox', '', 0, True, True, False, True, nil);
  CheckListBox.AddRadioButton('TNewCheckListBox', '', 1, True, True, nil);
  CheckListBox.AddRadioButton('TNewCheckListBox', '', 1, False, True, nil);
  CheckListBox.AddCheckBox('TNewCheckListBox', '', 0, True, True, False, True, nil);

  CheckListBox2 := TNewCheckListBox.Create(Page);
  CheckListBox2.Top := CheckListBox.Top + CheckListBox.Height + ScaleY(8);
  CheckListBox2.Width := Page.SurfaceWidth;
  CheckListBox2.Height := ScaleY(97);
  CheckListBox2.BorderStyle := bsNone;
  CheckListBox2.ParentColor := True;
  CheckListBox2.MinItemHeight := WizardForm.TasksList.MinItemHeight;
  CheckListBox2.ShowLines := False;
  CheckListBox2.WantTabs := True;
  CheckListBox2.Parent := Page.Surface;
  CheckListBox2.AddGroup('TNewCheckListBox', '', 0, nil);
  CheckListBox2.AddRadioButton('TNewCheckListBox', '', 0, True, True, nil);
  CheckListBox2.AddRadioButton('TNewCheckListBox', '', 0, False, True, nil);  }

  { TFolderTreeView }

 // Page := CreateCustomPage(Page.ID, 'Custom wizard page controls', 'TFolderTreeView');

//  FolderTreeView := TFolderTreeView.Create(Page);
//  FolderTreeView.Width := Page.SurfaceWidth;
//  FolderTreeView.Height := Page.SurfaceHeight;
//  FolderTreeView.Parent := Page.Surface;
//  FolderTreeView.Directory := ExpandConstant('{src}');

  { TBitmapImage }

  {Page := CreateCustomPage(Page.ID, 'Custom wizard page controls', 'TBitmapImage');    }

  //BitmapFileName := ExpandConstant('{tmp}\WizModernSmallImage.bmp');
  //ExtractTemporaryFile(ExtractFileName(BitmapFileName));

  {BitmapImage := TBitmapImage.Create(Page);
  BitmapImage.AutoSize := True;
  //BitmapImage.Bitmap.LoadFromFile(BitmapFileName);
  BitmapImage.Cursor := crHand;
  //BitmapImage.OnClick := @BitmapImageOnClick;
  BitmapImage.Parent := Page.Surface;

  BitmapImage2 := TBitmapImage.Create(Page);
  BitmapImage2.BackColor := $400000;
  BitmapImage2.Bitmap := BitmapImage.Bitmap;
  BitmapImage2.Center := True;
  BitmapImage2.Left := BitmapImage.Width + 10;
  BitmapImage2.Height := 2*BitmapImage.Height;
  BitmapImage2.Width := 2*BitmapImage.Width;
  BitmapImage2.Cursor := crHand;
  //BitmapImage2.OnClick := @BitmapImageOnClick;
  BitmapImage2.Parent := Page.Surface;

  BitmapImage3 := TBitmapImage.Create(Page);
  BitmapImage3.Bitmap := BitmapImage.Bitmap;
  BitmapImage3.Stretch := True;
  BitmapImage3.Left := 3*BitmapImage.Width + 20;
  BitmapImage3.Height := 4*BitmapImage.Height;
  BitmapImage3.Width := 4*BitmapImage.Width;
  BitmapImage3.Cursor := crHand;
  //BitmapImage3.OnClick := @BitmapImageOnClick;
  BitmapImage3.Parent := Page.Surface;       }

  { TRichViewer }

  //Page := CreateCustomPage(Page.ID, 'Custom wizard page controls', 'TRichViewer');

  //RichEditViewer := TRichEditViewer.Create(Page);
  //RichEditViewer.Width := Page.SurfaceWidth;
  //RichEditViewer.Height := Page.SurfaceHeight;
  //RichEditViewer.Parent := Page.Surface;
  //RichEditViewer.ScrollBars := ssVertical;
  //RichEditViewer.UseRichEdit := True;
  //RichEditViewer.RTFText := '{\rtf1\ansi\ansicpg1252\deff0\deflang1043{\fonttbl{\f0\fswiss\fcharset0 Arial;}}{\colortbl ;\red255\green0\blue0;\red0\green128\blue0;\red0\green0\blue128;}\viewkind4\uc1\pard\f0\fs20 T\cf1 Rich\cf2 Edit\cf3 Viewer\cf0\par}';
  //RichEditViewer.ReadOnly := True;
end;

procedure activateDownloadForm(Page: TWizardPage);
begin
   idpFormActivate(Page);
   MsgBox('Downloads complete', mbInformation, MB_OK);

   // TODO perform installs next
end;

function createDownloadForm(PreviousPageId: Integer): Integer;
begin
    IDPForm.Page := CreateCustomPage(PreviousPageId, ExpandConstant('{cm:IDP_FormCaption}'), ExpandConstant('{cm:IDP_FormDescription}'));

    IDPForm.TotalProgressBar := TNewProgressBar.Create(IDPForm.Page);
    with IDPForm.TotalProgressBar do
    begin
        Parent := IDPForm.Page.Surface;
        Left := ScaleX(0);
        Top := ScaleY(16);
        Width := ScaleX(410);
        Height := ScaleY(20);
        Min := 0;
        Max := 100;
    end;

    IDPForm.TotalProgressLabel := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.TotalProgressLabel do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_TotalProgress}');
        Left := ScaleX(0);
        Top := ScaleY(0);
        Width := ScaleX(200);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 1;
    end;

    IDPForm.CurrentFileLabel := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.CurrentFileLabel do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_CurrentFile}');
        Left := ScaleX(0);
        Top := ScaleY(48);
        Width := ScaleX(200);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 2;
    end;

    IDPForm.FileProgressBar := TNewProgressBar.Create(IDPForm.Page);
    with IDPForm.FileProgressBar do
    begin
        Parent := IDPForm.Page.Surface;
        Left := ScaleX(0);
        Top := ScaleY(64);
        Width := ScaleX(410);
        Height := ScaleY(20);
        Min := 0;
        Max := 100;
    end;

    IDPForm.TotalDownloaded := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.TotalDownloaded do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := '';
        Left := ScaleX(290);
        Top := ScaleY(0);
        Width := ScaleX(120);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 4;
    end;

    IDPForm.FileDownloaded := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.FileDownloaded do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := '';
        Left := ScaleX(290);
        Top := ScaleY(48);
        Width := ScaleX(120);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 5;
    end;

    IDPForm.FileNameLabel := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.FileNameLabel do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_File}');
        Left := ScaleX(0);
        Top := ScaleY(100);
        Width := ScaleX(116);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 6;
    end;

    IDPForm.SpeedLabel := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.SpeedLabel do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_Speed}');
        Left := ScaleX(0);
        Top := ScaleY(116);
        Width := ScaleX(116);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 7;
    end;

    IDPForm.StatusLabel := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.StatusLabel do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_Status}');
        Left := ScaleX(0);
        Top := ScaleY(132);
        Width := ScaleX(116);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 8;
    end;

    IDPForm.ElapsedTimeLabel := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.ElapsedTimeLabel do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_ElapsedTime}');
        Left := ScaleX(0);
        Top := ScaleY(148);
        Width := ScaleX(116);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 9;
    end;

    IDPForm.RemainingTimeLabel := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.RemainingTimeLabel do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_RemainingTime}');
        Left := ScaleX(0);
        Top := ScaleY(164);
        Width := ScaleX(116);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 10;
    end;

    IDPForm.FileName := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.FileName do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := '';
        Left := ScaleX(120);
        Top := ScaleY(100);
        Width := ScaleX(280);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 11;
    end;

    IDPForm.Speed := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.Speed do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := '';
        Left := ScaleX(120);
        Top := ScaleY(116);
        Width := ScaleX(280);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 12;
    end;

    IDPForm.Status := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.Status do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := '';
        Left := ScaleX(120);
        Top := ScaleY(132);
        Width := ScaleX(280);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 13;
    end;

    IDPForm.ElapsedTime := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.ElapsedTime do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := '';
        Left := ScaleX(120);
        Top := ScaleY(148);
        Width := ScaleX(280);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 14;
    end;

    IDPForm.RemainingTime := TNewStaticText.Create(IDPForm.Page);
    with IDPForm.RemainingTime do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := '';
        Left := ScaleX(120);
        Top := ScaleY(164);
        Width := ScaleX(280);
        Height := ScaleY(14);
        AutoSize := False;
        TabOrder := 15;
    end;

    IDPForm.DetailsButton := TNewButton.Create(IDPForm.Page);
    with IDPForm.DetailsButton do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('{cm:IDP_DetailsButton}');
        Left := ScaleX(336);
        Top := ScaleY(184);
        Width := ScaleX(75);
        Height := ScaleY(23);
        TabOrder := 16;
        OnClick := @idpDetailsButtonClick;
    end;

    IDPForm.InvisibleButton := TNewButton.Create(IDPForm.Page);
    with IDPForm.InvisibleButton do
    begin
        Parent := IDPForm.Page.Surface;
        Caption := ExpandConstant('You must not see this button');
        Left := ScaleX(0);
        Top := ScaleY(0);
        Width := ScaleX(10);
        Height := ScaleY(10);
        TabOrder := 17;
        Visible := False;
        OnClick := @idpReportErrorHelper;
    end;

    with IDPForm.Page do
    begin
        OnActivate          := @idpFormActivate;
        OnShouldSkipPage    := @idpShouldSkipPage;
        OnBackButtonClick   := @idpBackButtonClick;
        OnNextButtonClick   := @idpNextButtonClick;
        OnCancelButtonClick := @idpCancelButtonClick;
    end;

    Result := IDPForm.Page.ID;
end;

{procedure CurStepChanged(CurStep: TSetupStep);
var
  ErrorCode: Integer;
begin
  if CurStep = ssInstall then
     MsgBox('Installing', mbInformation, MB_OK);
end;}

procedure InitializeWizard();
var
  BackgroundBitmapImage: TBitmapImage;
  BackgroundBitmapText: TNewStaticText;
begin
  { Custom wizard pages }

  // These lines change the width and height of the wizard window, but components need to be repositioned
  //WizardForm.Width := 800;
  //WizardForm.Height := 600;

  // Sets the background color of the inner panel to white
  WizardForm.InnerPage.Color := clWhite;

  CreateWizardPages;

  // Show details by default
  idpSetOption('DetailedMode', '1');

  // Hide the 'Details' button
  idpSetOption('DetailsButton', '0');

  //idpSetOption('Referer', 'http://www.azulsystems.com/products/zulu/downloads');
  //idpAddFile('http://cdn.azulsystems.com/zulu/2015-07-8.8-bin/zulu1.8.0_51-8.8.0.3-win64.msi', 'Zulu OpenJDK');

  idpAddFile('http://download.virtualbox.org/virtualbox/5.0.2/VirtualBox-5.0.2-102096-Win.exe', 'VirtualBox');
  //idpAddFile('https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.4.msi', 'Vagrant');

  //idpDownloadAfter(wpWelcome);

  createDownloadForm(wpReady);
  idpConnectControls;
  idpInitMessages;
end;


installers.windows: Fix GIMP version check

; 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)
      or ((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}
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;