Setup.WizardForm.pas 123 KB

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