Updated windows installer script updated for latest Gtk2Hs version use a build for only a single ghc version (ghc 6.6.1) we previously included builds for both ghc 6.4.2 and 6.6 include the sourceview component

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

[Setup]
AppName=Gtk2Hs
AppId=Gtk2Hs
AppVerName=Gtk2Hs 0.9.11.3
AppVersion=0.9.11.3
AppPublisher=The Gtk2Hs Team
AppPublisherURL=http://haskell.org/gtk2hs/
AppSupportURL=http://haskell.org/gtk2hs/
AppUpdatesURL=http://haskell.org/gtk2hs/

DefaultDirName={pf}\Gtk2Hs
OutputBaseFilename=gtk2hs-0.9.11.3

VersionInfoVersion=0.9.11.3
VersionInfoCopyright=Copyright (C) 2001-2007 The Gtk2Hs Team

Compression=lzma/max
SolidCompression=yes

ChangesEnvironment=yes

[Components]
Name: "gtk";     Description: "Gtk+ libraries"; Types: full compact custom; Flags: fixed
;Name: "gtk2hs1"; Description: "Gtk2Hs libraries for GHC 6.4.2"; Check: UseWithGhcVersion('6.4.2'); Types: full compact custom; Flags: fixed
Name: "gtk2hs2"; Description: "Gtk2Hs libraries for GHC 6.6.1"; Check: UseWithGhcVersion('6.6.1'); Types: full compact custom; Flags: fixed
Name: "docs";    Description: "API reference documentation"; Types: full
Name: "demos";   Description: "Source files for the Gtk2Hs demo programs"; Types: full

[Files]
Source: "gtk+-2.10.13\*";                        DestDir: "{app}";       Components: gtk;     Flags: ignoreversion recursesubdirs createallsubdirs;
;Source: "gtk2hs-0.9.11.3-ghc-6.4.2-gtk-2.10\*"; DestDir: "{app}";       Components: gtk2hs1; Flags: ignoreversion recursesubdirs createallsubdirs; AfterInstall: AfterPkgInstall;
Source: "gtk2hs-0.9.11.3-ghc-6.6.1-gtk-2.10\*"; DestDir: "{app}";       Components: gtk2hs2; Flags: ignoreversion recursesubdirs createallsubdirs; AfterInstall: AfterPkgInstall;
Source: "gtk2hs-0.9.11.3-demo\*";               DestDir: "{app}\demos"; Components: demos;   Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "gtk2hs-0.9.11.3-docs\*";               DestDir: "{app}\docs";  Components: docs;    Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "COPYING.txt";                          DestDir: "{app}";                            Flags: ignoreversion;
Source: "AUTHORS.txt";                          DestDir: "{app}";                            Flags: ignoreversion;

[Registry]
Root: HKCU; Subkey: "Environment"; ValueName: "Path"; ValueType: "string"; ValueData: "{app}\bin;{olddata}"; Check: NotOnPathAlready(); Flags: preservestringtype;

[Run]
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glib.package.conf""";     StatusMsg: "Registering glib package...";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\cairo.package.conf""";    StatusMsg: "Registering cairo package...";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\svgcairo.package.conf"""; StatusMsg: "Registering svgcairo package..."; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtk.package.conf""";      StatusMsg: "Registering gtk package...";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glade.package.conf""";    StatusMsg: "Registering glade package...";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\soegtk.package.conf""";   StatusMsg: "Registering soegtk package...";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtkglext.package.conf"""; StatusMsg: "Registering gtkglext package..."; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\sourceview.package.conf"""; StatusMsg: "Registering sourceview package..."; Flags: runhidden
[UninstallRun]
Filename: "{code:ghcpkg}"; Parameters: "unregister sourceview-0.9.11.3"; RunOnceId: "gtkglext"; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gtkglext-0.9.11.3";   RunOnceId: "gtkglext"; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister soegtk-0.9.11.3";     RunOnceId: "soegtk";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glade-0.9.11.3";      RunOnceId: "glade";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gtk-0.9.11.3";        RunOnceId: "gtk";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister svgcairo-0.9.11.3";   RunOnceId: "svgcairo"; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister cairo-0.9.11.3";      RunOnceId: "cairo";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glib-0.9.11.3";       RunOnceId: "glib";     Flags: runhidden

[Code]
var
  GhcInstallDir: String;
  GhcInstallVersion: String;

  CheckingPage: TOutputProgressWizardPage;
  ErrorReportPage: TOutputMsgMemoWizardPage;
  InstallationErrorCaption: String;
  InstallationErrorMessage: String;
  InstallationErrorDetail: String;

function ghcpkg(Param: String): String;
begin
  Result := AddBackslash(GhcInstallDir) + 'bin\ghc-pkg.exe';
end;

function NotOnPathAlready(): Boolean;
var
  BinDir, Path: String;
begin
  Log('Checking if Gtk2Hs\bin dir is already on the %PATH%');
  if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path) then
  begin // Successfully read the value
    Log('HKCU\Environment\PATH = ' + Path);
    BinDir := ExpandConstant('{app}\bin');
    Log('Looking for Gtk2Hs\bin dir in %PATH%: ' + BinDir + ' in ' + Path);
    if Pos(LowerCase(BinDir), Lowercase(Path)) = 0 then
    begin
      Log('Did not find Gtk2Hs\bin dir in %PATH% so will add it');
      Result := True;
    end
    else
    begin
      Log('Found Gtk2Hs bin dir in %PATH% so will not add it again');
      Result := False;
    end
  end
  else // The key probably doesn't exist
  begin
    Log('Could not access HKCU\Environment\PATH so assume it is ok to add it');
    Result := True;
  end;
end;

{ exec a program and return its output }
function ExecOutput(const Filename, Params: String): String;
var
  TmpFile: String;
  ResultCode: Integer;
begin
  Result := '';
  TmpFile := GenerateUniqueName(ExpandConstant('{tmp}'), 'tmp');
  if Exec(ExpandConstant('{cmd}'), '/C ' + Filename + ' ' + Params + ' > ' + TmpFile,
     '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
    if ResultCode = 0 then
      if LoadStringFromFile(TmpFile, Result) then
      begin
        Result := Trim(Result);
        Log('ExecOutput: running ' + Filename + ' succeded with output: ' + Result);
      end
      else
        Log('ExecOutput: running ' + Filename + ' succeded, but the temp file was not created')
    else
      Log('ExecOutput: running ' + Filename + ' failed, code: ' + IntToStr(ResultCode))
  else
    { That's really odd, we should always be able to exec the command interpreter! }
    Log('ExecOutput: cannot exec command interpreter, code: ' + IntToStr(ResultCode) + ', message: ' + SysErrorMessage(ResultCode));
end;

function CheckGhcVersionIsOk(const Path: String; var Version: String):Boolean;
begin
  Version := ExecOutput(AddBackslash(Path) + 'bin\ghc.exe', '--numeric-version');
  GhcInstallVersion := Version;

  Result := {-(Version = '6.4.2')
           or-} (Version = '6.6.1');
end;

var
  DetectValidGhcInstallationCached: Boolean;
  DetectValidGhcInstallationResult: Boolean;

function DetectValidGhcInstallation(): Boolean;
var
  HaveSomeGHCInstalled: Boolean;
  GHCVersion: String;

begin
if DetectValidGhcInstallationCached then
  Result := DetectValidGhcInstallationResult
else
begin
  Result := False;

  { first check for ghc on the path, then look in the registry }

  begin
    GhcInstallDir := ExecOutput('ghc.exe', '--print-libdir');
    if GhcInstallDir <> '' then
    begin
      StringChange(GhcInstallDir, '/', '\');
      Log('DetectValidGhcInstallation: found ghc on %PATH% with libdir: ' + GhcInstallDir);
      HaveSomeGHCInstalled := True;
      Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
    end;
  end;

  if (Result = False) and RegKeyExists(HKEY_CURRENT_USER, 'Software\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKCU\Software\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Haskell\GHC\ghc-6.6.1', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
{-
  if (Result = False) and RegKeyExists(HKEY_CURRENT_USER, 'Software\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKCU\Software\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Haskell\GHC\ghc-6.4.2', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
-}
  if (Result = False) and RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKLM\SOFTWARE\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC\ghc-6.6.1', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
{-
  if (Result = False) and RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKLM\SOFTWARE\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC\ghc-6.4.2', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
-}
  if Result then
    Log('DetectValidGhcInstallation: correct version of ghc found at: ' + GhcInstallDir)
  else if HaveSomeGHCInstalled and (GHCVersion <> '') then
  begin
    Log('DetectValidGhcInstallation: incorrect ghc version installed: ' + GHCVersion);
    InstallationErrorCaption := 'The version of GHC currently installed is not suitable.';
    InstallationErrorMessage := 'This version of Gtk2Hs requires GHC version 6.6.1.' #13#10 #13#10
                                'Setup found GHC version ' + GHCVersion + ' installed in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled and (GhcInstallDir <> '') then
  begin
    Log('DetectValidGhcInstallation: some non-working version of ghc appears to be installed at: ' + GhcInstallDir);
    InstallationErrorCaption := 'GHC does not seem to be working.';
    InstallationErrorMessage := 'GHC does not appear to be installed correctly, try reinstalling GHC version 6.6.1' #13#10 #13#10
                                'Setup found what appears to be a non-working installation of GHC in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled then
  begin
    Log('DetectValidGhcInstallation: corrupted ghc installation detected, probably messed up registry keys');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'GHC does not appear to be installed (or the installation is corrupted), please install GHC version 6.6.1';
  end
  else
  begin
    Log('DetectValidGhcInstallation: no installation of ghc detected');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'Gtk2Hs requires GHC to be installed first, please install GHC version 6.6.1' #13#10 #13#10
                                'If you installed GHC manually then make sure ghc.exe is on the path.';
  end;

  If not Result then
  begin
    Log(InstallationErrorCaption);
    Log(InstallationErrorMessage);
  end;

  DetectValidGhcInstallationResult := Result;
  DetectValidGhcInstallationCached := True;
end;
end;

function UseWithGhcVersion(const GhcVersion: String): Boolean;
begin
  DetectValidGhcInstallation();
  Result := (GhcVersion = GhcInstallVersion);
end;

var
  ChecksOk : Boolean;

function ShouldSkipPage(CurPageID: Integer): Boolean;
begin
  Log('ShouldSkipPage: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = ErrorReportPage.ID then
  begin
    if ChecksOk then
      Log('ChecksOk = True')
    else
      Log('ChecksOk = False');
    Result := ChecksOk
  end
  else
    Result := False;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  DllFiles, Offenders: array of String;
  Path, Offender: String;
  N, M, NumFiles: Integer;
begin
  Log('NextButtonClick: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = wpWelcome then
  begin
    if not DetectValidGhcInstallation() then
      ChecksOk := False
    else
    begin
      DllFiles := ['charset.dll', 'gspawn-win32-helper-console.exe',
        'gspawn-win32-helper.exe', 'iconv.dll', 'intl.dll',
        'jpeg62.dll', 'libatk-1.0-0.dll', 'libcairo-2.dll',
        'libgdk-win32-2.0-0.dll', 'libgdk_pixbuf-2.0-0.dll',
        'libglade-2.0-0.dll', 'libglib-2.0-0.dll',
        'libgmodule-2.0-0.dll', 'libgobject-2.0-0.dll',
        'libgthread-2.0-0.dll', 'libgtk-win32-2.0-0.dll',
        'libpango-1.0-0.dll', 'libpangocairo-1.0-0.dll',
        'libpangoft2-1.0-0.dll', 'libpangowin32-1.0-0.dll',
        'libpng13.dll', 'libxml2.dll', 'zlib1.dll'];
      NumFiles := GetArrayLength(DllFiles);
      SetArrayLength(Offenders, NumFiles);

      RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', Path);
      Path := GetSystemDir() + ';' + GetWinDir() + ';' + Path;

      Log('Checking DLL path: ' + Path);

      CheckingPage.SetText('Checking Gtk+ DLL search path...', '');
      CheckingPage.SetProgress(0, NumFiles);
      CheckingPage.Show;
      try
        M := 0;
        for N := 0 to NumFiles - 1 do
        begin
          Offender := FileSearch(DllFiles[N], Path);
          if Offender <> '' then
          begin
            Log('Found clashing dll/exe file at ' + Offender);
            Offenders[M] := Offender;
            M := M + 1;
          end
          CheckingPage.SetProgress(N+1, NumFiles);
          Sleep(10);
        end;
        if M > 0 then
        begin
          ChecksOk := False;
          InstallationErrorCaption := 'Setup found clashing DLL files on the search path.';
          InstallationErrorMessage := 'Please read the following description of the problem and how to fix it:';

          InstallationErrorDetail :=
              'For Haskell Gtk programs to work properly, the Gtk+ DLL files need to be in the DLL '
            + 'search path. However, the following DLLs files that Gtk+ uses are already on the '
            + 'system-wide search path:' #13#10

          for N := 0 to M - 1 do
            InstallationErrorDetail := InstallationErrorDetail + #13#10 + Offenders[N];

          InstallationErrorDetail := InstallationErrorDetail + #13#10 #13#10
              'The problem is that when you run a Haskell Gtk program these DLLs would get used '
            + 'rather than the proper versions of the Gtk+ DLLs installed by this setup program.'
            + 'This would almost certainly cause all your Haskell Gtk programs to not work.' #13#10 #13#10
              'You can fix this problem in a couple of ways:' #13#10 #13#10
              '1) You could uninstall or delete the program that installed the offending files '
            + '(eg it might be that you have an old version of Gtk+ installed).' #13#10 #13#10
              '2) You could modify the system-wide "Path" envrionment variable and remove the '
            + 'offending directory from it.'
        end
        else
          ChecksOk := True;
      finally
        CheckingPage.Hide;
      end;
    end;
  end
  Result := True;
end;

procedure PackageFileVarSubstitute(const PackageFile: String);
var
  PackageFileContent: String;
  PackageLibDir: String;
begin
  PackageLibDir := ExpandConstant('{app}');
  StringChange(PackageLibDir, '\', '/');

  LoadStringFromFile(PackageFile, PackageFileContent);

  StringChange(PackageFileContent, '${pkglibdir}', PackageLibDir);
  StringChange(PackageFileContent, '${GTK_BASEPATH}', PackageLibDir);

  SaveStringToFile(PackageFile, PackageFileContent, False);
  Log('Expanding variables in ' + PackageFile);
end;

procedure AfterPkgInstall;
begin
  if (ExtractFileExt(CurrentFileName) = '.conf') then
    PackageFileVarSubstitute(ExpandConstant(CurrentFileName));
end;

// Detect when the ready info page gets displayed
function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
                         MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo,
                         MemoTasksInfo: String): String;
begin
		Result := MemoDirInfo + NewLine
		        + NewLine
		        +	'Using GHC ' + GhcInstallVersion + ' installed at:' + NewLine
		        + Space + GhcInstallDir + NewLine
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  BinDir, Path: String;
begin
  if (CurUninstallStep = usPostUninstall)
     and (RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path)) then
  begin
    BinDir := ExpandConstant('{app}\bin');
    if Pos(LowerCase(BinDir) + ';', Lowercase(Path)) <> 0 then
    begin
      StringChange(Path, BinDir + ';', '');
      RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path);
    end;
  end;
end;

procedure ErrorReportPageNotify(Sender: TWizardPage);
begin
  WizardForm.NextButton.Enabled := False;
  ErrorReportPage.Description      := InstallationErrorCaption;
  ErrorReportPage.SubCaptionLabel.Caption := InstallationErrorMessage;
  if InstallationErrorDetail = '' then
    ErrorReportPage.SubCaptionLabel.Height := 50
  else
  begin
    ErrorReportPage.RichEditViewer.RTFText := InstallationErrorDetail
    ErrorReportPage.RichEditViewer.Show();
  end;
end;

procedure InitializeWizard();
begin
  CheckingPage := CreateOutputProgressPage('Checking requirements', 'Checking for GHC and Gtk+');
  ErrorReportPage := CreateOutputMsgMemoPage(wpWelcome, 'Setup cannot continue', 'GHC or Gtk+ problem', 'Your installation of GHC or Gtk+ is messed up!', 'Foo bar!');
  ErrorReportPage.OnActivate := @ErrorReportPageNotify;
  ErrorReportPage.RichEditViewer.Hide();
end;

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

[Setup]
AppName=Gtk2Hs
AppId=Gtk2Hs
AppVerName=Gtk2Hs 0.9.10.6
AppVersion=0.9.10.6
AppPublisher=The Gtk2Hs Team
AppPublisherURL=http://haskell.org/gtk2hs/
AppSupportURL=http://haskell.org/gtk2hs/
AppUpdatesURL=http://haskell.org/gtk2hs/

DefaultDirName={pf}\Gtk2Hs
OutputBaseFilename=gtk2hs-0.9.10.6

VersionInfoVersion=0.9.10.6
VersionInfoCopyright=Copyright (C) 2001-2007 The Gtk2Hs Team

Compression=lzma/max
SolidCompression=yes

ChangesEnvironment=yes

[Components]
Name: "gtk";     Description: "Gtk+ libraries"; Types: full compact custom; Flags: fixed
Name: "gtk2hs1"; Description: "Gtk2Hs libraries for GHC 6.4.2"; Check: UseWithGhcVersion('6.4.2'); Types: full compact custom; Flags: fixed
Name: "gtk2hs2"; Description: "Gtk2Hs libraries for GHC 6.6"; Check: UseWithGhcVersion('6.6'); Types: full compact custom; Flags: fixed
Name: "docs";    Description: "API reference documentation"; Types: full
Name: "demos";   Description: "Source files for the Gtk2Hs demo programs"; Types: full

[Files]
Source: "gtk+-2.10.9\*";                        DestDir: "{app}";       Components: gtk;     Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "gtk2hs-0.9.10.6-ghc-6.4.2-gtk-2.10\*"; DestDir: "{app}";       Components: gtk2hs1; Flags: ignoreversion recursesubdirs createallsubdirs; AfterInstall: AfterPkgInstall;
Source: "gtk2hs-0.9.10.6-ghc-6.6-gtk-2.10\*";   DestDir: "{app}";       Components: gtk2hs2; Flags: ignoreversion recursesubdirs createallsubdirs; AfterInstall: AfterPkgInstall;
Source: "gtk2hs-0.9.10.6-demo\*";               DestDir: "{app}\demos"; Components: demos;   Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "gtk2hs-0.9.10.6-docs\*";               DestDir: "{app}\docs";  Components: docs;    Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "COPYING.txt";                          DestDir: "{app}";                            Flags: ignoreversion;
Source: "AUTHORS.txt";                          DestDir: "{app}";                            Flags: ignoreversion;

[Registry]
Root: HKCU; Subkey: "Environment"; ValueName: "Path"; ValueType: "string"; ValueData: "{app}\bin;{olddata}"; Check: NotOnPathAlready(); Flags: preservestringtype;

[Run]
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glib.package.conf""";     StatusMsg: "Registering glib package...";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\cairo.package.conf""";    StatusMsg: "Registering cairo package...";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\svgcairo.package.conf"""; StatusMsg: "Registering svgcairo package..."; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtk.package.conf""";      StatusMsg: "Registering gtk package...";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glade.package.conf""";    StatusMsg: "Registering glade package...";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\soegtk.package.conf""";   StatusMsg: "Registering soegtk package...";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtkglext.package.conf"""; StatusMsg: "Registering gtkglext package..."; Flags: runhidden

[UninstallRun]
Filename: "{code:ghcpkg}"; Parameters: "unregister gtkglext-0.9.10.6"; RunOnceId: "gtkglext"; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister soegtk-0.9.10.6";   RunOnceId: "soegtk";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glade-0.9.10.6";    RunOnceId: "glade";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gtk-0.9.10.6";      RunOnceId: "gtk";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister svgcairo-0.9.10.6"; RunOnceId: "svgcairo"; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister cairo-0.9.10.6";    RunOnceId: "cairo";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glib-0.9.10.6";     RunOnceId: "glib";     Flags: runhidden

[Code]
var
  GhcInstallDir: String;
  GhcInstallVersion: String;

  CheckingPage: TOutputProgressWizardPage;
  ErrorReportPage: TOutputMsgMemoWizardPage;
  InstallationErrorCaption: String;
  InstallationErrorMessage: String;
  InstallationErrorDetail: String;

function ghcpkg(Param: String): String;
begin
  Result := AddBackslash(GhcInstallDir) + 'bin\ghc-pkg.exe';
end;

function NotOnPathAlready(): Boolean;
var
  BinDir, Path: String;
begin
  Log('Checking if Gtk2Hs\bin dir is already on the %PATH%');
  if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path) then
  begin // Successfully read the value
    Log('HKCU\Environment\PATH = ' + Path);
    BinDir := ExpandConstant('{app}\bin');
    Log('Looking for Gtk2Hs\bin dir in %PATH%: ' + BinDir + ' in ' + Path);
    if Pos(LowerCase(BinDir), Lowercase(Path)) = 0 then
    begin
      Log('Did not find Gtk2Hs\bin dir in %PATH% so will add it');
      Result := True;
    end
    else
    begin
      Log('Found Gtk2Hs bin dir in %PATH% so will not add it again');
      Result := False;
    end
  end
  else // The key probably doesn't exist
  begin
    Log('Could not access HKCU\Environment\PATH so assume it is ok to add it');
    Result := True;
  end;
end;

{ exec a program and return its output }
function ExecOutput(const Filename, Params: String): String;
var
  TmpFile: String;
  ResultCode: Integer;
begin
  Result := '';
  TmpFile := GenerateUniqueName(ExpandConstant('{tmp}'), 'tmp');
  if Exec(ExpandConstant('{cmd}'), '/C ' + Filename + ' ' + Params + ' > ' + TmpFile,
     '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
    if ResultCode = 0 then
      if LoadStringFromFile(TmpFile, Result) then
      begin
        Result := Trim(Result);
        Log('ExecOutput: running ' + Filename + ' succeded with output: ' + Result);
      end
      else
        Log('ExecOutput: running ' + Filename + ' succeded, but the temp file was not created')
    else
      Log('ExecOutput: running ' + Filename + ' failed, code: ' + IntToStr(ResultCode))
  else
    { That's really odd, we should always be able to exec the command interpreter! }
    Log('ExecOutput: cannot exec command interpreter, code: ' + IntToStr(ResultCode) + ', message: ' + SysErrorMessage(ResultCode));
end;

function CheckGhcVersionIsOk(const Path: String; var Version: String):Boolean;
begin
  Version := ExecOutput(AddBackslash(Path) + 'bin\ghc.exe', '--numeric-version');
  GhcInstallVersion := Version;

  Result := (Version = '6.4.2')
         or (Version = '6.6');
end;

var
  DetectValidGhcInstallationCached: Boolean;
  DetectValidGhcInstallationResult: Boolean;

function DetectValidGhcInstallation(): Boolean;
var
  HaveSomeGHCInstalled: Boolean;
  GHCVersion: String;

begin
if DetectValidGhcInstallationCached then
  Result := DetectValidGhcInstallationResult
else
begin
  Result := False;

  { first check for ghc on the path, then look in the registry }

  begin
    GhcInstallDir := ExecOutput('ghc.exe', '--print-libdir');
    if GhcInstallDir <> '' then
    begin
      StringChange(GhcInstallDir, '/', '\');
      Log('DetectValidGhcInstallation: found ghc on %PATH% with libdir: ' + GhcInstallDir);
      HaveSomeGHCInstalled := True;
      Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
    end;
  end;

  if (Result = False) and RegKeyExists(HKEY_CURRENT_USER, 'Software\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKCU\Software\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Haskell\GHC\ghc-6.6', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;

  if (Result = False) and RegKeyExists(HKEY_CURRENT_USER, 'Software\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKCU\Software\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Haskell\GHC\ghc-6.4.2', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;

  if (Result = False) and RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKLM\SOFTWARE\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC\ghc-6.6', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;

  if (Result = False) and RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKLM\SOFTWARE\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC\ghc-6.4.2', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;

  if Result then
    Log('DetectValidGhcInstallation: correct version of ghc found at: ' + GhcInstallDir)
  else if HaveSomeGHCInstalled and (GHCVersion <> '') then
  begin
    Log('DetectValidGhcInstallation: incorrect ghc version installed: ' + GHCVersion);
    InstallationErrorCaption := 'The version of GHC currently installed is not suitable.';
    InstallationErrorMessage := 'This version of Gtk2Hs requires GHC version 6.6 or 6.4.2.' #13#10 #13#10
                                'Setup found GHC version ' + GHCVersion + ' installed in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled and (GhcInstallDir <> '') then
  begin
    Log('DetectValidGhcInstallation: some non-working version of ghc appears to be installed at: ' + GhcInstallDir);
    InstallationErrorCaption := 'GHC does not seem to be working.';
    InstallationErrorMessage := 'GHC does not appear to be installed correctly, try reinstalling GHC version 6.6 or 6.4.2' #13#10 #13#10
                                'Setup found what appears to be a non-working installation of GHC in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled then
  begin
    Log('DetectValidGhcInstallation: corrupted ghc installation detected, probably messed up registry keys');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'GHC does not appear to be installed (or the installation is corrupted), please install GHC version 6.6 or 6.4.2';
  end
  else
  begin
    Log('DetectValidGhcInstallation: no installation of ghc detected');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'Gtk2Hs requires GHC to be installed first, please install GHC version 6.6 or 6.4.2' #13#10 #13#10
                                'If you installed GHC manually then make sure ghc.exe is on the path.';
  end;

  If not Result then
  begin
    Log(InstallationErrorCaption);
    Log(InstallationErrorMessage);
  end;

  DetectValidGhcInstallationResult := Result;
  DetectValidGhcInstallationCached := True;
end;
end;

function UseWithGhcVersion(const GhcVersion: String): Boolean;
begin
  DetectValidGhcInstallation();
  Result := (GhcVersion = GhcInstallVersion);
end;

var
  ChecksOk : Boolean;

function ShouldSkipPage(CurPageID: Integer): Boolean;
begin
  Log('ShouldSkipPage: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = ErrorReportPage.ID then
  begin
    if ChecksOk then
      Log('ChecksOk = True')
    else
      Log('ChecksOk = False');
    Result := ChecksOk
  end
  else
    Result := False;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  DllFiles, Offenders: array of String;
  Path, Offender: String;
  N, M, NumFiles: Integer;
begin
  Log('NextButtonClick: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = wpWelcome then
  begin
    if not DetectValidGhcInstallation() then
      ChecksOk := False
    else
    begin
      DllFiles := ['charset.dll', 'gspawn-win32-helper-console.exe',
        'gspawn-win32-helper.exe', 'iconv.dll', 'intl.dll',
        'jpeg62.dll', 'libatk-1.0-0.dll', 'libcairo-2.dll',
        'libgdk-win32-2.0-0.dll', 'libgdk_pixbuf-2.0-0.dll',
        'libglade-2.0-0.dll', 'libglib-2.0-0.dll',
        'libgmodule-2.0-0.dll', 'libgobject-2.0-0.dll',
        'libgthread-2.0-0.dll', 'libgtk-win32-2.0-0.dll',
        'libpango-1.0-0.dll', 'libpangocairo-1.0-0.dll',
        'libpangoft2-1.0-0.dll', 'libpangowin32-1.0-0.dll',
        'libpng13.dll', 'libxml2.dll', 'zlib1.dll'];
      NumFiles := GetArrayLength(DllFiles);
      SetArrayLength(Offenders, NumFiles);

      RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', Path);
      Path := GetSystemDir() + ';' + GetWinDir() + ';' + Path;

      Log('Checking DLL path: ' + Path);

      CheckingPage.SetText('Checking Gtk+ DLL search path...', '');
      CheckingPage.SetProgress(0, NumFiles);
      CheckingPage.Show;
      try
        M := 0;
        for N := 0 to NumFiles - 1 do
        begin
          Offender := FileSearch(DllFiles[N], Path);
          if Offender <> '' then
          begin
            Log('Found clashing dll/exe file at ' + Offender);
            Offenders[M] := Offender;
            M := M + 1;
          end
          CheckingPage.SetProgress(N+1, NumFiles);
          Sleep(10);
        end;
        if M > 0 then
        begin
          ChecksOk := False;
          InstallationErrorCaption := 'Setup found clashing DLL files on the search path.';
          InstallationErrorMessage := 'Please read the following description of the problem and how to fix it:';

          InstallationErrorDetail :=
              'For Haskell Gtk programs to work properly, the Gtk+ DLL files need to be in the DLL '
            + 'search path. However, the following DLLs files that Gtk+ uses are already on the '
            + 'system-wide search path:' #13#10

          for N := 0 to M - 1 do
            InstallationErrorDetail := InstallationErrorDetail + #13#10 + Offenders[N];

          InstallationErrorDetail := InstallationErrorDetail + #13#10 #13#10
              'The problem is that when you run a Haskell Gtk program these DLLs would get used '
            + 'rather than the proper versions of the Gtk+ DLLs installed by this setup program.'
            + 'This would almost certainly cause all your Haskell Gtk programs to not work.' #13#10 #13#10
              'You can fix this problem in a couple of ways:' #13#10 #13#10
              '1) You could uninstall or delete the program that installed the offending files '
            + '(eg it might be that you have an old version of Gtk+ installed).' #13#10 #13#10
              '2) You could modify the system-wide "Path" envrionment variable and remove the '
            + 'offending directory from it.'
        end
        else
          ChecksOk := True;
      finally
        CheckingPage.Hide;
      end;
    end;
  end
  Result := True;
end;

procedure PackageFileVarSubstitute(const PackageFile: String);
var
  PackageFileContent: String;
  PackageLibDir: String;
begin
  PackageLibDir := ExpandConstant('{app}');
  StringChange(PackageLibDir, '\', '/');

  LoadStringFromFile(PackageFile, PackageFileContent);

  StringChange(PackageFileContent, '${pkglibdir}', PackageLibDir);
  StringChange(PackageFileContent, '${GTK_BASEPATH}', PackageLibDir);

  SaveStringToFile(PackageFile, PackageFileContent, False);
  Log('Expanding variables in ' + PackageFile);
end;

procedure AfterPkgInstall;
begin
  if (ExtractFileExt(CurrentFileName) = '.conf') then
    PackageFileVarSubstitute(ExpandConstant(CurrentFileName));
end;

// Detect when the ready info page gets displayed
function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
                         MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo,
                         MemoTasksInfo: String): String;
begin
		Result := MemoDirInfo + NewLine
		        + NewLine
		        +	'Using GHC ' + GhcInstallVersion + ' installed at:' + NewLine
		        + Space + GhcInstallDir + NewLine
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  BinDir, Path: String;
begin
  if (CurUninstallStep = usPostUninstall)
     and (RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path)) then
  begin
    BinDir := ExpandConstant('{app}\bin');
    if Pos(LowerCase(BinDir) + ';', Lowercase(Path)) <> 0 then
    begin
      StringChange(Path, BinDir + ';', '');
      RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path);
    end;
  end;
end;

procedure ErrorReportPageNotify(Sender: TWizardPage);
begin
  WizardForm.NextButton.Enabled := False;
  ErrorReportPage.Description      := InstallationErrorCaption;
  ErrorReportPage.SubCaptionLabel.Caption := InstallationErrorMessage;
  if InstallationErrorDetail = '' then
    ErrorReportPage.SubCaptionLabel.Height := 50
  else
  begin
    ErrorReportPage.RichEditViewer.RTFText := InstallationErrorDetail
    ErrorReportPage.RichEditViewer.Show();
  end;
end;

procedure InitializeWizard();
begin
  CheckingPage := CreateOutputProgressPage('Checking requirements', 'Checking for GHC and Gtk+');
  ErrorReportPage := CreateOutputMsgMemoPage(wpWelcome, 'Setup cannot continue', 'GHC or Gtk+ problem', 'Your installation of GHC or Gtk+ is messed up!', 'Foo bar!');
  ErrorReportPage.OnActivate := @ErrorReportPageNotify;
  ErrorReportPage.RichEditViewer.Hide();
end;


Fixed SED-ML example files.

Fixed SED-ML example files.

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

#define MyAppName "COPASI"
#define MyAppVersion "0.0.0"
#define MyAppPublisher "copasi.org"
#define MyAppURL "http://www.copasi.org/"
#define MyAppExeName "bin\CopasiUI.exe"
#define MyBuild "0"
#define MyAppId "{{00000000-0000-0000-0000-000000000000}"
#define MyWorkDir "C:\cygwin\home\shoops\environment\COPASI\InnoSetup"
#define MyStageDir "C:\cygwin\home\shoops\environment\setup\package"

[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={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
ChangesAssociations=true
ChangesEnvironment=true
DefaultDirName={code:DefDirRoot}\{#MyAppPublisher}\{#MyAppName} {#MyAppVersion}
DefaultGroupName={#MyAppName} {#MyAppVersion}
OutputDir={#MyWorkDir}
OutputBaseFilename=COPASI-{#MyAppVersion}-Windows
SetupIconFile={#MyWorkDir}\addremov.ico
Compression=lzma/Ultra
SolidCompression=true
InternalCompressLevel=Ultra
SignedUninstaller=false
VersionInfoVersion={#MyAppVersion}.0
ShowLanguageDialog=no
UninstallDisplayIcon={app}\share\copasi\icons\Copasi.ico
UninstallDisplayName={#MyAppName} {#MyAppVersion}
PrivilegesRequired=none
VersionInfoCompany=copasi.org
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64

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

[Tasks]
Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked
Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked; OnlyBelowVersion: 0,6.1

[Files]
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
; 32 bit Executables and DLLs
Source: {#MyStageDir}\bin\32\CopasiSE.exe; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\CopasiUI.exe; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtCore4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtGui4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtNetwork4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtOpenGL4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtSvg4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtWebKit4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtXml4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\libmmd.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\svml_dispmd.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\msvcr100.dll; DestDir: {app}\bin; Check: InstallUserRuntime32();
Source: {#MyStageDir}\bin\32\msvcp100.dll; DestDir: {app}\bin; Check: InstallUserRuntime32();

; 64 bit Executables and DLLs
Source: {#MyStageDir}\bin\64\CopasiSE.exe; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\CopasiUI.exe; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtCore4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtGui4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtNetwork4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtOpenGL4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtSvg4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtWebKit4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtXml4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\libmmd.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\svml_dispmd.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\msvcr100.dll; DestDir: {app}\bin; Check: InstallUserRuntime64();
Source: {#MyStageDir}\bin\64\msvcp100.dll; DestDir: {app}\bin; Check: InstallUserRuntime64();

; Common Resources
Source: {#MyStageDir}\README.txt; DestDir: {app}
Source: {#MyStageDir}\LICENSE.txt; DestDir: {app}

; Tutorial Wizzard
Source: {#MyStageDir}\share\copasi\config\MIRIAMResources.xml; DestDir: {app}\share\copasi\config
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step6.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step1.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step2.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step3.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step4.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step5.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\figures\TimeCourseDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ModelSettingsDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\NewPlotAdded.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ObjectBrowserSelection.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ObjectBrowserTree.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\OutputAssistant.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\PlotCurveSelectionDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\PlotDefinition.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\PlotWindow.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReactionDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReactionOverview.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReactionOverviewEmpty.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReportDefinitionDialog.png; DestDir: {app}\share\copasi\doc\html\figures

; Icons
Source: {#MyStageDir}\share\copasi\icons\CopasiDoc.ico; DestDir: {app}\share\copasi\icons
Source: {#MyStageDir}\share\copasi\icons\Copasi.ico; DestDir: {app}\share\copasi\icons

; Examples
Source: {#MyStageDir}\share\copasi\examples\YeastGlycolysis.gps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\brusselator.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\CircadianClock.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\DimericMWC-stiff.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\Genetic-2000Elo.xml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\MAPK-HF96-layout.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\Metabolism-2000Poo.xml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\NF-kappaB.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\Olsen2003_peroxidase.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\brusselator.sedml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\brusselator-model.xml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\NF-kappaB.sedml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\NF-kappaB-model.xml; DestDir: {app}\share\copasi\examples

; VC Redistributable
Source: {#MyStageDir}\bin\32\vcredist_x86.exe; DestDir: {app}; Flags: deleteafterinstall; Check: IsWin32();
Source: {#MyStageDir}\bin\64\vcredist_x64.exe; DestDir: {app}; Flags: deleteafterinstall; Check: IsWin64();

[Icons]
Name: {group}\CopasiUI; Filename: {app}\{#MyAppExeName}; WorkingDir: {userdocs}
Name: {group}\{cm:ProgramOnTheWeb,{#MyAppName}}; Filename: {#MyAppURL}
Name: {group}\License; Filename: {app}\LICENSE.txt
Name: {group}\README; Filename: {app}\README.txt
Name: {group}\{cm:UninstallProgram,{#MyAppName}}; Filename: {uninstallexe}
Name: {commondesktop}\{#MyAppName}; Filename: {app}\{#MyAppExeName}; Tasks: desktopicon; WorkingDir: {userdocs}
Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}; Filename: {app}\{#MyAppExeName}; Tasks: quicklaunchicon; WorkingDir: {userdocs}

[Run]
Filename: {app}\vcredist_x86.exe; StatusMsg: Installing Microsoft Visual C++ 2010 32 bit Runtime Libraries; Parameters: /q:a; Check: InstallSystemRuntime32()
Filename: {app}\vcredist_x64.exe; StatusMsg: Installing Microsoft Visual C++ 2010 64 bit Runtime Libraries; Parameters: /q:a; Check: InstallSystemRuntime64()

[Dirs]
Name: {app}\bin
Name: {app}\share
Name: {app}\share\copasi
Name: {app}\share\copasi\config
Name: {app}\share\copasi\doc
Name: {app}\share\copasi\doc\html
Name: {app}\share\copasi\doc\html\figures
Name: {app}\share\copasi\examples
Name: {app}\share\copasi\icons

[Registry]
Root: HKCR; SubKey: .cps; ValueType: string; ValueData: COPASI.document; Flags: uninsdeletekey; Check: IsAdminUser
Root: HKCR; SubKey: COPASI.document; ValueType: string; ValueData: COPASI File; Flags: uninsdeletekey; Check: IsAdminUser
Root: HKCR; SubKey: COPASI.document\Shell\Open\Command; ValueType: string; ValueData: """{app}\bin\CopasiUI.exe"" ""%1"""; Flags: uninsdeletevalue; Check: IsAdminUser
Root: HKCR; Subkey: COPASI.document\DefaultIcon; ValueType: string; ValueData: {app}\share\copasi\icons\CopasiDoc.ico,-1; Flags: uninsdeletevalue; Check: IsAdminUser
Root: HKLM; Subkey: SYSTEM\CurrentControlSet\Control\Session Manager\Environment; ValueType: string; ValueName: COPASIDIR; ValueData: {app}; Check: IsAdminUser
Root: HKLM; Subkey: SYSTEM\CurrentControlSet\Control\Session Manager\Environment; ValueType: expandsz; ValueName: Path; ValueData: "%COPASIDIR%\bin;{olddata}"; Check: UpdateSystemPath
Root: HKLM; Subkey: Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers; ValueType: string; ValueName: "{app}\bin\CopasiUI.exe"; ValueData: "~ WIN7RTM"; Flags: uninsdeletevalue; Check: IsAdminUserAndWindows8
Root: HKCU; SubKey: Software\Classes\.cps; ValueType: string; ValueData: COPASI.document; Flags: uninsdeletekey; Check: IsRegularUser
Root: HKCU; SubKey: Software\Classes\COPASI.document; ValueType: string; ValueData: COPASI File; Flags: uninsdeletekey; Check: IsRegularUser
Root: HKCU; SubKey: Software\Classes\COPASI.document\Shell\Open\Command; ValueType: string; ValueData: """{app}\bin\CopasiUI.exe"" ""%1"""; Flags: uninsdeletevalue; Check: IsRegularUser
Root: HKCU; Subkey: Software\Classes\COPASI.document\DefaultIcon; ValueType: string; ValueData: {app}\share\copasi\icons\CopasiDoc.ico,-1; Flags: uninsdeletevalue; Check: IsRegularUser
Root: HKCU; Subkey: Environment; ValueType: string; ValueName: COPASIDIR; ValueData: {app}; Check: IsRegularUser
Root: HKCU; Subkey: Environment; ValueType: expandsz; ValueName: Path; ValueData: "%COPASIDIR%\bin;{olddata}"; Check: UpdateUserPath
Root: HKCU; Subkey: Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers; ValueType: string; ValueName: "{app}\bin\CopasiUI.exe"; ValueData: "~ WIN7RTM"; Flags: uninsdeletevalue; Check: IsRegularUserAndWindows8

[Code]
type
  RunTimeVersion = array [0..3] of Integer;

function IsWin32(): Boolean;
begin
  Result := not IsWin64;
end;

function IsAdminUser(): Boolean;
begin
  Result := (IsAdminLoggedOn or IsPowerUserLoggedOn);
end;

function IsRegularUser(): Boolean;
begin
  Result := not (IsAdminLoggedOn or IsPowerUserLoggedOn);
end;

function isWindows8(): Boolean;
var
  Version: TWindowsVersion;

begin
  GetWindowsVersionEx(Version);

  Result := ((Version.Major = 6) and (Version.Minor = 2));
end;

function IsAdminUserAndWindows8(): Boolean;
begin
  Result := (IsAdminUser() and isWindows8());
end;

function IsRegularUserAndWindows8(): Boolean;
begin
  Result := (IsRegularUser() and isWindows8());
end;

function IsCopasiInSystemPath(): Boolean;
var
  CurrentPath: String;
  Position: Integer;

begin
  Result := False;

  if RegQueryStringValue(HKLM,
    'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
    'Path', CurrentPath) then
    // Successfully read the value
    begin
      Position := Pos('%COPASIDIR%\bin', CurrentPath);
      if (Position <> 0) then
        // Found an entry to the COPASI binaries in the Path
        begin
          Result := True;
        end;
    end;
end;

function IsCopasiInUserPath(): Boolean;
var
  CurrentPath: String;
  Position: Integer;

begin
  Result := IsCopasiInSystemPath();

  if RegQueryStringValue(HKCU,
    'Environment',
    'Path', CurrentPath) then
    // Successfully read the value
    begin
      Position := Pos('%COPASIDIR%\bin', CurrentPath);
      if (Position <> 0) then
        // Found an entry to the COPASI binaries in the Path
        begin
          Result := True;
        end;
    end;
end;

function UpdateSystemPath(): Boolean;
begin
  Result := (IsAdminUser() and not IsCopasiInSystemPath());
end;

function UpdateUserPath(): Boolean;
begin
  Result := (IsRegularUser() and not IsCopasiInUserPath());
end;

function StrToVersion(str: String): RunTimeVersion;
var
  Version: RunTimeVersion;
  Position: Integer;

begin
  try
    begin
      Position := Pos('.', str);
      Version[0] := StrToInt(Copy(str, 1, Position - 1));
      Delete(str, 1, Position);

      Position := Pos('.', str);
      Version[1] := StrToInt(Copy(str, 1, Position - 1));
      Delete(str, 1, Position);

      Position := Pos('.', str);
      Version[2] := StrToInt(Copy(str, 1, Position - 1));
      Delete(str, 1, Position);

      Version[3] := StrToInt(str);
    end;

  except
    begin
      Version[0] := 0;
      Version[1] := 0;
      Version[2] := 0;
      Version[3] := 0;
    end;
  end;

  Result := Version;
end;

function VersionToStr(Version: RunTimeVersion): String;
begin
  Result := IntToStr(Version[0]) + '.' +
            IntToStr(Version[1]) + '.' +
            IntToStr(Version[2]) + '.' +
            IntToStr(Version[3]);
end;

function CompareVersion(Version1: RunTimeVersion;
                        Version2: RunTimeVersion): Integer;
begin
  if Version1[0] <> Version2[0] then
    Result := Version1[0] - Version2[0]
  else if Version1[1] <> Version2[1] then
    Result := Version1[1] - Version2[1]
  else if Version1[2] <> Version2[2] then
    Result := Version1[2] - Version2[2]
  else
    Result := Version1[3] - Version2[3];
end;

function IsInVersionRange(OldVersionRange: String;
                          Version: RunTimeVersion): Boolean;
var
  LowerBound: RunTimeVersion;
  UpperBound: RunTimeVersion;
  Position: Integer;

begin
  Result := False;
  Position := Pos('-', OldVersionRange);

  if Position <> 0 then
    begin
      LowerBound := StrToVersion(Copy(OldVersionRange, 1, Position - 1));
      UpperBound := StrToVersion(Copy(OldVersionRange, Position + 1, 100));
    end
  else
    begin
      LowerBound := StrToVersion(OldVersionRange);
      UpperBound := LowerBound;
    end;

  if (CompareVersion(LowerBound, Version) <= 0) and
     (CompareVersion(Version, UpperBound) <= 0) then
    begin
      Result := True;
    end;
end;

function HaveRuntime32(): Boolean;
begin
  Result := RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}');
end;

function HaveRuntime64(): Boolean;
begin
  Result := RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1D8E6291-B0D5-35EC-8441-6616F567A0F7}');
end;

function InstallSystemRuntime32(): Boolean;
begin
  Result := IsWin32() and IsAdminUser() and not HaveRunTime32();
end;

function InstallSystemRuntime64(): Boolean;
begin
  Result := IsWin64() and IsAdminUser() and not HaveRunTime64();
end;

function InstallUserRuntime32(): Boolean;
begin
  Result := IsWin32() and IsRegularUser() and not HaveRunTime32();
end;

function InstallUserRuntime64(): Boolean;
begin
  Result := IsWin64() and IsRegularUser() and not HaveRunTime64();
end;

function InitializeSetup(): Boolean;
begin
  Result := True;

  RegDeleteValue(HKEY_CURRENT_USER, 'Environment', 'COPASIDIR');
end;

function DefDirRoot(Param: String): String;
begin
  if IsRegularUser() then
    Result := ExpandConstant('{localappdata}')
  else if IsWin64() then
    Result := ExpandConstant('{pf64}')
  else
    Result := ExpandConstant('{pf}');
end;

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

#define MyAppName "COPASI"
#define MyAppVersion "0.0.0"
#define MyAppPublisher "copasi.org"
#define MyAppURL "http://www.copasi.org/"
#define MyAppExeName "bin\CopasiUI.exe"
#define MyBuild "0"
#define MyAppId "{{00000000-0000-0000-0000-000000000000}"
#define MyWorkDir "C:\cygwin\home\shoops\environment\COPASI\InnoSetup"
#define MyStageDir "C:\cygwin\home\shoops\environment\setup\package"

[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={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
ChangesAssociations=true
ChangesEnvironment=true
DefaultDirName={code:DefDirRoot}\{#MyAppPublisher}\{#MyAppName} {#MyAppVersion}
DefaultGroupName={#MyAppName} {#MyAppVersion}
OutputDir={#MyWorkDir}
OutputBaseFilename=COPASI-{#MyAppVersion}-Windows
SetupIconFile={#MyWorkDir}\addremov.ico
Compression=lzma/Ultra
SolidCompression=true
InternalCompressLevel=Ultra
SignedUninstaller=false
VersionInfoVersion={#MyAppVersion}.0
ShowLanguageDialog=no
UninstallDisplayIcon={app}\share\copasi\icons\Copasi.ico
UninstallDisplayName={#MyAppName} {#MyAppVersion}
PrivilegesRequired=none
VersionInfoCompany=copasi.org
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64

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

[Tasks]
Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked
Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked; OnlyBelowVersion: 0,6.1

[Files]
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
; 32 bit Executables and DLLs
Source: {#MyStageDir}\bin\32\CopasiSE.exe; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\CopasiUI.exe; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtCore4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtGui4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtNetwork4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtOpenGL4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtSvg4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtWebKit4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\QtXml4.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\libmmd.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\svml_dispmd.dll; DestDir: {app}\bin; Check: IsWin32();
Source: {#MyStageDir}\bin\32\msvcr100.dll; DestDir: {app}\bin; Check: InstallUserRuntime32();
Source: {#MyStageDir}\bin\32\msvcp100.dll; DestDir: {app}\bin; Check: InstallUserRuntime32();

; 64 bit Executables and DLLs
Source: {#MyStageDir}\bin\64\CopasiSE.exe; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\CopasiUI.exe; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtCore4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtGui4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtNetwork4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtOpenGL4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtSvg4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtWebKit4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\QtXml4.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\libmmd.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\svml_dispmd.dll; DestDir: {app}\bin; Check: IsWin64();
Source: {#MyStageDir}\bin\64\msvcr100.dll; DestDir: {app}\bin; Check: InstallUserRuntime64();
Source: {#MyStageDir}\bin\64\msvcp100.dll; DestDir: {app}\bin; Check: InstallUserRuntime64();

; Common Resources
Source: {#MyStageDir}\README.txt; DestDir: {app}
Source: {#MyStageDir}\LICENSE.txt; DestDir: {app}

; Tutorial Wizzard
Source: {#MyStageDir}\share\copasi\config\MIRIAMResources.xml; DestDir: {app}\share\copasi\config
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step6.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step1.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step2.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step3.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step4.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\TutWiz-Step5.html; DestDir: {app}\share\copasi\doc\html
Source: {#MyStageDir}\share\copasi\doc\html\figures\TimeCourseDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ModelSettingsDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\NewPlotAdded.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ObjectBrowserSelection.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ObjectBrowserTree.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\OutputAssistant.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\PlotCurveSelectionDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\PlotDefinition.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\PlotWindow.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReactionDialog.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReactionOverview.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReactionOverviewEmpty.png; DestDir: {app}\share\copasi\doc\html\figures
Source: {#MyStageDir}\share\copasi\doc\html\figures\ReportDefinitionDialog.png; DestDir: {app}\share\copasi\doc\html\figures

; Icons
Source: {#MyStageDir}\share\copasi\icons\CopasiDoc.ico; DestDir: {app}\share\copasi\icons
Source: {#MyStageDir}\share\copasi\icons\Copasi.ico; DestDir: {app}\share\copasi\icons

; Examples
Source: {#MyStageDir}\share\copasi\examples\YeastGlycolysis.gps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\brusselator.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\CircadianClock.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\DimericMWC-stiff.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\Genetic-2000Elo.xml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\MAPK-HF96-layout.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\Metabolism-2000Poo.xml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\NF-kappaB.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\Olsen2003_peroxidase.cps; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\brusselator.sedml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\brusselator-model.xml.sedml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\NF-kappaB.sedml; DestDir: {app}\share\copasi\examples
Source: {#MyStageDir}\share\copasi\examples\NF-kappaB-model.xml.sedml; DestDir: {app}\share\copasi\examples

; VC Redistributable
Source: {#MyStageDir}\bin\32\vcredist_x86.exe; DestDir: {app}; Flags: deleteafterinstall; Check: IsWin32();
Source: {#MyStageDir}\bin\64\vcredist_x64.exe; DestDir: {app}; Flags: deleteafterinstall; Check: IsWin64();

[Icons]
Name: {group}\CopasiUI; Filename: {app}\{#MyAppExeName}; WorkingDir: {userdocs}
Name: {group}\{cm:ProgramOnTheWeb,{#MyAppName}}; Filename: {#MyAppURL}
Name: {group}\License; Filename: {app}\LICENSE.txt
Name: {group}\README; Filename: {app}\README.txt
Name: {group}\{cm:UninstallProgram,{#MyAppName}}; Filename: {uninstallexe}
Name: {commondesktop}\{#MyAppName}; Filename: {app}\{#MyAppExeName}; Tasks: desktopicon; WorkingDir: {userdocs}
Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}; Filename: {app}\{#MyAppExeName}; Tasks: quicklaunchicon; WorkingDir: {userdocs}

[Run]
Filename: {app}\vcredist_x86.exe; StatusMsg: Installing Microsoft Visual C++ 2010 32 bit Runtime Libraries; Parameters: /q:a; Check: InstallSystemRuntime32()
Filename: {app}\vcredist_x64.exe; StatusMsg: Installing Microsoft Visual C++ 2010 64 bit Runtime Libraries; Parameters: /q:a; Check: InstallSystemRuntime64()

[Dirs]
Name: {app}\bin
Name: {app}\share
Name: {app}\share\copasi
Name: {app}\share\copasi\config
Name: {app}\share\copasi\doc
Name: {app}\share\copasi\doc\html
Name: {app}\share\copasi\doc\html\figures
Name: {app}\share\copasi\examples
Name: {app}\share\copasi\icons

[Registry]
Root: HKCR; SubKey: .cps; ValueType: string; ValueData: COPASI.document; Flags: uninsdeletekey; Check: IsAdminUser
Root: HKCR; SubKey: COPASI.document; ValueType: string; ValueData: COPASI File; Flags: uninsdeletekey; Check: IsAdminUser
Root: HKCR; SubKey: COPASI.document\Shell\Open\Command; ValueType: string; ValueData: """{app}\bin\CopasiUI.exe"" ""%1"""; Flags: uninsdeletevalue; Check: IsAdminUser
Root: HKCR; Subkey: COPASI.document\DefaultIcon; ValueType: string; ValueData: {app}\share\copasi\icons\CopasiDoc.ico,-1; Flags: uninsdeletevalue; Check: IsAdminUser
Root: HKLM; Subkey: SYSTEM\CurrentControlSet\Control\Session Manager\Environment; ValueType: string; ValueName: COPASIDIR; ValueData: {app}; Check: IsAdminUser
Root: HKLM; Subkey: SYSTEM\CurrentControlSet\Control\Session Manager\Environment; ValueType: expandsz; ValueName: Path; ValueData: "%COPASIDIR%\bin;{olddata}"; Check: UpdateSystemPath
Root: HKLM; Subkey: Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers; ValueType: string; ValueName: "{app}\bin\CopasiUI.exe"; ValueData: "~ WIN7RTM"; Flags: uninsdeletevalue; Check: IsAdminUserAndWindows8
Root: HKCU; SubKey: Software\Classes\.cps; ValueType: string; ValueData: COPASI.document; Flags: uninsdeletekey; Check: IsRegularUser
Root: HKCU; SubKey: Software\Classes\COPASI.document; ValueType: string; ValueData: COPASI File; Flags: uninsdeletekey; Check: IsRegularUser
Root: HKCU; SubKey: Software\Classes\COPASI.document\Shell\Open\Command; ValueType: string; ValueData: """{app}\bin\CopasiUI.exe"" ""%1"""; Flags: uninsdeletevalue; Check: IsRegularUser
Root: HKCU; Subkey: Software\Classes\COPASI.document\DefaultIcon; ValueType: string; ValueData: {app}\share\copasi\icons\CopasiDoc.ico,-1; Flags: uninsdeletevalue; Check: IsRegularUser
Root: HKCU; Subkey: Environment; ValueType: string; ValueName: COPASIDIR; ValueData: {app}; Check: IsRegularUser
Root: HKCU; Subkey: Environment; ValueType: expandsz; ValueName: Path; ValueData: "%COPASIDIR%\bin;{olddata}"; Check: UpdateUserPath
Root: HKCU; Subkey: Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers; ValueType: string; ValueName: "{app}\bin\CopasiUI.exe"; ValueData: "~ WIN7RTM"; Flags: uninsdeletevalue; Check: IsRegularUserAndWindows8

[Code]
type
  RunTimeVersion = array [0..3] of Integer;

function IsWin32(): Boolean;
begin
  Result := not IsWin64;
end;

function IsAdminUser(): Boolean;
begin
  Result := (IsAdminLoggedOn or IsPowerUserLoggedOn);
end;

function IsRegularUser(): Boolean;
begin
  Result := not (IsAdminLoggedOn or IsPowerUserLoggedOn);
end;

function isWindows8(): Boolean;
var
  Version: TWindowsVersion;

begin
  GetWindowsVersionEx(Version);

  Result := ((Version.Major = 6) and (Version.Minor = 2));
end;

function IsAdminUserAndWindows8(): Boolean;
begin
  Result := (IsAdminUser() and isWindows8());
end;

function IsRegularUserAndWindows8(): Boolean;
begin
  Result := (IsRegularUser() and isWindows8());
end;

function IsCopasiInSystemPath(): Boolean;
var
  CurrentPath: String;
  Position: Integer;

begin
  Result := False;

  if RegQueryStringValue(HKLM,
    'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
    'Path', CurrentPath) then
    // Successfully read the value
    begin
      Position := Pos('%COPASIDIR%\bin', CurrentPath);
      if (Position <> 0) then
        // Found an entry to the COPASI binaries in the Path
        begin
          Result := True;
        end;
    end;
end;

function IsCopasiInUserPath(): Boolean;
var
  CurrentPath: String;
  Position: Integer;

begin
  Result := IsCopasiInSystemPath();

  if RegQueryStringValue(HKCU,
    'Environment',
    'Path', CurrentPath) then
    // Successfully read the value
    begin
      Position := Pos('%COPASIDIR%\bin', CurrentPath);
      if (Position <> 0) then
        // Found an entry to the COPASI binaries in the Path
        begin
          Result := True;
        end;
    end;
end;

function UpdateSystemPath(): Boolean;
begin
  Result := (IsAdminUser() and not IsCopasiInSystemPath());
end;

function UpdateUserPath(): Boolean;
begin
  Result := (IsRegularUser() and not IsCopasiInUserPath());
end;

function StrToVersion(str: String): RunTimeVersion;
var
  Version: RunTimeVersion;
  Position: Integer;

begin
  try
    begin
      Position := Pos('.', str);
      Version[0] := StrToInt(Copy(str, 1, Position - 1));
      Delete(str, 1, Position);

      Position := Pos('.', str);
      Version[1] := StrToInt(Copy(str, 1, Position - 1));
      Delete(str, 1, Position);

      Position := Pos('.', str);
      Version[2] := StrToInt(Copy(str, 1, Position - 1));
      Delete(str, 1, Position);

      Version[3] := StrToInt(str);
    end;

  except
    begin
      Version[0] := 0;
      Version[1] := 0;
      Version[2] := 0;
      Version[3] := 0;
    end;
  end;

  Result := Version;
end;

function VersionToStr(Version: RunTimeVersion): String;
begin
  Result := IntToStr(Version[0]) + '.' +
            IntToStr(Version[1]) + '.' +
            IntToStr(Version[2]) + '.' +
            IntToStr(Version[3]);
end;

function CompareVersion(Version1: RunTimeVersion;
                        Version2: RunTimeVersion): Integer;
begin
  if Version1[0] <> Version2[0] then
    Result := Version1[0] - Version2[0]
  else if Version1[1] <> Version2[1] then
    Result := Version1[1] - Version2[1]
  else if Version1[2] <> Version2[2] then
    Result := Version1[2] - Version2[2]
  else
    Result := Version1[3] - Version2[3];
end;

function IsInVersionRange(OldVersionRange: String;
                          Version: RunTimeVersion): Boolean;
var
  LowerBound: RunTimeVersion;
  UpperBound: RunTimeVersion;
  Position: Integer;

begin
  Result := False;
  Position := Pos('-', OldVersionRange);

  if Position <> 0 then
    begin
      LowerBound := StrToVersion(Copy(OldVersionRange, 1, Position - 1));
      UpperBound := StrToVersion(Copy(OldVersionRange, Position + 1, 100));
    end
  else
    begin
      LowerBound := StrToVersion(OldVersionRange);
      UpperBound := LowerBound;
    end;

  if (CompareVersion(LowerBound, Version) <= 0) and
     (CompareVersion(Version, UpperBound) <= 0) then
    begin
      Result := True;
    end;
end;

function HaveRuntime32(): Boolean;
begin
  Result := RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}');
end;

function HaveRuntime64(): Boolean;
begin
  Result := RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1D8E6291-B0D5-35EC-8441-6616F567A0F7}');
end;

function InstallSystemRuntime32(): Boolean;
begin
  Result := IsWin32() and IsAdminUser() and not HaveRunTime32();
end;

function InstallSystemRuntime64(): Boolean;
begin
  Result := IsWin64() and IsAdminUser() and not HaveRunTime64();
end;

function InstallUserRuntime32(): Boolean;
begin
  Result := IsWin32() and IsRegularUser() and not HaveRunTime32();
end;

function InstallUserRuntime64(): Boolean;
begin
  Result := IsWin64() and IsRegularUser() and not HaveRunTime64();
end;

function InitializeSetup(): Boolean;
begin
  Result := True;

  RegDeleteValue(HKEY_CURRENT_USER, 'Environment', 'COPASIDIR');
end;

function DefDirRoot(Param: String): String;
begin
  if IsRegularUser() then
    Result := ExpandConstant('{localappdata}')
  else if IsWin64() then
    Result := ExpandConstant('{pf64}')
  else
    Result := ExpandConstant('{pf}');
end;


installers.windows: Set path to plug-ins according to option chosen

#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.

[Code]

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.');

  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 GetPluginsDirpath(value: String) : String;
begin
  Result := PluginsDirpath;
end;

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

  InstallerState := Initialized;
  IsGimpDetected := True;

  if (not RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'DisplayVersion', gimpVersion)
      and not RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'DisplayVersion', gimpVersion)) then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    IsGimpDetected := False;
    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 := GetLocalPluginsDirpath(GimpVersionMajorMinor);

  if (not RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'InstallLocation', GimpDirpath)
      and not RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'InstallLocation', GimpDirpath)) then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    IsGimpDetected := False;
    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;
begin
  Result := True;

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

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

    PluginsDirpathEdit.Text := PluginsDirpath;
  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 (requires administrator privileges)');
  SelectPluginInstallPathPage.Add('Choose custom plug-in 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));
      versionNumberFieldCurrentArrayIndex := versionNumberFieldCurrentArrayIndex + 1;
      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;

#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.

[Code]

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.');

  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;

  InputDirsPage: TInputDirWizardPage;
  PluginsDirpathEdit: TEdit;
  GimpDirpathEdit: TEdit;

  SelectPluginInstallationDirPage: 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 CreateSelectPluginInstallationDirPage(const afterID: Integer); forward;
procedure CreateInputDirsPage(const afterID: Integer); forward;

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

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

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

  InstallerState := Initialized;
  IsGimpDetected := True;

  if (not RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'DisplayVersion', gimpVersion)
      and not RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'DisplayVersion', gimpVersion)) then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    IsGimpDetected := False;
    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 := GetLocalPluginsDirpath(gimpVersionMajorMinor);

  if (not RegQueryStringValue(HKLM64, GIMP_REG_PATH, 'InstallLocation', GimpDirpath)
      and not RegQueryStringValue(HKLM32, GIMP_REG_PATH, 'InstallLocation', GimpDirpath)) then begin
    MsgBox(GIMP_NOT_FOUND_MESSAGE, mbInformation, MB_OK);
    IsGimpDetected := False;
    Exit;
  end;

  CheckPythonScriptingEnabled();
end;

procedure InitializeWizard;
begin
  AddCustomizeToInstallPage();

  CreateSelectPluginInstallationDirPage(wpWelcome);
  CreateInputDirsPage(SelectPluginInstallationDirPage.ID);
end;

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

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

    // `DefaultDirName` may be empty at this point, causing the installer to fail.
    WizardForm.DirEdit.Text := PluginsDirpath;

    CheckPythonScriptingEnabled();
  end
  else if curPageID = SelectPluginInstallationDirPage.ID then begin
    // TODO: Assign GIMP plug-in dirpath; if the third option was chosen, display next page, otherwise don't
  end;
end;

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

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

  Result := isInstallerStarted or isInstallerStartedWithGimpNotDetected;
end;

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

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

  if curPageID = wpReady then begin
    InstallerState := ReadyToInstall;
  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 CreateSelectPluginInstallationDirPage(const afterID: Integer);
begin
  SelectPluginInstallationDirPage := CreateInputOptionPage(
    afterID,
    'Select Installation Path',
    'Where should {#PLUGIN_TITLE} be installed?',
    'Select one of the options below:',
    True,
    False
  );

  SelectPluginInstallationDirPage.Add('Install for just me');
  SelectPluginInstallationDirPage.Add(
    'Install for all users (requires administrator privileges)');
  SelectPluginInstallationDirPage.Add('Choose custom plug-in installation path');

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

procedure CreateInputDirsPage(const afterID: Integer);
var
  lastAddedDirIndex: Integer;
begin
  InputDirsPage := 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 := InputDirsPage.Add('Path to GIMP installation');
  GimpDirpathEdit := InputDirsPage.Edits[lastAddedDirIndex];
  InputDirsPage.Values[0] := GimpDirpath;

  lastAddedDirIndex := InputDirsPage.Add('Path to GIMP plug-ins');
  PluginsDirpathEdit := InputDirsPage.Edits[lastAddedDirIndex];
  InputDirsPage.Values[1] := PluginsDirpath;
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 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));
      versionNumberFieldCurrentArrayIndex := versionNumberFieldCurrentArrayIndex + 1;
      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;


Add icon to Windows installer

Add icon to Windows installer

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

[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={{0467A404-8C83-42A2-8C25-0903FBD14D9C}
AppName=EcoData Retriever
AppVersion=1.6
;AppVerName=EcoData Retriever 1.6
AppPublisher=Weecology
AppPublisherURL=http://ecodataretriever.org
AppSupportURL=http://ecodataretriever.org
AppUpdatesURL=http://ecodataretriever.org
DefaultDirName={pf}\EcoDataRetriever
DefaultGroupName=EcoData Retriever
OutputBaseFilename=RetrieverSetup
SetupIconFile=icon.ico
Compression=lzma
SolidCompression=yes

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

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "dist\retriever.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\EcoData Retriever"; Filename: "{app}\retriever.exe"
Name: "{group}\{cm:UninstallProgram,EcoData Retriever}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\EcoData Retriever"; Filename: "{app}\retriever.exe"; Tasks: desktopicon

[Run]
Filename: "{app}\retriever.exe"; Description: "{cm:LaunchProgram,EcoData Retriever}"; Flags: nowait postinstall skipifsilent

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

[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={{0467A404-8C83-42A2-8C25-0903FBD14D9C}
AppName=EcoData Retriever
AppVersion=1.6
;AppVerName=EcoData Retriever 1.6
AppPublisher=Weecology
AppPublisherURL=http://ecodataretriever.org
AppSupportURL=http://ecodataretriever.org
AppUpdatesURL=http://ecodataretriever.org
DefaultDirName={pf}\EcoDataRetriever
DefaultGroupName=EcoData Retriever
OutputBaseFilename=RetrieverSetup
Compression=lzma
SolidCompression=yes

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

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "dist\retriever.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\EcoData Retriever"; Filename: "{app}\retriever.exe"
Name: "{group}\{cm:UninstallProgram,EcoData Retriever}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\EcoData Retriever"; Filename: "{app}\retriever.exe"; Tasks: desktopicon

[Run]
Filename: "{app}\retriever.exe"; Description: "{cm:LaunchProgram,EcoData Retriever}"; Flags: nowait postinstall skipifsilent