{
'*******************************************************************************
'
'   Michael Koecher/six1 & Werner Pamler/wp_xyz  https://www.lazarusforum.de/
'
'   LGPL2/Linking Exception
'   oder
'   http://creativecommons.org/licenses/by-sa/3.0/de/
'
'   Based on post in the German Lazarus forum https://www.lazarusforum.de/
'
'*******************************************************************************
}
unit ExQuestionDlg;
 
{$mode ObjFPC}{$H+}
 
interface
 
uses
  LclIntf, LclType, Types,
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons,
  ExtCtrls, Menus;
 
 
// AX, AY >= 0 absolute position
//
// AX= -ord(poXXXX) and AY = MaxInt
//   0 poDesigned,        // places form as if AX and AY were not specified (both MaxInt).
//  -1 poDefault,         // LCL decision (normally window manager decides)
//  -2 poDefaultPosOnly,  // designed size and LCL position
//  -3 poDefaultSizeOnly, // designed position and LCL size
//  -4 poScreenCenter,    // center form on screen (depends on DefaultMonitor)
//  -5 poDesktopCenter,   // center form on desktop (total of all screens)
//  -6 poMainFormCenter,  // center form on main form (depends on DefaultMonitor)
//  -7 poOwnerFormCenter, // center form on owner form (depends on DefaultMonitor)
//  -8 poWorkAreaCenter
// NOTE: poDefaultXXXX are not working as expected
 
function QuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AX: Integer = MaxInt; AY: Integer = MaxInt): TModalResult; overload;
 
function QuestionDlgEx(const ACaption, AMsg: string; timeout:integer; ADlgType: TMsgDlgType;
  const AButtons: array of const; AX: Integer = MaxInt; AY: Integer = MaxInt): TModalResult; overload;
 
function QuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AHelpCtx, AX, AY: Integer): TModalResult; overload;
 
function QuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AHelpKeyword: String;
  AX: Integer = MaxInt; AY: Integer= MaxInt): TModalResult; overload;
 
function CreateQuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AX, AY: Integer; timeout:integer=-1 ): TForm;
 
var
  QuestionDlgEx_MaxWidth: Integer = 500;
  QuestionDlgEx_MinWidth: Integer = 250;
  QuestionDlgEx_MinHeight: Integer = 50;  // Text part only
  QuestionDlgEx_ButtonAlignment: TAlignment = taCenter;
  QuestionDlgEx_CustomIcon: TPicture;
  QuestionDlgEx_TextAlignment: TAlignment = taLeftJustify;
  QuestionDlgEx_TextBkColor: TColor = clWindow;
  QuestionDlgEx_TextLayout: TTextLayout = tlCenter;
  QuestionDlgEx_FontName: String = '';    // the same as "default"
  QuestionDlgEx_FontSize: Integer = 0;
  QuestionDlgEx_GlyphShowMode: TGlyphShowMode = gsmApplication;
 
implementation
 
uses
  TypInfo, Math;
 
const
  DEFAULT_IMAGE_SIZE = 32;
  DEFAULT_IMAGE_BORDER_X = 10;
  DEFAULT_IMAGE_BORDER_Y = 5;
  DEFAULT_TEXT_BORDER_X = 10;
  DEFAULT_TEXT_BORDER_Y = 10;
  BUTTON_MIN_WIDTH = 75;
  BUTTON_SPACING = 6;
 
 
function GetBitBtnModalResults(str:string):TModalResult;
const
  ModalResults: array[0..10] of TIdentMapEntry = (
    (Value: mrNone; Name: 'mrNone'),
    (Value: mrOk; Name: 'mrOk'),
    (Value: mrCancel; Name: 'mrCancel'),
    (Value: mrAbort; Name: 'mrAbort'),
    (Value: mrRetry; Name: 'mrRetry'),
    (Value: mrIgnore; Name: 'mrIgnore'),
    (Value: mrYes; Name: 'mrYes'),
    (Value: mrNo; Name: 'mrNo'),
    (Value: mrAll; Name: 'mrAll'),
    (Value: mrNoToAll; Name: 'mrNoToAll'),
    (Value: mrYesToAll; Name: 'mrYesToAll'));
 
var
  value: Integer;
begin
  {
  const
    BitBtnModalResults: array[TBitBtnKind] of TModalResult = (
      0, mrOK, mrCancel, 0, mrYes, mrNo,
      mrClose, mrAbort, mrRetry, mrIgnore, mrAll,
      mrNoToAll, mrYesToAll);
  }
  IdenttoInt( 'mr' + str, result, ModalResults);
  if result = -1 then
    Result := mrCancel;
end;
 
 
 
function GetBKType(str:string):TBitBtnKind;
var
  value: Integer;
begin
  {
  TBitBtnKind = (bkCustom, bkOK, bkCancel, bkHelp, bkYes, bkNo,
                 bkClose, bkAbort, bkRetry, bkIgnore, bkAll,
                 bkNoToAll, bkYesToAll);
  }
  value := GetEnumValue(TypeInfo(TBitBtnKind), 'bk' + str);
  if value > -1 then
    Result := TBitBtnKind(value)
  else
    Result := bkCancel;
end;
 
type
 
  { TQForm }
 
  TQForm = class(TForm)
    TimeoutTimer: TTimer;
    procedure TimerTimeoutTimer(Sender: TObject);
  private
    type
      TBtnParams = record
        Caption: string;
        Kind: integer;
        IsDefaultOrCancel: Byte;  // 1 = IsDefault, 2 = IsCancel
      end;
      TBtnParamsArray = array of TBtnParams;
  private
    FButtonAlignment: TAlignment;
    FButtons: array of TBitBtn;
    FButtonPanel: TPanel;
    FDialogType: TMsgDlgType;
    FImage: TGraphic;
    FImageBorder: TSize;
    FInnerButtonPanel: TPanel;
    FText: String;
    FTitle: String;
    FTimeout:integer;
    FTimeoutCounter:integer;
    FTextBorder: TSize;
    FTextAlignment: TAlignment;
    FTextBkColor: TColor;
    FTextLayout: TTextLayout;
    FTextPanel: TPanel;
    FXPos, FYPos: Integer;
    FDefaultModalResult:TModalResult;
    procedure SetDialogType(AValue: TMsgDlgType);
  protected
    procedure AdjustForm;
    procedure ApplyButtonAlignment;
    procedure CreateButtonPanel;
    procedure CreateInnerButtonPanel;
    procedure CreateTextPanel;
    procedure HelpClickHandler(Sender: TObject);
    procedure MeasureButtonPanel(var AWidth, AHeight: Integer);
    procedure MeasureText(var AWidth, AHeight: Integer);
    procedure MeasureTextPanel(Wrapped: Boolean; var AWidth, AHeight: Integer);
    procedure PaintTextPanelHandler(Sender: TObject);
    function PrepareButtons(const AButtons: array of const): TBtnParamsArray;
    procedure SetFormPosition(AX, AY: Integer);
  public
    constructor CreateNew(AOwner: TComponent; Num: Integer = 0); override;
    destructor Destroy; override;
    procedure AddButtons(const AButtons: array of const);
    function ShowModal: TModalResult; override;
    property ButtonAlignment: TAlignment read FButtonAlignment write FButtonAlignment;
    property DialogType: TMsgDlgType read FDialogType write SetDialogType;
    property Msg: String read FText write FText;
    property Title: String read FTitle write FTitle;
    property Timeout: integer read FTimeout write FTimeout;
    property TimeoutCounter: integer read FTimeoutCounter write FTimeoutCounter;
    property DefaultModalResult: TModalResult read FDefaultModalResult write FDefaultModalResult;
    property TextAlignment: TAlignment read FTextAlignment write FTextAlignment;
    property TextBkColor: TColor read FTextBkColor write FTextBkColor;
    property TextLayout: TTextLayout read FTextLayout write FTextLayout;
    property XPos: Integer read FXPos write FXPos;
    property YPos: Integer read FYPos write FYPos;
  end;
 
constructor TQForm.CreateNew(AOwner: TComponent; Num: Integer = 0);
begin
  inherited;
  BorderStyle := bsDialog;
  PopupMode := pmAuto;
 
  FImageBorder.CX := Scale96ToFont(DEFAULT_IMAGE_BORDER_X);
  FImageBorder.CY := Scale96ToFont(DEFAULT_IMAGE_BORDER_Y);
  FTextBorder.CX := Scale96ToFont(DEFAULT_TEXT_BORDER_X);
  FTextBorder.CY := Scale96ToFont(DEFAULT_TEXT_BORDER_Y);
 
  CreateTextPanel;
  CreateButtonPanel;
  CreateInnerButtonPanel;
end;
 
destructor TQForm.Destroy;
begin
  FImage.Free;
  inherited;
end;
 
procedure TQForm.AddButtons(const AButtons: array of const);
const
  MsgDlgBtnStr : array [0..11] of string = (
    'Yes', 'No', 'OK', 'Cancel', 'Abort', 'Retry', 'Ignore',
    'All', 'NoToAll', 'YesToAll', 'Help', 'Close'
  );
var
  btnParams: TBtnParamsArray;
  i: Integer;
  btn: TBitBtn;
begin
  // Translate given Buttons into array of button parameters.
  btnParams := PrepareButtons(AButtons);
 
  // Create BitBtn's
  SetLength(FButtons, Length(btnParams));
  for i := 0 to high(btnParams) do
  begin
    btn := TBitBtn.Create(Self);
    btn.Parent := FInnerButtonPanel;
    btn.GlyphShowMode := QuestionDlgEx_GlyphShowMode;
    btn.Kind := GetBKType(MsgDlgBtnStr[btnParams[i].Kind]);
    if btn.Kind = bkHelp then
      btn.OnClick := @HelpClickHandler;
    if trim(btnParams[i].Caption) <> '' then
     btn.Caption := btnParams[i].Caption;
    btn.Constraints.MinWidth := Scale96ToFont(BUTTON_MIN_WIDTH);
    btn.Cancel := (btnParams[i].IsDefaultOrCancel = 2);
    btn.Default := (btnParams[i].IsDefaultOrCancel = 1);
    if btn.Default then
    begin
      ActiveControl := btn;
      self.DefaultModalResult := GetBitBtnModalResults(MsgDlgBtnStr[btnParams[i].Kind]);
    end;
    btn.AutoSize := true;
    btn.BorderSpacing.Left := Scale96ToFont(BUTTON_SPACING);
    btn.BorderSpacing.Right := Scale96ToFont(BUTTON_SPACING);
//    btn.BorderSpacing.Around := Scale96ToFont(BUTTON_SPACING);
    if i = 0 then
      btn.AnchorSideLeft.Control := FInnerButtonPanel
    else
    begin
      btn.AnchorSideLeft.Control := FButtons[i-1];
      btn.AnchorSideLeft.Side := asrBottom;
    end;
    btn.AnchorSideTop.Control := FInnerButtonPanel;
    FButtons[i] := btn;
  end;
end;
 
procedure TQForm.AdjustForm;
var
  buttonPanelWidth: Integer = 0;
  buttonPanelHeight: Integer = 0;
  textPanelWidth: Integer = 0;
  textPanelHeight: Integer = 0;
begin
  if (FTextPanel = nil) or (FButtonPanel = nil) then
    exit;
 
  MeasureButtonPanel(buttonPanelWidth, buttonPanelHeight);
  MeasureTextPanel(false, textPanelWidth, textPanelHeight);
 
  if buttonPanelWidth > textPanelWidth then
  begin
    textPanelWidth := buttonPanelWidth;
  end else
  begin
    MeasureTextPanel(true, textPanelWidth, textPanelHeight);
    if textPanelWidth > QuestionDlgEx_MaxWidth then
    begin
      if buttonPanelWidth > QuestionDlgEx_Maxwidth then
        textPanelWidth := buttonPanelWidth
      else
        textPanelWidth := QuestionDlgEx_MaxWidth;
      MeasureTextPanel(true, textPanelWidth, textPanelheight);
    end;
  end;
 
  FTextPanel.SetBounds(0, 0, textPanelWidth, textPanelHeight);
  FInnerButtonPanel.SetBounds(0, 0, buttonPanelWidth, buttonPanelHeight);
  FButtonPanel.SetBounds(0, textPanelHeight, textPanelWidth, buttonPanelHeight);
 
  ClientWidth := textPanelWidth;
  ClientHeight := textPanelHeight + buttonPanelHeight;
 
  ApplyButtonAlignment;
end;
 
procedure TQForm.ApplyButtonAlignment;
begin
  // Set button and text alignments
  case FButtonAlignment of
    taLeftJustify:
      FInnerButtonPanel.AnchorSideLeft.Side := asrLeft;
    taCenter:
      FInnerButtonPanel.AnchorSideLeft.Side := asrCenter;
    taRightJustify:
      begin
        FInnerButtonPanel.Anchors := [akTop, akRight];
        FInnerButtonPanel.AnchorSideRight.Control := FButtonPanel;
        FInnerButtonPanel.AnchorSideRight.Side := asrRight;
      end;
  end;
end;
 
procedure TQForm.CreateButtonPanel;
begin
  FButtonPanel := TPanel.Create(Self);
  with FButtonPanel do
begin
    BevelOuter := bvNone;
    Caption := '';
    Color := clBtnFace;
    Parent := Self;
  end;
end;
 
procedure TQForm.CreateInnerButtonPanel;
begin
  FInnerButtonPanel := TPanel.Create(Self);
  with FInnerButtonPanel do
  begin
    AnchorSideLeft.Control := FButtonPanel;
    AnchorSideTop.Control := FButtonPanel;
    BevelOuter := bvNone;
    BorderSpacing.Top := Scale96ToFont(BUTTON_SPACING);
    BorderSpacing.Bottom := Scale96ToFont(BUTTON_SPACING);
    Caption := '';
    AutoSize := true;
    Parent := FButtonPanel;
  end;
end;
 
procedure TQForm.CreateTextPanel;
begin
  FTextPanel := TPanel.Create(Self);
  with FTextPanel do
  begin
    BevelOuter := bvNone;
    Caption := '';
    Color := QuestionDlgEx_TextBkColor;
    Parent := Self;
    OnPaint := @PaintTextPanelHandler;
  end;
end;
 
procedure TQForm.HelpClickHandler(Sender: TObject);
begin
  ShowHelp;
end;
 
procedure TQForm.MeasureButtonPanel(var AWidth, AHeight: Integer);
var
  dy1, dy2: Integer;
begin
  AWidth := 0;
  AHeight := 0;
 
  FInnerButtonPanel.HandleNeeded;
  FInnerButtonPanel.GetPreferredSize(AWidth, AHeight);
 
  dy1 := Max(FInnerButtonPanel.BorderSpacing.Top, FInnerButtonPanel.BorderSpacing.Around);
  dy2 := Max(FInnerButtonPanel.BorderSpacing.Bottom, FInnerButtonPanel.BorderSpacing.Around);
  inc(AHeight, dy1 + dy2);
 
  if QuestionDlgEx_MinWidth > AWidth then
    AWidth := QuestionDlgEx_MinWidth;
end;
 
{ Measures width and height of the message text.
  The max text width is given as input parameter AWidth. When the text is wider
  it is wrapped. }
procedure TQForm.MeasureText(var AWidth, AHeight: Integer);
var
  R: TRect;
  flags: integer;
begin
 R := Rect(0, 0, AWidth, AHeight);
  flags := DT_CALCRECT or DT_WORDBREAK;
  DrawText(Canvas.Handle, PChar(FText), Length(FText), R, flags);
  AWidth := R.Right;
  AHeight := R.Bottom;
end;
 
{ Measures the total width and heigth of the text panel including icon and
  borders.
  Is called twice:
  - When Wrapped is false the max width is determine so that wrapping does not
    occur.
  - When wrapped is true the parameter AWidth on input defines the width into
    which the text must fit; when it is wider it is wrapped. }
procedure TQForm.MeasureTextPanel(Wrapped: Boolean; var AWidth, AHeight: Integer);
var
  R: TRect;
  x: Integer;
  flags: Integer;
  imgHeight: Integer;
begin
  x := FImageBorder.CX;
  if Assigned(FImage) then
    inc(x, FImage.Width + Max(FImageBorder.CX, FTextBorder.CX));
  if not Wrapped then AWidth := 9999;
  R := Rect(x, FTextBorder.CY, AWidth, 9999);
  flags := DT_CALCRECT;
  if Wrapped then flags := flags or DT_WORDBREAK;
  HandleNeeded;
  DrawText(Canvas.Handle, PChar(FText), Length(FText), R, flags);
  inc(R.Bottom, FTextBorder.CY);
  AWidth := R.Right;
  if AWidth < QuestionDlgEx_MinWidth then
    AWidth := QuestionDlgEx_MinWidth;
  AHeight := R.Bottom;
  if Assigned(FImage) then
    imgHeight := FImage.Height
  else
    imgHeight := Scale96ToFont(DEFAULT_IMAGE_SIZE);
  if (AHeight < 2*FImageBorder.CY + imgHeight) then
    AHeight := 2*FImageBorder.CY + imgHeight;
  if AHeight < QuestionDlgEx_MinHeight then
    AHeight := QuestionDlgEx_MinHeight;
end;
 
{ The TextPanels OnPaint handler. It draws the icon and the text. }
procedure TQForm.PaintTextPanelHandler(Sender: TObject);
var
  x: Integer;
  R: TRect;
  flags: Integer;
  w, h: Integer;
begin
  with FTextPanel do begin
    Canvas.Brush.Color := Color;
    Canvas.FillRect(0, 0, Width, Height);
    x := FImageBorder.CX;
    if Assigned(FImage) then
    begin
      Canvas.Draw(x, FImageBorder.CY, FImage);
      inc(x, FImage.Width + Max(FImageBorder.CX, FTextBorder.CX));
    end;
    if FText <> '' then
    begin
      R := Rect(x, FTextBorder.CY, Width - FTextBorder.CX, Height - FTextBorder.CY);
      w := R.Right - x;
      h := R.Bottom - FTextBorder.CY;
      MeasureText(w, h);
      flags := DT_WORDBREAK;
      case FTextAlignment of
        taLeftJustify: flags := flags or DT_LEFT;
        taCenter: flags := flags or DT_CENTER;
        taRightJustify: flags := flags or DT_RIGHT;
      end;
      case FTextLayout of
        tlTop: ;
        tlCenter: R.Top := (R.Top + R.Bottom - h) div 2;
        tlBottom: R.Top := R.Bottom - h;
      end;
      DrawText(Canvas.Handle, PChar(FText), Length(FText), R, flags);
    end;
  end;
end;
 
function TQForm.PrepareButtons(const AButtons: array of const): TBtnParamsArray;
var
  i, n: Integer;
begin
  Result := nil;
  SetLength(Result, Length(AButtons));
  n := -1;
  for i := 0 to high(AButtons) do
  begin
    if AButtons[i].VType = vtAnsiString then //text
    begin
      if SameText(AButtons[i].VPChar, 'IsDefault') then
        Result[n].IsDefaultOrCancel := 1
      else if SameText(AButtons[i].VPChar, 'IsCancel') then
        Result[n].IsDefaultOrCancel := 2
      else
        Result[n].Caption := AButtons[i].VPChar
    end else
    if AButtons[i].VType = vtInteger then
    begin
      inc(n);
      Result[n].Kind := AButtons[i].VInteger;
    end;
  end;
  SetLength(Result, n+1);
end;
 
procedure TQForm.SetDialogType(AValue: TMsgDlgType);
var
  iconKind: Integer;
begin
  FDialogType := AValue;
  iconKind := idDialogBase + 1 + ord(FDialogType);
  FImage.Free;
  if (FDialogType = mtCustom) and Assigned(QuestionDlgEx_CustomIcon) then
  begin
    if QuestionDlgEx_CustomIcon.Png <> nil then
      FImage := TPortableNetworkGraphic.Create
    else if QuestionDlgEx_CustomIcon.Bitmap <> nil then
      FImage := TBitmap.Create
    else if QuestionDlgEx_CustomIcon.Icon <> nil then
      FImage := TIcon.Create
    else if QuestionDlgEx_CustomIcon.Graphic <> nil then
      FImage := TGraphic(QuestionDlgEx_CustomIcon.ClassType).Create
    else
      exit;
    Fimage.Assign(QuestionDlgEx_CustomIcon);
  end else
    FImage := GetDialogIcon(iconKind);
end;
 
procedure TQForm.SetFormPosition(AX, AY: Integer);
const
  NOT_USED = MaxInt;
var
  R: TRect;
begin
  if (AX < 0) and (AY = MaxInt) then
  begin
     Position := TPosition(-AX);
  end else
  begin
    R := Monitor.WorkAreaRect;
    Position := poDesigned;
    if AY = NOT_USED then // Screen Center  Y
      Top := (R.Top + R.Bottom - Height) div 2
    else
    if (AY + Height) <= (Monitor.WorkareaRect.Bottom - 30) then
      Top := AY
    else // prevent displaying outside bottom screen border
      Top := R.Bottom - Height - 30;
 
    if AX = NOT_USED then // Screen Center  X
      Left := (R.Left + R.Right - Width) div 2
    else
    if (AX + Width) <= Monitor.WorkareaRect.Right then
      Left := AX
    else // prevent displaying outside right screen border
      Left := R.Right - Width;
  end;
end;
 
function TQForm.ShowModal: TModalResult;
begin
  AdjustForm;
  SetFormPosition(FXPos, FYPos);
  Result := inherited;
end;
 
procedure TQForm.TimerTimeoutTimer(Sender: TObject);
begin
  if self.TimeoutCounter < self.Timeout then
  begin
    self.TimeoutCounter:=self.TimeoutCounter+TimeoutTimer.Interval;
    self.Caption:= self.Title+' ['+inttostr(trunc( (self.Timeout+TimeoutTimer.Interval - self.TimeoutCounter) / TimeoutTimer.Interval))+']';;
  end else
  begin
    // Timeout eingetreten
    TimeoutTimer.Enabled:=false;
    self.ModalResult:=self.DefaultModalResult;
    self.close;
  end;
end;
 
function CreateQuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AX, AY: Integer; timeout:integer=-1): TForm;
var
  QForm: TQForm;
begin
  QForm := TQForm.CreateNew(Screen.ActiveCustomForm);
  if QuestionDlgEx_FontName = '' then
    QForm.Font.Name := 'default'
  else
    QForm.Font.Name := QuestionDlgEx_FontName;
  QForm.Font.Size := QuestionDlgEx_FontSize;
  QForm.ButtonAlignment := QuestionDlgEx_ButtonAlignment;
  QForm.TextAlignment := QuestionDlgEx_TextAlignment;
  QForm.TextBkColor := QuestionDlgEx_TextBkColor;
  QForm.TextLayout := QuestionDlgEx_TextLayout;
  QForm.Title := ACaption;
  QForm.DialogType := ADlgtype;
  QForm.Caption := ACaption;
  QForm.Msg := AMsg;
  if rightstr(QForm.msg,1) <> #13 then
    QForm.Msg:=QForm.Msg+#13;
  QForm.AddButtons(AButtons);
  QForm.XPos := AX;
  QForm.YPos := AY;
  if timeout > 0 then
  begin
    QForm.Timeout:=timeout*1000;
    QForm.TimeoutCounter:=0;
    QForm.TimeoutTimer:=TTimer.Create(QForm);
    QForm.TimeoutTimer.Enabled:=false;
    QForm.TimeoutTimer.Interval:=1000;
    QForm.TimeoutTimer.OnTimer:=@QForm.TimerTimeoutTimer;
    QForm.TimeoutTimer.Enabled:=true;
  end;
  Result := QForm;
end;
 
function QuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AX: Integer = MaxInt; AY: Integer = MaxInt): TModalResult;
var
  QForm: TForm;
begin
  QForm := CreateQuestionDlgEx( ACaption, AMsg, ADlgType, AButtons, AX, AY);
  try
    result := QForm.ShowModal;
  finally
    FreeAndNil(QForm);
  end;
end;
 
function QuestionDlgEx(const ACaption, AMsg: string; timeout:integer; ADlgType: TMsgDlgType;
  const AButtons: array of const; AX: Integer = MaxInt; AY: Integer = MaxInt): TModalResult;
var
  QForm: TForm;
begin
  QForm := CreateQuestionDlgEx( ACaption, AMsg, ADlgType, AButtons, AX, AY, timeout);
  try
    result := QForm.ShowModal;
  finally
    FreeAndNil(QForm);
  end;
end;
 
function QuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AHelpCtx, AX, AY: Integer): TModalResult;
var
  QForm: TForm;
begin
  QForm := CreateQuestionDlgEx( ACaption, AMsg, ADlgType, AButtons, AX, AY);
  try
    QForm.HelpContext := AHelpCtx;
    result := QForm.ShowModal;
  finally
    FreeAndNil(QForm);
  end;
end;
 
function QuestionDlgEx(const ACaption, AMsg: string; ADlgType: TMsgDlgType;
  const AButtons: array of const; AHelpKeyword: String;
  AX: Integer = MaxInt; AY: Integer= MaxInt): TModalResult; overload;
var
  QForm: TForm;
begin
  QForm:=CreateQuestionDlgEx( ACaption, AMsg, ADlgType, AButtons, AX, AY);
  try
    QForm.HelpKeyword := AHelpKeyword;
    result := QForm.ShowModal;
  finally
    FreeAndNil(QForm);
  end;
end;
 
finalization
  FreeAndNil(QuestionDlgex_CustomIcon);
 
end.
