1 /*
2  *  STFL - The Structured Terminal Forms Language/Library
3  *  Copyright (C) 2006, 2007  Clifford Wolf <clifford@clifford.at>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 3 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  *  MA 02110-1301 USA
19  *
20  *  base.c: Core functions
21  */
22 
23 #include "stfl_internals.h"
24 #include "stfl_compat.h"
25 
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <assert.h>
30 #include <wchar.h>
31 
32 struct stfl_widget_type *stfl_widget_types[] = {
33 	&stfl_widget_type_label,
34 	&stfl_widget_type_input,
35 	&stfl_widget_type_vbox,
36 	&stfl_widget_type_hbox,
37 	&stfl_widget_type_table,
38 	&stfl_widget_type_tablebr,
39 	&stfl_widget_type_list,
40 	&stfl_widget_type_listitem,
41 	&stfl_widget_type_textview,
42 	&stfl_widget_type_textedit,
43 	&stfl_widget_type_checkbox,
44 	0
45 };
46 
47 int id_counter = 0;
48 int curses_active = 0;
49 
stfl_widget_new(const wchar_t * type)50 struct stfl_widget *stfl_widget_new(const wchar_t *type)
51 {
52 	struct stfl_widget_type *t;
53 	int setfocus = 0;
54 	int i;
55 
56 	while (*type == '!') {
57 		setfocus = 1;
58 		type++;
59 	}
60 
61 	for (i=0; (t = stfl_widget_types[i]) != 0; i++)
62 		if (!wcscmp(t->name, type))
63 			break;
64 
65 	if (!t)
66 		return 0;
67 
68 	struct stfl_widget *w = calloc(1, sizeof(struct stfl_widget));
69 	w->id = ++id_counter;
70 	w->type = t;
71 	w->setfocus = setfocus;
72 	if (w->type->f_init)
73 		w->type->f_init(w);
74 	return w;
75 }
76 
stfl_widget_free(struct stfl_widget * w)77 void stfl_widget_free(struct stfl_widget *w)
78 {
79 	while (w->first_child)
80 		stfl_widget_free(w->first_child);
81 
82 	if (w->type->f_done)
83 		w->type->f_done(w);
84 
85 	struct stfl_kv *kv = w->kv_list;
86 	while (kv) {
87 		struct stfl_kv *next = kv->next;
88 		free(kv->key);
89 		free(kv->value);
90 		if (kv->name)
91 			free(kv->name);
92 		free(kv);
93 		kv = next;
94 	}
95 
96 	if (w->parent)
97 	{
98 		struct stfl_widget **pp = &w->parent->first_child;
99 		while (*pp != w) {
100 			pp = &(*pp)->next_sibling;
101 		}
102 		*pp = w->next_sibling;
103 
104 		if (w->parent->last_child == w) {
105 			struct stfl_widget *p = w->parent->first_child;
106 			w->parent->last_child = 0;
107 			while (p) {
108 				w->parent->last_child = p;
109 				p = p->next_sibling;
110 			}
111 		}
112 	}
113 
114 	if (w->name)
115 		free(w->name);
116 
117 	if (w->cls)
118 		free(w->cls);
119 
120 	free(w);
121 }
122 
stfl_widget_setkv_int(struct stfl_widget * w,const wchar_t * key,int value)123 extern struct stfl_kv *stfl_widget_setkv_int(struct stfl_widget *w, const wchar_t *key, int value)
124 {
125 	wchar_t newtext[64];
126 	swprintf(newtext, 64, L"%d", value);
127 	return stfl_widget_setkv_str(w, key, newtext);
128 }
129 
stfl_widget_setkv_str(struct stfl_widget * w,const wchar_t * key,const wchar_t * value)130 struct stfl_kv *stfl_widget_setkv_str(struct stfl_widget *w, const wchar_t *key, const wchar_t *value)
131 {
132 	struct stfl_kv *kv = w->kv_list;
133 	while (kv) {
134 		if (!wcscmp(kv->key, key)) {
135 			free(kv->value);
136 			kv->value = compat_wcsdup(value);
137 			return kv;
138 		}
139 		kv = kv->next;
140 	}
141 
142 	kv = calloc(1, sizeof(struct stfl_kv));
143 	kv->widget = w;
144 	kv->key = compat_wcsdup(key);
145 	kv->value = compat_wcsdup(value);
146 	kv->id = ++id_counter;
147 	kv->next = w->kv_list;
148 	w->kv_list = kv;
149 	return kv;
150 }
151 
stfl_setkv_by_name_int(struct stfl_widget * w,const wchar_t * name,int value)152 extern struct stfl_kv *stfl_setkv_by_name_int(struct stfl_widget *w, const wchar_t *name, int value)
153 {
154 	wchar_t newtext[64];
155 	swprintf(newtext, 64, L"%d", value);
156 	return stfl_setkv_by_name_str(w, name, newtext);
157 }
158 
stfl_setkv_by_name_str(struct stfl_widget * w,const wchar_t * name,const wchar_t * value)159 extern struct stfl_kv *stfl_setkv_by_name_str(struct stfl_widget *w, const wchar_t *name, const wchar_t *value)
160 {
161 	struct stfl_kv *kv = stfl_kv_by_name(w, name);
162 
163 	if (!kv)
164 		return 0;
165 
166 	free(kv->value);
167 	kv->value = compat_wcsdup(value);
168 	return kv;
169 }
170 
stfl_widget_getkv_worker(struct stfl_widget * w,const wchar_t * key)171 static struct stfl_kv *stfl_widget_getkv_worker(struct stfl_widget *w, const wchar_t *key)
172 {
173 	struct stfl_kv *kv = w->kv_list;
174 	while (kv) {
175 		if (!wcscmp(kv->key, key))
176 			return kv;
177 		kv = kv->next;
178 	}
179 	return 0;
180 }
181 
stfl_widget_getkv(struct stfl_widget * w,const wchar_t * key)182 struct stfl_kv *stfl_widget_getkv(struct stfl_widget *w, const wchar_t *key)
183 {
184 	struct stfl_kv *kv = stfl_widget_getkv_worker(w, key);
185 	if (kv) return kv;
186 
187 	int key1_len = wcslen(key) + 2;
188 	wchar_t key1[key1_len];
189 
190 	int key2_len = key1_len + wcslen(w->type->name) + 1;
191 	wchar_t key2[key2_len];
192 
193 	int key3_len = w->cls ? key1_len + wcslen(w->cls) + 1 : 0;
194 	wchar_t key3[key3_len];
195 
196 	swprintf(key1, key1_len, L"@%ls", key);
197 	swprintf(key2, key2_len, L"@%ls#%ls", w->type->name, key);
198 
199 	if (key3_len)
200 		swprintf(key3, key3_len, L"@%ls#%ls", w->cls, key);
201 
202 	while (w)
203 	{
204 		if (key3_len) {
205 			kv = stfl_widget_getkv_worker(w, key3);
206 			if (kv) return kv;
207 		}
208 
209 		kv = stfl_widget_getkv_worker(w, key2);
210 		if (kv) return kv;
211 
212 		kv = stfl_widget_getkv_worker(w, key1);
213 		if (kv) return kv;
214 
215 		w = w->parent;
216 	}
217 
218 	return 0;
219 }
220 
stfl_widget_getkv_int(struct stfl_widget * w,const wchar_t * key,int defval)221 int stfl_widget_getkv_int(struct stfl_widget *w, const wchar_t *key, int defval)
222 {
223 	struct stfl_kv *kv = stfl_widget_getkv(w, key);
224 	int ret;
225 
226 	if (!kv || !kv->value[0])
227 		return defval;
228 
229 	if (swscanf(kv->value,L"%d",&ret) < 1)
230 		return defval;
231 
232 	return ret;
233 }
234 
stfl_widget_getkv_str(struct stfl_widget * w,const wchar_t * key,const wchar_t * defval)235 const wchar_t *stfl_widget_getkv_str(struct stfl_widget *w, const wchar_t *key, const wchar_t *defval)
236 {
237 	struct stfl_kv *kv = stfl_widget_getkv(w, key);
238 	return kv ? kv->value : defval;
239 }
240 
stfl_getkv_by_name_int(struct stfl_widget * w,const wchar_t * name,int defval)241 int stfl_getkv_by_name_int(struct stfl_widget *w, const wchar_t *name, int defval)
242 {
243 	struct stfl_kv *kv = stfl_kv_by_name(w, name);
244 	int ret;
245 
246 	if (!kv || !kv->value[0])
247 		return defval;
248 
249 	if (swscanf(kv->value,L"%d",&ret) < 1)
250 		return defval;
251 
252 	return ret;
253 }
254 
stfl_getkv_by_name_str(struct stfl_widget * w,const wchar_t * name,const wchar_t * defval)255 const wchar_t *stfl_getkv_by_name_str(struct stfl_widget *w, const wchar_t *name, const wchar_t *defval)
256 {
257 	struct stfl_kv *kv = stfl_kv_by_name(w, name);
258 	return kv ? kv->value : defval;
259 }
260 
stfl_widget_by_name(struct stfl_widget * w,const wchar_t * name)261 struct stfl_widget *stfl_widget_by_name(struct stfl_widget *w, const wchar_t *name)
262 {
263 	if (w->name && !wcscmp(w->name, name))
264 		return w;
265 
266 	w = w->first_child;
267 	while (w) {
268 		struct stfl_widget *r = stfl_widget_by_name(w, name);
269 		if (r) return r;
270 		w = w->next_sibling;
271 	}
272 
273 	return 0;
274 }
275 
stfl_widget_by_id(struct stfl_widget * w,int id)276 struct stfl_widget *stfl_widget_by_id(struct stfl_widget *w, int id)
277 {
278 	if (w->id == id)
279 		return w;
280 
281 	w = w->first_child;
282 	while (w) {
283 		struct stfl_widget *r = stfl_widget_by_id(w, id);
284 		if (r) return r;
285 		w = w->next_sibling;
286 	}
287 
288 	return 0;
289 }
290 
stfl_kv_by_name(struct stfl_widget * w,const wchar_t * name)291 struct stfl_kv *stfl_kv_by_name(struct stfl_widget *w, const wchar_t *name)
292 {
293 	struct stfl_kv *kv = w->kv_list;
294 	while (kv) {
295 		if (kv->name && !wcscmp(kv->name, name))
296 			return kv;
297 		kv = kv->next;
298 	}
299 
300 	w = w->first_child;
301 	while (w) {
302 		struct stfl_kv *r = stfl_kv_by_name(w, name);
303 		if (r) return r;
304 		w = w->next_sibling;
305 	}
306 
307 	return 0;
308 }
309 
stfl_kv_by_id(struct stfl_widget * w,int id)310 struct stfl_kv *stfl_kv_by_id(struct stfl_widget *w, int id)
311 {
312 	struct stfl_kv *kv = w->kv_list;
313 	while (kv) {
314 		if (kv->id == id)
315 			return kv;
316 		kv = kv->next;
317 	}
318 
319 	w = w->first_child;
320 	while (w) {
321 		struct stfl_kv *r = stfl_kv_by_id(w, id);
322 		if (r) return r;
323 		w = w->next_sibling;
324 	}
325 
326 	return 0;
327 }
328 
stfl_find_child_tree(struct stfl_widget * w,struct stfl_widget * c)329 struct stfl_widget *stfl_find_child_tree(struct stfl_widget *w, struct stfl_widget *c)
330 {
331 	while (c) {
332 		if (c->parent == w)
333 			return c;
334 		c = c->parent;
335 	}
336 	return 0;
337 }
338 
stfl_find_first_focusable(struct stfl_widget * w)339 extern struct stfl_widget *stfl_find_first_focusable(struct stfl_widget *w)
340 {
341 	if (w->allow_focus && stfl_widget_getkv_int(w, L"can_focus", 1) &&
342 	    stfl_widget_getkv_int(w, L".display", 1))
343 		return w;
344 
345 	struct stfl_widget *c = w->first_child;
346 	while (c) {
347 		if (stfl_widget_getkv_int(w, L".display", 1)) {
348 			struct stfl_widget *r = stfl_find_first_focusable(c);
349 			if (r)
350 				return r;
351 		}
352 		c = c->next_sibling;
353 	}
354 
355 	return 0;
356 }
357 
stfl_focus_prev(struct stfl_widget * w,struct stfl_widget * old_fw,struct stfl_form * f)358 int stfl_focus_prev(struct stfl_widget *w, struct stfl_widget *old_fw, struct stfl_form *f)
359 {
360 	struct stfl_widget *stop = stfl_find_child_tree(w, old_fw);
361 
362 	assert(stop);
363 
364 	while (w->first_child != stop)
365 	{
366 		struct stfl_widget *c = w->first_child;
367 		while (c->next_sibling != stop)
368 			c = c->next_sibling;
369 
370 		struct stfl_widget *new_fw = stfl_find_first_focusable(c);
371 		if (new_fw) {
372 			if (old_fw->type->f_leave)
373 				old_fw->type->f_leave(old_fw, f);
374 
375 			if (new_fw->type->f_enter)
376 				new_fw->type->f_enter(new_fw, f);
377 
378 			f->current_focus_id = new_fw->id;
379 			return 1;
380 		}
381 
382 		stop = c;
383 	}
384 
385 	return 0;
386 }
387 
stfl_focus_next(struct stfl_widget * w,struct stfl_widget * old_fw,struct stfl_form * f)388 int stfl_focus_next(struct stfl_widget *w, struct stfl_widget *old_fw, struct stfl_form *f)
389 {
390 	struct stfl_widget *c = stfl_find_child_tree(w, old_fw);
391 
392 	assert(c);
393 	c = c->next_sibling;
394 
395 	while (c) {
396 		struct stfl_widget *new_fw = stfl_find_first_focusable(c);
397 		if (new_fw) {
398 			if (old_fw->type->f_leave)
399 				old_fw->type->f_leave(old_fw, f);
400 
401 			if (new_fw->type->f_enter)
402 				new_fw->type->f_enter(new_fw, f);
403 
404 			f->current_focus_id = new_fw->id;
405 			return 1;
406 		}
407 		c = c->next_sibling;
408 	}
409 
410 	return 0;
411 }
412 
stfl_switch_focus(struct stfl_widget * old_fw,struct stfl_widget * new_fw,struct stfl_form * f)413 int stfl_switch_focus(struct stfl_widget *old_fw, struct stfl_widget *new_fw, struct stfl_form *f)
414 {
415 	if (!new_fw || !new_fw->allow_focus)
416 		return 0;
417 
418 	if (!old_fw && f->current_focus_id)
419 		old_fw = stfl_widget_by_id(f->root, f->current_focus_id);
420 
421 	if (old_fw && old_fw->type->f_leave)
422 		old_fw->type->f_leave(old_fw, f);
423 
424 	if (new_fw->type->f_enter)
425 		new_fw->type->f_enter(new_fw, f);
426 
427 	f->current_focus_id = new_fw->id;
428 	return 1;
429 }
430 
stfl_form_new()431 struct stfl_form *stfl_form_new()
432 {
433 	struct stfl_form *f = calloc(1, sizeof(struct stfl_form));
434 	if (f) {
435 		pthread_mutex_init(&f->mtx, NULL);
436 	}
437 	return f;
438 }
439 
stfl_form_event(struct stfl_form * f,wchar_t * event)440 void stfl_form_event(struct stfl_form *f, wchar_t *event)
441 {
442 	struct stfl_event **ep = &f->event_queue;
443 	struct stfl_event *e = calloc(1, sizeof(struct stfl_event));
444 	e->event = event;
445 	while (*ep)
446 		ep = &(*ep)->next;
447 	*ep = e;
448 }
449 
stfl_gather_focus_widget(struct stfl_form * f)450 static struct stfl_widget* stfl_gather_focus_widget(struct stfl_form* f)
451 {
452 	struct stfl_widget *fw = stfl_widget_by_id(f->root, f->current_focus_id);
453 
454 	if (fw == 0)
455 	{
456 		fw = stfl_find_first_focusable(f->root);
457 
458 		if (fw && fw->type->f_enter)
459 			fw->type->f_enter(fw, f);
460 	}
461 	return fw;
462 }
463 
stfl_form_run(struct stfl_form * f,int timeout)464 void stfl_form_run(struct stfl_form *f, int timeout)
465 {
466 	wchar_t *on_handler = 0;
467 
468 	pthread_mutex_lock(&f->mtx);
469 
470 	if (f->event)
471 		free(f->event);
472 	f->event = 0;
473 
474 	if (timeout >= 0 && f->event_queue)
475 		goto unshift_next_event;
476 
477 	if (timeout == -2)
478 		goto unshift_next_event;
479 
480 	if (!f->root) {
481 		fprintf(stderr, "STFL Fatal Error: Called stfl_form_run() without root widget.\n");
482 		abort();
483 	}
484 
485 	if (!curses_active)
486 	{
487 		initscr();
488 		cbreak();
489 		noecho();
490 		nonl();
491 		keypad(stdscr, TRUE);
492 		doupdate();
493 		start_color();
494 		use_default_colors();
495 		wbkgdset(stdscr, ' ');
496 		curses_active = 1;
497 	}
498 
499 	stfl_colorpair_counter = 1;
500 	f->root->type->f_prepare(f->root, f);
501 
502 	struct stfl_widget *fw = stfl_gather_focus_widget(f);
503 	f->current_focus_id = fw ? fw->id : 0;
504 
505 	getbegyx(stdscr, f->root->y, f->root->x);
506 	getmaxyx(stdscr, f->root->h, f->root->w);
507 
508 	if (timeout == -3) {
509 		WINDOW *dummywin = newwin(0, 0, 0, 0);
510 		if (dummywin == NULL) {
511 			fprintf(stderr, "STFL Fatal Error: stfl_form_run() got a NULL pointer from newwin(0, 0, 0, 0).\n");
512 			abort();
513 		}
514 		f->root->type->f_draw(f->root, f, dummywin);
515 		delwin(dummywin);
516 		pthread_mutex_unlock(&f->mtx);
517 		return;
518 	}
519 
520 	werase(stdscr);
521 	f->root->type->f_draw(f->root, f, stdscr);
522 	if (timeout == -1 && f->root->cur_y != -1 && f->root->cur_x != -1) {
523 		wmove(stdscr, f->root->cur_y, f->root->cur_x);
524 	}
525 	refresh();
526 
527 	if (timeout < 0) {
528 		pthread_mutex_unlock(&f->mtx);
529 		return;
530 	}
531 
532 	wtimeout(stdscr, timeout == 0 ? -1 : timeout);
533 	wmove(stdscr, f->cursor_y, f->cursor_x);
534 
535 	wint_t wch;
536 	pthread_mutex_unlock(&f->mtx);
537 	int rc = wget_wch(stdscr, &wch);
538 	pthread_mutex_lock(&f->mtx);
539 
540 	/* fw may be invalid, regather it */
541 	fw = stfl_gather_focus_widget(f);
542 	f->current_focus_id = fw ? fw->id : 0;
543 
544 	struct stfl_widget *w = fw;
545 
546 	if (rc == ERR) {
547 		stfl_form_event(f, compat_wcsdup(L"TIMEOUT"));
548 		goto unshift_next_event;
549 	}
550 
551 	wchar_t *on_event = stfl_keyname(wch, rc == KEY_CODE_YES);
552 	int on_handler_len = wcslen(on_event) + 4;
553 	on_handler = malloc(on_handler_len * sizeof(wchar_t));
554 	swprintf(on_handler, on_handler_len, L"on_%ls", on_event);
555 	free(on_event);
556 
557 	while (w) {
558 		const wchar_t *event = stfl_widget_getkv_str(w, on_handler, 0);
559 		if (event) {
560 			stfl_form_event(f, compat_wcsdup(event));
561 			goto unshift_next_event;
562 		}
563 
564 		if (w->type->f_process && stfl_widget_getkv_int(w, L"process", 1) && w->type->f_process(w, fw, f, wch, rc == KEY_CODE_YES))
565 			goto unshift_next_event;
566 
567 		if (stfl_widget_getkv_int(w, L"modal", 0))
568 			goto generate_event;
569 
570 		w = w->parent;
571 	}
572 
573 	if (rc != KEY_CODE_YES && wch == L'\t')
574 	{
575 		struct stfl_widget *old_fw = fw = stfl_widget_by_id(f->root, f->current_focus_id);
576 
577 		if (!fw)
578 			goto generate_event;
579 
580 		do {
581 			if (fw->first_child)
582 				fw = fw->first_child;
583 			else
584 			if (fw->next_sibling)
585 				fw = fw->next_sibling;
586 			else
587 			{
588 				while (fw->parent && !fw->parent->next_sibling)
589 					fw = fw->parent;
590 				fw = fw->parent ? fw->parent->next_sibling : 0;
591 			}
592 
593 			if (!fw && old_fw)
594 				fw = f->root;
595 		} while (fw && !(fw->allow_focus && stfl_widget_getkv_int(fw, L"can_focus", 1)));
596 
597 		if (old_fw != fw)
598 		{
599 			if (old_fw && old_fw->type->f_leave)
600 				old_fw->type->f_leave(old_fw, f);
601 
602 			if (fw && fw->type->f_enter)
603 				fw->type->f_enter(fw, f);
604 
605 			f->current_focus_id = fw ? fw->id : 0;
606 		}
607 
608 		goto unshift_next_event;
609 	}
610 	else if (rc == KEY_CODE_YES && wch == KEY_BTAB)
611 	{
612 		struct stfl_widget *old_fw = stfl_widget_by_id(f->root, f->current_focus_id);
613 		struct stfl_widget *tmp_fw = f->root;
614 		struct stfl_widget *fw = 0;
615 
616 focus_wrap_around:
617 		while (tmp_fw && tmp_fw != old_fw)
618 		{
619 			if (tmp_fw->allow_focus && stfl_widget_getkv_int(tmp_fw, L"can_focus", 1))
620 				fw = tmp_fw;
621 
622 			if (tmp_fw->first_child)
623 				tmp_fw = tmp_fw->first_child;
624 			else
625 			if (tmp_fw->next_sibling)
626 				tmp_fw = tmp_fw->next_sibling;
627 			else
628 			{
629 				while (tmp_fw->parent && !tmp_fw->parent->next_sibling)
630 					tmp_fw = tmp_fw->parent;
631 				tmp_fw = tmp_fw->parent ? tmp_fw->parent->next_sibling : 0;
632 			}
633 		}
634 
635 		if (!fw && old_fw)
636 		{
637 			old_fw = f->root->last_child;
638 			goto focus_wrap_around;
639 		}
640 
641 		if (fw && old_fw != fw)
642 		{
643 			if (old_fw && old_fw->type->f_leave)
644 				old_fw->type->f_leave(old_fw, f);
645 
646 			if (fw && fw->type->f_enter)
647 				fw->type->f_enter(fw, f);
648 
649 			f->current_focus_id = fw ? fw->id : 0;
650 		}
651 
652 		goto unshift_next_event;
653 	}
654 
655 generate_event:
656 	stfl_form_event(f, stfl_keyname(wch, rc == KEY_CODE_YES));
657 
658 unshift_next_event:;
659 	struct stfl_event *e = f->event_queue;
660 	if (e) {
661 		f->event_queue = e->next;
662 		f->event = e->event;
663 		free(e);
664 	}
665 
666 	pthread_mutex_unlock(&f->mtx);
667 	free(on_handler);
668 }
669 
stfl_form_reset()670 void stfl_form_reset()
671 {
672 	if (curses_active) {
673 		endwin();
674 		curses_active = 0;
675 	}
676 }
677 
stfl_form_redraw()678 void stfl_form_redraw()
679 {
680 	if (curses_active)
681 		clearok(curscr, 1);
682 }
683 
stfl_form_free(struct stfl_form * f)684 void stfl_form_free(struct stfl_form *f)
685 {
686 	pthread_mutex_lock(&f->mtx);
687 	if (f->root)
688 		stfl_widget_free(f->root);
689 	if (f->event)
690 		free(f->event);
691 	pthread_mutex_unlock(&f->mtx);
692 	free(f);
693 }
694 
stfl_check_setfocus(struct stfl_form * f,struct stfl_widget * w)695 void stfl_check_setfocus(struct stfl_form *f, struct stfl_widget *w)
696 {
697 	if (w->setfocus) {
698 		f->current_focus_id = w->id;
699 		w->setfocus = 0;
700 	}
701 
702 	w = w->first_child;
703 	while (w) {
704 		stfl_check_setfocus(f, w);
705 		w = w->next_sibling;
706 	}
707 }
708 
compute_len_from_width(const wchar_t * p,unsigned int width)709 static unsigned int compute_len_from_width(const wchar_t *p, unsigned int width)
710 {
711 	unsigned int len = 0;
712 	unsigned int end_loop = 0;
713 	while (p && *p && !end_loop) {
714 		if (wcwidth(*p) > width) {
715 			end_loop = 1;
716 		} else {
717 			width -= wcwidth(*p);
718 			p++;
719 			len++;
720 		}
721 	}
722 	return len;
723 }
724 
stfl_print_richtext(struct stfl_widget * w,WINDOW * win,unsigned int y,unsigned int x,const wchar_t * text,unsigned int width,const wchar_t * style_normal,int has_focus)725 unsigned int stfl_print_richtext(struct stfl_widget *w, WINDOW *win, unsigned int y, unsigned int x, const wchar_t * text, unsigned int width, const wchar_t * style_normal, int has_focus)
726 {
727 	const wchar_t *p = text;
728 	unsigned int retval = 0;
729 
730 	unsigned int end_col = x + width;
731 
732 	while (*p) {
733 		unsigned int len = compute_len_from_width(p, end_col - x);
734 		const wchar_t *p1 = wcschr(p, L'<');
735 		if (NULL == p1) {
736 			mvwaddnwstr(win, y, x, p, len);
737 			retval += len;
738 			break;
739 		} else {
740 			const wchar_t *p2 = wcschr(p1 + 1, L'>');
741 
742 			if (len > (p1 - p))
743 				len = p1 - p;
744 			mvwaddnwstr(win, y, x, p, len);
745 			retval += len;
746 			x += wcswidth(p, len);
747 
748 			if (p2) {
749 				wchar_t stylename[p2 - p1];
750 				wmemcpy(stylename, p1 + 1, p2 - p1 - 1);
751 				stylename[p2 - p1 - 1] = L'\0';
752 				if (wcscmp(stylename,L"")==0) {
753 					if (end_col - x > 0) {
754 						mvwaddnwstr(win, y, x, L"<", 1);
755 						retval += 1;
756 						++x;
757 					}
758 				} else if (wcscmp(stylename, L"/")==0) {
759 					stfl_style(win, style_normal);
760 				} else {
761 					wchar_t lookup_stylename[128];
762 					const wchar_t * style;
763 					if (has_focus)
764 						swprintf(lookup_stylename, sizeof(lookup_stylename)/sizeof(*lookup_stylename), L"style_%ls_focus", stylename);
765 					else
766 						swprintf(lookup_stylename, sizeof(lookup_stylename)/sizeof(*lookup_stylename), L"style_%ls_normal", stylename);
767 					style = stfl_widget_getkv_str(w, lookup_stylename, L"");
768 					stfl_style(win, style);
769 				}
770 				p = p2 + 1;
771 			} else {
772 				break;
773 			}
774 		}
775 	}
776 	return retval;
777 }
778 
779