# HG changeset patch # User Pantxo Diribarne # Date 1572391463 -3600 # Node ID 698c8b08fe8cd81a221e078428e1a7de0ea9ba3e # Parent df8942d19b7b977ef575bfb9916edf0bae0f3368 New function uisetfont (bug #57130). * base-text-renderer.h (base_text_renderer::get_system_fonts): Add new pure virtual method. * text-renderer.h (text_renderer::get_system_fonts): Ditto. * text-renderer.cc (text_renderer::get_system_fonts): New function to call rep->get_system_fonts. * ft-text-renderer.[h,cc] (ft_manager::get_system_fonts): New static method. (ft_manager::do_get_system_fonts): Use fontconfig to build a map of installed fonts. (ft_text_renderer::get_system_fonts): Call ft_manager::get_system_fonts. * graphics.cc (F__get_system_fonts__): New function. * scripts/gui/uisetfont.m: New function. * scripts/gui/private/__ok_cancel_dlg.m: Base implementation of a 2-button dialog. * scripts/gui/module.mk: Add uisetfont.m, __ok_cancel_dlg__.m to build system. * gui.txi: Add uisetfont docstring to the manual. diff -r df8942d19b7b -r 698c8b08fe8c NEWS --- a/NEWS Mon Sep 02 21:21:58 2019 +0200 +++ b/NEWS Wed Oct 30 00:24:23 2019 +0100 @@ -131,6 +131,7 @@ * `rotx` * `roty` * `rotz` +* `uisetfont` * `verLessThan` * `web` * `weboptions` diff -r df8942d19b7b -r 698c8b08fe8c doc/interpreter/gui.txi --- a/doc/interpreter/gui.txi Mon Sep 02 21:21:58 2019 +0200 +++ b/doc/interpreter/gui.txi Wed Oct 30 00:24:23 2019 +0100 @@ -86,6 +86,9 @@ @cindex dialog, displaying a warning dialog @DOCSTRING(warndlg) +@cindex dialog, displaying a font selection dialog +@DOCSTRING(uisetfont) + For creating new dialog types, there is a dialog function. @cindex dialog, displaying a modal dialog diff -r df8942d19b7b -r 698c8b08fe8c libinterp/corefcn/base-text-renderer.h --- a/libinterp/corefcn/base-text-renderer.h Mon Sep 02 21:21:58 2019 +0200 +++ b/libinterp/corefcn/base-text-renderer.h Wed Oct 30 00:24:23 2019 +0100 @@ -30,6 +30,7 @@ #include #include "dMatrix.h" +#include "oct-map.h" #include "uint8NDArray.h" #include "text-engine.h" @@ -66,6 +67,9 @@ set_font (const std::string& name, const std::string& weight, const std::string& angle, double size) = 0; + virtual octave_map + get_system_fonts (void) = 0; + virtual void set_color (const Matrix& c) = 0; virtual void diff -r df8942d19b7b -r 698c8b08fe8c libinterp/corefcn/ft-text-renderer.cc --- a/libinterp/corefcn/ft-text-renderer.cc Mon Sep 02 21:21:58 2019 +0200 +++ b/libinterp/corefcn/ft-text-renderer.cc Wed Oct 30 00:24:23 2019 +0100 @@ -165,6 +165,13 @@ : nullptr); } + static octave_map get_system_fonts (void) + { + return (instance_ok () + ? instance->do_get_system_fonts () + : octave_map ()); + } + static void font_destroyed (FT_Face face) { if (instance_ok ()) @@ -183,6 +190,88 @@ // in class text_renderer. ft_cache cache; + static octave_map do_get_system_fonts (void) + { + static octave_map font_map; + + if (font_map.isempty ()) + { + FcConfig *config = FcConfigGetCurrent(); + FcPattern *pat = FcPatternCreate (); + FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, FC_SLANT, FC_WEIGHT, + FC_CHARSET, nullptr); + FcFontSet *fs = FcFontList (config, pat, os); + + if (fs->nfont > 0) + { + // Mark fonts that have at least all printable ASCII chars + FcCharSet *minimal_charset = FcCharSetCreate (); + for (int i = 32; i < 127; i++) + FcCharSetAddChar (minimal_charset, static_cast (i)); + + string_vector fields (4); + fields(0) = "family"; + fields(1) = "angle"; + fields(2) = "weight"; + fields(3) = "suitable"; + + dim_vector dv (1, fs->nfont); + Cell families (dv); + Cell angles (dv); + Cell weights (dv); + Cell suitable (dv); + + unsigned char *family; + int val; + for (int i = 0; fs && i < fs->nfont; i++) + { + FcPattern *font = fs->fonts[i]; + if (FcPatternGetString (font, FC_FAMILY, 0, &family) + == FcResultMatch) + families(i) = std::string (reinterpret_cast (family)); + else + families(i) = "unknown"; + + if (FcPatternGetInteger (font, FC_SLANT, 0, &val) + == FcResultMatch) + angles(i) = (val == FC_SLANT_ITALIC + || val == FC_SLANT_OBLIQUE) + ? "italic" : "normal"; + else + angles(i) = "unknown"; + + if (FcPatternGetInteger (font, FC_WEIGHT, 0, &val) + == FcResultMatch) + weights(i) = (val == FC_WEIGHT_BOLD + || val == FC_WEIGHT_DEMIBOLD) + ? "bold" : "normal"; + else + weights(i) = "unknown"; + + FcCharSet *cset; + if (FcPatternGetCharSet (font, FC_CHARSET, 0, &cset) + == FcResultMatch) + suitable(i) = (FcCharSetIsSubset (minimal_charset, cset) + ? true : false); + else + suitable(i) = false; + } + + font_map = octave_map (dv, fields); + + font_map.assign ("family", families); + font_map.assign ("angle", angles); + font_map.assign ("weight", weights); + font_map.assign ("suitable", suitable); + + if (fs) + FcFontSetDestroy (fs); + } + } + + return font_map; + } + FT_Face do_get_font (const std::string& name, const std::string& weight, const std::string& angle, double size) { @@ -418,6 +507,8 @@ void set_font (const std::string& name, const std::string& weight, const std::string& angle, double size); + octave_map get_system_fonts (void); + void set_color (const Matrix& c); void set_mode (int m); @@ -546,12 +637,18 @@ }; void - ft_text_renderer::set_font (const std::string& name, const std::string& weight, + ft_text_renderer::set_font (const std::string& name, + const std::string& weight, const std::string& angle, double size) { // FIXME: take "fontunits" into account + font = ft_font (name, weight, angle, size, nullptr); + } - font = ft_font (name, weight, angle, size, nullptr); + octave_map + ft_text_renderer::get_system_fonts (void) + { + return ft_manager::get_system_fonts (); } void @@ -920,7 +1017,7 @@ mblen = 1; u32_c = 0xFFFD; } - + n -= mblen; if (m_do_strlist && mode == MODE_RENDER) diff -r df8942d19b7b -r 698c8b08fe8c libinterp/corefcn/graphics.cc --- a/libinterp/corefcn/graphics.cc Mon Sep 02 21:21:58 2019 +0200 +++ b/libinterp/corefcn/graphics.cc Wed Oct 30 00:24:23 2019 +0100 @@ -14479,3 +14479,17 @@ return ovl (go.get_toolkit ().get_pixels (go)); } + +DEFUN (__get_system_fonts__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{font_struct} =} __get_system_fonts__ () +Internal function. +@end deftypefn */) +{ + if (args.length () != 0) + print_usage (); + + octave::text_renderer txt_renderer; + + return ovl (txt_renderer.get_system_fonts ()); +} diff -r df8942d19b7b -r 698c8b08fe8c libinterp/corefcn/text-renderer.cc --- a/libinterp/corefcn/text-renderer.cc Mon Sep 02 21:21:58 2019 +0200 +++ b/libinterp/corefcn/text-renderer.cc Wed Oct 30 00:24:23 2019 +0100 @@ -93,6 +93,17 @@ rep->set_anti_aliasing (val); } + octave_map + text_renderer::get_system_fonts (void) + { + octave_map retval; + + if (ok ()) + retval = rep->get_system_fonts (); + + return retval; + } + void text_renderer::set_font (const std::string& name, const std::string& weight, const std::string& angle, double size) diff -r df8942d19b7b -r 698c8b08fe8c libinterp/corefcn/text-renderer.h --- a/libinterp/corefcn/text-renderer.h Mon Sep 02 21:21:58 2019 +0200 +++ b/libinterp/corefcn/text-renderer.h Wed Oct 30 00:24:23 2019 +0100 @@ -67,6 +67,8 @@ void set_font (const std::string& name, const std::string& weight, const std::string& angle, double size); + octave_map get_system_fonts (void); + void set_color (const Matrix& c); void text_to_pixels (const std::string& txt, diff -r df8942d19b7b -r 698c8b08fe8c scripts/gui/module.mk --- a/scripts/gui/module.mk Mon Sep 02 21:21:58 2019 +0200 +++ b/scripts/gui/module.mk Wed Oct 30 00:24:23 2019 +0100 @@ -7,6 +7,7 @@ %reldir%/private/__fltk_file_filter__.m \ %reldir%/private/__get_funcname__.m \ %reldir%/private/__is_function__.m \ + %reldir%/private/__ok_cancel_dlg__.m \ %reldir%/private/__uigetdir_fltk__.m \ %reldir%/private/__uigetfile_fltk__.m \ %reldir%/private/__uiobject_split_args__.m \ @@ -37,6 +38,7 @@ %reldir%/uipushtool.m \ %reldir%/uiputfile.m \ %reldir%/uiresume.m \ + %reldir%/uisetfont.m \ %reldir%/uitable.m \ %reldir%/uitoggletool.m \ %reldir%/uitoolbar.m \ diff -r df8942d19b7b -r 698c8b08fe8c scripts/gui/private/__ok_cancel_dlg__.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/gui/private/__ok_cancel_dlg__.m Wed Oct 30 00:24:23 2019 +0100 @@ -0,0 +1,58 @@ +## Copyright (C) 2019 Pantxo Diribarne +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {} {[@var{hf}, @var{hok}, @var{hcancel}] =} __ok_cancel_dlg__ (@var{title}) +## Undocumented internal function. +## @seealso{} +## @end deftypefn + +function [hf, hok, hcancel, hpanel] = __ok_cancel_dlg__ (ttl, varargin) + + hf = dialog ("name", ttl, varargin{:}); + setappdata (hf, "__ok_cancel_btn__", "cancel"); + + hpanel = uipanel (hf, "units", "pixels", "bordertype", "none"); + + hok = uicontrol (hf, "style", "pushbutton", "string", "Ok"); + + hcancel = uicontrol (hf, "style", "pushbutton", "string", "Cancel"); + + cb_fix_button_position (hf, [], hcancel, hok, hpanel); + set (hf, "sizechangedfcn", {@cb_fix_button_position, hcancel, hok, hpanel}); + +endfunction + +function cb_fix_button_position (hf, evt, hcancel, hok, hpanel) + persistent margin = 20; + persistent hgt = 30; + persistent wd = 70; + + units = get (hf, "units"); + unwind_protect + set (hf, "units", "pixels"); + pos = get (hf, "position"); + set (hok, "position", + [(pos(3) - 2 * margin - 2 * wd), margin, wd, hgt]); + set (hcancel, "position", + [(pos(3) - margin - wd), margin, wd, hgt]); + set (hpanel, "position", [0 60 pos(3) pos(4)-60]); + unwind_protect_cleanup + set (hf, "units", units); + end_unwind_protect +endfunction diff -r df8942d19b7b -r 698c8b08fe8c scripts/gui/uisetfont.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/gui/uisetfont.m Wed Oct 30 00:24:23 2019 +0100 @@ -0,0 +1,417 @@ +## Copyright (C) 2019 Pantxo Diribarne +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {} { } uisetfont () +## @deftypefnx {} { } uisetfont (@var{h}) +## @deftypefnx {} { } uisetfont (@var{fontstruct}) +## @deftypefnx {} { } uisetfont (@dots{}, @var{title}) +## @deftypefnx {} {@var{fontstruct} =} uisetfont (@dots{}) +## Open a font selection dialog. +## +## If the first argument is a handle to a text, axes, or uicontrol object, +## pressing the OK button will change the font properties of the object. +## +## The first argument may also be a structure with fields @code{FontName}, +## @code{FontWeight}, @code{FontAngle}, @code{FontUnits}, and @code{FontSize}, +## indicating the initially selected font. +## +## The title of the dialog window can be changed using the last argument +## @var{title}. +## +## If an output argument @var{fontstruct} is requested, the selected font +## structure is returned. Otherwise, the font information is displayed +## onscreen. +## +## @seealso{text, axes, uicontrol} +## @end deftypefn + +function varargout = uisetfont (varargin) + + persistent sysfonts = build_fontstruct (); + persistent fontfields = {"FontName", "FontWeight", "FontAngle", ... + "FontUnits", "FontSize"}; + + do_display = true; + h = []; + fontstruct = []; + ttl = "Font"; + nargin = numel (varargin); + + ## Input checking + if (nargin > 2) + print_usage (); + elseif (nargin == 0) + ## Do nothing + elseif (ishghandle (varargin{1})) + + h = varargin{1}; + typ = get (h, "type"); + if (! any (strcmp (typ, {"axes", "text", "uicontrol"}))) + error ("Octave:uisetfont:bad-object", + 'uisetfont: unhandled object type "%s"', typ); + endif + nargin--; + varargin(1) = []; + do_display = false; + + elseif (isstruct (varargin{1})) + + fontstruct = varargin{1}; + fields = fieldnames (fontstruct); + if (isempty (fields) + || ! all (cellfun (@(s) any (strcmp (s, fontfields)), fields))) + error ("Octave:uisetfont:bad-fontstruct", + "uisetfont: FONTSTRUCT structure must have fields %s", + strjoin (fontfields, ", ")); + endif + nargin--; + varargin(1) = []; + + endif + + ## Trailing TITLE argument + if (nargin == 1) + ttl = varargin{1}; + if (! (ischar (ttl) && isrow (ttl))) + error ("Octave:uisetfont:bad-title", + "uisetfont: TITLE must be a character vector"); + endif + elseif (nargin == 2) + print_usage (); + endif + + ## Populate fontstruct + persistent defstruct = []; + if (isempty (defstruct)) + factory_fields = strcat ("factorytext", tolower (fontfields)); + values = get (0, factory_fields); + defstruct = struct ([fontfields; values]{:}); + endif + + if (isempty (fontstruct)) + if (isempty (h)) + fontstruct = defstruct; + else + values = get (h, fontfields); + fontstruct = struct ([fontfields; values]{:}); + names = {sysfonts.name}; + if (! any (strcmpi (fontstruct.FontName, {sysfonts.name}))) + warning ("Octave:uisefont:unknown-font", + "uisetfont: unknown font %s", fontstruct.FontName); + fontstruct = defstruct; + endif + endif + endif + + ## Sample string + persistent str = {"Portez ce vieux whisky"; + "au juge blond qui fume"; + "0123456789"; + ['\alpha, \beta, \gamma, \delta, \epsilon, \zeta, \eta, ' ... + '\theta, \vartheta, \iota, \kappa, \lambda, \mu, \nu, ']; + ['\xi, \o, \pi, \varpi, \rho, \sigma, \varsigma, \tau, ' ... + '\upsilon, \phi, \chi, \psi, \omega']}; + + ## Run the dialog + warning ("off", "Octave:missing-glyph", "local"); + hf = run_fontdialog (sysfonts, h, fontstruct, ttl, str); + + ## Now wait for a button to be pressed or the figure to be closed + uiwait (hf); + + fontstruct = []; + if (ishghandle (hf)) + fontstruct = getappdata (hf, "__uisetfont_struct__"); + if (! isempty (h) && ! isempty (fontstruct)) + set (h, fontstruct); + endif + close (hf); + endif + + if (nargout > 0) + varargout{1} = fontstruct; + elseif (do_display && ! isempty (fontstruct)) + disp (fontstruct); + endif + +endfunction + +function fonts = build_fontstruct () + + fontfiles = __get_system_fonts__ (); + families = unique ({fontfiles.family}); + + fonts(numel (families)+1) = struct ("name", "", + "has_regular", false, + "has_bold", false, + "has_italic", false, + "has_bold_italic", false); + + fonts(1) = struct ("name", "*", + "has_regular", true, + "has_bold", true, + "has_italic", true, + "has_bold_italic", true); + + for i = 1:numel (families) + ii = i + 1; + fonts(ii).name = families{i}; + idx = strcmp ({fontfiles.family}, families{i}); + + isbold = strcmp ({fontfiles(idx).weight}, "bold"); + isitalic = strcmp ({fontfiles(idx).angle}, "italic"); + + fonts(ii).has_regular = any (! isbold & ! isitalic); + fonts(ii).has_bold = any (isbold & ! isitalic); + fonts(ii).has_italic = any (isitalic & ! isbold); + fonts(ii).has_bold_italic = any (isbold & isitalic); + endfor + +endfunction + +function hf = run_fontdialog (sysfonts, hobj, fontstruct, ttl, str) + + [hf, hok, hcancel, hp] = __ok_cancel_dlg__ (ttl, + "position", [200 200 400 400], + "windowstyle", "modal", + "resize", "on"); + + ## List controls + htmp = uipanel (hp, "title", "Font Name", + "units", "normalized", "position", [0.04 0.35 0.5 0.6]); + hnames = uicontrol (htmp, "style", "listbox", "string", {sysfonts.name}, + "units", "normalized", + "position", [0.02 0.01 0.96 .95]); + + htmp = uipanel (hp, "title", "Style", + "units", "normalized", "position", [0.56 0.35 0.25 0.6]); + hstyle = uicontrol (htmp, "style", "listbox", + "units", "normalized", + "position", [0.02 0.01 0.96 .95]); + + htmp = uipanel (hp, "title", "Size", + "units", "normalized", "position", [0.83 0.35 0.13 0.6]); + hsize = uicontrol (htmp, "style", "listbox", + "string", arrayfun (@num2str, (8:30), "uni", false), + "units", "normalized", + "position", [0.02 0.01 0.96 .95]); + + fcn = @(h) set (hstyle, "string", + getstylestring (sysfonts(get (h, "value")))); + set (hnames, "callback", fcn); + + ## Axes to display samples + htmp = uipanel (hp, "title", "Sample", + "units", "normalized", "position", [0.04 0 0.92 0.33]); + hax = axes ("parent", htmp, "visible", "off", "units", "normalized", + "position", [0 0 1 0.95], "xlim", [0 1], "ylim", [0 1]); + ht = text (hax, 0.5, 0.5, str, "horizontalalignment", "center"); + + hlists = [hnames, hstyle, hsize]; + + ## Update text and uicontrol objects according to the input fontstruct + struct_to_lists (fontstruct, sysfonts, hlists); + set (ht, fontstruct); + + ## Setup callbacks + set (hlists, "callback", {@cb_list_value_changed, hlists, ht, sysfonts}); + + set (hok, "callback", {@cb_button, hlists, "ok"}); + set (hcancel, "callback", {@cb_button, hlists, "cancel"}); + + ## Give focus to the OK button + uicontrol (hok); + +endfunction + +function str = getstylestring (fontitem) + + styles = {"Plain", "Bold", "Italic", "Bold Italic"}; + if (fontitem.has_bold_italic) + str = styles; + elseif (fontitem.has_bold && fontitem.has_italic) + str = styles(1:3); + elseif (fontitem.has_bold) + str = styles(1:2); + elseif (fontitem.has_italic) + str = styles(1:2:3); + else + str = styles{1}; + endif + +endfunction + +function fontstruct = struct_from_lists (hlists) + + name = get (hlists(1), "string"); + if (iscell (name)) + name = name{get(hlists(1), "value")}; + endif + + szstr = get (hlists(3), "string"); + sz = str2num (szstr{get(hlists(3), "value")}); + + fontstruct = struct ("FontName", name, "FontWeight", "normal", + "FontAngle", "normal", "FontUnits", "points", + "FontSize", sz); + + style = get (hlists(2), "string"); + if (iscell (style)) + style = style{get(hlists(2), "value")}; + endif + + if (strcmp (style, "Bold")) + fontstruct.FontWeight = "bold"; + elseif (strcmp (style, "Bold Italic")) + fontstruct.FontWeight = "bold"; + fontstruct.FontAngle = "italic"; + elseif (strcmp (style, "Italic")) + fontstruct.FontAngle = "italic"; + endif + +endfunction + +function struct_to_lists (fontstruct, sysfonts, hlists) + + ## Match font name + names = get (hlists(1), "string"); + idx = find (strcmpi (fontstruct.FontName, names)); + if (isempty (idx)) + idx = 1; + endif + set (hlists(1), "value", idx); + styles = getstylestring (sysfonts(idx)); + set (hlists(2), "string", styles); + + ## Match style + style = "Plain"; + if (strcmp (fontstruct.FontWeight, "bold") + && strcmp (fontstruct.FontAngle, "italic")) + style = "Bold Italic"; + elseif (strcmp (fontstruct.FontWeight, "bold")) + style = "Bold"; + elseif (strcmp (fontstruct.FontAngle, "italic")) + style = "Italic"; + endif + + idx = find (strcmpi (style, styles)); + if (isempty (idx)) + idx = 1; + endif + set (hlists(2), "value", idx); + + ## Match size + szs = (8:30); + idx = find (round (fontstruct.FontSize) == szs); + if (isempty (idx)) + idx = 1; + endif + set (hlists(3), "value", idx); + +endfunction + +function cb_button (h, evt, hlists, role) + + fontstruct = []; + if (strcmp (role, "ok")) + fontstruct = struct_from_lists (hlists); + endif + + setappdata (gcbf (), "__uisetfont_struct__", fontstruct); + uiresume (gcbf ()); + +endfunction + +function cb_list_value_changed (h, evt, hlists, htext, sysfonts) + + keyboard; + if (h == hlists(1)) + set (hlists(2), "string", getstylestring (sysfonts(get (h, "value"))), + "value", 1); + endif + fontstruct = struct_from_lists (hlists); + set (htext, fontstruct); + +endfunction + + +## Test input validation +%!test +%! [msg, id] = lasterr (); +%! unwind_protect +%! lasterr ("", ""); +%! try +%! uisetfont (1, 2, 3); +%! catch +%! end_try_catch +%! [~, id] = lasterr (); +%! assert (id, "Octave:invalid-fun-call"); +%! unwind_protect_cleanup +%! lasterr (msg, id); +%! end_unwind_protect + +%!test +%! [msg, id] = lasterr (); +%! unwind_protect +%! lasterr ("", ""); +%! try +%! uisetfont (110, struct ()); +%! catch +%! end_try_catch +%! [~, id] = lasterr (); +%! assert (id, "Octave:invalid-fun-call"); +%! unwind_protect_cleanup +%! lasterr (msg, id); +%! end_unwind_protect + +%!test +%! [msg, id] = lasterr (); +%! unwind_protect +%! lasterr ("", ""); +%! hf = figure ("visible", "off"); +%! try +%! uisetfont (hf); +%! catch +%! end_try_catch +%! [~, id] = lasterr (); +%! assert (id, "Octave:uisetfont:bad-object"); +%! unwind_protect_cleanup +%! lasterr (msg, id); +%! close (hf); +%! end_unwind_protect + +%!test +%! [msg, id] = lasterr (); +%! unwind_protect +%! lasterr ("", ""); +%! hf = figure ("visible", "off"); +%! hax = axes (); +%! try +%! uisetfont (hax, 1); +%! catch +%! end_try_catch +%! [~, id] = lasterr (); +%! assert (id, "Octave:uisetfont:bad-title"); +%! unwind_protect_cleanup +%! lasterr (msg, id); +%! close (hf); +%! end_unwind_protect + +%!error +%! uisetfont (struct ());