1 /*
2 * Copyright (c) 2008 Hypertriton, Inc. <http://hypertriton.com/>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include <agar/core/core.h>
27 #include <agar/core/config.h>
28 #include <agar/gui/font_selector.h>
29 #include <agar/gui/icons.h>
30 #include <agar/gui/primitive.h>
31
32 #include <string.h>
33
34 #include <agar/config/have_fontconfig.h>
35 #ifdef HAVE_FONTCONFIG
36 #include <fontconfig/fontconfig.h>
37 extern int agFontconfigInited; /* text.c */
38 #endif
39
40 AG_FontSelector *
AG_FontSelectorNew(void * parent,Uint flags)41 AG_FontSelectorNew(void *parent, Uint flags)
42 {
43 AG_FontSelector *fs;
44
45 fs = Malloc(sizeof(AG_FontSelector));
46 AG_ObjectInit(fs, &agFontSelectorClass);
47 fs->flags |= flags;
48 if (flags & AG_FONTSELECTOR_HFILL) { AG_ExpandHoriz(fs); }
49 if (flags & AG_FONTSELECTOR_VFILL) { AG_ExpandVert(fs); }
50
51 AG_ObjectAttach(parent, fs);
52 return (fs);
53 }
54
55 static void
Bound(AG_Event * event)56 Bound(AG_Event *event)
57 {
58 AG_FontSelector *fs = AG_SELF();
59 AG_Variable *b = AG_PTR(1);
60 AG_Font **pFont;
61
62 if (strcmp(b->name, "font") != 0)
63 return;
64
65 AG_ObjectLock(fs);
66
67 pFont = b->data.p;
68 AG_SetPointer(fs, "font", *pFont);
69 Strlcpy(fs->curFace, OBJECT(*pFont)->name, sizeof(fs->curFace));
70 fs->curSize = (*pFont)->spec.size;
71 fs->curStyle = (*pFont)->flags;
72 #if 0
73 fs->tlFaces->flags |= AG_TLIST_SCROLLTOSEL;
74 fs->tlSizes->flags |= AG_TLIST_SCROLLTOSEL;
75 #endif
76 AG_ObjectUnlock(fs);
77 }
78
79 static void
UpdateFontSelection(AG_FontSelector * fs)80 UpdateFontSelection(AG_FontSelector *fs)
81 {
82 AG_Variable *bFont;
83 AG_Font *font, **pFont;
84
85 font = AG_FetchFont(fs->curFace, fs->curSize, fs->curStyle);
86 if (font == NULL) {
87 AG_TextError(_("Error opening font: %s"), AG_GetError());
88 return;
89 }
90 bFont = AG_GetVariable(fs, "font", &pFont);
91 *pFont = font;
92 AG_UnlockVariable(bFont);
93 }
94
95 static void
UpdatePreview(AG_FontSelector * fs)96 UpdatePreview(AG_FontSelector *fs)
97 {
98 AG_Variable *bFont;
99 AG_Font **pFont;
100 AG_Surface *s;
101
102 bFont = AG_GetVariable(fs, "font", &pFont);
103 AG_PushTextState();
104
105 if (*pFont != NULL) {
106 AG_TextFont(*pFont);
107 }
108 s = AG_TextRender(_("The Quick Brown Fox Jumps Over The Lazy Dog"));
109 if (fs->sPreview == -1) {
110 fs->sPreview = AG_WidgetMapSurface(fs, s);
111 } else {
112 AG_WidgetReplaceSurface(fs, fs->sPreview, s);
113 }
114
115 AG_PopTextState();
116 AG_UnlockVariable(bFont);
117 }
118
119 static void
OnShow(AG_Event * event)120 OnShow(AG_Event *event)
121 {
122 AG_Variable *bFont;
123 AG_Font **pFont;
124 AG_FontSelector *fs = AG_SELF();
125 char fontPath[AG_SEARCHPATH_MAX], *pFontPath = &fontPath[0];
126 AG_TlistItem *ti;
127 char *s;
128 int i;
129 const int stdSizes[] = { 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
130 22,24,26,28,32,48,64 };
131 const int nStdSizes = sizeof(stdSizes) / sizeof(stdSizes[0]);
132
133 bFont = AG_GetVariable(fs, "font", &pFont);
134 AG_PushTextState();
135
136 fs->flags &= ~(AG_FONTSELECTOR_UPDATE);
137
138 for (i = 0; i < agBuiltinFontCount; i++) {
139 AG_StaticFont *font = agBuiltinFonts[i];
140
141 ti = AG_TlistAdd(fs->tlFaces, NULL, "_%s", font->name);
142 ti->p1 = font;
143 if (*pFont != NULL &&
144 strcmp(ti->text, OBJECT(*pFont)->name) == 0)
145 ti->selected++;
146 }
147
148 #ifdef HAVE_FONTCONFIG
149 if (agFontconfigInited) {
150 FcObjectSet *os;
151 FcFontSet *fset;
152 FcPattern *pat;
153
154 pat = FcPatternCreate();
155 os = FcObjectSetBuild(FC_FAMILY, (char *)0);
156 fset = FcFontList(NULL, pat, os);
157 if (fset != NULL) {
158 for (i = 0; i < fset->nfont; i++) {
159 FcPattern *font = fset->fonts[i];
160 FcChar8 *fam;
161
162 if (FcPatternGetString(font, FC_FAMILY, 0,
163 &fam) == FcResultMatch) {
164 ti = AG_TlistAddS(fs->tlFaces, NULL,
165 (char *)fam);
166 if (*pFont != NULL &&
167 strcmp((char *)fam,
168 OBJECT(*pFont)->name) == 0)
169 ti->selected++;
170 }
171 }
172 FcFontSetDestroy(fset);
173 }
174 AG_TlistSort(fs->tlFaces);
175 FcObjectSetDestroy(os);
176 FcPatternDestroy(pat);
177 } else
178 #endif /* HAVE_FONTCONFIG */
179
180 {
181 AG_GetString(AG_ConfigObject(), "font-path", fontPath, sizeof(fontPath));
182
183 while ((s = AG_Strsep(&pFontPath, AG_PATHSEPMULTI)) != NULL) {
184 AG_Dir *dir;
185 int i;
186
187 if ((dir = AG_OpenDir(s)) == NULL) {
188 AG_Verbose(_("Ignoring: %s\n"), AG_GetError());
189 continue;
190 }
191 for (i = 0; i < dir->nents; i++) {
192 char path[AG_FILENAME_MAX];
193 AG_FileInfo info;
194 char *file = dir->ents[i], *pExt;
195
196 if (file[0] == '.' ||
197 (pExt = strrchr(file, '.')) == NULL) {
198 continue;
199 }
200 if (Strcasecmp(pExt, ".ttf") != 0) /* XXX */
201 continue;
202
203 Strlcpy(path, s, sizeof(path));
204 Strlcat(path, AG_PATHSEP, sizeof(path));
205 Strlcat(path, file, sizeof(path));
206
207 if (AG_GetFileInfo(path, &info) == -1 ||
208 info.type != AG_FILE_REGULAR) {
209 continue;
210 }
211 ti = AG_TlistAddS(fs->tlFaces, NULL, file);
212 if (*pFont != NULL &&
213 strcmp(file, OBJECT(*pFont)->name) == 0)
214 ti->selected++;
215 }
216 AG_CloseDir(dir);
217 }
218 }
219
220 /* XXX */
221 for (i = 0; i < nStdSizes; i++) {
222 ti = AG_TlistAdd(fs->tlSizes, NULL, "%d", stdSizes[i]);
223 if (*pFont != NULL &&
224 stdSizes[i] == (*pFont)->spec.size)
225 ti->selected++;
226 }
227 ti = AG_TlistAdd(fs->tlStyles, NULL, _("Regular"));
228 if (*pFont != NULL && (*pFont)->flags == 0) { ti->selected++; }
229 ti = AG_TlistAdd(fs->tlStyles, NULL, _("Italic"));
230 if (*pFont != NULL && (*pFont)->flags == AG_FONT_ITALIC) { ti->selected++; }
231 ti = AG_TlistAdd(fs->tlStyles, NULL, _("Bold"));
232 if (*pFont != NULL && (*pFont)->flags == AG_FONT_BOLD) { ti->selected++; }
233 ti = AG_TlistAdd(fs->tlStyles, NULL, _("Bold Italic"));
234 if (*pFont != NULL && (*pFont)->flags == (AG_FONT_BOLD|AG_FONT_ITALIC)) { ti->selected++; }
235
236 UpdatePreview(fs);
237
238 AG_UnlockVariable(bFont);
239 }
240
241 static void
SelectedFace(AG_Event * event)242 SelectedFace(AG_Event *event)
243 {
244 AG_FontSelector *fs = AG_PTR(1);
245 AG_TlistItem *it = AG_PTR(2);
246
247 Strlcpy(fs->curFace, it->text, sizeof(fs->curFace));
248 UpdateFontSelection(fs);
249 UpdatePreview(fs);
250 }
251
252 static void
SelectedStyle(AG_Event * event)253 SelectedStyle(AG_Event *event)
254 {
255 AG_FontSelector *fs = AG_PTR(1);
256 AG_TlistItem *it = AG_PTR(2);
257 Uint flags = 0;
258
259 if (!strcmp(it->text, _("Italic")))
260 flags |= AG_FONT_ITALIC;
261 if (!strcmp(it->text, _("Bold")))
262 flags |= AG_FONT_BOLD;
263 if (!strcmp(it->text, _("Bold Italic")))
264 flags |= (AG_FONT_BOLD|AG_FONT_ITALIC);
265
266 fs->curStyle = flags;
267 UpdateFontSelection(fs);
268 UpdatePreview(fs);
269 }
270
271 static void
SelectedSize(AG_Event * event)272 SelectedSize(AG_Event *event)
273 {
274 AG_FontSelector *fs = AG_PTR(1);
275 AG_TlistItem *it = AG_PTR(2);
276
277 fs->curSize = atoi(it->text);
278 UpdateFontSelection(fs);
279 UpdatePreview(fs);
280 }
281
282 static void
Init(void * obj)283 Init(void *obj)
284 {
285 AG_FontSelector *fs = obj;
286
287 fs->flags = AG_FONTSELECTOR_UPDATE;
288
289 fs->hPane = AG_PaneNewHoriz(fs, AG_PANE_EXPAND);
290 fs->tlFaces = AG_TlistNew(fs->hPane->div[0], AG_TLIST_EXPAND);
291 fs->hPane2 = AG_PaneNewHoriz(fs->hPane->div[1], AG_PANE_EXPAND);
292 fs->tlStyles = AG_TlistNew(fs->hPane2->div[0], AG_TLIST_EXPAND);
293 fs->sizeBox = AG_BoxNewVert(fs->hPane2->div[1], AG_BOX_EXPAND);
294 fs->tlSizes = AG_TlistNew(fs->sizeBox, AG_TLIST_EXPAND);
295
296 fs->font = NULL;
297 fs->curFace[0] = '\0';
298 fs->curStyle = 0;
299 fs->curSize = 0;
300 fs->rPreview = AG_RECT(0,0,0,64);
301 fs->sPreview = -1;
302
303 AG_TlistSizeHint(fs->tlFaces, "XXXXXXXXXXXXXXX", 8);
304 AG_TlistSizeHint(fs->tlStyles, "XXXXXXXXX", 8);
305 AG_TlistSizeHint(fs->tlSizes, "100", 8);
306
307 AG_BindPointer(fs, "font", (void *)&fs->font);
308
309 AG_AddEvent(fs, "widget-shown", OnShow, NULL);
310 AG_SetEvent(fs, "bound", Bound, NULL);
311 AG_SetEvent(fs->tlFaces, "tlist-selected", SelectedFace, "%p", fs);
312 AG_SetEvent(fs->tlStyles, "tlist-selected", SelectedStyle, "%p", fs);
313 AG_SetEvent(fs->tlSizes, "tlist-selected", SelectedSize, "%p", fs);
314 }
315
316 static void
Draw(void * obj)317 Draw(void *obj)
318 {
319 AG_FontSelector *fs = obj;
320 AG_Widget *chld;
321
322 OBJECT_FOREACH_CHILD(chld, obj, ag_widget) {
323 AG_WidgetDraw(chld);
324 }
325 AG_DrawBox(fs, fs->rPreview, -1, WCOLOR(fs,0));
326 if (fs->sPreview != -1) {
327 AG_Surface *su = WSURFACE(fs,fs->sPreview);
328 AG_WidgetBlitSurface(fs, fs->sPreview,
329 fs->rPreview.x + fs->rPreview.w/2 - su->w/2,
330 fs->rPreview.y + fs->rPreview.h/2 - su->h/2);
331 }
332 }
333
334 static void
SizeRequest(void * obj,AG_SizeReq * r)335 SizeRequest(void *obj, AG_SizeReq *r)
336 {
337 AG_FontSelector *fs = obj;
338 AG_SizeReq rChld;
339
340 AG_WidgetSizeReq(fs->hPane, &rChld);
341 r->w = rChld.w;
342 r->h = rChld.h + fs->rPreview.h;
343 }
344
345 static int
SizeAllocate(void * obj,const AG_SizeAlloc * a)346 SizeAllocate(void *obj, const AG_SizeAlloc *a)
347 {
348 AG_FontSelector *fs = obj;
349 AG_SizeAlloc aChld;
350
351 /* Size horizontal pane */
352 aChld.x = 0;
353 aChld.y = 0;
354 aChld.w = a->w;
355 aChld.h = a->h - fs->rPreview.h;
356 AG_WidgetSizeAlloc(fs->hPane, &aChld);
357
358 fs->rPreview.x = 0;
359 fs->rPreview.y = a->h - fs->rPreview.h;
360 fs->rPreview.w = a->w;
361 return (0);
362 }
363
364 AG_WidgetClass agFontSelectorClass = {
365 {
366 "Agar(Widget:FontSelector)",
367 sizeof(AG_FontSelector),
368 { 0,0 },
369 Init,
370 NULL, /* free */
371 NULL, /* destroy */
372 NULL, /* load */
373 NULL, /* save */
374 NULL /* edit */
375 },
376 Draw,
377 SizeRequest,
378 SizeAllocate
379 };
380