1 /*
2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2021 Kim Woelders
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies of the Software, its documentation and marketing & publicity
14 * materials, and acknowledgment shall be given in the documentation, materials
15 * and software packages that this Software was used.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include "E.h"
25 #include "conf.h"
26 #include "emodule.h"
27 #include "iclass.h"
28 #include "list.h"
29 #include "tclass.h"
30 #include "xwin.h"
31
32 #define ENABLE_DESTROY 0 /* Broken */
33
34 static LIST_HEAD(tclass_list);
35
36 static TextClass *TextclassGetFallback(void);
37
38 static char *
TextstateFontLookup(const char * name)39 TextstateFontLookup(const char *name)
40 {
41 const char *font;
42
43 if (*name == '*')
44 {
45 font = FontLookup(name + 1);
46 if (font)
47 name = font;
48 }
49 return Estrdup(name);
50 }
51
52 static TextState *
TextstateCreate(const char * font)53 TextstateCreate(const char *font)
54 {
55 TextState *ts;
56
57 ts = ECALLOC(TextState, 1);
58 if (!ts)
59 return NULL;
60
61 ts->style.orientation = FONT_TO_RIGHT;
62
63 if (font)
64 ts->fontname = TextstateFontLookup(font);
65
66 return ts;
67 }
68
69 static void
TextstateDestroy(TextState * ts)70 TextstateDestroy(TextState * ts)
71 {
72 if (!ts)
73 return;
74
75 Efree(ts->fontname);
76 if (ts->ops)
77 ts->ops->Destroy(ts);
78
79 Efree(ts);
80 }
81
82 static TextState *
TextstateSet(TextState ** tsp,const char * name)83 TextstateSet(TextState ** tsp, const char *name)
84 {
85 TextState *ts;
86
87 ts = TextstateCreate(name);
88
89 if (*tsp)
90 TextstateDestroy(*tsp);
91 *tsp = ts;
92
93 return ts;
94 }
95
96 static TextClass *
TextclassCreate(const char * name)97 TextclassCreate(const char *name)
98 {
99 TextClass *tc;
100
101 tc = ECALLOC(TextClass, 1);
102 if (!tc)
103 return NULL;
104
105 LIST_PREPEND(TextClass, &tclass_list, tc);
106
107 tc->name = Estrdup(name);
108 tc->justification = 512;
109
110 return tc;
111 }
112
113 #if ENABLE_DESTROY
114 static void
TextclassDestroy(TextClass * tc)115 TextclassDestroy(TextClass * tc)
116 {
117 if (tc->ref_count > 0)
118 {
119 DialogOK("TextClass Error!", _("%u references remain"), tc->ref_count);
120 return;
121 }
122 Efree(tc->name);
123 TextStateDestroy(tc->norm.normal);
124 TextStateDestroy(tc->norm.hilited);
125 TextStateDestroy(tc->norm.clicked);
126 TextStateDestroy(tc->norm.disabled);
127 TextStateDestroy(tc->active.normal);
128 TextStateDestroy(tc->active.hilited);
129 TextStateDestroy(tc->active.clicked);
130 TextStateDestroy(tc->active.disabled);
131 TextStateDestroy(tc->sticky.normal);
132 TextStateDestroy(tc->sticky.hilited);
133 TextStateDestroy(tc->sticky.clicked);
134 TextStateDestroy(tc->sticky.disabled);
135 TextStateDestroy(tc->sticky_active.normal);
136 TextStateDestroy(tc->sticky_active.hilited);
137 TextStateDestroy(tc->sticky_active.clicked);
138 TextStateDestroy(tc->sticky_active.disabled);
139
140 Efree(tc);
141 }
142 #endif /* ENABLE_DESTROY */
143
144 TextClass *
TextclassAlloc(const char * name,int fallback)145 TextclassAlloc(const char *name, int fallback)
146 {
147 TextClass *tc;
148
149 if (!name || !name[0])
150 return NULL;
151
152 tc = TextclassFind(name, fallback);
153 if (tc)
154 tc->ref_count++;
155
156 return tc;
157 }
158
159 void
TextclassFree(TextClass * tc)160 TextclassFree(TextClass * tc)
161 {
162 if (tc)
163 tc->ref_count--;
164 }
165
166 int
TextclassGetJustification(TextClass * tc)167 TextclassGetJustification(TextClass * tc)
168 {
169 return tc->justification;
170 }
171
172 void
TextclassSetJustification(TextClass * tc,int just)173 TextclassSetJustification(TextClass * tc, int just)
174 {
175 tc->justification = just;
176 }
177
178 #define TSTATE_SET_STATE(which, fallback) \
179 if (!tc->which) tc->which = tc->fallback;
180
181 static void
TextclassPopulate(TextClass * tc)182 TextclassPopulate(TextClass * tc)
183 {
184 if (!tc || !tc->norm.normal)
185 return;
186
187 TSTATE_SET_STATE(norm.hilited, norm.normal);
188 TSTATE_SET_STATE(norm.clicked, norm.normal);
189 TSTATE_SET_STATE(norm.disabled, norm.normal);
190
191 TSTATE_SET_STATE(active.normal, norm.normal);
192 TSTATE_SET_STATE(active.hilited, active.normal);
193 TSTATE_SET_STATE(active.clicked, active.normal);
194 TSTATE_SET_STATE(active.disabled, active.normal);
195
196 TSTATE_SET_STATE(sticky.normal, norm.normal);
197 TSTATE_SET_STATE(sticky.hilited, sticky.normal);
198 TSTATE_SET_STATE(sticky.clicked, sticky.normal);
199 TSTATE_SET_STATE(sticky.disabled, sticky.normal);
200
201 TSTATE_SET_STATE(sticky_active.normal, norm.normal);
202 TSTATE_SET_STATE(sticky_active.hilited, sticky_active.normal);
203 TSTATE_SET_STATE(sticky_active.clicked, sticky_active.normal);
204 TSTATE_SET_STATE(sticky_active.disabled, sticky_active.normal);
205 }
206
207 static int
_TextclassMatchName(const void * data,const void * match)208 _TextclassMatchName(const void *data, const void *match)
209 {
210 return strcmp(((const TextClass *)data)->name, (const char *)match);
211 }
212
213 TextClass *
TextclassFind(const char * name,int fallback)214 TextclassFind(const char *name, int fallback)
215 {
216 TextClass *tc = NULL;
217
218 if (name)
219 tc = LIST_FIND(TextClass, &tclass_list, _TextclassMatchName, name);
220 if (tc || !fallback)
221 return tc;
222
223 #if 0
224 Eprintf("%s: Get fallback (%s)\n", __func__, name);
225 #endif
226 return TextclassGetFallback();
227 }
228
229 int
TextclassConfigLoad(FILE * fs)230 TextclassConfigLoad(FILE * fs)
231 {
232 int err = 0;
233 char s[FILEPATH_LEN_MAX];
234 char s2[FILEPATH_LEN_MAX];
235 int i1, r, g, b;
236 TextClass *tc = NULL;
237 TextState *ts = NULL;
238
239 while (GetLine(s, sizeof(s), fs))
240 {
241 i1 = ConfigParseline1(s, s2, NULL, NULL);
242
243 /* tc not needed */
244 switch (i1)
245 {
246 case CONFIG_CLOSE:
247 TextclassPopulate(tc);
248 goto done;
249 case CONFIG_CLASSNAME:
250 if (TextclassFind(s2, 0))
251 {
252 SkipTillEnd(fs);
253 goto done;
254 }
255 tc = TextclassCreate(s2);
256 continue;
257 }
258
259 /* tc needed */
260 if (!tc)
261 break;
262
263 switch (i1)
264 {
265 case TEXT_JUSTIFICATION:
266 tc->justification = atoi(s2);
267 continue;
268 case CONFIG_DESKTOP:
269 case ICLASS_NORMAL:
270 ts = TextstateSet(&tc->norm.normal, s2);
271 continue;
272 case ICLASS_CLICKED:
273 ts = TextstateSet(&tc->norm.clicked, s2);
274 continue;
275 case ICLASS_HILITED:
276 ts = TextstateSet(&tc->norm.hilited, s2);
277 continue;
278 case ICLASS_DISABLED:
279 ts = TextstateSet(&tc->norm.disabled, s2);
280 continue;
281 case ICLASS_STICKY_NORMAL:
282 ts = TextstateSet(&tc->sticky.normal, s2);
283 continue;
284 case ICLASS_STICKY_CLICKED:
285 ts = TextstateSet(&tc->sticky.clicked, s2);
286 continue;
287 case ICLASS_STICKY_HILITED:
288 ts = TextstateSet(&tc->sticky.hilited, s2);
289 continue;
290 case ICLASS_STICKY_DISABLED:
291 ts = TextstateSet(&tc->sticky.disabled, s2);
292 continue;
293 case ICLASS_ACTIVE_NORMAL:
294 ts = TextstateSet(&tc->active.normal, s2);
295 continue;
296 case ICLASS_ACTIVE_CLICKED:
297 ts = TextstateSet(&tc->active.clicked, s2);
298 continue;
299 case ICLASS_ACTIVE_HILITED:
300 ts = TextstateSet(&tc->active.hilited, s2);
301 continue;
302 case ICLASS_ACTIVE_DISABLED:
303 ts = TextstateSet(&tc->active.disabled, s2);
304 continue;
305 case ICLASS_STICKY_ACTIVE_NORMAL:
306 ts = TextstateSet(&tc->sticky_active.normal, s2);
307 continue;
308 case ICLASS_STICKY_ACTIVE_CLICKED:
309 ts = TextstateSet(&tc->sticky_active.clicked, s2);
310 continue;
311 case ICLASS_STICKY_ACTIVE_HILITED:
312 ts = TextstateSet(&tc->sticky_active.hilited, s2);
313 continue;
314 case ICLASS_STICKY_ACTIVE_DISABLED:
315 ts = TextstateSet(&tc->sticky_active.disabled, s2);
316 continue;
317 }
318
319 /* ts needed */
320 if (!ts)
321 break;
322
323 switch (i1)
324 {
325 case TEXT_ORIENTATION:
326 ts->style.orientation = atoi(s2);
327 continue;
328 case TEXT_EFFECT:
329 ts->style.effect = atoi(s2);
330 continue;
331 case TEXT_FG_COL:
332 r = g = b = 0;
333 sscanf(s, "%*s %i %i %i", &r, &g, &b);
334 COLOR32_FROM_RGB(ts->fg_col, r, g, b);
335 continue;
336 case TEXT_BG_COL:
337 r = g = b = 0;
338 sscanf(s, "%*s %i %i %i", &r, &g, &b);
339 COLOR32_FROM_RGB(ts->bg_col, r, g, b);
340 continue;
341 default:
342 ConfigParseError("TextClass", s);
343 continue;
344 }
345 }
346 err = -1;
347
348 done:
349 return err;
350 }
351
352 static TextClass *
TextclassGetFallback(void)353 TextclassGetFallback(void)
354 {
355 TextClass *tc;
356
357 tc = TextclassFind("__fb_tc", 0);
358 if (tc)
359 return tc;
360
361 /* Create fallback textclass */
362 tc = TextclassCreate("__fb_tc");
363 if (!tc)
364 return tc;
365
366 tc->norm.normal =
367 TextstateCreate("-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
368 COLOR32_FROM_RGB(tc->norm.normal->fg_col, 0, 0, 0);
369 TextclassPopulate(tc);
370
371 return tc;
372 }
373
374 /*
375 * Textclass Module
376 */
377
378 static void
TextclassIpc(const char * params)379 TextclassIpc(const char *params)
380 {
381 char param1[1024];
382 char param2[1024];
383 int l;
384 const char *p;
385 TextClass *tc;
386
387 if (!params)
388 {
389 IpcPrintf("Please specify...\n");
390 return;
391 }
392
393 p = params;
394 l = 0;
395 param1[0] = param2[0] = '\0';
396 sscanf(p, "%1000s %1000s %n", param1, param2, &l);
397 p += l;
398
399 if (!strncmp(param1, "list", 2))
400 {
401 LIST_FOR_EACH(TextClass, &tclass_list, tc) IpcPrintf("%s\n", tc->name);
402 return;
403 }
404
405 if (!param1[0])
406 {
407 IpcPrintf("TextClass not specified\n");
408 return;
409 }
410
411 tc = TextclassFind(param1, 0);
412 if (!tc)
413 {
414 IpcPrintf("TextClass not found: %s\n", param1);
415 return;
416 }
417
418 if (!strcmp(param2, "apply"))
419 {
420 EX_Window xwin;
421 Win win;
422 char state[20];
423 int x, y, st;
424
425 /* 3:xwin 4:x 5:y 6:state 7-:txt */
426 xwin = NoXID;
427 x = y = 0;
428 state[0] = '\0';
429 l = 0;
430 sscanf(p, "%x %d %d %16s %n", &xwin, &x, &y, state, &l);
431 p += l;
432
433 if (!strcmp(state, "normal"))
434 st = STATE_NORMAL;
435 else if (!strcmp(state, "hilited"))
436 st = STATE_HILITED;
437 else if (!strcmp(state, "clicked"))
438 st = STATE_CLICKED;
439 else if (!strcmp(state, "disabled"))
440 st = STATE_DISABLED;
441 else
442 st = STATE_NORMAL;
443
444 if (l == 0)
445 return;
446
447 win = ECreateWinFromXwin(xwin);
448 if (!win)
449 return;
450
451 TextDraw(tc, win, NoXID, 0, 0, st, p, x, y, 99999, 99999, 17, 0);
452 EDestroyWin(win);
453 }
454 else if (!strcmp(param2, "query_size"))
455 {
456 int w, h;
457
458 /* 3-:txt */
459
460 if (l == 0)
461 return;
462
463 w = h = 0;
464 TextSize(tc, 0, 0, STATE_NORMAL, p, &w, &h, 17);
465 IpcPrintf("%i %i\n", w, h);
466 }
467 else if (!strcmp(param2, "query"))
468 {
469 IpcPrintf("TextClass %s found\n", tc->name);
470 }
471 else if (!strcmp(param2, "ref_count"))
472 {
473 IpcPrintf("%u references remain\n", tc->ref_count);
474 }
475 else
476 {
477 IpcPrintf("Error: unknown operation specified\n");
478 }
479 }
480
481 static const IpcItem TextclassIpcArray[] = {
482 {
483 TextclassIpc,
484 "textclass", "tc",
485 "List textclasses, apply a textclass",
486 NULL}
487 ,
488 };
489
490 /*
491 * Module descriptor
492 */
493 extern const EModule ModTextclass;
494
495 const EModule ModTextclass = {
496 "textclass", "tc",
497 NULL,
498 MOD_ITEMS(TextclassIpcArray),
499 {0, NULL}
500 };
501