Setup.WizardForm.pas 123 KB

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