[Components]
Name: "Slasher"; Description: "Dagon Slasher"; Types: Slasher Full
Name: "Frankenstein"; Description: "Dagon Frankenstein"; Types: Frankenstein Full
[Types]
Name: "Full"; Description: "Dagon Video Tools"
Name: "Slasher"; Description: "Dagon Slasher"
Name: "Frankenstein"; Description: "Dagon FrankenStein"
[Tasks]
Name: "Debug"; Description: "Nothing"; Components: not Slasher
Name: "Vid"; Description: "Install Extra Codecs for Frankenstein"; Flags: unchecked; Components: not Slasher
[Code]
var
Warning: TNewStaticText;
procedure InitializeWizard;
begin
Warning := TNewStaticText.Create(WizardForm);
Warning.Parent := WizardForm.SelectTasksPage;
Warning.Visible := False;
Warning.AutoSize := False;
Warning.SetBounds(
WizardForm.TasksList.Left,
WizardForm.TasksList.Top + WizardForm.TasksList.Height,
WizardForm.TasksList.Width,
50
);
Warning.Font.Color := clRed;
Warning.Caption := 'Warning: This will result in a non-functional "Join in FrankenStein" button in the Tools Menu.';
end;
I used yet another amazing piece of code by TLama. The problem is I need the note to be visible when the user selects the task, and be hidden otherwise (while on the same page).
You have to handle WizardForm.TasksList.OnClickCheck event and update the Warning label visibility accordingly.
var
Warning: TNewStaticText;
procedure TasksListClickCheck(Sender: TObject);
begin
Warning.Visible :=
{ This (and the task index below) has to be kept in sync with the expression }
{ in "Components" parameter of the respective task. }
{ Though note that in your specific case the test }
{ is redundant as when "Slasher" is selected, you have no tasks, }
{ and the "Tasks" page is completely skipped, so you do not even get here. }
(not IsComponentSelected('Slasher')) and
WizardForm.TasksList.Checked[0]; { You can also use WizardIsTaskSelected }
end;
procedure InitializeWizard;
begin
Warning := TNewStaticText.Create(WizardForm);
...
{ Update Warning label visibility on task selection change }
WizardForm.TasksList.OnClickCheck := #TasksListClickCheck
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectTasks then
begin
{ Update initial visibility }
TasksListClickCheck(WizardForm.TasksList);
end;
end;
Side notes:
Do not hard code the height to fixed 50. Scale it with DPI instead: ScaleY(50).
You should set Warning.WordWrap := True as the caption does not fit page width.
You should shrink TasksList's height as the label does not fit below the list. You are missing the WizardForm.TasksList.Height := WizardForm.TasksList.Height - NoteHeight; from #TLama's code. Again note that he is missing the scaling of the NoteHeight.
const
NoteHeight = 50;
procedure InitializeWizard;
begin
WizardForm.TasksList.Height := WizardForm.TasksList.Height - ScaleY(NoteHeight);
Warning := TNewStaticText.Create(WizardForm);
Warning.Parent := WizardForm.SelectTasksPage;
Warning.AutoSize := False;
Warning.WordWrap := True;
Warning.SetBounds(
WizardForm.TasksList.Left,
WizardForm.TasksList.Top + WizardForm.TasksList.Height,
WizardForm.TasksList.Width,
ScaleY(NoteHeight)
);
Warning.Font.Color := clRed;
{ Update Warning label visibility on task selection change }
WizardForm.TasksList.OnClickCheck := #TasksListClickCheck
Warning.Caption :=
'Warning: This will result in a non-functional "Join in FrankenStein" button ' +
'in the Tools Menu.';
end;
Related
I would like to create a custom Inno Setup page that will be displayed at the very beginning of installation, with 3 radio buttons to select from. That selection should set the value in #define BinVer. Currently I'm forced to make 3 separate installers, but I prefer to have just one with such feature. I'm setting it up like that for each exe:
#define BinVer "111111", in another compilation I change it to #define BinVer "222222", and finaly I have #define BinVer "333333" for the third compilation. I'm using this define in such situations li ke that:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
case CurUninstallStep of
usPostUninstall:
begin
CreateDir(ExpandConstant('{app}\bin\{#BinVer}\files'));
end;
end;
end;
or
Source: "{#CompPath}\Folder\sed.exe"; DestDir: "{app}\bin\{#BinVer}\files\gui\unbound\"; Flags: deleteafterinstall
So far I have tried something like this, but it's just very beginning, not sure what to do next, I'm not that good with programming it:
[Code]
procedure InitializeWizard();
var
Page: TInputOptionWizardPage;
begin
Page := CreateInputOptionPage(wpWelcome, '', '', '', False, False);
Page.AddEx('Radio button 1', 0, True);
Page.AddEx('Radio button 2', 0, True);
Page.AddEx('Radio button 3', 0, True);
end;
Also tried something like that (but I don't know if this is correct way to achieve this, it's not even compiling without errors):
[Code]
var
BinVer: string;
function InitializeSetup(): Boolean;
var
Page: TInputOptionWizardPage;
begin
Page := CreateInputOptionPage(wpWelcome, 'Select BinVer', 'Select BinVer', '', False, False);
Page.Add('BinVer 1', '111111');
Page.Add('BinVer 2', '222222');
Page.Add('BinVer 3', '333333');
Page.DefaultValueIndex := 0; // initially select the first radio button
Result := True;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
Page: TInputOptionWizardPage;
begin
Result := True;
if CurPageID = wpWelcome then
begin
Page := GetInputOptionPage(wpWelcome);
case Page.SelectedValueIndex of
0: BinVer := '111111';
1: BinVer := '222222';
2: BinVer := '333333';
end;
end;
end;
You cannot change a value of a preprocessor variable (used on compile-time) on runtime.
But you do not have to. You want to use the value on runtime from Pascal Code. And you already have the value stored to runtime Pascal variable (BinVer: string). So use that:
CreateDir(ExpandConstant('{app}\bin\' + BinVer + '\files'));
Though you do not really need to store the value to the global variable either. You can query the "InputOptionPage" when needed.
Example of full code, linking the selection to the [Files] section entry destination:
[Files]
Source: "{#CompPath}\Folder\sed.exe"; \
DestDir: "{app}\bin\{code:GetBinVer}\files\gui\unbound\"; \
Flags: deleteafterinstall
[Code]
var
InputOptionPage: TInputOptionWizardPage;
procedure InitializeWizard();
begin
InputOptionPage := CreateInputOptionPage(wpWelcome, '', '', '', False, False);
InputOptionPage.AddEx('Radio button 1', 0, True);
InputOptionPage.AddEx('Radio button 2', 0, True);
InputOptionPage.AddEx('Radio button 3', 0, True);
// initially select the first radio button
InputOptionPage.SelectedValueIndex := 0;
end;
function GetBinVer(Param: string): string;
begin
case InputOptionPage.SelectedValueIndex of
0: BinVer := '111111';
1: BinVer := '222222';
2: BinVer := '333333';
else RaiseException('Unexpected selection');
end;
end;
(not tested)
Though linking it to a code in CurUninstallStepChanged is more complicated, that happens in another (uninstaller) process. So you will have to store the selection/BinVer somewhere (e.g. Windows Registry), where you will load it from in the uninstaller. You might by able to use SetPreviousData/GetPreviousData.
How to add the choice of language to the first page, as it is shown in the video?
http://screencast.com/t/SDI5VN67LFL
Thank you all in advance for your help.
Based on the code in this answer, it does what you wanted:
[Setup]
DisableWelcomePage=False
ShowLanguageDialog=no
{...}
[Languages]
Name: "English"; MessagesFile: "compiler:Default.isl"
Name: "French"; MessagesFile: "compiler:Languages\French.isl"
Name: "German"; MessagesFile: "compiler:Languages\German.isl"
Name: "Spanish"; MessagesFile: "compiler:Languages\Spanish.isl"
//Add languages as you like ...
{...}
[Code]
var
LangCombo: TNewComboBox;
SelectLangLabel: TNewStaticText;
LangArray: Array of String;
IsConfirm: Boolean;
function ShellExecute(hwnd: HWND; lpOperation, lpFile, lpParameters, lpDirectory: String; nShowCmd: Integer): THandle;
external 'ShellExecuteW#shell32.dll stdcall';
function IsSetLang: Boolean;
begin
Result := (ExpandConstant('{param:LANG}') = '');
end;
function IsActiveLang: Boolean;
begin
Result := (LangArray[LangCombo.ItemIndex] = ActiveLanguage);
end;
function ActiveLang: Integer;
var
I: Integer;
begin
for I := 0 to (GetArrayLength(LangArray) - 1) do
begin
if (LangArray[I] = ActiveLanguage) then Result := I;
end;
end;
procedure InitializeWizard;
begin
IsConfirm := True;
LangCombo := TNewComboBox.Create(WizardForm);
LangCombo.Parent := WizardForm.WelcomePage;
LangCombo.Top := WizardForm.Bevel.Top - LangCombo.Height - ScaleY(55);
LangCombo.Left := WizardForm.WelcomeLabel1.Left;
LangCombo.Width := WizardForm.WelcomeLabel1.Width;
LangCombo.Style := csDropDownList;
SelectLangLabel := TNewStaticText.Create(WizardForm);
SelectLangLabel.Parent := WizardForm.WelcomePage;
SelectLangLabel.Top := LangCombo.Top - SelectLangLabel.Height - ScaleY(8);
SelectLangLabel.Left := LangCombo.Left;
SelectLangLabel.Caption := SetupMessage(msgSelectLanguageLabel);
LangCombo.Items.Add('English'); //ItemIndex: 0 - English
LangCombo.Items.Add('Français'); //ItemIndex: 1 - French
LangCombo.Items.Add('Deutsch'); //ItemIndex: 2 - German
LangCombo.Items.Add('Español'); //ItemIndex: 3 - Spanish
//Add languages as you like, but make sure the order matches the order of the languages in the array.
SetArrayLength(LangArray,LangCombo.Items.Count);
LangArray[0] := 'English'; //=ItemIndex: 0
LangArray[1] := 'French'; //=ItemIndex: 1
LangArray[2] := 'German'; //=ItemIndex: 2
LangArray[3] := 'Spanish'; //=ItemIndex: 3
//Add languages as you like, but make sure the order matches the order of the languages in the combobox.
LangCombo.ItemIndex := ActiveLang; //set default language as active language
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
ResultCode: THandle;
begin
Result := True;
if (CurPageID = wpWelcome) and not IsActiveLang then
begin
Result := False;
IsConfirm := False;
ResultCode := ShellExecute(0,'',ExpandConstant('{srcexe}'),'/LANG='+LangArray[LangCombo.ItemIndex],'',SW_SHOW);
if ResultCode <= 32 then MsgBox(Format('Running installer with the selected language failed. Code: %d',[ResultCode]), mbCriticalError, MB_OK);
WizardForm.Close;
end;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := (PageID = wpWelcome) and IsSetLang;
end;
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
Cancel := True;
Confirm := IsConfirm;
end;
This code demonstrates how to create a language combobox on the WelcomePage, but can of course be replaced with any other or custom page.
Try this:
[Setup]
ShowLanguageDialog=yes
ShowUndisplayableLanguages=yes
[Languages]
Name: "cz"; MessagesFile: "compiler:Languages\Czech.isl";
Name: "de"; MessagesFile: "compiler:Languages\German.isl";
Name: "en"; MessagesFile: "compiler:Default.isl";
You cannot change the language on Inno Setup installer while it is running. The installer on the video must be using some custom build of Inno Setup.
But you can re-start the installer with a new language. See these related questions:
Choosing install language programmatically in Inno Setup
Change the setup language while setup is running (Inno Setup)
I'm trying to build a setup for my application , which contains two parts: server and client. The client part needs to have an IP address entered by the user. I'm using a custom page to prompt for the IP address. But I need to display the custom page, only if user selects "Client" component.
[Components]
Name: "Serveur"; Description: "Server installation"; Types: Serveur; Flags: exclusive;
Name: "Client"; Description: "Client installation"; Types: Client; Flags: exclusive
[Types]
Name: "Serveur"; Description: "Server Installation"
Name: "Client"; Description: "Client Installation"
[Code]
var
Page: TInputQueryWizardPage;
ip: String;
procedure InitializeWizard();
begin
Page := CreateInputQueryPage(wpWelcome,
'IP Adresse du serveur', 'par exemple : 192.168.1.120',
'Veuillez introduire l''adresse IP du serveur :');
Page.Add('IP :', False);
Page.Values[0] := ExpandConstant('192.168.x.x');
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if (CurPageID = Page.ID) then
begin
ip := Page.Values[0];
SaveStringToFile('C:\Program Files\AppClient\ipAddress.txt', ip, False);
end;
Result := True;
end;
Your custom page must go only after the "Select Components" page, so you need to pass wpSelectComponents to CreateInputQueryPage:
var
Page: TInputQueryWizardPage;
procedure InitializeWizard();
begin
Page :=
CreateInputQueryPage(
wpSelectComponents, 'IP Adresse du serveur', 'par exemple : 192.168.1.120',
'Veuillez introduire l''adresse IP du serveur :');
Page.Add('IP :', False);
Page.Values[0] := '192.168.x.x';
end;
(Also note that there's no point in calling ExpandConstant on a string literal that does not include any constants).
Skip the custom page, when the "Client" component is not selected:
function IsClient: Boolean;
begin
Result := IsComponentSelected('Client');
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = Page.ID then
begin
Result := not IsClient;
end;
end;
See also Skipping custom pages based on optional components in Inno Setup.
Well behaving installer should not make any modifications to a system, before the user finally confirms the installation. So make any changes only, once installation really starts, not already when user click "Next" on the custom page.
Also, you cannot hard-code a path to the file, use {app} constant.
procedure CurStepChanged(CurStep: TSetupStep);
var
IP: string;
begin
if (CurStep = ssInstall) and IsClient() then
begin
IP := Page.Values[0];
SaveStringToFile(ExpandConstant('{app}\ipAddress.txt'), IP, False);
end;
end;
Some time back I had this question. Some of that code is repeated here:
procedure RebuildRunList;
var
RunEntries: array of TRunEntry;
I: Integer;
begin
{ Save run list ... }
SetArrayLength(RunEntries, WizardForm.RunList.Items.Count);
for I := 0 to WizardForm.RunList.Items.Count - 1 do
begin
RunEntries[I].Caption := WizardForm.RunList.ItemCaption[I];
RunEntries[I].Checked := WizardForm.RunList.Checked[I];
RunEntries[I].Object := WizardForm.RunList.ItemObject[I];
end;
{ ... clear it ... }
WizardForm.RunList.Items.Clear;
{ ... and re-create }
for I := 0 to GetArrayLength(RunEntries) - 1 do
begin
{ the first two entries are radio buttons }
if (I = 0) or (I = 1) then
begin
WizardForm.RunList.AddRadioButton(
RunEntries[I].Caption, '', 0, RunEntries[I].Checked, True, RunEntries[I].Object);
end
else
begin
WizardForm.RunList.AddCheckBox(
RunEntries[I].Caption, '', 0, RunEntries[I].Checked, True, True, True,
RunEntries[I].Object);
end;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
{ Only now is the RunList populated. }
{ Two entries are on 64-bit systems only. }
if IsWin64 then RebuildRunList;
end;
end;
I would like to know how I can make a enhancement to the radio choices. The drawback at the moment is that the user is forced to start one or the other application. I would like to add another radio option for simply closing down the installer. Ideally it would use a Inno Setup supplied message so that I do not have to ask for translations. (See this question).
Can this be done?
The easiest solution is to add a no-op entry to the RunList:
[Run]
...
Filename: "{cmd}"; Parameters: "/C exit"; Description: "Exit setup"; \
Flags: nowait postinstall runasoriginaluser unchecked skipifsilent runhidden; \
Check: IsWin64
And turn it to a radio button:
{ the first three entries are radio buttons }
if (I = 0) or (I = 1) or (I = 2) then
I have an Information page, the page that that is activated with infobefore file:
[Setup]
InfoBeforeFile=infobefore.txt
I want to:
Add a checkbox (or a couple of checkboxes), that the user has to check to show that he has paid attention to the information.
The user should only be allowed to proceed when he checks the checkboxes right. I think of disabling the Next button or showing a messagebox, whatever is easier.
Is there an easy way to do this?
Just add a new checkbox on the InfoBeforePage page. And update the NextButton state based on the check box state.
[Setup]
InfoBeforeFile=infobefore.txt
[Code]
var
InfoBeforeCheck: TNewCheckBox;
procedure CheckInfoBeforeRead;
begin
{ Enable the NextButton only if InfoBeforeCheck is checked or }
{ installer is running in the silent mode }
WizardForm.NextButton.Enabled := InfoBeforeCheck.Checked or WizardSilent;
end;
procedure InfoBeforeCheckClick(Sender: TObject);
begin
{ Update state of the Next button, whenever the InfoBeforeCheck is toggled }
CheckInfoBeforeRead;
end;
procedure InitializeWizard();
begin
InfoBeforeCheck := TNewCheckBox.Create(WizardForm);
InfoBeforeCheck.Parent := WizardForm.InfoBeforePage;
{ Follow the License page layout }
InfoBeforeCheck.Top := WizardForm.LicenseNotAcceptedRadio.Top;
InfoBeforeCheck.Left := WizardForm.LicenseNotAcceptedRadio.Left;
InfoBeforeCheck.Width := WizardForm.LicenseNotAcceptedRadio.Width;
InfoBeforeCheck.Height := WizardForm.LicenseNotAcceptedRadio.Height;
InfoBeforeCheck.Caption := 'I swear I read this';
InfoBeforeCheck.OnClick := #InfoBeforeCheckClick;
{ Make the gap between the InfoBeforeMemo and the InfoBeforeCheck the same }
{ as the gap between LicenseMemo and LicenseAcceptedRadio }
WizardForm.InfoBeforeMemo.Height :=
((WizardForm.LicenseMemo.Top + WizardForm.LicenseMemo.Height) -
WizardForm.InfoBeforeMemo.Top) +
(InfoBeforeCheck.Top - WizardForm.LicenseAcceptedRadio.Top);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpInfoBefore then
begin
{ Initial state of the Next button }
CheckInfoBeforeRead;
end;
end;