| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- unit Setup.TaskDialogForm;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Task Dialog form which can be styled
- Supports up to three command links
- }
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics,
- Controls, Forms, Dialogs, WinXPanels, ExtCtrls, StdCtrls,
- NewCtrls, BitmapImage, NewStaticText,
- Setup.SetupForm;
- const
- TD_TASKFORM_HELP_ICON = MAKEINTRESOURCEW(Word(-100));
- type
- TCopyFormat = (cfTaskDialog, cfMessageBox);
- TTaskDialogForm = class(TSetupForm)
- BottomPanel: TPanel;
- MainPanel: TPanel;
- LeftPanel: TPanel;
- BitmapImage: TBitmapImage;
- MainStackPanel: TStackPanel;
- InstructionText: TNewStaticText;
- TextText: TNewStaticText;
- MainButton1: TNewButton;
- MainButton2: TNewButton;
- MainButton3: TNewButton;
- BottomStackPanel: TStackPanel;
- OkButton: TNewButton;
- YesButton: TNewButton;
- NoButton: TNewButton;
- RetryButton: TNewButton;
- CancelButton: TNewButton;
- BottomPanel2: TPanel;
- VerificationCheck: TNewCheckBox;
- procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
- private
- FCommonButtons: array of TNewButton;
- FCommonButtonFlags: array of Cardinal;
- FMainButtons: array of TNewButton;
- FCopyFormat: TCopyFormat;
- procedure Finish(const DefCommonButton: Integer);
- procedure UpdateCommonButtons(const CommonButtons: Cardinal);
- procedure UpdateIcon(const Icon: PChar);
- procedure UpdateInstructionAndText(const Instruction, Text: String);
- procedure UpdateHeight;
- procedure UpdateMainButtonsAndBorderIcons(const CommonButtons: Cardinal;
- const ButtonLabels: array of String; const ButtonIDs: array of Integer; const ShieldButton: Integer);
- procedure UpdateVerificationText(const VerificationText: String; const pfVerificationFlagChecked: PBOOL);
- public
- constructor Create(AOwner: TComponent; const ACopyFormat: TCopyFormat; const ASetForeground: Boolean); reintroduce;
- end;
- function TaskDialogForm(const Instruction, Text, Caption: String; const Icon: PChar;
- const CommonButtons: Cardinal; const ButtonLabels: array of String; const ButtonIDs: array of Integer;
- const DefCommonButton, ShieldButton: Integer; const TriggerMessageBoxCallbackFuncFlags: Cardinal;
- const VerificationText: String; const pfVerificationFlagChecked: PBOOL; const CopyFormat: TCopyFormat;
- const SetForeground: Boolean): Integer;
- implementation
- uses
- CommCtrl, Clipbrd, Themes, ShellAPI,
- Shared.SetupMessageIDs, Shared.CommonFunc, Shared.CommonFunc.Vcl,
- SetupLdrAndSetup.Messages, Setup.WizardForm, Setup.MainFunc;
- {$R *.dfm}
- function TaskDialogForm(const Instruction, Text, Caption: String; const Icon: PChar;
- const CommonButtons: Cardinal; const ButtonLabels: array of String; const ButtonIDs: array of Integer;
- const DefCommonButton, ShieldButton: Integer; const TriggerMessageBoxCallbackFuncFlags: Cardinal;
- const VerificationText: String; const pfVerificationFlagChecked: PBOOL; const CopyFormat: TCopyFormat;
- const SetForeground: Boolean): Integer;
- begin
- const Form = TTaskDialogForm.Create(nil, CopyFormat, SetForeground);
- try
- Form.Caption := Caption;
- Form.UpdateInstructionAndText(Instruction, Text);
- Form.UpdateIcon(Icon);
- Form.UpdateCommonButtons(CommonButtons);
- Form.UpdateVerificationText(VerificationText, pfVerificationFlagChecked);
- if (Pos(':\', Text) <> 0) or (Pos('\\', Text) <> 0) then
- Form.Width := MulDiv(Form.Width, 125, 100);
- if Form.InstructionText.Visible then
- Form.InstructionText.AdjustHeight;
- if Form.TextText.Visible then
- Form.TextText.AdjustHeight;
- Form.UpdateMainButtonsAndBorderIcons(CommonButtons, ButtonLabels, ButtonIDs, ShieldButton);
- Form.UpdateHeight;
- Form.Finish(DefCommonButton);
- TriggerMessageBoxCallbackFunc(TriggerMessageBoxCallbackFuncFlags, False);
- try
- Result := Form.ShowModal;
- if pfVerificationFlagChecked <> nil then
- pfVerificationFlagChecked^ := Form.VerificationCheck.Checked;
- finally
- TriggerMessageBoxCallbackFunc(TriggerMessageBoxCallbackFuncFlags, True);
- end;
- finally
- Form.Free;
- end;
- end;
- { TTaskDialogForm }
- constructor TTaskDialogForm.Create(AOwner: TComponent; const ACopyFormat: TCopyFormat; const ASetForeground: Boolean);
- begin
- inherited Create(AOwner);
- FCommonButtons := [OkButton, YesButton, NoButton, RetryButton, CancelButton];
- FCommonButtonFlags := [TDCBF_OK_BUTTON, TDCBF_YES_BUTTON, TDCBF_NO_BUTTON, TDCBF_RETRY_BUTTON, TDCBF_CANCEL_BUTTON];
- FMainButtons := [MainButton1, MainButton2, MainButton3];
- FCopyFormat := ACopyFormat;
- SetForeground := ASetForeground;
- if not CustomWizardBackground or (SetupHeader.WizardBackColor = clWindow) then begin
- var LStyle := StyleServices(Self);
- if not LStyle.Enabled or LStyle.IsSystemStyle then
- LStyle := nil;
- if LStyle <> nil then begin
- { Make MainPanel look the same as WizardForm's main area }
- MainPanel.StyleElements := [];
- MainPanel.Color := LStyle.GetStyleColor(scWindow);
- end;
- end else
- MainPanel.ParentBackground := True;
- { KeepSizeX: Already bit wider than regular task dialogs
- KeepSizeY: UpdateHeight will set height }
- InitializeFont(True, True);
- const Pad = 10;
- const PadX = TMarginSize(ScalePixelsX(Pad));
- const PadY = TMarginSize(ScalePixelsY(Pad));
- MainPanel.Padding.Left := PadX;
- MainPanel.Padding.Top := PadY;
- MainPanel.Padding.Right := PadX;
- MainPanel.Padding.Bottom := PadY;
- MainStackPanel.Padding.Left := PadX; { Also see Finish }
- MainStackPanel.Spacing := PadY;
- BottomStackPanel.Spacing := PadX;
- BottomStackPanel.Padding.Right := PadX; { Also see Finish }
- VerificationCheck.Left := PadX;
- OkButton.Caption := SetupMessages[msgButtonOK];
- YesButton.Caption := SetupMessages[msgButtonYes];
- NoButton.Caption := SetupMessages[msgButtonNo];
- RetryButton.Caption := SetupMessages[msgAbortRetryIgnoreRetry];
- CancelButton.Caption := SetupMessages[msgButtonCancel];
- end;
- procedure TTaskDialogForm.Finish(const DefCommonButton: Integer);
- begin
- if RightToLeft then begin
- { FlipSizeAndCenterIfNeeded does not update Align or Padding }
- if LeftPanel.Visible then
- LeftPanel.Align := alRight;
- MainStackPanel.Padding.Right := MainStackPanel.Padding.Left;
- MainStackPanel.Padding.Left := 0;
- if BottomPanel.Visible then begin
- BottomStackPanel.Align := alLeft;
- BottomStackPanel.Padding.Left := BottomStackPanel.Padding.Right;
- BottomStackPanel.Padding.Right := 0;
- end;
- end;
- if DefCommonButton > 0 then begin
- var I := DefCommonButton;
- for var CommonButton in FCommonButtons do begin
- if CommonButton.Visible then begin
- Dec(I);
- if I = 0 then begin
- ActiveControl := CommonButton;
- Exit;
- end;
- end;
- end;
- end;
- end;
- procedure TTaskDialogForm.UpdateCommonButtons(const CommonButtons: Cardinal);
- var
- VisibleCaptions: array of String;
- begin
- var NVisibleCaptions := 0;
- for var I := 0 to Length(FCommonButtons)-1 do begin
- const CommonButton = FCommonButtons[I];
- const CommonButtonFlag = FCommonButtonFlags[I];
- CommonButton.Visible := CommonButtons and CommonButtonFlag <> 0;
- if CommonButton.Visible then begin
- Inc(NVisibleCaptions);
- SetLength(VisibleCaptions, NVisibleCaptions);
- VisibleCaptions[NVisibleCaptions-1] := CommonButton.Caption;
- end;
- end;
- BottomPanel.Visible := NVisibleCaptions > 0;
- if BottomPanel.Visible then begin
- const W = CalculateButtonWidth(VisibleCaptions);
- for var CommonButton in FCommonButtons do
- if CommonButton.Visible then
- CommonButton.Width := W;
- end;
- end;
- procedure TTaskDialogForm.UpdateHeight;
- begin
- var BottomControl: TControl := nil;
- if MainButton3.Visible then
- BottomControl := MainButton3
- else if MainButton2.Visible then
- BottomControl := MainButton2
- else if MainButton1.Visible then
- BottomControl := MainButton1
- else if TextText.Visible then
- BottomControl := TextText
- else if InstructionText.Visible then
- BottomControl := InstructionText;
- var NewClientHeight := MainPanel.Padding.Top + MainStackPanel.Top;
- if BottomControl <> nil then
- NewClientHeight := NewClientHeight + BottomControl.Top + BottomControl.Height;
- if LeftPanel.Visible then begin
- { Make sure the height is enough to fit the icon }
- const MinimumClientHeight = MainPanel.Padding.Top + LeftPanel.Top + BitmapImage.Top + BitmapImage.Height + MainPanel.Padding.Bottom;
- if MinimumClientHeight > NewClientHeight then
- NewClientHeight := MinimumClientHeight;
- end;
- if BottomPanel.Visible then
- NewClientHeight := NewClientHeight + BottomPanel.Height;
- if BottomPanel2.Visible then
- NewClientHeight := NewClientHeight + BottomPanel2.Height;
- ClientHeight := NewClientHeight;
- end;
- procedure TTaskDialogForm.UpdateIcon(const Icon: PChar);
- begin
- var Siid: SHSTOCKICONID;
- if Icon = TD_ERROR_ICON then
- Siid := SIID_ERROR
- else if Icon = TD_TASKFORM_HELP_ICON then
- Siid := SIID_HELP
- else if Icon = TD_INFORMATION_ICON then
- Siid := SIID_INFO
- else if Icon = TD_WARNING_ICON then
- Siid := SIID_WARNING
- else
- Siid := SIID_INVALID;
- if Siid <> SIID_INVALID then
- BitmapImage.InitializeFromStockIcon(Siid, clNone, [32, 48, 64])
- else if Icon <> nil then
- BitmapImage.InitializeFromIcon(HInstance, Icon, clNone, [32, 48, 64])
- else
- LeftPanel.Visible := False;
- end;
- procedure TTaskDialogForm.UpdateInstructionAndText(const Instruction, Text: String);
- begin
- InstructionText.Visible := Instruction <> '';
- if InstructionText.Visible then begin
- InstructionText.Caption := Instruction;
- InstructionText.Font.Height := MulDiv(InstructionText.Font.Height, 12, 9);
- end;
- TextText.Visible := Text <> '';
- if TextText.Visible then
- TextText.Caption := Text;
- end;
- procedure TTaskDialogForm.UpdateMainButtonsAndBorderIcons(const CommonButtons: Cardinal;
- const ButtonLabels: array of String; const ButtonIDs: array of Integer; const ShieldButton: Integer);
- begin
- var HaveCancel := False;
- for var I := 0 to Length(FMainButtons)-1 do begin
- const MainButton = FMainButtons[I];
- MainButton.Visible := I < Length(ButtonLabels);
- if MainButton.Visible then begin
- var Caption := ButtonLabels[I];
- var Hint: String;
- const P = Pos(#10, Caption);
- if P <> 0 then begin
- Hint := Copy(Caption, P+1, MaxInt);
- Delete(Caption, P, MaxInt);
- end else
- Hint := '';
- MainButton.Caption := Caption;
- MainButton.Font.Height := MulDiv(MainButton.Font.Height, 12, 9);
- MainButton.CommandLinkHint := Hint;
- MainButton.ModalResult := ButtonIDs[I];
- if MainButton.ModalResult = IDCANCEL then begin
- MainButton.Cancel := True;
- HaveCancel := True;
- end;
- MainButton.ElevationRequired := MainButton.ModalResult = ShieldButton;
- MainButton.AdjustHeightIfCommandLink;
- end;
- end;
- if not HaveCancel and (CommonButtons and TDCBF_CANCEL_BUTTON = 0) then begin
- const SystemMenu = GetSystemMenu(Handle, False);
- if SystemMenu <> 0 then
- EnableMenuItem(SystemMenu, SC_CLOSE, MF_BYCOMMAND or MF_GRAYED);
- end;
- end;
- procedure TTaskDialogForm.UpdateVerificationText(const VerificationText: String;
- const pfVerificationFlagChecked: PBOOL);
- begin
- if VerificationText <> '' then begin
- VerificationCheck.Caption := VerificationText;
- if pfVerificationFlagChecked <> nil then
- VerificationCheck.Checked := pfVerificationFlagChecked^;
- end else
- BottomPanel2.Visible := False;
- end;
- procedure TTaskDialogForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
- procedure AddButtonCaption(const SB: TStringBuilder; const MessageBoxFormat: Boolean;
- const Button: TButton);
- begin
- const Caption = RemoveAccelChar(Button.Caption);
- if MessageBoxFormat then
- SB.Append(Format('%s ', [Caption]))
- else
- SB.Append(Format('[%s] ', [Caption]));
- end;
- begin
- if (Shift = [ssCtrl]) and (Key = Ord('C')) then begin
- Key := 0;
- const SB = TStringBuilder.Create;
- try
- { Do not localize }
- const SLine = '---------------------------';
- const SLineAndNewLine = SLine + SNewLine;
- const MessageBoxFormat = FCopyFormat = cfMessageBox;
- if MessageBoxFormat then
- SB.Append(SLine)
- else
- SB.Append('[Window Title]');
- SB.Append(SNewLine);
- SB.Append(Caption);
- SB.Append(SNewLine);
- if MessageBoxFormat then
- SB.Append(SLineAndNewLine)
- else
- SB.Append(SNewLine);
- if InstructionText.Visible then begin
- if not MessageBoxFormat then begin
- SB.Append('[Main Instruction]');
- SB.Append(SNewLine);
- end;
- SB.Append(InstructionText.Caption);
- SB.Append(SNewLine);
- if not MessageBoxFormat or TextText.Visible then
- SB.Append(SNewLine);
- end;
- if TextText.Visible then begin
- if not MessageBoxFormat then begin
- SB.Append('[Content]');
- SB.Append(SNewLine);
- end;
- SB.Append(TextText.Caption);
- SB.Append(SNewLine);
- if not MessageBoxFormat then
- SB.Append(SNewLine);
- end;
- if MessageBoxFormat then
- SB.Append(SLineAndNewLine);
- for var MainButton in FMainButtons do
- if MainButton.Visible then
- AddButtonCaption(SB, MessageBoxFormat, MainButton);
- for var CommonButton in FCommonButtons do
- if CommonButton.Visible then
- AddButtonCaption(SB, MessageBoxFormat, CommonButton);
- if MessageBoxFormat then begin
- SB.Append(SNewLine);
- SB.Append(SLine); { Causes the spaces after the last button caption not to be trimmed, but this is same as with native MessageBox }
- end;
- Clipboard.AsText := SB.ToString.Trim;
- finally
- SB.Free;
- end;
- end;
- end;
- end.
|