Setup.WizardForm.pas 122 KB

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