inno branch: cosmetics

inno branch: cosmetics

git-svn-id: f16454573dab46aadd183befef919c0955fb6fd5@632 28bd50df-7adb-d945-0439-6e466c6a13cc

;* Notepad2-mod - Installer script
;*
;* Copyright (C) 2010-2011 XhmikosR
;*
;* This file is part of Notepad2-mod.
;*
;* See License.txt for details.

; Requirements:
; Inno Setup v5.4.2(+): http://www.jrsoftware.org/isdl.php

; $Id$

;#define ICL12
;#define VS2010
;#define WDK

; Preprocessor related stuff
#if VER < 0x05040200
  #error Update your Inno Setup version
#endif

#if !defined(ICL12) && !defined(VS2010) && !defined(WDK)
  #error You need to define ICL12 or VS2010 or WDK first
#endif

#if defined(ICL12) && (defined(VS2010) || defined(WDK))
  #error You can't define ICL12 and at the same time
#endif

#if defined(VS2010) && ( defined(ICL12) || defined(WDK))
  #error You can't define WDK and at the same time
#endif

#if defined(WDK) && (defined(ICL12) || defined(VS2010))
  #error You can't use two defines at the same time
#endif

#if defined(ICL12)
  #define compiler "ICL12"
  #define sse2_required
#elif defined(VS2010)
  #define compiler "VS2010"
#elif defined(WDK)
  #define compiler "WDK"
#endif

#define bindir     "..\bin\" + compiler

#define VerMajor
#define VerMinor
#define VerBuild
#define VerRevision

#expr ParseVersion(bindir + "\Release_x86\Notepad2.exe", VerMajor, VerMinor, VerBuild, VerRevision)
#define app_version  str(VerMajor) + "." + str(VerMinor) + "." + str(VerBuild) + "." + str(VerRevision)
#define app_name     "Notepad2-mod"
#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch"
#define IFEO         "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe"

[Setup]
AppId={#app_name}
AppName={#app_name}
AppVersion={#app_version}
AppVerName={#app_name} {#app_version}
AppPublisher=XhmikosR
AppPublisherURL=http://code.google.com/p/notepad2-mod/
AppSupportURL=http://code.google.com/p/notepad2-mod/
AppUpdatesURL=http://code.google.com/p/notepad2-mod/
AppContact=http://code.google.com/p/notepad2-mod/
AppCopyright=Copyright  2004-2011, Florian Balmer et all
VersionInfoCompany=XhmikosR
VersionInfoCopyright=Copyright  2004-2011, Florian Balmer et all
VersionInfoDescription={#app_name} {#app_version} Setup
VersionInfoTextVersion={#app_version}
VersionInfoVersion={#app_version}
VersionInfoProductName={#app_name}
VersionInfoProductVersion={#app_version}
VersionInfoProductTextVersion={#app_version}
UninstallDisplayIcon={app}\Notepad2.exe
UninstallDisplayName={#app_name} {#app_version} ({#compiler})
DefaultDirName={pf}\Notepad2
LicenseFile=license.txt
OutputDir=.
OutputBaseFilename={#app_name}.{#app_version}_{#compiler}
SetupIconFile=Setup.ico
WizardImageFile=compiler:WizModernImage-IS.bmp
WizardSmallImageFile=WizardSmallImageFile.bmp
SolidCompression=yes
EnableDirDoesntExistWarning=no
AllowNoIcons=yes
ShowTasksTreeLines=yes
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyPage=yes
DisableWelcomePage=yes
AllowCancelDuringInstall=no
#if defined(ICL12) || defined(VS2010) || defined(USE_MSVC2010)
MinVersion=0,5.1.2600sp3
#else
MinVersion=0,5.0
#endif
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64

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

[Messages]
BeveledLabel     ={#app_name} {#app_version}  -  Compiled with {#compiler}
SetupAppTitle    =Setup - {#app_name}
SetupWindowTitle =Setup - {#app_name}

[CustomMessages]
en.msg_AppIsRunning          =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_DeleteSettings        =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them.
en.msg_SetupIsRunningWarning ={#app_name} setup is already running!
#if defined(sse_required)
en.msg_simd_sse              =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities.
#elif defined(sse2_required)
en.msg_simd_sse2             =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities.
#endif
en.tsk_AllUsers              =For all users
en.tsk_CurrentUser           =For the current user only
en.tsk_Other                 =Other tasks:
en.tsk_ResetSettings         =Reset {#app_name}'s settings
en.tsk_RemoveDefault         =Restore Windows notepad
en.tsk_SetDefault            =Replace Windows notepad with {#app_name}

[Tasks]
Name: desktopicon;        Description: {cm:CreateDesktopIcon};     GroupDescription: {cm:AdditionalIcons}; Flags: unchecked
Name: desktopicon\user;   Description: {cm:tsk_CurrentUser};       GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: desktopicon\common; Description: {cm:tsk_AllUsers};          GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: quicklaunchicon;    Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked;             OnlyBelowVersion: 0,6.01
Name: reset_settings;     Description: {cm:tsk_ResetSettings};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: SettingsExistCheck()
Name: set_default;        Description: {cm:tsk_SetDefault};        GroupDescription: {cm:tsk_Other};                                     Check: not DefaulNotepadCheck()
Name: remove_default;     Description: {cm:tsk_RemoveDefault};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: DefaulNotepadCheck()

[Files]
Source: psvince.dll;                        DestDir: {app};                  Flags: ignoreversion
Source: ..\License.txt;                     DestDir: {app};                  Flags: ignoreversion
Source: {#bindir}\Release_x64\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: Is64BitInstallMode()
Source: {#bindir}\Release_x86\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: not Is64BitInstallMode()
Source: Notepad2.ini;                       DestDir: {userappdata}\Notepad2; Flags: onlyifdoesntexist uninsneveruninstall
Source: ..\Notepad2.txt;                    DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme.txt;                      DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme-mod.txt;                  DestDir: {app};                  Flags: ignoreversion

[Icons]
Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {userdesktop}\{#app_name};   Filename: {app}\Notepad2.exe; Tasks: desktopicon\user;   Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: quicklaunchicon;    Comment: {#app_name} {#app_version}; WorkingDir: {app};                           IconFilename: {app}\Notepad2.exe; IconIndex: 0

[INI]
Filename: {app}\Notepad2.ini; Section: Notepad2; Key: Notepad2.ini; String: %APPDATA%\Notepad2\Notepad2.ini

[Run]
Filename: {app}\Notepad2.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked

[InstallDelete]
Type: files;      Name: {userdesktop}\{#app_name}.lnk;   Check: not IsTaskSelected('desktopicon\user')   and IsUpgrade()
Type: files;      Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade()
Type: files;      Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon')    and IsUpgrade(); OnlyBelowVersion: 0,6.01
Type: files;      Name: {app}\Notepad2.ini;              Check: IsUpgrade()

[UninstallDelete]
Type: files;      Name: {app}\Notepad2.ini
Type: dirifempty; Name: {app}

[Code]
// Global variables/constants and general functions
const installer_mutex_name = '{#app_name}' + '_setup_mutex';

function IsModuleLoaded(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@files:psvince.dll stdcall setuponly';

function IsModuleLoadedU(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@{app}\psvince.dll stdcall uninstallonly';

#if defined(sse_required) || defined(sse2_required)
function IsProcessorFeaturePresent(Feature: Integer): Boolean;
external 'IsProcessorFeaturePresent@kernel32.dll stdcall';
#endif

////////////////////////////////////////
//  Custom functions and procedures   //
////////////////////////////////////////

// Check if Notepad2 has replaced Windows Notepad
function DefaulNotepadCheck(): Boolean;
var
  sDebugger: String;
begin
  if RegQueryStringValue(HKLM, '{#IFEO}', 'Debugger', sDebugger) then begin
    if sDebugger = (ExpandConstant('"{app}\Notepad2.exe" /z')) then begin
      Log('Custom Code: {#app_name} is set as the default notepad');
      Result := True;
    end
    else begin
      Log('Custom Code: {#app_name} is NOT set as the default notepad');
      Result := False;
    end;
  end;
end;

#if defined(sse_required)
function Is_SSE_Supported(): Boolean;
begin
  // PF_XMMI_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(6);
end;

#elif defined(sse2_required)

function Is_SSE2_Supported(): Boolean;
begin
  // PF_XMMI64_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(10);
end;

#endif

function IsOldBuildInstalled(): Boolean;
begin
  if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and
  FileExists(ExpandConstant('{pf}\Notepad2\Uninstall.inf')) then begin
    Log('Custom Code: The old build is installed');
    Result := True;
  end
  else begin
    Log('Custom Code: The old build is NOT installed');
    Result := False;
  end;
end;

function IsUpgrade(): Boolean;
var
  sPrevPath: String;
begin
  sPrevPath := WizardForm.PrevAppDir;
  Result := (sPrevPath <> '');
end;

// Check if Notepad2's settings exist
function SettingsExistCheck(): Boolean;
begin
  if FileExists(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini')) then begin
    Log('Custom Code: Settings are present');
    Result := True;
  end
  else begin
    Log('Custom Code: Settings are NOT present');
    Result := False;
  end;
end;

function UninstallOldVersion(): Integer;
var
  iResultCode: Integer;
begin
  // Return Values:
  // 0 - no idea
  // 1 - error executing the command
  // 2 - successfully executed the command

  // default return value
  Log('Custom Code: Will try to uninstall the old build');
  Result := 0;
    if Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx "{pf}\Notepad2\Uninstall.inf",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin
      Result := 2;
      Sleep(500);
      Log('Custom Code: The old build was successfully uninstalled');
    end
    else begin
      Result := 1;
      Log('Custom Code: Something went wrong when uninstalling the old build');
    end;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  // Hide the license page
  if IsUpgrade() and (PageID = wpLicense) then begin
    Result := True;
  end
  else begin
    Result := False;
  end;
end;

procedure AddReg();
begin
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe', 'AppUserModelID', 'Notepad2');
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad2.exe" %1'));
  RegWriteStringValue(HKCR, '*\OpenWithList\notepad2.exe', '', '');
end;

procedure RemoveReg();
begin
  RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad2.exe');
  RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad2.exe');
end;

procedure CleanUpSettings();
begin
  DeleteFile(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini'));
  RemoveDir(ExpandConstant('{userappdata}\Notepad2'));
end;

procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = wpSelectTasks then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall)
  else if CurPageID = wpFinished then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish)
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin

  if CurStep = ssInstall then begin
    if IsOldBuildInstalled() then begin
      UninstallOldVersion();
      // This is the case where the old build is installed; the DefaulNotepadCheck() returns true
      // and the set_default task isn't selected
      if not IsTaskSelected('remove_default') then begin
        RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));
      end;

    end;
  end;

  if CurStep = ssPostInstall then begin
    if IsTaskSelected('reset_settings') then begin
      CleanUpSettings();
    end;

    if IsTaskSelected('set_default') then begin
      RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));
    end;

    if IsTaskSelected('remove_default') then begin
      RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
      RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    end;

    // Always add Notepad2's AppUserModelID and the rest registry values
    AddReg();
  end;

end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  // When uninstalling, ask the user to delete Notepad2's settings and logs
  if CurUninstallStep = usUninstall then begin
    if SettingsExistCheck() then begin
      if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then begin
        CleanUpSettings();
      end;
    end;

    RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
    RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    RemoveReg();

  end;
end;

procedure InitializeWizard();
begin
  WizardForm.SelectTasksLabel.Hide;
  WizardForm.TasksList.Top    := 0;
  WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight;
end;

function InitializeSetup(): Boolean;
var
  iMsgBoxResult: Integer;
begin
  // Create a mutex for the installer and if it's already running then show a message and stop installation
  if CheckForMutexes(installer_mutex_name) and not WizardSilent() then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    while IsModuleLoaded('Notepad2.exe') and (iMsgBoxResult <> IDCANCEL) do begin
      iMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunning'), mbError, MB_OKCANCEL, IDCANCEL);
    end;

    if iMsgBoxResult = IDCANCEL then begin
      Result := False;
    end;

#if defined(sse2_required)
    if not Is_SSE2_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#elif defined(sse_required)
    if not Is_SSE_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#endif

  end;
end;

function InitializeUninstall(): Boolean;
var
  iMsgBoxResult: Integer;
begin
  if CheckForMutexes(installer_mutex_name) then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    // Check if app is running during uninstallation
    while IsModuleLoadedU('Notepad2.exe') and (iMsgBoxResult <> IDCANCEL) do begin
      iMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunningUninstall'), mbError, MB_OKCANCEL, IDCANCEL);
    end;

    if iMsgBoxResult = IDCANCEL then begin
      Result := False;
    end;

    // Unload the psvince.dll in order to be uninstalled
    UnloadDLL(ExpandConstant('{app}\psvince.dll'));
  end;
end;

;* Notepad2-mod - Installer script
;*
;* Copyright (C) 2010-2011 XhmikosR
;*
;* This file is part of Notepad2-mod.
;*
;* See License.txt for details.

; Requirements:
; Inno Setup v5.4.2(+): http://www.jrsoftware.org/isdl.php

; $Id$

;#define ICL12
;#define VS2010
;#define WDK

; Preprocessor related stuff
#if VER < 0x05040200
  #error Update your Inno Setup version
#endif

#if !defined(ICL12) && !defined(VS2010) && !defined(WDK)
  #error You need to define ICL12 or VS2010 or WDK first
#endif

#if defined(ICL12) && (defined(VS2010) || defined(WDK))
  #error You can't define ICL12 and at the same time
#endif

#if defined(VS2010) && ( defined(ICL12) || defined(WDK))
  #error You can't define WDK and at the same time
#endif

#if defined(WDK) && (defined(ICL12) || defined(VS2010))
  #error You can't use two defines at the same time
#endif

#if defined(ICL12)
  #define compiler "ICL12"
  #define sse2_required
#elif defined(VS2010)
  #define compiler "VS2010"
#elif defined(WDK)
  #define compiler "WDK"
#endif

#define bindir     "..\bin\" + compiler

#define VerMajor
#define VerMinor
#define VerBuild
#define VerRevision

#expr ParseVersion(bindir + "\Release_x86\Notepad2.exe", VerMajor, VerMinor, VerBuild, VerRevision)
#define app_version  str(VerMajor) + "." + str(VerMinor) + "." + str(VerBuild) + "." + str(VerRevision)
#define app_name     "Notepad2-mod"
#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch"
#define IFEO         "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe"

[Setup]
AppId={#app_name}
AppName={#app_name}
AppVersion={#app_version}
AppVerName={#app_name} {#app_version}
AppPublisher=XhmikosR
AppPublisherURL=http://code.google.com/p/notepad2-mod/
AppSupportURL=http://code.google.com/p/notepad2-mod/
AppUpdatesURL=http://code.google.com/p/notepad2-mod/
AppContact=http://code.google.com/p/notepad2-mod/
AppCopyright=Copyright  2004-2011, Florian Balmer et all
VersionInfoCompany=XhmikosR
VersionInfoCopyright=Copyright  2004-2011, Florian Balmer et all
VersionInfoDescription={#app_name} {#app_version} Setup
VersionInfoTextVersion={#app_version}
VersionInfoVersion={#app_version}
VersionInfoProductName={#app_name}
VersionInfoProductVersion={#app_version}
VersionInfoProductTextVersion={#app_version}
UninstallDisplayIcon={app}\Notepad2.exe
UninstallDisplayName={#app_name} {#app_version} ({#compiler})
DefaultDirName={pf}\Notepad2
LicenseFile=license.txt
OutputDir=.
OutputBaseFilename={#app_name}.{#app_version}_{#compiler}
SetupIconFile=Setup.ico
WizardImageFile=compiler:WizModernImage-IS.bmp
WizardSmallImageFile=WizardSmallImageFile.bmp
SolidCompression=yes
EnableDirDoesntExistWarning=no
AllowNoIcons=yes
ShowTasksTreeLines=yes
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyPage=yes
DisableWelcomePage=yes
AllowCancelDuringInstall=no
#if defined(ICL12) || defined(VS2010) || defined(USE_MSVC2010)
MinVersion=0,5.1.2600sp3
#else
MinVersion=0,5.0
#endif
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64

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

[Messages]
BeveledLabel     ={#app_name} {#app_version}  -  Compiled with {#compiler}
SetupAppTitle    =Setup - {#app_name}
SetupWindowTitle =Setup - {#app_name}

[CustomMessages]
en.msg_AppIsRunning          =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_DeleteSettings        =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them.
en.msg_SetupIsRunningWarning ={#app_name} setup is already running!
#if defined(sse_required)
en.msg_simd_sse              =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities.
#elif defined(sse2_required)
en.msg_simd_sse2             =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities.
#endif
en.tsk_AllUsers              =For all users
en.tsk_CurrentUser           =For the current user only
en.tsk_Other                 =Other tasks:
en.tsk_ResetSettings         =Reset {#app_name}'s settings
en.tsk_RemoveDefault         =Restore Windows notepad
en.tsk_SetDefault            =Replace Windows notepad with {#app_name}

[Tasks]
Name: desktopicon;        Description: {cm:CreateDesktopIcon};     GroupDescription: {cm:AdditionalIcons}; Flags: unchecked
Name: desktopicon\user;   Description: {cm:tsk_CurrentUser};       GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: desktopicon\common; Description: {cm:tsk_AllUsers};          GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: quicklaunchicon;    Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked;             OnlyBelowVersion: 0,6.01
Name: reset_settings;     Description: {cm:tsk_ResetSettings};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: SettingsExistCheck()
Name: set_default;        Description: {cm:tsk_SetDefault};        GroupDescription: {cm:tsk_Other};                                     Check: not DefaulNotepadCheck()
Name: remove_default;     Description: {cm:tsk_RemoveDefault};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: DefaulNotepadCheck()

[Files]
Source: psvince.dll;                        DestDir: {app};                  Flags: ignoreversion
Source: ..\License.txt;                     DestDir: {app};                  Flags: ignoreversion
Source: {#bindir}\Release_x64\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: Is64BitInstallMode()
Source: {#bindir}\Release_x86\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: not Is64BitInstallMode()
Source: Notepad2.ini;                       DestDir: {userappdata}\Notepad2; Flags: onlyifdoesntexist uninsneveruninstall
Source: ..\Notepad2.txt;                    DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme.txt;                      DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme-mod.txt;                  DestDir: {app};                  Flags: ignoreversion

[Icons]
Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {userdesktop}\{#app_name};   Filename: {app}\Notepad2.exe; Tasks: desktopicon\user;   Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: quicklaunchicon;    Comment: {#app_name} {#app_version}; WorkingDir: {app};                           IconFilename: {app}\Notepad2.exe; IconIndex: 0

[INI]
Filename: {app}\Notepad2.ini; Section: Notepad2; Key: Notepad2.ini; String: %APPDATA%\Notepad2\Notepad2.ini

[Run]
Filename: {app}\Notepad2.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked

[InstallDelete]
Type: files;      Name: {userdesktop}\{#app_name}.lnk;   Check: not IsTaskSelected('desktopicon\user')   and IsUpgrade()
Type: files;      Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade()
Type: files;      Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon')    and IsUpgrade(); OnlyBelowVersion: 0,6.01
Type: files;      Name: {app}\Notepad2.ini;              Check: IsUpgrade()

[UninstallDelete]
Type: files;      Name: {app}\Notepad2.ini
Type: dirifempty; Name: {app}

[Code]
// Global variables/constants and general functions
const installer_mutex_name = '{#app_name}' + '_setup_mutex';

function IsModuleLoaded(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@files:psvince.dll stdcall setuponly';

function IsModuleLoadedU(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@{app}\psvince.dll stdcall uninstallonly';

#if defined(sse_required) || defined(sse2_required)
function IsProcessorFeaturePresent(Feature: Integer): Boolean;
external 'IsProcessorFeaturePresent@kernel32.dll stdcall';
#endif

////////////////////////////////////////
//  Custom functions and procedures   //
////////////////////////////////////////

// Check if Notepad2 has replaced Windows Notepad
function DefaulNotepadCheck(): Boolean;
var
  sDebugger: String;
begin
  if RegQueryStringValue(HKLM, '{#IFEO}', 'Debugger', sDebugger) then begin
    if sDebugger = (ExpandConstant('"{app}\Notepad2.exe" /z')) then begin
      Log('Custom Code: {#app_name} is set as the default notepad');
      Result := True;
    end
    else begin
      Log('Custom Code: {#app_name} is NOT set as the default notepad');
      Result := False;
    end;
  end;
end;

#if defined(sse_required)
function Is_SSE_Supported(): Boolean;
begin
  // PF_XMMI_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(6);
end;

#elif defined(sse2_required)

function Is_SSE2_Supported(): Boolean;
begin
  // PF_XMMI64_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(10);
end;

#endif

function IsOldBuildInstalled(): Boolean;
begin
  if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and
  FileExists(ExpandConstant('{pf}\Notepad2\Uninstall.inf')) then begin
    Log('Custom Code: The old build is installed');
    Result := True;
  end
  else begin
    Log('Custom Code: The old build is NOT installed');
    Result := False;
  end;
end;

function IsUpgrade(): Boolean;
var
  sPrevPath: String;
begin
  sPrevPath := WizardForm.PrevAppDir;
  Result := (sPrevPath <> '');
end;

// Check if Notepad2's settings exist
function SettingsExistCheck(): Boolean;
begin
  if FileExists(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini')) then begin
    Log('Custom Code: Settings are present');
    Result := True;
  end
  else begin
    Log('Custom Code: Settings are NOT present');
    Result := False;
  end;
end;

function UninstallOldVersion(): Integer;
var
  iResultCode: Integer;
begin
  // Return Values:
  // 0 - no idea
  // 1 - error executing the command
  // 2 - successfully executed the command

  // default return value
  Log('Custom Code: Will try to uninstall the old build');
  Result := 0;
    if Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx "{pf}\Notepad2\Uninstall.inf",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin
      Result := 2;
      Sleep(500);
      Log('Custom Code: The old build was successfully uninstalled');
    end
    else begin
      Result := 1;
      Log('Custom Code: Something went wrong when uninstalling the old build');
    end;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  // Hide the license page
  if IsUpgrade() and (PageID = wpLicense) then begin
    Result := True;
  end
  else begin
    Result := False;
  end;
end;

procedure AddReg();
begin
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe', 'AppUserModelID', 'Notepad2');
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad2.exe" %1'));
  RegWriteStringValue(HKCR, '*\OpenWithList\notepad2.exe', '', '');
end;

procedure RemoveReg();
begin
  RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad2.exe');
  RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad2.exe');
end;

procedure CleanUpSettings();
begin
  DeleteFile(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini'));
  RemoveDir(ExpandConstant('{userappdata}\Notepad2'));
end;

procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = wpSelectTasks then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall)
  else if CurPageID = wpFinished then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish)
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin

  if CurStep = ssInstall then begin
    if IsOldBuildInstalled() then begin
      UninstallOldVersion();
      // This is the case where the old build is installed; the DefaulNotepadCheck() returns true
      // and the set_default task isn't selected
      if not IsTaskSelected('remove_default') then begin
        RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));
      end;

    end;
  end;

  if CurStep = ssPostInstall then begin
    if IsTaskSelected('reset_settings') then begin
      CleanUpSettings();
    end;

    if IsTaskSelected('set_default') then begin
      RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));
    end;

    if IsTaskSelected('remove_default') then begin
      RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
      RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    end;

    // Always add Notepad2's AppUserModelID and the rest registry values
    AddReg();
  end;

end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  // When uninstalling, ask the user to delete Notepad2's settings and logs
  if CurUninstallStep = usUninstall then begin
    if SettingsExistCheck() then begin
      if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then begin
        CleanUpSettings();
      end;
    end;

    RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
    RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    RemoveReg();

  end;
end;

procedure InitializeWizard();
begin
  WizardForm.SelectTasksLabel.Hide;
  WizardForm.TasksList.Top    := 0;
  WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight;
end;

function InitializeSetup(): Boolean;
var
  nMsgBoxResult: Integer;
begin
  // Create a mutex for the installer and if it's already running then show a message and stop installation
  if CheckForMutexes(installer_mutex_name) and not WizardSilent() then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    while IsModuleLoaded('Notepad2.exe') and (nMsgBoxResult <> IDCANCEL) do begin
      nMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunning'), mbError, MB_OKCANCEL, IDCANCEL);
    end;

    if nMsgBoxResult = IDCANCEL then begin
      Result := False;
    end;

#if defined(sse2_required)
    if not Is_SSE2_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#elif defined(sse_required)
    if not Is_SSE_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#endif

  end;
end;

function InitializeUninstall(): Boolean;
var
  nMsgBoxResult: Integer;
begin
  if CheckForMutexes(installer_mutex_name) then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    // Check if app is running during uninstallation
    while IsModuleLoadedU('Notepad2.exe') and (nMsgBoxResult <> IDCANCEL) do begin
      nMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunningUninstall'), mbError, MB_OKCANCEL, IDCANCEL);
    end;

    if nMsgBoxResult = IDCANCEL then begin
      Result := False;
    end;

    // Unload the psvince.dll in order to be uninstalled
    UnloadDLL(ExpandConstant('{app}\psvince.dll'));
  end;
end;


Bump version to 1.3.1

Bump version to 1.3.1

#define ShortName "Pictus"
#define LongName "Pictus Image Viewer"

[Setup]
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64
AppName=Pictus
AppMutex=Local\Pictus Viewer
AppPublisherURL=http://www.poppeman.se/
AppSupportURL=http://www.poppeman.se/pictus
AppUpdatesURL=http://www.poppeman.se/pictus
AppVerName=Pictus
DefaultDirName={pf}\Pictus
MinVersion=0.0,5.01
UninstallDisplayIcon={app}\Pictus.exe
VersionInfoDescription=Pictus Setup
OutputBaseFilename=PictInst
OutputDir=../../dist/
DisableProgramGroupPage=yes
VersionInfoVersion=1.3.1.0
DisableDirPage=auto

[Languages]
Name: en; MessagesFile: "compiler:Default.isl"; LicenseFile: "license_en.txt"
Name: sv; MessagesFile: "compiler:Languages\Swedish.isl"; LicenseFile: "license_sv.txt"

[CustomMessages]
en.ExplorerIntegration=Show thumbnails in Explorer
sv.ExplorerIntegration=Visa tummnaglar i Utforskaren

[Components]
Name: "integration"; Description: "{cm:ExplorerIntegration}"; Types: full custom

[Files]
Source: "..\x64\release\Pictus.exe"; DestDir: "{app}"; Flags: replacesameversion; Check: Is64BitInstallMode
Source: "..\release\Pictus.exe"; DestDir: "{app}"; Flags: replacesameversion; Check: not Is64BitInstallMode
Source: "..\x64\release\PictThumbs.dll"; DestDir: "{app}"; Flags: replacesameversion regserver; Check: Is64BitInstallMode; Components: integration
Source: "..\release\PictThumbs.dll"; DestDir: "{app}"; Flags: replacesameversion regserver; Check: not Is64BitInstallMode; Components: integration
Source: "..\..\Readme.txt"; DestDir: "{app}"; Flags: replacesameversion

[Icons]
Name: "{commonprograms}\Pictus"; Filename: "{app}\Pictus.exe"

[Tasks]
Name: "assoc_bmp"; Description: "{cm:AssocFileExtension,{#ShortName},.bmp}"
Name: "assoc_gif"; Description: "{cm:AssocFileExtension,{#ShortName},.gif}"
Name: "assoc_jpg"; Description: "{cm:AssocFileExtension,{#ShortName},.jpg}"
Name: "assoc_pcx"; Description: "{cm:AssocFileExtension,{#ShortName},.pcx}"
Name: "assoc_png"; Description: "{cm:AssocFileExtension,{#ShortName},.png}"
Name: "assoc_psd"; Description: "{cm:AssocFileExtension,{#ShortName},.psd}"; Flags: unchecked
Name: "assoc_psp"; Description: "{cm:AssocFileExtension,{#ShortName},.psp}"; Flags: unchecked
Name: "assoc_tga"; Description: "{cm:AssocFileExtension,{#ShortName},.tga}"
Name: "assoc_tif"; Description: "{cm:AssocFileExtension,{#ShortName},.tiff}"
Name: "assoc_wbmp"; Description: "{cm:AssocFileExtension,{#ShortName},.wbmp}"
Name: "assoc_xyz"; Description: "{cm:AssocFileExtension,{#ShortName},.xyz}"

[Registry]
Root: HKLM; Subkey: "SOFTWARE\Poppeman"; Flags: uninsdeletekeyifempty
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities"; ValueType: string; ValueName: "ApplicationDescription"; ValueData: "Pictus is a fast, lightweight image viewer."
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities"; ValueType: string; ValueName: "ApplicationName"; ValueData: "{#LongName}"
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".bmp"; ValueData: "Pictus.BMP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".gif"; ValueData: "Pictus.GIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jfif"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jpe"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jpeg"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jpg"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".pcx"; ValueData: "Pictus.PCX"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".png"; ValueData: "Pictus.PNG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".psd"; ValueData: "Pictus.PSD"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".psp"; ValueData: "Pictus.PSP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".pspimage"; ValueData: "Pictus.PSP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".tga"; ValueData: "Pictus.TGA"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".tif"; ValueData: "Pictus.TIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".tiff"; ValueData: "Pictus.TIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".wbm"; ValueData: "Pictus.WBMP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".wbmp"; ValueData: "Pictus.WBMP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".xif"; ValueData: "Pictus.TIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".xyz"; ValueData: "Pictus.XYZ"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\RegisteredApplications"; ValueType: string; ValueName: "Pictus Image Viewer"; ValueData: "SOFTWARE\Poppeman\Pictus\Capabilities"; Flags: uninsdeletevalue

Root: HKCR; Subkey: "Pictus.BMP"; ValueType: string; ValueData: "Bitmap image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.BMP\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.GIF"; ValueType: string; ValueData: "GIF image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.GIF\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.JPG"; ValueType: string; ValueData: "Jpeg image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.JPG\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PCX"; ValueType: string; ValueData: "PCX image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PCX\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PNG"; ValueType: string; ValueData: "PNG image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PNG\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSD"; ValueType: string; ValueData: "Photoshop image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSD\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSP"; ValueType: string; ValueData: "Paint Shop Pro image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSP\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TGA"; ValueType: string; ValueData: "Targa TGA image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TGA\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TIF"; ValueType: string; ValueData: "TIFF image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TIF\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.WBMP"; ValueType: string; ValueData: "Wireless bitmap image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.WBMP\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.XYZ"; ValueType: string; ValueData: "XYZ image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.XYZ\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey

Root: HKCR; Subkey: ".bmp"; ValueType: String; ValueData: "Pictus.BMP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_bmp
Root: HKCR; Subkey: ".gif"; ValueType: String; ValueData: "Pictus.GIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_gif
Root: HKCR; Subkey: ".jfif"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".jpe"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".jpeg"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".jpg"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".pcx"; ValueType: String; ValueData: "Pictus.PCX"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_pcx
Root: HKCR; Subkey: ".png"; ValueType: String; ValueData: "Pictus.PNG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_png
Root: HKCR; Subkey: ".psd"; ValueType: String; ValueData: "Pictus.PSD"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_psd
Root: HKCR; Subkey: ".psp"; ValueType: String; ValueData: "Pictus.PSP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_psp
Root: HKCR; Subkey: ".pspimage"; ValueType: String; ValueData: "Pictus.PSP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_psp
Root: HKCR; Subkey: ".tga"; ValueType: String; ValueData: "Pictus.TGA"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tga
Root: HKCR; Subkey: ".tif"; ValueType: String; ValueData: "Pictus.TIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tif
Root: HKCR; Subkey: ".tiff"; ValueType: String; ValueData: "Pictus.TIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tif
Root: HKCR; Subkey: ".wbm"; ValueType: String; ValueData: "Pictus.WBMP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_wbmp
Root: HKCR; Subkey: ".wbmp"; ValueType: String; ValueData: "Pictus.WBMP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_wbmp
Root: HKCR; Subkey: ".xif"; ValueType: String; ValueData: "Pictus.TIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tif
Root: HKCR; Subkey: ".xyz"; ValueType: String; ValueData: "Pictus.XYZ"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_xyz

#define ShortName "Pictus"
#define LongName "Pictus Image Viewer"

[Setup]
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64
AppName=Pictus
AppMutex=Local\Pictus Viewer
AppPublisherURL=http://www.poppeman.se/
AppSupportURL=http://www.poppeman.se/pictus
AppUpdatesURL=http://www.poppeman.se/pictus
AppVerName=Pictus
DefaultDirName={pf}\Pictus
MinVersion=0.0,5.01
UninstallDisplayIcon={app}\Pictus.exe
VersionInfoDescription=Pictus Setup
OutputBaseFilename=PictInst
OutputDir=../../dist/
DisableProgramGroupPage=yes
VersionInfoVersion=1.1.5.0
DisableDirPage=auto

[Languages]
Name: en; MessagesFile: "compiler:Default.isl"; LicenseFile: "license_en.txt"
Name: sv; MessagesFile: "compiler:Languages\Swedish.isl"; LicenseFile: "license_sv.txt"

[CustomMessages]
en.ExplorerIntegration=Show thumbnails in Explorer
sv.ExplorerIntegration=Visa tummnaglar i Utforskaren

[Components]
Name: "integration"; Description: "{cm:ExplorerIntegration}"; Types: full custom

[Files]
Source: "..\x64\release\Pictus.exe"; DestDir: "{app}"; Flags: replacesameversion; Check: Is64BitInstallMode
Source: "..\release\Pictus.exe"; DestDir: "{app}"; Flags: replacesameversion; Check: not Is64BitInstallMode
Source: "..\x64\release\PictThumbs.dll"; DestDir: "{app}"; Flags: replacesameversion regserver; Check: Is64BitInstallMode; Components: integration
Source: "..\release\PictThumbs.dll"; DestDir: "{app}"; Flags: replacesameversion regserver; Check: not Is64BitInstallMode; Components: integration
Source: "..\..\Readme.txt"; DestDir: "{app}"; Flags: replacesameversion

[Icons]
Name: "{commonprograms}\Pictus"; Filename: "{app}\Pictus.exe"

[Tasks]
Name: "assoc_bmp"; Description: "{cm:AssocFileExtension,{#ShortName},.bmp}"
Name: "assoc_gif"; Description: "{cm:AssocFileExtension,{#ShortName},.gif}"
Name: "assoc_jpg"; Description: "{cm:AssocFileExtension,{#ShortName},.jpg}"
Name: "assoc_pcx"; Description: "{cm:AssocFileExtension,{#ShortName},.pcx}"
Name: "assoc_png"; Description: "{cm:AssocFileExtension,{#ShortName},.png}"
Name: "assoc_psd"; Description: "{cm:AssocFileExtension,{#ShortName},.psd}"; Flags: unchecked
Name: "assoc_psp"; Description: "{cm:AssocFileExtension,{#ShortName},.psp}"; Flags: unchecked
Name: "assoc_tga"; Description: "{cm:AssocFileExtension,{#ShortName},.tga}"
Name: "assoc_tif"; Description: "{cm:AssocFileExtension,{#ShortName},.tiff}"
Name: "assoc_wbmp"; Description: "{cm:AssocFileExtension,{#ShortName},.wbmp}"
Name: "assoc_xyz"; Description: "{cm:AssocFileExtension,{#ShortName},.xyz}"

[Registry]
Root: HKLM; Subkey: "SOFTWARE\Poppeman"; Flags: uninsdeletekeyifempty
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities"; ValueType: string; ValueName: "ApplicationDescription"; ValueData: "Pictus is a fast, lightweight image viewer."
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities"; ValueType: string; ValueName: "ApplicationName"; ValueData: "{#LongName}"
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".bmp"; ValueData: "Pictus.BMP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".gif"; ValueData: "Pictus.GIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jfif"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jpe"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jpeg"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".jpg"; ValueData: "Pictus.JPG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".pcx"; ValueData: "Pictus.PCX"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".png"; ValueData: "Pictus.PNG"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".psd"; ValueData: "Pictus.PSD"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".psp"; ValueData: "Pictus.PSP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".pspimage"; ValueData: "Pictus.PSP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".tga"; ValueData: "Pictus.TGA"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".tif"; ValueData: "Pictus.TIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".tiff"; ValueData: "Pictus.TIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".wbm"; ValueData: "Pictus.WBMP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".wbmp"; ValueData: "Pictus.WBMP"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".xif"; ValueData: "Pictus.TIF"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\Poppeman\Pictus\Capabilities\FileAssociations"; ValueType: string; ValueName: ".xyz"; ValueData: "Pictus.XYZ"; Flags: uninsdeletekey
Root: HKLM; Subkey: "SOFTWARE\RegisteredApplications"; ValueType: string; ValueName: "Pictus Image Viewer"; ValueData: "SOFTWARE\Poppeman\Pictus\Capabilities"; Flags: uninsdeletevalue

Root: HKCR; Subkey: "Pictus.BMP"; ValueType: string; ValueData: "Bitmap image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.BMP\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.GIF"; ValueType: string; ValueData: "GIF image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.GIF\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.JPG"; ValueType: string; ValueData: "Jpeg image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.JPG\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PCX"; ValueType: string; ValueData: "PCX image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PCX\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PNG"; ValueType: string; ValueData: "PNG image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PNG\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSD"; ValueType: string; ValueData: "Photoshop image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSD\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSP"; ValueType: string; ValueData: "Paint Shop Pro image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.PSP\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TGA"; ValueType: string; ValueData: "Targa TGA image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TGA\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TIF"; ValueType: string; ValueData: "TIFF image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.TIF\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.WBMP"; ValueType: string; ValueData: "Wireless bitmap image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.WBMP\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.XYZ"; ValueType: string; ValueData: "XYZ image"; Flags: uninsdeletekey
Root: HKCR; Subkey: "Pictus.XYZ\shell\open\command"; ValueType: string; ValueData: """{app}\Pictus.exe"" ""%1"""; Flags: uninsdeletekey

Root: HKCR; Subkey: ".bmp"; ValueType: String; ValueData: "Pictus.BMP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_bmp
Root: HKCR; Subkey: ".gif"; ValueType: String; ValueData: "Pictus.GIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_gif
Root: HKCR; Subkey: ".jfif"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".jpe"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".jpeg"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".jpg"; ValueType: String; ValueData: "Pictus.JPG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_jpg
Root: HKCR; Subkey: ".pcx"; ValueType: String; ValueData: "Pictus.PCX"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_pcx
Root: HKCR; Subkey: ".png"; ValueType: String; ValueData: "Pictus.PNG"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_png
Root: HKCR; Subkey: ".psd"; ValueType: String; ValueData: "Pictus.PSD"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_psd
Root: HKCR; Subkey: ".psp"; ValueType: String; ValueData: "Pictus.PSP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_psp
Root: HKCR; Subkey: ".pspimage"; ValueType: String; ValueData: "Pictus.PSP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_psp
Root: HKCR; Subkey: ".tga"; ValueType: String; ValueData: "Pictus.TGA"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tga
Root: HKCR; Subkey: ".tif"; ValueType: String; ValueData: "Pictus.TIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tif
Root: HKCR; Subkey: ".tiff"; ValueType: String; ValueData: "Pictus.TIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tif
Root: HKCR; Subkey: ".wbm"; ValueType: String; ValueData: "Pictus.WBMP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_wbmp
Root: HKCR; Subkey: ".wbmp"; ValueType: String; ValueData: "Pictus.WBMP"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_wbmp
Root: HKCR; Subkey: ".xif"; ValueType: String; ValueData: "Pictus.TIF"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_tif
Root: HKCR; Subkey: ".xyz"; ValueType: String; ValueData: "Pictus.XYZ"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: assoc_xyz


Remove option to run after install

Remove option to run after install

This allows the systray executable to use updated environment variables
that were changed during installation. A better compromise than forcing
a restart.

#define MyAppName "KA Lite"
#define MyAppPublisher "Foundation for Learning Equality"
#define MyAppURL "http://learningequality.org/"
#define MyAppExeName "KA Lite.exe"

#define getKALiteVersion() \
    Local[1] = Exec(SourcePath+"\getversion.bat") == 0 ? StringChange(FileRead(FileOpen(SourcePath+"\version.temp")), " ", "") : "null"

#define TargetVersion = getKALiteVersion();

#expr DeleteFile(SourcePath+"\version.temp")

[Setup]
AppId={#MyAppName}-{#MyAppPublisher}
AppName={#MyAppName}
AppVersion={#TargetVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
LicenseFile=..\ka-lite\LICENSE
OutputDir=..\
OutputBaseFilename=KALiteSetup-{#TargetVersion}
SetupIconFile=logo48.ico
Compression=lzma
SolidCompression=yes
PrivilegesRequired=admin
UsePreviousAppDir=yes
ChangesEnvironment=yes

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

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

[Files]
Source: "..\ka-lite\dist\ka-lite-static-*.zip"; DestDir: "{app}\ka-lite"
Source: "..\ka-lite\scripts\*.bat"; DestDir: "{app}\ka-lite\scripts\"
Source: "..\gui-packed\KA Lite.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\gui-packed\guitools.vbs"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\gui-packed\images\logo48.ico"; DestDir: "{app}\images"; Flags: ignoreversion
Source: "..\python-setup\*"; DestDir: "{app}\python-setup"; Flags: ignoreversion

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\images\logo48.ico"
Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon ; IconFilename: "{app}\images\logo48.ico"

[Dirs]
Name: "{app}\"; Permissions: everyone-readexec

[InstallDelete]
Type: Files; Name: "{app}\*"

[UninstallDelete]
Type: filesandordirs; Name: "{app}\ka-lite*"
Type: files; Name: "{userstartup}\KA Lite.lnk"
Type: files; Name: "{app}\CONFIG.dat"

[Code]
var
  installFlag : boolean;
  startupFlag : string;
  ServerInformationPage : TInputQueryWizardPage;
  StartupPage : TInputOptionWizardPage;
  isUpgrade : boolean;
  stopServerCode: integer;
  removeOldGuiTool: integer;
  uninstallError: integer;
  saveDatabaseTemp : integer;
  cleanOldKaliteFolder : integer;
  restoreDatabaseTemp : integer;
  forceCancel : boolean;
  prevVerStr : string;

procedure InitializeWizard;
begin
    isUpgrade := False;
    forceCancel := False;

    if WizardForm.PrevAppDir <> nil then
    begin
        ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        ShellExec('open', 'tskill.exe', '"KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C ka-lite\bin\windows\kalite.bat stop', WizardForm.PrevAppDir, SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C del winshortcut.vbs', WizardForm.PrevAppDir, SW_HIDE, ewWaitUntilTerminated, removeOldGuiTool);
    end;

    // Server data
    ServerInformationPage := CreateInputQueryPage(wpSelectDir,
    'Server Information', 'General data',
    'Please specify the server name and a description, then click Next. (you can leave blank both fields if you want to use the default server name or if you do not want to insert a description)');
    ServerInformationPage.Add('Server name:', False);
    ServerInformationPage.Add('Server description:', False);

    // Run at windows startup.
    StartupPage := CreateInputOptionPage(ServerInformationPage.ID,
    'Server Configuration', 'Startup configuration',
    'The server can run automatically. You may choose one of the following options:', True, False);
    StartupPage.Add('Run the server at windows startup');
    StartupPage.Add('Run the server when this user logs in');
    StartupPage.Add('Do not run the server at startup.');
    StartupPage.SelectedValueIndex := 2;
end;

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
  if forceCancel = True then
  begin
    Confirm := False;
  end;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
    result := False;
    if isUpgrade = True then
    begin
        if PageID = ServerInformationPage.ID then
        begin
            result := True;
        end;
        if PageID = wpSelectDir then
        begin
            result := True;
        end;
    end;
end;

procedure RemoveOldInstallation(targetPath : String);
begin
    WizardForm.Hide;
    if Not Exec(ExpandConstant('{cmd}'),'/C ( dir /b "unins***.exe" | findstr /r "unins[0-9][0-9][0-9].exe" ) > tempu & ( for /f "delims=" %A in ( tempu ) do call %A /SILENT /SUPPRESSMSGBOXES ) & del tempu', targetPath, SW_HIDE, ewWaitUntilTerminated, uninstallError) then
    begin
        Exec(ExpandConstant('{cmd}'),'/C mkdir '+ExpandConstant('{tmp}')+'\ka-lite\kalite\database & xcopy /y /s ka-lite\kalite\database\data.sqlite '+ExpandConstant('{tmp}')+'\ka-lite\kalite\database', targetPath, SW_HIDE, ewWaitUntilTerminated, saveDatabaseTemp);
        Exec(ExpandConstant('{cmd}'),'/C cd .. & del /q "'+targetPath+'\*" & for /d %x in ( "'+targetPath+'\*" ) do @rd /s /q "%x"', targetPath, SW_HIDE, ewWaitUntilTerminated, cleanOldKaliteFolder);
        Exec(ExpandConstant('{cmd}'),'/C mkdir ka-lite\kalite\database & xcopy /y /s '+ExpandConstant('{tmp}')+'\ka-lite\kalite\database\data.sqlite ka-lite\kalite\database', targetPath, SW_HIDE, ewWaitUntilTerminated, restoreDatabaseTemp);
    end;
    WizardForm.Show;
end;

{ Get the previous version number by checking the uninstall key registry values. }
{ IS writes quite a bit of information to the registry by default: https://github.com/jrsoftware/issrc/blob/5203240a7de9b83c5432bee0b5b09d467869a02b/Projects/Install.pas#L434 }
procedure GetPreviousVersion;
var
    subkey : String;
begin
    subkey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\KA Lite-Foundation for Learning Equality_is1';
    prevVerStr := ''
    { 32-bit programs have a virtualized registry on 64-bit windows. So check all possible root keys. }
    if Not RegQueryStringValue(HKLM, subkey, 'DisplayVersion', prevVerStr) then
    begin
        if Not RegQueryStringValue(HKCU, subkey, 'DisplayVersion', prevVerStr) then
        begin
            if IsWin64 then
            begin
                if Not RegQueryStringValue(HKLM64, subkey, 'DisplayVersion', prevVerStr) then
                begin
                    if Not RegQueryStringValue(HKCU64, subkey, 'DisplayVersion', prevVerStr) then
                    begin
                        { Couldn't determine the previous version, so prevVerStr is '' }
                    end;
                end;
            end;
        end;
    end;
end;

procedure ConfirmUpgradeDialog;
begin
    if MsgBox('We have detected an existing KA Lite installation; would you like to upgrade?', mbInformation,  MB_YESNO or MB_DEFBUTTON1) = IDYES then
    begin
        isUpgrade := True;
    end
    else if MsgBox('Installing fresh will delete all of your existing data; is this what you really want to do?', mbInformation,  MB_YESNO or MB_DEFBUTTON2) = IDYES then
    begin
        isUpgrade := False;
    end
    else
    begin
        isUpgrade := True;
    end;
end;

{ Copy files a la the gitmigrate management command, but use native windows executables instead. }
{ We assume the installing user is the main user of the app, so content goes into that user's .kalite dir}
procedure DoGitMigrate;
var
    retCodeContent, retCodeDB, retCode : integer;
begin
    MsgBox('Migrating old data to current user''s %USERPROFILE%\.kalite\ directory.', mbInformation, MB_OK);
    Exec(ExpandConstant('{cmd}'), '/S /C "mkdir "%USERPROFILE%\.kalite""', '', SW_SHOW, ewWaitUntilTerminated, retCode);
    Exec(ExpandConstant('{cmd}'), '/S /C "xcopy "' + WizardForm.PrevAppDir + '\ka-lite\content" "%USERPROFILE%\.kalite\content\" /Y"', '', SW_SHOW, ewWaitUntilTerminated, retCodeContent);
    Exec(ExpandConstant('{cmd}'), '/S /C "xcopy "' + WizardForm.PrevAppDir + '\ka-lite\kalite\database" "%USERPROFILE%\.kalite\database\" /Y"', '', SW_SHOW, ewWaitUntilTerminated, retCodeDB);
    if (retCodeContent <> 0) or (retCodeDB <> 0) then
    begin
        MsgBox('Something went wrong! Unable to backup your data. Continuing installation.', mbError, MB_OK);
    end;
end;

procedure HandleUpgrade(targetPath : String);
begin
    GetPreviousVersion;
    if FileExists(targetPath + '\ka-lite\kalite\database\data.sqlite') then
    begin
        ConfirmUpgradeDialog;
        if Not isUpgrade then
        begin
            if Not DeleteFile(targetPath + '\ka-lite\kalite\database\data.sqlite') then
            begin
                MsgBox('Error' #13#13 'Failed to delete the old database as requested; aborting the install.', mbError, MB_OK);
                forceCancel := True;
                WizardForm.Close;
            end;
        end
        else
        begin
            { This is where version-specific migration stuff should happen. }
            if CompareStr(prevVerStr, '0.13.99') < 0 then
            begin
                if CompareStr('{#TargetVersion}', '0.14.0') >= 0 then
                begin
                    DoGitMigrate;
                end;
            end;
        end;
    { forceCancel will be true if something went awry in DoGitMigrate... abort instead of trampling the user's data. }
    if Not forceCancel then
        RemoveOldInstallation(targetPath);
    end;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin
    result := True;

    if CurPageID = wpLicense then
    begin
        if WizardForm.PrevAppDir <> nil then
            HandleUpgrade(WizardForm.PrevAppDir);
    end;

    if CurPageID = wpSelectDir then
    begin
        { Unclear what the logic here is. This is only executed if HandleUpgrade was not previously run. }
        if Not isUpgrade then
            HandleUpgrade(ExpandConstant('{app}'));
    end;
end;

procedure HandlePythonSetup;
var
    installPythonErrorCode : Integer;
begin
    if(MsgBox('Python 2.7.9+ is required to install KA Lite on Windows; do you wish to first install Python 2.7.10, before continuing with the installation of KA Lite?', mbConfirmation, MB_YESNO) = idYes) then
    begin
        ExtractTemporaryFile('python-2.7.10.msi');
        ShellExec('open', ExpandConstant('{tmp}')+'\python-2.7.10.msi', '', '', SW_SHOWNORMAL, ewWaitUntilTerminated, installPythonErrorCode);
    end
    else begin
        MsgBox('Error' #13#13 'You must have Python 2.7.9+ installed to proceed! Installation will now exit.', mbError, MB_OK);
        forceCancel := True;
        WizardForm.Close;
    end;
end;

{ Used in GetPipPath below }
const
    DEFAULT_PATH = '\Python27\Scripts\pip.exe';

{ Returns the path of pip.exe on the system. }
{ Tries several different locations before prompting user. }
function GetPipPath: string;
var
    path : string;
    i : integer;
begin
    for i := Ord('C') to Ord('H') do
    begin
        path := Chr(i) + ':' + DEFAULT_PATH;
        if FileExists(path) then
        begin
            Result := path;
            exit;
        end;
    end;
    MsgBox('Could not find pip.exe. Please select the location of pip.exe to continue installation.', mbInformation, MB_OK);
    if GetOpenFileName('Please select pip.exe', path, '', 'All files (*.*)|*.*', 'exe') then
    begin
        Result := path;
    end
    else begin
        MsgBox('Fatal error'#13#13'Please install pip and try again.', mbError, MB_OK);
        forceCancel := True;
        Result := '';
    end;
end;

procedure HandlePipSetup;
var
    PipCommand: string;
    PipPath: string;
    ErrorCode: integer;

begin
    PipPath := GetPipPath;
    if PipPath = '' then
        exit;
    PipCommand := 'install "' + ExpandConstant('{app}') + '\ka-lite\ka-lite-static-'  + '{#TargetVersion}' + '.zip"';

    MsgBox('Setup will now install kalite source files to your Python site-packages.', mbInformation, MB_OK);
    if not ShellExec('open', PipPath, PipCommand, '', SW_SHOW, ewWaitUntilTerminated, ErrorCode) then
    begin
      MsgBox('Critical error.' #13#13 'Dependencies have failed to install. Error Number: ' + IntToStr(ErrorCode), mbInformation, MB_OK);
      forceCancel := True;
      WizardForm.Close;
    end;

    { Must set this environment variable so the systray executable knows where to find the installed kalite.bat script}
    { Should by in the same directory as pip.exe, e.g. 'C:\Python27\Scripts' }
    RegWriteStringValue(
        HKLM,
        'System\CurrentControlSet\Control\Session Manager\Environment',
        'KALITE_SCRIPT_DIR',
        ExtractFileDir(PipPath)
    );
end;

function InitializeSetup(): Boolean;
var
  PythonVersionCodeCheck: integer;
  killErrorCode: integer;
begin
    installFlag:=true;
    Result := true;
    startupFlag:='';

    ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, killErrorCode)
    ShellExec('open', 'tskill.exe', ' "KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, killErrorCode);

    RegDeleteValue(HKCU, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run', ExpandConstant('{#MyAppName}'));

    if ShellExec('open', 'python.exe','-c "import sys; (sys.version_info >= (2, 7, 9,) and sys.version_info < (3,) and sys.exit(0)) or sys.exit(1)"', '', SW_HIDE, ewWaitUntilTerminated, PythonVersionCodeCheck) then
    begin
        if PythonVersionCodeCheck = 1 then
        begin
            HandlePythonSetup();
        end;
    end
    else
    begin
        HandlePythonSetup();
    end;
end;

function InitializeUninstall(): Boolean;
var
ErrorCode: Integer;
begin
  ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
  ShellExec('open', 'tskill.exe', '"KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
  ShellExec('open', ExpandConstant('{app}') + '\ka-lite\bin\windows\kalite.bat stop', '', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
  result := True;
end;

{ Completes the setup with bundled empty database file. }
procedure DoSetup;
var
    retCode: integer;
begin
    { Copy the bundled empty db to the proper location. }
    { Used to have more responsibility, but we delegated those to the app itself! }
    Exec(ExpandConstant('{cmd}'), '/S /C "xcopy "' + ExpandConstant('{app}') + '\ka-lite\kalite\database" "%USERPROFILE%\.kalite\database\" /E /Y"', '', SW_HIDE, ewWaitUntilTerminated, retCode);
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  StartupCode: integer;
  moveKaliteFolderTemp: integer;
  moveContentFolderTemp: integer;
  cleanKaliteFolder: integer;
  restoreKaliteFolder: integer;
  restoreContentFolder: integer;
  informationBoxFlagged: boolean;

begin
    if CurStep = ssInstall then
    begin
        informationBoxFlagged :=False;

        ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        ShellExec('open', 'tskill.exe', '"KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C ka-lite\bin\windows\kalite.bat stop', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C del winshortcut.vbs', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, removeOldGuiTool);

        if DirExists(ExpandConstant('{app}') + '\kalite') then
        begin
            MsgBox('KA Lite old data structure' #13#13 'Setup detected that you have the old file structure. Setup will now move data to update the structure. Please be patient; this may take some time.', mbInformation, MB_OK);
            informationBoxFlagged :=True;
            Exec(ExpandConstant('{cmd}'),'/C mkdir '+ExpandConstant('{tmp}')+'\ka-lite\kalite & xcopy /y /s kalite\* '+ExpandConstant('{tmp}')+'\ka-lite\kalite', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, moveKaliteFolderTemp);
        end;

        if DirExists(ExpandConstant('{app}') + '\content') then
        begin
            if Not informationBoxFlagged then
            begin
                MsgBox('KA Lite old data structure' #13#13 'Setup detected that you have the old file structure. Setup will now move data to update the structure. Please be patient; this may take some time.', mbInformation, MB_OK);
                informationBoxFlagged :=True;
            end;
            Exec(ExpandConstant('{cmd}'),'/C mkdir '+ExpandConstant('{tmp}')+'\ka-lite\content & xcopy /y /s content\* '+ExpandConstant('{tmp}')+'\ka-lite\content', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, moveContentFolderTemp);
        end;

        if informationBoxFlagged then
        begin
            Exec(ExpandConstant('{cmd}'),'/C cd .. & del /q "'+ExpandConstant('{app}')+'\*" & for /d %x in ( "'+ExpandConstant('{app}')+'\*" ) do @rd /s /q "%x"', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, cleanKaliteFolder);

            if DirExists(ExpandConstant('{tmp}')+'\ka-lite\kalite') then
            begin
                Exec(ExpandConstant('{cmd}'),'/C mkdir ka-lite\kalite & xcopy /y /s '+ExpandConstant('{tmp}')+'\ka-lite\kalite\* ka-lite\kalite', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, restoreKaliteFolder);
            end;

            if DirExists(ExpandConstant('{tmp}')+'\ka-lite\content') then
            begin
                Exec(ExpandConstant('{cmd}'),'/C mkdir ka-lite\content & xcopy /y /s '+ExpandConstant('{tmp}')+'\ka-lite\content\* ka-lite\content', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, restoreContentFolder);
            end;
        end;

    end;

    if CurStep = ssPostInstall then
    begin
        if installFlag then
        begin
            HandlePipSetup();

            if Not forceCancel then
            begin
                DoSetup;
            end;

            if StartupPage.SelectedValueIndex = 0 then
            begin
                if ShellExec('open','guitools.vbs','4', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, StartupCode) then
                begin
                    if Not SaveStringToFile(ExpandConstant('{app}')+'\CONFIG.dat', 'RUN_AT_STARTUP:TRUE;' + #13#10, False) then
                    begin
                        MsgBox('Configuration file error.' #13#13 'Setup has failed to add the entry in the configuration file to run KA Lite at Windows startup. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                    end;
                end
                else begin
                    MsgBox('GUI tools error.' #13#13 'Setup has failed to register a task to run KA Lite at Windows startup. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                end;
            end
            else if StartupPage.SelectedValueIndex = 1 then
            begin
                if ShellExec('open', 'guitools.vbs', '0', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, StartupCode) then
                begin
                    if Not SaveStringToFile(ExpandConstant('{app}')+'\CONFIG.dat', 'RUN_AT_USER_LOGIN:TRUE;' + #13#10, False) then
                    begin
                        MsgBox('Configuration file error.' #13#13 'Setup has failed to add the entry in the configuration file to run KA Lite on user login. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                    end;
                end
                else begin
                    MsgBox('GUI tools error.' #13#13 'Setup has failed to add the shortcut at the startup folder to run KA Lite on user login. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                end;
            end;
        end;
    end;
end;

{ Called just prior to uninstall finishing. }
{ Clean up things we did during uninstall: }
{ * Remove environment variable KALITE_SCRIPT_DIR, which is set starting in version 0.16.x }
{ * Previously (versions 0.13.x to 0.15.x) KALITE_ROOT_DATA_PATH was set -- it should be unset by the respective }
{   uninstallers of those versions }
procedure DeinitializeUninstall();
begin
    RegDeleteValue(
        HKLM,
        'System\CurrentControlSet\Control\Session Manager\Environment',
        'KALITE_SCRIPT_DIR'
    )
end;

#define MyAppName "KA Lite"
#define MyAppPublisher "Foundation for Learning Equality"
#define MyAppURL "http://learningequality.org/"
#define MyAppExeName "KA Lite.exe"

#define getKALiteVersion() \
    Local[1] = Exec(SourcePath+"\getversion.bat") == 0 ? StringChange(FileRead(FileOpen(SourcePath+"\version.temp")), " ", "") : "null"

#define TargetVersion = getKALiteVersion();

#expr DeleteFile(SourcePath+"\version.temp")

[Setup]
AppId={#MyAppName}-{#MyAppPublisher}
AppName={#MyAppName}
AppVersion={#TargetVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
LicenseFile=..\ka-lite\LICENSE
OutputDir=..\
OutputBaseFilename=KALiteSetup-{#TargetVersion}
SetupIconFile=logo48.ico
Compression=lzma
SolidCompression=yes
PrivilegesRequired=admin
UsePreviousAppDir=yes
ChangesEnvironment=yes

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

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

[Files]
Source: "..\ka-lite\dist\ka-lite-static-*.zip"; DestDir: "{app}\ka-lite"
Source: "..\ka-lite\scripts\*.bat"; DestDir: "{app}\ka-lite\scripts\"
Source: "..\gui-packed\KA Lite.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\gui-packed\guitools.vbs"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\gui-packed\images\logo48.ico"; DestDir: "{app}\images"; Flags: ignoreversion
Source: "..\python-setup\*"; DestDir: "{app}\python-setup"; Flags: ignoreversion

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\images\logo48.ico"
Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon ; IconFilename: "{app}\images\logo48.ico"

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: shellexec postinstall skipifsilent

[Dirs]
Name: "{app}\"; Permissions: everyone-readexec

[InstallDelete]
Type: Files; Name: "{app}\*"

[UninstallDelete]
Type: filesandordirs; Name: "{app}\ka-lite*"
Type: files; Name: "{userstartup}\KA Lite.lnk"
Type: files; Name: "{app}\CONFIG.dat"

[Code]
var
  installFlag : boolean;
  startupFlag : string;
  ServerInformationPage : TInputQueryWizardPage;
  StartupPage : TInputOptionWizardPage;
  isUpgrade : boolean;
  stopServerCode: integer;
  removeOldGuiTool: integer;
  uninstallError: integer;
  saveDatabaseTemp : integer;
  cleanOldKaliteFolder : integer;
  restoreDatabaseTemp : integer;
  forceCancel : boolean;
  prevVerStr : string;

procedure InitializeWizard;
begin
    isUpgrade := False;
    forceCancel := False;

    if WizardForm.PrevAppDir <> nil then
    begin
        ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        ShellExec('open', 'tskill.exe', '"KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C ka-lite\bin\windows\kalite.bat stop', WizardForm.PrevAppDir, SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C del winshortcut.vbs', WizardForm.PrevAppDir, SW_HIDE, ewWaitUntilTerminated, removeOldGuiTool);
    end;

    // Server data
    ServerInformationPage := CreateInputQueryPage(wpSelectDir,
    'Server Information', 'General data',
    'Please specify the server name and a description, then click Next. (you can leave blank both fields if you want to use the default server name or if you do not want to insert a description)');
    ServerInformationPage.Add('Server name:', False);
    ServerInformationPage.Add('Server description:', False);

    // Run at windows startup.
    StartupPage := CreateInputOptionPage(ServerInformationPage.ID,
    'Server Configuration', 'Startup configuration',
    'The server can run automatically. You may choose one of the following options:', True, False);
    StartupPage.Add('Run the server at windows startup');
    StartupPage.Add('Run the server when this user logs in');
    StartupPage.Add('Do not run the server at startup.');
    StartupPage.SelectedValueIndex := 2;
end;

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
  if forceCancel = True then
  begin
    Confirm := False;
  end;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
    result := False;
    if isUpgrade = True then
    begin
        if PageID = ServerInformationPage.ID then
        begin
            result := True;
        end;
        if PageID = wpSelectDir then
        begin
            result := True;
        end;
    end;
end;

procedure RemoveOldInstallation(targetPath : String);
begin
    WizardForm.Hide;
    if Not Exec(ExpandConstant('{cmd}'),'/C ( dir /b "unins***.exe" | findstr /r "unins[0-9][0-9][0-9].exe" ) > tempu & ( for /f "delims=" %A in ( tempu ) do call %A /SILENT /SUPPRESSMSGBOXES ) & del tempu', targetPath, SW_HIDE, ewWaitUntilTerminated, uninstallError) then
    begin
        Exec(ExpandConstant('{cmd}'),'/C mkdir '+ExpandConstant('{tmp}')+'\ka-lite\kalite\database & xcopy /y /s ka-lite\kalite\database\data.sqlite '+ExpandConstant('{tmp}')+'\ka-lite\kalite\database', targetPath, SW_HIDE, ewWaitUntilTerminated, saveDatabaseTemp);
        Exec(ExpandConstant('{cmd}'),'/C cd .. & del /q "'+targetPath+'\*" & for /d %x in ( "'+targetPath+'\*" ) do @rd /s /q "%x"', targetPath, SW_HIDE, ewWaitUntilTerminated, cleanOldKaliteFolder);
        Exec(ExpandConstant('{cmd}'),'/C mkdir ka-lite\kalite\database & xcopy /y /s '+ExpandConstant('{tmp}')+'\ka-lite\kalite\database\data.sqlite ka-lite\kalite\database', targetPath, SW_HIDE, ewWaitUntilTerminated, restoreDatabaseTemp);
    end;
    WizardForm.Show;
end;

{ Get the previous version number by checking the uninstall key registry values. }
{ IS writes quite a bit of information to the registry by default: https://github.com/jrsoftware/issrc/blob/5203240a7de9b83c5432bee0b5b09d467869a02b/Projects/Install.pas#L434 }
procedure GetPreviousVersion;
var
    subkey : String;
begin
    subkey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\KA Lite-Foundation for Learning Equality_is1';
    prevVerStr := ''
    { 32-bit programs have a virtualized registry on 64-bit windows. So check all possible root keys. }
    if Not RegQueryStringValue(HKLM, subkey, 'DisplayVersion', prevVerStr) then
    begin
        if Not RegQueryStringValue(HKCU, subkey, 'DisplayVersion', prevVerStr) then
        begin
            if IsWin64 then
            begin
                if Not RegQueryStringValue(HKLM64, subkey, 'DisplayVersion', prevVerStr) then
                begin
                    if Not RegQueryStringValue(HKCU64, subkey, 'DisplayVersion', prevVerStr) then
                    begin
                        { Couldn't determine the previous version, so prevVerStr is '' }
                    end;
                end;
            end;
        end;
    end;
end;

procedure ConfirmUpgradeDialog;
begin
    if MsgBox('We have detected an existing KA Lite installation; would you like to upgrade?', mbInformation,  MB_YESNO or MB_DEFBUTTON1) = IDYES then
    begin
        isUpgrade := True;
    end
    else if MsgBox('Installing fresh will delete all of your existing data; is this what you really want to do?', mbInformation,  MB_YESNO or MB_DEFBUTTON2) = IDYES then
    begin
        isUpgrade := False;
    end
    else
    begin
        isUpgrade := True;
    end;
end;

{ Copy files a la the gitmigrate management command, but use native windows executables instead. }
{ We assume the installing user is the main user of the app, so content goes into that user's .kalite dir}
procedure DoGitMigrate;
var
    retCodeContent, retCodeDB, retCode : integer;
begin
    MsgBox('Migrating old data to current user''s %USERPROFILE%\.kalite\ directory.', mbInformation, MB_OK);
    Exec(ExpandConstant('{cmd}'), '/S /C "mkdir "%USERPROFILE%\.kalite""', '', SW_SHOW, ewWaitUntilTerminated, retCode);
    Exec(ExpandConstant('{cmd}'), '/S /C "xcopy "' + WizardForm.PrevAppDir + '\ka-lite\content" "%USERPROFILE%\.kalite\content\" /Y"', '', SW_SHOW, ewWaitUntilTerminated, retCodeContent);
    Exec(ExpandConstant('{cmd}'), '/S /C "xcopy "' + WizardForm.PrevAppDir + '\ka-lite\kalite\database" "%USERPROFILE%\.kalite\database\" /Y"', '', SW_SHOW, ewWaitUntilTerminated, retCodeDB);
    if (retCodeContent <> 0) or (retCodeDB <> 0) then
    begin
        MsgBox('Something went wrong! Unable to backup your data. Continuing installation.', mbError, MB_OK);
    end;
end;

procedure HandleUpgrade(targetPath : String);
begin
    GetPreviousVersion;
    if FileExists(targetPath + '\ka-lite\kalite\database\data.sqlite') then
    begin
        ConfirmUpgradeDialog;
        if Not isUpgrade then
        begin
            if Not DeleteFile(targetPath + '\ka-lite\kalite\database\data.sqlite') then
            begin
                MsgBox('Error' #13#13 'Failed to delete the old database as requested; aborting the install.', mbError, MB_OK);
                forceCancel := True;
                WizardForm.Close;
            end;
        end
        else
        begin
            { This is where version-specific migration stuff should happen. }
            if CompareStr(prevVerStr, '0.13.99') < 0 then
            begin
                if CompareStr('{#TargetVersion}', '0.14.0') >= 0 then
                begin
                    DoGitMigrate;
                end;
            end;
        end;
    { forceCancel will be true if something went awry in DoGitMigrate... abort instead of trampling the user's data. }
    if Not forceCancel then
        RemoveOldInstallation(targetPath);
    end;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin
    result := True;

    if CurPageID = wpLicense then
    begin
        if WizardForm.PrevAppDir <> nil then
            HandleUpgrade(WizardForm.PrevAppDir);
    end;

    if CurPageID = wpSelectDir then
    begin
        { Unclear what the logic here is. This is only executed if HandleUpgrade was not previously run. }
        if Not isUpgrade then
            HandleUpgrade(ExpandConstant('{app}'));
    end;
end;

procedure HandlePythonSetup;
var
    installPythonErrorCode : Integer;
begin
    if(MsgBox('Python 2.7.9+ is required to install KA Lite on Windows; do you wish to first install Python 2.7.10, before continuing with the installation of KA Lite?', mbConfirmation, MB_YESNO) = idYes) then
    begin
        ExtractTemporaryFile('python-2.7.10.msi');
        ShellExec('open', ExpandConstant('{tmp}')+'\python-2.7.10.msi', '', '', SW_SHOWNORMAL, ewWaitUntilTerminated, installPythonErrorCode);
    end
    else begin
        MsgBox('Error' #13#13 'You must have Python 2.7.9+ installed to proceed! Installation will now exit.', mbError, MB_OK);
        forceCancel := True;
        WizardForm.Close;
    end;
end;

{ Used in GetPipPath below }
const
    DEFAULT_PATH = '\Python27\Scripts\pip.exe';

{ Returns the path of pip.exe on the system. }
{ Tries several different locations before prompting user. }
function GetPipPath: string;
var
    path : string;
    i : integer;
begin
    for i := Ord('C') to Ord('H') do
    begin
        path := Chr(i) + ':' + DEFAULT_PATH;
        if FileExists(path) then
        begin
            Result := path;
            exit;
        end;
    end;
    MsgBox('Could not find pip.exe. Please select the location of pip.exe to continue installation.', mbInformation, MB_OK);
    if GetOpenFileName('Please select pip.exe', path, '', 'All files (*.*)|*.*', 'exe') then
    begin
        Result := path;
    end
    else begin
        MsgBox('Fatal error'#13#13'Please install pip and try again.', mbError, MB_OK);
        forceCancel := True;
        Result := '';
    end;
end;

procedure HandlePipSetup;
var
    PipCommand: string;
    PipPath: string;
    ErrorCode: integer;

begin
    PipPath := GetPipPath;
    if PipPath = '' then
        exit;
    PipCommand := 'install "' + ExpandConstant('{app}') + '\ka-lite\ka-lite-static-'  + '{#TargetVersion}' + '.zip"';

    MsgBox('Setup will now install kalite source files to your Python site-packages.', mbInformation, MB_OK);
    if not ShellExec('open', PipPath, PipCommand, '', SW_SHOW, ewWaitUntilTerminated, ErrorCode) then
    begin
      MsgBox('Critical error.' #13#13 'Dependencies have failed to install. Error Number: ' + IntToStr(ErrorCode), mbInformation, MB_OK);
      forceCancel := True;
      WizardForm.Close;
    end;

    { Must set this environment variable so the systray executable knows where to find the installed kalite.bat script}
    { Should by in the same directory as pip.exe, e.g. 'C:\Python27\Scripts' }
    RegWriteStringValue(
        HKLM,
        'System\CurrentControlSet\Control\Session Manager\Environment',
        'KALITE_SCRIPT_DIR',
        ExtractFileDir(PipPath)
    );
end;

function InitializeSetup(): Boolean;
var
  PythonVersionCodeCheck: integer;
  killErrorCode: integer;
begin
    installFlag:=true;
    Result := true;
    startupFlag:='';

    ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, killErrorCode)
    ShellExec('open', 'tskill.exe', ' "KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, killErrorCode);

    RegDeleteValue(HKCU, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run', ExpandConstant('{#MyAppName}'));

    if ShellExec('open', 'python.exe','-c "import sys; (sys.version_info >= (2, 7, 9,) and sys.version_info < (3,) and sys.exit(0)) or sys.exit(1)"', '', SW_HIDE, ewWaitUntilTerminated, PythonVersionCodeCheck) then
    begin
        if PythonVersionCodeCheck = 1 then
        begin
            HandlePythonSetup();
        end;
    end
    else
    begin
        HandlePythonSetup();
    end;
end;

function InitializeUninstall(): Boolean;
var
ErrorCode: Integer;
begin
  ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
  ShellExec('open', 'tskill.exe', '"KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
  ShellExec('open', ExpandConstant('{app}') + '\ka-lite\bin\windows\kalite.bat stop', '', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
  result := True;
end;

{ Completes the setup with bundled empty database file. }
procedure DoSetup;
var
    retCode: integer;
begin
    { Copy the bundled empty db to the proper location. }
    { Used to have more responsibility, but we delegated those to the app itself! }
    Exec(ExpandConstant('{cmd}'), '/S /C "xcopy "' + ExpandConstant('{app}') + '\ka-lite\kalite\database" "%USERPROFILE%\.kalite\database\" /E /Y"', '', SW_HIDE, ewWaitUntilTerminated, retCode);
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  StartupCode: integer;
  moveKaliteFolderTemp: integer;
  moveContentFolderTemp: integer;
  cleanKaliteFolder: integer;
  restoreKaliteFolder: integer;
  restoreContentFolder: integer;
  informationBoxFlagged: boolean;

begin
    if CurStep = ssInstall then
    begin
        informationBoxFlagged :=False;

        ShellExec('open', 'taskkill.exe', '/F /T /im "KA Lite.exe"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        ShellExec('open', 'tskill.exe', '"KA Lite"', '', SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C ka-lite\bin\windows\kalite.bat stop', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, stopServerCode);
        Exec(ExpandConstant('{cmd}'),'/C del winshortcut.vbs', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, removeOldGuiTool);

        if DirExists(ExpandConstant('{app}') + '\kalite') then
        begin
            MsgBox('KA Lite old data structure' #13#13 'Setup detected that you have the old file structure. Setup will now move data to update the structure. Please be patient; this may take some time.', mbInformation, MB_OK);
            informationBoxFlagged :=True;
            Exec(ExpandConstant('{cmd}'),'/C mkdir '+ExpandConstant('{tmp}')+'\ka-lite\kalite & xcopy /y /s kalite\* '+ExpandConstant('{tmp}')+'\ka-lite\kalite', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, moveKaliteFolderTemp);
        end;

        if DirExists(ExpandConstant('{app}') + '\content') then
        begin
            if Not informationBoxFlagged then
            begin
                MsgBox('KA Lite old data structure' #13#13 'Setup detected that you have the old file structure. Setup will now move data to update the structure. Please be patient; this may take some time.', mbInformation, MB_OK);
                informationBoxFlagged :=True;
            end;
            Exec(ExpandConstant('{cmd}'),'/C mkdir '+ExpandConstant('{tmp}')+'\ka-lite\content & xcopy /y /s content\* '+ExpandConstant('{tmp}')+'\ka-lite\content', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, moveContentFolderTemp);
        end;

        if informationBoxFlagged then
        begin
            Exec(ExpandConstant('{cmd}'),'/C cd .. & del /q "'+ExpandConstant('{app}')+'\*" & for /d %x in ( "'+ExpandConstant('{app}')+'\*" ) do @rd /s /q "%x"', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, cleanKaliteFolder);

            if DirExists(ExpandConstant('{tmp}')+'\ka-lite\kalite') then
            begin
                Exec(ExpandConstant('{cmd}'),'/C mkdir ka-lite\kalite & xcopy /y /s '+ExpandConstant('{tmp}')+'\ka-lite\kalite\* ka-lite\kalite', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, restoreKaliteFolder);
            end;

            if DirExists(ExpandConstant('{tmp}')+'\ka-lite\content') then
            begin
                Exec(ExpandConstant('{cmd}'),'/C mkdir ka-lite\content & xcopy /y /s '+ExpandConstant('{tmp}')+'\ka-lite\content\* ka-lite\content', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, restoreContentFolder);
            end;
        end;

    end;

    if CurStep = ssPostInstall then
    begin
        if installFlag then
        begin
            HandlePipSetup();

            if Not forceCancel then
            begin
                DoSetup;
            end;

            if StartupPage.SelectedValueIndex = 0 then
            begin
                if ShellExec('open','guitools.vbs','4', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, StartupCode) then
                begin
                    if Not SaveStringToFile(ExpandConstant('{app}')+'\CONFIG.dat', 'RUN_AT_STARTUP:TRUE;' + #13#10, False) then
                    begin
                        MsgBox('Configuration file error.' #13#13 'Setup has failed to add the entry in the configuration file to run KA Lite at Windows startup. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                    end;
                end
                else begin
                    MsgBox('GUI tools error.' #13#13 'Setup has failed to register a task to run KA Lite at Windows startup. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                end;
            end
            else if StartupPage.SelectedValueIndex = 1 then
            begin
                if ShellExec('open', 'guitools.vbs', '0', ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, StartupCode) then
                begin
                    if Not SaveStringToFile(ExpandConstant('{app}')+'\CONFIG.dat', 'RUN_AT_USER_LOGIN:TRUE;' + #13#10, False) then
                    begin
                        MsgBox('Configuration file error.' #13#13 'Setup has failed to add the entry in the configuration file to run KA Lite on user login. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                    end;
                end
                else begin
                    MsgBox('GUI tools error.' #13#13 'Setup has failed to add the shortcut at the startup folder to run KA Lite on user login. The installation may proceed and you can set this option later while using KA Lite.', mbError, MB_OK);
                end;
            end;
        end;
    end;
end;

{ Called just prior to uninstall finishing. }
{ Clean up things we did during uninstall: }
{ * Remove environment variable KALITE_SCRIPT_DIR, which is set starting in version 0.16.x }
{ * Previously (versions 0.13.x to 0.15.x) KALITE_ROOT_DATA_PATH was set -- it should be unset by the respective }
{   uninstallers of those versions }
procedure DeinitializeUninstall();
begin
    RegDeleteValue(
        HKLM,
        'System\CurrentControlSet\Control\Session Manager\Environment',
        'KALITE_SCRIPT_DIR'
    )
end;