winscpsetup.iss 48 KB

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