######################################################################## ## ## Copyright (C) 2019-2021 The Octave Project Developers ## ## See the file COPYRIGHT.md in the top-level directory of this ## distribution or . ## ## 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) 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 %!testif HAVE_FONTCONFIG %! fail ("uisetfont (1, 2, 3)", "Invalid call"); %!testif HAVE_FONTCONFIG %! fail ("uisetfont (110, struct ())", "Invalid call"); %!testif HAVE_FONTCONFIG %! fail ("uisetfont (groot ())", "unhandled object type"); %!testif HAVE_FONTCONFIG %! fail ("uisetfont (struct ())", "FONTSTRUCT .* must have fields FontName,.*"); %!testif HAVE_FONTCONFIG %! fail ("uisetfont ({'Title'})", "TITLE must be a character vector");