Setup.WizardForm.pas 124 KB

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