Setup.WizardForm.pas 117 KB

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