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