use BLI_strncpy and BLI_snprintf when the size of the string is known.
[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_math.h"
42 #include "BLI_listbase.h"
43 #include "BLI_rect.h"
44 #include "BLI_string.h"
45 #include "BLI_utildefines.h"
46
47 #include "BKE_global.h"
48
49
50 #include "BLF_api.h"
51 #include "BLF_translation.h"
52
53 #include "UI_interface.h"
54
55 #include "ED_datafiles.h"
56
57 #include "interface_intern.h"
58
59
60 /* style + theme + layout-engine = UI */
61
62 /* 
63  This is a complete set of layout rules, the 'state' of the Layout 
64  Engine. Multiple styles are possible, defined via C or Python. Styles 
65  get a name, and will typically get activated per region type, like 
66  "Header", or "Listview" or "Toolbar". Properties of Style definitions 
67  are:
68  
69  - default collumn properties, internal spacing, aligning, min/max width
70  - button alignment rules (for groups)
71  - label placement rules
72  - internal labeling or external labeling default
73  - default minimum widths for buttons/labels (in amount of characters)
74  - font types, styles and relative sizes for Panel titles, labels, etc.
75
76 */
77
78
79 /* ********************************************** */
80
81 static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id)
82 {
83         uiStyle *style= MEM_callocN(sizeof(uiStyle), "new style");
84         
85         BLI_addtail(styles, style);
86         BLI_strncpy(style->name, name, MAX_STYLE_NAME);
87         
88         style->panelzoom= 1.0; /* unused */
89
90         style->paneltitle.uifont_id= uifont_id;
91         style->paneltitle.points= 12;
92         style->paneltitle.kerning= 1;
93         style->paneltitle.shadow= 1;
94         style->paneltitle.shadx= 0;
95         style->paneltitle.shady= -1;
96         style->paneltitle.shadowalpha= 0.15f;
97         style->paneltitle.shadowcolor= 1.0f;
98         
99         style->grouplabel.uifont_id= uifont_id;
100         style->grouplabel.points= 12;
101         style->grouplabel.kerning= 1;
102         style->grouplabel.shadow= 3;
103         style->grouplabel.shadx= 0;
104         style->grouplabel.shady= -1;
105         style->grouplabel.shadowalpha= 0.25f;
106         
107         style->widgetlabel.uifont_id= uifont_id;
108         style->widgetlabel.points= 11;
109         style->widgetlabel.kerning= 1;
110         style->widgetlabel.shadow= 3;
111         style->widgetlabel.shadx= 0;
112         style->widgetlabel.shady= -1;
113         style->widgetlabel.shadowalpha= 0.15f;
114         style->widgetlabel.shadowcolor= 1.0f;
115         
116         style->widget.uifont_id= uifont_id;
117         style->widget.points= 11;
118         style->widget.kerning= 1;
119         style->widget.shadowalpha= 0.25f;
120
121         style->columnspace= 8;
122         style->templatespace= 5;
123         style->boxspace= 5;
124         style->buttonspacex= 8;
125         style->buttonspacey= 2;
126         style->panelspace= 8;
127         style->panelouter= 4;
128         
129         return style;
130 }
131
132 static uiFont *uifont_to_blfont(int id)
133 {
134         uiFont *font= U.uifonts.first;
135         
136         for(; font; font= font->next) {
137                 if(font->uifont_id==id) {
138                         return font;
139                 }
140         }
141         return U.uifonts.first;
142 }
143
144 /* *************** draw ************************ */
145
146
147 void uiStyleFontDrawExt(uiFontStyle *fs, rcti *rect, const char *str,
148         float *r_xofs, float *r_yofs)
149 {
150         float height;
151         int xofs=0, yofs;
152         
153         uiStyleFontSet(fs);
154
155         height= BLF_ascender(fs->uifont_id);
156         yofs= ceil( 0.5f*(rect->ymax - rect->ymin - height));
157
158         if(fs->align==UI_STYLE_TEXT_CENTER) {
159                 xofs= floor( 0.5f*(rect->xmax - rect->xmin - BLF_width(fs->uifont_id, str)));
160                 /* don't center text if it chops off the start of the text, 2 gives some margin */
161                 if(xofs < 2) {
162                         xofs= 2;
163                 }
164         }
165         else if(fs->align==UI_STYLE_TEXT_RIGHT) {
166                 xofs= rect->xmax - rect->xmin - BLF_width(fs->uifont_id, str) - 1;
167         }
168         
169         /* clip is very strict, so we give it some space */
170         BLF_clipping(fs->uifont_id, rect->xmin-1, rect->ymin-4, rect->xmax+1, rect->ymax+4);
171         BLF_enable(fs->uifont_id, BLF_CLIPPING);
172         BLF_position(fs->uifont_id, rect->xmin+xofs, rect->ymin+yofs, 0.0f);
173
174         if (fs->shadow) {
175                 BLF_enable(fs->uifont_id, BLF_SHADOW);
176                 BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha);
177                 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
178         }
179
180         if (fs->kerning == 1)
181                 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
182
183         BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
184         BLF_disable(fs->uifont_id, BLF_CLIPPING);
185         if (fs->shadow)
186                 BLF_disable(fs->uifont_id, BLF_SHADOW);
187         if (fs->kerning == 1)
188                 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
189
190         *r_xofs= xofs;
191         *r_yofs= yofs;
192 }
193
194 void uiStyleFontDraw(uiFontStyle *fs, rcti *rect, const char *str)
195 {
196         float xofs, yofs;
197         uiStyleFontDrawExt(fs, rect, str,
198                 &xofs, &yofs);
199 }
200
201 /* drawn same as above, but at 90 degree angle */
202 void uiStyleFontDrawRotated(uiFontStyle *fs, rcti *rect, const char *str)
203 {
204         float height;
205         int xofs, yofs;
206         float angle;
207         rcti txtrect;
208
209         uiStyleFontSet(fs);
210
211         height= BLF_ascender(fs->uifont_id);
212         /* becomes x-offset when rotated */
213         xofs= ceil( 0.5f*(rect->ymax - rect->ymin - height));
214
215         /* ignore UI_STYLE, always aligned to top */
216
217         /* rotate counter-clockwise for now (assumes left-to-right language)*/
218         xofs+= height;
219         yofs= BLF_width(fs->uifont_id, str) + 5;
220         angle= 90.0f;
221
222         /* translate rect to vertical */
223         txtrect.xmin= rect->xmin - (rect->ymax - rect->ymin);
224         txtrect.ymin= rect->ymin - (rect->xmax - rect->xmin);
225         txtrect.xmax= rect->xmin;
226         txtrect.ymax= rect->ymin;
227
228         /* clip is very strict, so we give it some space */
229         /* clipping is done without rotation, so make rect big enough to contain both positions */
230         BLF_clipping(fs->uifont_id, txtrect.xmin-1, txtrect.ymin-yofs-xofs-4, rect->xmax+1, rect->ymax+4);
231         BLF_enable(fs->uifont_id, BLF_CLIPPING);
232         BLF_position(fs->uifont_id, txtrect.xmin+xofs, txtrect.ymax-yofs, 0.0f);
233
234         BLF_enable(fs->uifont_id, BLF_ROTATION);
235         BLF_rotation(fs->uifont_id, angle);
236
237         if (fs->shadow) {
238                 BLF_enable(fs->uifont_id, BLF_SHADOW);
239                 BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha);
240                 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
241         }
242
243         if (fs->kerning == 1)
244                 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
245
246         BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
247         BLF_disable(fs->uifont_id, BLF_ROTATION);
248         BLF_disable(fs->uifont_id, BLF_CLIPPING);
249         if (fs->shadow)
250                 BLF_disable(fs->uifont_id, BLF_SHADOW);
251         if (fs->kerning == 1)
252                 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
253 }
254
255 /* ************** helpers ************************ */
256 /* XXX: read a style configure */
257 uiStyle* UI_GetStyle(void)
258 {
259         uiStyle *style = NULL;
260         /* offset is two struct uiStyle pointers */
261         /* style = BLI_findstring( &U.uistyles, "Unifont Style", sizeof(style)*2 ) */;
262         return (style != NULL) ? style : U.uistyles.first;
263 }
264
265 /* temporarily, does widget font */
266 int UI_GetStringWidth(const char *str)
267 {
268         uiStyle *style= UI_GetStyle();
269         uiFontStyle *fstyle= &style->widget;
270         int width;
271         
272         if (fstyle->kerning==1) /* for BLF_width */
273                 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
274         
275         uiStyleFontSet(fstyle);
276         width= BLF_width(fstyle->uifont_id, str);       
277         
278         if (fstyle->kerning==1)
279                 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
280         
281         return width;
282 }
283
284 /* temporarily, does widget font */
285 void UI_DrawString(float x, float y, const char *str)
286 {
287         uiStyle *style= UI_GetStyle();
288         
289         if (style->widget.kerning == 1)
290                 BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
291
292         uiStyleFontSet(&style->widget);
293         BLF_position(style->widget.uifont_id, x, y, 0.0f);
294         BLF_draw(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
295
296         if (style->widget.kerning == 1)
297                 BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
298 }
299
300 /* ************** init exit ************************ */
301
302 /* called on each startup.blend read */
303 /* reading without uifont will create one */
304 void uiStyleInit(void)
305 {
306         uiFont *font= U.uifonts.first;
307         uiStyle *style= U.uistyles.first;
308         
309         /* recover from uninitialized dpi */
310         if(U.dpi == 0)
311                 U.dpi= 72;
312         CLAMP(U.dpi, 48, 128);
313         
314         /* default builtin */
315         if(font==NULL) {
316                 font= MEM_callocN(sizeof(uiFont), "ui font");
317                 BLI_addtail(&U.uifonts, font);
318                 
319                 BLI_strncpy(font->filename, "default", sizeof(font->filename));
320                 font->uifont_id= UIFONT_DEFAULT;
321         }
322         
323         for(font= U.uifonts.first; font; font= font->next) {
324                 
325                 if(font->uifont_id==UIFONT_DEFAULT) {
326 #ifdef WITH_INTERNATIONAL
327                         int font_size= datatoc_bfont_ttf_size;
328                         unsigned char *font_ttf= (unsigned char*)datatoc_bfont_ttf;
329                         static int last_font_size = 0;
330
331                         /* use unicode font for translation */
332                         if(U.transopts & USER_DOTRANSLATE) {
333                                 font_ttf= BLF_get_unifont(&font_size);
334
335                                 if(!font_ttf) {
336                                         /* fall back if not found */
337                                         font_size= datatoc_bfont_ttf_size;
338                                         font_ttf= (unsigned char*)datatoc_bfont_ttf;
339                                 }
340                         }
341
342                         /* relload only if needed */
343                         if(last_font_size != font_size) {
344                                 BLF_unload("default");
345                                 last_font_size = font_size;
346                         }
347
348                         font->blf_id= BLF_load_mem("default", font_ttf, font_size);
349 #else
350                         font->blf_id= BLF_load_mem("default", (unsigned char*)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
351 #endif
352                 }               
353                 else {
354                         font->blf_id= BLF_load(font->filename);
355                         if(font->blf_id == -1)
356                                 font->blf_id= BLF_load_mem("default", (unsigned char*)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
357                 }
358
359                 if (font->blf_id == -1) {
360                         if (G.f & G_DEBUG)
361                                 printf("%s: error, no fonts available\n", __func__);
362                 }
363                 else {
364                         /* ? just for speed to initialize?
365                          * Yes, this build the glyph cache and create
366                          * the texture.
367                          */
368                         BLF_size(font->blf_id, 11, U.dpi);
369                         BLF_size(font->blf_id, 12, U.dpi);
370                         BLF_size(font->blf_id, 14, U.dpi);
371                 }
372         }
373         
374         if(style==NULL) {
375                 ui_style_new(&U.uistyles, "Default Style", UIFONT_DEFAULT );
376         }
377         
378         // XXX, this should be moved into a style, but for now best only load the monospaced font once.
379         if (blf_mono_font == -1)
380                 blf_mono_font= BLF_load_mem_unique("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
381
382         BLF_size(blf_mono_font, 12, 72);
383         
384         /* second for rendering else we get threading problems */
385         if (blf_mono_font_render == -1)
386                 blf_mono_font_render= BLF_load_mem_unique("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
387
388         BLF_size(blf_mono_font_render, 12, 72);
389 }
390
391 void uiStyleFontSet(uiFontStyle *fs)
392 {
393         uiFont *font= uifont_to_blfont(fs->uifont_id);
394         
395         BLF_size(font->blf_id, fs->points, U.dpi);
396 }
397