# HG changeset patch
# User Pantxo Diribarne <pantxo.diribarne@gmail.com>
# Date 1538508389 -7200
#      Tue Oct 02 21:26:29 2018 +0200
# Node ID 50a8f54b55c977fa12f0f8c4190fd212392b11dc
# Parent  b3272f0a6480768a7cf47120a440b1e94434f4bf
msgbox.m: use graphics objects to build dialog (bug #42490)

* msgbox.m : New subfunction to build a standard dialog using
  graphics objects (__msgbox__). Implement Matlab compatible options argument
  for changing the "windowstyle" property of the figure and the "interpreter"
  property of the diplayed text. Add BIST for input checks and demos.
  Update documentation.

* errordlg.m, helpdlg.m, warndlg.m: Adapt argument checks. Update documentation.

* BaseControl.cc: Prevent BaseControl objects from using invalid pointers to
  figure objects.

* script/gui/__error-icon__.png, __help-icon__.png, __warn-icon__.png: New icon
  files from the Tango icon theme.
  
* script/gui/module.mk: add new images to SCRIPTS_IMAGES variable

* octave-link.h (octave_link::message_dialog, octave_link::do_message_dialog):
  Remove methods.
  
* octave-link.cc (F__octave_link_message_dialog__): Remove interpreter function.

* octave-link.h/cc ( octave_qt_link::do_message_dialog): Remove method.

diff --git a/libgui/graphics/BaseControl.cc b/libgui/graphics/BaseControl.cc
--- a/libgui/graphics/BaseControl.cc
+++ b/libgui/graphics/BaseControl.cc
@@ -200,8 +200,8 @@
           uicontrol::properties& up = Utils::properties<uicontrol> (go);
           graphics_object fig = go.get_ancestor ("figure");
 
-          if (m->button () != Qt::LeftButton
-              || ! up.enable_is ("on"))
+          if (fig && (m->button () != Qt::LeftButton
+                      || ! up.enable_is ("on")))
             {
               gh_manager::post_set (fig.get_handle (), "selectiontype",
                                     Utils::figureSelectionType (m), false);
@@ -236,9 +236,13 @@
             graphics_object go = object ();
             graphics_object fig = go.get_ancestor ("figure");
 
-            gh_manager::post_set (fig.get_handle (), "currentpoint",
-                                  Utils::figureCurrentPoint (fig, m), false);
-            gh_manager::post_callback (fig.get_handle (), "windowbuttonmotionfcn");
+            if (fig)
+              {
+                gh_manager::post_set (fig.get_handle (), "currentpoint",
+                                      Utils::figureCurrentPoint (fig, m), false);
+                gh_manager::post_callback (fig.get_handle (),
+                                           "windowbuttonmotionfcn");
+              }
           }
         break;
 
diff --git a/libgui/src/octave-qt-link.cc b/libgui/src/octave-qt-link.cc
--- a/libgui/src/octave-qt-link.cc
+++ b/libgui/src/octave-qt-link.cc
@@ -127,31 +127,6 @@
     return (answer == tr ("Create"));
   }
 
-  int octave_qt_link::do_message_dialog (const std::string& dlg,
-                                         const std::string& msg,
-                                         const std::string& title)
-  {
-    // Lock mutex before signaling.
-    uiwidget_creator.lock ();
-
-    uiwidget_creator.signal_dialog (QString::fromStdString (msg),
-                                    QString::fromStdString (title),
-                                    QString::fromStdString (dlg),
-                                    QStringList (), QString (),
-                                    QStringList ());
-
-    // Wait while the user is responding to message box.
-    uiwidget_creator.wait ();
-
-    // The GUI has sent a signal and the thread has been awakened.
-
-    int answer = uiwidget_creator.get_dialog_result ();
-
-    uiwidget_creator.unlock ();
-
-    return answer;
-  }
-
   std::string octave_qt_link::do_question_dialog (const std::string& msg,
                                                   const std::string& title,
                                                   const std::string& btn1,
diff --git a/libgui/src/octave-qt-link.h b/libgui/src/octave-qt-link.h
--- a/libgui/src/octave-qt-link.h
+++ b/libgui/src/octave-qt-link.h
@@ -75,9 +75,6 @@
     bool do_edit_file (const std::string& file);
     bool do_prompt_new_edit_file (const std::string& file);
 
-    int do_message_dialog (const std::string& dlg, const std::string& msg,
-                           const std::string& title);
-
     std::string
     do_question_dialog (const std::string& msg, const std::string& title,
                         const std::string& btn1, const std::string& btn2,
diff --git a/libinterp/corefcn/octave-link.cc b/libinterp/corefcn/octave-link.cc
--- a/libinterp/corefcn/octave-link.cc
+++ b/libinterp/corefcn/octave-link.cc
@@ -152,28 +152,6 @@
   return retval;
 }
 
-DEFUN (__octave_link_message_dialog__, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} __octave_link_message_dialog__ (@var{dlg}, @var{msg}, @var{title})
-Undocumented internal function.
-@end deftypefn */)
-{
-  octave_value retval;
-
-  if (args.length () == 3)
-    {
-      std::string dlg = args(0).xstring_value ("invalid arguments");
-      std::string msg = args(1).xstring_value ("invalid arguments");
-      std::string title = args(2).xstring_value ("invalid arguments");
-
-      octave::flush_stdout ();
-
-      retval = octave_link::message_dialog (dlg, msg, title);
-    }
-
-  return retval;
-}
-
 DEFUN (__octave_link_question_dialog__, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn {} {} __octave_link_question_dialog__ (@var{msg}, @var{title}, @var{btn1}, @var{btn2}, @var{btn3}, @var{default})
diff --git a/libinterp/corefcn/octave-link.h b/libinterp/corefcn/octave-link.h
--- a/libinterp/corefcn/octave-link.h
+++ b/libinterp/corefcn/octave-link.h
@@ -148,13 +148,6 @@
     return enabled () ? instance->do_prompt_new_edit_file (file) : false;
   }
 
-  static int
-  message_dialog (const std::string& dlg, const std::string& msg,
-                  const std::string& title)
-  {
-    return enabled () ? instance->do_message_dialog (dlg, msg, title) : 0;
-  }
-
   static std::string
   question_dialog (const std::string& msg, const std::string& title,
                    const std::string& btn1, const std::string& btn2,
@@ -492,10 +485,6 @@
   virtual bool do_edit_file (const std::string& file) = 0;
   virtual bool do_prompt_new_edit_file (const std::string& file) = 0;
 
-  virtual int
-  do_message_dialog (const std::string& dlg, const std::string& msg,
-                     const std::string& title) = 0;
-
   virtual std::string
   do_question_dialog (const std::string& msg, const std::string& title,
                       const std::string& btn1, const std::string& btn2,
diff --git a/scripts/gui/__error-icon__.png b/scripts/gui/__error-icon__.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..659d3b9956b5b93493063f4e0c599667e5a0e749
GIT binary patch
diff --git a/scripts/gui/__help-icon__.png b/scripts/gui/__help-icon__.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8b04f6d7d5a0ebb89d24deb228a11c096314ac3d
GIT binary patch
diff --git a/scripts/gui/__warn-icon__.png b/scripts/gui/__warn-icon__.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2d19e7a2f328a426031dc85adb4116687ccc4b39
GIT binary patch
diff --git a/scripts/gui/errordlg.m b/scripts/gui/errordlg.m
--- a/scripts/gui/errordlg.m
+++ b/scripts/gui/errordlg.m
@@ -17,10 +17,11 @@
 ## <https://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {} {@var{h} =} errordlg ()
-## @deftypefnx {} {@var{h} =} errordlg (@var{msg})
-## @deftypefnx {} {@var{h} =} errordlg (@var{msg}, @var{title})
-## @deftypefnx {} {@var{h} =} errordlg (@var{msg}, @var{title}, @var{createmode})
+## @deftypefn  {} {} errordlg ()
+## @deftypefnx {} {} errordlg (@var{msg})
+## @deftypefnx {} {} errordlg (@var{msg}, @var{title})
+## @deftypefnx {} {} errordlg (@var{msg}, @var{title}, @var{opt})
+## @deftypefnx {} {@var{h} =} errordlg (@dots{})
 ## Display an error dialog box with error message @var{msg} and caption
 ## @var{title}.
 ##
@@ -30,11 +31,11 @@
 ## The error message may have multiple lines separated by newline characters
 ## ("\n"), or it may be a cellstr array with one element for each line.
 ##
-## The return value @var{h} is always 1.
+## The third optionnal argument @var{opt} controls the behavior of the dialog.
+## See @code{msgbox} for details.
 ##
-## Compatibility Note: The optional argument @var{createmode} is accepted for
-## @sc{matlab} compatibility, but is not implemented.  See @code{msgbox} for
-## details.
+## The return value @var{h} is a handle to the figure object used for
+## building the dialog.
 ##
 ## Examples:
 ##
@@ -47,32 +48,37 @@
 ## @end group
 ## @end example
 ##
-## @seealso{helpdlg, inputdlg, listdlg, msgbox, questdlg, warndlg}
+## @seealso{helpdlg, warndlg, msgbox, inputdlg, listdlg, questdlg}
 ## @end deftypefn
 
-function retval = errordlg (varargin)
+function h = errordlg (varargin)
 
-  narginchk (0, 3);
+  msg = "This is the default error.";
+  tit = "Error Dialog";
+  opt = "non-modal";
 
-  msg = "This is the default error string.";
-  title = "Error Dialog";
-
-  if (nargin > 0)
+  nargs = numel (varargin);
+  
+  if (nargs > 3)
+    print_usage ();
+  elseif (nargs == 1)
     msg = varargin{1};
+  elseif (nargs == 2)
+    msg = varargin{1};
+    tit = varargin{2};
+  elseif (nargs == 3)
+    msg = varargin{1};
+    tit = varargin{2};
+    opt = varargin{3};
   endif
-  if (nargin > 1)
-    title = varargin{2};
-  endif
+  
+  retval = msgbox (msg, tit, "error", opt);
 
-  if (nargin < 3)
-    retval = msgbox (msg, title, "error");
-  else
-    retval = msgbox (msg, title, "error", varargin{3});
+  if (nargout)
+    h = htmp;
   endif
 
 endfunction
 
-
-%!error errordlg (1, 2, 3, 4)
-%!error <MSG must be a character string> errordlg (1)
-%!error <TITLE must be a character string> errordlg ("msg", 1)
+## No BIST tests.  This function just dispatches to msgbox().
+%!assert (1)
diff --git a/scripts/gui/helpdlg.m b/scripts/gui/helpdlg.m
--- a/scripts/gui/helpdlg.m
+++ b/scripts/gui/helpdlg.m
@@ -17,9 +17,10 @@
 ## <https://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {} {@var{h} =} helpdlg ()
-## @deftypefnx {} {@var{h} =} helpdlg (@var{msg})
-## @deftypefnx {} {@var{h} =} helpdlg (@var{msg}, @var{title})
+## @deftypefn  {} {} helpdlg ()
+## @deftypefnx {} {} helpdlg (@var{msg})
+## @deftypefnx {} {} helpdlg (@var{msg}, @var{title})
+## @deftypefnx {} {@var{h} =} helpdlg (@dots{})
 ## Display a help dialog box with help message @var{msg} and caption
 ## @var{title}.
 ##
@@ -29,7 +30,8 @@
 ## The help message may have multiple lines separated by newline characters
 ## ("\n"), or it may be a cellstr array with one element for each line.
 ##
-## The return value @var{h} is always 1.
+## The return value @var{h} is a handle to the figure object used for
+## building the dialog.
 ##
 ## Examples:
 ##
@@ -42,28 +44,33 @@
 ## @end group
 ## @end example
 ##
-## @seealso{errordlg, inputdlg, listdlg, msgbox, questdlg, warndlg}
+## @seealso{errordlg, warndlg, msgbox, inputdlg, listdlg, questdlg}
 ## @end deftypefn
 
-function retval = helpdlg (varargin)
+function h = helpdlg (varargin)
 
-  narginchk (0, 2);
-
-  msg = "This is the default help string.";
-  title = "Help Dialog";
+  msg = "This is the default help.";
+  tit = "Help Dialog";
+  opt = "non-modal";
 
-  if (nargin > 0)
+  nargs = numel (varargin);
+  
+  if (nargs > 2)
+    print_usage ();
+  elseif (nargs == 1)
     msg = varargin{1};
+  elseif (nargs == 2)
+    msg = varargin{1};
+    tit = varargin{2};
   endif
-  if (nargin > 1)
-    title = varargin{2};
+  
+  htmp = msgbox (msg, tit, "help", opt);
+
+  if (nargout)
+    h = htmp;
   endif
 
-  retval = msgbox (msg, title, "help");
-
 endfunction
 
-
-%!error helpdlg (1, 2, 3)
-%!error <MSG must be a character string> helpdlg (1)
-%!error <TITLE must be a character string> helpdlg ("msg", 1)
+## No BIST tests.  This function just dispatches to msgbox().
+%!assert (1)
diff --git a/scripts/gui/module.mk b/scripts/gui/module.mk
--- a/scripts/gui/module.mk
+++ b/scripts/gui/module.mk
@@ -43,6 +43,11 @@
   %reldir%/waitforbuttonpress.m \
   %reldir%/warndlg.m
 
+SCRIPTS_IMAGES += \
+  %reldir%/__error-icon__.png \
+  %reldir%/__help-icon__.png \
+  %reldir%/__warn-icon__.png
+
 %canon_reldir%dir = $(fcnfiledir)/gui
 
 %canon_reldir%_DATA = $(%canon_reldir%_FCN_FILES)
diff --git a/scripts/gui/msgbox.m b/scripts/gui/msgbox.m
--- a/scripts/gui/msgbox.m
+++ b/scripts/gui/msgbox.m
@@ -20,7 +20,9 @@
 ## @deftypefn  {} {@var{h} =} msgbox (@var{msg})
 ## @deftypefnx {} {@var{h} =} msgbox (@var{msg}, @var{title})
 ## @deftypefnx {} {@var{h} =} msgbox (@var{msg}, @var{title}, @var{icon})
-## @deftypefnx {} {@var{h} =} msgbox (@dots{}, @var{createmode})
+## @deftypefnx {} {@var{h} =} msgbox (@var{msg}, @var{title}, "custom", @var{cdata})
+## @deftypefnx {} {@var{h} =} msgbox (@var{msg}, @var{title}, "custom", @var{cdata}, @var{colormap})
+## @deftypefnx {} {@var{h} =} msgbox (@dots{}, @var{opt})
 ## Display @var{msg} using a message dialog box.
 ##
 ## The message may have multiple lines separated by newline characters ("\n"),
@@ -31,15 +33,37 @@
 ##
 ## The optional argument @var{icon} selects a dialog icon.
 ## It can be one of @qcode{"none"} (default), @qcode{"error"}, @qcode{"help"},
-## or @qcode{"warn"}.
+## @qcode{"warn"} or @qcode{"custom"}. The latter must be followed at
+## least by an image array  @var{cdata} and eventually its associated
+## colormap for an indexed image.
 ##
-## The return value is always 1.
+## Finally the optional argument @var{opt} controls the behavior of the dialog.
+## If @var{opt} is a string, it may be one of 
 ##
-## Compatibility Note: The optional argument @var{createmode} is accepted for
-## @sc{matlab} compatibility, but is not implemented.  A valid @var{createmode}
-## is either one of the character strings @qcode{"nonmodal"}, @qcode{"modal"},
-## or @qcode{"replace"}, or a structure containing a field
-## @qcode{"WindowStyle"} with one of the three character strings.
+## @table @asis
+## @item "modal"
+## The dialog is displayed "modal" which means it prevents users from
+## interacting with any other GUI element.
+## @item "non-modal"
+## The dialog is normal.
+## @item "replace"
+## If dialogs already exists with the same title, the latest is reused
+## and others are closed. The resulting dialog is set "non-modal".
+## @end table
+##
+## If @var{opt} is a structure, it must contain fields "WindowStyle" and
+## "Interpreter":
+##
+## @table @asis
+## @item "WindowStyle"
+## The value must be "modal", "non-modal" or "replace". See above.
+## @item "Interpreter"
+## Controls the @qcode{"interpreter"} property of the text object used
+## for displaying the message. The value must be "none", "tex" or "latex".
+## @end table
+## 
+## The return value @var{h} is a handle to the figure object used for
+## building the dialog.
 ##
 ## Examples:
 ##
@@ -58,70 +82,220 @@
 ## @seealso{errordlg, helpdlg, inputdlg, listdlg, questdlg, warndlg}
 ## @end deftypefn
 
-function retval = msgbox (msg, varargin)
-
-  narginchk (1, 4);
+function retval = msgbox (msg, tit = "", icon = "none", varargin)
 
-  if (! ischar (msg))
-    if (iscell (msg))
-      msg = sprintf ("%s\n", msg{:});
-      msg(end) = "";
+  ## Input checks
+  nargs = numel (varargin);
+  if (nargin < 1)
+    print_usage ();
+  elseif (! ischar (msg) && ! iscellstr (msg))
+    error ("msgbox: MSG must be a string or a cell array of strings");
+  elseif (! ischar (tit))
+    error ("msgbox: TITLE must be a string or a cell array of strings");
+  elseif (! any (strcmp (icon, {"help", "warn", "error", "none", "custom"})))
+    error ("msgbox: unhandled value for ICON data");
+  elseif (strcmp (icon, "custom"))
+    if (nargs < 1)
+      error ("msgbox: missing data for 'custom' icon");
     else
-      error ("MSG must be a character string or cellstr array");
+      icon = struct ("cdata", varargin{1}, "colormap", []);
+      if (! (ismatrix (icon.cdata) || size (icon.cdata, 3) == 3))
+        error ("msgbox: unhandled data for 'custom' icon");
+      elseif (size (icon.cdata, 3) == 1 && nargs > 1)
+        icon.colormap = varargin{2};
+        varargin(2) = [];
+        nargs--;
+      endif
+      varargin(1) = [];
+      nargs--;
     endif
   endif
 
-  box_title = "";
-  box_icon = "none";
-
-  if (nargin > 1)
-    ## check last element to be a structure CREATEMODE
-    if (isstruct (varargin{end}) && isfield (varargin{end}, "WindowStyle"))
-      varargin{end} = varargin{end}.WindowStyle;
-    endif
-    ## print warning for unsupported CREATEMODE
-    if ((ischar (varargin{end}))
-        && (ismember (varargin{end}, {"nonmodal", "modal", "replace"})))
-      warning ("CREATEMODE %s is not yet supported", varargin{end});
-      nargin = nargin - 1;
-    elseif (nargin == 4)
-      error ("CREATEMODE is not a valid type");
+  ## Window behavior and text interpreter
+  windowstyle = "normal";
+  interpreter = "tex";
+  if (nargs > 0)
+    if (isstruct (varargin{1}))
+      if (isfield (varargin{1}, "WindowStyle"))
+        windowstyle = varargin{1}.WindowStyle;
+      endif
+      if (isfield (varargin{1}, "Interpreter"))
+        interpreter = varargin{1}.Interpreter;
+      endif
+    else
+      windowstyle = varargin{1};
     endif
 
-    if ((nargin > 1) && (! ischar (varargin{1})))
-      error ("TITLE must be a character string");
-    else
-      box_title = varargin{1};
-    endif
-
-    if (nargin > 2)
-      box_icon = varargin{2};
-      switch (box_icon)
-        case {"error", "help", "warn", "none"}
-          ## do nothing, all valid
-        case "custom"
-          warning ("custom icons are not yet supported");
-        otherwise
-          error ("ICON is not a valid type")
-      endswitch
+    if (! any (strcmp (windowstyle, {"non-modal", "modal", "replace"})))
+      error ("msgbox: unhandled value %s for OPT", windowstyle);
+    elseif (strcmp (windowstyle, "non-modal"))
+      windowstyle = "normal";
     endif
   endif
-
-  ## make a GUI element or print to console
+  
+  ## Make a GUI element or print to console
   if (__octave_link_enabled__ ())
-    retval = __octave_link_message_dialog__ (box_icon, msg, box_title);
+    retval = __msgbox__ (msg, tit, icon, windowstyle, interpreter);
   else
-    disp (sprintf ("\n%s:\t%s\n\t%s\n", upper (box_icon), box_title,
+    if (iscellstr (msg))
+      msg = strjoin (msg, "\n");
+    endif
+    if (isstruct (icon))
+      icon = "custom";
+    endif
+    disp (sprintf ("\n%s:\t%s\n\t%s\n", upper (icon), tit,
       strrep (msg, "\n", "\n\t")));
     retval = 1;
   endif
 
 endfunction
 
+function hf = __msgbox__ (msg, tit, icon, windowstyle, interpreter)
+
+  ## Prepare graphics objects
+  hf = [];
+  if (strcmp (windowstyle, "replace"))
+    windowstyle = "normal";
+    hf = findall (groot, "tag", "__dialog__", "-and", "name", tit);
+  endif
+
+  if (! isempty (hf))
+    if (numel (hf) > 1)
+      close (hf(2:end));
+    endif
+    hf = hf(1);
+    set (hf, "visible", "off", "windowstyle", "normal");
+  else
+    hf = dialog ("visible", "off", "name", tit, "tag", "__dialog__", ...
+                 "windowstyle", "normal");
+    if (! strcmp (graphics_toolkit (), "qt"))
+      graphics_toolkit (hf, "qt");
+    endif
+  endif
+  
+  hp = uipanel (hf);
+  
+  hax = axes ("parent", hp, "visible", "off", "units", "pixels", ...
+              "ydir", "reverse");
+  
+  ht = text ("parent", hax, "string", msg, "units", "pixels", ...
+             "fontsize", 14, "interpreter", interpreter);
+  
+  ## Hold default icons data in a persistent variable
+  persistent cdata = struct ("help", [], "warn", [], "error", []);
+
+  icon_name = "custom";
+  if (ischar (icon))
+    icon_name = icon;
+    icon = struct ("cdata", [], "colormap", []);
+    if (isfield (cdata, icon_name))
+      if (! isempty (cdata.(icon_name)))
+        icon.cdata = cdata.(icon_name);
+      else
+        [tmp, ~, alpha] = imread (which (["__" icon_name "-icon__.png"]));
+        
+        ## Fake transparency until the opengl renderer handles it
+        tmp = im2double (tmp);
+        alpha = im2double (alpha);
+        backgnd = get (hp, "backgroundcolor");
+        tmp(:,:,1) = tmp(:,:,1).*alpha + backgnd(1) * (1-alpha);
+        tmp(:,:,2) = tmp(:,:,2).*alpha + backgnd(2) * (1-alpha);
+        tmp(:,:,3) = tmp(:,:,3).*alpha + backgnd(3) * (1-alpha);
+        icon.cdata = tmp;
+        cdata.(icon_name) = tmp;
+        
+      endif
+    endif
+  endif
+
+  ## Compute bbox in pixels
+  ax_sz = [200 60];
+  ax_margin = 12;
+  txt_margin = 20;
+  button_margin = 40;
+
+  extent = get (ht, "extent");
+  extent(3) += txt_margin;
+
+  im_sz = size (icon.cdata);
+  
+  if (! isempty (icon.cdata))
+    extent(3) += im_sz(2);
+    extent(4) = max (extent(4), im_sz(1));
+  endif
+
+  ax_sz = max ([ax_sz; extent(3:4)+2*ax_margin]);
+  
+  ## Align text left when there is an icon
+  text_offset = txt_margin;
+  if (! isempty (icon.cdata))
+    if (! isempty (icon.colormap))
+      set (hax, "colormap", icon.colormap)
+    endif
+    image ("parent", hax, "cdata", icon.cdata, "cdatamapping", "direct", ...
+           "xdata", [1 im_sz(2)], "ydata", [-(im_sz(1))/2+1 im_sz(1)/2]);
+    text_offset = im_sz(2) + txt_margin;
+  endif
+
+  ## Set objects position
+  wd = ax_sz(1);
+  hg = ax_sz(2) + button_margin;
+  center = get (0, "screensize")(3:4) / 2;
+  
+  set (hf, "position", [center(1)-wd/2 center(2)-hg/2 wd hg]);
+  
+  set (hax, "position", [0 button_margin ax_sz], ...
+       "xlim", [1 ax_sz(1)]-ax_margin, "ylim", [-ax_sz(2)/2 ax_sz(2)/2], ...
+       "units", "normalized");
+
+  set (ht, "units", "data", "position", [text_offset 0 0]);
+
+  hui = uicontrol ("string", "OK", "callback", @() close (gcbf ()), ...
+                   "position", [ax_sz(1)/2-40 ax_margin 80 28], "parent", hp);
+  
+  set (hf, "windowstyle", windowstyle, "visible", "on");
+
+endfunction
+
+%!demo
+%! msgbox ("A bare dialog");
+
+%!demo
+%! msgbox ("An informative string", "Documentation", "help");
+
+%!demo
+%! msgbox ("Something the user should hear about before continuing", ...
+%!         "Take care!", "warn");
+
+%!demo
+%! msgbox ("A critical message for the user", "Error", "error");
+
+%!demo
+%! msgbox ({"The default interpreter is'tex':", ...
+%!          "\\Delta_{1-2} = r_2 - r_1"}, "Documentation", "help");
+
+%!demo
+%! msgbox ({"Modal help dialog with uninterpreted string:", ...
+%!          "\\Delta_{1-2} = r_2 - r_1"}, "Documentation", "help", ...
+%!          struct ("Interpreter", "none", "WindowStyle", "replace"));
+
+%!demo
+%! msgbox ("Custom dialog with a random 32-by-32-by-3 RGB icon.", ...
+%!         "Dialog Title", "custom", rand (32, 32, 3));
+
+%!demo
+%! cdata = get (0, "factoryimagecdata"); 
+%! msgbox ({"Custom dialog with the default Octave image.", ...
+%!          "The image is indexed into the 'copper' colormap"}, ...
+%!         "Dialog Title", "custom", cdata, copper (64));
 
 ## Test input validation
-%!error <narginchk> msgbox (1, 2, 3, 4, 5)
-%!error <MSG must be a character string> msgbox (1)
-%!error <TITLE must be a character string> msgbox ("msg", 1)
-%!error <ICON is not a valid type> msgbox ("msg", "title", 1)
-%!error <CREATEMODE is not a valid> msgbox ("msg", "title", "help", "wrong")
+%!error <msgbox: MSG must be a string or a cell array of strings> msgbox (1)
+%!error <msgbox: TITLE must be a string or a cell array of strings>
+%! msgbox ("msg", 1)
+%!error <msgbox: unhandled value for ICON data> msgbox ("msg", "title", 1)
+%!error <msgbox: missing data for 'custom' icon>
+%! msgbox ("msg", "title", "custom")
+%!error <msgbox: missing colormap for 'custom' icon>
+%! msgbox ("msg", "title", "custom", rand (3, 3))
+
diff --git a/scripts/gui/warndlg.m b/scripts/gui/warndlg.m
--- a/scripts/gui/warndlg.m
+++ b/scripts/gui/warndlg.m
@@ -17,10 +17,11 @@
 ## <https://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {} {@var{h} =} warndlg ()
-## @deftypefnx {} {@var{h} =} warndlg (@var{msg})
-## @deftypefnx {} {@var{h} =} warndlg (@var{msg}, @var{title})
-## @deftypefnx {} {@var{h} =} warndlg (@var{msg}, @var{title}, @var{createmode})
+## @deftypefn  {} {} warndlg ()
+## @deftypefnx {} {} warndlg (@var{msg})
+## @deftypefnx {} {} warndlg (@var{msg}, @var{title})
+## @deftypefnx {} {} warndlg (@var{msg}, @var{title}, @var{opt})
+## @deftypefnx {} {@var{h} =} warndlg (@dots{})
 ## Display a warning dialog box with warning message @var{msg} and caption
 ## @var{title}.
 ##
@@ -30,11 +31,11 @@
 ## The warning message may have multiple lines separated by newline characters
 ## ("\n"), or it may be a cellstr array with one element for each line.
 ##
-## The return value @var{h} is always 1.
+## The third optionnal argument @var{opt} controls the behavior of the dialog.
+## See @code{msgbox} for details.
 ##
-## Compatibility Note: The optional argument @var{createmode} is accepted for
-## @sc{matlab} compatibility, but is not implemented.  See @code{msgbox} for
-## details.
+## The return value @var{h} is a handle to the figure object used for
+## building the dialog.
 ##
 ## Examples:
 ##
@@ -47,32 +48,37 @@
 ## @end group
 ## @end example
 ##
-## @seealso{errordlg, helpdlg, inputdlg, listdlg, msgbox, questdlg}
+## @seealso{errordlg, helpdlg, msgbox, inputdlg, listdlg, questdlg}
 ## @end deftypefn
 
-function retval = warndlg (varargin)
+function h = warndlg (varargin)
 
-  narginchk (0, 3);
+  msg = "This is the default warning.";
+  tit = "Warning Dialog";
+  opt = "non-modal";
 
-  msg = "This is the default warning string.";
-  title = "Warning Dialog";
-
-  if (nargin > 0)
+  nargs = numel (varargin);
+  
+  if (nargs > 3)
+    print_usage ();
+  elseif (nargs == 1)
     msg = varargin{1};
+  elseif (nargs == 2)
+    msg = varargin{1};
+    tit = varargin{2};
+  elseif (nargs == 3)
+    msg = varargin{1};
+    tit = varargin{2};
+    opt = varargin{3};
   endif
-  if (nargin > 1)
-    title = varargin{2};
-  endif
+  
+  retval = msgbox (msg, tit, "warn", opt);
 
-  if (nargin < 3)
-    retval = msgbox (msg, title, "warn");
-  else
-    retval = msgbox (msg, title, "warn", varargin{3});
+  if (nargout)
+    h = htmp;
   endif
 
 endfunction
 
-
-%!error warndlg (1, 2, 3, 4)
-%!error <MSG must be a character string> warndlg (1)
-%!error <TITLE must be a character string> warndlg ("msg", 1)
+## No BIST tests.  This function just dispatches to msgbox().
+%!assert (1)
