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