1######################################################################## 2## 3## Copyright (C) 2019-2021 The Octave Project Developers 4## 5## See the file COPYRIGHT.md in the top-level directory of this 6## distribution or <https://octave.org/copyright/>. 7## 8## This file is part of Octave. 9## 10## Octave is free software: you can redistribute it and/or modify it 11## under the terms of the GNU General Public License as published by 12## the Free Software Foundation, either version 3 of the License, or 13## (at your option) any later version. 14## 15## Octave is distributed in the hope that it will be useful, but 16## WITHOUT ANY WARRANTY; without even the implied warranty of 17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18## GNU General Public License for more details. 19## 20## You should have received a copy of the GNU General Public License 21## along with Octave; see the file COPYING. If not, see 22## <https://www.gnu.org/licenses/>. 23## 24######################################################################## 25 26## -*- texinfo -*- 27## @deftypefn {} { } uisetfont () 28## @deftypefnx {} { } uisetfont (@var{h}) 29## @deftypefnx {} { } uisetfont (@var{fontstruct}) 30## @deftypefnx {} { } uisetfont (@dots{}, @var{title}) 31## @deftypefnx {} {@var{fontstruct} =} uisetfont (@dots{}) 32## Open a font selection dialog. 33## 34## If the first argument is a handle to a text, axes, or uicontrol object, 35## pressing the OK button will change the font properties of the object. 36## 37## The first argument may also be a structure with fields @code{FontName}, 38## @code{FontWeight}, @code{FontAngle}, @code{FontUnits}, and @code{FontSize}, 39## indicating the initially selected font. 40## 41## The title of the dialog window can be changed using the last argument 42## @var{title}. 43## 44## If an output argument @var{fontstruct} is requested, the selected font 45## structure is returned. Otherwise, the font information is displayed 46## onscreen. 47## 48## @seealso{text, axes, uicontrol} 49## @end deftypefn 50 51function varargout = uisetfont (varargin) 52 53 persistent sysfonts = build_fontstruct (); 54 persistent fontfields = {"FontName", "FontWeight", "FontAngle", ... 55 "FontUnits", "FontSize"}; 56 57 do_display = true; 58 h = []; 59 fontstruct = []; 60 ttl = "Font"; 61 nargin = numel (varargin); 62 63 ## Input checking 64 if (nargin > 2) 65 print_usage (); 66 elseif (nargin == 0) 67 ## Do nothing 68 elseif (ishghandle (varargin{1})) 69 70 h = varargin{1}; 71 typ = get (h, "type"); 72 if (! any (strcmp (typ, {"axes", "text", "uicontrol"}))) 73 error ("Octave:uisetfont:bad-object", 74 'uisetfont: unhandled object type "%s"', typ); 75 endif 76 nargin--; 77 varargin(1) = []; 78 do_display = false; 79 80 elseif (isstruct (varargin{1})) 81 82 fontstruct = varargin{1}; 83 fields = fieldnames (fontstruct); 84 if (isempty (fields) 85 || ! all (cellfun (@(s) any (strcmp (s, fontfields)), fields))) 86 error ("Octave:uisetfont:bad-fontstruct", 87 "uisetfont: FONTSTRUCT structure must have fields %s", 88 strjoin (fontfields, ", ")); 89 endif 90 nargin--; 91 varargin(1) = []; 92 93 endif 94 95 ## Trailing TITLE argument 96 if (nargin == 1) 97 ttl = varargin{1}; 98 if (! (ischar (ttl) && isrow (ttl))) 99 error ("Octave:uisetfont:bad-title", 100 "uisetfont: TITLE must be a character vector"); 101 endif 102 elseif (nargin == 2) 103 print_usage (); 104 endif 105 106 ## Populate fontstruct 107 persistent defstruct = []; 108 if (isempty (defstruct)) 109 factory_fields = strcat ("factorytext", tolower (fontfields)); 110 values = get (0, factory_fields); 111 defstruct = struct ([fontfields; values]{:}); 112 endif 113 114 if (isempty (fontstruct)) 115 if (isempty (h)) 116 fontstruct = defstruct; 117 else 118 values = get (h, fontfields); 119 fontstruct = struct ([fontfields; values]{:}); 120 names = {sysfonts.name}; 121 if (! any (strcmpi (fontstruct.FontName, {sysfonts.name}))) 122 warning ("Octave:uisefont:unknown-font", 123 "uisetfont: unknown font %s", fontstruct.FontName); 124 fontstruct = defstruct; 125 endif 126 endif 127 endif 128 129 ## Sample string 130 persistent str = {"Portez ce vieux whisky"; 131 "au juge blond qui fume"; 132 "0123456789"; 133 ['\alpha, \beta, \gamma, \delta, \epsilon, \zeta, \eta, ' ... 134 '\theta, \vartheta, \iota, \kappa, \lambda, \mu, \nu, ']; 135 ['\xi, \o, \pi, \varpi, \rho, \sigma, \varsigma, \tau, ' ... 136 '\upsilon, \phi, \chi, \psi, \omega']}; 137 138 ## Run the dialog 139 warning ("off", "Octave:missing-glyph", "local"); 140 hf = run_fontdialog (sysfonts, h, fontstruct, ttl, str); 141 142 ## Now wait for a button to be pressed or the figure to be closed 143 uiwait (hf); 144 145 fontstruct = []; 146 if (ishghandle (hf)) 147 fontstruct = getappdata (hf, "__uisetfont_struct__"); 148 if (! isempty (h) && ! isempty (fontstruct)) 149 set (h, fontstruct); 150 endif 151 close (hf); 152 endif 153 154 if (nargout > 0) 155 varargout{1} = fontstruct; 156 elseif (do_display && ! isempty (fontstruct)) 157 disp (fontstruct); 158 endif 159 160endfunction 161 162function fonts = build_fontstruct () 163 164 fontfiles = __get_system_fonts__ (); 165 families = unique ({fontfiles.family}); 166 167 fonts(numel (families)+1) = struct ("name", "", 168 "has_regular", false, 169 "has_bold", false, 170 "has_italic", false, 171 "has_bold_italic", false); 172 173 fonts(1) = struct ("name", "*", 174 "has_regular", true, 175 "has_bold", true, 176 "has_italic", true, 177 "has_bold_italic", true); 178 179 for i = 1:numel (families) 180 ii = i + 1; 181 fonts(ii).name = families{i}; 182 idx = strcmp ({fontfiles.family}, families{i}); 183 184 isbold = strcmp ({fontfiles(idx).weight}, "bold"); 185 isitalic = strcmp ({fontfiles(idx).angle}, "italic"); 186 187 fonts(ii).has_regular = any (! isbold & ! isitalic); 188 fonts(ii).has_bold = any (isbold & ! isitalic); 189 fonts(ii).has_italic = any (isitalic & ! isbold); 190 fonts(ii).has_bold_italic = any (isbold & isitalic); 191 endfor 192 193endfunction 194 195function hf = run_fontdialog (sysfonts, hobj, fontstruct, ttl, str) 196 197 [hf, hok, hcancel, hp] = __ok_cancel_dlg__ (ttl, 198 "position", [200 200 400 400], 199 "windowstyle", "modal", 200 "resize", "on"); 201 202 ## List controls 203 htmp = uipanel (hp, "title", "Font Name", 204 "units", "normalized", "position", [0.04 0.35 0.5 0.6]); 205 hnames = uicontrol (htmp, "style", "listbox", "string", {sysfonts.name}, 206 "units", "normalized", 207 "position", [0.02 0.01 0.96 .95]); 208 209 htmp = uipanel (hp, "title", "Style", 210 "units", "normalized", "position", [0.56 0.35 0.25 0.6]); 211 hstyle = uicontrol (htmp, "style", "listbox", 212 "units", "normalized", 213 "position", [0.02 0.01 0.96 .95]); 214 215 htmp = uipanel (hp, "title", "Size", 216 "units", "normalized", "position", [0.83 0.35 0.13 0.6]); 217 hsize = uicontrol (htmp, "style", "listbox", 218 "string", arrayfun (@num2str, (8:30), "uni", false), 219 "units", "normalized", 220 "position", [0.02 0.01 0.96 .95]); 221 222 fcn = @(h) set (hstyle, "string", 223 getstylestring (sysfonts(get (h, "value")))); 224 set (hnames, "callback", fcn); 225 226 ## Axes to display samples 227 htmp = uipanel (hp, "title", "Sample", 228 "units", "normalized", "position", [0.04 0 0.92 0.33]); 229 hax = axes ("parent", htmp, "visible", "off", "units", "normalized", 230 "position", [0 0 1 0.95], "xlim", [0 1], "ylim", [0 1]); 231 ht = text (hax, 0.5, 0.5, str, "horizontalalignment", "center"); 232 233 hlists = [hnames, hstyle, hsize]; 234 235 ## Update text and uicontrol objects according to the input fontstruct 236 struct_to_lists (fontstruct, sysfonts, hlists); 237 set (ht, fontstruct); 238 239 ## Setup callbacks 240 set (hlists, "callback", {@cb_list_value_changed, hlists, ht, sysfonts}); 241 242 set (hok, "callback", {@cb_button, hlists, "ok"}); 243 set (hcancel, "callback", {@cb_button, hlists, "cancel"}); 244 245 ## Give focus to the OK button 246 uicontrol (hok); 247 248endfunction 249 250function str = getstylestring (fontitem) 251 252 styles = {"Plain", "Bold", "Italic", "Bold Italic"}; 253 if (fontitem.has_bold_italic) 254 str = styles; 255 elseif (fontitem.has_bold && fontitem.has_italic) 256 str = styles(1:3); 257 elseif (fontitem.has_bold) 258 str = styles(1:2); 259 elseif (fontitem.has_italic) 260 str = styles(1:2:3); 261 else 262 str = styles{1}; 263 endif 264 265endfunction 266 267function fontstruct = struct_from_lists (hlists) 268 269 name = get (hlists(1), "string"); 270 if (iscell (name)) 271 name = name{get(hlists(1), "value")}; 272 endif 273 274 szstr = get (hlists(3), "string"); 275 sz = str2num (szstr{get(hlists(3), "value")}); 276 277 fontstruct = struct ("FontName", name, "FontWeight", "normal", 278 "FontAngle", "normal", "FontUnits", "points", 279 "FontSize", sz); 280 281 style = get (hlists(2), "string"); 282 if (iscell (style)) 283 style = style{get(hlists(2), "value")}; 284 endif 285 286 if (strcmp (style, "Bold")) 287 fontstruct.FontWeight = "bold"; 288 elseif (strcmp (style, "Bold Italic")) 289 fontstruct.FontWeight = "bold"; 290 fontstruct.FontAngle = "italic"; 291 elseif (strcmp (style, "Italic")) 292 fontstruct.FontAngle = "italic"; 293 endif 294 295endfunction 296 297function struct_to_lists (fontstruct, sysfonts, hlists) 298 299 ## Match font name 300 names = get (hlists(1), "string"); 301 idx = find (strcmpi (fontstruct.FontName, names)); 302 if (isempty (idx)) 303 idx = 1; 304 endif 305 set (hlists(1), "value", idx); 306 styles = getstylestring (sysfonts(idx)); 307 set (hlists(2), "string", styles); 308 309 ## Match style 310 style = "Plain"; 311 if (strcmp (fontstruct.FontWeight, "bold") 312 && strcmp (fontstruct.FontAngle, "italic")) 313 style = "Bold Italic"; 314 elseif (strcmp (fontstruct.FontWeight, "bold")) 315 style = "Bold"; 316 elseif (strcmp (fontstruct.FontAngle, "italic")) 317 style = "Italic"; 318 endif 319 320 idx = find (strcmpi (style, styles)); 321 if (isempty (idx)) 322 idx = 1; 323 endif 324 set (hlists(2), "value", idx); 325 326 ## Match size 327 szs = (8:30); 328 idx = find (round (fontstruct.FontSize) == szs); 329 if (isempty (idx)) 330 idx = 1; 331 endif 332 set (hlists(3), "value", idx); 333 334endfunction 335 336function cb_button (h, evt, hlists, role) 337 338 fontstruct = []; 339 if (strcmp (role, "ok")) 340 fontstruct = struct_from_lists (hlists); 341 endif 342 343 setappdata (gcbf (), "__uisetfont_struct__", fontstruct); 344 uiresume (gcbf ()); 345 346endfunction 347 348function cb_list_value_changed (h, evt, hlists, htext, sysfonts) 349 350 if (h == hlists(1)) 351 set (hlists(2), "string", getstylestring (sysfonts(get (h, "value"))), 352 "value", 1); 353 endif 354 fontstruct = struct_from_lists (hlists); 355 set (htext, fontstruct); 356 357endfunction 358 359 360## Test input validation 361%!testif HAVE_FONTCONFIG 362%! fail ("uisetfont (1, 2, 3)", "Invalid call"); 363%!testif HAVE_FONTCONFIG 364%! fail ("uisetfont (110, struct ())", "Invalid call"); 365%!testif HAVE_FONTCONFIG 366%! fail ("uisetfont (groot ())", "unhandled object type"); 367%!testif HAVE_FONTCONFIG 368%! fail ("uisetfont (struct ())", "FONTSTRUCT .* must have fields FontName,.*"); 369%!testif HAVE_FONTCONFIG 370%! fail ("uisetfont ({'Title'})", "TITLE must be a character vector"); 371