winscpsetup.iss 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491
  1. #define AppId "winscp3"
  2. #define AppMutex "WinSCP"
  3. #define ParentRegistryKey "Software\Martin Prikryl"
  4. #define RegistryKey ParentRegistryKey+"\WinSCP 2"
  5. #define DefaultLang "en"
  6. #define WebRoot "http://winscp.net/"
  7. #define WebForum WebRoot+"forum/"
  8. #define WebDocumentation WebRoot+"eng/docs/"
  9. #define WebReport WebRoot+"install.php"
  10. #define WebPuTTY "http://www.chiark.greenend.org.uk/~sgtatham/putty/"
  11. #define Year 2016
  12. #define EnglishLang "English"
  13. #define SetupTypeData "SetupType"
  14. #define InnoSetupReg "Software\Microsoft\Windows\CurrentVersion\Uninstall\" + AppId + "_is1"
  15. #define InnoSetupAppPathReg "Inno Setup: App Path"
  16. #ifndef CompletenessThreshold
  17. #define CompletenessThreshold 100
  18. #else
  19. #define CompletenessThreshold Int(CompletenessThreshold)
  20. #endif
  21. #ifndef PuttySourceDir
  22. #define PuttySourceDir "c:\Program Files\PuTTY"
  23. #endif
  24. #ifndef Status
  25. #define Status "unofficial"
  26. #endif
  27. #ifndef SourceDir
  28. #define SourceDir "..\source"
  29. #endif
  30. #ifndef BinariesDir
  31. #define BinariesDir SourceDir + "\Win32\Release"
  32. #endif
  33. #ifndef BinariesDir64
  34. #define BinariesDir64 SourceDir + "\Win64\Release"
  35. #endif
  36. #ifndef AllTranslations
  37. #define AllTranslations
  38. #endif
  39. #define TranslationDir "translations"
  40. #define ImagesDir "images"
  41. #define OutputDir "."
  42. #define TranslationFileMask "WinSCP.???"
  43. #define MainFileName "WinSCP.exe"
  44. #define MainFileSource BinariesDir+"\"+MainFileName
  45. #define ShellExtFileName "DragExt.dll"
  46. #define ShellExtFileSource BinariesDir+"\"+ShellExtFileName
  47. #define ShellExt64FileName "DragExt64.dll"
  48. #define ShellExt64FileSource BinariesDir64+"\"+ShellExt64FileName
  49. #define ConsoleFileSource BinariesDir+"\WinSCP.com"
  50. #define MapFileSource BinariesDir+"\WinSCP.map"
  51. #ifdef Donations
  52. #define PayPalCardImage "PayPalCard.bmp"
  53. #endif
  54. #define Major
  55. #define Minor
  56. #define Rev
  57. #define Build
  58. #expr ParseVersion(MainFileSource, Major, Minor, Rev, Build)
  59. #define VersionOnly Str(Major)+"."+Str(Minor)+(Rev > 0 ? "."+Str(Rev) : "")
  60. #define Version VersionOnly+(Status != "" ? " "+Status : "")
  61. #define FTag VersionOnly+(Status != "" ? "."+Status : "")
  62. #define WebArguments "ver=" +VersionOnly + "&lang={language}&utm_source=winscp&utm_medium=setup&utm_campaign=" + VersionOnly
  63. #define WebGettingStarted WebRoot + "eng/installed.php?" + WebArguments + "&prevver="
  64. #define MessagesPath(L) TranslationDir + "\" + "WinSCP." + L + ".islu"
  65. #define ExplorerFileBase "Explorer"
  66. #define CommanderFileBase "Commander"
  67. #define WizardImageFileBase "Tall"
  68. #define WizardSmallImageFileBase "Square"
  69. #define SelectDirFileBase "Opened bookmark folder-stored session folder"
  70. [Setup]
  71. AppId={#AppId}
  72. AppName=WinSCP
  73. AppPublisher=Martin Prikryl
  74. AppPublisherURL={#WebRoot}
  75. AppSupportURL={#WebForum}
  76. AppUpdatesURL={#WebRoot}eng/download.php
  77. VersionInfoCompany=Martin Prikryl
  78. VersionInfoDescription=Setup for WinSCP {#Version} (SFTP, FTP, WebDAV and SCP client)
  79. VersionInfoVersion={#Major}.{#Minor}.{#Rev}.{#Build}
  80. VersionInfoTextVersion={#Version}
  81. VersionInfoCopyright=(c) 2000-{#Year} Martin Prikryl
  82. DefaultDirName={pf}\WinSCP
  83. DefaultGroupName=WinSCP
  84. AllowNoIcons=yes
  85. LicenseFile=license.setup.txt
  86. UninstallDisplayIcon={app}\WinSCP.exe
  87. OutputDir={#OutputDir}
  88. DisableStartupPrompt=yes
  89. AppVersion={#Version}
  90. AppVerName=WinSCP {#Version}
  91. OutputBaseFilename=WinSCP-{#FTag}-Setup
  92. SolidCompression=yes
  93. WizardImageFile={#ImagesDir}\{#WizardImageFileBase} 100.bmp
  94. WizardSmallImageFile={#ImagesDir}\{#WizardSmallImageFileBase} 100.bmp
  95. ShowTasksTreeLines=yes
  96. PrivilegesRequired=none
  97. UsePreviousLanguage=yes
  98. DisableProgramGroupPage=yes
  99. MinVersion=0,5.1
  100. SetupIconFile=winscpsetup.ico
  101. DisableDirPage=no
  102. #ifdef Sign
  103. SignTool=sign $f "WinSCP Installer" http://winscp.net/eng/docs/installation
  104. #endif
  105. [Languages]
  106. ; English has to be first so that it is pre-selected
  107. ; on Setup Select Language window, when no translation matching
  108. ; Windows UI locale is available
  109. Name: {#DefaultLang}; MessagesFile: {#MessagesPath(DefaultLang)}
  110. #define FindHandle
  111. #dim Languages[200]
  112. #define LanguageCount 0
  113. #define AnyLanguageComplete 0
  114. #define LangI
  115. #sub ProcessTranslationFile
  116. #define FileName FindGetFileName(FindHandle)
  117. #define Lang Copy(FileName, Pos(".", FileName)+1)
  118. #define LangNameFull ReadIni(MessagesPath(Lang), "LangOptions", "LanguageName")
  119. #define Sep Pos(" -", LangNameFull)
  120. #if Sep > 0
  121. #define LangName Copy(LangNameFull, 1, Sep - 1)
  122. #else
  123. #define LangName LangNameFull
  124. #endif
  125. #define LangID ReadIni(MessagesPath(Lang), "LangOptions", "LanguageID")
  126. #define LangCompleteness Int(ReadIni(MessagesPath(Lang), "CustomOptions", "TranslationCompleteness"))
  127. #expr Languages[LanguageCount*4] = Lang
  128. #expr Languages[LanguageCount*4+1] = LangName
  129. #expr Languages[LanguageCount*4+2] = LangID
  130. #expr Languages[LanguageCount*4+3] = LangCompleteness
  131. #expr LanguageCount++
  132. #if LangCompleteness > CompletenessThreshold
  133. Name: {#Lang}; MessagesFile: {#MessagesPath(Lang)}
  134. #expr AnyLanguageComplete = 1
  135. #endif
  136. #endsub /* sub ProcessTranslationFile */
  137. #if FindHandle = FindFirst(TranslationDir + "\" + TranslationFileMask, 0)
  138. #define FResult 1
  139. #for {0; FResult; FResult = FindNext(FindHandle)} ProcessTranslationFile
  140. #expr FindClose(FindHandle)
  141. #endif
  142. ; Types are not used anymore, they are preserved only to let setup
  143. ; detect previous installation type and decide between typical/custom setup
  144. [Types]
  145. Name: full; Description: "full"
  146. Name: compact; Description: "compact"
  147. Name: custom; Description: "custom"; Flags: iscustom
  148. [Components]
  149. Name: main; Description: {cm:ApplicationComponent}; \
  150. Types: full custom compact; Flags: fixed
  151. Name: shellext; Description: {cm:ShellExtComponent}; \
  152. Types: full compact
  153. Name: pageant; Description: {cm:PageantComponent}; \
  154. Types: full
  155. Name: puttygen; Description: {cm:PuTTYgenComponent}; \
  156. Types: full
  157. #if AnyLanguageComplete == 1
  158. Name: transl; Description: {cm:TranslationsComponent}; \
  159. Types: full
  160. #endif
  161. [Tasks]
  162. Name: enableupdates; Description: {cm:EnableUpdates}
  163. Name: enableupdates\enablecollectusage; Description: {cm:EnableCollectUsage}
  164. ; Windows integration
  165. Name: desktopicon; Description: {cm:DesktopIconTask}
  166. Name: desktopicon\user; Description: {cm:DesktopIconUserTask}; \
  167. Flags: exclusive unchecked
  168. Name: desktopicon\common; Description: {cm:DesktopIconCommonTask}; \
  169. Flags: exclusive
  170. ; No Quick Launch on Win7
  171. Name: quicklaunchicon; Description: {cm:QuickLaunchIconTask}; \
  172. Flags: unchecked; OnlyBelowVersion: 6.1.7600
  173. Name: sendtohook; Description: {cm:SendToHookTask}
  174. Name: urlhandler; Description: {cm:RegisterAsUrlHandlers}
  175. Name: searchpath; Description: {cm:AddSearchPath}; \
  176. Flags: unchecked; Check: IsAdminLoggedOn
  177. [Icons]
  178. Name: "{commonprograms}\WinSCP"; Filename: "{app}\WinSCP.exe"; Components: main; \
  179. Comment: "{cm:ProgramComment2}"
  180. ; This is created when desktopicon task is selected
  181. Name: "{userdesktop}\WinSCP"; Filename: "{app}\WinSCP.exe"; \
  182. Tasks: desktopicon\user; Comment: "{cm:ProgramComment2}"
  183. Name: "{commondesktop}\WinSCP"; Filename: "{app}\WinSCP.exe"; \
  184. Tasks: desktopicon\common; Comment: "{cm:ProgramComment2}"
  185. ; This is created when quicklaunchicon task is selected
  186. Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\WinSCP"; \
  187. Filename: "{app}\WinSCP.exe"; Tasks: quicklaunchicon
  188. ; This is created when sendtohook task is selected
  189. Name: "{sendto}\{cm:SendToHookNew}"; Filename: "{app}\WinSCP.exe"; \
  190. Parameters: "/upload"; Tasks: sendtohook
  191. [InstallDelete]
  192. ; Remove pre-524 licence file (without .txt extension)
  193. Type: files; Name: "{app}\license"
  194. ; Remove pre-520 start menu folders
  195. Type: filesandordirs; Name: "{commonprograms}\WinSCP"
  196. Type: filesandordirs; Name: "{userprograms}\WinSCP"
  197. [Run]
  198. Filename: "{app}\WinSCP.exe"; Parameters: "/RegisterForDefaultProtocols"; \
  199. StatusMsg: {cm:RegisteringAsUrlHandlers}; Tasks: urlhandler
  200. Filename: "{app}\WinSCP.exe"; Parameters: "/AddSearchPath"; \
  201. StatusMsg: {cm:AddingSearchPath}; Tasks: searchpath
  202. Filename: "{app}\WinSCP.exe"; Parameters: "/ImportSitesIfAny"; \
  203. StatusMsg: {cm:ImportSites}; Flags: skipifsilent
  204. [UninstallDelete]
  205. ; These additional files are created by application
  206. Type: files; Name: "{app}\WinSCP.ini"
  207. Type: files; Name: "{app}\WinSCP.cgl"
  208. [Files]
  209. ; Put these to the top as we extract them on demand and
  210. ; that can take long with solid compression enabled
  211. Source: "{#ImagesDir}\{#ExplorerFileBase} *.bmp"; Flags: dontcopy
  212. Source: "{#ImagesDir}\{#CommanderFileBase} *.bmp"; Flags: dontcopy
  213. ; We do not need 100% images here, they are embedded already automatically
  214. ; by WizardImageFile and WizardSmallImageFile
  215. Source: "{#ImagesDir}\{#WizardImageFileBase} *.bmp"; Excludes: "* 100.bmp"; \
  216. Flags: dontcopy
  217. Source: "{#ImagesDir}\{#WizardSmallImageFileBase} *.bmp"; Excludes: "* 100.bmp"; \
  218. Flags: dontcopy
  219. Source: "{#ImagesDir}\{#SelectDirFileBase} *.bmp"; Flags: dontcopy
  220. #ifdef Donations
  221. Source: "{#ImagesDir}\{#PayPalCardImage}"; Flags: dontcopy
  222. #endif
  223. Source: "{#MainFileSource}"; DestDir: "{app}"; \
  224. Components: main; Flags: ignoreversion
  225. Source: "{#ConsoleFileSource}"; DestDir: "{app}"; \
  226. Components: main; Flags: ignoreversion
  227. Source: "{#MapFileSource}"; DestDir: "{app}"; \
  228. Components: main; Flags: ignoreversion
  229. Source: "license.txt"; DestDir: "{app}"; \
  230. Components: main; Flags: ignoreversion
  231. Source: "{#ShellExtFileSource}"; DestDir: "{app}"; \
  232. Components: shellext; \
  233. Flags: regserver restartreplace uninsrestartdelete; \
  234. Check: not IsWin64 and IsShellExtNewer(ExpandConstant('{app}\{#ShellExtFileName}'), '{#GetFileVersion(ShellExtFileSource)}')
  235. Source: "{#ShellExt64FileSource}"; DestDir: "{app}"; \
  236. Components: shellext; \
  237. Flags: regserver restartreplace uninsrestartdelete; \
  238. Check: IsWin64 and IsShellExtNewer(ExpandConstant('{app}\{#ShellExt64FileName}'), '{#GetFileVersion(ShellExt64FileSource)}')
  239. Source: "{#PuttySourceDir}\LICENCE"; DestDir: "{app}\PuTTY"; \
  240. Components: pageant puttygen; Flags: ignoreversion
  241. Source: "{#PuttySourceDir}\putty.hlp"; DestDir: "{app}\PuTTY"; \
  242. Components: pageant puttygen; Flags: ignoreversion
  243. Source: "{#PuttySourceDir}\pageant.exe"; DestDir: "{app}\PuTTY"; \
  244. Components: pageant; Flags: ignoreversion
  245. Source: "{#PuttySourceDir}\puttygen.exe"; DestDir: "{app}\PuTTY"; \
  246. Components: puttygen; Flags: ignoreversion
  247. [Registry]
  248. Root: HKCU; Subkey: "{#ParentRegistryKey}"; Flags: uninsdeletekeyifempty
  249. Root: HKCU; Subkey: "{#RegistryKey}"; Flags: uninsdeletekeyifempty
  250. ; Norton Commander interface
  251. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  252. ValueName: "Interface"; ValueData: 0; Check: UserSettings(1)
  253. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  254. ValueName: "DefaultInterfaceInterface"; ValueData: 0; \
  255. Check: UserSettings(1); Flags: noerror
  256. ; Explorer-like interface
  257. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  258. ValueName: "Interface"; ValueData: 1; Check: not UserSettings(1)
  259. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  260. ValueName: "DefaultInterfaceInterface"; ValueData: 1; \
  261. Check: not UserSettings(1); Flags: noerror
  262. ; If installer enabled ddext, let it reset the settings on uninstall,
  263. ; so the default is used on the next run
  264. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  265. ValueName: "DDExtEnabled"; ValueData: 1; Components: shellext; \
  266. Flags: uninsdeletevalue
  267. ; Updates
  268. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface\Updates"; \
  269. ValueType: dword; ValueName: "Period"; ValueData: 7; \
  270. Tasks: enableupdates; Check: not UpdatesEnabled
  271. Root: HKLM; SubKey: "{#RegistryKey}"; \
  272. ValueType: dword; ValueName: "DefaultUpdatesPeriod"; ValueData: 7; \
  273. Tasks: enableupdates; Flags: noerror
  274. Root: HKLM; SubKey: "{#RegistryKey}"; \
  275. ValueType: dword; ValueName: "DefaultCollectUsage"; ValueData: 1; \
  276. Tasks: enableupdates\enablecollectusage; Flags: noerror
  277. #if AnyLanguageComplete == 1
  278. [Components]
  279. Name: transl\eng; Description: {#EnglishLang}; Types: full custom compact; \
  280. Flags: fixed
  281. #endif
  282. #sub EmitLang
  283. #if Languages[LangI*4+3] > CompletenessThreshold
  284. [Components]
  285. Name: transl\{#Languages[LangI*4]}; Description: {#Languages[LangI*4+1]}; \
  286. Types: full compact custom; Check: IsLang('{#Languages[LangI*4]}')
  287. Name: transl\{#Languages[LangI*4]}; Description: {#Languages[LangI*4+1]}; \
  288. Check: not IsLang('{#Languages[LangI*4]}')
  289. [Files]
  290. Source: "{#TranslationDir}\WinSCP.{#Languages[LangI*4]}"; DestDir: "{app}"; \
  291. Components: transl\{#Languages[LangI*4]}; Flags: ignoreversion
  292. [Registry]
  293. ; set program default language to setup language, but only if user installs it
  294. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; \
  295. ValueType: dword; ValueName: "LocaleSafe"; ValueData: {#Languages[LangI*4+2]}; \
  296. Components: transl\{#Languages[LangI*4]}; Languages: {#Languages[LangI*4]}
  297. #endif
  298. #endsub /* sub EmitLang */
  299. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang
  300. [UninstallRun]
  301. ; Make sure no later uninstall task recreate the configuration
  302. Filename: "{app}\WinSCP.exe"; Parameters: "/UninstallCleanup"; \
  303. RunOnceId: "UninstallCleanup"
  304. Filename: "{app}\WinSCP.exe"; Parameters: "/RemoveSearchPath"; \
  305. RunOnceId: "RemoveSearchPath"
  306. Filename: "{app}\WinSCP.exe"; Parameters: "/UnregisterForProtocols"; \
  307. RunOnceId: "UnregisterForProtocols"
  308. [Code]
  309. const
  310. wpSetupType = 100;
  311. wpInterface = 101;
  312. NewLine = #13#10;
  313. var
  314. TypicalTypeButton: TRadioButton;
  315. CustomTypeButton: TRadioButton;
  316. CommanderRadioButton: TRadioButton;
  317. ExplorerRadioButton: TRadioButton;
  318. LaunchCheckbox: TCheckbox;
  319. OpenGettingStartedCheckbox: TCheckbox;
  320. AreUpdatesEnabled: Boolean;
  321. AutomaticUpdate: Boolean;
  322. Upgrade: Boolean;
  323. MissingTranslations: string;
  324. PrevVersion: string;
  325. ShellExtNewerCacheFileName: string;
  326. ShellExtNewerCacheResult: Boolean;
  327. #ifdef Donations
  328. DonationPanel: TPanel;
  329. AboutDonationCaption: TLabel;
  330. #endif
  331. InstallationDone: Boolean;
  332. LicenseAccepted: Boolean;
  333. InitDir: string;
  334. InitComponents: string;
  335. InitTasks: string;
  336. InitInterface: Integer;
  337. Donated: Boolean;
  338. procedure ShowMessage(Text: string);
  339. begin
  340. MsgBox(Text, mbInformation, MB_OK);
  341. end;
  342. function IsLang(Lang: string): Boolean;
  343. begin
  344. Result := (Lang = ActiveLanguage);
  345. end;
  346. function IsWin8: Boolean;
  347. var
  348. Version: TWindowsVersion;
  349. begin
  350. GetWindowsVersionEx(Version);
  351. Result :=
  352. (Version.Major > 6) or
  353. ((Version.Major = 6) and (Version.Minor >= 2));
  354. end;
  355. procedure CutVersionPart(var VersionString: string; var VersionPart: Word);
  356. var
  357. P: Integer;
  358. begin
  359. P := Pos('.', VersionString);
  360. if P > 0 then
  361. begin
  362. VersionPart := StrToIntDef(Copy(VersionString, 1, P - 1), 0);
  363. Delete(VersionString, 1, P);
  364. end
  365. else
  366. begin
  367. VersionPart := StrToIntDef(VersionString, 0);
  368. VersionString := '';
  369. end;
  370. end;
  371. function IsShellExtNewer(FileName: string; InstalledVersion: string): Boolean;
  372. var
  373. ExistingMS, ExistingLS: Cardinal;
  374. ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild: Cardinal;
  375. InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild: Word;
  376. begin
  377. if ShellExtNewerCacheFileName = FileName then
  378. begin
  379. if ShellExtNewerCacheResult then
  380. begin
  381. Log(Format('Allowing installation of shell extension %s as already decided', [FileName]));
  382. Result := True;
  383. end
  384. else
  385. begin
  386. Log(Format('Skipping installation of shell extension %s as already decided', [FileName]));
  387. Result := False;
  388. end;
  389. end
  390. else
  391. if not FileExists(FileName) then
  392. begin
  393. Log(Format('Shell extension %s does not exist yet, allowing installation', [FileName]));
  394. Result := True;
  395. end
  396. else
  397. if not GetVersionNumbers(FileName, ExistingMS, ExistingLS) then
  398. begin
  399. Log(Format('Cannot retrieve version of existing shell extension %s, allowing installation', [FileName]));
  400. Result := True;
  401. end
  402. else
  403. begin
  404. ExistingMajor := ExistingMS shr 16;
  405. ExistingMinor := ExistingMS and $FFFF;
  406. ExistingRev := ExistingLS shr 16;
  407. ExistingBuild := ExistingLS and $FFFF;
  408. Log(Format('Existing shell extension %s version: %d.%d.%d[.%d]', [FileName, ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild]));
  409. Log(Format('Installed extension version string: %s', [InstalledVersion]));
  410. CutVersionPart(InstalledVersion, InstalledMajor);
  411. CutVersionPart(InstalledVersion, InstalledMinor);
  412. CutVersionPart(InstalledVersion, InstalledRev);
  413. CutVersionPart(InstalledVersion, InstalledBuild);
  414. Log(Format('Installed extension version: %d.%d.%d[.%d]', [InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild]));
  415. if ((InstalledMajor > ExistingMajor)) or
  416. ((InstalledMajor = ExistingMajor) and (InstalledMinor > ExistingMinor)) or
  417. ((InstalledMajor = ExistingMajor) and (InstalledMinor = ExistingMinor) and (InstalledRev > ExistingRev)) then
  418. begin
  419. Log('Installed extension is newer than existing extension, allowing installation');
  420. Result := True;
  421. end
  422. else
  423. begin
  424. Log('Installed extension is same or older than existing extension, skipping installation');
  425. Result := False;
  426. end;
  427. end;
  428. ShellExtNewerCacheFileName := FileName;
  429. ShellExtNewerCacheResult := Result;
  430. end;
  431. function UpdatesEnabled: Boolean;
  432. begin
  433. Result := AreUpdatesEnabled;
  434. end;
  435. function UserSettings(Settings: Integer): Boolean;
  436. begin
  437. case Settings of
  438. 1: Result := CommanderRadioButton.Checked;
  439. else Result := False;
  440. end;
  441. end;
  442. function LanguageName(Lang: string; Unknown: string): string;
  443. begin
  444. #sub EmitLang2
  445. if Lang = '{#Languages[LangI*4]}' then Result := '{#Languages[LangI*4+1]}'
  446. else
  447. #endsub /* sub EmitLang2 */
  448. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang2
  449. Result := Unknown;
  450. end;
  451. function ContainsLanguage(Lang: string): Boolean;
  452. begin
  453. #sub EmitLang3
  454. #if Languages[LangI*4+3] > CompletenessThreshold
  455. if (Lang = '{#Languages[LangI*4]}') then Result := True
  456. else
  457. #endif
  458. #endsub /* sub EmitLang3 */
  459. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang3
  460. Result := False;
  461. end;
  462. function LanguageCompleteness(Lang: string): Integer;
  463. begin
  464. #sub EmitLang4
  465. if (Lang = '{#Languages[LangI*4]}') then
  466. begin
  467. Result := {#Languages[LangI*4+3]};
  468. end
  469. else
  470. #endsub /* sub EmitLang4 */
  471. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang4
  472. // used also for the default language
  473. Result := 100;
  474. end;
  475. procedure OpenBrowser(Url: string);
  476. var
  477. ErrorCode: Integer;
  478. begin
  479. ShellExec('open', Url, '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
  480. end;
  481. procedure OpenHelp;
  482. begin
  483. OpenBrowser('{#WebDocumentation}installation?page=' + IntToStr(WizardForm.CurPageID) + '&' + ExpandConstant('{#WebArguments}'));
  484. end;
  485. procedure HelpButtonClick(Sender: TObject);
  486. begin
  487. OpenHelp;
  488. end;
  489. procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  490. begin
  491. if Key = 112 { VK_F1 } then
  492. begin
  493. OpenHelp;
  494. Key := 0;
  495. end;
  496. end;
  497. procedure CaptionClick(Sender: TObject);
  498. begin
  499. WizardForm.ActiveControl := TLabel(Sender).FocusControl;
  500. end;
  501. procedure ImageClick(Sender: TObject);
  502. begin
  503. WizardForm.ActiveControl := TWinControl(TControl(Sender).Tag);
  504. end;
  505. type
  506. TProcessTranslationEvent = procedure(Lang: string; FileName: string);
  507. procedure CollectNames(Lang: string; FileName: string);
  508. begin
  509. if Length(MissingTranslations) > 0 then
  510. MissingTranslations := MissingTranslations + ', ';
  511. MissingTranslations := MissingTranslations + LanguageName(Lang, Lang);
  512. end;
  513. procedure DeleteTranslation(Lang: string; FileName: string);
  514. begin
  515. DeleteFile(FileName);
  516. end;
  517. procedure ProcessMissingTranslations(OnProcessTranslation: TProcessTranslationEvent);
  518. var
  519. Path: string;
  520. FindRec: TFindRec;
  521. Ext: string;
  522. LExt: string;
  523. begin
  524. Path := AddBackslash(WizardDirValue);
  525. if FindFirst(Path + '{#TranslationFileMask}', FindRec) then
  526. begin
  527. try
  528. repeat
  529. if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
  530. begin
  531. Ext := Uppercase(ExtractFileExt(FindRec.Name));
  532. if Pos('.', Ext) = 1 then
  533. begin
  534. Ext := Uppercase(Copy(Ext, 2, Length(Ext) - 1));
  535. LExt := Lowercase(Ext);
  536. if (Pos('-' + Ext + '-', '-{#AllTranslations}-') > 0) and
  537. not ContainsLanguage(LExt) then
  538. OnProcessTranslation(LExt, Path + FindRec.Name);
  539. end;
  540. end;
  541. until not FindNext(FindRec);
  542. finally
  543. FindClose(FindRec);
  544. end;
  545. end;
  546. end;
  547. function WillRestart: Boolean;
  548. begin
  549. Result := WizardForm.YesRadio.Visible and WizardForm.YesRadio.Checked;
  550. end;
  551. procedure UpdatePostInstallRunCheckboxes(Sender: TObject);
  552. begin
  553. LaunchCheckbox.Enabled := not WillRestart;
  554. OpenGettingStartedCheckbox.Enabled :=
  555. LaunchCheckbox.Enabled
  556. end;
  557. procedure LinkLabel(Control: TLabel);
  558. begin
  559. Control.ParentFont := True;
  560. Control.Font.Style := Control.Font.Style + [fsUnderline];
  561. Control.Font.Color := clBlue;
  562. Control.Cursor := crHand;
  563. end;
  564. function IsTypicalInstallation: Boolean;
  565. begin
  566. Result := TypicalTypeButton.Checked;
  567. end;
  568. #ifdef Donations
  569. procedure AboutDonationsLinkClick(Sender: TObject);
  570. begin
  571. OpenBrowser('{#WebRoot}eng/donate.php?' + ExpandConstant('{#WebArguments}'));
  572. Donated := true;
  573. end;
  574. procedure DonateLinkClick(Sender: TObject);
  575. var
  576. Control: TControl;
  577. begin
  578. Control := TControl(Sender);
  579. OpenBrowser('{#WebRoot}eng/donate.php?amount=' + IntToStr(Control.Tag) + '&currency=' + CustomMessage('Currency') + '&' + ExpandConstant('{#WebArguments}'));
  580. Donated := true;
  581. end;
  582. procedure CreateDonateLink(Amount: Integer; Row: Integer; Top: Integer);
  583. var
  584. Caption: TLabel;
  585. begin
  586. Caption := TLabel.Create(DonationPanel);
  587. Caption.Left := 0;
  588. Caption.Top := Top + Row * ScaleY(16);
  589. Caption.Tag := Amount;
  590. Caption.Parent := DonationPanel;
  591. Caption.Caption := Format(CustomMessage('Donate'), ['$' + IntToStr(Amount)]);
  592. Caption.OnClick := @DonateLinkClick;
  593. LinkLabel(Caption);
  594. end;
  595. #endif
  596. const
  597. fsSurface = 0;
  598. procedure LoadEmbededBitmap(Image: TBitmapImage; Name: string; BackgroundColor: TColor);
  599. var
  600. FileName: string;
  601. Bitmap: TAlphaBitmap;
  602. begin
  603. ExtractTemporaryFile(Name);
  604. Bitmap := TAlphaBitmap.Create();
  605. FileName := ExpandConstant('{tmp}\' + Name);
  606. Bitmap.LoadFromFile(FileName);
  607. // we won't need this anymore
  608. DeleteFile(FileName);
  609. if BackgroundColor <> 0 then
  610. begin
  611. // Solving transparency on run-time to avoid having to
  612. // change the background color in the image file,
  613. // so they can stay identical to the original
  614. // (except for conversion from png to bmp)
  615. Image.ReplaceColor := clFuchsia;
  616. Bitmap.Canvas.Brush.Color := Image.ReplaceColor;
  617. Bitmap.Canvas.FloodFill(0, 0, Bitmap.Canvas.Pixels[0, 0], fsSurface);
  618. Image.ReplaceWithColor := BackgroundColor;
  619. end;
  620. Image.Bitmap := Bitmap;
  621. Bitmap.Free;
  622. Image.AutoSize := True;
  623. end;
  624. function GetScalingFactor: Integer;
  625. begin
  626. if WizardForm.Font.PixelsPerInch >= 192 then Result := 200
  627. else
  628. if WizardForm.Font.PixelsPerInch >= 144 then Result := 150
  629. else
  630. if WizardForm.Font.PixelsPerInch >= 120 then Result := 125
  631. else Result := 100;
  632. end;
  633. procedure LoadEmbededScaledBitmap(Image: TBitmapImage; NameBase: string; SizeBase: Integer; BackgroundColor: TColor);
  634. var
  635. Name: String;
  636. begin
  637. Name := Format('%s %d.bmp', [NameBase, SizeBase * GetScalingFactor div 100]);
  638. LoadEmbededBitmap(Image, Name, BackgroundColor);
  639. end;
  640. // WORKAROUND
  641. // Checkboxes and Radio buttons created on runtime do
  642. // not scale their height automatically
  643. procedure ScaleFixedHeightControl(Control: TButtonControl);
  644. begin
  645. Control.Height := ScaleY(Control.Height);
  646. end;
  647. function GetBottom(Control: TControl): Integer;
  648. begin
  649. Result := Control.Top + Control.Height;
  650. end;
  651. function CmdLineParamExists(const Value: string): Boolean;
  652. var
  653. I: Integer;
  654. begin
  655. Result := False;
  656. for I := 1 to ParamCount do
  657. begin
  658. if CompareText(ParamStr(I), Value) = 0 then
  659. begin
  660. Result := True;
  661. Exit;
  662. end;
  663. end;
  664. end;
  665. function InitializeSetup: Boolean;
  666. var
  667. WaitInterval: Integer;
  668. Wait: Integer;
  669. begin
  670. AutomaticUpdate := CmdLineParamExists('/AutomaticUpdate');
  671. if AutomaticUpdate then
  672. begin
  673. Log('Automatic update');
  674. Wait := 10000;
  675. end
  676. else
  677. begin
  678. Wait := 0;
  679. end;
  680. WaitInterval := 250;
  681. while (Wait > 0) and CheckForMutexes('{#AppMutex}') do
  682. begin
  683. Log('Application is still running, waiting');
  684. Sleep(WaitInterval);
  685. Wait := Wait - WaitInterval;
  686. end;
  687. while CheckForMutexes('{#AppMutex}') do
  688. begin
  689. if MsgBox(
  690. FmtMessage(SetupMessage(msgSetupAppRunningError), ['WinSCP']),
  691. mbError, MB_OKCANCEL) <> IDOK then
  692. begin
  693. Abort;
  694. end;
  695. end;
  696. Result := True;
  697. end;
  698. procedure InitializeWizard;
  699. var
  700. DefaultLang: Boolean;
  701. UserInterface: Cardinal;
  702. UpdatesPeriod: Cardinal;
  703. InterfacePage: TWizardPage;
  704. SetupTypePage: TWizardPage;
  705. Caption: TLabel;
  706. Image: TBitmapImage;
  707. HelpButton: TButton;
  708. #ifdef Donations
  709. P: Integer;
  710. #endif
  711. S: string;
  712. Completeness: Integer;
  713. begin
  714. InstallationDone := False;
  715. LicenseAccepted := False;
  716. InitInterface := -1;
  717. DefaultLang := (ActiveLanguage = '{#DefaultLang}');
  718. Upgrade :=
  719. RegQueryStringValue(HKLM, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S) or
  720. RegQueryStringValue(HKCU, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S)
  721. if Upgrade and GetVersionNumbersString(AddBackslash(WizardDirValue) + '{#MainFileName}', PrevVersion) and
  722. (PrevVersion[2] = '.') and (PrevVersion[4] = '.') and (PrevVersion[6] = '.') then
  723. begin
  724. PrevVersion := Copy(PrevVersion, 1, 5);
  725. end;
  726. Completeness := LanguageCompleteness(ActiveLanguage);
  727. if Completeness < 100 then
  728. begin
  729. ShowMessage(FmtMessage(CustomMessage('IncompleteTranslation'), [IntToStr(Completeness)]));
  730. end;
  731. ProcessMissingTranslations(@CollectNames);
  732. WizardForm.KeyPreview := True;
  733. WizardForm.OnKeyDown := @FormKeyDown;
  734. // to accomodate one more task
  735. WizardForm.TasksList.Height := WizardForm.TasksList.Height + ScaleY(8);
  736. // allow installation without requiring user to accept license
  737. WizardForm.LicenseAcceptedRadio.Checked := True;
  738. WizardForm.LicenseAcceptedRadio.Visible := False;
  739. WizardForm.LicenseNotAcceptedRadio.Visible := False;
  740. WizardForm.LicenseMemo.Height :=
  741. GetBottom(WizardForm.LicenseNotAcceptedRadio) -
  742. WizardForm.LicenseMemo.Top - 5;
  743. // hide installation types combo
  744. WizardForm.TypesCombo.Visible := False;
  745. WizardForm.ComponentsList.Height :=
  746. GetBottom(WizardForm.ComponentsList) -
  747. WizardForm.TypesCombo.Top;
  748. WizardForm.ComponentsList.Top := WizardForm.TypesCombo.Top;
  749. // add help button
  750. HelpButton := TButton.Create(WizardForm);
  751. HelpButton.Parent := WizardForm;
  752. HelpButton.Left :=
  753. WizardForm.ClientWidth -
  754. (WizardForm.CancelButton.Left + WizardForm.CancelButton.Width);
  755. HelpButton.Top := WizardForm.CancelButton.Top;
  756. HelpButton.Width := WizardForm.CancelButton.Width;
  757. HelpButton.Height := WizardForm.CancelButton.Height;
  758. HelpButton.Caption := CustomMessage('HelpButton');
  759. HelpButton.OnClick := @HelpButtonClick;
  760. // installation type page
  761. SetupTypePage := CreateCustomPage(wpLicense,
  762. CustomMessage('SetupTypeTitle'),
  763. CustomMessage('SetupTypePrompt'));
  764. TypicalTypeButton := TRadioButton.Create(SetupTypePage);
  765. if not Upgrade then
  766. S := CustomMessage('TypicalType')
  767. else
  768. S := CustomMessage('TypicalUpgradeType');
  769. TypicalTypeButton.Caption :=
  770. FmtMessage(CustomMessage('Recommended'), [S]);
  771. // check typical install, if typical install was installed before or
  772. // when version without setup type support was installed with
  773. // "full" installation or when there were no installation before
  774. // ("full" installation is default)
  775. TypicalTypeButton.Checked :=
  776. ((GetPreviousData('{#SetupTypeData}', '') = 'typical')) or
  777. ((GetPreviousData('{#SetupTypeData}', '') = '') and
  778. (WizardSetupType(False) = 'full'));
  779. TypicalTypeButton.Left := ScaleX(4);
  780. TypicalTypeButton.Width := SetupTypePage.SurfaceWidth -
  781. TypicalTypeButton.Left;
  782. ScaleFixedHeightControl(TypicalTypeButton);
  783. TypicalTypeButton.Parent := SetupTypePage.Surface;
  784. Caption := TLabel.Create(SetupTypePage);
  785. Caption.WordWrap := True;
  786. if not Upgrade then
  787. begin
  788. if DefaultLang then
  789. S := CustomMessage('TypicalType2Eng')
  790. else
  791. S := FmtMessage(CustomMessage('TypicalType2Intl'), [CustomMessage('LocalLanguageName')]);
  792. Caption.Caption :=
  793. CustomMessage('TypicalType1') + NewLine +
  794. S + NewLine +
  795. CustomMessage('TypicalType3');
  796. end
  797. else
  798. begin
  799. if Length(MissingTranslations) > 0 then
  800. begin
  801. #if AnyLanguageComplete
  802. S := FmtMessage(CustomMessage('TypicalUpgradeTypeMissingTransl'), [MissingTranslations]);
  803. #else
  804. S := CustomMessage('TypicalUpgradeTypeNoTransl');
  805. #endif
  806. S := NewLine + S;
  807. end
  808. else S := '';
  809. Caption.Caption :=
  810. CustomMessage('TypicalUpgradeType1') + S;
  811. end;
  812. Caption.Left := ScaleX(4) + ScaleX(20);
  813. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  814. Caption.Top := GetBottom(TypicalTypeButton) + ScaleY(6);
  815. Caption.Parent := SetupTypePage.Surface;
  816. Caption.FocusControl := TypicalTypeButton;
  817. Caption.OnClick := @CaptionClick;
  818. CustomTypeButton := TRadioButton.Create(SetupTypePage);
  819. if not Upgrade then
  820. CustomTypeButton.Caption := CustomMessage('CustomType')
  821. else
  822. CustomTypeButton.Caption := CustomMessage('CustomUpgradeType');
  823. CustomTypeButton.Checked := (not TypicalTypeButton.Checked);
  824. CustomTypeButton.Left := ScaleX(4);
  825. CustomTypeButton.Width := SetupTypePage.SurfaceWidth -
  826. CustomTypeButton.Left;
  827. CustomTypeButton.Top := GetBottom(Caption) + ScaleY(10);
  828. ScaleFixedHeightControl(CustomTypeButton);
  829. CustomTypeButton.Parent := SetupTypePage.Surface;
  830. Caption := TLabel.Create(SetupTypePage);
  831. Caption.WordWrap := True;
  832. if not Upgrade then
  833. begin
  834. Caption.Caption :=
  835. CustomMessage('CustomType1');
  836. end
  837. else
  838. begin
  839. Caption.Caption :=
  840. CustomMessage('CustomUpgradeType1') + NewLine +
  841. CustomMessage('CustomUpgradeType2');
  842. end;
  843. Caption.Left := ScaleX(4) + ScaleX(20);
  844. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  845. Caption.Top := GetBottom(CustomTypeButton) + ScaleY(6);
  846. Caption.Parent := SetupTypePage.Surface;
  847. Caption.FocusControl := CustomTypeButton;
  848. Caption.OnClick := @CaptionClick;
  849. // interface page
  850. InterfacePage := CreateCustomPage(wpSelectTasks,
  851. CustomMessage('UserSettingsTitle'),
  852. CustomMessage('UserSettingsPrompt'));
  853. UpdatesPeriod := 0;
  854. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface\Updates',
  855. 'Period', UpdatesPeriod);
  856. AreUpdatesEnabled := (UpdatesPeriod <> 0);
  857. UserInterface := 0; { default is commander }
  858. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface',
  859. 'Interface', UserInterface);
  860. Caption := TLabel.Create(InterfacePage);
  861. Caption.Caption := CustomMessage('UserInterfaceStyle');
  862. Caption.Width := InterfacePage.SurfaceWidth;
  863. Caption.Parent := InterfacePage.Surface;
  864. CommanderRadioButton := TRadioButton.Create(InterfacePage);
  865. CommanderRadioButton.Caption := CustomMessage('NortonCommanderInterfaceC');
  866. CommanderRadioButton.Checked := (UserInterface = 0);
  867. CommanderRadioButton.Left := ScaleX(4);
  868. CommanderRadioButton.Width := ScaleX(116);
  869. CommanderRadioButton.Top := GetBottom(Caption) + ScaleY(6);
  870. ScaleFixedHeightControl(CommanderRadioButton);
  871. CommanderRadioButton.Parent := InterfacePage.Surface;
  872. Image := TBitmapImage.Create(InterfacePage);
  873. Image.Top := GetBottom(CommanderRadioButton) + ScaleY(6);
  874. Image.Left := CommanderRadioButton.Left + ScaleX(45);
  875. Image.Parent := InterfacePage.Surface;
  876. LoadEmbededScaledBitmap(Image, '{#CommanderFileBase}', 32, InterfacePage.Surface.Color);
  877. Image.OnClick := @ImageClick;
  878. Image.Tag := Integer(CommanderRadioButton);
  879. Caption := TLabel.Create(InterfacePage);
  880. Caption.WordWrap := True;
  881. Caption.Caption :=
  882. CustomMessage('NortonCommanderInterface1') + NewLine +
  883. CustomMessage('NortonCommanderInterface2') + NewLine +
  884. CustomMessage('NortonCommanderInterface3');
  885. Caption.Left := CommanderRadioButton.Left + CommanderRadioButton.Width;
  886. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  887. Caption.Top := CommanderRadioButton.Top;
  888. Caption.Parent := InterfacePage.Surface;
  889. Caption.FocusControl := CommanderRadioButton;
  890. Caption.OnClick := @CaptionClick;
  891. ExplorerRadioButton := TRadioButton.Create(InterfacePage);
  892. ExplorerRadioButton.Caption := CustomMessage('ExplorerInterfaceC');
  893. ExplorerRadioButton.Checked := (UserInterface <> 0);
  894. ExplorerRadioButton.Left := ScaleX(4);
  895. ExplorerRadioButton.Width := CommanderRadioButton.Width;
  896. ExplorerRadioButton.Top := GetBottom(Caption) + ScaleY(10);
  897. ScaleFixedHeightControl(ExplorerRadioButton);
  898. ExplorerRadioButton.Parent := InterfacePage.Surface;
  899. Image := TBitmapImage.Create(InterfacePage);
  900. Image.Top := GetBottom(ExplorerRadioButton) + ScaleY(6);
  901. Image.Left := ExplorerRadioButton.Left + ScaleX(45);
  902. Image.Parent := InterfacePage.Surface;
  903. LoadEmbededScaledBitmap(Image, '{#ExplorerFileBase}', 32, InterfacePage.Surface.Color);
  904. Image.OnClick := @ImageClick;
  905. Image.Tag := Integer(ExplorerRadioButton);
  906. Caption := TLabel.Create(InterfacePage);
  907. Caption.WordWrap := True;
  908. Caption.Caption :=
  909. CustomMessage('ExplorerInterface1') + NewLine +
  910. CustomMessage('ExplorerInterface2') + NewLine +
  911. CustomMessage('ExplorerInterface3');
  912. Caption.Left := ExplorerRadioButton.Left + ExplorerRadioButton.Width;
  913. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  914. Caption.Top := ExplorerRadioButton.Top;
  915. Caption.Parent := InterfacePage.Surface;
  916. Caption.FocusControl := ExplorerRadioButton;
  917. Caption.OnClick := @CaptionClick;
  918. // run checkbox
  919. LaunchCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  920. LaunchCheckbox.Caption := CustomMessage('Launch');
  921. LaunchCheckbox.Checked := True;
  922. LaunchCheckbox.Left := WizardForm.YesRadio.Left;
  923. LaunchCheckbox.Width := WizardForm.YesRadio.Width;
  924. ScaleFixedHeightControl(LaunchCheckbox);
  925. LaunchCheckbox.Parent := WizardForm.FinishedPage;
  926. OpenGettingStartedCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  927. OpenGettingStartedCheckbox.Caption := CustomMessage('OpenGettingStarted');
  928. OpenGettingStartedCheckbox.Checked := True;
  929. OpenGettingStartedCheckbox.Left := WizardForm.YesRadio.Left;
  930. OpenGettingStartedCheckbox.Width := WizardForm.YesRadio.Width;
  931. ScaleFixedHeightControl(OpenGettingStartedCheckbox);
  932. OpenGettingStartedCheckbox.Parent := WizardForm.FinishedPage;
  933. #ifdef Donations
  934. DonationPanel := TPanel.Create(WizardForm.FinishedPage);
  935. DonationPanel.Left := WizardForm.YesRadio.Left;
  936. DonationPanel.Width := WizardForm.YesRadio.Width;
  937. DonationPanel.Parent := WizardForm.FinishedPage;
  938. DonationPanel.BevelInner := bvNone;
  939. DonationPanel.BevelOuter := bvNone;
  940. DonationPanel.Color := WizardForm.FinishedPage.Color;
  941. Caption := TLabel.Create(DonationPanel);
  942. Caption.WordWrap := True;
  943. Caption.Caption := CustomMessage('PleaseDonate');
  944. Caption.Left := 0;
  945. Caption.Top := 0;
  946. Caption.Width := DonationPanel.Width;
  947. Caption.Parent := DonationPanel;
  948. P := GetBottom(Caption) + ScaleY(12);
  949. CreateDonateLink( 9, 0, P);
  950. CreateDonateLink(19, 1, P);
  951. CreateDonateLink(29, 2, P);
  952. CreateDonateLink(49, 3, P);
  953. AboutDonationCaption := TLabel.Create(DonationPanel);
  954. AboutDonationCaption.Left := 0;
  955. AboutDonationCaption.Top := P + 3 * ScaleY(16) + ScaleY(24);
  956. AboutDonationCaption.Parent := DonationPanel;
  957. AboutDonationCaption.Caption := CustomMessage('AboutDonations');
  958. AboutDonationCaption.OnClick := @AboutDonationsLinkClick;
  959. LinkLabel(AboutDonationCaption);
  960. Image := TBitmapImage.Create(DonationPanel);
  961. LoadEmbededBitmap(Image, '{#PayPalCardImage}', DonationPanel.Color);
  962. Image.Cursor := crHand;
  963. Image.Parent := DonationPanel;
  964. Image.Left := ScaleX(100);
  965. Image.Top := P + ScaleX(8);
  966. Image.Hint := CustomMessage('AboutDonations');
  967. Image.ShowHint := True;
  968. Image.OnClick := @AboutDonationsLinkClick;
  969. DonationPanel.Height := GetBottom(AboutDonationCaption);
  970. #endif
  971. WizardForm.YesRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  972. WizardForm.NoRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  973. UpdatePostInstallRunCheckboxes(nil);
  974. if IsWin8 then
  975. begin
  976. WizardForm.NoIconsCheck.Checked := True;
  977. end;
  978. // 100% images are automatically loaded by
  979. // WizardImageFile and WizardSmallImageFile
  980. if GetScalingFactor > 100 then
  981. begin
  982. LoadEmbededScaledBitmap(WizardForm.WizardBitmapImage, '{#WizardImageFileBase}', 100, 0);
  983. LoadEmbededScaledBitmap(WizardForm.WizardBitmapImage2, '{#WizardImageFileBase}', 100, 0);
  984. LoadEmbededScaledBitmap(WizardForm.WizardSmallBitmapImage, '{#WizardSmallImageFileBase}', 100, 0);
  985. end;
  986. LoadEmbededScaledBitmap(WizardForm.SelectDirBitmapImage, '{#SelectDirFileBase}', 32, WizardForm.SelectDirPage.Color);
  987. end;
  988. procedure RegisterPreviousData(PreviousDataKey: Integer);
  989. var
  990. S: string;
  991. begin
  992. if IsTypicalInstallation then S := 'typical'
  993. else S := 'custom';
  994. SetPreviousData(PreviousDataKey, '{#SetupTypeData}', S);
  995. end;
  996. function SaveCheckListBoxState(ListBox: TNewCheckListBox): string;
  997. var
  998. I: Integer;
  999. begin
  1000. for I := 0 to ListBox.Items.Count - 1 do
  1001. begin
  1002. Result := Result + IntToStr(Integer(ListBox.State[I]));
  1003. end;
  1004. end;
  1005. procedure CurPageChanged(CurPageID: Integer);
  1006. var
  1007. Delta: Integer;
  1008. LineHeight: Integer;
  1009. LaunchCheckboxTop: Integer;
  1010. begin
  1011. if CurPageID = wpLicense then
  1012. begin
  1013. WizardForm.NextButton.Caption := CustomMessage('AcceptButton')
  1014. end;
  1015. if CurPageID = wpSelectDir then
  1016. begin
  1017. if InitDir = '' then
  1018. InitDir := WizardForm.DirEdit.Text;
  1019. end
  1020. else
  1021. if CurPageID = wpSelectComponents then
  1022. begin
  1023. if InitComponents = '' then
  1024. InitComponents := SaveCheckListBoxState(WizardForm.ComponentsList);
  1025. end
  1026. else
  1027. if CurPageID = wpSelectTasks then
  1028. begin
  1029. if InitTasks = '' then
  1030. InitTasks := SaveCheckListBoxState(WizardForm.TasksList);
  1031. end
  1032. else
  1033. if CurPageID = wpInterface then
  1034. begin
  1035. if InitInterface < 0 then
  1036. InitInterface := Integer(CommanderRadioButton.Checked);
  1037. end
  1038. else
  1039. if CurPageID = wpFinished then
  1040. begin
  1041. LineHeight := (WizardForm.NoRadio.Top - WizardForm.YesRadio.Top);
  1042. // Are we at the "Restart?" screen
  1043. if WizardForm.YesRadio.Visible then
  1044. begin
  1045. WizardForm.FinishedLabel.Caption :=
  1046. CustomMessage('FinishedRestartDragExtLabel') + NewLine;
  1047. Delta := WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
  1048. WizardForm.YesRadio.Top := WizardForm.YesRadio.Top + Delta;
  1049. WizardForm.NoRadio.Top := WizardForm.NoRadio.Top + Delta;
  1050. LaunchCheckboxTop := WizardForm.NoRadio.Top + LineHeight;
  1051. #ifdef Donations
  1052. DonationPanel.Visible := False;
  1053. #endif
  1054. end
  1055. else
  1056. begin
  1057. LaunchCheckboxTop := WizardForm.RunList.Top;
  1058. end;
  1059. LaunchCheckbox.Top := LaunchCheckboxTop;
  1060. OpenGettingStartedCheckbox.Top := LaunchCheckbox.Top + LineHeight;
  1061. UpdatePostInstallRunCheckboxes(nil);
  1062. #ifdef Donations
  1063. if DonationPanel.Visible then
  1064. begin
  1065. DonationPanel.Top := GetBottom(OpenGettingStartedCheckbox) + ScaleY(12);
  1066. // Hide "about donations" if it does not fit nicely
  1067. // (happens on "long" languages, as German)
  1068. if (DonationPanel.Top + GetBottom(AboutDonationCaption)) >
  1069. (WizardForm.FinishedPage.Height - ScaleY(8)) then
  1070. begin
  1071. AboutDonationCaption.Visible := False;
  1072. end;
  1073. end;
  1074. #endif
  1075. end
  1076. else
  1077. if CurPageID = wpSetupType then
  1078. begin
  1079. Log('License accepted');
  1080. LicenseAccepted := True;
  1081. end
  1082. else
  1083. if CurPageID = wpPreparing then
  1084. begin
  1085. // Are we at the "Restart applications?" screen
  1086. if WizardForm.PreparingLabel.Visible then
  1087. begin
  1088. WizardForm.PreparingLabel.Caption :=
  1089. CustomMessage('ApplicationsFoundDragExt');
  1090. WizardForm.IncTopDecHeight(WizardForm.PreparingMemo,
  1091. WizardForm.AdjustLabelHeight(WizardForm.PreparingLabel));
  1092. end;
  1093. end;
  1094. end;
  1095. function AskedRestart: Boolean;
  1096. begin
  1097. Result := WizardForm.YesRadio.Visible;
  1098. end;
  1099. procedure DeinitializeSetup;
  1100. var
  1101. WinHttpReq: Variant;
  1102. ReportUrl: string;
  1103. ReportData: string;
  1104. begin
  1105. // cannot send report, unless user already accepted license
  1106. // (with privacy policy)
  1107. if LicenseAccepted then
  1108. begin
  1109. Log('Preparing intallation report');
  1110. ReportData := Format(
  1111. 'installed=%d&silent=%d&ver=%s&lang=%s&prevver=%s&', [
  1112. Integer(InstallationDone), Integer(WizardSilent),
  1113. '{#VersionOnly}', ActiveLanguage,
  1114. PrevVersion]);
  1115. try
  1116. ReportUrl := '{#WebReport}?' + ReportData;
  1117. Log('Sending installation report: ' + ReportUrl);
  1118. WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
  1119. WinHttpReq.Open('GET', ReportUrl, False);
  1120. WinHttpReq.Send('');
  1121. Log('Installation report send result: ' + IntToStr(WinHttpReq.Status) + ' ' + WinHttpReq.StatusText);
  1122. except
  1123. Log('Error sending installation report: ' + GetExceptionMessage);
  1124. end;
  1125. end;
  1126. end;
  1127. procedure ExecApp(Params: string; ShowCmd: Integer; Wait: TExecWait);
  1128. var
  1129. Path: string;
  1130. ErrorCode: Integer;
  1131. begin
  1132. Path := ExpandConstant('{app}\{#MainFileName}');
  1133. ExecAsOriginalUser(Path, Params, '', ShowCmd, Wait, ErrorCode)
  1134. end;
  1135. procedure OpenBrowserGettingStarted;
  1136. var
  1137. WebGettingStarted: string;
  1138. begin
  1139. WebGettingStarted :=
  1140. ExpandConstant('{#WebGettingStarted}') + PrevVersion +
  1141. '&automatic=' + IntToStr(Integer(AutomaticUpdate));
  1142. Log('Opening getting started page: ' + WebGettingStarted);
  1143. OpenBrowser(WebGettingStarted);
  1144. end;
  1145. procedure CurStepChanged(CurStep: TSetupStep);
  1146. var
  1147. ShowCmd: Integer;
  1148. OpenGettingStarted: Boolean;
  1149. UsageData: string;
  1150. CanPostInstallRuns: Boolean;
  1151. begin
  1152. if CurStep = ssPostInstall then
  1153. begin
  1154. Log('Post install');
  1155. if Length(MissingTranslations) > 0 then
  1156. begin
  1157. Log('Removing obsolete translations');
  1158. WizardForm.StatusLabel.Caption :=
  1159. CustomMessage('RemovingObsoleteTranslations');
  1160. ProcessMissingTranslations(@DeleteTranslation);
  1161. end;
  1162. InstallationDone := True;
  1163. end
  1164. else
  1165. if CurStep = ssDone then
  1166. begin
  1167. Log('Done');
  1168. // bug in InnoSetup causes it using ssDone even when
  1169. // setup failed because machine was not restarted to complete previous
  1170. // installation. double check that ssPostInstall was called
  1171. if InstallationDone then
  1172. begin
  1173. CanPostInstallRuns := (not WizardSilent) and (not WillRestart);
  1174. OpenGettingStarted :=
  1175. OpenGettingStartedCheckbox.Enabled and
  1176. OpenGettingStartedCheckbox.Checked;
  1177. UsageData := '/Usage=';
  1178. // old style counter
  1179. UsageData := UsageData + Format('TypicalInstallation:%d,', [Integer(IsTypicalInstallation)]);
  1180. // new style counters
  1181. if not Upgrade then
  1182. begin
  1183. if IsTypicalInstallation then
  1184. UsageData := UsageData + 'InstallationsFirstTypical+,'
  1185. else
  1186. UsageData := UsageData + 'InstallationsFirstCustom+,';
  1187. end
  1188. else
  1189. begin
  1190. if AutomaticUpdate then
  1191. UsageData := UsageData + 'InstallationsUpgradeAutomatic+,'
  1192. else if IsTypicalInstallation then
  1193. UsageData := UsageData + 'InstallationsUpgradeTypical+,'
  1194. else
  1195. UsageData := UsageData + 'InstallationsUpgradeCustom+,';
  1196. end;
  1197. if (InitDir <> '') and (InitDir <> WizardForm.DirEdit.Text) then
  1198. UsageData := UsageData + 'InstallationsCustomDir+,';
  1199. if (InitComponents <> '') and (InitComponents <> SaveCheckListBoxState(WizardForm.ComponentsList)) then
  1200. UsageData := UsageData + 'InstallationsCustomComponents+,';
  1201. if (InitTasks <> '') and (InitTasks <> SaveCheckListBoxState(WizardForm.TasksList)) then
  1202. UsageData := UsageData + 'InstallationsCustomTasks+,';
  1203. if (InitInterface >= 0) and (InitInterface <> Integer(CommanderRadioButton.Checked)) then
  1204. UsageData := UsageData + 'InstallationsCustomInterface+,';
  1205. if CanPostInstallRuns and OpenGettingStarted then
  1206. UsageData := UsageData + 'InstallationsGettingStarted+,';
  1207. if CanPostInstallRuns and LaunchCheckbox.Checked then
  1208. UsageData := UsageData + 'InstallationsLaunch+,';
  1209. if WizardSilent then
  1210. UsageData := UsageData + 'InstallationsSilent+,';
  1211. if AskedRestart then
  1212. UsageData := UsageData + 'InstallationsNeedRestart+,';
  1213. if WillRestart then
  1214. UsageData := UsageData + 'InstallationsRestart+,';
  1215. if Donated then
  1216. UsageData := UsageData + 'InstallationsDonate+,';
  1217. // have to do this before running WinSCP GUI instance below,
  1218. // otherwise it loads the empty/previous counters and overwrites our changes,
  1219. // when it's closed
  1220. Log('Recording installer usage statistics: ' + UsageData);
  1221. // make sure we write the counters using the "normal" account
  1222. // (the account that will be used to report the counters)
  1223. ExecApp(UsageData, SW_HIDE, ewWaitUntilTerminated);
  1224. if AutomaticUpdate then
  1225. begin
  1226. Log('Launching WinSCP after automatic update');
  1227. ExecApp('', SW_SHOWNORMAL, ewNoWait);
  1228. if CmdLineParamExists('/OpenGettingStarted') then
  1229. begin
  1230. OpenBrowserGettingStarted;
  1231. end;
  1232. end
  1233. else
  1234. if CanPostInstallRuns then
  1235. begin
  1236. if OpenGettingStarted then
  1237. begin
  1238. OpenBrowserGettingStarted;
  1239. end;
  1240. if LaunchCheckbox.Checked then
  1241. begin
  1242. if OpenGettingStarted then
  1243. begin
  1244. Log('Will launch WinSCP minimized');
  1245. ShowCmd := SW_SHOWMINIMIZED
  1246. end
  1247. else
  1248. begin
  1249. ShowCmd := SW_SHOWNORMAL;
  1250. end;
  1251. Log('Launching WinSCP');
  1252. ExecApp('', ShowCmd, ewNoWait);
  1253. end;
  1254. end;
  1255. end;
  1256. end;
  1257. end;
  1258. function ShouldSkipPage(PageID: Integer): Boolean;
  1259. begin
  1260. Result :=
  1261. { Hide most pages during typical installation }
  1262. IsTypicalInstallation and
  1263. ((PageID = wpSelectDir) or (PageID = wpSelectComponents) or
  1264. (PageID = wpSelectTasks) or
  1265. { Hide Interface page for upgrades only, show for fresh installs }
  1266. ((PageID = wpInterface) and Upgrade));
  1267. end;
  1268. function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
  1269. MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: string): string;
  1270. var
  1271. S: string;
  1272. S2: string;
  1273. begin
  1274. S := '';
  1275. S := S + MemoDirInfo + NewLine + NewLine;
  1276. if not Upgrade then
  1277. begin
  1278. if IsTypicalInstallation then S2 := CustomMessage('TypicalType')
  1279. else S2 := CustomMessage('CustomType');
  1280. end
  1281. else
  1282. begin
  1283. if IsTypicalInstallation then S2 := CustomMessage('TypicalUpgradeType')
  1284. else S2 := CustomMessage('CustomUpgradeType');
  1285. end;
  1286. StringChange(S2, '&', '');
  1287. S := S + SetupMessage(msgReadyMemoType) + NewLine + Space + S2 + NewLine + NewLine;
  1288. S := S + MemoComponentsInfo + NewLine + NewLine;
  1289. if Length(MemoGroupInfo) > 0 then
  1290. S := S + MemoGroupInfo + NewLine + NewLine;
  1291. if Length(MemoTasksInfo) > 0 then
  1292. S := S + MemoTasksInfo + NewLine + NewLine;
  1293. S := S + CustomMessage('UserSettingsOverview') + NewLine;
  1294. S := S + Space;
  1295. if CommanderRadioButton.Checked then S2 := CustomMessage('NortonCommanderInterfaceC')
  1296. else S2 := CustomMessage('ExplorerInterfaceC');
  1297. StringChange(S2, '&', '');
  1298. S := S + S2;
  1299. S := S + NewLine;
  1300. Result := S;
  1301. end;
  1302. function InitializeUninstall: Boolean;
  1303. begin
  1304. // let application know that we are running silent uninstall,
  1305. // this turns UninstallCleanup to noop
  1306. if UninstallSilent then
  1307. CreateMutex('WinSCPSilentUninstall');
  1308. Result := True;
  1309. end;
  1310. #expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")