Setup.WizardForm.pas 122 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182
  1. unit Setup.WizardForm;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Wizard form
  8. }
  9. interface
  10. uses
  11. Windows, SysUtils, Messages, Classes, Graphics, Controls,
  12. Forms, Dialogs, StdCtrls, ExtCtrls,
  13. Setup.SetupForm, Shared.Struct, Shared.Int64Em, NewCheckListBox, RichEditViewer, NewStaticText,
  14. NewProgressBar, Shared.SetupMessageIDs, PasswordEdit, FolderTreeView, BitmapImage,
  15. NewNotebook, BidiCtrls;
  16. type
  17. TWizardForm = class;
  18. TWizardPage = class;
  19. TWizardPageClass = class of TWizardPage;
  20. TWizardPageStyle = set of (psAlwaysSkip, psNoButtons);
  21. TWizardPageNotifyEvent = procedure(Sender: TWizardPage) of object;
  22. TWizardPageButtonEvent = function(Sender: TWizardPage): Boolean of object;
  23. TWizardPageCancelEvent = procedure(Sender: TWizardPage; var ACancel, AConfirm: Boolean) of object;
  24. TWizardPageShouldSkipEvent = function(Sender: TWizardPage): Boolean of object;
  25. TWizardPage = class(TComponent)
  26. private
  27. FID: Integer;
  28. FOuterNotebookPage: TNewNotebookPage;
  29. FInnerNotebookPage: TNewNotebookPage;
  30. FCaption: String;
  31. FDescription: String;
  32. FOnActivate: TWizardPageNotifyEvent;
  33. FOnBackButtonClick: TWizardPageButtonEvent;
  34. FOnCancelButtonClick: TWizardPageCancelEvent;
  35. FOnNextButtonClick: TWizardPageButtonEvent;
  36. FOnShouldSkipPage: TWizardPageShouldSkipEvent;
  37. FStyle: TWizardPageStyle;
  38. FWizardForm: TWizardForm;
  39. function GetSurface: TNewNotebookPage;
  40. function GetSurfaceColor: TColor;
  41. function GetSurfaceHeight: Integer;
  42. function GetSurfaceWidth: Integer;
  43. procedure SetCaption(const Value: String);
  44. procedure SetDescription(const Value: String);
  45. procedure SyncCaptionAndDescription;
  46. protected
  47. procedure Activate; virtual;
  48. procedure BackButtonClick(var AContinue: Boolean); virtual;
  49. procedure CancelButtonClick(var ACancel, AConfirm: Boolean); virtual;
  50. procedure NextButtonClick(var AContinue: Boolean); virtual;
  51. procedure ShouldSkipPage(var AShouldSkip: Boolean); virtual;
  52. property InnerNotebookPage: TNewNotebookPage read FInnerNotebookPage;
  53. property OuterNotebookPage: TNewNotebookPage read FOuterNotebookPage;
  54. property Style: TWizardPageStyle read FStyle write FStyle;
  55. public
  56. constructor Create(AOwner: TComponent); override;
  57. destructor Destroy; override;
  58. published
  59. property Caption: String read FCaption write SetCaption;
  60. property Description: String read FDescription write SetDescription;
  61. property ID: Integer read FID;
  62. property Surface: TNewNotebookPage read GetSurface;
  63. property SurfaceColor: TColor read GetSurfaceColor;
  64. property SurfaceHeight: Integer read GetSurfaceHeight;
  65. property SurfaceWidth: Integer read GetSurfaceWidth;
  66. property OnActivate: TWizardPageNotifyEvent read FOnActivate write FOnActivate;
  67. property OnBackButtonClick: TWizardPageButtonEvent read FOnBackButtonClick write FOnBackButtonClick;
  68. property OnCancelButtonClick: TWizardPageCancelEvent read FOnCancelButtonClick write FOnCancelButtonClick;
  69. property OnNextButtonClick: TWizardPageButtonEvent read FOnNextButtonClick write FOnNextButtonClick;
  70. property OnShouldSkipPage: TWizardPageShouldSkipEvent read FOnShouldSkipPage write FOnShouldSkipPage;
  71. end;
  72. TWizardForm = class(TSetupForm)
  73. FCancelButton: TNewButton;
  74. FNextButton: TNewButton;
  75. FBackButton: TNewButton;
  76. FOuterNotebook: TNewNotebook;
  77. FInnerNotebook: TNewNotebook;
  78. FWelcomePage: TNewNotebookPage;
  79. FInnerPage: TNewNotebookPage;
  80. FFinishedPage: TNewNotebookPage;
  81. FLicensePage: TNewNotebookPage;
  82. FPasswordPage: TNewNotebookPage;
  83. FInfoBeforePage: TNewNotebookPage;
  84. FUserInfoPage: TNewNotebookPage;
  85. FSelectDirPage: TNewNotebookPage;
  86. FSelectComponentsPage: TNewNotebookPage;
  87. FSelectProgramGroupPage: TNewNotebookPage;
  88. FSelectTasksPage: TNewNotebookPage;
  89. FReadyPage: TNewNotebookPage;
  90. FPreparingPage: TNewNotebookPage;
  91. FInstallingPage: TNewNotebookPage;
  92. FInfoAfterPage: TNewNotebookPage;
  93. FDiskSpaceLabel: TNewStaticText;
  94. FDirEdit: TEdit;
  95. FGroupEdit: TNewEdit;
  96. FNoIconsCheck: TNewCheckBox;
  97. FPasswordLabel: TNewStaticText;
  98. FPasswordEdit: TPasswordEdit;
  99. FPasswordEditLabel: TNewStaticText;
  100. FReadyMemo: TNewMemo;
  101. FTypesCombo: TNewComboBox;
  102. FBevel: TBevel;
  103. FWizardBitmapImage: TBitmapImage;
  104. FWelcomeLabel1: TNewStaticText;
  105. FInfoBeforeMemo: TRichEditViewer;
  106. FInfoBeforeClickLabel: TNewStaticText;
  107. FMainPanel: TPanel;
  108. FBevel1: TBevel;
  109. FPageNameLabel: TNewStaticText;
  110. FPageDescriptionLabel: TNewStaticText;
  111. FWizardSmallBitmapImage: TBitmapImage;
  112. FReadyLabel: TNewStaticText;
  113. FFinishedLabel: TNewStaticText;
  114. FYesRadio: TNewRadioButton;
  115. FNoRadio: TNewRadioButton;
  116. FWizardBitmapImage2: TBitmapImage;
  117. FWelcomeLabel2: TNewStaticText;
  118. FLicenseLabel1: TNewStaticText;
  119. FLicenseMemo: TRichEditViewer;
  120. FInfoAfterMemo: TRichEditViewer;
  121. FInfoAfterClickLabel: TNewStaticText;
  122. FComponentsList: TNewCheckListBox;
  123. FComponentsDiskSpaceLabel: TNewStaticText;
  124. FBeveledLabel: TNewStaticText;
  125. FStatusLabel: TNewStaticText;
  126. FFilenameLabel: TNewStaticText;
  127. FProgressGauge: TNewProgressBar;
  128. FSelectDirLabel: TNewStaticText;
  129. FSelectStartMenuFolderLabel: TNewStaticText;
  130. FSelectComponentsLabel: TNewStaticText;
  131. FSelectTasksLabel: TNewStaticText;
  132. FLicenseAcceptedRadio: TNewRadioButton;
  133. FLicenseNotAcceptedRadio: TNewRadioButton;
  134. FUserInfoNameLabel: TNewStaticText;
  135. FUserInfoNameEdit: TNewEdit;
  136. FUserInfoOrgLabel: TNewStaticText;
  137. FUserInfoOrgEdit: TNewEdit;
  138. FPreparingErrorBitmapImage: TBitmapImage;
  139. FPreparingLabel: TNewStaticText;
  140. FFinishedHeadingLabel: TNewStaticText;
  141. FUserInfoSerialLabel: TNewStaticText;
  142. FUserInfoSerialEdit: TNewEdit;
  143. FTasksList: TNewCheckListBox;
  144. FRunList: TNewCheckListBox;
  145. FDirBrowseButton: TNewButton;
  146. FGroupBrowseButton: TNewButton;
  147. FSelectDirBitmapImage: TBitmapImage;
  148. FSelectGroupBitmapImage: TBitmapImage;
  149. FSelectDirBrowseLabel: TNewStaticText;
  150. FSelectStartMenuFolderBrowseLabel: TNewStaticText;
  151. FPreparingYesRadio: TNewRadioButton;
  152. FPreparingNoRadio: TNewRadioButton;
  153. FPreparingMemo: TNewMemo;
  154. procedure NextButtonClick(Sender: TObject);
  155. procedure BackButtonClick(Sender: TObject);
  156. procedure CancelButtonClick(Sender: TObject);
  157. procedure FormClose(Sender: TObject; var Action: TCloseAction);
  158. procedure NoIconsCheckClick(Sender: TObject);
  159. procedure TypesComboChange(Sender: TObject);
  160. procedure ComponentsListClickCheck(Sender: TObject);
  161. procedure LicenseAcceptedRadioClick(Sender: TObject);
  162. procedure LicenseNotAcceptedRadioClick(Sender: TObject);
  163. procedure UserInfoEditChange(Sender: TObject);
  164. procedure DirBrowseButtonClick(Sender: TObject);
  165. procedure GroupBrowseButtonClick(Sender: TObject);
  166. procedure FormResize(Sender: TObject);
  167. private
  168. { Private declarations }
  169. FPageList: TList;
  170. FCurPageID, FNextPageID: Integer;
  171. ExpandedDefaultDirName, ExpandedDefaultGroupName: String;
  172. FPrevAppDir, PrevGroup, PrevSetupType, PrevUserInfoName, PrevUserInfoOrg, PrevUserInfoSerial: String;
  173. PrevNoIcons: Boolean;
  174. PrevSelectedComponents, PrevDeselectedComponents: TStringList;
  175. PrevSelectedTasks, PrevDeselectedTasks: TStringList;
  176. DisableDirPage, DisableProgramGroupPage: Boolean;
  177. InitialSelectedComponents: TStringList;
  178. InitialSetupTypeIndex: Integer;
  179. HasLargeComponents: Boolean;
  180. DoneWithWizard: Boolean;
  181. PrepareToInstallNeedsRestart: Boolean;
  182. EnableAnchorOuterPagesOnResize: Boolean;
  183. EnableAdjustReadyLabelHeightOnResize: Boolean;
  184. FDownloadArchivesPage: TWizardPage; { TWizardPage to avoid circular reference. Is always a TDownloadWizardPage. }
  185. procedure AdjustFocus;
  186. procedure AnchorOuterPages;
  187. procedure CalcCurrentComponentsSpace;
  188. procedure ChangeReadyLabel(const S: String);
  189. function CheckSerialOk: Boolean;
  190. procedure CreateTaskButtons(const SelectedComponents: TStringList);
  191. procedure FindPreviousData;
  192. function GetPreviousPageID: Integer;
  193. function PrepareToInstall(const WizardComponents, WizardTasks: TStringList): String;
  194. function QueryRestartManager(const WizardComponents, WizardTasks: TStringList): String;
  195. procedure RegisterExistingPage(const ID: Integer;
  196. const AOuterNotebookPage, AInnerNotebookPage: TNewNotebookPage;
  197. const ACaption, ADescription: String);
  198. procedure SelectComponents(const SelectComponents, DeselectComponents: TStringList; const KeepFixedComponents: Boolean); overload;
  199. procedure SelectComponentsFromType(const TypeName: String; const OnlySelectFixedComponents: Boolean);
  200. procedure SelectTasks(const SelectTasks, DeselectTasks: TStringList); overload;
  201. function ShouldSkipPage(const PageID: Integer): Boolean;
  202. procedure UpdateComponentSizes;
  203. procedure UpdateComponentSizesEnum(Index: Integer; HasChildren: Boolean; Ext: LongInt);
  204. procedure UpdateCurPageButtonState;
  205. procedure UpdatePage(const PageID: Integer);
  206. procedure UpdateSelectTasksPage;
  207. procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
  208. procedure WMWindowPosChanging(var Message: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
  209. public
  210. { Public declarations }
  211. PrepareToInstallFailureMessage: String;
  212. constructor Create(AOwner: TComponent); override;
  213. destructor Destroy; override;
  214. procedure AddPage(const APage: TWizardPage; const AfterID: Integer);
  215. function AdjustLabelHeight(const ALabel: TNewStaticText): Integer;
  216. function AdjustLinkLabelHeight(const ALinkLabel: TNewLinkLabel): Integer;
  217. procedure CallCancelButtonClick(var ACancel, AConfirm: Boolean);
  218. procedure ChangeFinishedLabel(const S: String);
  219. procedure ClickToStartPage;
  220. procedure ClickThroughPages;
  221. procedure DirTreeRename(Sender: TCustomFolderTreeView; var NewName: string; var Accept: Boolean);
  222. procedure GetComponents(SelectedComponents, DeselectedComponents: TStringList);
  223. procedure GetSelectedComponents(Components: TStringList; const Descriptions, IndentDescriptions: Boolean);
  224. procedure GetSelectedTasks(Tasks: TStringList; const Descriptions, IndentDescriptions, GroupDescriptions: Boolean);
  225. function GetSetupType: PSetupTypeEntry;
  226. procedure GetTasks(SelectedTasks, DeselectedTasks: TStringList);
  227. procedure GroupTreeRename(Sender: TCustomFolderTreeView; var NewName: string; var Accept: Boolean);
  228. procedure IncTopDecHeight(const AControl: TControl; const Amount: Integer);
  229. function PageFromID(const ID: Integer): TWizardPage;
  230. function PageIndexFromID(const ID: Integer): Integer;
  231. procedure SetCurPage(const NewPageID: Integer);
  232. procedure SelectComponents(const ASelectComponents: TStringList); overload;
  233. procedure SelectTasks(const ASelectTasks: TStringList); overload;
  234. procedure FlipSizeAndCenterIfNeeded(const ACenterInsideControl: Boolean;
  235. const CenterInsideControlCtl: TWinControl; const CenterInsideControlInsideClientArea: Boolean); override;
  236. procedure UpdateRunList(const SelectedComponents, SelectedTasks: TStringList);
  237. function ValidateDirEdit: Boolean;
  238. function ValidateGroupEdit: Boolean;
  239. published
  240. property CurPageID: Integer read FCurPageID;
  241. property PrevAppDir: String read FPrevAppDir;
  242. property CancelButton: TNewButton read FCancelButton;
  243. property NextButton: TNewButton read FNextButton;
  244. property BackButton: TNewButton read FBackButton;
  245. property OuterNotebook: TNewNotebook read FOuterNotebook;
  246. property InnerNotebook: TNewNotebook read FInnerNotebook;
  247. property WelcomePage: TNewNotebookPage read FWelcomePage;
  248. property InnerPage: TNewNotebookPage read FInnerPage;
  249. property FinishedPage: TNewNotebookPage read FFinishedPage;
  250. property LicensePage: TNewNotebookPage read FLicensePage;
  251. property PasswordPage: TNewNotebookPage read FPasswordPage;
  252. property InfoBeforePage: TNewNotebookPage read FInfoBeforePage;
  253. property UserInfoPage: TNewNotebookPage read FUserInfoPage;
  254. property SelectDirPage: TNewNotebookPage read FSelectDirPage;
  255. property SelectComponentsPage: TNewNotebookPage read FSelectComponentsPage;
  256. property SelectProgramGroupPage: TNewNotebookPage read FSelectProgramGroupPage;
  257. property SelectTasksPage: TNewNotebookPage read FSelectTasksPage;
  258. property ReadyPage: TNewNotebookPage read FReadyPage;
  259. property PreparingPage: TNewNotebookPage read FPreparingPage;
  260. property InstallingPage: TNewNotebookPage read FInstallingPage;
  261. property InfoAfterPage: TNewNotebookPage read FInfoAfterPage;
  262. property DiskSpaceLabel: TNewStaticText read FDiskSpaceLabel;
  263. property DirEdit: TEdit read FDirEdit;
  264. property GroupEdit: TNewEdit read FGroupEdit;
  265. property NoIconsCheck: TNewCheckBox read FNoIconsCheck;
  266. property PasswordLabel: TNewStaticText read FPasswordLabel;
  267. property PasswordEdit: TPasswordEdit read FPasswordEdit;
  268. property PasswordEditLabel: TNewStaticText read FPasswordEditLabel;
  269. property ReadyMemo: TNewMemo read FReadyMemo;
  270. property TypesCombo: TNewComboBox read FTypesCombo;
  271. property Bevel: TBevel read FBevel;
  272. property WizardBitmapImage: TBitmapImage read FWizardBitmapImage;
  273. property WelcomeLabel1: TNewStaticText read FWelcomeLabel1;
  274. property InfoBeforeMemo: TRichEditViewer read FInfoBeforeMemo;
  275. property InfoBeforeClickLabel: TNewStaticText read FInfoBeforeClickLabel;
  276. property MainPanel: TPanel read FMainPanel;
  277. property Bevel1: TBevel read FBevel1;
  278. property PageNameLabel: TNewStaticText read FPageNameLabel;
  279. property PageDescriptionLabel: TNewStaticText read FPageDescriptionLabel;
  280. property WizardSmallBitmapImage: TBitmapImage read FWizardSmallBitmapImage;
  281. property ReadyLabel: TNewStaticText read FReadyLabel;
  282. property FinishedLabel: TNewStaticText read FFinishedLabel;
  283. property YesRadio: TNewRadioButton read FYesRadio;
  284. property NoRadio: TNewRadioButton read FNoRadio;
  285. property WizardBitmapImage2: TBitmapImage read FWizardBitmapImage2;
  286. property WelcomeLabel2: TNewStaticText read FWelcomeLabel2;
  287. property LicenseLabel1: TNewStaticText read FLicenseLabel1;
  288. property LicenseMemo: TRichEditViewer read FLicenseMemo;
  289. property InfoAfterMemo: TRichEditViewer read FInfoAfterMemo;
  290. property InfoAfterClickLabel: TNewStaticText read FInfoAfterClickLabel;
  291. property ComponentsList: TNewCheckListBox read FComponentsList;
  292. property ComponentsDiskSpaceLabel: TNewStaticText read FComponentsDiskSpaceLabel;
  293. property BeveledLabel: TNewStaticText read FBeveledLabel;
  294. property StatusLabel: TNewStaticText read FStatusLabel;
  295. property FilenameLabel: TNewStaticText read FFileNameLabel;
  296. property ProgressGauge: TNewProgressBar read FProgressGauge;
  297. property SelectDirLabel: TNewStaticText read FSelectDirLabel;
  298. property SelectStartMenuFolderLabel: TNewStaticText read FSelectStartMenuFolderLabel;
  299. property SelectComponentsLabel: TNewStaticText read FSelectComponentsLabel;
  300. property SelectTasksLabel: TNewStaticText read FSelectTasksLabel;
  301. property LicenseAcceptedRadio: TNewRadioButton read FLicenseAcceptedRadio;
  302. property LicenseNotAcceptedRadio: TNewRadioButton read FLicenseNotAcceptedRadio;
  303. property UserInfoNameLabel: TNewStaticText read FUserInfoNameLabel;
  304. property UserInfoNameEdit: TNewEdit read FUserInfoNameEdit;
  305. property UserInfoOrgLabel: TNewStaticText read FUserInfoOrgLabel;
  306. property UserInfoOrgEdit: TNewEdit read FUserInfoOrgEdit;
  307. property PreparingErrorBitmapImage: TBitmapImage read FPreparingErrorBitmapImage;
  308. property PreparingLabel: TNewStaticText read FPreparingLabel;
  309. property FinishedHeadingLabel: TNewStaticText read FFinishedHeadingLabel;
  310. property UserInfoSerialLabel: TNewStaticText read FUserInfoSerialLabel;
  311. property UserInfoSerialEdit: TNewEdit read FUserInfoSerialEdit;
  312. property TasksList: TNewCheckListBox read FTasksList;
  313. property RunList: TNewCheckListBox read FRunList;
  314. property DirBrowseButton: TNewButton read FDirBrowseButton;
  315. property GroupBrowseButton: TNewButton read FGroupBrowseButton;
  316. property SelectDirBitmapImage: TBitmapImage read FSelectDirBitmapImage;
  317. property SelectGroupBitmapImage: TBitmapImage read FSelectGroupBitmapImage;
  318. property SelectDirBrowseLabel: TNewStaticText read FSelectDirBrowseLabel;
  319. property SelectStartMenuFolderBrowseLabel: TNewStaticText read FSelectStartMenuFolderBrowseLabel;
  320. property PreparingYesRadio: TNewRadioButton read FPreparingYesRadio;
  321. property PreparingNoRadio: TNewRadioButton read FPreparingNoRadio;
  322. property PreparingMemo: TNewMemo read FPreparingMemo;
  323. end;
  324. var
  325. WizardForm: TWizardForm;
  326. function ExpandSetupMessage(const Msg: String): String; overload;
  327. function ExpandSetupMessage(const ID: TSetupMessageID): String; overload;
  328. function ListContains(const List: TStringList; const S: String): Boolean;
  329. procedure TidyUpDirName(var Path: String);
  330. procedure TidyUpGroupName(var Path: String);
  331. function ValidateCustomDirEdit(const AEdit: TEdit;
  332. const AllowUNCPath, AllowRootDirectory, AllowNetworkDrive: Boolean): Boolean;
  333. implementation
  334. uses
  335. ShellApi, ShlObj, Types, Generics.Collections,
  336. PathFunc, RestartManager, SHA256,
  337. SetupLdrAndSetup.Messages, Setup.MainForm, Setup.MainFunc, Shared.CommonFunc.Vcl,
  338. Shared.CommonFunc, Setup.InstFunc, Setup.SelectFolderForm, Setup.FileExtractor,
  339. Setup.LoggingFunc, Setup.ScriptRunner, Shared.SetupTypes, Shared.EncryptionFunc, Shared.SetupSteps,
  340. Setup.ScriptDlg, SetupLdrAndSetup.InstFunc, Setup.DownloadFileFunc;
  341. {$R *.DFM}
  342. const
  343. BadDirChars = '/:*?"<>|';
  344. var
  345. CurrentComponentsSpace: Int64;
  346. function IntToKBStr(const I: Int64): String;
  347. var
  348. X: Extended;
  349. begin
  350. X := Comp(I) / 1024;
  351. if Frac(X) > 0 then
  352. X := Int(X) + 1; { always round up }
  353. Result := Format('%.0n', [X]);
  354. end;
  355. function IntToMBStr(const I: Int64): String;
  356. var
  357. X: Extended;
  358. begin
  359. X := (Comp(I) / 1048576) * 10; { * 10 to include a decimal }
  360. if Frac(X) > 0 then
  361. X := Int(X) + 1; { always round up }
  362. X := X / 10;
  363. Result := Format('%.1n', [X]);
  364. end;
  365. function IntToGBStr(const I: Int64): String;
  366. var
  367. X: Extended;
  368. begin
  369. X := (Comp(I) / 1073741824) * 100; { * 100 to include 2 decimals }
  370. if Frac(X) > 0 then
  371. X := Int(X) + 1; { always round up }
  372. X := X / 100;
  373. Result := Format('%.2n', [X]);
  374. end;
  375. function ExpandSetupMessageEx(const Msg: String; const Space: Int64): String; overload;
  376. begin
  377. Result := Msg;
  378. {don't localize these}
  379. StringChange(Result, '[name]', ExpandedAppName);
  380. StringChange(Result, '[name/ver]', ExpandedAppVerName);
  381. StringChange(Result, '[kb]', IntToKBStr(Space));
  382. StringChange(Result, '[mb]', IntToMBStr(Space));
  383. StringChange(Result, '[gb]', IntToGBStr(Space));
  384. end;
  385. function ExpandSetupMessageEx(const ID: TSetupMessageID; const Space: Int64): String; overload;
  386. begin
  387. Result := ExpandSetupMessageEx(SetupMessages[ID], Space);
  388. end;
  389. function ExpandMBOrGBSetupMessage(const MBID, GBID: TSetupMessageID;
  390. const Space: Int64): String;
  391. begin
  392. if Space > 1048471142 then begin
  393. { Don't allow it to display 1000.0 MB or more. Takes the 'always round up' into account:
  394. 1048471142 bytes = 999.8999996185303 MB = '999.9 MB',
  395. 1048471143 bytes = 999.9000005722046 MB = '1,000.0 MB'. }
  396. Result := ExpandSetupMessageEx(GBID, Space)
  397. end else
  398. Result := ExpandSetupMessageEx(MBID, Space);
  399. end;
  400. function ExpandSetupMessage(const Msg: String): String; overload;
  401. begin
  402. Result := ExpandSetupMessageEx(Msg, MinimumSpace);
  403. end;
  404. function ExpandSetupMessage(const ID: TSetupMessageID): String; overload;
  405. begin
  406. Result := ExpandSetupMessageEx(ID, MinimumSpace);
  407. end;
  408. function ListContains(const List: TStringList; const S: String): Boolean;
  409. { Similar to "List.IndexOf(S) <> -1", except it uses CompareText instead of
  410. AnsiCompareText (which is locale-sensitive and thus unsuitable for our
  411. purposes). }
  412. var
  413. I: Integer;
  414. begin
  415. for I := 0 to List.Count-1 do
  416. if CompareText(List[I], S) = 0 then begin
  417. Result := True;
  418. Exit;
  419. end;
  420. Result := False;
  421. end;
  422. procedure TidyUpDirName(var Path: String);
  423. { Tidies up a directory name }
  424. begin
  425. { Trim spaces, normalize slashes, remove any trailing backslash, then repeat
  426. the process if necessary (e.g. in the 'C:\Program Files\My Program\ \'
  427. case) }
  428. repeat
  429. Path := RemoveBackslashUnlessRoot(PathNormalizeSlashes(Trim(Path)));
  430. until Length(Path) = Length(Trim(Path));
  431. end;
  432. procedure TidyUpGroupName(var Path: String);
  433. { Tidies up a program group name }
  434. begin
  435. { Trim spaces, remove leading/extra/trailing backslash(es), then repeat the
  436. process if necessary (e.g. in the '\ \My Program\ \' case) }
  437. repeat
  438. Path := Trim(Path);
  439. while (Path <> '') and PathCharIsSlash(Path[1]) do
  440. Delete(Path, 1, 1);
  441. Path := RemoveBackslash(PathNormalizeSlashes(Path));
  442. until Length(Path) = Length(Trim(Path));
  443. end;
  444. function ContainsControlCharacters(const S: String): Boolean;
  445. { Returns True if S contains any control characters (#0..#31) }
  446. var
  447. I: Integer;
  448. begin
  449. for I := 1 to Length(S) do
  450. if S[I] <= #31 then begin
  451. Result := True;
  452. Exit;
  453. end;
  454. Result := False;
  455. end;
  456. function PathComponentsContainTrailingSpaces(const S: String): Boolean;
  457. { Returns True if one or more components of the path contain trailing spaces,
  458. which are invalid in Win32. }
  459. var
  460. P: PChar;
  461. begin
  462. P := PChar(S);
  463. while P^ <> #0 do begin
  464. if (P^ = ' ') and ((P[1] = '\') or (P[1] = #0)) then begin
  465. Result := True;
  466. Exit;
  467. end;
  468. P := PathStrNextChar(P);
  469. end;
  470. Result := False;
  471. end;
  472. function PathComponentsContainInvalidDots(const S: String): Boolean;
  473. { Returns True if one or more components of the path contain only dots,
  474. i.e. '.', '..', '...', etc. One or two dots represent relative paths; three
  475. or more dots are invalid. }
  476. var
  477. P: PChar;
  478. HasDots: Boolean;
  479. begin
  480. P := PChar(S);
  481. while P^ <> #0 do begin
  482. { Skip over leading spaces; we want ' .' to return True also }
  483. while P^ = ' ' do
  484. Inc(P);
  485. HasDots := False;
  486. while P^ = '.' do begin
  487. HasDots := True;
  488. Inc(P);
  489. end;
  490. { Skip over trailing spaces; we want '. ' to return True also }
  491. while P^ = ' ' do
  492. Inc(P);
  493. if HasDots and ((P^ = '\') or (P^ = #0)) then begin
  494. Result := True;
  495. Exit;
  496. end;
  497. { Skip to next component }
  498. while (P^ <> #0) and (P^ <> '\') do
  499. P := PathStrNextChar(P);
  500. if P^ = '\' then
  501. Inc(P);
  502. end;
  503. Result := False;
  504. end;
  505. function SpaceString(const S: String): String;
  506. var
  507. I: Integer;
  508. begin
  509. Result := '';
  510. for I := 1 to Length(S) do begin
  511. if S[I] = ' ' then Continue;
  512. if Result <> '' then Result := Result + ' ';
  513. Result := Result + S[I];
  514. end;
  515. end;
  516. function TWizardForm.AdjustLabelHeight(const ALabel: TNewStaticText): Integer;
  517. { Increases or decreases a label's height so that all text fits.
  518. Returns the difference in height. }
  519. begin
  520. Result := ALabel.AdjustHeight;
  521. end;
  522. function TWizardForm.AdjustLinkLabelHeight(const ALinkLabel: TNewLinkLabel): Integer;
  523. begin
  524. Result := ALinkLabel.AdjustHeight;
  525. end;
  526. procedure TWizardForm.IncTopDecHeight(const AControl: TControl; const Amount: Integer);
  527. begin
  528. AControl.SetBounds(AControl.Left, AControl.Top + Amount,
  529. AControl.Width, AControl.Height - Amount);
  530. end;
  531. function TWizardForm.CheckSerialOk(): Boolean;
  532. begin
  533. if NeedSerial and (CodeRunner <> nil) then begin
  534. WizardUserInfoName := UserInfoNameEdit.Text;
  535. WizardUserInfoOrg := UserInfoOrgEdit.Text;
  536. WizardUserInfoSerial := UserInfoSerialEdit.Text;
  537. Result := CodeRunner.RunBooleanFunctions('CheckSerial', [UserInfoSerialEdit.Text], bcTrue, True, False)
  538. end else
  539. Result := True;
  540. end;
  541. procedure TWizardForm.CalcCurrentComponentsSpace();
  542. var
  543. SelectedComponents: TStringList;
  544. I: Integer;
  545. CurFile: PSetupFileEntry;
  546. begin
  547. CurrentComponentsSpace := SetupHeader.ExtraDiskSpaceRequired;
  548. SelectedComponents := TStringList.Create();
  549. GetSelectedComponents(SelectedComponents, False, False);
  550. //we can't simply sum component sizes because of shared files -> add file sizes
  551. for I := 0 to Entries[seFile].Count-1 do begin
  552. CurFile := PSetupFileEntry(Entries[seFile][I]);
  553. if (CurFile.Tasks = '') and (CurFile.Check = '') and {don't count tasks or scripted entries}
  554. ShouldProcessFileEntry(SelectedComponents, nil, CurFile, True) then begin
  555. with CurFile^ do begin
  556. if LocationEntry <> -1 then
  557. Inc(CurrentComponentsSpace, PSetupFileLocationEntry(Entries[seFileLocation][LocationEntry])^.OriginalSize)
  558. else
  559. Inc(CurrentComponentsSpace, ExternalSize)
  560. end;
  561. end;
  562. end;
  563. //don't forget to add extradiskspacerequired values
  564. for I := 0 to Entries[seComponent].Count-1 do
  565. with PSetupComponentEntry(Entries[seComponent][I])^ do
  566. if ListContains(SelectedComponents, Name) then
  567. Inc(CurrentComponentsSpace, ExtraDiskSpaceRequired);
  568. SelectedComponents.Free();
  569. ComponentsDiskSpaceLabel.Caption := ExpandMBOrGBSetupMessage(
  570. msgComponentsDiskSpaceMBLabel, msgComponentsDiskSpaceGBLabel, CurrentComponentsSpace);
  571. end;
  572. procedure TWizardForm.UpdateComponentSizesEnum(Index: Integer; HasChildren: Boolean; Ext: LongInt);
  573. begin
  574. const ComponentEntry = PSetupComponentEntry(ComponentsList.ItemObject[Index]);
  575. var ChildrenSize: Int64 := 0;
  576. if HasChildren then
  577. ComponentsList.EnumChildrenOf(Index, UpdateComponentSizesEnum, LongInt(@ChildrenSize));
  578. var ComponentSize := ComponentEntry.Size;
  579. Inc(ComponentSize, ChildrenSize);
  580. if ComponentsList.Checked[Index] then
  581. Inc(Int64(Pointer(Ext)^), ComponentSize);
  582. if ComponentSize <> 0 then begin
  583. if not HasLargeComponents then
  584. ComponentsList.ItemSubItem[Index] := FmtSetupMessage1(msgComponentSize1, IntToKBStr(ComponentSize))
  585. else
  586. ComponentsList.ItemSubItem[Index] := FmtSetupMessage1(msgComponentSize2, IntToMBStr(ComponentSize));
  587. end else
  588. ComponentsList.ItemSubItem[Index] := '';
  589. end;
  590. procedure TWizardForm.UpdateComponentSizes();
  591. begin
  592. if shShowComponentSizes in SetupHeader.Options then begin
  593. var Size: Int64 := 0;
  594. ComponentsList.EnumChildrenOf(-1, UpdateComponentSizesEnum, LongInt(@Size));
  595. end;
  596. end;
  597. { TWizardPage }
  598. constructor TWizardPage.Create(AOwner: TComponent);
  599. begin
  600. inherited;
  601. FWizardForm := AOwner as TWizardForm;
  602. end;
  603. destructor TWizardPage.Destroy;
  604. begin
  605. if Assigned(FWizardForm) and Assigned(FWizardForm.FPageList) then
  606. FWizardForm.FPageList.Remove(Self);
  607. inherited;
  608. end;
  609. procedure TWizardPage.Activate;
  610. begin
  611. if Assigned(FOnActivate) then
  612. FOnActivate(Self);
  613. end;
  614. procedure TWizardPage.BackButtonClick(var AContinue: Boolean);
  615. begin
  616. if Assigned(FOnBackButtonClick) then
  617. AContinue := FOnBackButtonClick(Self);
  618. end;
  619. procedure TWizardPage.CancelButtonClick(var ACancel, AConfirm: Boolean);
  620. begin
  621. if Assigned(FOnCancelButtonClick) then
  622. FOnCancelButtonClick(Self, ACancel, AConfirm);
  623. end;
  624. procedure TWizardPage.NextButtonClick(var AContinue: Boolean);
  625. begin
  626. if Assigned(FOnNextButtonClick) then
  627. AContinue := FOnNextButtonClick(Self);
  628. end;
  629. procedure TWizardPage.ShouldSkipPage(var AShouldSkip: Boolean);
  630. begin
  631. if Assigned(FOnShouldSkipPage) then
  632. AShouldSkip := FOnShouldSkipPage(Self);
  633. end;
  634. function TWizardPage.GetSurface: TNewNotebookPage;
  635. begin
  636. if FOuterNotebookPage = FWizardForm.InnerPage then
  637. Result := FInnerNotebookPage
  638. else
  639. Result := FOuterNotebookPage;
  640. end;
  641. function TWizardPage.GetSurfaceColor: TColor;
  642. begin
  643. Result := TNewNotebook(Surface.Parent).Color;
  644. end;
  645. function TWizardPage.GetSurfaceHeight: Integer;
  646. begin
  647. Result := Surface.Parent.Height;
  648. end;
  649. function TWizardPage.GetSurfaceWidth: Integer;
  650. begin
  651. Result := Surface.Parent.Width;
  652. end;
  653. procedure TWizardPage.SetCaption(const Value: String);
  654. begin
  655. FCaption := ExpandSetupMessage(Value);
  656. SyncCaptionAndDescription;
  657. end;
  658. procedure TWizardPage.SetDescription(const Value: String);
  659. begin
  660. FDescription := ExpandSetupMessage(Value);
  661. SyncCaptionAndDescription;
  662. end;
  663. procedure TWizardPage.SyncCaptionAndDescription;
  664. begin
  665. if FWizardForm.CurPageID = FID then begin
  666. FWizardForm.PageNameLabel.Caption := FCaption;
  667. FWizardForm.PageDescriptionLabel.Caption := FDescription;
  668. end;
  669. end;
  670. { TWizardForm }
  671. constructor TWizardForm.Create(AOwner: TComponent);
  672. { Do all initialization of the wizard form. We're overriding Create instead of
  673. using the FormCreate event, because if an exception is raised in FormCreate
  674. it's not propagated out. }
  675. function SelectBestImage(WizardImages: TWizardImages; TargetWidth, TargetHeight: Integer): TGraphic;
  676. var
  677. TargetArea, Difference, SmallestDifference, I: Integer;
  678. begin
  679. if WizardImages.Count <> 1 then begin
  680. { Find the image with the smallest area difference compared to the target area. }
  681. TargetArea := TargetWidth*TargetHeight;
  682. SmallestDifference := -1;
  683. Result := nil;
  684. for I := 0 to WizardImages.Count-1 do begin
  685. Difference := Abs(TargetArea-WizardImages[I].Width*WizardImages[I].Height);
  686. if (SmallestDifference = -1) or (Difference < SmallestDifference) then begin
  687. Result := WizardImages[I];
  688. SmallestDifference := Difference;
  689. end;
  690. end;
  691. end else
  692. Result := WizardImages[0];
  693. end;
  694. var
  695. X, W1, W2: Integer;
  696. SystemMenu: HMENU;
  697. P: String;
  698. I, DefaultSetupTypeIndex: Integer;
  699. IgnoreInitComponents: Boolean;
  700. TypeEntry: PSetupTypeEntry;
  701. ComponentEntry: PSetupComponentEntry;
  702. begin
  703. inherited;
  704. FPageList := TList.Create;
  705. InitialSelectedComponents := TStringList.Create();
  706. PrevSelectedComponents := TStringList.Create();
  707. PrevDeselectedComponents := TStringList.Create();
  708. PrevSelectedTasks := TStringList.Create();
  709. PrevDeselectedTasks := TStringList.Create();
  710. MainPanel.ParentBackground := False;
  711. { Not sure why the following is needed but various things related to
  712. positioning and anchoring don't work without this (you get positions of
  713. page controls back as if there was no anchoring until the page handle
  714. is automatically created. Cause might be related to the comment in
  715. TNewNotebook.AlignControls. }
  716. for I := 0 to OuterNotebook.PageCount-1 do
  717. OuterNotebook.Pages[I].HandleNeeded;
  718. for I := 0 to InnerNotebook.PageCount-1 do
  719. InnerNotebook.Pages[I].HandleNeeded;
  720. InitializeFont;
  721. SetFontNameSize(WelcomeLabel1.Font, LangOptions.WelcomeFontName,
  722. LangOptions.WelcomeFontSize, '', 12);
  723. WelcomeLabel1.Font.Style := [fsBold];
  724. PageNameLabel.Font.Style := [fsBold];
  725. if shDisableWelcomePage in SetupHeader.Options then
  726. Caption := FmtSetupMessage1(msgSetupWindowTitle, ExpandedAppVerName)
  727. else
  728. Caption := FmtSetupMessage1(msgSetupWindowTitle, ExpandedAppName);
  729. if shWizardResizable in SetupHeader.Options then begin
  730. const SaveClientWidth = ClientWidth;
  731. const SaveClientHeight = ClientHeight;
  732. BorderStyle := bsSizeable;
  733. ClientWidth := SaveClientWidth;
  734. ClientHeight := SaveClientHeight;
  735. EnableAnchorOuterPagesOnResize := True;
  736. { Do not allow user to resize it smaller than 100% nor larger than 150%. }
  737. Constraints.MinHeight := Height;
  738. Constraints.MinWidth := Width;
  739. Constraints.MaxHeight := MulDiv(Height, 150, 100);
  740. Constraints.MaxWidth := MulDiv(Width, 150, 100);
  741. end;
  742. { Position the buttons, and scale their size }
  743. W1 := CalculateButtonWidth([SetupMessages[msgButtonBack], SetupMessages[msgButtonCancel],
  744. SetupMessages[msgButtonFinish], SetupMessages[msgButtonInstall],
  745. SetupMessages[msgButtonNext]]); { width of each button }
  746. W2 := ScalePixelsX(10); { margin, and space between Next & Cancel }
  747. BackButton.Width := W1;
  748. NextButton.Width := W1;
  749. CancelButton.Width := W1;
  750. X := ClientWidth - W2 - W1;
  751. CancelButton.Left := X;
  752. Dec(X, W2);
  753. Dec(X, W1);
  754. NextButton.Left := X;
  755. Dec(X, W1);
  756. BackButton.Left := X;
  757. { Initialize wizard style }
  758. if SetupHeader.WizardStyle = wsModern then begin
  759. OuterNotebook.Color := clWindow;
  760. Bevel1.Visible := False;
  761. end;
  762. { Correct aspect ratio of the large wizard images after scaling }
  763. AnchorOuterPages;
  764. { Adjust small wizard image's size and position }
  765. begin
  766. { Make sure the control is still perfectly square after scaling and flush
  767. with the right edge of its parent }
  768. I := WizardSmallBitmapImage.Left;
  769. WizardSmallBitmapImage.Width := WizardSmallBitmapImage.Height;
  770. WizardSmallBitmapImage.Left := WizardSmallBitmapImage.Parent.ClientWidth -
  771. WizardSmallBitmapImage.Width;
  772. Dec(I, WizardSmallBitmapImage.Left);
  773. PageNameLabel.Width := PageNameLabel.Width - I;
  774. PageDescriptionLabel.Width := PageDescriptionLabel.Width - I;
  775. { Reduce the size of the control if appropriate:
  776. - If the user supplied a single image AND that image is not larger than
  777. the default control size before scaling (58x58), then reduce the
  778. control size to match the image dimensions. That avoids stretching to
  779. 58x58 when the user is purposely using a smaller-than-default image
  780. (such as 55x55 or 32x32) and WizardImageStretch=yes.
  781. - Otherwise, it's unclear what size/shape the user prefers for the
  782. control. Keep the default control size. }
  783. var NewWidth := WizardSmallImages[0].Width;
  784. var NewHeight := WizardSmallImages[0].Height;
  785. if (WizardSmallImages.Count > 1) or
  786. (NewWidth > 58) or
  787. (NewHeight > 58) then begin
  788. NewWidth := 58;
  789. NewHeight := 58;
  790. end;
  791. { Scale the new width and height }
  792. NewWidth := MulDiv(NewWidth, WizardSmallBitmapImage.Width, 58);
  793. NewHeight := MulDiv(NewHeight, WizardSmallBitmapImage.Height, 58);
  794. I := WizardSmallBitmapImage.Height - NewHeight;
  795. if I > 0 then begin
  796. WizardSmallBitmapImage.Height := WizardSmallBitmapImage.Height - I;
  797. WizardSmallBitmapImage.Top := WizardSmallBitmapImage.Top + (I div 2);
  798. end;
  799. I := WizardSmallBitmapImage.Width - NewWidth;
  800. if I > 0 then begin
  801. WizardSmallBitmapImage.Width := WizardSmallBitmapImage.Width - I;
  802. WizardSmallBitmapImage.Left := WizardSmallBitmapImage.Left + (I div 2);
  803. end;
  804. end;
  805. { Initialize images }
  806. WizardBitmapImage.Graphic := SelectBestImage(WizardImages, WizardBitmapImage.Width, WizardBitmapImage.Height);
  807. WizardBitmapImage.BackColor := SetupHeader.WizardImageBackColor;
  808. WizardBitmapImage.Center := True;
  809. WizardBitmapImage.Stretch := (shWizardImageStretch in SetupHeader.Options);
  810. WizardBitmapImage2.Bitmap := WizardBitmapImage.Bitmap;
  811. WizardBitmapImage2.BackColor := SetupHeader.WizardImageBackColor;
  812. WizardBitmapImage2.Center := True;
  813. WizardBitmapImage2.Stretch := (shWizardImageStretch in SetupHeader.Options);
  814. WizardSmallBitmapImage.Graphic := SelectBestImage(WizardSmallImages, WizardSmallBitmapImage.Width, WizardSmallBitmapImage.Height);
  815. WizardSmallBitmapImage.BackColor := SetupHeader.WizardSmallImageBackColor;
  816. WizardSmallBitmapImage.Stretch := (shWizardImageStretch in SetupHeader.Options);
  817. SelectDirBitmapImage.InitializeFromIcon(HInstance, 'Z_DIRICON', clNone, [32, 48, 64]); {don't localize}
  818. SelectGroupBitmapImage.InitializeFromIcon(HInstance, 'Z_GROUPICON', clNone, [32, 48, 64]); {don't localize}
  819. PreparingErrorBitmapImage.InitializeFromIcon(HInstance, 'Z_STOPICON', clNone, [16, 24, 32]); {don't localize}
  820. { Initialize wpWelcome page }
  821. RegisterExistingPage(wpWelcome, WelcomePage, nil, '', '');
  822. WelcomeLabel1.Caption := ExpandSetupMessage(msgWelcomeLabel1) + SNewLine;
  823. AdjustLabelHeight(WelcomeLabel1);
  824. IncTopDecHeight(WelcomeLabel2, (WelcomeLabel1.Top + WelcomeLabel1.Height) -
  825. WelcomeLabel2.Top);
  826. WelcomeLabel2.Caption := ExpandSetupMessage(msgWelcomeLabel2) + SNewLine2 +
  827. SetupMessages[msgClickNext];
  828. { Initialize wpLicense page }
  829. RegisterExistingPage(wpLicense, InnerPage, LicensePage,
  830. SetupMessages[msgWizardLicense], SetupMessages[msgLicenseLabel]);
  831. LicenseLabel1.Caption := ExpandSetupMessage(msgLicenseLabel3);
  832. I := AdjustLabelHeight(LicenseLabel1);
  833. IncTopDecHeight(LicenseMemo, I);
  834. LicenseAcceptedRadio.Caption := SetupMessages[msgLicenseAccepted];
  835. LicenseNotAcceptedRadio.Caption := SetupMessages[msgLicenseNotAccepted];
  836. { Initialize wpPassword page }
  837. RegisterExistingPage(wpPassword, InnerPage, PasswordPage,
  838. SetupMessages[msgWizardPassword], SetupMessages[msgPasswordLabel1]);
  839. PasswordLabel.Caption := SetupMessages[msgPasswordLabel3];
  840. PasswordEditLabel.Caption := SetupMessages[msgPasswordEditLabel];
  841. I := AdjustLabelHeight(PasswordLabel);
  842. PasswordEditLabel.Top := PasswordEditLabel.Top + I;
  843. Inc(I, AdjustLabelHeight(PasswordEditLabel));
  844. PasswordEdit.Top := PasswordEdit.Top + I;
  845. { Initialize wpInfoBefore page }
  846. RegisterExistingPage(wpInfoBefore, InnerPage, InfoBeforePage,
  847. SetupMessages[msgWizardInfoBefore], SetupMessages[msgInfoBeforeLabel]);
  848. InfoBeforeClickLabel.Caption := SetupMessages[msgInfoBeforeClickLabel];
  849. I := AdjustLabelHeight(InfoBeforeClickLabel);
  850. IncTopDecHeight(InfoBeforeMemo, I);
  851. { Initialize wpUserInfo page }
  852. RegisterExistingPage(wpUserInfo, InnerPage, UserInfoPage,
  853. SetupMessages[msgWizardUserInfo], SetupMessages[msgUserInfoDesc]);
  854. UserInfoNameLabel.Caption := SetupMessages[msgUserInfoName];
  855. I := AdjustLabelHeight(UserInfoNameLabel);
  856. UserInfoNameEdit.Top := UserInfoNameEdit.Top + I;
  857. UserInfoOrgLabel.Top := UserInfoOrgLabel.Top + I;
  858. UserInfoOrgLabel.Caption := SetupMessages[msgUserInfoOrg];
  859. Inc(I, AdjustLabelHeight(UserInfoOrgLabel));
  860. UserInfoOrgEdit.Top := UserInfoOrgEdit.Top + I;
  861. if NeedSerial then begin
  862. UserInfoSerialLabel.Top := UserInfoSerialLabel.Top + I;
  863. UserInfoSerialLabel.Caption := SetupMessages[msgUserInfoSerial];
  864. Inc(I, AdjustLabelHeight(UserInfoSerialLabel));
  865. UserInfoSerialEdit.Top := UserInfoSerialEdit.Top + I;
  866. end else begin
  867. UserInfoSerialLabel.Visible := False;
  868. UserInfoSerialEdit.Visible := False;
  869. end;
  870. { Initialize wpSelectDir page }
  871. RegisterExistingPage(wpSelectDir, InnerPage, SelectDirPage,
  872. SetupMessages[msgWizardSelectDir], ExpandSetupMessage(msgSelectDirDesc));
  873. SelectDirLabel.Caption := ExpandSetupMessage(msgSelectDirLabel3);
  874. X := SelectDirBitmapImage.Left + SelectDirBitmapImage.Width + ScalePixelsX(12);
  875. SelectDirLabel.SetBounds(X, SelectDirLabel.Top,
  876. SelectDirLabel.Width - (X - SelectDirLabel.Left), SelectDirLabel.Height);
  877. AdjustLabelHeight(SelectDirLabel);
  878. if SelectDirLabel.Height < SelectDirBitmapImage.Height then
  879. SelectDirLabel.Top := SelectDirLabel.Top +
  880. (SelectDirBitmapImage.Height - (SelectDirLabel.Height - 1)) div 2;
  881. SelectDirBrowseLabel.Caption := ExpandSetupMessage(msgSelectDirBrowseLabel);
  882. I := IntMax(
  883. SelectDirBitmapImage.Top + SelectDirBitmapImage.Height + ScalePixelsY(12),
  884. SelectDirLabel.Top + SelectDirLabel.Height - 1 + ScalePixelsY(13)) -
  885. SelectDirBrowseLabel.Top;
  886. SelectDirBrowseLabel.Top := SelectDirBrowseLabel.Top + I;
  887. Inc(I, AdjustLabelHeight(SelectDirBrowseLabel));
  888. DirEdit.Top := DirEdit.Top + I;
  889. TryEnableAutoCompleteFileSystem(DirEdit.Handle);
  890. DirBrowseButton.Caption := SetupMessages[msgButtonWizardBrowse];
  891. X := CalculateButtonWidth([SetupMessages[msgButtonWizardBrowse]]);
  892. DirBrowseButton.SetBounds(InnerNotebook.Width - X,
  893. DirBrowseButton.Top + I, X, DirBrowseButton.Height);
  894. DirEdit.Width := DirBrowseButton.Left - ScalePixelsX(10) - DirEdit.Left;
  895. DiskSpaceLabel.Caption := ExpandMBOrGBSetupMessage(
  896. msgDiskSpaceMBLabel, msgDiskSpaceGBLabel, MinimumSpace);
  897. DiskSpaceLabel.Top := DiskSpaceLabel.Top - AdjustLabelHeight(DiskSpaceLabel);
  898. { Initialize wpSelectComponents page }
  899. RegisterExistingPage(wpSelectComponents, InnerPage, SelectComponentsPage,
  900. SetupMessages[msgWizardSelectComponents], ExpandSetupMessage(msgSelectComponentsDesc));
  901. SelectComponentsLabel.Caption := ExpandSetupMessage(msgSelectComponentsLabel2);
  902. I := AdjustLabelHeight(SelectComponentsLabel);
  903. TypesCombo.Top := TypesCombo.Top + I;
  904. IncTopDecHeight(ComponentsList, I);
  905. ComponentsDiskSpaceLabel.Caption := ExpandMBOrGBSetupMessage(
  906. msgComponentsDiskSpaceMBLabel, msgComponentsDiskSpaceGBLabel, MinimumSpace);
  907. AdjustLabelHeight(ComponentsDiskSpaceLabel);
  908. if HasCustomType and (Entries[seType].Count = 1) then begin
  909. TypesCombo.Visible := False;
  910. IncTopDecHeight(ComponentsList, TypesCombo.Top - ComponentsList.Top);
  911. end;
  912. { Initialize wpSelectProgramGroup page }
  913. RegisterExistingPage(wpSelectProgramGroup, InnerPage, SelectProgramGroupPage,
  914. SetupMessages[msgWizardSelectProgramGroup], ExpandSetupMessage(msgSelectStartMenuFolderDesc));
  915. SelectStartMenuFolderLabel.Caption := ExpandSetupMessage(msgSelectStartMenuFolderLabel3);
  916. X := SelectGroupBitmapImage.Left + SelectGroupBitmapImage.Width + ScalePixelsX(12);
  917. SelectStartMenuFolderLabel.SetBounds(X, SelectStartMenuFolderLabel.Top,
  918. SelectStartMenuFolderLabel.Width - (X - SelectStartMenuFolderLabel.Left),
  919. SelectStartMenuFolderLabel.Height);
  920. AdjustLabelHeight(SelectStartMenuFolderLabel);
  921. if SelectStartMenuFolderLabel.Height < SelectGroupBitmapImage.Height then
  922. SelectStartMenuFolderLabel.Top := SelectStartMenuFolderLabel.Top +
  923. (SelectGroupBitmapImage.Height - (SelectStartMenuFolderLabel.Height - 1)) div 2;
  924. SelectStartMenuFolderBrowseLabel.Caption := ExpandSetupMessage(msgSelectStartMenuFolderBrowseLabel);
  925. I := IntMax(
  926. SelectGroupBitmapImage.Top + SelectGroupBitmapImage.Height + ScalePixelsY(12),
  927. SelectStartMenuFolderLabel.Top + SelectStartMenuFolderLabel.Height - 1 + ScalePixelsY(13)) -
  928. SelectStartMenuFolderBrowseLabel.Top;
  929. SelectStartMenuFolderBrowseLabel.Top := SelectStartMenuFolderBrowseLabel.Top + I;
  930. Inc(I, AdjustLabelHeight(SelectStartMenuFolderBrowseLabel));
  931. GroupEdit.Top := GroupEdit.Top + I;
  932. GroupBrowseButton.Caption := SetupMessages[msgButtonWizardBrowse];
  933. X := CalculateButtonWidth([SetupMessages[msgButtonWizardBrowse]]);
  934. GroupBrowseButton.SetBounds(InnerNotebook.Width - X,
  935. GroupBrowseButton.Top + I, X, GroupBrowseButton.Height);
  936. GroupEdit.Width := GroupBrowseButton.Left - ScalePixelsX(10) - GroupEdit.Left;
  937. NoIconsCheck.Caption := SetupMessages[msgNoProgramGroupCheck2];
  938. { Initialize wpSelectTasks page }
  939. RegisterExistingPage(wpSelectTasks, InnerPage, SelectTasksPage,
  940. SetupMessages[msgWizardSelectTasks], ExpandSetupMessage(msgSelectTasksDesc));
  941. SelectTasksLabel.Caption := ExpandSetupMessage(msgSelectTasksLabel2);
  942. I := AdjustLabelHeight(SelectTasksLabel);
  943. IncTopDecHeight(TasksList, I);
  944. TasksList.BorderStyle := bsNone;
  945. TasksList.MinItemHeight := ScalePixelsY(22);
  946. TasksList.ShowLines := shShowTasksTreeLines in SetupHeader.Options;
  947. { Initialize wpReady page }
  948. RegisterExistingPage(wpReady, InnerPage, ReadyPage,
  949. SetupMessages[msgWizardReady], ExpandSetupMessage(msgReadyLabel1));
  950. { Initialize wpPreparing page }
  951. RegisterExistingPage(wpPreparing, InnerPage, PreparingPage,
  952. SetupMessages[msgWizardPreparing], ExpandSetupMessage(msgPreparingDesc));
  953. { Initialize wpInstalling page }
  954. RegisterExistingPage(wpInstalling, InnerPage, InstallingPage,
  955. SetupMessages[msgWizardInstalling], ExpandSetupMessage(msgInstallingLabel));
  956. { Initialize wpInfoAfter page }
  957. RegisterExistingPage(wpInfoAfter, InnerPage, InfoAfterPage,
  958. SetupMessages[msgWizardInfoAfter], SetupMessages[msgInfoAfterLabel]);
  959. InfoAfterClickLabel.Caption := SetupMessages[msgInfoAfterClickLabel];
  960. I := AdjustLabelHeight(InfoAfterClickLabel);
  961. IncTopDecHeight(InfoAfterMemo, I);
  962. { Initialize wpFinished page }
  963. RegisterExistingPage(wpFinished, FinishedPage, nil, '', '');
  964. SetFontNameSize(FinishedHeadingLabel.Font, LangOptions.WelcomeFontName,
  965. LangOptions.WelcomeFontSize, '', 12);
  966. FinishedHeadingLabel.Font.Style := [fsBold];
  967. FinishedHeadingLabel.Caption := ExpandSetupMessage(msgFinishedHeadingLabel) +
  968. SNewLine;
  969. AdjustLabelHeight(FinishedHeadingLabel);
  970. FinishedLabel.Top := FinishedHeadingLabel.Top + FinishedHeadingLabel.Height;
  971. YesRadio.Caption := SetupMessages[msgYesRadio];
  972. NoRadio.Caption := SetupMessages[msgNoRadio];
  973. RunList.MinItemHeight := ScalePixelsY(22);
  974. { Initialize BeveledLabel }
  975. if SetupMessages[msgBeveledLabel] <> '' then
  976. BeveledLabel.Caption := ' ' + SetupMessages[msgBeveledLabel] + ' '
  977. else
  978. BeveledLabel.Caption := '';
  979. { Don't set UseRichEdit to True on the TRichEditViewers unless they are going
  980. to be used. There's no need to load riched*.dll unnecessarily. }
  981. if ActiveLicenseText <> '' then begin
  982. LicenseMemo.UseRichEdit := True;
  983. LicenseMemo.RTFText := ActiveLicenseText;
  984. end;
  985. if ActiveInfoBeforeText <> '' then begin
  986. InfoBeforeMemo.UseRichEdit := True;
  987. InfoBeforeMemo.RTFText := ActiveInfoBeforeText;
  988. end;
  989. if ActiveInfoAfterText <> '' then begin
  990. InfoAfterMemo.UseRichEdit := True;
  991. InfoAfterMemo.RTFText := ActiveInfoAfterText;
  992. end;
  993. { Append an 'About Setup' item to the wizard form also }
  994. SystemMenu := GetSystemMenu(Handle, False);
  995. AppendMenu(SystemMenu, MF_SEPARATOR, 0, nil);
  996. AppendMenu(SystemMenu, MF_STRING, 9999, PChar(SetupMessages[msgAboutSetupMenuItem]));
  997. { Read settings from a previous install if available }
  998. FindPreviousData;
  999. DisableDirPage := (SetupHeader.DisableDirPage = dpYes) or
  1000. ((SetupHeader.DisableDirPage = dpAuto) and (PrevAppDir <> ''));
  1001. DisableProgramGroupPage := (SetupHeader.DisableProgramGroupPage = dpYes) or
  1002. ((SetupHeader.DisableProgramGroupPage = dpAuto) and (PrevGroup <> ''));
  1003. DefaultSetupTypeIndex := -1; //assigned later
  1004. IgnoreInitComponents := False;
  1005. { Assign default user name & organization on User Info page }
  1006. if shUserInfoPage in SetupHeader.Options then begin
  1007. if PrevUserInfoName = '' then begin
  1008. UserInfoNameEdit.Text := ExpandConst(SetupHeader.DefaultUserInfoName);
  1009. UserInfoOrgEdit.Text := ExpandConst(SetupHeader.DefaultUserInfoOrg);
  1010. UserInfoSerialEdit.Text := ExpandConst(SetupHeader.DefaultUserInfoSerial);
  1011. end
  1012. else begin
  1013. UserInfoNameEdit.Text := PrevUserInfoName;
  1014. UserInfoOrgEdit.Text := PrevUserInfoOrg;
  1015. UserInfoSerialEdit.Text := PrevUserInfoSerial;
  1016. end;
  1017. end;
  1018. { Assign default directory name }
  1019. if shCreateAppDir in SetupHeader.Options then begin
  1020. ExpandedDefaultDirName := ExpandConst(SetupHeader.DefaultDirName);
  1021. if InitDir <> '' then
  1022. P := ExpandConstIfPrefixed(InitDir)
  1023. else begin
  1024. P := PrevAppDir;
  1025. if P = '' then
  1026. P := ExpandedDefaultDirName;
  1027. end;
  1028. P := RemoveBackslashUnlessRoot(PathExpand(P));
  1029. DirEdit.Text := P;
  1030. end
  1031. else
  1032. DirEdit.Text := WinDir;
  1033. { Fill types list and assign default type }
  1034. if Entries[seType].Count > 0 then begin
  1035. //first fill list
  1036. TypesCombo.Clear();
  1037. for I := 0 to Entries[seType].Count-1 do begin
  1038. TypeEntry := PSetupTypeEntry(Entries[seType][I]);
  1039. TypesCombo.Items.AddObject(ExpandConst(TypeEntry.Description), TObject(TypeEntry));
  1040. { If a setup type was specified on the command line, use it as default }
  1041. if (DefaultSetupTypeIndex = -1) and (InitSetupType <> '') and
  1042. (CompareText(TypeEntry.Name, InitSetupType) = 0) then begin
  1043. DefaultSetupTypeIndex := I;
  1044. { If components are specified as well, they should be ignored if the
  1045. setup type is non-custom }
  1046. if not (toIsCustom in TypeEntry.Options) then
  1047. IgnoreInitComponents := True;
  1048. end;
  1049. end;
  1050. { Use setup type from previous installation if no type was specified on the
  1051. command line (or if the type specified doesn't exist) }
  1052. if (DefaultSetupTypeIndex = -1) and (PrevSetupType <> '') then begin
  1053. for I := 0 to Entries[seType].Count-1 do begin
  1054. TypeEntry := PSetupTypeEntry(Entries[seType][I]);
  1055. if CompareText(TypeEntry.Name, PrevSetupType) = 0 then begin
  1056. DefaultSetupTypeIndex := I;
  1057. Break;
  1058. end;
  1059. end;
  1060. end;
  1061. //now assign default type
  1062. if DefaultSetupTypeIndex <> -1 then
  1063. TypesCombo.ItemIndex := DefaultSetupTypeIndex
  1064. else
  1065. TypesCombo.ItemIndex := 0;
  1066. end;
  1067. { Fill components list and assign default components}
  1068. //first fill list
  1069. ComponentsList.Clear();
  1070. ComponentsList.Flat := shFlatComponentsList in SetupHeader.Options;
  1071. for I := 0 to Entries[seComponent].Count-1 do begin
  1072. ComponentEntry := PSetupComponentEntry(Entries[seComponent][I]);
  1073. if coExclusive in ComponentEntry.Options then
  1074. ComponentsList.AddRadioButton(ExpandConst(ComponentEntry.Description), '', ComponentEntry.Level,
  1075. False, not (coFixed in ComponentEntry.Options), TObject(ComponentEntry))
  1076. else
  1077. ComponentsList.AddCheckBox(ExpandConst(ComponentEntry.Description), '', ComponentEntry.Level,
  1078. False, not (coFixed in ComponentEntry.Options), ComponentEntry.Used,
  1079. not (coDontInheritCheck in ComponentEntry.Options), TObject(ComponentEntry));
  1080. if ComponentEntry.Size >= 1024*1024 then
  1081. HasLargeComponents := True;
  1082. end;
  1083. //now assign default components
  1084. if not IgnoreInitComponents and InitComponentsSpecified and HasCustomType then begin
  1085. for I := 0 to Entries[seType].Count-1 do begin
  1086. TypeEntry := PSetupTypeEntry(Entries[seType][I]);
  1087. if toIsCustom in TypeEntry.Options then begin
  1088. TypesCombo.ItemIndex := I;
  1089. SelectComponentsFromType(TypeEntry.Name, True);
  1090. SelectComponents(InitComponents, nil, True);
  1091. Break;
  1092. end;
  1093. end;
  1094. end else begin
  1095. if DefaultSetupTypeIndex <> -1 then begin
  1096. TypeEntry := PSetupTypeEntry(Entries[seType][DefaultSetupTypeIndex]);
  1097. if toIsCustom in TypeEntry.Options then begin
  1098. //the previous setup type is a custom type: first select the default components
  1099. //for the default type (usually the full type). needed for new components.
  1100. SelectComponentsFromType(PSetupTypeEntry(Entries[seType][0]).Name, False);
  1101. //then select/deselect the custom type's fixed components
  1102. SelectComponentsFromType(TypeEntry.Name, True);
  1103. //now restore the customization
  1104. SelectComponents(PrevSelectedComponents, PrevDeselectedComponents, True);
  1105. end else begin
  1106. //this is not a custom type, so just select components based on the previous type
  1107. SelectComponentsFromType(TypeEntry.Name, False);
  1108. end;
  1109. end else if Entries[seType].Count > 0 then begin
  1110. TypeEntry := PSetupTypeEntry(Entries[seType][0]);
  1111. SelectComponentsFromType(TypeEntry.Name, False);
  1112. end;
  1113. end;
  1114. UpdateComponentSizes;
  1115. CalcCurrentComponentsSpace;
  1116. //Show or hide the components list based on the selected type
  1117. if HasCustomType then begin
  1118. TypeEntry := PSetupTypeEntry(Entries[seType][TypesCombo.ItemIndex]);
  1119. if (toIsCustom in TypeEntry.Options) or (shAlwaysShowComponentsList in SetupHeader.Options) then
  1120. ComponentsList.Visible := True
  1121. else
  1122. ComponentsList.Visible := False;
  1123. end else
  1124. ComponentsList.Visible := False;
  1125. ComponentsDiskSpaceLabel.Visible := ComponentsList.Visible;
  1126. //Store the initial setup type and components (only necessary if customizable)
  1127. if HasCustomType then begin
  1128. InitialSetupTypeIndex := TypesCombo.ItemIndex;
  1129. GetSelectedComponents(InitialSelectedComponents, False, False);
  1130. end;
  1131. { Assign default group name }
  1132. ExpandedDefaultGroupName := ExpandConst(SetupHeader.DefaultGroupName);
  1133. if (InitProgramGroup <> '') and not DisableProgramGroupPage then
  1134. { ^ InitProgramGroup currently isn't supported for installations with
  1135. DisableProgramGroupPage set. If the wizard page isn't displayed, it
  1136. doesn't get a chance to validate the program group name specified. }
  1137. P := ExpandConstIfPrefixed(InitProgramGroup)
  1138. else begin
  1139. if (PrevGroup = '') or (PrevGroup = '(Default)') then
  1140. P := ExpandedDefaultGroupName
  1141. else
  1142. P := PrevGroup;
  1143. end;
  1144. GroupEdit.Text := P;
  1145. if shAllowNoIcons in SetupHeader.Options then begin
  1146. if InitNoIcons or PrevNoIcons then
  1147. NoIconsCheck.Checked := True;
  1148. NoIconsCheck.Visible := True;
  1149. end
  1150. else
  1151. NoIconsCheck.Visible := False;
  1152. end;
  1153. procedure TWizardForm.AnchorOuterPages;
  1154. procedure AnchorOuterPage(const Page: TNewNotebookPage;
  1155. const BitmapImage: TBitmapImage);
  1156. var
  1157. ExpectedAnchors: TAnchors;
  1158. Ctl: TControl;
  1159. I, NewLeft, NewWidth: Integer;
  1160. begin
  1161. { BitmapImage's size is already corrected by the Anchors property but this
  1162. doesn't keep the aspect ratio. Calculate and set new width to restore the
  1163. aspect ratio and update all the other controls in the page for this. Don't
  1164. do this if [Code] made any change to BitmapImage's Visible, Align or Anchors
  1165. signalling that it wants a custom layout. }
  1166. if ControlsFlipped then
  1167. ExpectedAnchors := [akTop, akRight, akBottom]
  1168. else
  1169. ExpectedAnchors := [akLeft, akTop, akBottom];
  1170. if BitmapImage.Visible and (BitmapImage.Align = alNone) and (BitmapImage.Anchors = ExpectedAnchors) then begin
  1171. if BaseUnitX = 0 then
  1172. InternalError('AnchorOuterPage: BaseUnitX = 0');
  1173. NewWidth := MulDiv(BitmapImage.Height, 164, 314); //164x314 is the original bitmapimage size
  1174. if ControlsFlipped then
  1175. BitmapImage.Left := Page.ClientWidth - NewWidth;
  1176. BitmapImage.Width := NewWidth;
  1177. for I := 0 to Page.ControlCount-1 do begin
  1178. Ctl := Page.Controls[I];
  1179. if Ctl <> BitmapImage then begin
  1180. NewLeft := BitmapImage.Width + ScalePixelsX(12); //12 is original space between bitmapimage and controls
  1181. Ctl.Width := Page.ClientWidth - ScalePixelsX(20) - NewLeft; //20 is original space between controls and right border
  1182. if not ControlsFlipped then
  1183. Ctl.Left := NewLeft;
  1184. end;
  1185. end;
  1186. end;
  1187. end;
  1188. begin
  1189. AnchorOuterPage(WelcomePage, WizardBitmapImage);
  1190. AnchorOuterPage(FinishedPage, WizardBitmapImage2);
  1191. end;
  1192. procedure TWizardForm.FormResize(Sender: TObject);
  1193. begin
  1194. if EnableAnchorOuterPagesOnResize then
  1195. AnchorOuterPages;
  1196. if EnableAdjustReadyLabelHeightOnResize then
  1197. IncTopDecHeight(ReadyMemo, AdjustLabelHeight(ReadyLabel));
  1198. end;
  1199. procedure TWizardForm.FlipSizeAndCenterIfNeeded(const ACenterInsideControl: Boolean;
  1200. const CenterInsideControlCtl: TWinControl; const CenterInsideControlInsideClientArea: Boolean);
  1201. begin
  1202. if ShouldSizeX or ShouldSizeY then
  1203. EnableAnchorOuterPagesOnResize := True;
  1204. inherited;
  1205. end;
  1206. destructor TWizardForm.Destroy;
  1207. begin
  1208. FreeAndNil(PrevDeselectedComponents);
  1209. FreeAndNil(PrevSelectedTasks);
  1210. FreeAndNil(PrevDeselectedTasks);
  1211. FreeAndNil(PrevSelectedComponents);
  1212. FreeAndNil(InitialSelectedComponents);
  1213. FreeAndNil(FPageList);
  1214. FreeAndNil(FDownloadArchivesPage);
  1215. inherited;
  1216. end;
  1217. function TWizardForm.PageIndexFromID(const ID: Integer): Integer;
  1218. { Given a page ID, returns the index of the page in FPageList. An exception is
  1219. raised if a page with the specified ID is not found. }
  1220. var
  1221. I: Integer;
  1222. begin
  1223. for I := 0 to FPageList.Count-1 do begin
  1224. if TWizardPage(FPageList[I]).ID = ID then begin
  1225. Result := I;
  1226. Exit;
  1227. end;
  1228. end;
  1229. InternalError(Format('Could not find page with ID %d', [ID]));
  1230. Result := -1; { avoid compiler warning }
  1231. end;
  1232. function TWizardForm.PageFromID(const ID: Integer): TWizardPage;
  1233. begin
  1234. Result := FPageList[PageIndexFromID(ID)];
  1235. end;
  1236. procedure TWizardForm.RegisterExistingPage(const ID: Integer;
  1237. const AOuterNotebookPage, AInnerNotebookPage: TNewNotebookPage;
  1238. const ACaption, ADescription: String);
  1239. var
  1240. P: TWizardPage;
  1241. begin
  1242. FPageList.Expand;
  1243. P := TWizardPage.Create(Self);
  1244. P.FID := ID;
  1245. P.FOuterNotebookPage := AOuterNotebookPage;
  1246. P.FInnerNotebookPage := AInnerNotebookPage;
  1247. P.Caption := ACaption;
  1248. P.Description := ADescription;
  1249. FPageList.Add(P);
  1250. end;
  1251. procedure TWizardForm.AddPage(const APage: TWizardPage; const AfterID: Integer);
  1252. { Adds a new wizard page entry in FPageList, and an associated page in
  1253. InnerNotebook. AfterID specifies where the page should be inserted, or -1
  1254. which inserts the page at the end. }
  1255. var
  1256. InsertIndex: Integer;
  1257. NotebookPage: TNewNotebookPage;
  1258. begin
  1259. if AfterID <> -1 then
  1260. InsertIndex := PageIndexFromID(AfterID) + 1
  1261. else
  1262. InsertIndex := FPageList.Count;
  1263. FPageList.Expand;
  1264. Inc(FNextPageID);
  1265. if FNextPageID = 1 then
  1266. FNextPageID := 100;
  1267. NotebookPage := TNewNotebookPage.Create(APage);
  1268. NotebookPage.Notebook := InnerNotebook;
  1269. NotebookPage.HandleNeeded; { See TWizardForm.Create comment }
  1270. APage.FID := FNextPageID;
  1271. APage.FOuterNotebookPage := InnerPage;
  1272. APage.FInnerNotebookPage := NotebookPage;
  1273. FPageList.Insert(InsertIndex, APage);
  1274. end;
  1275. { Also see GetPreviousData in Main.pas }
  1276. procedure TWizardForm.FindPreviousData;
  1277. var
  1278. H: HKEY;
  1279. S, ExpandedAppId: String;
  1280. begin
  1281. ExpandedAppId := ExpandConst(SetupHeader.AppId);
  1282. if ExpandedAppId <> '' then begin
  1283. if RegOpenKeyExView(InstallDefaultRegView, InstallModeRootKey,
  1284. PChar(GetUninstallRegSubkeyName(GetUninstallRegKeyBaseName(ExpandedAppId))),
  1285. 0, KEY_QUERY_VALUE, H) = ERROR_SUCCESS then begin
  1286. try
  1287. { do not localize or change the following strings }
  1288. if shUsePreviousAppDir in SetupHeader.Options then
  1289. RegQueryStringValue(H, 'Inno Setup: App Path', FPrevAppDir);
  1290. if shUsePreviousGroup in SetupHeader.Options then begin
  1291. RegQueryStringValue(H, 'Inno Setup: Icon Group', PrevGroup);
  1292. if RegValueExists(H, 'Inno Setup: No Icons') then
  1293. PrevNoIcons := True;
  1294. end;
  1295. if shUsePreviousSetupType in SetupHeader.Options then begin
  1296. RegQueryStringValue(H, 'Inno Setup: Setup Type', PrevSetupType);
  1297. if RegQueryStringValue(H, 'Inno Setup: Selected Components', S) then
  1298. SetStringsFromCommaString(PrevSelectedComponents, S);
  1299. if RegQueryStringValue(H, 'Inno Setup: Deselected Components', S) then
  1300. SetStringsFromCommaString(PrevDeselectedComponents, S);
  1301. end;
  1302. if shUsePreviousTasks in SetupHeader.Options then begin
  1303. if RegQueryStringValue(H, 'Inno Setup: Selected Tasks', S) then
  1304. SetStringsFromCommaString(PrevSelectedTasks, S);
  1305. if RegQueryStringValue(H, 'Inno Setup: Deselected Tasks', S) then
  1306. SetStringsFromCommaString(PrevDeselectedTasks, S);
  1307. end;
  1308. if shUsePreviousUserInfo in SetupHeader.Options then begin
  1309. RegQueryStringValue(H, 'Inno Setup: User Info: Name', PrevUserInfoName);
  1310. RegQueryStringValue(H, 'Inno Setup: User Info: Organization', PrevUserInfoOrg);
  1311. RegQueryStringValue(H, 'Inno Setup: User Info: Serial', PrevUserInfoSerial);
  1312. end;
  1313. finally
  1314. RegCloseKey(H);
  1315. end;
  1316. end;
  1317. end;
  1318. end;
  1319. procedure TWizardForm.ChangeReadyLabel(const S: String);
  1320. begin
  1321. ReadyLabel.Caption := S;
  1322. IncTopDecHeight(ReadyMemo, AdjustLabelHeight(ReadyLabel));
  1323. EnableAdjustReadyLabelHeightOnResize := True;
  1324. end;
  1325. procedure TWizardForm.ChangeFinishedLabel(const S: String);
  1326. var
  1327. Y: Integer;
  1328. begin
  1329. FinishedLabel.Caption := S + SNewLine;
  1330. AdjustLabelHeight(FinishedLabel);
  1331. Y := FinishedLabel.Top + FinishedLabel.Height;
  1332. IncTopDecHeight(RunList, Y-YesRadio.Top);
  1333. YesRadio.Top := Y;
  1334. NoRadio.Top := Y + ScalePixelsY(22);
  1335. end;
  1336. procedure TWizardForm.UpdateRunList(const SelectedComponents, SelectedTasks: TStringList);
  1337. var
  1338. RunEntry: PSetupRunEntry;
  1339. Caption: String;
  1340. I: Integer;
  1341. begin
  1342. RunList.Items.Clear();
  1343. for I := 0 to Entries[seRun].Count-1 do begin
  1344. RunEntry := PSetupRunEntry(Entries[seRun][I]);
  1345. if (roPostInstall in RunEntry.Options) and ShouldProcessRunEntry(SelectedComponents, SelectedTasks, RunEntry) then begin
  1346. try
  1347. if RunEntry.Description <> '' then
  1348. Caption := ExpandConst(RunEntry.Description)
  1349. else if not(roShellExec in RunEntry.Options) then
  1350. Caption := FmtSetupMessage1(msgRunEntryExec, PathExtractName(ExpandConst(RunEntry.Name)))
  1351. else
  1352. Caption := FmtSetupMessage1(msgRunEntryShellExec, PathExtractName(ExpandConst(RunEntry.Name)));
  1353. except
  1354. { An exception here killing the entire Setup is not too desirable,
  1355. as post-install [Run] entries are normally unimportant. Just
  1356. display the message and move on. }
  1357. Application.HandleException(Self);
  1358. Caption := '[Error]';
  1359. end;
  1360. RunList.AddCheckBox(Caption, '', 0, not(roUnchecked in RunEntry.Options), True, True, True, TObject(I));
  1361. end;
  1362. end;
  1363. end;
  1364. procedure TWizardForm.CreateTaskButtons(const SelectedComponents: TStringList);
  1365. var
  1366. SaveSelectedTasks, SaveDeselectedTasks: TStringList;
  1367. LastShownTaskEntry, TaskEntry: PSetupTaskEntry;
  1368. NextAllowedLevel, I: Integer;
  1369. Description, GroupDescription: String;
  1370. LastGroupDescription: String;
  1371. begin
  1372. SaveDeselectedTasks := nil;
  1373. SaveSelectedTasks := TStringList.Create;
  1374. try
  1375. SaveDeselectedTasks := TStringList.Create;
  1376. { Save state of current items (if any) }
  1377. GetTasks(SaveSelectedTasks, SaveDeselectedTasks);
  1378. TasksList.Items.Clear();
  1379. LastGroupDescription := '';
  1380. { Create the task items with their default checked states }
  1381. NextAllowedLevel := 0;
  1382. LastShownTaskEntry := nil;
  1383. for I := 0 to Entries[seTask].Count-1 do begin
  1384. TaskEntry := PSetupTaskEntry(Entries[seTask][I]);
  1385. if (TaskEntry.Level <= NextAllowedLevel) and
  1386. (InstallOnThisVersion(TaskEntry.MinVersion, TaskEntry.OnlyBelowVersion) = irInstall) and
  1387. ShouldProcessEntry(SelectedComponents, nil, TaskEntry.Components, '', TaskEntry.Languages, TaskEntry.Check) then begin
  1388. Description := ExpandConst(TaskEntry.Description);
  1389. GroupDescription := ExpandConst(TaskEntry.GroupDescription);
  1390. { See if we should add a group label }
  1391. if (TaskEntry.Level = 0) and (GroupDescription <> LastGroupDescription) then begin
  1392. TasksList.AddGroup(GroupDescription, '', 0, nil);
  1393. LastGroupDescription := GroupDescription;
  1394. end;
  1395. { Create a check box or radio button }
  1396. if toExclusive in TaskEntry.Options then
  1397. TasksList.AddRadioButton(Description, '', TaskEntry.Level,
  1398. not InitDeselectAllTasks and not(toUnchecked in TaskEntry.Options),
  1399. True, TObject(TaskEntry))
  1400. else
  1401. TasksList.AddCheckBox(Description, '', TaskEntry.Level,
  1402. not InitDeselectAllTasks and not(toUnchecked in TaskEntry.Options),
  1403. True, TaskEntry.Used, not(toDontInheritCheck in TaskEntry.Options),
  1404. TObject(TaskEntry));
  1405. NextAllowedLevel := TaskEntry.Level + 1;
  1406. LastShownTaskEntry := TaskEntry;
  1407. end
  1408. else begin
  1409. { Not showing }
  1410. if Assigned(LastShownTaskEntry) and
  1411. (TaskEntry.Level = LastShownTaskEntry.Level) and
  1412. (CompareText(TaskEntry.Name, LastShownTaskEntry.Name) = 0) then begin
  1413. { It's a duplicate of the last shown item. Leave NextAllowedLevel
  1414. alone, so that any child items that follow can attach to the last
  1415. shown item. }
  1416. end
  1417. else begin
  1418. { Not a duplicate of the last shown item, so the next item must be
  1419. at the same level or less }
  1420. if NextAllowedLevel > TaskEntry.Level then
  1421. NextAllowedLevel := TaskEntry.Level;
  1422. { Clear LastShownTaskEntry so that no subsequent item can be
  1423. considered a duplicate of it. Needed in this case:
  1424. foo (shown)
  1425. foo\childA (not shown)
  1426. foo (not shown)
  1427. foo\childB
  1428. "foo\childB" should be hidden, not made a child of "foo" #1. }
  1429. LastShownTaskEntry := nil;
  1430. end;
  1431. end;
  1432. end;
  1433. { Restore the previous checked state of the items we just created }
  1434. if not InitDeselectAllTasks then begin
  1435. for I := 0 to TasksList.Items.Count-1 do begin
  1436. TaskEntry := PSetupTaskEntry(TasksList.ItemObject[I]);
  1437. if TaskEntry <> nil then begin
  1438. if ListContains(PrevSelectedTasks, TaskEntry.Name) then
  1439. TasksList.Checked[I] := not(toCheckedOnce in TaskEntry.Options)
  1440. else if ListContains(PrevDeselectedTasks, TaskEntry.Name) then
  1441. TasksList.Checked[I] := False;
  1442. end;
  1443. end;
  1444. end;
  1445. { Override previous state with tasks specified on the command line }
  1446. if InitTasks.Count > 0 then begin
  1447. for I := 0 to TasksList.Items.Count-1 do begin
  1448. TaskEntry := PSetupTaskEntry(TasksList.ItemObject[I]);
  1449. if TaskEntry <> nil then begin
  1450. if ListContains(InitTasks, '*' + TaskEntry.Name) then
  1451. TasksList.CheckItem(I, coCheckWithChildren)
  1452. else if ListContains(InitTasks, TaskEntry.Name) then
  1453. TasksList.Checked[I] := True
  1454. else if ListContains(InitTasks, '!' + TaskEntry.Name) then
  1455. TasksList.Checked[I] := False;
  1456. end;
  1457. end;
  1458. end;
  1459. { Finally, restore any saved state from when the page was last shown }
  1460. SelectTasks(SaveSelectedTasks, SaveDeselectedTasks);
  1461. finally
  1462. SaveDeselectedTasks.Free;
  1463. SaveSelectedTasks.Free;
  1464. end;
  1465. end;
  1466. function TWizardForm.GetSetupType(): PSetupTypeEntry;
  1467. var
  1468. Index: Integer;
  1469. begin
  1470. Index := TypesCombo.ItemIndex;
  1471. if Index <> -1 then
  1472. Result := PSetupTypeEntry(TypesCombo.Items.Objects[TypesCombo.ItemIndex])
  1473. else
  1474. Result := nil;
  1475. end;
  1476. procedure TWizardForm.SelectComponents(const SelectComponents, DeselectComponents: TStringList; const KeepFixedComponents: Boolean);
  1477. var
  1478. I: Integer;
  1479. ComponentEntry: PSetupComponentEntry;
  1480. begin
  1481. for I := 0 to Entries[seComponent].Count-1 do begin
  1482. ComponentEntry := PSetupComponentEntry(Entries[seComponent][I]);
  1483. if not (KeepFixedComponents and (coFixed in ComponentEntry.Options)) then begin
  1484. if SelectComponents <> nil then begin
  1485. if ListContains(SelectComponents, '*' + ComponentEntry.Name) then begin
  1486. ComponentsList.CheckItem(I, coCheckWithChildren);
  1487. Continue;
  1488. end;
  1489. if ListContains(SelectComponents, ComponentEntry.Name) then begin
  1490. ComponentsList.Checked[I] := True;
  1491. Continue;
  1492. end;
  1493. if ListContains(SelectComponents, '!' + ComponentEntry.Name) then begin
  1494. ComponentsList.Checked[I] := False;
  1495. Continue;
  1496. end;
  1497. end;
  1498. if DeselectComponents <> nil then begin
  1499. if ListContains(DeselectComponents, ComponentEntry.Name) then
  1500. ComponentsList.Checked[I] := False;
  1501. end;
  1502. end;
  1503. end;
  1504. end;
  1505. procedure TWizardForm.SelectComponents(const ASelectComponents: TStringList);
  1506. begin
  1507. SelectComponents(ASelectComponents, nil, False);
  1508. UpdateComponentSizes;
  1509. CalcCurrentComponentsSpace;
  1510. end;
  1511. procedure TWizardForm.SelectTasks(const SelectTasks, DeselectTasks: TStringList);
  1512. var
  1513. I: Integer;
  1514. TaskEntry: PSetupTaskEntry;
  1515. begin
  1516. for I := 0 to TasksList.Items.Count-1 do begin
  1517. TaskEntry := PSetupTaskEntry(TasksList.ItemObject[I]);
  1518. if TaskEntry <> nil then begin
  1519. if SelectTasks <> nil then begin
  1520. if ListContains(SelectTasks, TaskEntry.Name) then begin
  1521. TasksList.Checked[I] := True;
  1522. Continue;
  1523. end;
  1524. if ListContains(SelectTasks, '!' + TaskEntry.Name) then begin
  1525. TasksList.Checked[I] := False;
  1526. Continue;
  1527. end;
  1528. end;
  1529. if DeselectTasks <> nil then begin
  1530. if ListContains(DeselectTasks, TaskEntry.Name) then
  1531. TasksList.Checked[I] := False;
  1532. end;
  1533. end;
  1534. end;
  1535. end;
  1536. procedure TWizardForm.SelectTasks(const ASelectTasks: TStringList);
  1537. begin
  1538. SelectTasks(ASelectTasks, nil);
  1539. end;
  1540. procedure TWizardForm.SelectComponentsFromType(const TypeName: String; const OnlySelectFixedComponents: Boolean);
  1541. var
  1542. ComponentTypes: TStringList;
  1543. ComponentEntry: PSetupComponentEntry;
  1544. I: Integer;
  1545. begin
  1546. ComponentTypes := TStringList.Create();
  1547. for I := 0 to Entries[seComponent].Count-1 do begin
  1548. ComponentEntry := PSetupComponentEntry(Entries[seComponent][I]);
  1549. if not OnlySelectFixedComponents or (coFixed in ComponentEntry.Options) then begin
  1550. SetStringsFromCommaString(ComponentTypes, ComponentEntry.Types);
  1551. ComponentsList.Checked[I] := ListContains(ComponentTypes, TypeName);
  1552. end;
  1553. end;
  1554. ComponentTypes.Free();
  1555. end;
  1556. procedure TWizardForm.UpdateSelectTasksPage;
  1557. var
  1558. SelectedComponents: TStringList;
  1559. begin
  1560. SelectedComponents := TStringList.Create();
  1561. try
  1562. GetSelectedComponents(SelectedComponents, False, False);
  1563. CreateTaskButtons(SelectedComponents);
  1564. finally
  1565. SelectedComponents.Free();
  1566. end;
  1567. end;
  1568. procedure TWizardForm.GetSelectedComponents(Components: TStringList; const Descriptions, IndentDescriptions: Boolean);
  1569. function GetString(ComponentEntry: PSetupComponentEntry; Descriptions: Boolean): String;
  1570. begin
  1571. if Descriptions then begin
  1572. Result := ExpandConst(ComponentEntry.Description);
  1573. if IndentDescriptions then
  1574. Result := StringOfChar(' ', 3*ComponentEntry.Level) + Result;
  1575. end else
  1576. Result := ComponentEntry.Name;
  1577. end;
  1578. var
  1579. ComponentEntry: PSetupComponentEntry;
  1580. I: Integer;
  1581. begin
  1582. Components.Clear();
  1583. for I := 0 to ComponentsList.Items.Count-1 do begin
  1584. if ComponentsList.Checked[I] then begin
  1585. ComponentEntry := PSetupComponentEntry(ComponentsList.ItemObject[I]);
  1586. Components.Add(GetString(ComponentEntry, Descriptions));
  1587. end;
  1588. end;
  1589. end;
  1590. procedure TWizardForm.GetSelectedTasks(Tasks: TStringList; const Descriptions, IndentDescriptions, GroupDescriptions: Boolean);
  1591. function GetString(TaskEntry: PSetupTaskEntry; Descriptions, IndentDescriptions: Boolean; IndentLevel: Integer): String;
  1592. begin
  1593. if Descriptions then begin
  1594. Result := RemoveAccelChar(ExpandConst(TaskEntry.Description));
  1595. if IndentDescriptions then
  1596. Result := StringOfChar(' ', 3*IndentLevel) + Result;
  1597. end else
  1598. Result := TaskEntry.Name;
  1599. end;
  1600. var
  1601. TaskEntry: PSetupTaskEntry;
  1602. I, IndentLevel: Integer;
  1603. GroupDescription, LastGroupDescription: String;
  1604. begin
  1605. Tasks.Clear();
  1606. if GroupDescriptions then
  1607. LastGroupDescription := '';
  1608. for I := 0 to TasksList.Items.Count-1 do begin
  1609. if TasksList.Checked[I] and (TasksList.ItemObject[I] <> nil) then begin
  1610. TaskEntry := PSetupTaskEntry(TasksList.ItemObject[I]);
  1611. if GroupDescriptions then begin
  1612. GroupDescription := ExpandConst(TaskEntry.GroupDescription);
  1613. if (TaskEntry.Level = 0) and (GroupDescription <> LastGroupDescription) then begin
  1614. if GroupDescription <> '' then
  1615. Tasks.Add(RemoveAccelChar(GroupDescription));
  1616. LastGroupDescription := GroupDescription;
  1617. end;
  1618. IndentLevel := TaskEntry.Level;
  1619. if LastGroupDescription <> '' then
  1620. Inc(IndentLevel);
  1621. end else
  1622. IndentLevel := TaskEntry.Level;
  1623. Tasks.Add(GetString(TaskEntry, Descriptions, IndentDescriptions, IndentLevel));
  1624. end;
  1625. end;
  1626. end;
  1627. procedure TWizardForm.GetComponents(SelectedComponents, DeselectedComponents: TStringList);
  1628. { Gets names of components that are currently selected and deselected }
  1629. var
  1630. I: Integer;
  1631. ComponentEntry: PSetupComponentEntry;
  1632. begin
  1633. SelectedComponents.Clear;
  1634. if DeselectedComponents <> nil then
  1635. DeselectedComponents.Clear;
  1636. for I := 0 to ComponentsList.Items.Count-1 do begin
  1637. ComponentEntry := PSetupComponentEntry(ComponentsList.ItemObject[I]);
  1638. if ComponentsList.Checked[I] then
  1639. SelectedComponents.Add(ComponentEntry.Name)
  1640. else if DeselectedComponents <> nil then
  1641. DeselectedComponents.Add(ComponentEntry.Name);
  1642. end;
  1643. end;
  1644. procedure TWizardForm.GetTasks(SelectedTasks, DeselectedTasks: TStringList);
  1645. { Gets names of tasks that are currently selected and deselected }
  1646. var
  1647. I: Integer;
  1648. TaskEntry: PSetupTaskEntry;
  1649. begin
  1650. SelectedTasks.Clear;
  1651. if DeselectedTasks <> nil then
  1652. DeselectedTasks.Clear;
  1653. for I := 0 to TasksList.Items.Count-1 do begin
  1654. TaskEntry := PSetupTaskEntry(TasksList.ItemObject[I]);
  1655. if TaskEntry <> nil then begin
  1656. if TasksList.Checked[I] then
  1657. SelectedTasks.Add(TaskEntry.Name)
  1658. else if DeselectedTasks <> nil then
  1659. DeselectedTasks.Add(TaskEntry.Name);
  1660. end;
  1661. end;
  1662. end;
  1663. function TWizardForm.PrepareToInstall(const WizardComponents, WizardTasks: TStringList): String;
  1664. procedure ShowPreparing;
  1665. begin
  1666. SetCurPage(wpPreparing);
  1667. BackButton.Visible := False;
  1668. NextButton.Visible := False;
  1669. CancelButton.Enabled := False;
  1670. if InstallMode = imSilent then
  1671. WizardForm.Visible := True;
  1672. WizardForm.Update;
  1673. end;
  1674. function GetClearedDownloadArchivesPage: TDownloadWizardPage;
  1675. begin
  1676. if FDownloadArchivesPage = nil then begin
  1677. Result := TDownloadWizardPage.Create(Self);
  1678. try
  1679. Result.Caption := SetupMessages[msgWizardPreparing];
  1680. Result.Description := SetupMessages[msgPreparingDesc];
  1681. Result.ShowBaseNameInsteadOfUrl := True;
  1682. AddPage(Result, -1);
  1683. Result.Initialize;
  1684. FDownloadArchivesPage := Result;
  1685. except
  1686. FreeAndNil(Result);
  1687. raise;
  1688. end;
  1689. end else begin
  1690. Result := FDownloadArchivesPage as TDownloadWizardPage;
  1691. Result.Clear;
  1692. end;
  1693. end;
  1694. function AskRetryDownloadArchivesToExtract(const LastBaseNameOrUrl, Failed: String): Integer;
  1695. begin
  1696. const LastOperation = SetupMessages[msgErrorDownloading];
  1697. const Text = LastBaseNameOrUrl + SNewLine2 + LastOperation + SNewLine + Failed;
  1698. Result := LoggedTaskDialogMsgBox('', SetupMessages[msgRetryCancelSelectAction], Text, '',
  1699. mbError, MB_RETRYCANCEL, [SetupMessages[msgRetryCancelRetry], SetupMessages[msgRetryCancelCancel]],
  1700. 0, True, IDCANCEL);
  1701. if (Result <> IDRETRY) and (Result <> IDCANCEL) then begin
  1702. Log('LoggedTaskDialogMsgBox returned an unexpected value. Assuming Cancel.');
  1703. Result := IDCANCEL;
  1704. end;
  1705. end;
  1706. procedure DownloadArchivesToExtract(const SelectedComponents, SelectedTasks: TStringList);
  1707. begin
  1708. var DownloadPage: TDownloadWizardPage := nil;
  1709. for var I := 0 to Entries[seFile].Count-1 do begin
  1710. const FileEntry: PSetupFileEntry = Entries[seFile][I];
  1711. if (foDownload in FileEntry.Options) and (foExtractArchive in FileEntry.Options) and
  1712. ShouldProcessFileEntry(SelectedComponents, SelectedTasks, FileEntry, False) then begin
  1713. if DownloadPage = nil then
  1714. DownloadPage := GetClearedDownloadArchivesPage;
  1715. if not(foCustomDestName in FileEntry.Options) then
  1716. InternalError('Expected CustomDestName flag');
  1717. { Prepare }
  1718. const TempDir = AddBackslash(TempInstallDir);
  1719. const DestDir = GenerateUniqueName(False, TempDir + '_isetup', '.tmp');
  1720. const DestFile = AddBackslash(DestDir) + PathExtractName(FileEntry.DestName);
  1721. const BaseName = Copy(DestFile, Length(TempDir)+1, MaxInt);
  1722. { Add to DownloadPage }
  1723. const Url = ExpandConst(FileEntry.SourceFilename);
  1724. const UserName = ExpandConst(FileEntry.DownloadUserName);
  1725. const Password = ExpandConst(FileEntry.DownloadPassword);
  1726. if FileEntry.Verification.Typ = fvISSig then begin
  1727. const ISSigUrl = GetISSigUrl(Url, ExpandConst(FileEntry.DownloadISSigSource));
  1728. DownloadPage.AddExWithISSigVerify(Url, ISSigUrl, BaseName, UserName, Password,
  1729. FileEntry.Verification.ISSigAllowedKeys, I)
  1730. end else begin
  1731. var RequiredSHA256OfFile: String;
  1732. if FileEntry.Verification.Typ = fvHash then
  1733. RequiredSHA256OfFile := SHA256DigestToString(FileEntry.Verification.Hash)
  1734. else
  1735. RequiredSHA256OfFile := '';
  1736. DownloadPage.AddEx(Url, BaseName, RequiredSHA256OfFile, UserName, Password, I);
  1737. end;
  1738. end;
  1739. end;
  1740. if DownloadPage <> nil then begin
  1741. ShowPreparing;
  1742. DownloadPage.Show;
  1743. try
  1744. var Failed, LastBaseNameOrUrl: String;
  1745. repeat
  1746. Failed := '';
  1747. LastBaseNameOrUrl := '';
  1748. try
  1749. DownloadPage.Download(procedure(const DownloadedFile: TDownloadFile; const DestFile: String; var Remove: Boolean)
  1750. begin
  1751. if not DownloadedFile.DotISSigEntry then begin { Check for the extra entries which download .issig }
  1752. const FileEntry: PSetupFileEntry = Entries[seFile][DownloadedFile.Data];
  1753. FileEntry.SourceFilename := DestFile;
  1754. { Remove Download flag since download has been done, and remove CustomDestName flag
  1755. since ExtractArchive flag doesn't like that }
  1756. FileEntry.Options := FileEntry.Options - [foDownload, foCustomDestName];
  1757. { DestName should now not include a filename, see TSetupCompiler.EnumFilesProc.ProcessFileList }
  1758. FileEntry.DestName := PathExtractPath(FileEntry.DestName);
  1759. FileEntry.Verification.Typ := fvNone;
  1760. end;
  1761. { Tell DownloadPage to not download this file again on retry. Without this it would
  1762. redownload files that don't use verification. }
  1763. Remove := True;
  1764. end);
  1765. except
  1766. if DownloadPage.AbortedByUser then
  1767. raise; { This is a regular exception and not EAbort (which is what we want) }
  1768. Failed := GetExceptMessage;
  1769. LastBaseNameOrUrl := DownloadPage.LastBaseNameOrUrl;
  1770. end;
  1771. until (Failed = '') or (AskRetryDownloadArchivesToExtract(LastBaseNameOrUrl, Failed) = IDCANCEL);
  1772. if Failed <> '' then
  1773. raise Exception.Create(Failed);
  1774. finally
  1775. DownloadPage.Hide;
  1776. UpdateCurPageButtonState;
  1777. end;
  1778. end;
  1779. end;
  1780. var
  1781. CodeNeedsRestart: Boolean;
  1782. Y: Integer;
  1783. begin
  1784. Result := '';
  1785. PrepareToInstallNeedsRestart := False;
  1786. PreparingErrorBitmapImage.Visible := False;
  1787. PreparingLabel.Visible := False;
  1788. PreparingYesRadio.Visible := False;
  1789. PreparingNoRadio.Visible := False;
  1790. PreparingMemo.Visible := False;
  1791. try
  1792. DownloadArchivesToExtract(WizardComponents, WizardTasks);
  1793. except
  1794. Result := GetExceptMessage;
  1795. end;
  1796. if Result = '' then begin
  1797. if not PreviousInstallCompleted(WizardComponents, WizardTasks) then begin
  1798. Result := ExpandSetupMessage(msgPreviousInstallNotCompleted);
  1799. PrepareToInstallNeedsRestart := True;
  1800. end else if (CodeRunner <> nil) and CodeRunner.FunctionExists('PrepareToInstall', True) then begin
  1801. ShowPreparing;
  1802. try
  1803. DownloadTemporaryFileOrExtractArchiveProcessMessages := True;
  1804. CodeNeedsRestart := False;
  1805. Result := CodeRunner.RunStringFunctions('PrepareToInstall', [@CodeNeedsRestart], bcNonEmpty, True, '');
  1806. PrepareToInstallNeedsRestart := (Result <> '') and CodeNeedsRestart;
  1807. finally
  1808. DownloadTemporaryFileOrExtractArchiveProcessMessages := False;
  1809. UpdateCurPageButtonState;
  1810. end;
  1811. if WindowState <> wsMinimized then { VCL bug workaround }
  1812. Application.BringToFront;
  1813. end;
  1814. end;
  1815. if Result <> '' then begin
  1816. if PrepareToInstallNeedsRestart then
  1817. PreparingLabel.Caption := Result +
  1818. SNewLine + SNewLine + SNewLine + ExpandSetupMessage(msgPrepareToInstallNeedsRestart) + SNewLine
  1819. else
  1820. PreparingLabel.Caption := Result +
  1821. SNewLine + SNewLine + SNewLine + SetupMessages[msgCannotContinue];
  1822. AdjustLabelHeight(PreparingLabel);
  1823. PreparingErrorBitmapImage.Visible := True;
  1824. PreparingLabel.Visible := True;
  1825. if PrepareToInstallNeedsRestart then begin
  1826. Y := PreparingLabel.Top + PreparingLabel.Height;
  1827. PreparingYesRadio.Top := Y;
  1828. PreparingYesRadio.Anchors := [akLeft, akTop, akRight];
  1829. PreparingYesRadio.Caption := SetupMessages[msgYesRadio];
  1830. PreparingYesRadio.Visible := True;
  1831. PreparingNoRadio.Top := Y + ScalePixelsY(22);
  1832. PreparingNoRadio.Anchors := [akLeft, akTop, akRight];
  1833. PreparingNoRadio.Caption := SetupMessages[msgNoRadio];
  1834. PreparingNoRadio.Visible := True;
  1835. end;
  1836. end;
  1837. end;
  1838. function TWizardForm.QueryRestartManager(const WizardComponents, WizardTasks: TStringList): String;
  1839. procedure CheckAndAddRebootReasonToString(var S: String; const RebootReasons, RebootReason: Integer; const RebootReasonString: String);
  1840. begin
  1841. if (RebootReasons and RebootReason) <> 0 then begin
  1842. if S <> '' then
  1843. S := S + '+';
  1844. S := S + RebootReasonString;
  1845. end;
  1846. end;
  1847. function RebootReasonsToString(const RebootReasons: Integer): String;
  1848. var
  1849. UnknownReasons: Integer;
  1850. begin
  1851. Result := '';
  1852. if RebootReasons <> RmRebootReasonNone then begin
  1853. CheckAndAddRebootReasonToString(Result, RebootReasons, RmRebootReasonPermissionDenied, 'Permission Denied');
  1854. CheckAndAddRebootReasonToString(Result, RebootReasons, RmRebootReasonSessionMismatch, 'Session Mismatch');
  1855. CheckAndAddRebootReasonToString(Result, RebootReasons, RmRebootReasonCriticalProcess, 'Critical Process');
  1856. CheckAndAddRebootReasonToString(Result, RebootReasons, RmRebootReasonCriticalService, 'Critical Service');
  1857. CheckAndAddRebootReasonToString(Result, RebootReasons, RmRebootReasonDetectedSelf, 'Detected Self');
  1858. UnknownReasons := RebootReasons and not (RmRebootReasonNone or RmRebootReasonPermissionDenied or
  1859. RmRebootReasonSessionMismatch or RmRebootReasonCriticalProcess or
  1860. RmRebootReasonCriticalService or RmRebootReasonDetectedSelf);
  1861. CheckAndAddRebootReasonToString(Result, RebootReasons, UnknownReasons, Format('Unknown Reason(s) %d', [UnknownReasons]));
  1862. Result := ': ' + Result;
  1863. end;
  1864. Result := IntToStr(RebootReasons) + Result;
  1865. end;
  1866. type
  1867. TArrayOfProcessInfo = array[0..(MaxInt div SizeOf(RM_PROCESS_INFO))-1] of RM_PROCESS_INFO;
  1868. PArrayOfProcessInfo = ^TArrayOfProcessInfo;
  1869. var
  1870. Y, I: Integer;
  1871. ProcessInfosCount, ProcessInfosCountNeeded, RebootReasons: Integer;
  1872. ProcessInfos: PArrayofProcessInfo;
  1873. AppName: String;
  1874. begin
  1875. { Clear existing registered resources if we get here a second time (user clicked Back after first time). There
  1876. doesn't seem to be function to do this directly, so restart the session instead. }
  1877. if RmRegisteredFilesCount <> 0 then begin
  1878. RmEndSession(RmSessionHandle);
  1879. if RmStartSession(@RmSessionHandle, 0, RmSessionKey) <> ERROR_SUCCESS then
  1880. RmSessionStarted := False;
  1881. end;
  1882. if RmSessionStarted then
  1883. RegisterResourcesWithRestartManager(WizardComponents, WizardTasks); { This will update RmSessionStarted and RmRegisteredFilesCount }
  1884. if RmSessionStarted then begin
  1885. LogFmt('Found %d files to register with RestartManager.', [RmRegisteredFilesCount]);
  1886. if RmRegisteredFilesCount > 0 then begin
  1887. ProcessInfosCount := 0;
  1888. ProcessInfosCountNeeded := 5; { Start with 5 to hopefully avoid a realloc }
  1889. ProcessInfos := nil;
  1890. try
  1891. Log('Calling RestartManager''s RmGetList.');
  1892. while ProcessInfosCount < ProcessInfosCountNeeded do begin
  1893. if ProcessInfos <> nil then
  1894. FreeMem(ProcessInfos);
  1895. GetMem(ProcessInfos, ProcessInfosCountNeeded * SizeOf(ProcessInfos[0]));
  1896. ProcessInfosCount := ProcessInfosCountNeeded;
  1897. if not RmGetList(RmSessionHandle, @ProcessInfosCountNeeded, @ProcessInfosCount, ProcessInfos, @RebootReasons) in [ERROR_SUCCESS, ERROR_MORE_DATA] then begin
  1898. RmEndSession(RmSessionHandle);
  1899. RmSessionStarted := False;
  1900. Break;
  1901. end;
  1902. end;
  1903. if RmSessionStarted then begin
  1904. Log('RmGetList finished successfully.');
  1905. if ProcessInfosCount > 0 then begin
  1906. for I := 0 to ProcessInfosCount-1 do begin
  1907. AppName := WideCharToString(ProcessInfos[I].strAppName);
  1908. LogFmt('RestartManager found an application using one of our files: %s', [AppName]);
  1909. if RebootReasons = RmRebootReasonNone then begin
  1910. if Result <> '' then
  1911. Result := Result + #13#10;
  1912. Result := Result + AppName;
  1913. end;
  1914. end;
  1915. LogFmt('Can use RestartManager to avoid reboot? %s (%s)', [SYesNo[RebootReasons = RmRebootReasonNone], RebootReasonsToString(RebootReasons)]);
  1916. end else
  1917. Log('RestartManager found no applications using one of our files.');
  1918. end else
  1919. Log('RmGetList failed.');
  1920. finally
  1921. if ProcessInfos <> nil then
  1922. FreeMem(ProcessInfos);
  1923. end;
  1924. end;
  1925. end;
  1926. if Result <> '' then begin
  1927. if InitRestartApplications or
  1928. ((shRestartApplications in SetupHeader.Options) and not InitNoRestartApplications) then
  1929. PreparingLabel.Caption := SetupMessages[msgApplicationsFound2]
  1930. else
  1931. PreparingLabel.Caption := SetupMessages[msgApplicationsFound];
  1932. Y := PreparingLabel.Top + PreparingLabel.Height + ScalePixelsY(12);
  1933. PreparingMemo.Top := Y;
  1934. IncTopDecHeight(PreparingMemo, AdjustLabelHeight(PreparingLabel));
  1935. AdjustLabelHeight(PreparingLabel);
  1936. PreparingErrorBitmapImage.Visible := True;
  1937. PreparingLabel.Visible := True;
  1938. PreparingMemo.Text := Result;
  1939. PreparingMemo.Visible := True;
  1940. Y := PreparingMemo.Top + PreparingMemo.Height + ScalePixelsY(12);
  1941. PreparingYesRadio.Top := Y;
  1942. PreparingYesRadio.Anchors := [akLeft, akRight, akBottom];
  1943. PreparingYesRadio.Caption := SetupMessages[msgCloseApplications];
  1944. PreparingYesRadio.Visible := True;
  1945. PreparingNoRadio.Top := Y + ScalePixelsY(22);
  1946. PreparingNoRadio.Anchors := [akLeft, akRight, akBottom];
  1947. PreparingNoRadio.Caption := SetupMessages[msgDontCloseApplications];
  1948. PreparingNoRadio.Visible := True;
  1949. end;
  1950. end;
  1951. procedure TWizardForm.UpdatePage(const PageID: Integer);
  1952. procedure ReadyMemoAppend(const Lines: String);
  1953. begin
  1954. if Lines <> '' then begin
  1955. if ReadyMemo.Lines.Count > 0 then
  1956. ReadyMemo.Lines.Append('');
  1957. ReadyMemo.Lines.Append(Lines);
  1958. end;
  1959. end;
  1960. procedure UpdateReadyPage;
  1961. const
  1962. Space = ' ';
  1963. var
  1964. TypeEntry: PSetupTypeEntry;
  1965. SelectedComponents, SelectedTasks: TStringList;
  1966. S, MemoUserInfoInfo, MemoDirInfo, MemoGroupInfo, MemoTypeInfo, MemoComponentsInfo, MemoTasksInfo: String;
  1967. I: Integer;
  1968. begin
  1969. ReadyMemo.Visible := False;
  1970. if not (shDisableReadyMemo in SetupHeader.Options) then begin
  1971. ReadyMemo.Lines.Clear();
  1972. if shUserInfoPage in SetupHeader.Options then begin
  1973. MemoUserInfoInfo := SetupMessages[msgReadyMemoUserInfo];
  1974. MemoUserInfoInfo := MemoUserInfoInfo+SNewLine+Space+UserInfoNameEdit.Text;
  1975. if UserInfoOrgEdit.Text <> '' then
  1976. MemoUserInfoInfo := MemoUserInfoInfo+SNewLine+Space+UserInfoOrgEdit.Text;
  1977. end;
  1978. if (shAlwaysShowDirOnReadyPage in SetupHeader.Options) or
  1979. (not DisableDirPage and
  1980. (shCreateAppDir in SetupHeader.Options)) then begin
  1981. MemoDirInfo := SetupMessages[msgReadyMemoDir];
  1982. MemoDirInfo := MemoDirInfo+SNewLine+Space+DirEdit.Text;
  1983. end else
  1984. MemoDirInfo := '';
  1985. if HasComponents then begin
  1986. TypeEntry := GetSetupType();
  1987. if TypeEntry <> nil then begin
  1988. MemoTypeInfo := SetupMessages[msgReadyMemoType];
  1989. MemoTypeInfo := MemoTypeInfo+SNewLine+Space+ExpandConst(TypeEntry.Description);
  1990. end else
  1991. MemoTypeInfo := ''; { can get here if all types failed their Check }
  1992. SelectedComponents := TStringList.Create();
  1993. GetSelectedComponents(SelectedComponents, True, True);
  1994. if SelectedComponents.Count > 0 then begin
  1995. MemoComponentsInfo := SetupMessages[msgReadyMemoComponents];
  1996. for I := 0 to SelectedComponents.Count-1 do
  1997. MemoComponentsInfo := MemoComponentsInfo+SNewLine+Space+SelectedComponents[I];
  1998. end else
  1999. MemoComponentsInfo := '';
  2000. SelectedComponents.Free();
  2001. end;
  2002. if HasIcons and not NoIconsCheck.Checked and
  2003. ((shAlwaysShowGroupOnReadyPage in SetupHeader.Options) or
  2004. not DisableProgramGroupPage) then begin
  2005. MemoGroupInfo := SetupMessages[msgReadyMemoGroup];
  2006. MemoGroupInfo := MemoGroupInfo+SNewLine+Space+GroupEdit.Text;
  2007. end else
  2008. MemoGroupInfo := '';
  2009. SelectedTasks := TStringList.Create();
  2010. GetSelectedTasks(SelectedTasks, True, True, True);
  2011. if SelectedTasks.Count > 0 then begin
  2012. MemoTasksInfo := SetupMessages[msgReadyMemoTasks];
  2013. for I := 0 to SelectedTasks.Count-1 do
  2014. MemoTasksInfo := MemoTasksInfo+SNewLine+Space+SelectedTasks[I];
  2015. end else
  2016. MemoTasksInfo := '';
  2017. SelectedTasks.Free();
  2018. if (CodeRunner <> nil) and CodeRunner.FunctionExists('UpdateReadyMemo', True) then begin
  2019. try
  2020. ReadyMemo.Lines.Text := CodeRunner.RunStringFunctions('UpdateReadyMemo',
  2021. [Space, SNewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  2022. MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo], bcNonEmpty, True, '');
  2023. except
  2024. Application.HandleException(Self);
  2025. end;
  2026. end else begin
  2027. ReadyMemoAppend(MemoUserInfoInfo);
  2028. ReadyMemoAppend(MemoDirInfo);
  2029. ReadyMemoAppend(MemoTypeInfo);
  2030. ReadyMemoAppend(MemoComponentsInfo);
  2031. ReadyMemoAppend(MemoGroupInfo);
  2032. ReadyMemoAppend(MemoTasksInfo);
  2033. end;
  2034. ReadyMemo.SelStart := 0;
  2035. ReadyMemo.SelLength := 0;
  2036. end;
  2037. if ReadyMemo.Lines.Count > 0 then begin
  2038. S := SetupMessages[msgReadyLabel2a];
  2039. ChangeReadyLabel(S);
  2040. ReadyMemo.Visible := True;
  2041. end else begin
  2042. S := SetupMessages[msgReadyLabel2b];
  2043. ChangeReadyLabel(S);
  2044. end;
  2045. end;
  2046. begin
  2047. case PageID of
  2048. wpSelectTasks: UpdateSelectTasksPage;
  2049. wpReady: UpdateReadyPage;
  2050. end;
  2051. end;
  2052. procedure TWizardForm.AdjustFocus;
  2053. var
  2054. NewActiveControl: TWinControl;
  2055. begin
  2056. if CurPageID = wpReady then
  2057. NewActiveControl := NextButton
  2058. else if (CurPageID = wpPreparing) and (PrepareToInstallFailureMessage <> '') and not PrepareToInstallNeedsRestart then
  2059. NewActiveControl := CancelButton
  2060. else if (CurPageID = wpPreparing) and (PrepareToInstallFailureMessage = '') and PreparingYesRadio.CanFocus then
  2061. NewActiveControl := PreparingYesRadio
  2062. else
  2063. NewActiveControl := FindNextControl(nil, True, True, False);
  2064. if (NewActiveControl = BackButton) and NextButton.CanFocus then
  2065. NewActiveControl := NextButton;
  2066. ActiveControl := NewActiveControl;
  2067. end;
  2068. function TWizardForm.GetPreviousPageID: Integer;
  2069. { Finds ID of previous page (not counting skipped pages), or -1 if there is
  2070. no previous page to return to. }
  2071. var
  2072. CurPageIndex, I: Integer;
  2073. begin
  2074. CurPageIndex := PageIndexFromID(CurPageID);
  2075. for I := CurPageIndex-1 downto 0 do begin
  2076. Result := TWizardPage(FPageList[I]).ID;
  2077. { Never go back to wpInstalling }
  2078. if Result = wpInstalling then
  2079. Break;
  2080. if not ShouldSkipPage(Result) then
  2081. Exit;
  2082. end;
  2083. Result := -1;
  2084. end;
  2085. procedure TWizardForm.UpdateCurPageButtonState;
  2086. var
  2087. PageIndex: Integer;
  2088. Page: TWizardPage;
  2089. Flags: UINT;
  2090. begin
  2091. PageIndex := PageIndexFromID(CurPageID);
  2092. Page := FPageList[PageIndex];
  2093. if not(psNoButtons in Page.Style) then begin
  2094. BackButton.Visible := (CurPageID <> wpInstalling) and (GetPreviousPageID <> -1);
  2095. NextButton.Visible := CurPageID <> wpInstalling;
  2096. case CurPageID of
  2097. wpLicense: NextButton.Enabled := LicenseAcceptedRadio.Checked;
  2098. wpPreparing: NextButton.Enabled := (PrepareToInstallFailureMessage = '') or PrepareToInstallNeedsRestart;
  2099. else
  2100. NextButton.Enabled := True;
  2101. end;
  2102. CancelButton.Visible := (PageIndex <= PageIndexFromID(wpInstalling)) and
  2103. not ((CurPageID = wpPreparing) and PrepareToInstallNeedsRestart);
  2104. CancelButton.Enabled := (CurPageID <> wpInstalling) or
  2105. ((shAllowCancelDuringInstall in SetupHeader.Options) and not InitNoCancel);
  2106. end
  2107. else begin
  2108. BackButton.Visible := False;
  2109. NextButton.Visible := False;
  2110. CancelButton.Visible := False;
  2111. end;
  2112. { Set the enabled state of the close button to match the Cancel button }
  2113. if CancelButton.CanFocus then
  2114. Flags := 0
  2115. else
  2116. Flags := MF_GRAYED;
  2117. EnableMenuItem(GetSystemMenu(Handle, False), SC_CLOSE, MF_BYCOMMAND or Flags);
  2118. end;
  2119. procedure TWizardForm.SetCurPage(const NewPageID: Integer);
  2120. { Changes which page is currently visible }
  2121. begin
  2122. const OldCurPageID = CurPageID;
  2123. const Page = PageFromID(NewPageID);
  2124. FCurPageID := NewPageID;
  2125. { Select the page in the notebooks }
  2126. if Assigned(Page.InnerNotebookPage) then
  2127. InnerNotebook.ActivePage := Page.InnerNotebookPage;
  2128. OuterNotebook.ActivePage := Page.OuterNotebookPage;
  2129. { Set the page description }
  2130. Page.SyncCaptionAndDescription;
  2131. BeveledLabel.Visible := (BeveledLabel.Caption <> '') and
  2132. not(CurPageID in [wpWelcome, wpFinished]);
  2133. { Set button visibility and captions }
  2134. UpdateCurPageButtonState;
  2135. BackButton.Caption := SetupMessages[msgButtonBack];
  2136. if CurPageID = wpReady then begin
  2137. NextButton.Caption := SetupMessages[msgButtonInstall];
  2138. CancelButton.Caption := SetupMessages[msgButtonCancel];
  2139. end else if ((CurPageID = wpPreparing) and PrepareToInstallNeedsRestart) or (CurPageID = wpFinished) then begin
  2140. NextButton.Caption := SetupMessages[msgButtonFinish];
  2141. CancelButton.Caption := SetupMessages[msgButtonCancel];
  2142. end else begin
  2143. NextButton.Caption := SetupMessages[msgButtonNext];
  2144. CancelButton.Caption := SetupMessages[msgButtonCancel];
  2145. end;
  2146. { Adjust focus }
  2147. AdjustFocus;
  2148. { If on the wpUserInfo page, check the serial now, after the rest of the
  2149. page is initialized in case the event function happens to display a
  2150. message box or raise an exception }
  2151. if CurPageID = wpUserInfo then begin
  2152. try
  2153. NextButton.Enabled := CheckSerialOk();
  2154. except
  2155. NextButton.Enabled := False;
  2156. Application.HandleException(Self);
  2157. end;
  2158. end;
  2159. try
  2160. PageFromID(CurPageID).Activate;
  2161. except
  2162. Application.HandleException(Self);
  2163. end;
  2164. try
  2165. if (CodeRunner <> nil) and (CurPageID <> OldCurPageID) then
  2166. CodeRunner.RunProcedures('CurPageChanged', [CurPageID], False);
  2167. except
  2168. Application.HandleException(Self);
  2169. end;
  2170. end;
  2171. function TWizardForm.ShouldSkipPage(const PageID: Integer): Boolean;
  2172. begin
  2173. if (PageID = wpReady) and not Visible then begin
  2174. Result := False;
  2175. Exit;
  2176. end;
  2177. Result :=
  2178. (psAlwaysSkip in PageFromID(PageID).Style) or
  2179. ((PageID = wpWelcome) and (shDisableWelcomePage in SetupHeader.Options)) or
  2180. ((PageID = wpLicense) and ((ActiveLicenseText = '') or (InstallMode <> imNormal))) or
  2181. ((PageID = wpPassword) and not NeedPassword) or
  2182. ((PageID = wpInfoBefore) and (ActiveInfoBeforeText = '')) or
  2183. ((PageID = wpUserInfo) and not(shUserInfoPage in SetupHeader.Options)) or
  2184. ((PageID = wpSelectDir) and (DisableDirPage or not(shCreateAppDir in SetupHeader.Options))) or
  2185. ((PageID = wpSelectComponents) and not HasComponents) or
  2186. ((PageID = wpSelectProgramGroup) and (DisableProgramGroupPage or not HasIcons)) or
  2187. ((PageID = wpSelectTasks) and (TasksList.Items.Count = 0)) or
  2188. ((PageID = wpReady) and (shDisableReadyPage in SetupHeader.Options)) or
  2189. ((PageID = wpPreparing)) or
  2190. ((PageID = wpInfoAfter) and (ActiveInfoAfterText = '')) or
  2191. ((PageID = wpFinished) and (shDisableFinishedPage in SetupHeader.Options) and not (NeedsRestart and not InitNoRestart));
  2192. if not Result and not (PageID in [wpPreparing]) then begin
  2193. try
  2194. PageFromID(PageID).ShouldSkipPage(Result);
  2195. except
  2196. Application.HandleException(Self);
  2197. end;
  2198. if not Result then begin
  2199. try
  2200. if CodeRunner <> nil then
  2201. Result := CodeRunner.RunBooleanFunctions('ShouldSkipPage', [PageID], bcTrue, False, Result);
  2202. except
  2203. Application.HandleException(Self);
  2204. end;
  2205. end;
  2206. end;
  2207. end;
  2208. procedure TWizardForm.NextButtonClick(Sender: TObject);
  2209. function CheckPassword: Boolean;
  2210. { Also see MainFunc.HandleInitPassword }
  2211. begin
  2212. Result := False;
  2213. var S := PasswordEdit.Text;
  2214. var Timer: TOneShotTimer;
  2215. Timer.Start(750); { See comment below }
  2216. var CryptKey: TSetupEncryptionKey;
  2217. var SaveCursor := GetCursor;
  2218. SetCursor(LoadCursor(0, IDC_WAIT));
  2219. try
  2220. GenerateEncryptionKey(S, SetupEncryptionHeader.KDFSalt, SetupEncryptionHeader.KDFIterations, CryptKey);
  2221. finally
  2222. SetCursor(SaveCursor);
  2223. end;
  2224. if shPassword in SetupHeader.Options then
  2225. Result := TestPassword(CryptKey, SetupEncryptionHeader.BaseNonce, SetupEncryptionHeader.PasswordTest);
  2226. if not Result and (CodeRunner <> nil) then
  2227. Result := CodeRunner.RunBooleanFunctions('CheckPassword', [S], bcTrue, False, Result);
  2228. if Result then begin
  2229. NeedPassword := False;
  2230. if SetupEncryptionHeader.EncryptionUse = euFiles then
  2231. FileExtractor.CryptKey := CryptKey;
  2232. PasswordEdit.Text := '';
  2233. end else begin
  2234. { Ensure a total time of 750 ms when an incorrect password is entered to
  2235. discourage brute-force attempts }
  2236. if Visible then begin
  2237. SaveCursor := GetCursor;
  2238. SetCursor(LoadCursor(0, IDC_WAIT));
  2239. try
  2240. Timer.SleepUntilExpired;
  2241. finally
  2242. SetCursor(SaveCursor);
  2243. end;
  2244. end;
  2245. LoggedMsgBox(SetupMessages[msgIncorrectPassword], '', mbError, MB_OK, True, IDOK);
  2246. if Visible then begin
  2247. PasswordEdit.Text := '';
  2248. PasswordEdit.SetFocus;
  2249. end;
  2250. end;
  2251. end;
  2252. function CheckUserInfoPage: Boolean;
  2253. begin
  2254. UserInfoNameEdit.Text := Trim(UserInfoNameEdit.Text);
  2255. UserInfoOrgEdit.Text := Trim(UserInfoOrgEdit.Text);
  2256. { Note: We don't require a user name to be entered on silent installs,
  2257. since the default value in the registry could be blank (at least this
  2258. was the case for one user). }
  2259. Result := (UserInfoNameEdit.Text <> '') or (InstallMode <> imNormal);
  2260. if Result then begin
  2261. WizardUserInfoName := UserInfoNameEdit.Text;
  2262. WizardUserInfoOrg := UserInfoOrgEdit.Text;
  2263. WizardUserInfoSerial := UserInfoSerialEdit.Text;
  2264. end
  2265. else begin
  2266. LoggedMsgBox(SetupMessages[msgUserInfoNameRequired], '', mbError, MB_OK, True, IDOK);
  2267. if Visible then
  2268. UserInfoNameEdit.SetFocus;
  2269. end;
  2270. end;
  2271. function CheckSelectDirPage: Boolean;
  2272. var
  2273. T: String;
  2274. FreeSpace, TotalSpace: Integer64;
  2275. begin
  2276. Result := False;
  2277. if not ValidateDirEdit then
  2278. Exit;
  2279. T := DirEdit.Text;
  2280. if InstallMode = imNormal then begin
  2281. { Check if there's enough free disk space }
  2282. if GetSpaceOnNearestMountPoint(False, T, FreeSpace, TotalSpace) then begin
  2283. if Compare64(FreeSpace, MinimumSpace) < 0 then
  2284. { If not, show warning }
  2285. if LoggedMsgBox(FmtSetupMessage(msgDiskSpaceWarning,
  2286. [IntToKBStr(MinimumSpace), IntToKBStr(FreeSpace)]),
  2287. SetupMessages[msgDiskSpaceWarningTitle],
  2288. mbConfirmation, MB_YESNO or MB_DEFBUTTON2, True, IDYES) <> IDYES then
  2289. Exit;
  2290. end;
  2291. { Check if directory already exists }
  2292. if ((SetupHeader.DirExistsWarning = ddYes) or
  2293. ((SetupHeader.DirExistsWarning = ddAuto) and (T <> PrevAppDir))) and
  2294. DirExists(T) then
  2295. { If so, ask if user wants to install there anyway }
  2296. if LoggedMsgBox(FmtSetupMessage1(msgDirExists, T), SetupMessages[msgDirExistsTitle],
  2297. mbConfirmation, MB_YESNO, True, IDYES) <> IDYES then Exit;
  2298. { Check if directory *doesn't* already exist }
  2299. if (shEnableDirDoesntExistWarning in SetupHeader.Options) and
  2300. not DirExists(T) then
  2301. { If not, ask if user wants to install there anyway }
  2302. if LoggedMsgBox(FmtSetupMessage1(msgDirDoesntExist, T), SetupMessages[msgDirDoesntExistTitle],
  2303. mbConfirmation, MB_YESNO, True, IDYES) <> IDYES then Exit;
  2304. end;
  2305. Result := True;
  2306. WizardDirValue := T;
  2307. end;
  2308. function CheckSelectComponentsPage: Boolean;
  2309. var
  2310. ComponentEntry: PSetupComponentEntry;
  2311. FreeSpace, TotalSpace: Integer64;
  2312. S: String;
  2313. I: Integer;
  2314. begin
  2315. Result := False;
  2316. if InstallMode = imNormal then begin
  2317. if GetSpaceOnNearestMountPoint(False, DirEdit.Text, FreeSpace, TotalSpace) then begin
  2318. if Compare64(FreeSpace, CurrentComponentsSpace) < 0 then
  2319. if LoggedMsgBox(FmtSetupMessage(msgDiskSpaceWarning,
  2320. [IntToKBStr(CurrentComponentsSpace), IntToKBStr(FreeSpace)]),
  2321. SetupMessages[msgDiskSpaceWarningTitle],
  2322. mbConfirmation, MB_YESNO or MB_DEFBUTTON2, True, IDYES) <> IDYES then
  2323. Exit;
  2324. end;
  2325. //now see if there are unchecked components that are already installed
  2326. if PrevSelectedComponents.Count > 0 then begin
  2327. S := '';
  2328. for I := 0 to ComponentsList.Items.Count-1 do begin
  2329. if not ComponentsList.Checked[I] then begin
  2330. ComponentEntry := PSetupComponentEntry(ComponentsList.ItemObject[I]);
  2331. if not (coDisableNoUninstallWarning in ComponentEntry.Options) then begin
  2332. if ListContains(PrevSelectedComponents, ComponentEntry.Name) then begin
  2333. if S <> '' then
  2334. S := S + #13;
  2335. S := S + ExpandConst(ComponentEntry.Description);
  2336. end;
  2337. end;
  2338. end;
  2339. end;
  2340. if (S <> '') and (LoggedMsgBox(FmtSetupMessage1(msgNoUninstallWarning, S),
  2341. SetupMessages[msgNoUninstallWarningTitle], mbConfirmation, MB_YESNO, True, IDYES) <> IDYES) then
  2342. Exit;
  2343. end;
  2344. end;
  2345. Result := True;
  2346. end;
  2347. function CheckSelectProgramGroupPage: Boolean;
  2348. begin
  2349. Result := ValidateGroupEdit;
  2350. if Result then
  2351. WizardGroupValue := GroupEdit.Text;
  2352. end;
  2353. var
  2354. PageIndex: Integer;
  2355. Continue: Boolean;
  2356. NewPageID: Integer;
  2357. WizardComponents, WizardTasks: TStringList;
  2358. label Again;
  2359. begin
  2360. if CurPageID = wpInstalling then
  2361. Exit;
  2362. case CurPageID of
  2363. wpLicense: if not LicenseAcceptedRadio.Checked then Exit; { paranoia }
  2364. wpPassword: if not CheckPassword then Exit;
  2365. wpUserInfo: if not CheckUserInfoPage then Exit;
  2366. wpSelectDir: if not CheckSelectDirPage then Exit;
  2367. wpSelectComponents: if not CheckSelectComponentsPage then Exit;
  2368. wpSelectProgramGroup: if not CheckSelectProgramGroupPage then Exit;
  2369. wpReady: if (InstallMode = imNormal) and not Visible then Exit;
  2370. end;
  2371. Continue := True;
  2372. PageFromID(CurPageID).NextButtonClick(Continue);
  2373. if not Continue then
  2374. Exit;
  2375. if CodeRunner <> nil then
  2376. if CodeRunner.RunBooleanFunctions( 'NextButtonClick', [CurPageID], bcFalse, False, True) = False then
  2377. Exit;
  2378. { Go to the next page, or close wizard if it was on the last page }
  2379. Again:
  2380. NewPageID := CurPageID;
  2381. PageIndex := PageIndexFromID(NewPageID);
  2382. repeat
  2383. case NewPageID of
  2384. wpUserInfo: begin
  2385. { Ensure these variables are still set when a user's ShouldSkipPage
  2386. function returns True for the wpUserInfo page }
  2387. WizardUserInfoName := UserInfoNameEdit.Text;
  2388. WizardUserInfoOrg := UserInfoOrgEdit.Text;
  2389. WizardUserInfoSerial := UserInfoSerialEdit.Text;
  2390. end;
  2391. wpSelectDir: WizardDirValue := RemoveBackslashUnlessRoot(DirEdit.Text);
  2392. wpSelectProgramGroup: WizardGroupValue := RemoveBackslashUnlessRoot(GroupEdit.Text);
  2393. wpPreparing, wpFinished: begin
  2394. { Note: wpPreparing only 'gets' here if there was no PrepareToInstall failure or if
  2395. PrepareToInstallNeedsRestart is true, else the Cancel button is used instead of the Next button }
  2396. if (NewPageID <> wpPreparing) or (PrepareToInstallFailureMessage <> '') then begin
  2397. DoneWithWizard := True;
  2398. MainForm.Finish(NewPageID = wpPreparing);
  2399. Exit;
  2400. end;
  2401. end;
  2402. end;
  2403. Inc(PageIndex);
  2404. NewPageID := TWizardPage(FPageList[PageIndex]).ID;
  2405. UpdatePage(NewPageID);
  2406. case NewPageID of
  2407. wpPreparing: begin
  2408. WizardComponents := nil;
  2409. WizardTasks := nil;
  2410. try
  2411. WizardComponents := TStringList.Create;
  2412. WizardForm.GetComponents(WizardComponents, nil);
  2413. WizardTasks := TStringList.Create;
  2414. WizardForm.GetTasks(WizardTasks, nil);
  2415. PrepareToInstallFailureMessage := PrepareToInstall(WizardComponents, WizardTasks);
  2416. if PrepareToInstallFailureMessage <> '' then begin
  2417. LogFmt('PrepareToInstall failed: %s', [PrepareToInstallFailureMessage]);
  2418. LogFmt('Need to restart Windows? %s', [SYesNo[PrepareToInstallNeedsRestart]]);
  2419. Break; { stop on the page }
  2420. end else if RmSessionStarted then begin
  2421. SetCurPage(wpPreparing); { controls are already hidden by PrepareToInstall }
  2422. BackButton.Visible := False;
  2423. NextButton.Visible := False;
  2424. if InstallMode = imSilent then
  2425. WizardForm.Visible := True;
  2426. try
  2427. WizardForm.Update;
  2428. RmFoundApplications := QueryRestartManager(WizardComponents, WizardTasks) <> '';
  2429. if RmFoundApplications then
  2430. Break; { stop on the page }
  2431. finally
  2432. UpdateCurPageButtonState;
  2433. end;
  2434. end;
  2435. finally
  2436. WizardTasks.Free;
  2437. WizardComponents.Free;
  2438. end;
  2439. end;
  2440. wpInstalling: begin
  2441. SetCurPage(NewPageID);
  2442. { Start the actual installation process }
  2443. if not MainForm.Install then begin
  2444. { The installation process failed }
  2445. DoneWithWizard := True;
  2446. Exit;
  2447. end;
  2448. goto Again;
  2449. end;
  2450. end;
  2451. until not ShouldSkipPage(NewPageID);
  2452. SetCurPage(NewPageID);
  2453. end;
  2454. procedure TWizardForm.BackButtonClick(Sender: TObject);
  2455. var
  2456. Continue: Boolean;
  2457. PrevPageID: Integer;
  2458. begin
  2459. if CurPageID = wpInstalling then
  2460. Exit;
  2461. Continue := True;
  2462. PageFromID(CurPageID).BackButtonClick(Continue);
  2463. if not Continue then
  2464. Exit;
  2465. if CodeRunner <> nil then
  2466. if CodeRunner.RunBooleanFunctions('BackButtonClick', [CurPageID], bcFalse, False, True) = False then
  2467. Exit;
  2468. PrevPageID := GetPreviousPageID;
  2469. if PrevPageID <> -1 then
  2470. SetCurPage(PrevPageID);
  2471. end;
  2472. procedure TWizardForm.CancelButtonClick(Sender: TObject);
  2473. begin
  2474. { Clicking Cancel will do the same thing as the Close button }
  2475. Close;
  2476. end;
  2477. procedure TWizardForm.CallCancelButtonClick(var ACancel, AConfirm: Boolean);
  2478. begin
  2479. PageFromID(CurPageID).CancelButtonClick(ACancel, AConfirm);
  2480. if not ACancel then
  2481. Exit;
  2482. if CodeRunner <> nil then
  2483. CodeRunner.RunProcedures('CancelButtonClick', [CurPageID, @ACancel,
  2484. @AConfirm], False);
  2485. end;
  2486. procedure TWizardForm.FormClose(Sender: TObject; var Action: TCloseAction);
  2487. begin
  2488. { Redirect an attempt to close this form to MainForm }
  2489. MainForm.Close;
  2490. Action := caNone;
  2491. end;
  2492. procedure TWizardForm.TypesComboChange(Sender: TObject);
  2493. var
  2494. TypeEntry: PSetupTypeEntry;
  2495. begin
  2496. //select the components for this type. if the type is custom only select
  2497. //fixed components
  2498. TypeEntry := PSetupTypeEntry(TypesCombo.Items.Objects[TypesCombo.ItemIndex]);
  2499. SelectComponentsFromType(TypeEntry.Name, (toIsCustom in TypeEntry.Options));
  2500. //if customization is possible remember the type and components that are
  2501. //selected, so that we can reselect the setup type later if after customization
  2502. //the user didn't really change anything
  2503. //also hide the components list if necessary
  2504. if HasCustomType then begin
  2505. InitialSetupTypeIndex := TypesCombo.ItemIndex;
  2506. GetSelectedComponents(InitialSelectedComponents, False, False);
  2507. if not (shAlwaysShowComponentsList in SetupHeader.Options) then begin
  2508. ComponentsList.Visible := toIsCustom in TypeEntry.Options;
  2509. ComponentsDiskSpaceLabel.Visible := ComponentsList.Visible;
  2510. end;
  2511. end;
  2512. UpdateComponentSizes;
  2513. CalcCurrentComponentsSpace;
  2514. end;
  2515. procedure TWizardForm.ComponentsListClickCheck(Sender: TObject);
  2516. var
  2517. SelectedComponents: TStringList;
  2518. TypeEntry: PSetupTypeEntry;
  2519. Equals: Boolean;
  2520. I: Integer;
  2521. begin
  2522. //first see if this current selection equals the initial selection
  2523. //if so, reselect the initial setup type
  2524. SelectedComponents := TStringList.Create();
  2525. GetSelectedComponents(SelectedComponents, False, False);
  2526. Equals := SelectedComponents.Equals(InitialSelectedComponents);
  2527. SelectedComponents.Free();
  2528. if Equals then begin
  2529. //select the intial type
  2530. TypesCombo.ItemIndex := InitialSetupTypeIndex;
  2531. end else begin
  2532. //select a custom type
  2533. for I := 0 to Entries[seType].Count-1 do begin
  2534. TypeEntry := Entries[seType][I];
  2535. if (toIsCustom in TypeEntry.Options) then begin
  2536. TypesCombo.ItemIndex := TypesCombo.Items.IndexOfObject(TObject(TypeEntry));
  2537. SelectComponentsFromType(TypeEntry.Name, True);
  2538. Break;
  2539. end;
  2540. end
  2541. end;
  2542. UpdateComponentSizes;
  2543. CalcCurrentComponentsSpace;
  2544. end;
  2545. procedure TWizardForm.NoIconsCheckClick(Sender: TObject);
  2546. const
  2547. ColorChange: array[Boolean] of TColor = (clBtnFace, clWindow);
  2548. begin
  2549. GroupEdit.Enabled := not NoIconsCheck.Checked;
  2550. GroupEdit.Color := ColorChange[GroupEdit.Enabled];
  2551. GroupBrowseButton.Enabled := not NoIconsCheck.Checked;
  2552. end;
  2553. procedure TWizardForm.WMSysCommand(var Message: TWMSysCommand);
  2554. begin
  2555. if Message.CmdType = 9999 then begin
  2556. { Removing the About box or modifying any existing text inside it is a
  2557. violation of the Inno Setup license agreement; see LICENSE.TXT.
  2558. However, adding additional lines to the end of the About box is
  2559. permitted. }
  2560. var S := SetupTitle + ' version ' + SetupVersion + SNewLine;
  2561. if SetupTitle <> 'Inno Setup' then
  2562. S := S + (SNewLine + 'Based on Inno Setup' + SNewLine);
  2563. S := S + ('Copyright (C) 1997-2025 Jordan Russell' + SNewLine +
  2564. 'Portions Copyright (C) 2000-2025 Martijn Laan' + SNewLine +
  2565. 'All rights reserved.' + SNewLine2 +
  2566. 'Inno Setup home page:' + SNewLine +
  2567. 'https://www.innosetup.com/');
  2568. S := S + SNewLine2 + 'RemObjects Pascal Script home page:' + SNewLine +
  2569. 'https://www.remobjects.com/ps';
  2570. if SetupMessages[msgAboutSetupNote] <> '' then
  2571. S := S + SNewLine2 + SetupMessages[msgAboutSetupNote];
  2572. if SetupMessages[msgTranslatorNote] <> '' then
  2573. S := S + SNewLine2 + SetupMessages[msgTranslatorNote];
  2574. StringChangeEx(S, '(C)', #$00A9, True);
  2575. LoggedMsgBox(S, SetupMessages[msgAboutSetupTitle], mbInformation, MB_OK, False, 0)
  2576. end else
  2577. inherited;
  2578. end;
  2579. procedure TWizardForm.WMWindowPosChanging(var Message: TWMWindowPosChanging);
  2580. begin
  2581. { Work around a VCL issue (Delphi 11.3) when MainFormOnTaskBar=True:
  2582. If Application.Restore is called while the main form is hidden
  2583. (Visible=False), the window can become visible because of the SW_RESTORE
  2584. command it uses, which both unminimizes and shows a window. Reproducer:
  2585. Application.Minimize;
  2586. Hide;
  2587. Application.Restore;
  2588. This blocks any attempt to show the window while Visible=False.
  2589. (SW_RESTORE will still unminimize the window; it just cannot show it.) }
  2590. inherited;
  2591. if not Visible then
  2592. Message.WindowPos.flags := Message.WindowPos.flags and not SWP_SHOWWINDOW;
  2593. end;
  2594. procedure TWizardForm.LicenseAcceptedRadioClick(Sender: TObject);
  2595. begin
  2596. if CurPageID = wpLicense then
  2597. NextButton.Enabled := True;
  2598. end;
  2599. procedure TWizardForm.LicenseNotAcceptedRadioClick(Sender: TObject);
  2600. begin
  2601. if CurPageID = wpLicense then
  2602. NextButton.Enabled := False;
  2603. end;
  2604. procedure TWizardForm.UserInfoEditChange(Sender: TObject);
  2605. begin
  2606. if CurPageID = wpUserInfo then begin
  2607. try
  2608. NextButton.Enabled := CheckSerialOk();
  2609. except
  2610. NextButton.Enabled := False;
  2611. raise;
  2612. end;
  2613. end;
  2614. end;
  2615. function TWizardForm.ValidateDirEdit: Boolean;
  2616. begin
  2617. Result := ValidateCustomDirEdit(DirEdit, shAllowUNCPath in SetupHeader.Options,
  2618. shAllowRootDirectory in SetupHeader.Options,
  2619. shAllowNetworkDrive in SetupHeader.Options);
  2620. end;
  2621. const
  2622. SHPPFW_NONE = $00000000;
  2623. var
  2624. SHPathPrepareForWriteFunc: function(hwnd: HWND; punkEnableModless: Pointer;
  2625. pszPath: PChar; dwFlags: DWORD): HRESULT; stdcall;
  2626. procedure ReconnectPath(const Path: String);
  2627. { Attempts to re-establish the connection to Path if it's on a network drive
  2628. since mapped network drives are initially disconnected in elevated processes. }
  2629. var
  2630. WindowList: Pointer;
  2631. begin
  2632. { If this fails, we shouldn't display any message boxes since the install
  2633. might be running silently with /SUPPRESSMSGBOXES and this is indeed so:
  2634. The SHPathPrepareForWrite documentation claims that "user interface
  2635. windows will not be created" when hwnd is NULL. }
  2636. if Assigned(SHPathPrepareForWriteFunc) then begin
  2637. { "Just in case" it tries to display UI anyway (it never did in tests),
  2638. disable our windows }
  2639. WindowList := DisableTaskWindows(0);
  2640. try
  2641. SHPathPrepareForWriteFunc(0, nil, PChar(Path), SHPPFW_NONE);
  2642. finally
  2643. EnableTaskWindows(WindowList);
  2644. end;
  2645. end;
  2646. end;
  2647. function ValidateCustomDirEdit(const AEdit: TEdit;
  2648. const AllowUNCPath, AllowRootDirectory, AllowNetworkDrive: Boolean): Boolean;
  2649. { Checks if AEdit.Text contains a valid-looking pathname, and returns True
  2650. if so. May alter AEdit.Text to remove redundant spaces and backslashes. }
  2651. var
  2652. T: String;
  2653. IsUNCPath: Boolean;
  2654. I: Integer;
  2655. P: PChar;
  2656. RootPath: String;
  2657. begin
  2658. Result := False;
  2659. T := AEdit.Text;
  2660. TidyUpDirName(T);
  2661. AEdit.Text := T;
  2662. { Check if the path is too long.
  2663. Note: There's no sense in allowing paths to be as long as MAX_PATH (260)
  2664. since there wouldn't be any room left to append a filename. 240 should be
  2665. a reasonable limit. }
  2666. if Length(T) > 240 then begin
  2667. LoggedMsgBox(SetupMessages[msgDirNameTooLong], '', mbError, MB_OK, True, IDOK);
  2668. Exit;
  2669. end;
  2670. { Check for UNC pathname }
  2671. IsUNCPath := (Length(T) >= 2) and (T[1] = '\') and (T[2] = '\');
  2672. if IsUNCPath and not AllowUNCPath then begin
  2673. LoggedMsgBox(SetupMessages[msgCannotInstallToUNCPath], '', mbError, MB_OK, True, IDOK);
  2674. Exit;
  2675. end;
  2676. if not IsUNCPath then begin
  2677. { Check if is at least 4 chars long and it has a drive letter, colon,
  2678. and backslash }
  2679. if not AllowRootDirectory then
  2680. I := 4
  2681. else
  2682. I := 3;
  2683. if (Length(T) < I) or not CharInSet(UpCase(T[1]), ['A'..'Z']) or
  2684. (T[2] <> ':') or (T[3] <> '\') then begin
  2685. LoggedMsgBox(SetupMessages[msgInvalidPath], '', mbError, MB_OK, True, IDOK);
  2686. Exit;
  2687. end;
  2688. end
  2689. else begin
  2690. { Check if there's at least one backslash at least one character past the
  2691. initial '\\' }
  2692. P := @PChar(Pointer(T))[2]; { the casts avoid a UniqueString call... }
  2693. if PathStrScan(P, '\') <= P then begin
  2694. LoggedMsgBox(SetupMessages[msgInvalidPath], '', mbError, MB_OK, True, IDOK);
  2695. Exit;
  2696. end;
  2697. end;
  2698. { Verify that no path components contain control characters, end in spaces,
  2699. or consist only of dots }
  2700. if ContainsControlCharacters(T) or
  2701. PathComponentsContainTrailingSpaces(T) or
  2702. PathComponentsContainInvalidDots(T) then begin
  2703. LoggedMsgBox(SetupMessages[msgInvalidDirName], '', mbError, MB_OK, True, IDOK);
  2704. Exit;
  2705. end;
  2706. { Check for invalid characters after 'x:' or '\\' }
  2707. if PathLastDelimiter(BadDirChars, Copy(T, 3, Maxint)) <> 0 then begin
  2708. LoggedMsgBox(FmtSetupMessage1(msgBadDirName32, SpaceString(BadDirChars)), '',
  2709. mbError, MB_OK, True, IDOK);
  2710. Exit;
  2711. end;
  2712. { Check if it's a valid drive, reconnecting it first if necessary }
  2713. RootPath := RemoveBackslashUnlessRoot(AddBackslash(PathExtractDrive(T)));
  2714. ReconnectPath(RootPath);
  2715. if not DirExists(RootPath) then begin
  2716. LoggedMsgBox(SetupMessages[msgInvalidDrive], '', mbError, MB_OK, True, IDOK);
  2717. Exit;
  2718. end;
  2719. { After reconnecting, check if it's a disallowed network drive }
  2720. if not IsUNCPath and not AllowNetworkDrive and
  2721. (GetDriveType(PChar(RootPath)) = DRIVE_REMOTE) then begin
  2722. LoggedMsgBox(SetupMessages[msgCannotInstallToNetworkDrive], '', mbError, MB_OK, True, IDOK);
  2723. Exit;
  2724. end;
  2725. Result := True;
  2726. end;
  2727. function TWizardForm.ValidateGroupEdit: Boolean;
  2728. var
  2729. T: String;
  2730. begin
  2731. Result := False;
  2732. if not NoIconsCheck.Checked then begin
  2733. T := GroupEdit.Text;
  2734. TidyUpGroupName(T);
  2735. GroupEdit.Text := T;
  2736. { Check if the path is too long }
  2737. if Length(T) > 120 then begin
  2738. LoggedMsgBox(SetupMessages[msgGroupNameTooLong], '', mbError, MB_OK, True, IDOK);
  2739. Exit;
  2740. end;
  2741. { Verify that no path components contain control characters or end in
  2742. spaces }
  2743. if ContainsControlCharacters(T) or
  2744. PathComponentsContainTrailingSpaces(T) then begin
  2745. LoggedMsgBox(SetupMessages[msgInvalidGroupName], '', mbError, MB_OK, True, IDOK);
  2746. Exit;
  2747. end;
  2748. if T = '' then begin
  2749. LoggedMsgBox(SetupMessages[msgMustEnterGroupName], '', mbError, MB_OK, True, IDOK);
  2750. Exit;
  2751. end;
  2752. { Check for invalid characters }
  2753. if PathLastDelimiter(BadDirChars, T) <> 0 then begin
  2754. LoggedMsgBox(FmtSetupMessage1(msgBadGroupName, SpaceString(BadDirChars)),
  2755. '', mbError, MB_OK, True, IDOK);
  2756. Exit;
  2757. end;
  2758. end;
  2759. Result := True;
  2760. end;
  2761. procedure TWizardForm.DirBrowseButtonClick(Sender: TObject);
  2762. var
  2763. NewFolderName, Path: String;
  2764. begin
  2765. NewFolderName := Trim(PathExtractName(RemoveBackslashUnlessRoot(ExpandedDefaultDirName)));
  2766. { If ExpandedDefaultDirName is a root directory, there will be no name }
  2767. if NewFolderName = '' then
  2768. NewFolderName := Trim(SetupMessages[msgNewFolderName]);
  2769. Path := DirEdit.Text;
  2770. if ShowSelectFolderDialog(False, shAppendDefaultDirName in SetupHeader.Options,
  2771. Path, NewFolderName) then
  2772. DirEdit.Text := Path;
  2773. end;
  2774. procedure TWizardForm.GroupBrowseButtonClick(Sender: TObject);
  2775. var
  2776. NewFolderName, Path: String;
  2777. begin
  2778. NewFolderName := Trim(PathExtractName(ExpandedDefaultGroupName));
  2779. if NewFolderName = '' then
  2780. NewFolderName := Trim(SetupMessages[msgNewFolderName]);
  2781. Path := GroupEdit.Text;
  2782. if ShowSelectFolderDialog(True, shAppendDefaultGroupName in SetupHeader.Options,
  2783. Path, NewFolderName) then
  2784. GroupEdit.Text := Path;
  2785. end;
  2786. { also used by ScriptDlg! }
  2787. procedure TWizardForm.DirTreeRename(Sender: TCustomFolderTreeView;
  2788. var NewName: string; var Accept: Boolean);
  2789. const
  2790. NewFolderBadDirChars = '\' + BadDirChars;
  2791. begin
  2792. NewName := Trim(NewName);
  2793. if (NewName = '') or PathComponentsContainInvalidDots(NewName) then begin
  2794. Accept := False;
  2795. LoggedMsgBox(SetupMessages[msgInvalidDirName], '', mbError, MB_OK, True, IDOK);
  2796. Exit;
  2797. end;
  2798. if PathLastDelimiter(NewFolderBadDirChars, NewName) <> 0 then begin
  2799. Accept := False;
  2800. LoggedMsgBox(FmtSetupMessage1(msgBadDirName32, SpaceString(NewFolderBadDirChars)),
  2801. '', mbError, MB_OK, True, IDOK);
  2802. Exit;
  2803. end;
  2804. end;
  2805. procedure TWizardForm.GroupTreeRename(Sender: TCustomFolderTreeView;
  2806. var NewName: string; var Accept: Boolean);
  2807. const
  2808. NewFolderBadDirChars = '\' + BadDirChars;
  2809. begin
  2810. NewName := Trim(NewName);
  2811. if (NewName = '') or PathComponentsContainInvalidDots(NewName) then begin
  2812. Accept := False;
  2813. LoggedMsgBox(SetupMessages[msgInvalidGroupName], '', mbError, MB_OK, True, IDOK);
  2814. Exit;
  2815. end;
  2816. if PathLastDelimiter(NewFolderBadDirChars, NewName) <> 0 then begin
  2817. Accept := False;
  2818. LoggedMsgBox(FmtSetupMessage1(msgBadGroupName, SpaceString(NewFolderBadDirChars)),
  2819. '', mbError, MB_OK, True, IDOK);
  2820. Exit;
  2821. end;
  2822. end;
  2823. procedure TWizardForm.ClickToStartPage;
  2824. { Simulates clicks on the Next button until Setup is ready to start.
  2825. This is called on non-silent installs. }
  2826. begin
  2827. while ShouldSkipPage(CurPageID) and NextButton.CanFocus do
  2828. NextButton.Click;
  2829. end;
  2830. procedure TWizardForm.ClickThroughPages;
  2831. { Simulates clicks on the Next button until Setup is ready to terminate.
  2832. This is called on silent installs. }
  2833. var
  2834. BeforeID: Integer;
  2835. begin
  2836. while True do begin
  2837. if (CurPageID = wpPreparing) and (PrepareToInstallFailureMessage <> '') and not (PrepareToInstallNeedsRestart and not InitNoRestart) then begin
  2838. { Special handling needed for wpPreparing since it displays its error
  2839. message inline on the wizard. Since the wizard isn't currently visible,
  2840. we have to display the message in a message box if it won't be displayed
  2841. by a reboot confirmation message box later on. }
  2842. LoggedMsgBox(PrepareToInstallFailureMessage, '',
  2843. mbCriticalError, MB_OK, True, IDOK);
  2844. if PrepareToInstallNeedsRestart then
  2845. SetupExitCode := ecPrepareToInstallFailedRestartNeeded
  2846. else
  2847. SetupExitCode := ecPrepareToInstallFailed;
  2848. Abort;
  2849. { Note: no special handling if it stops on wpPreparing because of in-use
  2850. files ((CurPageID = wpPreparing) and (PrepareToInstallFailureMessage = '')),
  2851. instead it will always choose to close applications when running silently
  2852. unless /NOCLOSEAPPLICATIONS was used. }
  2853. end;
  2854. BeforeID := CurPageID;
  2855. { Simulate a click on NextButton if it's enabled & visible }
  2856. try
  2857. if NextButton.CanFocus then
  2858. NextButton.Click;
  2859. except
  2860. { Mustn't propagate post-install exceptions }
  2861. if MainForm.CurStep <= ssInstall then
  2862. raise
  2863. else
  2864. Application.HandleException(Self);
  2865. end;
  2866. if DoneWithWizard then
  2867. Break;
  2868. { If the page didn't change, there must've been an error }
  2869. if CurPageID = BeforeID then begin
  2870. if MainForm.CurStep <= ssInstall then begin
  2871. Log('Failed to proceed to next wizard page; aborting.');
  2872. Abort;
  2873. end
  2874. else begin
  2875. { After installation, we can't abort since e.g. a restart might be
  2876. needed. Instead, to avoid getting stuck in a loop, show the wizard
  2877. (even though this is a silent install) and let the user deal with the
  2878. problem on their own. }
  2879. Log('Failed to proceed to next wizard page; showing wizard.');
  2880. WizardForm.Visible := True;
  2881. Application.Restore;
  2882. Break;
  2883. end;
  2884. end;
  2885. end;
  2886. end;
  2887. initialization
  2888. SHPathPrepareForWriteFunc := GetProcAddress(SafeLoadLibrary(AddBackslash(GetSystemDir) + shell32,
  2889. SEM_NOOPENFILEERRORBOX), 'SHPathPrepareForWriteW');
  2890. end.