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