Inno Setup: Enlarge component page only with preview and description - inno-setup

I need help from you to merge two scripts found here to have preview image on a maximized components windows:
Add image into the components list - component description
Long descriptions on Inno Setup components
Edit: Thanks to Martin Prikryl. With his patience and his help, I successfully merge these two scripts.
Your preview image should have a resolution of 208x165 in .bmp
[Files]
...
Source: "1.bmp"; Flags: dontcopy
Source: "2.bmp"; Flags: dontcopy
Source: "3.bmp"; Flags: dontcopy
Source: "InnoCallback.dll"; Flags: dontcopy
[Code]
var
LastMouse: TPoint;
type
TTimerProc = procedure(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
function GetCursorPos(var lpPoint: TPoint): BOOL; external 'GetCursorPos#user32.dll stdcall';
function SetTimer(hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external 'SetTimer#user32.dll stdcall';
function ScreenToClient(hWnd: HWND; var lpPoint: TPoint): BOOL; external 'ScreenToClient#user32.dll stdcall';
function ClientToScreen(hWnd: HWND; var lpPoint: TPoint): BOOL; external 'ClientToScreen#user32.dll stdcall';
function ListBox_GetItemRect(const hWnd: HWND; const Msg: Integer; Index: LongInt; var Rect: TRect): LongInt; external 'SendMessageW#user32.dll stdcall';
const
LB_GETITEMRECT = $0198;
LB_GETTOPINDEX = $018E;
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord; external 'wrapcallback#files:InnoCallback.dll stdcall';
function FindControl(Parent: TWinControl; P: TPoint): TControl;
var
Control: TControl;
WinControl: TWinControl;
I: Integer;
P2: TPoint;
begin
for I := 0 to Parent.ControlCount - 1 do
begin
Control := Parent.Controls[I];
if Control.Visible and
(Control.Left <= P.X) and (P.X < Control.Left + Control.Width) and
(Control.Top <= P.Y) and (P.Y < Control.Top + Control.Height) then
begin
if Control is TWinControl then
begin
P2 := P;
ClientToScreen(Parent.Handle, P2);
WinControl := TWinControl(Control);
ScreenToClient(WinControl.Handle, P2);
Result := FindControl(WinControl, P2);
if Result <> nil then Exit;
end;
Result := Control;
Exit;
end;
end;
Result := nil;
end;
function PointInRect(const Rect: TRect; const Point: TPoint): Boolean;
begin
Result := (Point.X >= Rect.Left) and (Point.X <= Rect.Right) and
(Point.Y >= Rect.Top) and (Point.Y <= Rect.Bottom);
end;
function ListBoxItemAtPos(ListBox: TCustomListBox; Pos: TPoint): Integer;
var
Count: Integer;
ItemRect: TRect;
begin
Result := SendMessage(ListBox.Handle, LB_GETTOPINDEX, 0, 0);
Count := ListBox.Items.Count;
while Result < Count do
begin
ListBox_GetItemRect(ListBox.Handle, LB_GETITEMRECT, Result, ItemRect);
if PointInRect(ItemRect, Pos) then Exit;
Inc(Result);
end;
Result := -1;
end;
var
CompLabel: TLabel;
CompImage: TBitmapImage;
LoadingImage: Boolean;
procedure HoverComponentChanged(Index: Integer);
var
Description: string;
Image: string;
ImagePath: string;
begin
case Index of
0: begin Description := 'Component 1'; Image := '1.bmp'; end;
1: begin Description := 'Component 2'; Image := '2.bmp'; end;
2: begin Description := 'Component 3'; Image := '3.bmp'; end;
else
Description := 'Move your mouse over a component to see its description.';
end;
CompLabel.Caption := Description;
if Image <> '' then
begin
// The ExtractTemporaryFile pumps the message queue, prevent recursion
if not LoadingImage then
begin
LoadingImage := True;
try
ImagePath := ExpandConstant('{tmp}\' + Image);
if not FileExists(ImagePath) then
begin
ExtractTemporaryFile(Image);
end;
CompImage.Bitmap.LoadFromFile(ImagePath);
finally
LoadingImage := False;
end;
end;
CompImage.Visible := True;
end
else
begin
CompImage.Visible := False;
end;
end;
procedure HoverTimerProc(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
P: TPoint;
Control: TControl;
Index: Integer;
begin
GetCursorPos(P);
if P <> LastMouse then // just optimization
begin
LastMouse := P;
ScreenToClient(WizardForm.Handle, P);
if (P.X < 0) or (P.Y < 0) or
(P.X > WizardForm.ClientWidth) or (P.Y > WizardForm.ClientHeight) then
begin
Control := nil;
end
else
begin
Control := FindControl(WizardForm, P);
end;
Index := -1;
if Control = WizardForm.ComponentsList then
begin
P := LastMouse;
ScreenToClient(WizardForm.ComponentsList.Handle, P);
Index := ListBoxItemAtPos(WizardForm.ComponentsList, P);
end;
HoverComponentChanged(Index);
end;
end;
type
TPositionStorage = array of Integer;
var
CompPageModified: Boolean;
CompPagePositions: TPositionStorage;
procedure SaveComponentsPage(out Storage: TPositionStorage);
begin
SetArrayLength(Storage, 10);
Storage[0] := WizardForm.Height;
Storage[1] := WizardForm.NextButton.Top;
Storage[2] := WizardForm.BackButton.Top;
Storage[3] := WizardForm.CancelButton.Top;
Storage[4] := WizardForm.ComponentsList.Height;
Storage[5] := WizardForm.OuterNotebook.Height;
Storage[6] := WizardForm.InnerNotebook.Height;
Storage[7] := WizardForm.Bevel.Top;
Storage[8] := WizardForm.BeveledLabel.Top;
Storage[9] := WizardForm.ComponentsDiskSpaceLabel.Top;
end;
procedure LoadComponentsPage(const Storage: TPositionStorage;
HeightOffset: Integer);
begin
if GetArrayLength(Storage) <> 10 then
RaiseException('Invalid storage array length.');
WizardForm.Height := Storage[0] + HeightOffset;
WizardForm.NextButton.Top := Storage[1] + HeightOffset;
WizardForm.BackButton.Top := Storage[2] + HeightOffset;
WizardForm.CancelButton.Top := Storage[3] + HeightOffset;
WizardForm.ComponentsList.Height := Storage[4] + ScaleY(150);
WizardForm.OuterNotebook.Height := Storage[5] + HeightOffset;
WizardForm.InnerNotebook.Height := Storage[6] + HeightOffset;
WizardForm.Bevel.Top := Storage[7] + HeightOffset;
WizardForm.BeveledLabel.Top := Storage[8] + HeightOffset;
WizardForm.ComponentsDiskSpaceLabel.Top := Storage[9] + HeightOffset;
end;
procedure InitializeWizard1();
var
HoverTimerCallback: LongWord;
begin
HoverTimerCallback := WrapTimerProc(#HoverTimerProc, 4);
SetTimer(0, 0, 50, HoverTimerCallback);
CompLabel := TLabel.Create(WizardForm);
CompLabel.Parent := WizardForm.SelectComponentsPage;
CompLabel.Left := WizardForm.ComponentsList.Left;
CompLabel.Width := WizardForm.ComponentsList.Width div 2;
CompLabel.Height := ScaleY(64);
CompLabel.Top := WizardForm.ComponentsList.Top + WizardForm.ComponentsList.Height - CompLabel.Height + ScaleY(150);
CompLabel.AutoSize := false;
CompLabel.WordWrap := True;
CompImage := TBitmapImage.Create(WizardForm);
CompImage.Parent := WizardForm.SelectComponentsPage;
CompImage.Top := CompLabel.Top;
CompImage.Width := CompImage.Width + ScaleX(128);
CompImage.Height := CompLabel.Height + ScaleX(128);
CompImage.Left := WizardForm.ComponentsList.Left + WizardForm.ComponentsList.Width - CompLabel.Width;
WizardForm.ComponentsList.Height := WizardForm.ComponentsList.Height - CompLabel.Height - ScaleY(8);
end;
procedure InitializeWizard2();
begin
CompPageModified := False;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurpageID = wpSelectComponents then
begin
SaveComponentsPage(CompPagePositions);
LoadComponentsPage(CompPagePositions, ScaleY(250));
CompPageModified := True;
end
else
if CompPageModified then
begin
LoadComponentsPage(CompPagePositions, 0);
CompPageModified := False;
end;
end;
procedure InitializeWizard();
begin
InitializeWizard1();
InitializeWizard2();
end;

Just copy both codes and merge the InitializeWizard implementations according to this guide:
Merging event function (InitializeWizard) implementations from different sources.
Though, actually, the InitializeWizard in the Larger “Select Components” page in Inno Setup is noop. You can skip that. And you do not need to merge anything.
The only other change you need to do, is not to enlarge the ComponentsList in the LoadComponentsPage.

Related

Inno Setup - Define music button and error with language selector?

This is the code with the error:
#include "Music\botva2.iss"
#include "Music\BASS_Module.iss"
[Code]
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
external 'ShellExecuteW#shell32.dll stdcall';
var
LanguageForm: TSetupForm;
SelectLabel: TNewStaticText;
CancelButton: TNewButton;
procedure LangChange(Sender : TObject);
begin
case TNewComboBox(Sender).ItemIndex of
0: { English }
begin
SelectLabel.Caption := 'Select the language to the installation:';
CancelButton.Caption := 'Cancel';
LanguageForm.Caption := 'PH';
end;
1: { Español }
begin
SelectLabel.Caption := 'Selecciona el idioma de la instalación:';
CancelButton.Caption := 'Cancelar';
LanguageForm.Caption := 'PH';
end;
end;
end;
procedure SelectLanguage();
var
OKButton: TNewButton;
LangCombo: TNewComboBox;
Languages: TStrings;
Params: string;
Instance: THandle;
P, I: Integer;
S, L: string;
begin
Languages := TStringList.Create();
Languages.Add('eng=English');
Languages.Add('spa=Español');
LanguageForm := CreateCustomForm;
LanguageForm.Caption := SetupMessage(msgSelectLanguageTitle);
LanguageForm.ClientWidth := ScaleX(240);
LanguageForm.ClientHeight := ScaleY(125);
LanguageForm.BorderStyle := bsDialog;
LanguageForm.Center;
CancelButton := TNewButton.Create(LanguageForm);
CancelButton.Parent := LanguageForm;
CancelButton.Left := ScaleX(140);
CancelButton.Top := ScaleY(93);
CancelButton.Width := ScaleY(90);
CancelButton.Height := ScaleY(23);
CancelButton.TabOrder := 3;
CancelButton.ModalResult := mrCancel;
CancelButton.Caption := SetupMessage(msgButtonCancel);
OKButton := TNewButton.Create(LanguageForm);
OKButton.Parent := LanguageForm;
OKButton.Left := ScaleX(10);
OKButton.Top := ScaleY(93);
OKButton.Width := ScaleX(90);
OKButton.Height := ScaleY(23);
OKButton.Caption := SetupMessage(msgButtonOK);
OKButton.Default := True
OKButton.ModalResult := mrOK;
OKButton.TabOrder := 2;
LangCombo := TNewComboBox.Create(LanguageForm);
LangCombo.Parent := LanguageForm;
LangCombo.Left := ScaleX(16);
LangCombo.Top := ScaleY(56);
LangCombo.Width := ScaleX(206);
LangCombo.Height := ScaleY(21);
LangCombo.Style := csDropDownList;
LangCombo.DropDownCount := 16;
LangCombo.TabOrder := 1;
SelectLabel := TNewStaticText.Create(LanguageForm);
SelectLabel.Parent := LanguageForm;
SelectLabel.Left := ScaleX(16);
SelectLabel.Top := ScaleY(15);
SelectLabel.Width := ScaleX(273);
SelectLabel.Height := ScaleY(39);
SelectLabel.AutoSize := False
SelectLabel.Caption := SetupMessage(msgSelectLanguageLabel);
SelectLabel.TabOrder := 0;
SelectLabel.WordWrap := True;
for I := 0 to Languages.Count - 1 do
begin
P := Pos('=', Languages.Strings[I]);
L := Copy(Languages.Strings[I], 0, P - 1);
S := Copy(Languages.Strings[I], P + 1, Length(Languages.Strings[I]) - P);
LangCombo.Items.Add(S);
if L = ActiveLanguage then
LangCombo.ItemIndex := I;
LangCombo.OnChange := #LangChange;
end;
if LanguageForm.ShowModal = mrOK then
begin
// Collect current instance parameters
for I := 1 to ParamCount do
begin
S := ParamStr(I);
// Unique log file name for the elevated instance
if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
begin
S := S + '-localized';
end;
// Do not pass our /SL5 switch
if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
begin
Params := Params + AddQuotes(S) + ' ';
end;
end;
L := Languages.Strings[LangCombo.ItemIndex];
P := Pos('=', L);
L := Copy(L, 0, P-1);
// ... and add selected language
Params := Params + '/LANG=' + L;
Instance := ShellExecute(0, '', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
if Instance <= 32 then
begin
MsgBox(
Format('Running installer with selected language failed. Code: %d', [Instance]),
mbError, MB_OK);
end;
end;
end;
function InitializeSetup(): Boolean;
var
Language: string;
begin
Result := True;
Language := ExpandConstant('{param:LANG}');
if Language = '' then
begin
Log('No language specified, showing language dialog');
SelectLanguage();
Result := False;
Exit;
end
else
begin
Log('Language specified, proceeding with installation');
end;
end;
procedure RedesignWizardForm;
begin
with WizardForm do
begin
BorderIcons:=[];
Bevel1.Hide;
AutoScroll := False;
ClientHeight := ScaleY(349);
end;
with WizardForm.CancelButton do
begin
Top := ScaleY(319);
end;
with WizardForm.NextButton do
begin
Top := ScaleY(319);
end;
with WizardForm.BackButton do
begin
Top := ScaleY(319);
end;
with WizardForm.WizardBitmapImage do
begin
Width := ScaleX(500);
end;
with WizardForm.WelcomeLabel2 do
begin
Visible := False;
end;
with WizardForm.WelcomeLabel1 do
begin
Visible := False;
end;
with WizardForm.WizardSmallBitmapImage do
begin
Left := ScaleX(0);
Width := ScaleX(500);
Height := ScaleY(60);
end;
with WizardForm.PageDescriptionLabel do
begin
Visible := False;
end;
with WizardForm.PageNameLabel do
begin
Visible := False;
end;
with WizardForm.WizardBitmapImage2 do
begin
Width := ScaleX(500);
ExtractTemporaryFile('WizardForm.WizardBitmapImage2.bmp');
Bitmap.LoadFromFile(ExpandConstant('{tmp}\WizardForm.WizardBitmapImage2.bmp'));
end;
with WizardForm.FinishedLabel do
begin
Visible := False;
end;
with WizardForm.FinishedHeadingLabel do
begin
Visible := False;
end;
end;
procedure InitializeWizard1();
begin
RedesignWizardForm;
WizardForm.DiskSpaceLabel.Visible := False;
end;
procedure InitializeWizard2();
begin
ExtractTemporaryFile('BASS.dll');
ExtractTemporaryFile('CallbackCtrl.dll');
ExtractTemporaryFile('botva2.dll');
ExtractTemporaryFile('MusicButton.png');
ExtractTemporaryFile('Music.mp3');
BASS_Init('{tmp}\Music.mp3')
BASS_CreateOnOffButton(WizardForm, '{tmp}\MusicButton.png', 20, 320, 36, 36, 4)
end;
procedure InitializeWizard();
begin
InitializeWizard1();
InitializeWizard2();
end;
procedure DeinitializeSetup();
begin
BASS_DeInit; //Îñâîáîæäàåì ïðîöåññ
gdipShutdown
end;
This code includes the language selector Inno Setup - How to change a label caption [or other controls in general], when selected value in combox box changes and a code that define music and music button. If i delete all about language selector, the code works fine. What is the problem?
The code of music and button includes: botva2.iss, BASS_Module.iss, botva2.dll, CallbackCtrl.dll.
This error appears when you select accept or cancel on the language selector.
The DeinitializeSetup is called, even when the setup is aborted by returning False from the InitializeSetup.
So I guess the BASS_DeInit (or the gdipShutdown) fails, because an equivalent BASS_Init was never called.
You have to avoid calling the code in the DeinitializeSetup, when the BASS_Init was never called.
var
BASS_Initialized: Boolean;
procedure InitializeWizard2();
begin
ExtractTemporaryFile('BASS.dll');
ExtractTemporaryFile('CallbackCtrl.dll');
ExtractTemporaryFile('botva2.dll');
ExtractTemporaryFile('MusicButton.png');
ExtractTemporaryFile('Music.mp3');
BASS_Init('{tmp}\Music.mp3')
BASS_CreateOnOffButton(WizardForm, '{tmp}\MusicButton.png', 20, 320, 36, 36, 4);
BASS_Initialized := True;
end;
procedure DeinitializeSetup();
begin
if BASS_Initialized then
begin
BASS_DeInit;
gdipShutdown;
end;
end;

Adding MouseOver descriptions for InnoSetup [duplicate]

I am building an install with Inno Setup and I am using the components section to allow the end user to select optional items to install.
Some of these items need a longer description in order for the user to have enough information to intelligently select them.
Is there a way to add more in-depth descriptions somewhere?
This solution uses only Inno Setup proper (not the obsolete 3rd party build of the Inno Setup of suspicious origin).
The solution is partially based on my answer to Inno Setup: OnHover event.
Adjust the HoverComponentChanged procedure to your needs.
[Code]
var
LastMouse: TPoint;
CompLabel: TLabel;
function GetCursorPos(var lpPoint: TPoint): BOOL;
external 'GetCursorPos#user32.dll stdcall';
function SetTimer(
hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord;
external 'SetTimer#user32.dll stdcall';
function ScreenToClient(hWnd: HWND; var lpPoint: TPoint): BOOL;
external 'ScreenToClient#user32.dll stdcall';
function ClientToScreen(hWnd: HWND; var lpPoint: TPoint): BOOL;
external 'ClientToScreen#user32.dll stdcall';
function ListBox_GetItemRect(
const hWnd: HWND; const Msg: Integer; Index: LongInt; var Rect: TRect): LongInt;
external 'SendMessageW#user32.dll stdcall';
const
LB_GETITEMRECT = $0198;
LB_GETTOPINDEX = $018E;
function FindControl(Parent: TWinControl; P: TPoint): TControl;
var
Control: TControl;
WinControl: TWinControl;
I: Integer;
P2: TPoint;
begin
for I := 0 to Parent.ControlCount - 1 do
begin
Control := Parent.Controls[I];
if Control.Visible and
(Control.Left <= P.X) and (P.X < Control.Left + Control.Width) and
(Control.Top <= P.Y) and (P.Y < Control.Top + Control.Height) then
begin
if Control is TWinControl then
begin
P2 := P;
ClientToScreen(Parent.Handle, P2);
WinControl := TWinControl(Control);
ScreenToClient(WinControl.Handle, P2);
Result := FindControl(WinControl, P2);
if Result <> nil then Exit;
end;
Result := Control;
Exit;
end;
end;
Result := nil;
end;
function PointInRect(const Rect: TRect; const Point: TPoint): Boolean;
begin
Result :=
(Point.X >= Rect.Left) and (Point.X <= Rect.Right) and
(Point.Y >= Rect.Top) and (Point.Y <= Rect.Bottom);
end;
function ListBoxItemAtPos(ListBox: TCustomListBox; Pos: TPoint): Integer;
var
Count: Integer;
ItemRect: TRect;
begin
Result := SendMessage(ListBox.Handle, LB_GETTOPINDEX, 0, 0);
Count := ListBox.Items.Count;
while Result < Count do
begin
ListBox_GetItemRect(ListBox.Handle, LB_GETITEMRECT, Result, ItemRect);
if PointInRect(ItemRect, Pos) then Exit;
Inc(Result);
end;
Result := -1;
end;
procedure HoverComponentChanged(Index: Integer);
var
Description: string;
begin
case Index of
0: Description := 'This is the description of Main Files';
1: Description := 'This is the description of Additional Files';
2: Description := 'This is the description of Help Files';
else
Description := 'Move your mouse over a component to see its description.';
end;
CompLabel.Caption := Description;
end;
procedure HoverTimerProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
P: TPoint;
Control: TControl;
Index: Integer;
begin
GetCursorPos(P);
if P <> LastMouse then { just optimization }
begin
LastMouse := P;
ScreenToClient(WizardForm.Handle, P);
if (P.X < 0) or (P.Y < 0) or
(P.X > WizardForm.ClientWidth) or (P.Y > WizardForm.ClientHeight) then
begin
Control := nil;
end
else
begin
Control := FindControl(WizardForm, P);
end;
Index := -1;
if (Control = WizardForm.ComponentsList) and
(not WizardForm.TypesCombo.DroppedDown) then
begin
P := LastMouse;
ScreenToClient(WizardForm.ComponentsList.Handle, P);
Index := ListBoxItemAtPos(WizardForm.ComponentsList, P);
end;
HoverComponentChanged(Index);
end;
end;
procedure InitializeWizard();
begin
SetTimer(0, 0, 50, CreateCallback(#HoverTimerProc));
CompLabel := TLabel.Create(WizardForm);
CompLabel.Parent := WizardForm.SelectComponentsPage;
CompLabel.Left := WizardForm.ComponentsList.Left;
CompLabel.Width := WizardForm.ComponentsList.Width;
CompLabel.Height := ScaleY(32);
CompLabel.Top :=
WizardForm.ComponentsList.Top + WizardForm.ComponentsList.Height -
CompLabel.Height;
CompLabel.AutoSize := False;
CompLabel.WordWrap := True;
WizardForm.ComponentsList.Height :=
WizardForm.ComponentsList.Height - CompLabel.Height - ScaleY(8);
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library. Use the Unicode version of Inno Setup 5.
To make this work correctly in resized/resizable/modern wizard, you will need some adjustments. See Inno Setup - how to center an animated gif in resized wizard and Long components descriptions in enlarged Inno Setup wizard.
Use this advanced compiler (download link is somewhere below).
It supports more classes and events than the standard compiler. You can access the property "OnItemMouseMove". Using this you can store a description for every item that is shown by a label. Here is an example:
var
CompLabel: TLabel;
procedure OnItemMouseMove(Sender: TObject; X, Y: Integer; Index: Integer; Area: TItemArea);
begin
case Index of
0: CompLabel.Caption := 'This is the description of Component 1';
1: CompLabel.Caption := 'This is the description of Component 2';
2: CompLabel.Caption := 'This is the description of Component 3';
3: CompLabel.Caption := 'This is the description of Component 4'
else
CompLabel.Caption := 'Move your mouse over a component to see its description.';
end;
end;
procedure OnMouseLeave(Sender: TObject);
begin
CompLabel.Caption := 'Move your mouse over a component to see its description.';
end;
procedure InitializeWizard();
begin
CompLabel := TLabel.Create(WizardForm);
CompLabel.Parent := WizardForm.SelectComponentsPage;
CompLabel.SetBounds(WizardForm.ComponentsList.Left,180,WizardForm.ComponentsList.Width,200);
CompLabel.Caption := 'Move your mouse over a component to see its description.';
WizardForm.ComponentsList.OnItemMouseMove := #OnItemMouseMove;
WizardForm.ComponentsList.OnMouseLeave := #OnMouseLeave;
WizardForm.ComponentsList.Height := WizardForm.ComponentsList.Height - 40;
end;

how to pass a variable value to an xml file?

this is my code to enter a port number from user.upon installing i want to get the port number changed in apache tomcat server.xml file.
Iam passing apache tomcat zip file also using files section and unzip it in run section
var
javaVersion: String;
javaPath: String;
//port number code
function SetFocus(hWnd: HWND): HWND;
external 'SetFocus#user32.dll stdcall';
var
SerialPage: TWizardPage;
SerialEdits: array of TEdit;
const
CF_TEXT = 1;
VK_BACK = 8;
SC_EDITCOUNT = 1;
SC_CHARCOUNT = 4;
procedure OnSerialEditChange(Sender: TObject);
var
I: Integer;
CanContinue: Boolean;
begin
CanContinue := True;
for I := 0 to GetArrayLength(SerialEdits) - 1 do
if Length(SerialEdits[I].Text) < SC_CHARCOUNT then
begin
CanContinue := False;
Break;
end;
WizardForm.NextButton.Enabled := CanContinue;
end;
function GetSerialNumber(Param: String): string;
var
I: Integer;
begin
Result := '';
for I := 0 to GetArrayLength(SerialEdits) - 1 do
Result := Result + SerialEdits[I].Text ;
end;
procedure OnSerialEditKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
Edit: TEdit;
EditIndex: Integer;
begin
Edit := TEdit(Sender);
EditIndex := Edit.TabOrder - SerialEdits[0].TabOrder;
if (EditIndex = 0) and (Key = Ord('V')) and (Shift = [ssCtrl]) then
begin
if TryPasteSerialNumber then
Key := 0;
end
else
if (Key >= 32) and (Key <= 255) then
begin
if Length(Edit.Text) = SC_CHARCOUNT - 1 then
begin
if EditIndex < GetArrayLength(SerialEdits) - 1 then
SetFocus(SerialEdits[EditIndex + 1].Handle)
else
SetFocus(WizardForm.NextButton.Handle);
end;
end
else
if Key = VK_BACK then
if (EditIndex > 0) and (Edit.Text = '') and (Edit.SelStart = 0) then
SetFocus(SerialEdits[EditIndex - 1].Handle);
end;
procedure CreateSerialNumberPage;
var
I: Integer;
Edit: TEdit;
DescLabel: TLabel;
EditWidth: Integer;
begin
SerialPage := CreateCustomPage(wpWelcome, 'Serial number validation',
'Enter the valid serial number');
DescLabel := TLabel.Create(SerialPage);
DescLabel.Top := 16;
DescLabel.Left := 0;
DescLabel.Parent := SerialPage.Surface;
DescLabel.Caption := 'Enter the valid serial number and continue with the installation...';
DescLabel.Font.Style := [fsBold];
SetArrayLength(SerialEdits, SC_EDITCOUNT);
EditWidth := (SerialPage.SurfaceWidth - ((SC_EDITCOUNT - 1) * 8)) div SC_EDITCOUNT;
for I := 0 to SC_EDITCOUNT - 1 do
begin
Edit := TEdit.Create(SerialPage);
Edit.Top := 40;
Edit.Left := I * (EditWidth + 8);
Edit.Width := EditWidth;
Edit.CharCase := ecUpperCase;
Edit.MaxLength := SC_CHARCOUNT;
Edit.Parent := SerialPage.Surface;
Edit.OnChange := #OnSerialEditChange;
Edit.OnKeyDown := #OnSerialEditKeyDown;
SerialEdits[I] := Edit;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = SerialPage.ID then
WizardForm.NextButton.Enabled := False;
end;
procedure InitializeWizard;
begin
CreateSerialNumberPage;
end ;
i want to replace the port number which was entered by user in tomcats server.xml using tokens
<Connector port="##portnumber##" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Here's a script I've made for you. I've changed the way of entering port number and shown how to modify attribute values in XML files. Also notice the usage of the AfterInstall function:
#define TomcatDest "{app}\tomcat"
#define TomcatFullPath TomcatDest + "\apache-tomcat-7.0.42"
#define TomcatSrvConfigFile TomcatFullPath + "\conf\server.xml"
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Files]
Source: "unzip.exe"; DestDir: "{tmp}\installertemps"
Source: "apache-tomcat-7.0.42.zip"; DestDir: "{tmp}\installertemps"
[Run]
Filename: "{tmp}\installertemps\unzip.exe"; Parameters: " ""{tmp}\installertemps\apache-tomcat-7.0.42.zip"" -d ""{#TomcatDest}"" "; AfterInstall: UpdateConfigFile(ExpandConstant('{#TomcatSrvConfigFile}'))
[Code]
const
DefaultPort = 8080;
var
ConfigPage: TInputQueryWizardPage;
procedure SaveAttrValueToXML(const FileName, NodePath, Attribute,
Value: string);
var
XMLNode: Variant;
XMLDocument: Variant;
begin
XMLDocument := CreateOleObject('Msxml2.DOMDocument');
try
XMLDocument.async := False;
XMLDocument.load(FileName);
if (XMLDocument.parseError.errorCode <> 0) then
MsgBox('The XML file could not be parsed. ' +
XMLDocument.parseError.reason, mbError, MB_OK)
else
begin
XMLDocument.setProperty('SelectionLanguage', 'XPath');
XMLNode := XMLDocument.selectSingleNode(NodePath);
XMLNode.setAttribute(Attribute, Value);
XMLDocument.save(FileName);
end;
except
MsgBox('An error occured!' + #13#10 + GetExceptionMessage,
mbError, MB_OK);
end;
end;
procedure InitializeWizard;
begin
ConfigPage := CreateInputQueryPage(wpSelectDir, 'Tomcat configuration',
'Description', 'SubCaption');
ConfigPage.Add('Port:', False);
ConfigPage.Values[0] := IntToStr(DefaultPort);
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
PortNumber: Integer;
begin
Result := True;
if CurPageID = ConfigPage.ID then
begin
PortNumber := StrToIntDef(ConfigPage.Values[0], -1);
// modify the statement to allow users enter only valid port numbers;
// currently the value of -1 means that there is not even a number entered
// in the edit box
if (PortNumber = -1) then
begin
Result := False;
MsgBox('You''ve entered invalid port number. The setup cannot continue...', mbError, MB_OK);
end;
end;
end;
procedure UpdateConfigFile(const FileName: string);
begin
SaveAttrValueToXML(FileName, '//Server/Service/Connector', 'port',
ConfigPage.Values[0]);
end;

Long descriptions on Inno Setup components

I am building an install with Inno Setup and I am using the components section to allow the end user to select optional items to install.
Some of these items need a longer description in order for the user to have enough information to intelligently select them.
Is there a way to add more in-depth descriptions somewhere?
This solution uses only Inno Setup proper (not the obsolete 3rd party build of the Inno Setup of suspicious origin).
The solution is partially based on my answer to Inno Setup: OnHover event.
Adjust the HoverComponentChanged procedure to your needs.
[Code]
var
LastMouse: TPoint;
CompLabel: TLabel;
function GetCursorPos(var lpPoint: TPoint): BOOL;
external 'GetCursorPos#user32.dll stdcall';
function SetTimer(
hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord;
external 'SetTimer#user32.dll stdcall';
function ScreenToClient(hWnd: HWND; var lpPoint: TPoint): BOOL;
external 'ScreenToClient#user32.dll stdcall';
function ClientToScreen(hWnd: HWND; var lpPoint: TPoint): BOOL;
external 'ClientToScreen#user32.dll stdcall';
function ListBox_GetItemRect(
const hWnd: HWND; const Msg: Integer; Index: LongInt; var Rect: TRect): LongInt;
external 'SendMessageW#user32.dll stdcall';
const
LB_GETITEMRECT = $0198;
LB_GETTOPINDEX = $018E;
function FindControl(Parent: TWinControl; P: TPoint): TControl;
var
Control: TControl;
WinControl: TWinControl;
I: Integer;
P2: TPoint;
begin
for I := 0 to Parent.ControlCount - 1 do
begin
Control := Parent.Controls[I];
if Control.Visible and
(Control.Left <= P.X) and (P.X < Control.Left + Control.Width) and
(Control.Top <= P.Y) and (P.Y < Control.Top + Control.Height) then
begin
if Control is TWinControl then
begin
P2 := P;
ClientToScreen(Parent.Handle, P2);
WinControl := TWinControl(Control);
ScreenToClient(WinControl.Handle, P2);
Result := FindControl(WinControl, P2);
if Result <> nil then Exit;
end;
Result := Control;
Exit;
end;
end;
Result := nil;
end;
function PointInRect(const Rect: TRect; const Point: TPoint): Boolean;
begin
Result :=
(Point.X >= Rect.Left) and (Point.X <= Rect.Right) and
(Point.Y >= Rect.Top) and (Point.Y <= Rect.Bottom);
end;
function ListBoxItemAtPos(ListBox: TCustomListBox; Pos: TPoint): Integer;
var
Count: Integer;
ItemRect: TRect;
begin
Result := SendMessage(ListBox.Handle, LB_GETTOPINDEX, 0, 0);
Count := ListBox.Items.Count;
while Result < Count do
begin
ListBox_GetItemRect(ListBox.Handle, LB_GETITEMRECT, Result, ItemRect);
if PointInRect(ItemRect, Pos) then Exit;
Inc(Result);
end;
Result := -1;
end;
procedure HoverComponentChanged(Index: Integer);
var
Description: string;
begin
case Index of
0: Description := 'This is the description of Main Files';
1: Description := 'This is the description of Additional Files';
2: Description := 'This is the description of Help Files';
else
Description := 'Move your mouse over a component to see its description.';
end;
CompLabel.Caption := Description;
end;
procedure HoverTimerProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
P: TPoint;
Control: TControl;
Index: Integer;
begin
GetCursorPos(P);
if P <> LastMouse then { just optimization }
begin
LastMouse := P;
ScreenToClient(WizardForm.Handle, P);
if (P.X < 0) or (P.Y < 0) or
(P.X > WizardForm.ClientWidth) or (P.Y > WizardForm.ClientHeight) then
begin
Control := nil;
end
else
begin
Control := FindControl(WizardForm, P);
end;
Index := -1;
if (Control = WizardForm.ComponentsList) and
(not WizardForm.TypesCombo.DroppedDown) then
begin
P := LastMouse;
ScreenToClient(WizardForm.ComponentsList.Handle, P);
Index := ListBoxItemAtPos(WizardForm.ComponentsList, P);
end;
HoverComponentChanged(Index);
end;
end;
procedure InitializeWizard();
begin
SetTimer(0, 0, 50, CreateCallback(#HoverTimerProc));
CompLabel := TLabel.Create(WizardForm);
CompLabel.Parent := WizardForm.SelectComponentsPage;
CompLabel.Left := WizardForm.ComponentsList.Left;
CompLabel.Width := WizardForm.ComponentsList.Width;
CompLabel.Height := ScaleY(32);
CompLabel.Top :=
WizardForm.ComponentsList.Top + WizardForm.ComponentsList.Height -
CompLabel.Height;
CompLabel.AutoSize := False;
CompLabel.WordWrap := True;
WizardForm.ComponentsList.Height :=
WizardForm.ComponentsList.Height - CompLabel.Height - ScaleY(8);
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library. Use the Unicode version of Inno Setup 5.
To make this work correctly in resized/resizable/modern wizard, you will need some adjustments. See Inno Setup - how to center an animated gif in resized wizard and Long components descriptions in enlarged Inno Setup wizard.
Use this advanced compiler (download link is somewhere below).
It supports more classes and events than the standard compiler. You can access the property "OnItemMouseMove". Using this you can store a description for every item that is shown by a label. Here is an example:
var
CompLabel: TLabel;
procedure OnItemMouseMove(Sender: TObject; X, Y: Integer; Index: Integer; Area: TItemArea);
begin
case Index of
0: CompLabel.Caption := 'This is the description of Component 1';
1: CompLabel.Caption := 'This is the description of Component 2';
2: CompLabel.Caption := 'This is the description of Component 3';
3: CompLabel.Caption := 'This is the description of Component 4'
else
CompLabel.Caption := 'Move your mouse over a component to see its description.';
end;
end;
procedure OnMouseLeave(Sender: TObject);
begin
CompLabel.Caption := 'Move your mouse over a component to see its description.';
end;
procedure InitializeWizard();
begin
CompLabel := TLabel.Create(WizardForm);
CompLabel.Parent := WizardForm.SelectComponentsPage;
CompLabel.SetBounds(WizardForm.ComponentsList.Left,180,WizardForm.ComponentsList.Width,200);
CompLabel.Caption := 'Move your mouse over a component to see its description.';
WizardForm.ComponentsList.OnItemMouseMove := #OnItemMouseMove;
WizardForm.ComponentsList.OnMouseLeave := #OnMouseLeave;
WizardForm.ComponentsList.Height := WizardForm.ComponentsList.Height - 40;
end;

wpLicese Page check if ScrollBars position is max (Inno Setup)

Is it possible to check the position of ScrollBar in wpLicense Page in Inno Setup without having to write custom memo page?
e.g.
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpLicense then
WizardForm.LicenseAcceptedRadio.Enabled := False;
WizardForm.LicenseNotAcceptedRadio.Enabled := False;
if ScrollBar.Position := ScrollBar.Max then
WizardForm.LicenseAcceptedRadio.Enabled := True;
WizardForm.LicenseNotAcceptedRadio.Enabled := True;
end;
There is no direct access to those scroll bars, however you can use the GetScrollInfo function this way:
[code]
const
SB_VERT = 1;
SIF_RANGE = 1;
SIF_POS = 4;
SIF_PAGE = 2;
type
TScrollInfo = record
cbSize: UINT;
fMask: UINT;
nMin: Integer;
nMax: Integer;
nPage: UINT;
nPos: Integer;
nTrackPos: Integer;
end;
function GetScrollInfo(hWnd: HWND; BarFlag: Integer;
var ScrollInfo: TScrollInfo): BOOL;
external 'GetScrollInfo#user32.dll stdcall';
procedure CurPageChanged(CurPageID: Integer);
var
ScrollInfo: TScrollInfo;
begin
if CurPageID = wpLicense then
begin
ScrollInfo.cbSize := SizeOf(ScrollInfo);
ScrollInfo.fMask := SIF_RANGE or SIF_POS or SIF_PAGE;
if GetScrollInfo(WizardForm.LicenseMemo.Handle, SB_VERT, ScrollInfo) then
if ScrollInfo.nPos = ScrollInfo.nMax - ScrollInfo.nPage then
MsgBox('You are at the end of the license!', mbInformation, MB_OK);
end;
end;
That is what I have now. It works, however if you find any issues, just say as I like comments.
procedure OnScrollPosition(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
var
ScrollInfo: TScrollInfo;
begin
ScrollInfo.cbSize := SizeOf(ScrollInfo);
ScrollInfo.fMask := SIF_RANGE or SIF_POS or SIF_PAGE;
if GetScrollInfo(WizardForm.LicenseMemo.Handle, SB_VERT, ScrollInfo) then
if ScrollInfo.nPos = ScrollInfo.nMax - ScrollInfo.nPage then
begin
WizardForm.LicenseAcceptedRadio.Enabled := True;
WizardForm.LicenseNotAcceptedRadio.Enabled := True;
end;
end;
procedure ScrollPosition();
var
TimerCallback: LongWord;
begin
TimerCallback := WrapTimerProc(#OnScrollPosition, 4);
TimerID := SetTimer(0, 0, 500, TimerCallback);
end;
procedure CurPageChanged(CurPageID: Integer);
var
ScrollInfo: TScrollInfo;
begin
if CurPageID = wpInstalling then
StartSlideTimer
else
KillSlideTimer;
if CurPageID = wpLicense then
WizardForm.LicenseAcceptedRadio.Enabled := False;
WizardForm.LicenseNotAcceptedRadio.Enabled := False;
ScrollPosition
end;

Resources