4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
34 enum style_range_type type
;
38 TAILQ_ENTRY(format_range
) entry
;
40 TAILQ_HEAD(format_ranges
, format_range
);
42 /* Does this range match this style? */
44 format_is_type(struct format_range
*fr
, struct style
*sy
)
46 if (fr
->type
!= sy
->range_type
)
49 case STYLE_RANGE_NONE
:
50 case STYLE_RANGE_LEFT
:
51 case STYLE_RANGE_RIGHT
:
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);
65 format_free_range(struct format_ranges
*frs
, struct format_range
*fr
)
67 TAILQ_REMOVE(frs
, fr
, entry
);
71 /* Fix range positions. */
73 format_update_ranges(struct format_ranges
*frs
, struct screen
*s
, u_int offset
,
74 u_int start
, u_int width
)
76 struct format_range
*fr
, *fr1
;
81 TAILQ_FOREACH_SAFE(fr
, frs
, entry
, fr1
) {
85 if (fr
->end
<= start
|| fr
->start
>= start
+ width
) {
86 format_free_range(frs
, fr
);
90 if (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
);
107 /* Draw a part of the format. */
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
,
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.
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
);
122 /* Draw list part of format. */
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
)
129 u_int start
, focus_centre
;
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
);
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)
142 start
= focus_centre
- width
/ 2;
143 if (start
+ width
> list
->cx
)
144 start
= list
->cx
- width
;
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
;
154 if (start
+ width
< list
->cx
&& width
> list_right
->cx
) {
155 screen_write_cursormove(octx
, ocx
+ offset
+ width
-
156 list_right
->cx
, ocy
, 0);
157 screen_write_fast_copy(octx
, list_right
, 0, 0, list_right
->cx
,
159 width
-= list_right
->cx
;
162 /* Draw the list screen itself. */
163 format_draw_put(octx
, ocx
, ocy
, list
, frs
, offset
, start
, width
);
166 /* Draw format with no list. */
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
,
170 struct screen
*abs_centre
, struct format_ranges
*frs
)
172 u_int width_left
, width_centre
, width_right
, width_abs_centre
;
174 width_left
= left
->cx
;
175 width_centre
= centre
->cx
;
176 width_right
= right
->cx
;
177 width_abs_centre
= abs_centre
->cx
;
180 * Try to keep as much of the left and right as possible at the expense
183 while (width_left
+ width_centre
+ width_right
> available
) {
184 if (width_centre
> 0)
186 else if (width_right
> 0)
193 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
202 * Write centre halfway between
205 * available - width_right.
207 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
209 + ((available
- width_right
) - width_left
) / 2
211 centre
->cx
/ 2 - width_centre
/ 2,
215 * Write abs_centre in the perfect centre of all horizontal space.
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,
225 /* Draw format with list on the left. */
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
,
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
)
233 u_int width_left
, width_centre
, width_right
;
234 u_int width_list
, width_after
, width_abs_centre
;
235 struct screen_write_ctx ctx
;
237 width_left
= left
->cx
;
238 width_centre
= centre
->cx
;
239 width_right
= right
->cx
;
240 width_abs_centre
= abs_centre
->cx
;
241 width_list
= list
->cx
;
242 width_after
= after
->cx
;
245 * Trim first the centre, then the list, then the right, then after the
246 * list, then the left.
252 width_after
> available
) {
253 if (width_centre
> 0)
255 else if (width_list
> 0)
257 else if (width_right
> 0)
259 else if (width_after
> 0)
265 /* If there is no list left, pass off to the no list function. */
266 if (width_list
== 0) {
267 screen_write_start(&ctx
, left
);
268 screen_write_fast_copy(&ctx
, after
, 0, 0, width_after
, 1);
269 screen_write_stop(&ctx
);
271 format_draw_none(octx
, available
, ocx
, ocy
, left
, centre
,
272 right
, abs_centre
, frs
);
276 /* Write left at 0. */
277 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
285 /* Write after at width_left + width_list. */
286 format_draw_put(octx
, ocx
, ocy
, after
, frs
,
287 width_left
+ width_list
,
292 * Write centre halfway between
293 * width_left + width_list + width_after
295 * available - width_right.
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
302 centre
->cx
/ 2 - width_centre
/ 2,
306 * The list now goes from
309 * width_left + width_list.
310 * If there is no focus given, keep the left in focus.
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
);
318 * Write abs_centre in the perfect centre of all horizontal space.
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,
328 /* Draw format with list in the centre. */
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
,
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
)
336 u_int width_left
, width_centre
, width_right
, middle
;
337 u_int width_list
, width_after
, width_abs_centre
;
338 struct screen_write_ctx ctx
;
340 width_left
= left
->cx
;
341 width_centre
= centre
->cx
;
342 width_right
= right
->cx
;
343 width_abs_centre
= abs_centre
->cx
;
344 width_list
= list
->cx
;
345 width_after
= after
->cx
;
348 * Trim first the list, then after the list, then the centre, then the
349 * right, then the left.
355 width_after
> available
) {
358 else if (width_after
> 0)
360 else if (width_centre
> 0)
362 else if (width_right
> 0)
368 /* If there is no list left, pass off to the no list function. */
369 if (width_list
== 0) {
370 screen_write_start(&ctx
, centre
);
371 screen_write_fast_copy(&ctx
, after
, 0, 0, width_after
, 1);
372 screen_write_stop(&ctx
);
374 format_draw_none(octx
, available
, ocx
, ocy
, left
, centre
,
375 right
, abs_centre
, frs
);
379 /* Write left at 0. */
380 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
389 * All three centre sections are offset from the middle of the
392 middle
= (width_left
+ ((available
- width_right
) - width_left
) / 2);
396 * middle - width_list / 2 - width_centre.
398 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
399 middle
- width_list
/ 2 - width_centre
,
405 * middle - width_list / 2 + width_list
407 format_draw_put(octx
, ocx
, ocy
, after
, frs
,
408 middle
- width_list
/ 2 + width_list
,
413 * The list now goes from
414 * middle - width_list / 2
416 * middle + width_list / 2
417 * If there is no focus given, keep the centre in focus.
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
,
426 * Write abs_centre in the perfect centre of all horizontal space.
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,
436 /* Draw format with list on the right. */
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
,
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
)
444 u_int width_left
, width_centre
, width_right
;
445 u_int width_list
, width_after
, width_abs_centre
;
446 struct screen_write_ctx ctx
;
448 width_left
= left
->cx
;
449 width_centre
= centre
->cx
;
450 width_right
= right
->cx
;
451 width_abs_centre
= abs_centre
->cx
;
452 width_list
= list
->cx
;
453 width_after
= after
->cx
;
456 * Trim first the centre, then the list, then the right, then
457 * after the list, then the left.
463 width_after
> available
) {
464 if (width_centre
> 0)
466 else if (width_list
> 0)
468 else if (width_right
> 0)
470 else if (width_after
> 0)
476 /* If there is no list left, pass off to the no list function. */
477 if (width_list
== 0) {
478 screen_write_start(&ctx
, right
);
479 screen_write_fast_copy(&ctx
, after
, 0, 0, width_after
, 1);
480 screen_write_stop(&ctx
);
482 format_draw_none(octx
, available
, ocx
, ocy
, left
, centre
,
483 right
, abs_centre
, frs
);
487 /* Write left at 0. */
488 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
498 * available - width_right - width_list - width_after.
500 format_draw_put(octx
, ocx
, ocy
, right
, frs
,
501 available
- width_right
- width_list
- width_after
,
506 * Write centre halfway between
509 * available - width_right - width_list - width_after.
511 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
513 + ((available
- width_right
- width_list
- width_after
)
516 centre
->cx
/ 2 - width_centre
/ 2,
520 * The list now goes from
521 * available - width_list - width_after
523 * available - width_after
524 * If there is no focus given, keep the right in focus.
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
,
533 * Write abs_centre in the perfect centre of all horizontal space.
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,
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
)
550 u_int width_left
, width_centre
, width_right
, width_abs_centre
;
551 u_int width_list
, width_after
, middle
, abs_centre_offset
;
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
;
561 * Trim first centre, then the right, then the left.
565 width_right
> available
) {
566 if (width_centre
> 0)
568 else if (width_right
> 0)
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
579 while (width_list
+ width_after
+ width_abs_centre
> available
) {
582 else if (width_after
> 0)
588 /* Write left at 0. */
589 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
598 * Keep writing centre at the relative centre. Only the list is written
599 * in the absolute centre of the horizontal space.
601 middle
= (width_left
+ ((available
- width_right
) - width_left
) / 2);
605 * middle - width_centre.
607 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
608 middle
- width_centre
,
613 * If there is no focus given, keep the centre in focus.
615 if (focus_start
== -1 || focus_end
== -1)
616 focus_start
= focus_end
= list
->cx
/ 2;
619 * We centre abs_centre and the list together, so their shared centre is
620 * in the perfect centre of horizontal space.
622 abs_centre_offset
= (available
- width_list
- width_abs_centre
) / 2;
625 * Write abs_centre before the list.
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
;
632 * Draw the list in the absolute centre
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
;
639 * Write after at the end of the centre
641 format_draw_put(octx
, ocx
, ocy
, after
, frs
, abs_centre_offset
, 0,
645 /* Get width and count of any leading #s. */
647 format_leading_hashes(const char *cp
, u_int
*n
, u_int
*width
)
649 for (*n
= 0; cp
[*n
] == '#'; (*n
)++)
659 *width
= (*n
/ 2) + 1;
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
671 /* This is a style, so return pointing to the #. */
672 return (cp
+ *n
- 1);
675 /* Draw multiple characters. */
677 format_draw_many(struct screen_write_ctx
*ctx
, struct style
*sy
, char ch
,
682 utf8_set(&sy
->gc
.data
, ch
);
683 for (i
= 0; i
< n
; i
++)
684 screen_write_cell(ctx
, &sy
->gc
);
687 /* Draw a format to a screen. */
689 format_draw(struct screen_write_ctx
*octx
, const struct grid_cell
*base
,
690 u_int available
, const char *expanded
, struct style_ranges
*srs
,
701 TOTAL
} current
= LEFT
, last
= LEFT
;
702 const char *names
[] = { "LEFT",
710 size_t size
= strlen(expanded
);
711 struct screen
*os
= octx
->s
, s
[TOTAL
];
712 struct screen_write_ctx ctx
[TOTAL
];
713 u_int ocx
= os
->cx
, ocy
= os
->cy
, n
, i
, width
[TOTAL
];
714 u_int map
[] = { LEFT
,
719 int focus_start
= -1, focus_end
= -1;
720 int list_state
= -1, fill
= -1, even
;
721 enum style_align list_align
= STYLE_ALIGN_DEFAULT
;
722 struct grid_cell gc
, current_default
;
723 struct style sy
, saved_sy
;
724 struct utf8_data
*ud
= &sy
.gc
.data
;
725 const char *cp
, *end
;
726 enum utf8_state more
;
728 struct format_range
*fr
= NULL
, *fr1
;
729 struct format_ranges frs
;
730 struct style_range
*sr
;
732 memcpy(¤t_default
, base
, sizeof current_default
);
733 style_set(&sy
, ¤t_default
);
735 log_debug("%s: %s", __func__
, expanded
);
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
742 for (i
= 0; i
< TOTAL
; i
++) {
743 screen_init(&s
[i
], size
, 1, 0);
744 screen_write_start(&ctx
[i
], &s
[i
]);
745 screen_write_clearendofline(&ctx
[i
], current_default
.bg
);
750 * Walk the string and add to the corresponding screens,
751 * parsing styles as we go.
754 while (*cp
!= '\0') {
755 /* Handle sequences of #. */
756 if (cp
[0] == '#' && cp
[1] != '[' && cp
[1] != '\0') {
757 for (n
= 1; cp
[n
] == '#'; n
++)
759 even
= ((n
% 2) == 0);
767 format_draw_many(&ctx
[current
], &sy
, '#', n
);
776 format_draw_many(&ctx
[current
], &sy
, '#', n
/ 2);
777 width
[current
] += (n
/ 2);
780 screen_write_cell(&ctx
[current
], &sy
.gc
);
786 /* Is this not a style? */
787 if (cp
[0] != '#' || cp
[1] != '[' || sy
.ignore
) {
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
)
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. */
807 /* Draw the cell to the current screen. */
808 screen_write_cell(&ctx
[current
], &sy
.gc
);
809 width
[current
] += ud
->width
;
813 /* This is a style. Work out where the end is and parse it. */
814 end
= format_skip(cp
+ 2, "]");
816 log_debug("%s: no terminating ] at '%s'", __func__
,
818 TAILQ_FOREACH_SAFE(fr
, &frs
, entry
, fr1
)
819 format_free_range(&frs
, fr
);
822 tmp
= xstrndup(cp
+ 2, end
- (cp
+ 2));
823 style_copy(&saved_sy
, &sy
);
824 if (style_parse(&sy
, ¤t_default
, tmp
) != 0) {
825 log_debug("%s: invalid style '%s'", __func__
, tmp
);
830 log_debug("%s: style '%s' -> '%s'", __func__
, tmp
,
831 style_tostring(&sy
));
833 if (default_colours
) {
838 /* If this style has a fill colour, store it for later. */
842 /* If this style pushed or popped the default, update it. */
843 if (sy
.default_type
== STYLE_DEFAULT_PUSH
) {
844 memcpy(¤t_default
, &saved_sy
.gc
,
845 sizeof current_default
);
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
;
852 /* Check the list state. */
856 * Entering the list, exiting a marker, or exiting the
859 if (list_state
!= 0) {
860 if (fr
!= NULL
) { /* abort any region */
865 list_align
= sy
.align
;
868 /* End the focus if started. */
869 if (focus_start
!= -1 && focus_end
== -1)
870 focus_end
= s
[LIST
].cx
;
874 case STYLE_LIST_FOCUS
:
875 /* Entering the focus. */
876 if (list_state
!= 0) /* not inside the list */
878 if (focus_start
== -1) /* focus already started */
879 focus_start
= s
[LIST
].cx
;
882 /* Exiting or outside the list. */
883 if (list_state
== 0) {
884 if (fr
!= NULL
) { /* abort any region */
888 if (focus_start
!= -1 && focus_end
== -1)
889 focus_end
= s
[LIST
].cx
;
891 map
[list_align
] = AFTER
;
892 if (list_align
== STYLE_ALIGN_LEFT
)
893 map
[STYLE_ALIGN_DEFAULT
] = AFTER
;
896 current
= map
[sy
.align
];
898 case STYLE_LIST_LEFT_MARKER
:
899 /* Entering left marker. */
900 if (list_state
!= 0) /* not inside the list */
902 if (s
[LIST_LEFT
].cx
!= 0) /* already have marker */
904 if (fr
!= NULL
) { /* abort any region */
908 if (focus_start
!= -1 && focus_end
== -1)
909 focus_start
= focus_end
= -1;
912 case STYLE_LIST_RIGHT_MARKER
:
913 /* Entering right marker. */
914 if (list_state
!= 0) /* not inside the list */
916 if (s
[LIST_RIGHT
].cx
!= 0) /* already have marker */
918 if (fr
!= NULL
) { /* abort any region */
922 if (focus_start
!= -1 && focus_end
== -1)
923 focus_start
= focus_end
= -1;
924 current
= LIST_RIGHT
;
927 if (current
!= last
) {
928 log_debug("%s: change %s -> %s", __func__
,
929 names
[last
], names
[current
]);
934 * Check if the range style has changed and if so end the
935 * current range and start a new one if needed.
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
);
946 if (fr
== NULL
&& sy
.range_type
!= STYLE_RANGE_NONE
) {
947 fr
= xcalloc(1, sizeof *fr
);
951 fr
->start
= s
[current
].cx
;
953 fr
->type
= sy
.range_type
;
954 fr
->argument
= sy
.range_argument
;
955 strlcpy(fr
->string
, sy
.range_string
,
964 for (i
= 0; i
< TOTAL
; i
++) {
965 screen_write_stop(&ctx
[i
]);
966 log_debug("%s: width %s is %u", __func__
, names
[i
], width
[i
]);
968 if (focus_start
!= -1 && focus_end
!= -1)
969 log_debug("%s: focus %d-%d", __func__
, focus_start
, focus_end
);
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
);
975 /* Clear the available area. */
977 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
979 for (i
= 0; i
< available
; i
++)
980 screen_write_putc(octx
, &gc
, ' ');
984 * Draw the screens. How they are arranged depends on where the list
987 switch (list_align
) {
988 case STYLE_ALIGN_DEFAULT
:
990 format_draw_none(octx
, available
, ocx
, ocy
, &s
[LEFT
],
991 &s
[CENTRE
], &s
[RIGHT
], &s
[ABSOLUTE_CENTRE
], &frs
);
993 case STYLE_ALIGN_LEFT
:
994 /* List is part of the left. */
995 format_draw_left(octx
, available
, ocx
, ocy
, &s
[LEFT
],
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
);
1000 case STYLE_ALIGN_CENTRE
:
1001 /* List is part of the centre. */
1002 format_draw_centre(octx
, available
, ocx
, ocy
, &s
[LEFT
],
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
);
1007 case STYLE_ALIGN_RIGHT
:
1008 /* List is part of the right. */
1009 format_draw_right(octx
, available
, ocx
, ocy
, &s
[LEFT
],
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
);
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
);
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
;
1028 strlcpy(sr
->string
, fr
->string
, sizeof sr
->string
);
1029 sr
->start
= fr
->start
;
1031 TAILQ_INSERT_TAIL(srs
, sr
, entry
);
1034 case STYLE_RANGE_NONE
:
1036 case STYLE_RANGE_LEFT
:
1037 log_debug("%s: range left at %u-%u", __func__
,
1038 sr
->start
, sr
->end
);
1040 case STYLE_RANGE_RIGHT
:
1041 log_debug("%s: range right at %u-%u", __func__
,
1042 sr
->start
, sr
->end
);
1044 case STYLE_RANGE_PANE
:
1045 log_debug("%s: range pane|%%%u at %u-%u", __func__
,
1046 sr
->argument
, sr
->start
, sr
->end
);
1048 case STYLE_RANGE_WINDOW
:
1049 log_debug("%s: range window|%u at %u-%u", __func__
,
1050 sr
->argument
, sr
->start
, sr
->end
);
1052 case STYLE_RANGE_SESSION
:
1053 log_debug("%s: range session|$%u at %u-%u", __func__
,
1054 sr
->argument
, sr
->start
, sr
->end
);
1056 case STYLE_RANGE_USER
:
1057 log_debug("%s: range user|%u at %u-%u", __func__
,
1058 sr
->argument
, sr
->start
, sr
->end
);
1061 format_free_range(&frs
, fr
);
1065 /* Free the screens. */
1066 for (i
= 0; i
< TOTAL
; i
++)
1069 /* Restore the original cursor position. */
1070 screen_write_cursormove(octx
, ocx
, ocy
, 0);
1073 /* Get width, taking #[] into account. */
1075 format_width(const char *expanded
)
1077 const char *cp
, *end
;
1078 u_int n
, leading_width
, width
= 0;
1079 struct utf8_data ud
;
1080 enum utf8_state more
;
1083 while (*cp
!= '\0') {
1085 end
= format_leading_hashes(cp
, &n
, &leading_width
);
1086 width
+= leading_width
;
1089 end
= format_skip(cp
+ 2, "]");
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
)
1101 } else if (*cp
> 0x1f && *cp
< 0x7f) {
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
1116 format_trim_left(const char *expanded
, u_int limit
)
1119 const char *cp
= expanded
, *end
;
1120 u_int n
, width
= 0, leading_width
;
1121 struct utf8_data ud
;
1122 enum utf8_state more
;
1124 out
= copy
= xcalloc(2, strlen(expanded
) + 1);
1125 while (*cp
!= '\0') {
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) {
1136 memset(out
, '#', 2 * leading_width
);
1137 out
+= 2 * leading_width
;
1139 width
+= leading_width
;
1143 end
= format_skip(cp
+ 2, "]");
1146 memcpy(out
, cp
, end
+ 1 - cp
);
1147 out
+= (end
+ 1 - cp
);
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
);
1163 } else if (*cp
> 0x1f && *cp
< 0x7f) {
1164 if (width
+ 1 <= limit
)
1175 /* Trim on the right, taking #[] into account. */
1177 format_trim_right(const char *expanded
, u_int limit
)
1180 const char *cp
= expanded
, *end
;
1181 u_int width
= 0, total_width
, skip
, n
;
1182 u_int leading_width
, copy_width
;
1183 struct utf8_data ud
;
1184 enum utf8_state more
;
1186 total_width
= format_width(expanded
);
1187 if (total_width
<= limit
)
1188 return (xstrdup(expanded
));
1189 skip
= total_width
- limit
;
1191 out
= copy
= xcalloc(2, strlen(expanded
) + 1);
1192 while (*cp
!= '\0') {
1194 end
= format_leading_hashes(cp
, &n
, &leading_width
);
1195 copy_width
= leading_width
;
1196 if (width
<= skip
) {
1197 if (skip
- width
>= copy_width
)
1200 copy_width
-= (skip
- width
);
1202 if (copy_width
!= 0) {
1206 memset(out
, '#', 2 * copy_width
);
1207 out
+= 2 * copy_width
;
1210 width
+= leading_width
;
1213 end
= format_skip(cp
+ 2, "]");
1216 memcpy(out
, cp
, end
+ 1 - cp
);
1217 out
+= (end
+ 1 - cp
);
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
);
1233 } else if (*cp
> 0x1f && *cp
< 0x7f) {