UI: Add back ability to select a custom interface font
[blender.git] / source / blender / editors / interface / interface_style.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation.
19  * All rights reserved.
20  * 
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_style.c
27  *  \ingroup edinterface
28  */
29
30
31 #include <limits.h>
32 #include <math.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_screen_types.h"
39 #include "DNA_userdef_types.h"
40
41 #include "BLI_listbase.h"
42 #include "BLI_rect.h"
43 #include "BLI_string.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_global.h"
47
48
49 #include "BLF_api.h"
50 #include "BLF_translation.h"
51
52 #include "UI_interface.h"
53
54 #include "ED_datafiles.h"
55
56 #include "interface_intern.h"
57
58 #ifdef WIN32
59 #  include "BLI_math_base.h" /* M_PI */
60 #endif
61
62 /* style + theme + layout-engine = UI */
63
64 /* 
65  * This is a complete set of layout rules, the 'state' of the Layout 
66  * Engine. Multiple styles are possible, defined via C or Python. Styles 
67  * get a name, and will typically get activated per region type, like 
68  * "Header", or "Listview" or "Toolbar". Properties of Style definitions 
69  * are:
70  *
71  * - default column properties, internal spacing, aligning, min/max width
72  * - button alignment rules (for groups)
73  * - label placement rules
74  * - internal labeling or external labeling default
75  * - default minimum widths for buttons/labels (in amount of characters)
76  * - font types, styles and relative sizes for Panel titles, labels, etc.
77  */
78
79
80 /* ********************************************** */
81
82 static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id)
83 {
84         uiStyle *style = MEM_callocN(sizeof(uiStyle), "new style");
85         
86         BLI_addtail(styles, style);
87         BLI_strncpy(style->name, name, MAX_STYLE_NAME);
88         
89         style->panelzoom = 1.0; /* unused */
90
91         style->paneltitle.uifont_id = uifont_id;
92         style->paneltitle.points = 12;
93         style->paneltitle.kerning = 1;
94         style->paneltitle.shadow = 1;
95         style->paneltitle.shadx = 0;
96         style->paneltitle.shady = -1;
97         style->paneltitle.shadowalpha = 0.15f;
98         style->paneltitle.shadowcolor = 1.0f;
99
100         style->grouplabel.uifont_id = uifont_id;
101         style->grouplabel.points = 12;
102         style->grouplabel.kerning = 1;
103         style->grouplabel.shadow = 3;
104         style->grouplabel.shadx = 0;
105         style->grouplabel.shady = -1;
106         style->grouplabel.shadowalpha = 0.25f;
107
108         style->widgetlabel.uifont_id = uifont_id;
109         style->widgetlabel.points = 11;
110         style->widgetlabel.kerning = 1;
111         style->widgetlabel.shadow = 3;
112         style->widgetlabel.shadx = 0;
113         style->widgetlabel.shady = -1;
114         style->widgetlabel.shadowalpha = 0.15f;
115         style->widgetlabel.shadowcolor = 1.0f;
116
117         style->widget.uifont_id = uifont_id;
118         style->widget.points = 11;
119         style->widget.kerning = 1;
120         style->widget.shadowalpha = 0.25f;
121
122         style->columnspace = 8;
123         style->templatespace = 5;
124         style->boxspace = 5;
125         style->buttonspacex = 8;
126         style->buttonspacey = 2;
127         style->panelspace = 8;
128         style->panelouter = 4;
129         
130         return style;
131 }
132
133 static uiFont *uifont_to_blfont(int id)
134 {
135         uiFont *font = U.uifonts.first;
136         
137         for (; font; font = font->next) {
138                 if (font->uifont_id == id) {
139                         return font;
140                 }
141         }
142         return U.uifonts.first;
143 }
144
145 /* *************** draw ************************ */
146
147
148 void uiStyleFontDrawExt(uiFontStyle *fs, const rcti *rect, const char *str,
149                         size_t len, float *r_xofs, float *r_yofs)
150 {
151         float height;
152         int xofs = 0, yofs;
153         
154         uiStyleFontSet(fs);
155
156         height = BLF_ascender(fs->uifont_id);
157         yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
158
159         if (fs->align == UI_STYLE_TEXT_CENTER) {
160                 xofs = floor(0.5f * (BLI_rcti_size_x(rect) - BLF_width(fs->uifont_id, str, len)));
161                 /* don't center text if it chops off the start of the text, 2 gives some margin */
162                 if (xofs < 2) {
163                         xofs = 2;
164                 }
165         }
166         else if (fs->align == UI_STYLE_TEXT_RIGHT) {
167                 xofs = BLI_rcti_size_x(rect) - BLF_width(fs->uifont_id, str, len) - 0.1f * U.widget_unit;
168         }
169         
170         /* clip is very strict, so we give it some space */
171         BLF_clipping(fs->uifont_id, rect->xmin - 2, rect->ymin - 4, rect->xmax + 1, rect->ymax + 4);
172         BLF_enable(fs->uifont_id, BLF_CLIPPING);
173         BLF_position(fs->uifont_id, rect->xmin + xofs, rect->ymin + yofs, 0.0f);
174
175         if (fs->shadow) {
176                 BLF_enable(fs->uifont_id, BLF_SHADOW);
177                 BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha);
178                 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
179         }
180
181         if (fs->kerning == 1)
182                 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
183
184         BLF_draw(fs->uifont_id, str, len);
185         BLF_disable(fs->uifont_id, BLF_CLIPPING);
186         if (fs->shadow)
187                 BLF_disable(fs->uifont_id, BLF_SHADOW);
188         if (fs->kerning == 1)
189                 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
190
191         *r_xofs = xofs;
192         *r_yofs = yofs;
193 }
194
195 void uiStyleFontDraw(uiFontStyle *fs, const rcti *rect, const char *str)
196 {
197         float xofs, yofs;
198         uiStyleFontDrawExt(fs, rect, str,
199                            BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs);
200 }
201
202 /* drawn same as above, but at 90 degree angle */
203 void uiStyleFontDrawRotated(uiFontStyle *fs, const rcti *rect, const char *str)
204 {
205         float height;
206         int xofs, yofs;
207         float angle;
208         rcti txtrect;
209
210         uiStyleFontSet(fs);
211
212         height = BLF_ascender(fs->uifont_id);
213         /* becomes x-offset when rotated */
214         xofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
215
216         /* ignore UI_STYLE, always aligned to top */
217
218         /* rotate counter-clockwise for now (assumes left-to-right language)*/
219         xofs += height;
220         yofs = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX) + 5;
221         angle = (float)M_PI / 2.0f;
222
223         /* translate rect to vertical */
224         txtrect.xmin = rect->xmin - BLI_rcti_size_y(rect);
225         txtrect.ymin = rect->ymin - BLI_rcti_size_x(rect);
226         txtrect.xmax = rect->xmin;
227         txtrect.ymax = rect->ymin;
228
229         /* clip is very strict, so we give it some space */
230         /* clipping is done without rotation, so make rect big enough to contain both positions */
231         BLF_clipping(fs->uifont_id, txtrect.xmin - 1, txtrect.ymin - yofs - xofs - 4, rect->xmax + 1, rect->ymax + 4);
232         BLF_enable(fs->uifont_id, BLF_CLIPPING);
233         BLF_position(fs->uifont_id, txtrect.xmin + xofs, txtrect.ymax - yofs, 0.0f);
234
235         BLF_enable(fs->uifont_id, BLF_ROTATION);
236         BLF_rotation(fs->uifont_id, angle);
237
238         if (fs->shadow) {
239                 BLF_enable(fs->uifont_id, BLF_SHADOW);
240                 BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha);
241                 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
242         }
243
244         if (fs->kerning == 1)
245                 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
246
247         BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
248         BLF_disable(fs->uifont_id, BLF_ROTATION);
249         BLF_disable(fs->uifont_id, BLF_CLIPPING);
250         if (fs->shadow)
251                 BLF_disable(fs->uifont_id, BLF_SHADOW);
252         if (fs->kerning == 1)
253                 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
254 }
255
256 /* ************** helpers ************************ */
257 /* XXX: read a style configure */
258 uiStyle *UI_GetStyle(void)
259 {
260         uiStyle *style = NULL;
261         /* offset is two struct uiStyle pointers */
262         /* style = BLI_findstring(&U.uistyles, "Unifont Style", sizeof(style) * 2) */;
263         return (style != NULL) ? style : U.uistyles.first;
264 }
265
266 /* for drawing, scaled with DPI setting */
267 uiStyle *UI_GetStyleDraw(void)
268 {
269         uiStyle *style = UI_GetStyle();
270         static uiStyle _style;
271         
272         _style = *style;
273         
274         _style.paneltitle.shadx = (short)(UI_DPI_FAC * _style.paneltitle.shadx);
275         _style.paneltitle.shady = (short)(UI_DPI_FAC * _style.paneltitle.shady);
276         _style.grouplabel.shadx = (short)(UI_DPI_FAC * _style.grouplabel.shadx);
277         _style.grouplabel.shady = (short)(UI_DPI_FAC * _style.grouplabel.shady);
278         _style.widgetlabel.shadx = (short)(UI_DPI_FAC * _style.widgetlabel.shadx);
279         _style.widgetlabel.shady = (short)(UI_DPI_FAC * _style.widgetlabel.shady);
280         
281         _style.columnspace = (short)(UI_DPI_FAC * _style.columnspace);
282         _style.templatespace = (short)(UI_DPI_FAC * _style.templatespace);
283         _style.boxspace = (short)(UI_DPI_FAC * _style.boxspace);
284         _style.buttonspacex = (short)(UI_DPI_FAC * _style.buttonspacex);
285         _style.buttonspacey = (short)(UI_DPI_FAC * _style.buttonspacey);
286         _style.panelspace = (short)(UI_DPI_FAC * _style.panelspace);
287         _style.panelouter = (short)(UI_DPI_FAC * _style.panelouter);
288         
289         return &_style;
290 }
291
292 /* temporarily, does widget font */
293 int UI_GetStringWidth(const char *str)
294 {
295         uiStyle *style = UI_GetStyle();
296         uiFontStyle *fstyle = &style->widget;
297         int width;
298         
299         if (fstyle->kerning == 1) /* for BLF_width */
300                 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
301         
302         uiStyleFontSet(fstyle);
303         width = BLF_width(fstyle->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
304         
305         if (fstyle->kerning == 1)
306                 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
307         
308         return width;
309 }
310
311 /* temporarily, does widget font */
312 void UI_DrawString(float x, float y, const char *str)
313 {
314         uiStyle *style = UI_GetStyle();
315         
316         if (style->widget.kerning == 1)
317                 BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
318
319         uiStyleFontSet(&style->widget);
320         BLF_position(style->widget.uifont_id, x, y, 0.0f);
321         BLF_draw(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
322
323         if (style->widget.kerning == 1)
324                 BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
325 }
326
327 /* ************** init exit ************************ */
328
329 /* called on each startup.blend read */
330 /* reading without uifont will create one */
331 void uiStyleInit(void)
332 {
333         uiFont *font = U.uifonts.first;
334         uiStyle *style = U.uistyles.first;
335         int monofont_size = datatoc_bmonofont_ttf_size;
336         unsigned char *monofont_ttf = (unsigned char *)datatoc_bmonofont_ttf;
337         
338         /* recover from uninitialized dpi */
339         if (U.dpi == 0)
340                 U.dpi = 72;
341         CLAMP(U.dpi, 48, 144);
342         
343         for (font = U.uifonts.first; font; font = font->next) {
344                 BLF_unload_id(font->blf_id);
345         }
346
347         font = U.uifonts.first;
348
349         /* default builtin */
350         if (font == NULL) {
351                 font = MEM_callocN(sizeof(uiFont), "ui font");
352                 BLI_addtail(&U.uifonts, font);
353         }
354
355         if (U.font_path_ui[0]) {
356                 BLI_strncpy(font->filename, U.font_path_ui, sizeof(font->filename));
357                 font->uifont_id = UIFONT_CUSTOM1;
358         }
359         else {
360                 BLI_strncpy(font->filename, "default", sizeof(font->filename));
361                 font->uifont_id = UIFONT_DEFAULT;
362         }
363         
364         for (font = U.uifonts.first; font; font = font->next) {
365                 
366                 if (font->uifont_id == UIFONT_DEFAULT) {
367 #ifdef WITH_INTERNATIONAL
368                         int font_size = datatoc_bfont_ttf_size;
369                         unsigned char *font_ttf = (unsigned char *)datatoc_bfont_ttf;
370                         static int last_font_size = 0;
371
372                         /* use unicode font for translation */
373                         if (U.transopts & USER_DOTRANSLATE) {
374                                 font_ttf = BLF_get_unifont(&font_size);
375
376                                 if (!font_ttf) {
377                                         /* fall back if not found */
378                                         font_size = datatoc_bfont_ttf_size;
379                                         font_ttf = (unsigned char *)datatoc_bfont_ttf;
380                                 }
381                         }
382
383                         /* relload only if needed */
384                         if (last_font_size != font_size) {
385                                 BLF_unload("default");
386                                 last_font_size = font_size;
387                         }
388
389                         font->blf_id = BLF_load_mem("default", font_ttf, font_size);
390 #else
391                         font->blf_id = BLF_load_mem("default", (unsigned char *)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
392 #endif
393                 }
394                 else {
395                         font->blf_id = BLF_load(font->filename);
396                         if (font->blf_id == -1) {
397                                 font->blf_id = BLF_load_mem("default", (unsigned char *)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
398                         }
399                         else {
400                                 BLF_default_set(font->blf_id);
401                         }
402                 }
403
404                 if (font->blf_id == -1) {
405                         if (G.debug & G_DEBUG)
406                                 printf("%s: error, no fonts available\n", __func__);
407                 }
408                 else {
409                         /* ? just for speed to initialize?
410                          * Yes, this build the glyph cache and create
411                          * the texture.
412                          */
413                         BLF_size(font->blf_id, 11 * U.pixelsize, U.dpi);
414                         BLF_size(font->blf_id, 12 * U.pixelsize, U.dpi);
415                         BLF_size(font->blf_id, 14 * U.pixelsize, U.dpi);
416                 }
417         }
418         
419         if (style == NULL) {
420                 ui_style_new(&U.uistyles, "Default Style", UIFONT_DEFAULT);
421         }
422         
423 #ifdef WITH_INTERNATIONAL
424         /* use unicode font for text editor and interactive console */
425         if (U.transopts & USER_DOTRANSLATE) {
426                 monofont_ttf = BLF_get_unifont_mono(&monofont_size);
427
428                 if (!monofont_ttf) {
429                         /* fall back if not found */
430                         monofont_size = datatoc_bmonofont_ttf_size;
431                         monofont_ttf = (unsigned char *)datatoc_bmonofont_ttf;
432                 }
433         }
434
435         /* reload */
436         BLF_unload("monospace");
437         blf_mono_font = -1;
438         blf_mono_font_render = -1;
439 #endif
440
441         /* XXX, this should be moved into a style, but for now best only load the monospaced font once. */
442         if (blf_mono_font == -1)
443                 blf_mono_font = BLF_load_mem_unique("monospace", monofont_ttf, monofont_size);
444
445         BLF_size(blf_mono_font, 12 * U.pixelsize, 72);
446         
447         /* second for rendering else we get threading problems */
448         if (blf_mono_font_render == -1)
449                 blf_mono_font_render = BLF_load_mem_unique("monospace", monofont_ttf, monofont_size);
450
451         BLF_size(blf_mono_font_render, 12 * U.pixelsize, 72);
452 }
453
454 void uiStyleFontSet(uiFontStyle *fs)
455 {
456         uiFont *font = uifont_to_blfont(fs->uifont_id);
457         
458         BLF_size(font->blf_id, fs->points * U.pixelsize, U.dpi);
459 }
460