Add 4 license pages in Inno Setup - user-interface

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;

Related

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

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;

Pascal: Error trying to rewrite array and assistance with printing my array

So i'm working on this pascal application which has a menu where you can do multiple things.
After entering an album (which is what my program does) and trying to edit it by writing over the current album I get an error as shown in the image.
There have been no errors when compiling except the warning:
(100,9) Warning: Function result variable does not seem to initialized
Here is my code:
program MusicPlayer;
uses TerminalUserInput;
type
// You should have a track record
TrackRec = record
name: String;
location: String;
end;
type TrackArray = array of TrackRec;
GenreType = (Pop, Rap, Rock, Classic);
AlbumRec = Record
name: String;
genre: GenreType;
location: array of TrackRec; // this and track should be track: array of TrackRec
numberOfTracks: Integer;
tracks: TrackArray;
end;
type AlbumArray = array of AlbumRec; // this should be an array of AlbumRec
function ReadGenre(prompt: String): GenreType;
var
option: Integer;
begin
WriteLn('Press 1 for Pop');
WriteLn('Press 2 for Rap');
WriteLn('Press 3 for Rock');
WriteLn('Press 4 for Classic');
option := ReadInteger(prompt);
while (option<1) or (option>3) do
begin
WriteLn('Please enter a number between 1-4');
option := ReadInteger(prompt);
end;
case option of
1: result := Pop;
2: result := Rap;
3: result := Rock;
else
result := Classic;
end;
end;
function CheckLength(prompt: string): Integer;
var
i: Integer;
begin
i := ReadInteger(prompt);
while (i < 0) or (i > 20) do
begin
WriteLn('Please enter a number between 1-20');
i := ReadInteger(prompt);
end;
result := i;
end;
function ReadTracks(count: Integer): TrackArray;
var
i: Integer;
begin
setLength(result, count);
for i := 0 to High(result) do
begin
result[i].name := ReadString('Track Name: ');
result[i].location := ReadString('Track Location: ');
end;
end;
function ReadAlbum(): AlbumRec;
begin
result.name := ReadString('What is the name of the album?');
result.genre := ReadGenre('What is the genre of the album?');
result.numberOfTracks := CheckLength('How many tracks are in the album?');
result.tracks := ReadTracks(result.numberOfTracks);
end;
function ReadAlbums(count: Integer): AlbumArray;
var
i: Integer;
begin
SetLength(result, count);
for i := 0 to High(result) do
begin
result[i] := ReadAlbum();
end;
end;
function ChangeAlbum(count: Integer): AlbumArray;
var
i: Integer;
begin
for i := count to count do
begin
result[i] := ReadAlbum();
end;
end;
procedure PrintAlbum(count: Integer; album: array of AlbumRec);
var
i: Integer;
begin
if count = 1 then
begin
for i := 0 to High(album) do
begin
WriteLn('Album Number: ', i);
WriteLn('Album name is: ', album[i].name);
WriteLn('Album genre is: ', album[i].genre);
end
end;
for i := 1 to count - 1 do
begin
WriteLn('Album name is: ', album[i].name);
WriteLn('Album genre is: ', album[i].genre);
end;
end;
procedure PrintTrack(tracks: TrackArray);
var
i: Integer;
begin
i := ReadInteger('Which track number do you wish to play?');
i := i - 1;
WriteLn('Now playing track: ', tracks[i].name);
WriteLn('Track location: ', tracks[i].location);
end;
function CheckIfFinished(): Boolean;
var answer: String;
begin
WriteLn('Do you want to enter another set of tracks? ');
ReadLn(answer);
LowerCase(answer);
case answer of
'no': result := true;
'n': result := true;
'x': result := true;
else
result := false;
end;
end;
procedure Main();
var
i, count, select, change: Integer;
albums: AlbumArray;
begin
WriteLn('Please select an option: ');
WriteLn('-------------------------');
WriteLn('1. Read Albums');
WriteLn('2. Display Albums');
WriteLn('3. Select an Album');
WriteLn('4. Update an Album');
WriteLn('5. Exit');
WriteLn('-------------------------');
repeat
i := ReadInteger('Your Option:');
case i of
1:
begin
count := ReadInteger('How many albums: ');
albums := ReadAlbums(count);
end;
2:
begin
WriteLn('1. Display All Albums');
WriteLn('2. Display All Albums by Genre');
select := ReadInteger('Your Option: ');
if i = 1 then
begin
PrintAlbum(select, albums);
end;
// if i = 2 then
// WriteLn('1. Pop');
// WriteLn('2. Rap');
// WriteLn('3. Rock');
// WriteLn('4. Classic');
// albums := ReadAlbums(count);
end;
3:
begin
select := ReadInteger('Which album would you like to play? ');
PrintAlbum(select, albums);
PrintTrack(albums[select-1].tracks);
end;
4:
begin
change := ReadInteger('Which album would you like to edit?');
albums := ChangeAlbum(change);
end;
end;
until i = 5;
end;
begin
Main();
end.
The function that the warning refers to, on line 100, is
function ChangeAlbum(count: Integer): AlbumArray;
var
i: Integer;
begin
for i := count to count do
begin
result[i] := ReadAlbum();
end;
end;
The warning says:
Warning: Function result variable does not seem to initialized
And indeed the result variable has not been initialized.
The design of the function is wrong though. You are trying to modify an existing element in an array. You should not be returning a new array. The function is not necessary though. You should simply remove it. Then you need to look at the one place where you call the function.
change := ReadInteger('Which album would you like to edit?');
albums := ChangeAlbum(change);
You should instead code that like this:
change := ReadInteger('Which album would you like to edit?');
albums[change] := ReadAlbum();
I've not checked anything else in your program. I would not be surprised if there are other problems. I've just tried to address the specific question that you asked.

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