Annotation of src/usr.bin/tmux/format-draw.c, Revision 1.23
1.23 ! nicm 1: /* $OpenBSD: format-draw.c,v 1.22 2020/12/01 08:12:58 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.21 nicm 635: /* Draw multiple characters. */
636: static void
637: format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
638: u_int n)
639: {
640: u_int i;
641:
642: utf8_set(&sy->gc.data, ch);
643: for (i = 0; i < n; i++)
644: screen_write_cell(ctx, &sy->gc);
645: }
646:
1.1 nicm 647: /* Draw a format to a screen. */
648: void
649: format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
650: u_int available, const char *expanded, struct style_ranges *srs)
651: {
652: enum { LEFT,
653: CENTRE,
654: RIGHT,
1.23 ! nicm 655: ABSOLUTE_CENTRE,
1.1 nicm 656: LIST,
657: LIST_LEFT,
658: LIST_RIGHT,
659: AFTER,
660: TOTAL } current = LEFT, last = LEFT;
661: const char *names[] = { "LEFT",
662: "CENTRE",
663: "RIGHT",
1.23 ! nicm 664: "ABSOLUTE_CENTRE",
1.1 nicm 665: "LIST",
666: "LIST_LEFT",
667: "LIST_RIGHT",
668: "AFTER" };
669: size_t size = strlen(expanded);
670: struct screen *os = octx->s, s[TOTAL];
671: struct screen_write_ctx ctx[TOTAL];
1.21 nicm 672: u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
1.23 ! nicm 673: u_int map[] = { LEFT,
! 674: LEFT,
! 675: CENTRE,
! 676: RIGHT,
! 677: ABSOLUTE_CENTRE };
1.1 nicm 678: int focus_start = -1, focus_end = -1;
1.21 nicm 679: int list_state = -1, fill = -1, even;
1.1 nicm 680: enum style_align list_align = STYLE_ALIGN_DEFAULT;
1.13 nicm 681: struct grid_cell gc, current_default;
682: struct style sy, saved_sy;
1.1 nicm 683: struct utf8_data *ud = &sy.gc.data;
684: const char *cp, *end;
685: enum utf8_state more;
686: char *tmp;
687: struct format_range *fr = NULL, *fr1;
688: struct format_ranges frs;
689: struct style_range *sr;
690:
1.13 nicm 691: memcpy(¤t_default, base, sizeof current_default);
692: style_set(&sy, ¤t_default);
1.1 nicm 693: TAILQ_INIT(&frs);
1.3 nicm 694: log_debug("%s: %s", __func__, expanded);
1.1 nicm 695:
696: /*
697: * We build three screens for left, right, centre alignment, one for
698: * the list, one for anything after the list and two for the list left
699: * and right markers.
700: */
701: for (i = 0; i < TOTAL; i++) {
702: screen_init(&s[i], size, 1, 0);
1.18 nicm 703: screen_write_start(&ctx[i], &s[i]);
1.13 nicm 704: screen_write_clearendofline(&ctx[i], current_default.bg);
1.1 nicm 705: width[i] = 0;
706: }
707:
708: /*
709: * Walk the string and add to the corresponding screens,
710: * parsing styles as we go.
711: */
712: cp = expanded;
713: while (*cp != '\0') {
1.21 nicm 714: /* Handle sequences of #. */
715: if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
716: for (n = 1; cp[n] == '#'; n++)
717: /* nothing */;
1.22 nicm 718: even = ((n % 2) == 0);
1.21 nicm 719: if (cp[n] != '[') {
1.22 nicm 720: cp += n;
721: if (even)
722: n = (n / 2);
723: else
724: n = (n / 2) + 1;
1.21 nicm 725: width[current] += n;
726: format_draw_many(&ctx[current], &sy, '#', n);
727: continue;
728: }
729: if (even)
730: cp += (n + 1);
731: else
732: cp += (n - 1);
733: if (sy.ignore)
734: continue;
735: format_draw_many(&ctx[current], &sy, '#', n / 2);
736: width[current] += (n / 2);
737: if (even) {
738: utf8_set(ud, '[');
739: screen_write_cell(&ctx[current], &sy.gc);
740: width[current]++;
741: }
742: continue;
743: }
744:
745: /* Is this not a style? */
1.19 nicm 746: if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
1.1 nicm 747: /* See if this is a UTF-8 character. */
748: if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
749: while (*++cp != '\0' && more == UTF8_MORE)
750: more = utf8_append(ud, *cp);
751: if (more != UTF8_DONE)
752: cp -= ud->have;
753: }
754:
755: /* Not a UTF-8 character - ASCII or not valid. */
756: if (more != UTF8_DONE) {
757: if (*cp < 0x20 || *cp > 0x7e) {
758: /* Ignore nonprintable characters. */
759: cp++;
760: continue;
761: }
762: utf8_set(ud, *cp);
763: cp++;
764: }
765:
1.12 nicm 766: /* Draw the cell to the current screen. */
1.1 nicm 767: screen_write_cell(&ctx[current], &sy.gc);
768: width[current] += ud->width;
769: continue;
770: }
771:
772: /* This is a style. Work out where the end is and parse it. */
773: end = format_skip(cp + 2, "]");
1.3 nicm 774: if (end == NULL) {
1.6 nicm 775: log_debug("%s: no terminating ] at '%s'", __func__,
776: cp + 2);
1.5 nicm 777: TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
778: format_free_range(&frs, fr);
779: goto out;
1.3 nicm 780: }
1.1 nicm 781: tmp = xstrndup(cp + 2, end - (cp + 2));
1.13 nicm 782: style_copy(&saved_sy, &sy);
783: if (style_parse(&sy, ¤t_default, tmp) != 0) {
1.6 nicm 784: log_debug("%s: invalid style '%s'", __func__, tmp);
1.1 nicm 785: free(tmp);
1.4 nicm 786: cp = end + 1;
787: continue;
1.1 nicm 788: }
1.6 nicm 789: log_debug("%s: style '%s' -> '%s'", __func__, tmp,
790: style_tostring(&sy));
1.1 nicm 791: free(tmp);
792:
1.11 nicm 793: /* If this style has a fill colour, store it for later. */
794: if (sy.fill != 8)
795: fill = sy.fill;
1.13 nicm 796:
797: /* If this style pushed or popped the default, update it. */
798: if (sy.default_type == STYLE_DEFAULT_PUSH) {
1.20 nicm 799: memcpy(¤t_default, &saved_sy.gc,
800: sizeof current_default);
1.13 nicm 801: sy.default_type = STYLE_DEFAULT_BASE;
802: } else if (sy.default_type == STYLE_DEFAULT_POP) {
803: memcpy(¤t_default, base, sizeof current_default);
804: sy.default_type = STYLE_DEFAULT_BASE;
805: }
1.11 nicm 806:
1.1 nicm 807: /* Check the list state. */
808: switch (sy.list) {
809: case STYLE_LIST_ON:
810: /*
811: * Entering the list, exiting a marker, or exiting the
812: * focus.
813: */
814: if (list_state != 0) {
815: if (fr != NULL) { /* abort any region */
816: free(fr);
817: fr = NULL;
818: }
819: list_state = 0;
820: list_align = sy.align;
821: }
822:
823: /* End the focus if started. */
824: if (focus_start != -1 && focus_end == -1)
825: focus_end = s[LIST].cx;
826:
827: current = LIST;
828: break;
829: case STYLE_LIST_FOCUS:
830: /* Entering the focus. */
831: if (list_state != 0) /* not inside the list */
832: break;
833: if (focus_start == -1) /* focus already started */
834: focus_start = s[LIST].cx;
835: break;
836: case STYLE_LIST_OFF:
837: /* Exiting or outside the list. */
838: if (list_state == 0) {
839: if (fr != NULL) { /* abort any region */
840: free(fr);
841: fr = NULL;
842: }
843: if (focus_start != -1 && focus_end == -1)
844: focus_end = s[LIST].cx;
845:
846: map[list_align] = AFTER;
847: if (list_align == STYLE_ALIGN_LEFT)
848: map[STYLE_ALIGN_DEFAULT] = AFTER;
849: list_state = 1;
850: }
851: current = map[sy.align];
852: break;
853: case STYLE_LIST_LEFT_MARKER:
854: /* Entering left marker. */
855: if (list_state != 0) /* not inside the list */
856: break;
857: if (s[LIST_LEFT].cx != 0) /* already have marker */
858: break;
859: if (fr != NULL) { /* abort any region */
860: free(fr);
861: fr = NULL;
862: }
863: if (focus_start != -1 && focus_end == -1)
864: focus_start = focus_end = -1;
865: current = LIST_LEFT;
866: break;
867: case STYLE_LIST_RIGHT_MARKER:
868: /* Entering right marker. */
869: if (list_state != 0) /* not inside the list */
870: break;
871: if (s[LIST_RIGHT].cx != 0) /* already have marker */
872: break;
873: if (fr != NULL) { /* abort any region */
874: free(fr);
875: fr = NULL;
876: }
877: if (focus_start != -1 && focus_end == -1)
878: focus_start = focus_end = -1;
879: current = LIST_RIGHT;
880: break;
881: }
882: if (current != last) {
883: log_debug("%s: change %s -> %s", __func__,
884: names[last], names[current]);
885: last = current;
886: }
887:
888: /*
889: * Check if the range style has changed and if so end the
890: * current range and start a new one if needed.
891: */
892: if (srs != NULL) {
893: if (fr != NULL && !format_is_type(fr, &sy)) {
894: if (s[current].cx != fr->start) {
895: fr->end = s[current].cx + 1;
896: TAILQ_INSERT_TAIL(&frs, fr, entry);
897: } else
898: free(fr);
899: fr = NULL;
900: }
901: if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
902: fr = xcalloc(1, sizeof *fr);
903: fr->index = current;
904:
905: fr->s = &s[current];
906: fr->start = s[current].cx;
907:
908: fr->type = sy.range_type;
909: fr->argument = sy.range_argument;
910: }
911: }
912:
913: cp = end + 1;
914: }
915: free(fr);
916:
1.2 nicm 917: for (i = 0; i < TOTAL; i++) {
918: screen_write_stop(&ctx[i]);
1.1 nicm 919: log_debug("%s: width %s is %u", __func__, names[i], width[i]);
1.2 nicm 920: }
1.1 nicm 921: if (focus_start != -1 && focus_end != -1)
1.6 nicm 922: log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
1.1 nicm 923: TAILQ_FOREACH(fr, &frs, entry) {
924: log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
925: fr->argument, names[fr->index], fr->start, fr->end);
1.11 nicm 926: }
927:
928: /* Clear the available area. */
929: if (fill != -1) {
930: memcpy(&gc, &grid_default_cell, sizeof gc);
931: gc.bg = fill;
932: for (i = 0; i < available; i++)
933: screen_write_putc(octx, &gc, ' ');
1.1 nicm 934: }
935:
936: /*
937: * Draw the screens. How they are arranged depends on where the list
1.17 nicm 938: * appears.
1.1 nicm 939: */
940: switch (list_align) {
941: case STYLE_ALIGN_DEFAULT:
942: /* No list. */
943: format_draw_none(octx, available, ocx, ocy, &s[LEFT],
1.23 ! nicm 944: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
1.1 nicm 945: break;
946: case STYLE_ALIGN_LEFT:
947: /* List is part of the left. */
948: format_draw_left(octx, available, ocx, ocy, &s[LEFT],
1.23 ! nicm 949: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
! 950: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
! 951: focus_start, focus_end, &frs);
1.1 nicm 952: break;
953: case STYLE_ALIGN_CENTRE:
954: /* List is part of the centre. */
955: format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
1.23 ! nicm 956: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
! 957: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
! 958: focus_start, focus_end, &frs);
1.1 nicm 959: break;
960: case STYLE_ALIGN_RIGHT:
961: /* List is part of the right. */
962: format_draw_right(octx, available, ocx, ocy, &s[LEFT],
1.23 ! nicm 963: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
! 964: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
! 965: focus_start, focus_end, &frs);
! 966: break;
! 967: case STYLE_ALIGN_ABSOLUTE_CENTRE:
! 968: /* List is in the centre of the entire horizontal space. */
! 969: format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
! 970: &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
! 971: &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
! 972: focus_start, focus_end, &frs);
1.1 nicm 973: break;
974: }
975:
976: /* Create ranges to return. */
977: TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
978: sr = xcalloc(1, sizeof *sr);
979: sr->type = fr->type;
980: sr->argument = fr->argument;
981: sr->start = fr->start;
982: sr->end = fr->end;
983: TAILQ_INSERT_TAIL(srs, sr, entry);
984:
985: log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
986: sr->argument, sr->start, sr->end);
987:
988: format_free_range(&frs, fr);
989: }
1.2 nicm 990:
1.5 nicm 991: out:
1.2 nicm 992: /* Free the screens. */
993: for (i = 0; i < TOTAL; i++)
994: screen_free(&s[i]);
1.1 nicm 995:
996: /* Restore the original cursor position. */
997: screen_write_cursormove(octx, ocx, ocy, 0);
998: }
999:
1000: /* Get width, taking #[] into account. */
1001: u_int
1002: format_width(const char *expanded)
1003: {
1004: const char *cp, *end;
1.21 nicm 1005: u_int n, width = 0;
1.1 nicm 1006: struct utf8_data ud;
1007: enum utf8_state more;
1008:
1009: cp = expanded;
1010: while (*cp != '\0') {
1.21 nicm 1011: if (*cp == '#') {
1012: for (n = 1; cp[n] == '#'; n++)
1013: /* nothing */;
1014: if (cp[n] != '[') {
1015: width += n;
1016: cp += n;
1017: continue;
1018: }
1019: width += (n / 2); /* one for each ## */
1020:
1021: if ((n % 2) == 0) {
1022: /*
1023: * An even number of #s means that all #s are
1024: * escaped, so not a style.
1025: */
1026: width++; /* one for the [ */
1027: cp += (n + 1);
1028: continue;
1029: }
1030: cp += (n - 1); /* point to the [ */
1031:
1.1 nicm 1032: end = format_skip(cp + 2, "]");
1033: if (end == NULL)
1.16 nicm 1034: return (0);
1.1 nicm 1035: cp = end + 1;
1036: } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1037: while (*++cp != '\0' && more == UTF8_MORE)
1038: more = utf8_append(&ud, *cp);
1039: if (more == UTF8_DONE)
1040: width += ud.width;
1041: else
1042: cp -= ud.have;
1043: } else if (*cp > 0x1f && *cp < 0x7f) {
1044: width++;
1045: cp++;
1.10 nicm 1046: } else
1047: cp++;
1.1 nicm 1048: }
1049: return (width);
1050: }
1051:
1.21 nicm 1052: /*
1053: * Trim on the left, taking #[] into account. Note, we copy the whole set of
1054: * unescaped #s, but only add their escaped size to width. This is because the
1055: * format_draw function will actually do the escaping when it runs
1056: */
1.1 nicm 1057: char *
1058: format_trim_left(const char *expanded, u_int limit)
1059: {
1060: char *copy, *out;
1061: const char *cp = expanded, *end;
1.21 nicm 1062: u_int even, n, width = 0;
1.1 nicm 1063: struct utf8_data ud;
1064: enum utf8_state more;
1065:
1.21 nicm 1066: out = copy = xcalloc(1, strlen(expanded) + 1);
1.1 nicm 1067: while (*cp != '\0') {
1.21 nicm 1068: if (width >= limit)
1069: break;
1070: if (*cp == '#') {
1071: for (end = cp + 1; *end == '#'; end++)
1072: /* nothing */;
1073: n = end - cp;
1074: if (*end != '[') {
1075: if (n > limit - width)
1076: n = limit - width;
1077: memcpy(out, cp, n);
1078: out += n;
1079: width += n;
1080: cp = end;
1081: continue;
1082: }
1083: even = ((n % 2) == 0);
1084:
1085: n /= 2;
1086: if (n > limit - width)
1087: n = limit - width;
1088: width += n;
1089: n *= 2;
1090: memcpy(out, cp, n);
1091: out += n;
1092:
1093: if (even) {
1094: if (width + 1 <= limit) {
1095: *out++ = '[';
1096: width++;
1097: }
1098: cp = end + 1;
1099: continue;
1100: }
1101: cp = end - 1;
1102:
1.1 nicm 1103: end = format_skip(cp + 2, "]");
1104: if (end == NULL)
1105: break;
1106: memcpy(out, cp, end + 1 - cp);
1107: out += (end + 1 - cp);
1108: cp = end + 1;
1109: } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1110: while (*++cp != '\0' && more == UTF8_MORE)
1111: more = utf8_append(&ud, *cp);
1112: if (more == UTF8_DONE) {
1113: if (width + ud.width <= limit) {
1114: memcpy(out, ud.data, ud.size);
1115: out += ud.size;
1116: }
1117: width += ud.width;
1.14 nicm 1118: } else {
1.1 nicm 1119: cp -= ud.have;
1.14 nicm 1120: cp++;
1121: }
1.1 nicm 1122: } else if (*cp > 0x1f && *cp < 0x7f) {
1123: if (width + 1 <= limit)
1124: *out++ = *cp;
1125: width++;
1126: cp++;
1.8 nicm 1127: } else
1128: cp++;
1.1 nicm 1129: }
1130: *out = '\0';
1131: return (copy);
1132: }
1133:
1134: /* Trim on the right, taking #[] into account. */
1135: char *
1136: format_trim_right(const char *expanded, u_int limit)
1137: {
1138: char *copy, *out;
1139: const char *cp = expanded, *end;
1.21 nicm 1140: u_int width = 0, total_width, skip, old_n, even, n;
1.1 nicm 1141: struct utf8_data ud;
1142: enum utf8_state more;
1143:
1144: total_width = format_width(expanded);
1145: if (total_width <= limit)
1146: return (xstrdup(expanded));
1147: skip = total_width - limit;
1148:
1.21 nicm 1149: out = copy = xcalloc(1, strlen(expanded) + 1);
1.1 nicm 1150: while (*cp != '\0') {
1.21 nicm 1151: if (*cp == '#') {
1152: for (end = cp + 1; *end == '#'; end++)
1153: /* nothing */;
1154: old_n = n = end - cp;
1155: if (*end != '[') {
1156: if (width <= skip) {
1157: if (skip - width >= n)
1158: n = 0;
1159: else
1160: n -= (skip - width);
1161: }
1162: if (n != 0) {
1163: memcpy(out, cp, n);
1164: out += n;
1165: }
1166:
1167: /*
1168: * The width always increases by the full
1169: * amount even if we can't copy anything yet.
1170: */
1171: width += old_n;
1172: cp = end;
1173: continue;
1174: }
1175: even = ((n % 2) == 0);
1176:
1177: n /= 2;
1178: if (width <= skip) {
1179: if (skip - width >= n)
1180: n = 0;
1181: else
1182: n -= (skip - width);
1183: }
1184: if (n != 0) {
1185: /*
1186: * Copy the full amount because it hasn't been
1187: * escaped yet.
1188: */
1189: memcpy(out, cp, old_n);
1190: out += old_n;
1191: }
1192: cp += old_n;
1193: width += (old_n / 2) - even;
1194:
1195: if (even) {
1196: if (width > skip)
1197: *out++ = '[';
1198: width++;
1199: continue;
1200: }
1201: cp = end - 1;
1202:
1.1 nicm 1203: end = format_skip(cp + 2, "]");
1.21 nicm 1204: if (end == NULL) {
1.1 nicm 1205: break;
1.21 nicm 1206: }
1.1 nicm 1207: memcpy(out, cp, end + 1 - cp);
1208: out += (end + 1 - cp);
1209: cp = end + 1;
1210: } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1211: while (*++cp != '\0' && more == UTF8_MORE)
1212: more = utf8_append(&ud, *cp);
1213: if (more == UTF8_DONE) {
1214: if (width >= skip) {
1215: memcpy(out, ud.data, ud.size);
1216: out += ud.size;
1217: }
1218: width += ud.width;
1.14 nicm 1219: } else {
1.1 nicm 1220: cp -= ud.have;
1.14 nicm 1221: cp++;
1222: }
1.1 nicm 1223: } else if (*cp > 0x1f && *cp < 0x7f) {
1224: if (width >= skip)
1225: *out++ = *cp;
1226: width++;
1227: cp++;
1.8 nicm 1228: } else
1229: cp++;
1.1 nicm 1230: }
1231: *out = '\0';
1232: return (copy);
1233: }