Inno Setup: Custom page to select Update or Remove/Uninstall - installation

I need to create a custom uninstall page that let the user choose if he wants to update the software or uninstall it (if the software is already installed).
I have already done my custom page and is like this:
How can I get the values of those radio buttons when the user click the Next Button?
And, how can I update or uninstall the program?
UPDATE:
procedure InitializeWizard();
var
InstallPath: String;
BackgroundBitmapImage: TBitmapImage;
BmpFileName : String;
Temp : String;
AppId : String;
Color : String;
begin
AppId:=ExpandConstant('{#AppId}');
if(AppIsInstalled(AppId, InstallPath)) Then
begin
UpdateRemovePageID := RepairRemove_CreatePage(wpWelcome);
end;
BmpFileName:= ExpandConstant('{src}\Background.bmp');
if FileExists(BmpFileName) then begin
BackgroundBitmapImage := TBitmapImage.Create(MainForm);
BackgroundBitmapImage.Align := alClient;
BackgroundBitmapImage.Autosize := True;
BackgroundBitmapImage.Center := True;
BackgroundBitmapImage.Bitmap.LoadFromFile(BmpFileName);
end;
BackgroundBitmapImage.BackColor := StringToColor('8cceff');
BackgroundBitmapImage.Parent := MainForm;
WizardForm.Caption := MainForm.Caption;
if(FileExists(ExpandConstant('{src}\WizImage.bmp'))) then begin
WizardForm.WizardBitmapImage.Bitmap.LoadFromFile(ExpandConstant('{src}') + '\WizImage.bmp');
end
if(FileExists(ExpandConstant('{src}\WizSmallImage.bmp'))) then begin
WizardForm.WizardSmallBitmapImage.Bitmap.LoadFromFile(ExpandConstant('{src}') + '\WizSmallImage.bmp');
end
end;
function RepairRemove_CreatePage(PreviousPageId: Integer): Integer;
var
Page: TWizardPage;
UpdateBmpFileName : String;
RemoveBmpFileName : String;
begin
Page := CreateCustomPage(PreviousPageId, ExpandConstant('{cm:RepairRemove_Caption}'), ExpandConstant('{cm:RepairRemove_Description}'));
BitmapImageUpdate := TBitmapImage.Create(Page);
UpdateBmpFileName := ExpandConstant('{tmp}\Update.bmp');
if not FileExists(UpdateBmpFileName) then begin
ExtractTemporaryFile(ExtractFileName(UpdateBmpFileName));
end;
BitmapImageUpdate.Bitmap.LoadFromFile(UpdateBmpFileName);
with BitmapImageUpdate do
begin
Parent := Page.Surface;
Left := ScaleX(64);
Top := ScaleY(64);
Width := ScaleX(32);
Height := ScaleY(32);
end;
Label1 := TLabel.Create(Page);
with Label1 do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:RepairRemove_Label1_Caption0}');
Left := ScaleX(120);
Top := ScaleY(72);
Width := ScaleX(243);
Height := ScaleY(13);
end;
BitmapImageRemove := TBitmapImage.Create(Page);
RemoveBmpFileName := ExpandConstant('{tmp}\TrashCan.bmp');
if not FileExists(RemoveBmpFileName) then begin
ExtractTemporaryFile(ExtractFileName(RemoveBmpFileName));
end;
BitmapImageRemove.Bitmap.LoadFromFile(RemoveBmpFileName);
with BitmapImageRemove do
begin
Parent := Page.Surface;
Left := ScaleX(64);
Top := ScaleY(120);
Width := ScaleX(32);
Height := ScaleY(32);
end;
Label2 := TLabel.Create(Page);
with Label2 do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:RepairRemove_Label2_Caption0}');
Left := ScaleX(120);
Top := ScaleY(128);
Width := ScaleX(243);
Height := ScaleY(13);
end;
UpdateButton := TRadioButton.Create(Page);
with UpdateButton do
begin
Parent := Page.Surface;
Caption := ExpandConstant('');
Left := ScaleX(32);
Top := ScaleY(72);
Width := ScaleX(17);
Height := ScaleY(17);
TabOrder := 0;
end;
RemoveButton := TRadioButton.Create(Page);
with RemoveButton do
begin
Parent := Page.Surface;
Caption := ExpandConstant('');
Left := ScaleX(32);
Top := ScaleY(128);
Width := ScaleX(17);
Height := ScaleY(17);
Checked := True;
TabOrder := 1;
TabStop := True;
end;
with Page do
begin
OnActivate := #RepairRemove_Activate;
OnShouldSkipPage := #RepairRemove_ShouldSkipPage;
OnBackButtonClick := #RepairRemove_BackButtonClick;
OnNextButtonClick := #RepairRemove_NextButtonClick;
OnCancelButtonClick := #RepairRemove_CancelButtonClick;
end;
Result := Page.ID;
end;
function RepairRemove_NextButtonClick(Page: TWizardPage): Boolean;
begin
Result := True;
//What I have to do here to correctly handle the user choice?
end;

how can I update or uninstall the program?
Update - That's what the installer does by default.
Uninstall - See How to detect old installation and offer removal?
You will also want to abort the installer after uninstallation:
Exit from Inno Setup Installation from [code].
function RepairRemove_NextButtonClick(Page: TWizardPage): Boolean;
begin
if RemoveButton.Checked then
begin
{ Uninstall here }
{ And abort installer }
ExitProcess(1);
end;
Result := True;
end;

Related

Add 4 license pages in Inno Setup

I followed Martin's answer here to create UI for 4 license pages in my Inno Setup installer.
The code looks like below (work in progress..)
[Files]
Source: "license2_english.txt"; Flags: dontcopy
Source: "license3_english.txt"; Flags: dontcopy
Source: "license4_english.txt"; Flags: dontcopy
[Code]
var
SecondLicensePage: TOutputMsgMemoWizardPage;
License2AcceptedRadio: TRadioButton;
License2NotAcceptedRadio: TRadioButton;
ThirdLicensePage: TOutputMsgMemoWizardPage;
License3AcceptedRadio: TRadioButton;
License3NotAcceptedRadio: TRadioButton;
FourthLicensePage: TOutputMsgMemoWizardPage;
License4AcceptedRadio: TRadioButton;
License4NotAcceptedRadio: TRadioButton;
procedure CheckLicense2Accepted(Sender: TObject);
begin
// Update Next button when user (un)accepts the license
WizardForm.NextButton.Enabled := License2AcceptedRadio.Checked;
end;
procedure CheckLicense3Accepted(Sender: TObject);
begin
// Update Next button when user (un)accepts the license
WizardForm.NextButton.Enabled := License3AcceptedRadio.Checked;
end;
procedure CheckLicense4Accepted(Sender: TObject);
begin
// Update Next button when user (un)accepts the license
WizardForm.NextButton.Enabled := License4AcceptedRadio.Checked;
end;
function CloneLicenseRadioButtonL2(Source: TRadioButton): TRadioButton;
begin
Result := TRadioButton.Create(WizardForm);
Result.Parent := SecondLicensePage.Surface;
Result.Caption := Source.Caption;
Result.Left := Source.Left;
Result.Top := Source.Top;
Result.Width := Source.Width;
Result.Height := Source.Height;
Result.Anchors := Source.Anchors;
Result.OnClick := #CheckLicense2Accepted;
end;
function CloneLicenseRadioButtonL3(Source: TRadioButton): TRadioButton;
begin
Result := TRadioButton.Create(WizardForm);
Result.Parent := ThirdLicensePage.Surface;
Result.Caption := Source.Caption;
Result.Left := Source.Left;
Result.Top := Source.Top;
Result.Width := Source.Width;
Result.Height := Source.Height;
Result.Anchors := Source.Anchors;
Result.OnClick := #CheckLicense3Accepted;
end;
function CloneLicenseRadioButtonL4(Source: TRadioButton): TRadioButton;
begin
Result := TRadioButton.Create(WizardForm);
Result.Parent := FourthLicensePage.Surface;
Result.Caption := Source.Caption;
Result.Left := Source.Left;
Result.Top := Source.Top;
Result.Width := Source.Width;
Result.Height := Source.Height;
Result.Anchors := Source.Anchors;
Result.OnClick := #CheckLicense4Accepted;
end;
//Create license wizards
procedure InitializeWizard();
var
LicenseFileNameL2: string;
LicenseFileNameL3: string;
LicenseFilenameL4: string;
LicenseFilePathL2: string;
LicenseFilePathL3: string;
LicenseFilePathL4: string;
begin
Log(Format('Temp : %s', [ExpandConstant('{tmp}')]));
// Create second license page, with the same labels as the original license page
SecondLicensePage :=
CreateOutputMsgMemoPage(
wpLicense, SetupMessage(msgWizardLicense), SetupMessage(msgLicenseLabel),
SetupMessage(msgLicenseLabel3), '');
// Create third license page, with the same labels as the original license page
ThirdLicensePage :=
CreateOutputMsgMemoPage(
wpLicense, SetupMessage(msgWizardLicense), SetupMessage(msgLicenseLabel),
SetupMessage(msgLicenseLabel3), '');
FourthLicensePage :=
CreateOutputMsgMemoPage(
wpLicense, SetupMessage(msgWizardLicense), SetupMessage(msgLicenseLabel),
SetupMessage(msgLicenseLabel3), '');
// Shrink license box to make space for radio buttons
SecondLicensePage.RichEditViewer.Height := WizardForm.LicenseMemo.Height;
ThirdLicensePage.RichEditViewer.Height := WizardForm.LicenseMemo.Height;
FourthLicensePage.RichEditViewer.Height := WizardForm.LicenseMemo.Height;
// Load license
// Loading ex-post, as Lines.LoadFromFile supports UTF-8,
// contrary to LoadStringFromFile.
LicenseFileNameL2 := 'license2_english.txt';
LicenseFileNameL3 := 'license3_english.txt';
LicenseFileNameL4 := 'license4_english.txt';
LicenseFilePathL2 := ExpandConstant('{tmp}\' + LicenseFileNameL2);
LicenseFilePathL3 := ExpandConstant('{tmp}\' + LicenseFileNameL3);
LicenseFilePathL4 := ExpandConstant('{tmp}\' + LicenseFileNameL4);
ExtractTemporaryFile(LicenseFileNameL2);
ExtractTemporaryFile(LicenseFileNameL3);
ExtractTemporaryFile(LicenseFileNameL4);
SecondLicensePage.RichEditViewer.Lines.LoadFromFile(LicenseFilePathL2);
ThirdLicensePage.RichEditViewer.Lines.LoadFromFile(LicenseFilePathL3);
FourthLicensePage.RichEditViewer.Lines.LoadFromFile(LicenseFilePathL4);
DeleteFile(LicenseFilePathL2);
DeleteFile(LicenseFilePathL3);
DeleteFile(LicenseFilePathL4);
// Clone accept/do not accept radio buttons for the second license
License2AcceptedRadio :=
CloneLicenseRadioButtonL2(WizardForm.LicenseAcceptedRadio);
License2NotAcceptedRadio :=
CloneLicenseRadioButtonL2(WizardForm.LicenseNotAcceptedRadio);
License3AcceptedRadio :=
CloneLicenseRadioButtonL3(WizardForm.LicenseAcceptedRadio);
License3NotAcceptedRadio :=
CloneLicenseRadioButtonL3(WizardForm.LicenseNotAcceptedRadio);
License4AcceptedRadio :=
CloneLicenseRadioButtonL4(WizardForm.LicenseAcceptedRadio);
License4NotAcceptedRadio :=
CloneLicenseRadioButtonL4(WizardForm.LicenseNotAcceptedRadio);
// Initially not accepted
License2NotAcceptedRadio.Checked := True;
License3NotAcceptedRadio.Checked := True;
License4NotAcceptedRadio.Checked := True;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
// Update Next button when user gets to second license page
if CurPageID = SecondLicensePage.ID then
begin
CheckLicense2Accepted(nil);
end;
end;
procedure CurPageChangedL3(CurPageID: Integer);
begin
// Update Next button when user gets to second license page
if CurPageID = ThirdLicensePage.ID then
begin
CheckLicense3Accepted(nil);
end;
end;
procedure CurPageChangedL4(CurPageID: Integer);
begin
// Update Next button when user gets to second license page
if CurPageID = FourthLicensePage.ID then
begin
CheckLicense4Accepted(nil);
end;
end;
With this code, I see the following issues:
License 4 page comes up before License 2 and 3
In page 2, initially the radio button is initialized to "I do not accept". In this case the "Next" button is enabled and user can move to the next screen.
Image shows Next button enabled even when "I do not accept is selected". Also License 4 is coming before License 2
I know I have made a basic mistake somewhere when I tried to expand Martin's answer to cover additional licenses, but I couldn't figure it out yet.
Let me know if anyone has a idea to fix/debug this.
Thanks!
a
I'm not gonna try to fix your issues as the way you triplicated all the code is pretty inefficient and hard to maintain. Factor out the creation of the additional license pages instead. Something like this:
[Setup]
LicenseFile=license1.txt
[Files]
Source: "license2.txt"; Flags: dontcopy
Source: "license3.txt"; Flags: dontcopy
Source: "license4.txt"; Flags: dontcopy
[Code]
var
LicenseAcceptedRadioButtons: array of TRadioButton;
procedure CheckLicenseAccepted(Sender: TObject);
begin
// Update Next button when user (un)accepts the license
WizardForm.NextButton.Enabled :=
LicenseAcceptedRadioButtons[TComponent(Sender).Tag].Checked;
end;
procedure LicensePageActivate(Sender: TWizardPage);
begin
// Update Next button when user gets to second license page
CheckLicenseAccepted(LicenseAcceptedRadioButtons[Sender.Tag]);
end;
function CloneLicenseRadioButton(
Page: TWizardPage; Source: TRadioButton): TRadioButton;
begin
Result := TRadioButton.Create(WizardForm);
Result.Parent := Page.Surface;
Result.Caption := Source.Caption;
Result.Left := Source.Left;
Result.Top := Source.Top;
Result.Width := Source.Width;
Result.Height := Source.Height;
// Needed for WizardStyle=modern / WizardResizable=yes
Result.Anchors := Source.Anchors;
Result.OnClick := #CheckLicenseAccepted;
Result.Tag := Page.Tag;
end;
var
LicenseAfterPage: Integer;
procedure AddLicensePage(LicenseFileName: string);
var
Idx: Integer;
Page: TOutputMsgMemoWizardPage;
LicenseFilePath: string;
RadioButton: TRadioButton;
begin
Idx := GetArrayLength(LicenseAcceptedRadioButtons);
SetArrayLength(LicenseAcceptedRadioButtons, Idx + 1);
Page :=
CreateOutputMsgMemoPage(
LicenseAfterPage, SetupMessage(msgWizardLicense),
SetupMessage(msgLicenseLabel), SetupMessage(msgLicenseLabel3), '');
Page.Tag := Idx;
// Shrink license box to make space for radio buttons
Page.RichEditViewer.Height := WizardForm.LicenseMemo.Height;
Page.OnActivate := #LicensePageActivate;
// Load license
// Loading ex-post, as Lines.LoadFromFile supports UTF-8,
// contrary to LoadStringFromFile.
ExtractTemporaryFile(LicenseFileName);
LicenseFilePath := ExpandConstant('{tmp}\' + LicenseFileName);
Page.RichEditViewer.Lines.LoadFromFile(LicenseFilePath);
DeleteFile(LicenseFilePath);
// Clone accept/do not accept radio buttons
RadioButton :=
CloneLicenseRadioButton(Page, WizardForm.LicenseAcceptedRadio);
LicenseAcceptedRadioButtons[Idx] := RadioButton;
RadioButton :=
CloneLicenseRadioButton(Page, WizardForm.LicenseNotAcceptedRadio);
// Initially not accepted
RadioButton.Checked := True;
LicenseAfterPage := Page.ID;
end;
procedure InitializeWizard();
begin
LicenseAfterPage := wpLicense;
AddLicensePage('license2.txt');
AddLicensePage('license3.txt');
AddLicensePage('license4.txt');
end;

Highlight word in TListView Delphi 7

I'm trying to highlight a word from TListView but I can't make it work. My first attempt is to highlight the first letter of each row but won't work. TListView won't display anything. Here is my code:
procedure TfrmMain.lvMainDrawItem(Sender: TCustomListView; Item: TListItem; Rect: TRect; State: TOwnerDrawState);
var
r : TRect;
c : TCanvas;
begin
r := Item.DisplayRect(drBounds);
c := Sender.Canvas;
c.Brush.Color := clWindow;
c.Font.Color := clWindowText;
c.TextRect(r, 1, 1, 'a');
end;
I based my code here but it is written in XE10.4. I'm using delphi 7. How can I highlight word/first letter of each row in TListView?
You are drawing outside of the item's bounding rectangle, that is why you don't see anything. The X,Y coordinates you specify to TextRect() are relative to the top/left corner of the ListView's client area, but are clipped by the specified TRect.
Try this instead:
c.TextRect(r, r.Left+1, r.Top+1, 'a');
Here is my working code:
set and populate list view
procedure TForm1.Button1Click(Sender: TObject);
begin
ListView1.Clear;
ListView1.ViewStyle := vsReport;
ListView1.RowSelect := True;
ListView1.Items.Add.Caption := 'banana';
ListView1.Items.Add.Caption := 'apple and banana';
ListView1.Items.Add.Caption := 'orange apple and banana';
ListView1.Items.Add.Caption := 'banana and orange';
ListView1.Items.Add.Caption := 'banana orange and apple';
ListView1.Items.Add.Caption := 'appleXandXbanana';
ListView1.Items.Add.Caption := 'orangeXappleXand banana';
ListView1.Items.Add.Caption := 'bananaXandXorange';
ListView1.Items.Add.Caption := 'bananaXorangeXandXapple';
end;
painting
procedure TForm1.ListView1DrawItem(Sender: TCustomListView;Item: TListItem; Rect: TRect; State: TOwnerDrawState);
var
r : TRect;
c : TCanvas;
sText : string;
sTemp : string;
nPos : Integer;
nLen : Integer;
lv : TListView;
begin
if item = nil then
begin
Exit;
end;
r := Item.DisplayRect(drBounds);
c := Sender.Canvas;
lv := TListView(Sender);
// fKeyword := 'apple';
sText := Item.Caption;
nLen := Length(fKeyword);
nPos := AnsiPos(fKeyword, sText);
// first part : before match
sTemp := Copy(sText, 1, nPos - 1);
if sTemp <> '' then
begin
c.Brush.Color := clWindow;
c.Font.Color := clWindowText;
c.TextRect(r, r.Left, r.Top, sTemp);
Inc(r.Left, c.TextWidth(sTemp));
end;
// second part: match
sTemp := Copy(sText, nPos, nLen);
if (nPos > 0) and (sTemp <> '') then
begin
c.Brush.Color := clRed;
c.Font.Color := clBlue;
c.TextRect(r, r.Left + 1, r.Top, sTemp);
Inc(r.Left, c.TextWidth(sTemp));
end;
// third part : after match
if nPos = 0 then
begin
sTemp := sText;
end
else
begin
sTemp := Copy(sText, nPos + nLen, Length(sText) - nPos - nLen + 1);
end;
c.Brush.Color := clWindow;
c.Font.Color := clWindowText;
c.TextRect(r, r.Left + 1, r.Top, sTemp);
Inc(r.Left, c.TextWidth(sTemp));
if odFocused in State then
begin
lv.Canvas.Brush.Style := bsSolid;
lv.Canvas.Brush.Color := clBlack;
lv.Canvas.Font.Color := clWhite;
DrawFocusRect(lv.Canvas.Handle, Rect);
end;
end;

TChart : The check/unchecked event not working in ExtraLegendTool

I displayed an ExtraLegendTool with checkboxes but the checkbox event is not working.
Here is the code to display the ExtraLegend:
procedure TFRChart.TCChartAfterDraw(Sender: TObject);
begin
if CKDisplay.Checked then
begin
with ExtraLegend do
begin
Active:= True;
Series := TBarSeries(Self.FindComponent('SeriesTotal'));
with Legend do
begin
LegendStyle := lsAuto;
CheckBoxes := True;
//MaxNumRows := 3;
CustomPosition := True;
Left:= TCChart.Legend.Left;
Top:= TCChart.Legend.ShapeBounds.Bottom + 10;
Width := TCChart.Legend.Width;
ShapeBounds.Right := TCChart.Legend.ShapeBounds.Bottom;
DrawLegend;
end;
end;
end;
end;
Please check the folowing image for more details :
As you can see in the image, I have 2 legends one of type 'Chart1.Legend.LegendStyle := lsSeriesGroups' and the other one is an ExtraLegend.
How can I NOT display all the blue bars for all the series groups when I uncheck the blue series in the Extralegend?
You can use the ExtraLegendTool Clicked() function at the chart OnClick event to get the item of the legend that has been clicked. Then, you can activate/deactivate any series you desire.
This simple example seems to work fine for me here:
procedure TForm1.Chart1Click(Sender: TObject);
var MousePos: TPoint;
index: Integer;
begin
MousePos:=Chart1.GetCursorPos;
index:=ChartTool1.Legend.Clicked(MousePos);
while (index>-1) and (index<Chart1.SeriesCount) do
begin
Chart1[index].Active:=not Chart1[index].Active;
index:=index+3;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.SeriesGroups.Add;
Chart1.SeriesGroups.Add;
Chart1.SeriesGroups[0].Name:='This is Group 1';
Chart1.SeriesGroups[1].Name:='This is Group 2';
for i:=0 to 9 do
with Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
FillSampleValues(5);
if (i<3) then
begin
Chart1.SeriesGroups[0].Add(Chart1[i]);
StackGroup:=0;
end
else
begin
Chart1.SeriesGroups[1].Add(Chart1[i]);
StackGroup:=1;
end;
MultiBar:=mbStacked;
end;
Chart1.Legend.LegendStyle := lsSeriesGroups;
Chart1.Draw;
with ChartTool1 do
begin
Active:= True;
//Series := TBarSeries(Self.FindComponent('SeriesTotal'));
Series := Chart1[0];
with Legend do
begin
LegendStyle := lsAuto;
CheckBoxes := True;
MaxNumRows := 3;
CustomPosition := True;
Left:= Chart1.Legend.Left;
Top:= Chart1.Legend.ShapeBounds.Bottom + 10;
Width := Chart1.Legend.Width;
ShapeBounds.Right := Chart1.Legend.ShapeBounds.Bottom;
DrawLegend;
end;
end;
end;

Can I obtain information about the windows explorer tree (left pane)?

Is it possible to obtain information about the Windows Explorer tree (what nodes are currently expanded, when a node is expanded etc.)?
Starting from Vista there is official way to communicate with tree in Explorer window. This way uses INameSpaceTreeControl interface.
If you want to get INameSpaceTreeControl from external app you must:
1) Get IDispatch of shell window:
var
ShellWindows: IShellWindows;
i: Integer;
Dispatch: IDispatch;
SL: TStrings;
begin
OleCheck(CoCreateInstance(CLASS_ShellWindows, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IShellWindows, ShellWindows));
try
for i := ShellWindows.Count - 1 downto 0 do
begin
Dispatch := ShellWindows.Item(i);
try
SL := CreateNodeList(Dispatch);
try
Memo1.Lines.Assign(SL);
finally
SL.Free;
end;
Exit;
finally
Dispatch := nil;
end;
end;
finally
ShellWindows := nil;
end;
end;
2) Query IServiceProvider from IDispatch.
3) Query INameSpaceTreeControl from IServiceProvider.
4) After this you can enum elements of tree. I used the following algorithm:
function GetItemName(AShellItem: IShellItem): UnicodeString;
var
Name: PWideChar;
begin
OleCheck(AShellItem.GetDisplayName(SIGDN_NORMALDISPLAY, Name));
try
Result := Name;
finally
CoTaskMemFree(Name);
end;
end;
function GetLevelSpaces(ALevel: Integer): UnicodeString;
var
i: Integer;
begin
Result := '';
for i := 0 to ALevel - 1 do
Result := Result + ' ';
end;
function CalcLavel(AShellItem: IShellItem): Integer;
var Parent: IShellItem;
begin
Result := 0;
if Succeeded(AShellItem.GetParent(Parent)) then
try
Inc(Result);
Result := Result + CalcLavel(Parent);
finally
Parent := nil;
end;
end;
function GetExpanded(ATree: INameSpaceTreeControl; AItem: IShellItem): WideChar;
var
State: DWORD;
begin
OleCheck(ATree.GetItemState(AItem, NSTCIS_EXPANDED, State));
if State and NSTCIS_EXPANDED <> 0 then Result := '+'
else Result := '-';
end;
function CreateNodeList(ADispatch: IDispatch): TStrings;
var
ServiceProvider: IServiceProvider;
Tree: INameSpaceTreeControl;
L: Integer;
ShellItem, ShellItem2: IShellItem;
begin
OleCheck(ADispatch.QueryInterface(IServiceProvider, ServiceProvider));
try
OleCheck(ServiceProvider.QueryService(SID_SNavigationPane, INameSpaceTreeControl, Tree));
try
Result := TStringList.Create;
try
if Succeeded(Tree.GetNextItem(nil, NSTCGNI_CHILD, ShellItem)) then
repeat
try
L := CalcLavel(ShellItem);
Result.Add(GetLevelSpaces(L - 1) + GetExpanded(Tree, ShellItem) + ' ' + GetItemName(ShellItem));
finally
ShellItem2 := ShellItem;
ShellItem := nil;
end;
until Failed(Tree.GetNextItem(ShellItem2, NSTCGNI_NEXTVISIBLE, ShellItem));
finally
ShellItem2 := nil;
end;
finally
Tree := nil;
end;
finally
ServiceProvider := nil;
end;
end;
Result:
If you want to subscribe to tree actions use INameSpaceTreeControl.TreeAdvise.

inno setup custom page with checkbox and dropdown list

Is it possible to have a custom page with a drop down list, check boxes, and a button possibly changing the check boxes based on what is chosen from the drop down list. The button will just be used to display a readme text file. I am really not familiar with python scripting but have managed to create a drop down list.
You might take a script like this as an inspiration:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
OutputDir=userdocs:Inno Setup Examples Output
[Files]
Source: "Readme.txt"; Flags: dontcopy
[Code]
var
Button: TNewButton;
ComboBox: TNewComboBox;
CheckBox1: TNewCheckBox;
CheckBox2: TNewCheckBox;
CustomPage: TWizardPage;
procedure ComboBoxChange(Sender: TObject);
begin
case ComboBox.ItemIndex of
0:
begin
CheckBox1.Checked := True;
CheckBox2.Checked := False;
end;
1:
begin
CheckBox1.Checked := False;
CheckBox2.Checked := True;
end;
2:
begin
CheckBox1.Checked := True;
CheckBox2.Checked := True;
end;
3:
begin
CheckBox1.Checked := False;
CheckBox2.Checked := False;
end;
end;
end;
procedure ButtonClick(Sender: TObject);
var
ErrorCode: Integer;
begin
ExtractTemporaryFile('Readme.txt');
if not ShellExec('', ExpandConstant('{tmp}\Readme.txt'), '', '',
SW_SHOW, ewNoWait, ErrorCode)
then
MsgBox(SysErrorMessage(ErrorCode), mbError, MB_OK);
end;
procedure InitializeWizard;
var
DescLabel: TLabel;
begin
CustomPage := CreateCustomPage(wpSelectDir, 'Caption', 'Description');
DescLabel := TLabel.Create(WizardForm);
DescLabel.Parent := CustomPage.Surface;
DescLabel.Left := 0;
DescLabel.Top := 0;
DescLabel.Caption := 'Select an item...';
ComboBox := TNewComboBox.Create(WizardForm);
ComboBox.Parent := CustomPage.Surface;
ComboBox.Left := 0;
ComboBox.Top := DescLabel.Top + DescLabel.Height + 6;
ComboBox.Width := 220;
ComboBox.Style := csDropDownList;
ComboBox.Items.Add('Check CheckBox1');
ComboBox.Items.Add('Check CheckBox2');
ComboBox.Items.Add('Check CheckBox1 and CheckBox2');
ComboBox.Items.Add('Uncheck CheckBox1 and CheckBox2');
ComboBox.OnChange := #ComboBoxChange;
CheckBox1 := TNewCheckBox.Create(WizardForm);
CheckBox1.Parent := CustomPage.Surface;
CheckBox1.Left := 0;
CheckBox1.Top := ComboBox.Top + ComboBox.Height + 6;
CheckBox1.Caption := 'CheckBox1';
CheckBox2 := TNewCheckBox.Create(WizardForm);
CheckBox2.Parent := CustomPage.Surface;
CheckBox2.Left := 0;
CheckBox2.Top := CheckBox1.Top + CheckBox1.Height + 6;
CheckBox2.Caption := 'CheckBox2';
Button := TNewButton.Create(WizardForm);
Button.Parent := CustomPage.Surface;
Button.Left := 0;
Button.Top := CheckBox2.Top + CheckBox2.Height + 6;
Button.Caption := 'Readme';
Button.OnClick := #ButtonClick;
end;

Resources