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