Annotation of src/usr.bin/tmux/format-draw.c, Revision 1.26
1.26 ! nicm 1: /* $OpenBSD: format-draw.c,v 1.25 2021/10/26 12:22:23 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20:
21: #include <stdlib.h>
22: #include <string.h>
23:
24: #include "tmux.h"
25:
26: /* Format range. */
27: struct format_range {
28: u_int index;
29: struct screen *s;
30:
31: u_int start;
32: u_int end;
33:
34: enum style_range_type type;
35: u_int argument;
36:
37: TAILQ_ENTRY(format_range) entry;
38: };
39: TAILQ_HEAD(format_ranges, format_range);
40:
41: /* Does this range match this style? */
42: static int
43: format_is_type(struct format_range *fr, struct style *sy)
44: {
45: if (fr->type != sy->range_type)
46: return (0);
47: if (fr->type == STYLE_RANGE_WINDOW &&
48: fr->argument != sy->range_argument)
49: return (0);
50: return (1);
51: }
52:
53: /* Free a range. */
54: static void
55: format_free_range(struct format_ranges *frs, struct format_range *fr)
56: {
57: TAILQ_REMOVE(frs, fr, entry);
58: free(fr);
59: }
60:
61: /* Fix range positions. */
62: static void
63: format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
64: u_int start, u_int width)
65: {
66: struct format_range *fr, *fr1;
67:
68: if (frs == NULL)
69: return;
70:
71: TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
72: if (fr->s != s)
73: continue;
74:
75: if (fr->end <= start || fr->start >= start + width) {
76: format_free_range(frs, fr);
77: continue;
78: }
79:
80: if (fr->start < start)
81: fr->start = start;
82: if (fr->end > start + width)
83: fr->end = start + width;
84: if (fr->start == fr->end) {
85: format_free_range(frs, fr);
86: continue;
87: }
1.7 nicm 88:
89: fr->start -= start;
90: fr->end -= start;
1.1 nicm 91:
92: fr->start += offset;
93: fr->end += offset;
94: }
95: }
96:
97: /* Draw a part of the format. */
98: static void
99: format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
100: struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
101: u_int width)
102: {
103: /*
104: * The offset is how far from the cursor on the target screen; start
105: * and width how much to copy from the source screen.
106: */
107: screen_write_cursormove(octx, ocx + offset, ocy, 0);
108: screen_write_fast_copy(octx, s, start, 0, width, 1);
109: format_update_ranges(frs, s, offset, start, width);
110: }
111:
112: /* Draw list part of format. */
113: static void
114: format_draw_put_list(struct screen_write_ctx *octx,
115: u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
116: struct screen *list_left, struct screen *list_right, int focus_start,
117: int focus_end, struct format_ranges *frs)
118: {
119: u_int start, focus_centre;
120:
121: /* If there is enough space for the list, draw it entirely. */
122: if (width >= list->cx) {
123: format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
124: return;
125: }
126:
127: /* The list needs to be trimmed. Try to keep the focus visible. */
128: focus_centre = focus_start + (focus_end - focus_start) / 2;
129: if (focus_centre < width / 2)
130: start = 0;
131: else
132: start = focus_centre - width / 2;
133: if (start + width > list->cx)
134: start = list->cx - width;
135:
136: /* Draw <> markers at either side if needed. */
137: if (start != 0 && width > list_left->cx) {
138: screen_write_cursormove(octx, ocx + offset, ocy, 0);
139: screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
140: offset += list_left->cx;
141: start += list_left->cx;
142: width -= list_left->cx;
143: }
144: if (start + width < list->cx && width > list_right->cx) {
1.15 nicm 145: screen_write_cursormove(octx, ocx + offset + width -
146: list_right->cx, ocy, 0);
1.1 nicm 147: screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
148: 1);
149: width -= list_right->cx;
150: }
151:
152: /* Draw the list screen itself. */
153: format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
154: }
155:
156: /* Draw format with no list. */
157: static void
158: format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
159: u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23 nicm 160: struct screen *abs_centre, struct format_ranges *frs)
1.1 nicm 161: {
1.23 nicm 162: u_int width_left, width_centre, width_right, width_abs_centre;
1.1 nicm 163:
164: width_left = left->cx;
165: width_centre = centre->cx;
166: width_right = right->cx;
1.23 nicm 167: width_abs_centre = abs_centre->cx;
1.1 nicm 168:
169: /*
170: * Try to keep as much of the left and right as possible at the expense
171: * of the centre.
172: */
173: while (width_left + width_centre + width_right > available) {
174: if (width_centre > 0)
175: width_centre--;
176: else if (width_right > 0)
177: width_right--;
178: else
179: width_left--;
180: }
181:
182: /* Write left. */
183: format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
184:
185: /* Write right at available - width_right. */
186: format_draw_put(octx, ocx, ocy, right, frs,
187: available - width_right,
188: right->cx - width_right,
189: width_right);
190:
191: /*
192: * Write centre halfway between
193: * width_left
194: * and
195: * available - width_right.
196: */
197: format_draw_put(octx, ocx, ocy, centre, frs,
198: width_left
199: + ((available - width_right) - width_left) / 2
200: - width_centre / 2,
201: centre->cx / 2 - width_centre / 2,
202: width_centre);
1.23 nicm 203:
204: /*
205: * Write abs_centre in the perfect centre of all horizontal space.
206: */
207: if (width_abs_centre > available)
208: width_abs_centre = available;
209: format_draw_put(octx, ocx, ocy, abs_centre, frs,
210: (available - width_abs_centre) / 2,
211: 0,
212: width_abs_centre);
1.1 nicm 213: }
214:
215: /* Draw format with list on the left. */
216: static void
217: format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
218: u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23 nicm 219: struct screen *abs_centre, struct screen *list, struct screen *list_left,
220: struct screen *list_right, struct screen *after, int focus_start,
221: int focus_end, struct format_ranges *frs)
1.1 nicm 222: {
223: u_int width_left, width_centre, width_right;
1.23 nicm 224: u_int width_list, width_after, width_abs_centre;
1.1 nicm 225: struct screen_write_ctx ctx;
226:
227: width_left = left->cx;
228: width_centre = centre->cx;
229: width_right = right->cx;
1.23 nicm 230: width_abs_centre = abs_centre->cx;
1.1 nicm 231: width_list = list->cx;
232: width_after = after->cx;
233:
234: /*
235: * Trim first the centre, then the list, then the right, then after the
236: * list, then the left.
237: */
238: while (width_left +
239: width_centre +
240: width_right +
241: width_list +
242: width_after > available) {
243: if (width_centre > 0)
244: width_centre--;
245: else if (width_list > 0)
246: width_list--;
247: else if (width_right > 0)
248: width_right--;
249: else if (width_after > 0)
250: width_after--;
251: else
252: width_left--;
253: }
254:
255: /* If there is no list left, pass off to the no list function. */
256: if (width_list == 0) {
1.18 nicm 257: screen_write_start(&ctx, left);
1.1 nicm 258: screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
259: screen_write_stop(&ctx);
260:
261: format_draw_none(octx, available, ocx, ocy, left, centre,
1.23 nicm 262: right, abs_centre, frs);
1.1 nicm 263: return;
264: }
265:
266: /* Write left at 0. */
267: format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
268:
269: /* Write right at available - width_right. */
270: format_draw_put(octx, ocx, ocy, right, frs,
271: available - width_right,
272: right->cx - width_right,
273: width_right);
274:
275: /* Write after at width_left + width_list. */
276: format_draw_put(octx, ocx, ocy, after, frs,
277: width_left + width_list,
278: 0,
279: width_after);
280:
281: /*
282: * Write centre halfway between
283: * width_left + width_list + width_after
284: * and
285: * available - width_right.
286: */
287: format_draw_put(octx, ocx, ocy, centre, frs,
288: (width_left + width_list + width_after)
289: + ((available - width_right)
290: - (width_left + width_list + width_after)) / 2
291: - width_centre / 2,
292: centre->cx / 2 - width_centre / 2,
293: width_centre);
294:
295: /*
296: * The list now goes from
297: * width_left
298: * to
299: * width_left + width_list.
300: * If there is no focus given, keep the left in focus.
301: */
302: if (focus_start == -1 || focus_end == -1)
303: focus_start = focus_end = 0;
304: format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
305: list_left, list_right, focus_start, focus_end, frs);
1.23 nicm 306:
307: /*
308: * Write abs_centre in the perfect centre of all horizontal space.
309: */
310: if (width_abs_centre > available)
311: width_abs_centre = available;
312: format_draw_put(octx, ocx, ocy, abs_centre, frs,
313: (available - width_abs_centre) / 2,
314: 0,
315: width_abs_centre);
1.1 nicm 316: }
317:
318: /* Draw format with list in the centre. */
319: static void
320: format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
321: u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23 nicm 322: struct screen *abs_centre, struct screen *list, struct screen *list_left,
323: struct screen *list_right, struct screen *after, int focus_start,
324: int focus_end, struct format_ranges *frs)
1.1 nicm 325: {
1.23 nicm 326: u_int width_left, width_centre, width_right, middle;
327: u_int width_list, width_after, width_abs_centre;
1.1 nicm 328: struct screen_write_ctx ctx;
329:
330: width_left = left->cx;
331: width_centre = centre->cx;
332: width_right = right->cx;
1.23 nicm 333: width_abs_centre = abs_centre->cx;
1.1 nicm 334: width_list = list->cx;
335: width_after = after->cx;
336:
337: /*
338: * Trim first the list, then after the list, then the centre, then the
339: * right, then the left.
340: */
341: while (width_left +
342: width_centre +
343: width_right +
344: width_list +
345: width_after > available) {
346: if (width_list > 0)
347: width_list--;
348: else if (width_after > 0)
349: width_after--;
350: else if (width_centre > 0)
351: width_centre--;
352: else if (width_right > 0)
353: width_right--;
354: else
355: width_left--;
356: }
357:
358: /* If there is no list left, pass off to the no list function. */
359: if (width_list == 0) {
1.18 nicm 360: screen_write_start(&ctx, centre);
1.1 nicm 361: screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
362: screen_write_stop(&ctx);
363:
364: format_draw_none(octx, available, ocx, ocy, left, centre,
1.23 nicm 365: right, abs_centre, frs);
1.1 nicm 366: return;
367: }
368:
369: /* Write left at 0. */
370: format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
371:
372: /* Write right at available - width_right. */
373: format_draw_put(octx, ocx, ocy, right, frs,
374: available - width_right,
375: right->cx - width_right,
376: width_right);
377:
378: /*
379: * All three centre sections are offset from the middle of the
380: * available space.
381: */
382: middle = (width_left + ((available - width_right) - width_left) / 2);
383:
384: /*
385: * Write centre at
386: * middle - width_list / 2 - width_centre.
387: */
388: format_draw_put(octx, ocx, ocy, centre, frs,
389: middle - width_list / 2 - width_centre,
390: 0,
391: width_centre);
392:
393: /*
394: * Write after at
1.9 nicm 395: * middle - width_list / 2 + width_list
1.1 nicm 396: */
397: format_draw_put(octx, ocx, ocy, after, frs,
1.9 nicm 398: middle - width_list / 2 + width_list,
1.1 nicm 399: 0,
400: width_after);
401:
402: /*
403: * The list now goes from
404: * middle - width_list / 2
405: * to
406: * middle + width_list / 2
407: * If there is no focus given, keep the centre in focus.
408: */
409: if (focus_start == -1 || focus_end == -1)
410: focus_start = focus_end = list->cx / 2;
411: format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
412: width_list, list, list_left, list_right, focus_start, focus_end,
413: frs);
1.23 nicm 414:
415: /*
416: * Write abs_centre in the perfect centre of all horizontal space.
417: */
418: if (width_abs_centre > available)
419: width_abs_centre = available;
420: format_draw_put(octx, ocx, ocy, abs_centre, frs,
421: (available - width_abs_centre) / 2,
422: 0,
423: width_abs_centre);
1.1 nicm 424: }
425:
426: /* Draw format with list on the right. */
427: static void
428: format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
429: u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23 nicm 430: struct screen *abs_centre, struct screen *list,
431: struct screen *list_left, struct screen *list_right, struct screen *after,
432: int focus_start, int focus_end, struct format_ranges *frs)
1.1 nicm 433: {
434: u_int width_left, width_centre, width_right;
1.23 nicm 435: u_int width_list, width_after, width_abs_centre;
1.1 nicm 436: struct screen_write_ctx ctx;
437:
438: width_left = left->cx;
439: width_centre = centre->cx;
440: width_right = right->cx;
1.23 nicm 441: width_abs_centre = abs_centre->cx;
1.1 nicm 442: width_list = list->cx;
443: width_after = after->cx;
444:
445: /*
446: * Trim first the centre, then the list, then the right, then
447: * after the list, then the left.
448: */
449: while (width_left +
450: width_centre +
451: width_right +
452: width_list +
453: width_after > available) {
454: if (width_centre > 0)
455: width_centre--;
456: else if (width_list > 0)
457: width_list--;
458: else if (width_right > 0)
459: width_right--;
460: else if (width_after > 0)
461: width_after--;
462: else
463: width_left--;
464: }
465:
466: /* If there is no list left, pass off to the no list function. */
467: if (width_list == 0) {
1.18 nicm 468: screen_write_start(&ctx, right);
1.1 nicm 469: screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
470: screen_write_stop(&ctx);
471:
472: format_draw_none(octx, available, ocx, ocy, left, centre,
1.23 nicm 473: right, abs_centre, frs);
1.1 nicm 474: return;
475: }
476:
477: /* Write left at 0. */
478: format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
479:
480: /* Write after at available - width_after. */
481: format_draw_put(octx, ocx, ocy, after, frs,
482: available - width_after,
483: after->cx - width_after,
484: width_after);
485:
486: /*
487: * Write right at
488: * available - width_right - width_list - width_after.
489: */
490: format_draw_put(octx, ocx, ocy, right, frs,
491: available - width_right - width_list - width_after,
492: 0,
493: width_right);
494:
495: /*
496: * Write centre halfway between
497: * width_left
498: * and
499: * available - width_right - width_list - width_after.
500: */
501: format_draw_put(octx, ocx, ocy, centre, frs,
502: width_left
503: + ((available - width_right - width_list - width_after)
504: - width_left) / 2
505: - width_centre / 2,
506: centre->cx / 2 - width_centre / 2,
507: width_centre);
508:
509: /*
510: * The list now goes from
511: * available - width_list - width_after
512: * to
513: * available - width_after
514: * If there is no focus given, keep the right in focus.
515: */
516: if (focus_start == -1 || focus_end == -1)
517: focus_start = focus_end = 0;
518: format_draw_put_list(octx, ocx, ocy, available - width_list -
519: width_after, width_list, list, list_left, list_right, focus_start,
520: focus_end, frs);
1.23 nicm 521:
522: /*
523: * Write abs_centre in the perfect centre of all horizontal space.
524: */
525: if (width_abs_centre > available)
526: width_abs_centre = available;
527: format_draw_put(octx, ocx, ocy, abs_centre, frs,
528: (available - width_abs_centre) / 2,
529: 0,
530: width_abs_centre);
531: }
532:
533: static void
534: format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available,
535: u_int ocx, u_int ocy, struct screen *left, struct screen *centre,
536: struct screen *right, struct screen *abs_centre, struct screen *list,
537: struct screen *list_left, struct screen *list_right, struct screen *after,
538: int focus_start, int focus_end, struct format_ranges *frs)
539: {
540: u_int width_left, width_centre, width_right, width_abs_centre;
541: u_int width_list, width_after, middle, abs_centre_offset;
542:
543: width_left = left->cx;
544: width_centre = centre->cx;
545: width_right = right->cx;
546: width_abs_centre = abs_centre->cx;
547: width_list = list->cx;
548: width_after = after->cx;
549:
550: /*
551: * Trim first centre, then the right, then the left.
552: */
553: while (width_left +
554: width_centre +
555: width_right > available) {
556: if (width_centre > 0)
557: width_centre--;
558: else if (width_right > 0)
559: width_right--;
560: else
561: width_left--;
562: }
563:
564: /*
565: * We trim list after and abs_centre independently, as we are drawing
566: * them over the rest. Trim first the list, then after the list, then
567: * abs_centre.
568: */
569: while (width_list + width_after + width_abs_centre > available) {
570: if (width_list > 0)
571: width_list--;
572: else if (width_after > 0)
573: width_after--;
574: else
575: width_abs_centre--;
576: }
577:
578: /* Write left at 0. */
579: format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
580:
581: /* Write right at available - width_right. */
582: format_draw_put(octx, ocx, ocy, right, frs,
583: available - width_right,
584: right->cx - width_right,
585: width_right);
586:
587: /*
588: * Keep writing centre at the relative centre. Only the list is written
589: * in the absolute centre of the horizontal space.
590: */
591: middle = (width_left + ((available - width_right) - width_left) / 2);
592:
593: /*
594: * Write centre at
595: * middle - width_centre.
596: */
597: format_draw_put(octx, ocx, ocy, centre, frs,
598: middle - width_centre,
599: 0,
600: width_centre);
601:
602: /*
603: * If there is no focus given, keep the centre in focus.
604: */
605: if (focus_start == -1 || focus_end == -1)
606: focus_start = focus_end = list->cx / 2;
607:
608: /*
609: * We centre abs_centre and the list together, so their shared centre is
610: * in the perfect centre of horizontal space.
611: */
612: abs_centre_offset = (available - width_list - width_abs_centre) / 2;
613:
614: /*
615: * Write abs_centre before the list.
616: */
617: format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset,
618: 0, width_abs_centre);
619: abs_centre_offset += width_abs_centre;
620:
621: /*
622: * Draw the list in the absolute centre
623: */
624: format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list,
625: list, list_left, list_right, focus_start, focus_end, frs);
626: abs_centre_offset += width_list;
627:
628: /*
629: * Write after at the end of the centre
630: */
631: format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0,
632: width_after);
1.1 nicm 633: }
634:
1.24 nicm 635: /* Get width and count of any leading #s. */
636: static const char *
637: format_leading_hashes(const char *cp, u_int *n, u_int *width)
638: {
639: for (*n = 0; cp[*n] == '#'; (*n)++)
640: /* nothing */;
641: if (*n == 0) {
642: *width = 0;
643: return (cp);
644: }
645: if (cp[*n] != '[') {
646: if ((*n % 2) == 0)
647: *width = (*n / 2);
648: else
649: *width = (*n / 2) + 1;
650: return (cp + *n);
651: }
652: *width = (*n / 2);
653: if ((*n % 2) == 0) {
654: /*
655: * An even number of #s means that all #s are escaped, so not a
656: * style. The caller should not skip this. Return pointing to
657: * the [.
658: */
659: return (cp + *n);
660: }
661: /* This is a style, so return pointing to the #. */
662: return (cp + *n - 1);
663: }
664:
1.21 nicm 665: /* Draw multiple characters. */
666: static void
667: format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
668: u_int n)
669: {
670: u_int i;
671:
672: utf8_set(&sy->gc.data, ch);
673: for (i = 0; i < n; i++)
674: screen_write_cell(ctx, &sy->gc);
675: }
676:
1.1 nicm 677: /* Draw a format to a screen. */
678: void
679: format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
1.25 nicm 680: u_int available, const char *expanded, struct style_ranges *srs,
681: int default_colours)
1.1 nicm 682: {
683: enum { LEFT,
684: CENTRE,
685: RIGHT,
1.23 nicm 686: ABSOLUTE_CENTRE,
1.1 nicm 687: LIST,
688: LIST_LEFT,
689: LIST_RIGHT,
690: AFTER,
691: TOTAL } current = LEFT, last = LEFT;
692: const char *names[] = { "LEFT",
693: "CENTRE",
694: "RIGHT",
1.23 nicm 695: "ABSOLUTE_CENTRE",
1.1 nicm 696: "LIST",
697: "LIST_LEFT",
698: "LIST_RIGHT",
699: "AFTER" };
700: size_t size = strlen(expanded);
701: struct screen *os = octx->s, s[TOTAL];
702: struct screen_write_ctx ctx[TOTAL];
1.21 nicm 703: u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
1.23 nicm 704: u_int map[] = { LEFT,
705: LEFT,
706: CENTRE,
707: RIGHT,
708: ABSOLUTE_CENTRE };
1.1 nicm 709: int focus_start = -1, focus_end = -1;
1.21 nicm 710: int list_state = -1, fill = -1, even;
1.1 nicm 711: enum style_align list_align = STYLE_ALIGN_DEFAULT;
1.13 nicm 712: struct grid_cell gc, current_default;
713: struct style sy, saved_sy;
1.1 nicm 714: struct utf8_data *ud = &sy.gc.data;
715: const char *cp, *end;
716: enum utf8_state more;
717: char *tmp;
718: struct format_range *fr = NULL, *fr1;
719: struct format_ranges frs;
720: struct style_range *sr;
721:
1.13 nicm 722: memcpy(¤t_default, base, sizeof current_default);
723: style_set(&sy, ¤t_default);
1.1 nicm 724: TAILQ_INIT(&frs);
1.3 nicm 725: log_debug("%s: %s", __func__, expanded);
1.1 nicm 726:
727: /*
728: * We build three screens for left, right, centre alignment, one for
729: * the list, one for anything after the list and two for the list left
730: * and right markers.
731: */
732: for (i = 0; i < TOTAL; i++) {
733: screen_init(&s[i], size, 1, 0);
1.18 nicm 734: screen_write_start(&ctx[i], &s[i]);
1.13 nicm 735: screen_write_clearendofline(&ctx[i], current_default.bg);
1.1 nicm 736: width[i] = 0;
737: }
738:
739: /*
740: * Walk the string and add to the corresponding screens,
741: * parsing styles as we go.
742: */
743: cp = expanded;
744: while (*cp != '\0') {
1.21 nicm 745: /* Handle sequences of #. */
746: if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
747: for (n = 1; cp[n] == '#'; n++)
748: /* nothing */;
1.22 nicm 749: even = ((n % 2) == 0);
1.21 nicm 750: if (cp[n] != '[') {
1.22 nicm 751: cp += n;
752: if (even)
753: n = (n / 2);
754: else
755: n = (n / 2) + 1;
1.21 nicm 756: width[current] += n;
757: format_draw_many(&ctx[current], &sy, '#', n);
758: continue;
759: }
760: if (even)
761: cp += (n + 1);
762: else
763: cp += (n - 1);
764: if (sy.ignore)
765: continue;
766: format_draw_many(&ctx[current], &sy, '#', n / 2);
767: width[current] += (n / 2);
768: if (even) {
769: utf8_set(ud, '[');
770: screen_write_cell(&ctx[current], &sy.gc);
771: width[current]++;
772: }
773: continue;
774: }
775:
776: /* Is this not a style? */
1.19 nicm 777: if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
1.1 nicm 778: /* See if this is a UTF-8 character. */
779: if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
780: while (*++cp != '\0' && more == UTF8_MORE)
781: more = utf8_append(ud, *cp);
782: if (more != UTF8_DONE)
783: cp -= ud->have;
784: }
785:
786: /* Not a UTF-8 character - ASCII or not valid. */
787: if (more != UTF8_DONE) {
788: if (*cp < 0x20 || *cp > 0x7e) {
789: /* Ignore nonprintable characters. */
790: cp++;
791: continue;
792: }
793: utf8_set(ud, *cp);
794: cp++;
795: }
796:
1.12 nicm 797: /* Draw the cell to the current screen. */
1.1 nicm 798: screen_write_cell(&ctx[current], &sy.gc);
799: width[current] += ud->width;
800: continue;
801: }
802:
803: /* This is a style. Work out where the end is and parse it. */
804: end = format_skip(cp + 2, "]");
1.3 nicm 805: if (end == NULL) {
1.6 nicm 806: log_debug("%s: no terminating ] at '%s'", __func__,
807: cp + 2);
1.5 nicm 808: TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
809: format_free_range(&frs, fr);
810: goto out;
1.3 nicm 811: }
1.1 nicm 812: tmp = xstrndup(cp + 2, end - (cp + 2));
1.13 nicm 813: style_copy(&saved_sy, &sy);
814: if (style_parse(&sy, ¤t_default, tmp) != 0) {
1.6 nicm 815: log_debug("%s: invalid style '%s'", __func__, tmp);
1.1 nicm 816: free(tmp);
1.4 nicm 817: cp = end + 1;
818: continue;
1.1 nicm 819: }
1.6 nicm 820: log_debug("%s: style '%s' -> '%s'", __func__, tmp,
821: style_tostring(&sy));
1.1 nicm 822: free(tmp);
1.25 nicm 823: if (default_colours) {
824: sy.gc.bg = base->bg;
825: sy.gc.fg = base->fg;
826: }
1.1 nicm 827:
1.11 nicm 828: /* If this style has a fill colour, store it for later. */
829: if (sy.fill != 8)
830: fill = sy.fill;
1.13 nicm 831:
832: /* If this style pushed or popped the default, update it. */
833: if (sy.default_type == STYLE_DEFAULT_PUSH) {
1.20 nicm 834: memcpy(¤t_default, &saved_sy.gc,
835: sizeof current_default);
1.13 nicm 836: sy.default_type = STYLE_DEFAULT_BASE;
837: } else if (sy.default_type == STYLE_DEFAULT_POP) {
838: memcpy(¤t_default, base, sizeof current_default);
839: sy.default_type = STYLE_DEFAULT_BASE;
840: }
1.11 nicm 841:
1.1 nicm 842: /* Check the list state. */
843: switch (sy.list) {
844: case STYLE_LIST_ON:
845: /*
846: * Entering the list, exiting a marker, or exiting the
847: * focus.
848: */
849: if (list_state != 0) {
850: if (fr != NULL) { /* abort any region */
851: free(fr);
852: fr = NULL;
853: }
854: list_state = 0;
855: list_align = sy.align;
856: }
857:
858: /* End the focus if started. */
859: if (focus_start != -1 && focus_end == -1)
860: focus_end = s[LIST].cx;
861:
862: current = LIST;
863: break;
864: case STYLE_LIST_FOCUS:
865: /* Entering the focus. */
866: if (list_state != 0) /* not inside the list */
867: break;
868: if (focus_start == -1) /* focus already started */
869: focus_start = s[LIST].cx;
870: break;
871: case STYLE_LIST_OFF:
872: /* Exiting or outside the list. */
873: if (list_state == 0) {
874: if (fr != NULL) { /* abort any region */
875: free(fr);
876: fr = NULL;
877: }
878: if (focus_start != -1 && focus_end == -1)
879: focus_end = s[LIST].cx;
880:
881: map[list_align] = AFTER;
882: if (list_align == STYLE_ALIGN_LEFT)
883: map[STYLE_ALIGN_DEFAULT] = AFTER;
884: list_state = 1;
885: }
886: current = map[sy.align];
887: break;
888: case STYLE_LIST_LEFT_MARKER:
889: /* Entering left marker. */
890: if (list_state != 0) /* not inside the list */
891: break;
892: if (s[LIST_LEFT].cx != 0) /* already have marker */
893: break;
894: if (fr != NULL) { /* abort any region */
895: free(fr);
896: fr = NULL;
897: }
898: if (focus_start != -1 && focus_end == -1)
899: focus_start = focus_end = -1;
900: current = LIST_LEFT;
901: break;
902: case STYLE_LIST_RIGHT_MARKER:
903: /* Entering right marker. */
904: if (list_state != 0) /* not inside the list */
905: break;
906: if (s[LIST_RIGHT].cx != 0) /* already have marker */
907: break;
908: if (fr != NULL) { /* abort any region */
909: free(fr);
910: fr = NULL;
911: }
912: if (focus_start != -1 && focus_end == -1)
913: focus_start = focus_end = -1;
914: current = LIST_RIGHT;
915: break;
916: }
917: if (current != last) {
918: log_debug("%s: change %s -> %s", __func__,
919: names[last], names[current]);
920: last = current;
921: }
922:
923: /*
924: * Check if the range style has changed and if so end the
925: * current range and start a new one if needed.
926: */
927: if (srs != NULL) {
928: if (fr != NULL && !format_is_type(fr, &sy)) {
929: if (s[current].cx != fr->start) {
930: fr->end = s[current].cx + 1;
931: TAILQ_INSERT_TAIL(&frs, fr, entry);
932: } else
933: free(fr);
934: fr = NULL;
935: }
936: if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
937: fr = xcalloc(1, sizeof *fr);
938: fr->index = current;
939:
940: fr->s = &s[current];
941: fr->start = s[current].cx;
942:
943: fr->type = sy.range_type;
944: fr->argument = sy.range_argument;
945: }
946: }
947:
948: cp = end + 1;
949: }
950: free(fr);
951:
1.2 nicm 952: for (i = 0; i < TOTAL; i++) {
953: screen_write_stop(&ctx[i]);
1.1 nicm 954: log_debug("%s: width %s is %u", __func__, names[i], width[i]);
1.2 nicm 955: }
1.1 nicm 956: if (focus_start != -1 && focus_end != -1)
1.6 nicm 957: log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
1.1 nicm 958: TAILQ_FOREACH(fr, &frs, entry) {
959: log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
960: fr->argument, names[fr->index], fr->start, fr->end);
1.11 nicm 961: }
962:
963: /* Clear the available area. */
964: if (fill != -1) {
965: memcpy(&gc, &grid_default_cell, sizeof gc);
966: gc.bg = fill;
967: for (i = 0; i < available; i++)
968: screen_write_putc(octx, &gc, ' ');
1.1 nicm 969: }
970:
971: /*
972: * Draw the screens. How they are arranged depends on where the list
1.17 nicm 973: * appears.
1.1 nicm 974: */
975: switch (list_align) {
976: case STYLE_ALIGN_DEFAULT:
977: /* No list. */
978: format_draw_none(octx, available, ocx, ocy, &s[LEFT],
1.23 nicm 979: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
1.1 nicm 980: break;
981: case STYLE_ALIGN_LEFT:
982: /* List is part of the left. */
983: format_draw_left(octx, available, ocx, ocy, &s[LEFT],
1.23 nicm 984: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
985: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
986: focus_start, focus_end, &frs);
1.1 nicm 987: break;
988: case STYLE_ALIGN_CENTRE:
989: /* List is part of the centre. */
990: format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
1.23 nicm 991: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
992: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
993: focus_start, focus_end, &frs);
1.1 nicm 994: break;
995: case STYLE_ALIGN_RIGHT:
996: /* List is part of the right. */
997: format_draw_right(octx, available, ocx, ocy, &s[LEFT],
1.23 nicm 998: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
999: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
1000: focus_start, focus_end, &frs);
1001: break;
1002: case STYLE_ALIGN_ABSOLUTE_CENTRE:
1003: /* List is in the centre of the entire horizontal space. */
1004: format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
1005: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
1006: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
1007: focus_start, focus_end, &frs);
1.1 nicm 1008: break;
1009: }
1010:
1011: /* Create ranges to return. */
1012: TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
1013: sr = xcalloc(1, sizeof *sr);
1014: sr->type = fr->type;
1015: sr->argument = fr->argument;
1016: sr->start = fr->start;
1017: sr->end = fr->end;
1018: TAILQ_INSERT_TAIL(srs, sr, entry);
1019:
1020: log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
1021: sr->argument, sr->start, sr->end);
1022:
1023: format_free_range(&frs, fr);
1024: }
1.2 nicm 1025:
1.5 nicm 1026: out:
1.2 nicm 1027: /* Free the screens. */
1028: for (i = 0; i < TOTAL; i++)
1029: screen_free(&s[i]);
1.1 nicm 1030:
1031: /* Restore the original cursor position. */
1032: screen_write_cursormove(octx, ocx, ocy, 0);
1033: }
1034:
1035: /* Get width, taking #[] into account. */
1036: u_int
1037: format_width(const char *expanded)
1038: {
1039: const char *cp, *end;
1.24 nicm 1040: u_int n, leading_width, width = 0;
1.1 nicm 1041: struct utf8_data ud;
1042: enum utf8_state more;
1043:
1044: cp = expanded;
1045: while (*cp != '\0') {
1.21 nicm 1046: if (*cp == '#') {
1.24 nicm 1047: end = format_leading_hashes(cp, &n, &leading_width);
1048: width += leading_width;
1049: cp = end;
1050: if (*cp == '#') {
1051: end = format_skip(cp + 2, "]");
1052: if (end == NULL)
1053: return (0);
1054: cp = end + 1;
1.21 nicm 1055: }
1.1 nicm 1056: } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1057: while (*++cp != '\0' && more == UTF8_MORE)
1058: more = utf8_append(&ud, *cp);
1059: if (more == UTF8_DONE)
1060: width += ud.width;
1061: else
1062: cp -= ud.have;
1063: } else if (*cp > 0x1f && *cp < 0x7f) {
1064: width++;
1065: cp++;
1.10 nicm 1066: } else
1067: cp++;
1.1 nicm 1068: }
1069: return (width);
1070: }
1071:
1.21 nicm 1072: /*
1073: * Trim on the left, taking #[] into account. Note, we copy the whole set of
1074: * unescaped #s, but only add their escaped size to width. This is because the
1075: * format_draw function will actually do the escaping when it runs
1076: */
1.1 nicm 1077: char *
1078: format_trim_left(const char *expanded, u_int limit)
1079: {
1080: char *copy, *out;
1081: const char *cp = expanded, *end;
1.24 nicm 1082: u_int n, width = 0, leading_width;
1.1 nicm 1083: struct utf8_data ud;
1084: enum utf8_state more;
1085:
1.21 nicm 1086: out = copy = xcalloc(1, strlen(expanded) + 1);
1.1 nicm 1087: while (*cp != '\0') {
1.21 nicm 1088: if (width >= limit)
1089: break;
1090: if (*cp == '#') {
1.24 nicm 1091: end = format_leading_hashes(cp, &n, &leading_width);
1092: if (leading_width > limit - width)
1093: leading_width = limit - width;
1094: if (leading_width != 0) {
1095: if (n == 1)
1096: *out++ = '#';
1097: else {
1098: memset(out, '#', 2 * leading_width);
1099: out += 2 * leading_width;
1100: }
1101: width += leading_width;
1.21 nicm 1102: }
1.24 nicm 1103: cp = end;
1104: if (*cp == '#') {
1105: end = format_skip(cp + 2, "]");
1106: if (end == NULL)
1107: break;
1108: memcpy(out, cp, end + 1 - cp);
1109: out += (end + 1 - cp);
1.21 nicm 1110: cp = end + 1;
1111: }
1.1 nicm 1112: } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1113: while (*++cp != '\0' && more == UTF8_MORE)
1114: more = utf8_append(&ud, *cp);
1115: if (more == UTF8_DONE) {
1116: if (width + ud.width <= limit) {
1117: memcpy(out, ud.data, ud.size);
1118: out += ud.size;
1119: }
1120: width += ud.width;
1.14 nicm 1121: } else {
1.1 nicm 1122: cp -= ud.have;
1.14 nicm 1123: cp++;
1124: }
1.1 nicm 1125: } else if (*cp > 0x1f && *cp < 0x7f) {
1126: if (width + 1 <= limit)
1127: *out++ = *cp;
1128: width++;
1129: cp++;
1.8 nicm 1130: } else
1131: cp++;
1.1 nicm 1132: }
1133: *out = '\0';
1134: return (copy);
1135: }
1136:
1137: /* Trim on the right, taking #[] into account. */
1138: char *
1139: format_trim_right(const char *expanded, u_int limit)
1140: {
1141: char *copy, *out;
1142: const char *cp = expanded, *end;
1.24 nicm 1143: u_int width = 0, total_width, skip, n;
1144: u_int leading_width, copy_width;
1.1 nicm 1145: struct utf8_data ud;
1146: enum utf8_state more;
1147:
1148: total_width = format_width(expanded);
1149: if (total_width <= limit)
1150: return (xstrdup(expanded));
1151: skip = total_width - limit;
1152:
1.21 nicm 1153: out = copy = xcalloc(1, strlen(expanded) + 1);
1.1 nicm 1154: while (*cp != '\0') {
1.21 nicm 1155: if (*cp == '#') {
1.24 nicm 1156: end = format_leading_hashes(cp, &n, &leading_width);
1.26 ! nicm 1157: copy_width = leading_width;
1.21 nicm 1158: if (width <= skip) {
1.26 ! nicm 1159: if (skip - width >= copy_width)
1.24 nicm 1160: copy_width = 0;
1.21 nicm 1161: else
1.24 nicm 1162: copy_width -= (skip - width);
1.26 ! nicm 1163: }
1.24 nicm 1164: if (copy_width != 0) {
1165: if (n == 1)
1166: *out++ = '#';
1167: else {
1168: memset(out, '#', 2 * copy_width);
1169: out += 2 * copy_width;
1170: }
1.21 nicm 1171: }
1.24 nicm 1172: width += leading_width;
1173: cp = end;
1174: if (*cp == '#') {
1175: end = format_skip(cp + 2, "]");
1176: if (end == NULL)
1177: break;
1178: memcpy(out, cp, end + 1 - cp);
1179: out += (end + 1 - cp);
1180: cp = end + 1;
1.21 nicm 1181: }
1.1 nicm 1182: } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1183: while (*++cp != '\0' && more == UTF8_MORE)
1184: more = utf8_append(&ud, *cp);
1185: if (more == UTF8_DONE) {
1186: if (width >= skip) {
1187: memcpy(out, ud.data, ud.size);
1188: out += ud.size;
1189: }
1190: width += ud.width;
1.14 nicm 1191: } else {
1.1 nicm 1192: cp -= ud.have;
1.14 nicm 1193: cp++;
1194: }
1.1 nicm 1195: } else if (*cp > 0x1f && *cp < 0x7f) {
1196: if (width >= skip)
1197: *out++ = *cp;
1198: width++;
1199: cp++;
1.8 nicm 1200: } else
1201: cp++;
1.1 nicm 1202: }
1203: *out = '\0';
1204: return (copy);
1205: }