Inno how to add <Skip> button for optional wizard page? - inno-setup

I have an optional wizard page of TInputDirWizardPage type.
How to add button which will not validate data and skip if data were not entered?

Did you check the inno help, here you see the available functions on the TInputDirWizardPage
TInputDirWizardPage = class(TWizardPage)
function Add(const APrompt: String): Integer;
property Buttons[Index: Integer]: TNewButton; read;
property Edits[Index: Integer]: TEdit; read;
property PromptLabels[Index: Integer]: TNewStaticText; read;
property SubCaptionLabel: TNewStaticText; read;
property Values[Index: Integer]: String; read write;
end;
I used this way only if i needed a text input on the page. I recommend you to create a complete custom WizardPage, with that you are more flexible. For the creation of the page you can use a designer, I created all my custom pages with the InnoSetup Form Designer. Here you can see it in action http://www.cenadep.org/2012/02/09/innosetup-form-designer/

I've found simple and working solution. Instead of adding additional button to avoid validation of empty path. I've just added default dir creation. in [Dirs] section.
[Dirs]
Name: {code:WrkGetWorkingDir}; Flags: uninsneveruninstall
My application could accept working dir cmdline arg or create default one if it is not specified. So. I just always specifying that cmdline arg and creating that default dir (if user did not changed that path) in installation script.

Hope this will help
procedure OnClickMyButton(Sender: TObject);
begin
MsgBox('OnClickMyButton', mbInformation, MB_OK);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = YourPageID.ID then
begin
MyButton := TButton.Create(YourPageID);
MyButton.Parent := YourPageID.Buttons[0].Parent;
MyButton.Top := YourPageID.Edits[0].Top + YourPageID.Edits[0].Height + 10;
MyButton.Left := YourPageID.Edits[0].Left;
MyButton.Width := 100;
MyButton.Caption := 'My Custom Button';
MyButton.OnClick := #OnClickMyButton;
end;
end;

Related

Inno Setup Detect changed task/item in TasksList.OnClickCheck event

I'm stuck on simple situation with OnClickCheck property. The problem is that I see a Msgbox every time I turn on backup task, but also (while it's switched on) OnClickCheckappeared on pressing uninst task too! Seems that OnClickCheck checks all clicks, but I need to check click only on the first task.
It is logical to add to "WizardForm.TasksList.OnClickCheck" exact number of task (WizardForm.TasksList.OnClickCheck[0]), but compiler doesn't agree with it.
[Tasks]
Name: backup; Description: do backup
Name: uninst; Description: do not create uninstaller
[Code]
procedure TaskOnClick(Sender: TObject);
begin
if IsTaskSelected('backup') then
begin
MsgBox('backup task has been checked.', mbInformation, MB_OK)
end;
end;
procedure InitializeWizard();
begin
WizardForm.TasksList.OnClickCheck := #TaskOnClick;
end;
There's no way tell exactly what task (list item) was changed in the OnClickCheck event.
To tell which item was checked by the user, you can use the ItemIndex property. The user can check only the selected item.
Though if you have a task hierarchy, even unselected task can be toggled automatically by the installer due to a change in child/parent items. So to tell all changes, all you can do is to remember the previous state and compare it against the current state, when the OnClickCheck is called.
var
TasksState: array of TCheckBoxState;
procedure TasksClickCheck(Sender: TObject);
var
I: Integer;
begin
for I := 0 to WizardForm.TasksList.Items.Count - 1 do
begin
if TasksState[I] <> WizardForm.TasksList.State[I] then
begin
Log(Format('Task %d state changed from %d to %d',
[I, TasksState[I], WizardForm.TasksList.State[I]]));
TasksState[I] := WizardForm.TasksList.State[I];
end;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
var
I: Integer;
begin
if CurPageID = wpSelectTasks then
begin
{ Only now is the task list initialized (e.g. based on selected setup }
{ type and components). Remember what is the current/initial state. }
SetArrayLength(TasksState, WizardForm.TasksList.Items.Count);
for I := 0 to WizardForm.TasksList.Items.Count - 1 do
TasksState[I] := WizardForm.TasksList.State[I];
end;
end;
procedure InitializeWizard();
begin
WizardForm.TasksList.OnClickCheck := #TasksClickCheck;
end;
Instead of using indexes, you can also use task names with use of WizardSelectedTasks or WizardIsTaskSelected. For an example, see Inno Setup: how to auto select a component if another component is selected?
Also see:
Inno Setup ComponentsList OnClick event
Inno Setup Uncheck a task when another task is checked
Inno Setup - Show children component as sibling and show check instead of square in checkbox

How to display localized Program Files name (display name) during installation?

I'm currently creating an installer, which has Program Files as its default installation directory. To do this, I used {pf}.
It's a German program and only used in Germany and while the installer is entirely in German during selection of the destination directory, setup still displays C:\Program Files instead of the localized name C:\Programme.
Is it possible to get it to display C:\Programme instead? Functionally everything works fine, the application is installed in C:\Programme. I'm just concerned a basic user may be confused by reading C:\Program Files.
EDIT: Further information: I know C:\Programme or any other localized name for Program Files is just a display name, the physical path is always Program Files. Doesn't matter which Windows version or what language Windows has. Yet I'd still like setup to display C:\Programme during installation.
My test machines are on Windows 7 and Windows 10.
Inno Setup does not support that.
You would have to fake it. You can dynamically translate contents of the DirEdit to/from a display name as needed:
translate to display name, when the "Select Destination Location" page is activated
translate to physical path, when "Browse" button is clicked.
translate to display name, when new path is selected.
translate to physical path, when "Next" button is clicked.
function ToDisplayName(Path: string): string;
begin
Result := ???;
end;
function FromDisplayName(Path: string): string;
begin
Result := ???;
end;
var
DirBrowseButtonClickOrig: TNotifyEvent;
OnSelectDir: Boolean;
procedure DirBrowseButtonClick(Sender: TObject);
begin
WizardForm.DirEdit.Text := FromDisplayName(WizardForm.DirEdit.Text);
DirBrowseButtonClickOrig(Sender);
WizardForm.DirEdit.Text := ToDisplayName(WizardForm.DirEdit.Text);
end;
procedure InitializeWizard();
begin
DirBrowseButtonClickOrig := WizardForm.DirBrowseButton.OnClick;
WizardForm.DirBrowseButton.OnClick := #DirBrowseButtonClick;
OnSelectDir := False;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectDir then
begin
OnSelectDir := True;
WizardForm.DirEdit.Text := ToDisplayName(WizardForm.DirEdit.Text);
end
else
begin
if OnSelectDir then
begin
OnSelectDir := False;
WizardForm.DirEdit.Text := FromDisplayName(WizardForm.DirEdit.Text);
end;
end;
end;
A tricky part is of course the implementation of the ToDisplayName and FromDisplayName functions.
A real native implementation would be pretty complex and it's even questionable if you can implement it with limited features of the Pascal Script (particularly a lack of pointers).
But for your specific needs, you can use something as trivial as:
[CustomMessages]
ProgramFilesLocalized=Programme
[Code]
function ToDisplayName(Path: string): string;
begin
StringChange(Path, '\Program Files', '\' + CustomMessage('ProgramFilesLocalized'));
Result := Path;
end;
function FromDisplayName(Path: string): string;
begin
StringChange(Path, '\' + CustomMessage('ProgramFilesLocalized'), '\Program Files');
Result := Path;
end;
If you need a real implementation for converting to/from display name, consider asking a separate question.

Inno Setup BrowseForFolder dialog with Address bar

When I use the BrowseForFolder function in Inno Setup I get a dialog like this
Is it possible to get a dialog with an address bar such as this one?
Use the TInputDirWizardPage (CreateInputDirPage), instead of using the BrowseForFolder function.
The page has the browse dialog as you want (actually I assume your second screenshot shows this dialog).
If you do not want to add a new page, just create a fake one and abuse it.
var
FakePage: TInputDirWizardPage;
procedure BrowseForFolderEx(var Directory: String);
begin
FakePage.Values[0] := Directory;
FakePage.Buttons[0].OnClick(FakePage.Buttons[0]);
Directory := FakePage.Values[0];
end;
procedure InitializeWizard();
begin
FakePage :=
CreateInputDirPage(wpWelcome, '', '', '', False, SetupMessage(msgButtonNewFolder));
FakePage.Add('');
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := (PageID = FakePage.ID);
end;

Uncheck a task checkbox at setup initialisation

In my Inno Setup script there's a task that may be used under certain conditions which are determined by code. In no other conditions this task should be executed. In fact that entire Tasks page is skipped then. Unfortunately the task selection is remembered by Inno Setup and restored on every following update setup, even if the page isn't visible at all.
I now need to uncheck that task generally at every setup initialisation in order to forget the last selected state. But I can't get this to work. Here's my latest try:
[Tasks]
Name: DeleteConfig; Description: "{cm:Task_DeleteConfig}"; Flags: unchecked
#define Task_DeleteConfig_Index 0
[InstallDelete]
; Delete user configuration files if the task is selected
Type: files; Name: "{userappdata}\...\App.conf"; Tasks: DeleteConfig
[Code]
var
IsDowngradeSetup: Boolean;
function InitializeSetup: Boolean;
begin
// More code not shown here, but the following may be set under certain conditions
IsDowngradeSetup := true;
end;
procedure InitializeWizard;
begin
// Clear possibly remembered value from previous downgrade install
WizardForm.TasksList.Checked[{#Task_DeleteConfig_Index}] := false;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
// Make upgrade install quicker
Result := ((PageID = wpSelectTasks) or ((PageID = wpReady) and (GetArrayLength(products) = 0))) and PrevInstallExists;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpWelcome then
begin
if PrevInstallExists then
begin
// Change "Next" button to "Upgrade" on the first page, because it won't ask any more
WizardForm.NextButton.Caption := ExpandConstant('{cm:Upgrade}');
WizardForm.FinishedHeadingLabel.Caption := ExpandConstant('{cm:UpdatedHeadingLabel}');
end;
end;
if CurPageID = wpSelectTasks then
begin
if IsDowngradeSetup then
begin
// Pre-select task to delete existing configuration on downgrading (user can deselect it again)
// (Use the zero-based index of all rows in the tasks list GUI)
// Source: http://stackoverflow.com/a/10490352/143684
WizardForm.TasksList.Checked[{#Task_DeleteConfig_Index}] := true;
end;
end;
end;
This gives me a
Runtime error (at 85:77): List index out of bounds (0).
I don't know where "85:77" is supposed to be but from the only recent changes it can only be the quoted code above.
I first had that in the InitializeSetup function but that didn't work either.
Where should I put this code so that it works and finds a fully initialised tasks list? The tasks page may not be shown so I think it's too late to wait for the page to become visible. In fact the code used to be there and wasn't called when the page was skipped.
I do not understand, why you need to reset the task. I have an impression that you have that conditional skip of the task implemented incorrectly.
It's just a guess, but I assume that you skip the task page using the ShouldSkipPage. So the task stays checked, if it was enabled in a previous installation.
Do not use the ShouldSkipPage for this, use the Check parameter instead. If there's a single task only that is conditionally disabled using the Check parameter, whole task page gets skipped.
[Tasks]
Name: DeleteConfig; Description: "{cm:Task_DeleteConfig}"; Flags: unchecked; \
Check: UseDeleteConfig
[Code]
function UseDeleteConfig: Boolean;
begin
Result := IsDowngradeSetup;
end;
To answer your actual question, you can do this:
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageId = wpReady then
begin
if PrevInstallExists then
begin
{ In Inno Setup 6, you can use WizardSelectTasks }
WizardForm.TasksList.Checked[0] := False;
end;
end;
end;
function UpdateReadyMemo(
Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo,
MemoGroupInfo, MemoTasksInfo: String): String;
begin
if PrevInstallExists then
begin
MemoTasksInfo := '';
end;
end;
Though again, I do not think, that this is a good solution.
Or even easier, use the UsePreviousTasks:
[Setup]
UsePreviousTasks=no
Or similarly using the checkedonce flag:
[Tasks]
Name: DeleteConfig; Description: "{cm:Task_DeleteConfig}"; Flags: unchecked checkedonce

how to refresh a page in inno setup

I am completely new to inno setup.
I have an existing inno setup code which loads all the pages in the InitializeWizard(). I am trying to change the caption dynamically in the next page based on the radio button selected in the previous page.
ExpandConstant('Special note for the Microsoft ' + SelectedSQLServerVersion + ' Setup')
Here the SelectedSQLServerVersion is a variable which holds the dynamic value from the previous page and I can see the value in log. I tried to load the page again and was expecting the variable will be replaced with the dynamic value in the second time, but it was empty. Is there any way to solve this.
Thanks in advance,
DeeJay
Wizard pages have two common properties for the top bar labels, Caption and Description. In your case you can update them e.g. when the page is just displayed, from the CurPageChanged event:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Code]
var
MyPage: TWizardPage;
procedure InitializeWizard;
begin
MyPage := CreateCustomPage(wpWelcome, 'Caption', 'Description');
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = MyPage.ID then
begin
MyPage.Caption := 'New caption';
MyPage.Description := 'New description';
end;
end;

Resources