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
;
37 TAILQ_ENTRY(format_range
) entry
;
39 TAILQ_HEAD(format_ranges
, format_range
);
41 /* Does this range match this style? */
43 format_is_type(struct format_range
*fr
, struct style
*sy
)
45 if (fr
->type
!= sy
->range_type
)
47 if (fr
->type
== STYLE_RANGE_WINDOW
&&
48 fr
->argument
!= sy
->range_argument
)
55 format_free_range(struct format_ranges
*frs
, struct format_range
*fr
)
57 TAILQ_REMOVE(frs
, fr
, entry
);
61 /* Fix range positions. */
63 format_update_ranges(struct format_ranges
*frs
, struct screen
*s
, u_int offset
,
64 u_int start
, u_int width
)
66 struct format_range
*fr
, *fr1
;
71 TAILQ_FOREACH_SAFE(fr
, frs
, entry
, fr1
) {
75 if (fr
->end
<= start
|| fr
->start
>= start
+ width
) {
76 format_free_range(frs
, fr
);
80 if (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
);
97 /* Draw a part of the format. */
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
,
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.
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
);
112 /* Draw list part of format. */
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
)
119 u_int start
, focus_centre
;
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
);
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)
132 start
= focus_centre
- width
/ 2;
133 if (start
+ width
> list
->cx
)
134 start
= list
->cx
- width
;
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
;
144 if (start
+ width
< list
->cx
&& width
> list_right
->cx
) {
145 screen_write_cursormove(octx
, ocx
+ offset
+ width
-
146 list_right
->cx
, ocy
, 0);
147 screen_write_fast_copy(octx
, list_right
, 0, 0, list_right
->cx
,
149 width
-= list_right
->cx
;
152 /* Draw the list screen itself. */
153 format_draw_put(octx
, ocx
, ocy
, list
, frs
, offset
, start
, width
);
156 /* Draw format with no list. */
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
,
160 struct screen
*abs_centre
, struct format_ranges
*frs
)
162 u_int width_left
, width_centre
, width_right
, width_abs_centre
;
164 width_left
= left
->cx
;
165 width_centre
= centre
->cx
;
166 width_right
= right
->cx
;
167 width_abs_centre
= abs_centre
->cx
;
170 * Try to keep as much of the left and right as possible at the expense
173 while (width_left
+ width_centre
+ width_right
> available
) {
174 if (width_centre
> 0)
176 else if (width_right
> 0)
183 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
192 * Write centre halfway between
195 * available - width_right.
197 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
199 + ((available
- width_right
) - width_left
) / 2
201 centre
->cx
/ 2 - width_centre
/ 2,
205 * Write abs_centre in the perfect centre of all horizontal space.
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,
215 /* Draw format with list on the left. */
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
,
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
)
223 u_int width_left
, width_centre
, width_right
;
224 u_int width_list
, width_after
, width_abs_centre
;
225 struct screen_write_ctx ctx
;
227 width_left
= left
->cx
;
228 width_centre
= centre
->cx
;
229 width_right
= right
->cx
;
230 width_abs_centre
= abs_centre
->cx
;
231 width_list
= list
->cx
;
232 width_after
= after
->cx
;
235 * Trim first the centre, then the list, then the right, then after the
236 * list, then the left.
242 width_after
> available
) {
243 if (width_centre
> 0)
245 else if (width_list
> 0)
247 else if (width_right
> 0)
249 else if (width_after
> 0)
255 /* If there is no list left, pass off to the no list function. */
256 if (width_list
== 0) {
257 screen_write_start(&ctx
, left
);
258 screen_write_fast_copy(&ctx
, after
, 0, 0, width_after
, 1);
259 screen_write_stop(&ctx
);
261 format_draw_none(octx
, available
, ocx
, ocy
, left
, centre
,
262 right
, abs_centre
, frs
);
266 /* Write left at 0. */
267 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
275 /* Write after at width_left + width_list. */
276 format_draw_put(octx
, ocx
, ocy
, after
, frs
,
277 width_left
+ width_list
,
282 * Write centre halfway between
283 * width_left + width_list + width_after
285 * available - width_right.
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
292 centre
->cx
/ 2 - width_centre
/ 2,
296 * The list now goes from
299 * width_left + width_list.
300 * If there is no focus given, keep the left in focus.
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
);
308 * Write abs_centre in the perfect centre of all horizontal space.
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,
318 /* Draw format with list in the centre. */
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
,
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
)
326 u_int width_left
, width_centre
, width_right
, middle
;
327 u_int width_list
, width_after
, width_abs_centre
;
328 struct screen_write_ctx ctx
;
330 width_left
= left
->cx
;
331 width_centre
= centre
->cx
;
332 width_right
= right
->cx
;
333 width_abs_centre
= abs_centre
->cx
;
334 width_list
= list
->cx
;
335 width_after
= after
->cx
;
338 * Trim first the list, then after the list, then the centre, then the
339 * right, then the left.
345 width_after
> available
) {
348 else if (width_after
> 0)
350 else if (width_centre
> 0)
352 else if (width_right
> 0)
358 /* If there is no list left, pass off to the no list function. */
359 if (width_list
== 0) {
360 screen_write_start(&ctx
, centre
);
361 screen_write_fast_copy(&ctx
, after
, 0, 0, width_after
, 1);
362 screen_write_stop(&ctx
);
364 format_draw_none(octx
, available
, ocx
, ocy
, left
, centre
,
365 right
, abs_centre
, frs
);
369 /* Write left at 0. */
370 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
379 * All three centre sections are offset from the middle of the
382 middle
= (width_left
+ ((available
- width_right
) - width_left
) / 2);
386 * middle - width_list / 2 - width_centre.
388 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
389 middle
- width_list
/ 2 - width_centre
,
395 * middle - width_list / 2 + width_list
397 format_draw_put(octx
, ocx
, ocy
, after
, frs
,
398 middle
- width_list
/ 2 + width_list
,
403 * The list now goes from
404 * middle - width_list / 2
406 * middle + width_list / 2
407 * If there is no focus given, keep the centre in focus.
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
,
416 * Write abs_centre in the perfect centre of all horizontal space.
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,
426 /* Draw format with list on the right. */
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
,
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
)
434 u_int width_left
, width_centre
, width_right
;
435 u_int width_list
, width_after
, width_abs_centre
;
436 struct screen_write_ctx ctx
;
438 width_left
= left
->cx
;
439 width_centre
= centre
->cx
;
440 width_right
= right
->cx
;
441 width_abs_centre
= abs_centre
->cx
;
442 width_list
= list
->cx
;
443 width_after
= after
->cx
;
446 * Trim first the centre, then the list, then the right, then
447 * after the list, then the left.
453 width_after
> available
) {
454 if (width_centre
> 0)
456 else if (width_list
> 0)
458 else if (width_right
> 0)
460 else if (width_after
> 0)
466 /* If there is no list left, pass off to the no list function. */
467 if (width_list
== 0) {
468 screen_write_start(&ctx
, right
);
469 screen_write_fast_copy(&ctx
, after
, 0, 0, width_after
, 1);
470 screen_write_stop(&ctx
);
472 format_draw_none(octx
, available
, ocx
, ocy
, left
, centre
,
473 right
, abs_centre
, frs
);
477 /* Write left at 0. */
478 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
488 * available - width_right - width_list - width_after.
490 format_draw_put(octx
, ocx
, ocy
, right
, frs
,
491 available
- width_right
- width_list
- width_after
,
496 * Write centre halfway between
499 * available - width_right - width_list - width_after.
501 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
503 + ((available
- width_right
- width_list
- width_after
)
506 centre
->cx
/ 2 - width_centre
/ 2,
510 * The list now goes from
511 * available - width_list - width_after
513 * available - width_after
514 * If there is no focus given, keep the right in focus.
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
,
523 * Write abs_centre in the perfect centre of all horizontal space.
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,
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
)
540 u_int width_left
, width_centre
, width_right
, width_abs_centre
;
541 u_int width_list
, width_after
, middle
, abs_centre_offset
;
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
;
551 * Trim first centre, then the right, then the left.
555 width_right
> available
) {
556 if (width_centre
> 0)
558 else if (width_right
> 0)
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
569 while (width_list
+ width_after
+ width_abs_centre
> available
) {
572 else if (width_after
> 0)
578 /* Write left at 0. */
579 format_draw_put(octx
, ocx
, ocy
, left
, frs
, 0, 0, width_left
);
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
,
588 * Keep writing centre at the relative centre. Only the list is written
589 * in the absolute centre of the horizontal space.
591 middle
= (width_left
+ ((available
- width_right
) - width_left
) / 2);
595 * middle - width_centre.
597 format_draw_put(octx
, ocx
, ocy
, centre
, frs
,
598 middle
- width_centre
,
603 * If there is no focus given, keep the centre in focus.
605 if (focus_start
== -1 || focus_end
== -1)
606 focus_start
= focus_end
= list
->cx
/ 2;
609 * We centre abs_centre and the list together, so their shared centre is
610 * in the perfect centre of horizontal space.
612 abs_centre_offset
= (available
- width_list
- width_abs_centre
) / 2;
615 * Write abs_centre before the list.
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
;
622 * Draw the list in the absolute centre
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
;
629 * Write after at the end of the centre
631 format_draw_put(octx
, ocx
, ocy
, after
, frs
, abs_centre_offset
, 0,
635 /* Get width and count of any leading #s. */
637 format_leading_hashes(const char *cp
, u_int
*n
, u_int
*width
)
639 for (*n
= 0; cp
[*n
] == '#'; (*n
)++)
649 *width
= (*n
/ 2) + 1;
655 * An even number of #s means that all #s are escaped, so not a
656 * style. The caller should not skip this. Return pointing to
661 /* This is a style, so return pointing to the #. */
662 return (cp
+ *n
- 1);
665 /* Draw multiple characters. */
667 format_draw_many(struct screen_write_ctx
*ctx
, struct style
*sy
, char ch
,
672 utf8_set(&sy
->gc
.data
, ch
);
673 for (i
= 0; i
< n
; i
++)
674 screen_write_cell(ctx
, &sy
->gc
);
677 /* Draw a format to a screen. */
679 format_draw(struct screen_write_ctx
*octx
, const struct grid_cell
*base
,
680 u_int available
, const char *expanded
, struct style_ranges
*srs
,
691 TOTAL
} current
= LEFT
, last
= LEFT
;
692 const char *names
[] = { "LEFT",
700 size_t size
= strlen(expanded
);
701 struct screen
*os
= octx
->s
, s
[TOTAL
];
702 struct screen_write_ctx ctx
[TOTAL
];
703 u_int ocx
= os
->cx
, ocy
= os
->cy
, n
, i
, width
[TOTAL
];
704 u_int map
[] = { LEFT
,
709 int focus_start
= -1, focus_end
= -1;
710 int list_state
= -1, fill
= -1, even
;
711 enum style_align list_align
= STYLE_ALIGN_DEFAULT
;
712 struct grid_cell gc
, current_default
;
713 struct style sy
, saved_sy
;
714 struct utf8_data
*ud
= &sy
.gc
.data
;
715 const char *cp
, *end
;
716 enum utf8_state more
;
718 struct format_range
*fr
= NULL
, *fr1
;
719 struct format_ranges frs
;
720 struct style_range
*sr
;
722 memcpy(¤t_default
, base
, sizeof current_default
);
723 style_set(&sy
, ¤t_default
);
725 log_debug("%s: %s", __func__
, expanded
);
728 * We build three screens for left, right, centre alignment, one for
729 * the list, one for anything after the list and two for the list left
732 for (i
= 0; i
< TOTAL
; i
++) {
733 screen_init(&s
[i
], size
, 1, 0);
734 screen_write_start(&ctx
[i
], &s
[i
]);
735 screen_write_clearendofline(&ctx
[i
], current_default
.bg
);
740 * Walk the string and add to the corresponding screens,
741 * parsing styles as we go.
744 while (*cp
!= '\0') {
745 /* Handle sequences of #. */
746 if (cp
[0] == '#' && cp
[1] != '[' && cp
[1] != '\0') {
747 for (n
= 1; cp
[n
] == '#'; n
++)
749 even
= ((n
% 2) == 0);
757 format_draw_many(&ctx
[current
], &sy
, '#', n
);
766 format_draw_many(&ctx
[current
], &sy
, '#', n
/ 2);
767 width
[current
] += (n
/ 2);
770 screen_write_cell(&ctx
[current
], &sy
.gc
);
776 /* Is this not a style? */
777 if (cp
[0] != '#' || cp
[1] != '[' || sy
.ignore
) {
778 /* See if this is a UTF-8 character. */
779 if ((more
= utf8_open(ud
, *cp
)) == UTF8_MORE
) {
780 while (*++cp
!= '\0' && more
== UTF8_MORE
)
781 more
= utf8_append(ud
, *cp
);
782 if (more
!= UTF8_DONE
)
786 /* Not a UTF-8 character - ASCII or not valid. */
787 if (more
!= UTF8_DONE
) {
788 if (*cp
< 0x20 || *cp
> 0x7e) {
789 /* Ignore nonprintable characters. */
797 /* Draw the cell to the current screen. */
798 screen_write_cell(&ctx
[current
], &sy
.gc
);
799 width
[current
] += ud
->width
;
803 /* This is a style. Work out where the end is and parse it. */
804 end
= format_skip(cp
+ 2, "]");
806 log_debug("%s: no terminating ] at '%s'", __func__
,
808 TAILQ_FOREACH_SAFE(fr
, &frs
, entry
, fr1
)
809 format_free_range(&frs
, fr
);
812 tmp
= xstrndup(cp
+ 2, end
- (cp
+ 2));
813 style_copy(&saved_sy
, &sy
);
814 if (style_parse(&sy
, ¤t_default
, tmp
) != 0) {
815 log_debug("%s: invalid style '%s'", __func__
, tmp
);
820 log_debug("%s: style '%s' -> '%s'", __func__
, tmp
,
821 style_tostring(&sy
));
823 if (default_colours
) {
828 /* If this style has a fill colour, store it for later. */
832 /* If this style pushed or popped the default, update it. */
833 if (sy
.default_type
== STYLE_DEFAULT_PUSH
) {
834 memcpy(¤t_default
, &saved_sy
.gc
,
835 sizeof current_default
);
836 sy
.default_type
= STYLE_DEFAULT_BASE
;
837 } else if (sy
.default_type
== STYLE_DEFAULT_POP
) {
838 memcpy(¤t_default
, base
, sizeof current_default
);
839 sy
.default_type
= STYLE_DEFAULT_BASE
;
842 /* Check the list state. */
846 * Entering the list, exiting a marker, or exiting the
849 if (list_state
!= 0) {
850 if (fr
!= NULL
) { /* abort any region */
855 list_align
= sy
.align
;
858 /* End the focus if started. */
859 if (focus_start
!= -1 && focus_end
== -1)
860 focus_end
= s
[LIST
].cx
;
864 case STYLE_LIST_FOCUS
:
865 /* Entering the focus. */
866 if (list_state
!= 0) /* not inside the list */
868 if (focus_start
== -1) /* focus already started */
869 focus_start
= s
[LIST
].cx
;
872 /* Exiting or outside the list. */
873 if (list_state
== 0) {
874 if (fr
!= NULL
) { /* abort any region */
878 if (focus_start
!= -1 && focus_end
== -1)
879 focus_end
= s
[LIST
].cx
;
881 map
[list_align
] = AFTER
;
882 if (list_align
== STYLE_ALIGN_LEFT
)
883 map
[STYLE_ALIGN_DEFAULT
] = AFTER
;
886 current
= map
[sy
.align
];
888 case STYLE_LIST_LEFT_MARKER
:
889 /* Entering left marker. */
890 if (list_state
!= 0) /* not inside the list */
892 if (s
[LIST_LEFT
].cx
!= 0) /* already have marker */
894 if (fr
!= NULL
) { /* abort any region */
898 if (focus_start
!= -1 && focus_end
== -1)
899 focus_start
= focus_end
= -1;
902 case STYLE_LIST_RIGHT_MARKER
:
903 /* Entering right marker. */
904 if (list_state
!= 0) /* not inside the list */
906 if (s
[LIST_RIGHT
].cx
!= 0) /* already have marker */
908 if (fr
!= NULL
) { /* abort any region */
912 if (focus_start
!= -1 && focus_end
== -1)
913 focus_start
= focus_end
= -1;
914 current
= LIST_RIGHT
;
917 if (current
!= last
) {
918 log_debug("%s: change %s -> %s", __func__
,
919 names
[last
], names
[current
]);
924 * Check if the range style has changed and if so end the
925 * current range and start a new one if needed.
928 if (fr
!= NULL
&& !format_is_type(fr
, &sy
)) {
929 if (s
[current
].cx
!= fr
->start
) {
930 fr
->end
= s
[current
].cx
+ 1;
931 TAILQ_INSERT_TAIL(&frs
, fr
, entry
);
936 if (fr
== NULL
&& sy
.range_type
!= STYLE_RANGE_NONE
) {
937 fr
= xcalloc(1, sizeof *fr
);
941 fr
->start
= s
[current
].cx
;
943 fr
->type
= sy
.range_type
;
944 fr
->argument
= sy
.range_argument
;
952 for (i
= 0; i
< TOTAL
; i
++) {
953 screen_write_stop(&ctx
[i
]);
954 log_debug("%s: width %s is %u", __func__
, names
[i
], width
[i
]);
956 if (focus_start
!= -1 && focus_end
!= -1)
957 log_debug("%s: focus %d-%d", __func__
, focus_start
, focus_end
);
958 TAILQ_FOREACH(fr
, &frs
, entry
) {
959 log_debug("%s: range %d|%u is %s %u-%u", __func__
, fr
->type
,
960 fr
->argument
, names
[fr
->index
], fr
->start
, fr
->end
);
963 /* Clear the available area. */
965 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
967 for (i
= 0; i
< available
; i
++)
968 screen_write_putc(octx
, &gc
, ' ');
972 * Draw the screens. How they are arranged depends on where the list
975 switch (list_align
) {
976 case STYLE_ALIGN_DEFAULT
:
978 format_draw_none(octx
, available
, ocx
, ocy
, &s
[LEFT
],
979 &s
[CENTRE
], &s
[RIGHT
], &s
[ABSOLUTE_CENTRE
], &frs
);
981 case STYLE_ALIGN_LEFT
:
982 /* List is part of the left. */
983 format_draw_left(octx
, available
, ocx
, ocy
, &s
[LEFT
],
984 &s
[CENTRE
], &s
[RIGHT
], &s
[ABSOLUTE_CENTRE
], &s
[LIST
],
985 &s
[LIST_LEFT
], &s
[LIST_RIGHT
], &s
[AFTER
],
986 focus_start
, focus_end
, &frs
);
988 case STYLE_ALIGN_CENTRE
:
989 /* List is part of the centre. */
990 format_draw_centre(octx
, available
, ocx
, ocy
, &s
[LEFT
],
991 &s
[CENTRE
], &s
[RIGHT
], &s
[ABSOLUTE_CENTRE
], &s
[LIST
],
992 &s
[LIST_LEFT
], &s
[LIST_RIGHT
], &s
[AFTER
],
993 focus_start
, focus_end
, &frs
);
995 case STYLE_ALIGN_RIGHT
:
996 /* List is part of the right. */
997 format_draw_right(octx
, available
, ocx
, ocy
, &s
[LEFT
],
998 &s
[CENTRE
], &s
[RIGHT
], &s
[ABSOLUTE_CENTRE
], &s
[LIST
],
999 &s
[LIST_LEFT
], &s
[LIST_RIGHT
], &s
[AFTER
],
1000 focus_start
, focus_end
, &frs
);
1002 case STYLE_ALIGN_ABSOLUTE_CENTRE
:
1003 /* List is in the centre of the entire horizontal space. */
1004 format_draw_absolute_centre(octx
, available
, ocx
, ocy
, &s
[LEFT
],
1005 &s
[CENTRE
], &s
[RIGHT
], &s
[ABSOLUTE_CENTRE
], &s
[LIST
],
1006 &s
[LIST_LEFT
], &s
[LIST_RIGHT
], &s
[AFTER
],
1007 focus_start
, focus_end
, &frs
);
1011 /* Create ranges to return. */
1012 TAILQ_FOREACH_SAFE(fr
, &frs
, entry
, fr1
) {
1013 sr
= xcalloc(1, sizeof *sr
);
1014 sr
->type
= fr
->type
;
1015 sr
->argument
= fr
->argument
;
1016 sr
->start
= fr
->start
;
1018 TAILQ_INSERT_TAIL(srs
, sr
, entry
);
1020 log_debug("%s: range %d|%u at %u-%u", __func__
, sr
->type
,
1021 sr
->argument
, sr
->start
, sr
->end
);
1023 format_free_range(&frs
, fr
);
1027 /* Free the screens. */
1028 for (i
= 0; i
< TOTAL
; i
++)
1031 /* Restore the original cursor position. */
1032 screen_write_cursormove(octx
, ocx
, ocy
, 0);
1035 /* Get width, taking #[] into account. */
1037 format_width(const char *expanded
)
1039 const char *cp
, *end
;
1040 u_int n
, leading_width
, width
= 0;
1041 struct utf8_data ud
;
1042 enum utf8_state more
;
1045 while (*cp
!= '\0') {
1047 end
= format_leading_hashes(cp
, &n
, &leading_width
);
1048 width
+= leading_width
;
1051 end
= format_skip(cp
+ 2, "]");
1056 } else if ((more
= utf8_open(&ud
, *cp
)) == UTF8_MORE
) {
1057 while (*++cp
!= '\0' && more
== UTF8_MORE
)
1058 more
= utf8_append(&ud
, *cp
);
1059 if (more
== UTF8_DONE
)
1063 } else if (*cp
> 0x1f && *cp
< 0x7f) {
1073 * Trim on the left, taking #[] into account. Note, we copy the whole set of
1074 * unescaped #s, but only add their escaped size to width. This is because the
1075 * format_draw function will actually do the escaping when it runs
1078 format_trim_left(const char *expanded
, u_int limit
)
1081 const char *cp
= expanded
, *end
;
1082 u_int n
, width
= 0, leading_width
;
1083 struct utf8_data ud
;
1084 enum utf8_state more
;
1086 out
= copy
= xcalloc(2, strlen(expanded
) + 1);
1087 while (*cp
!= '\0') {
1091 end
= format_leading_hashes(cp
, &n
, &leading_width
);
1092 if (leading_width
> limit
- width
)
1093 leading_width
= limit
- width
;
1094 if (leading_width
!= 0) {
1098 memset(out
, '#', 2 * leading_width
);
1099 out
+= 2 * leading_width
;
1101 width
+= leading_width
;
1105 end
= format_skip(cp
+ 2, "]");
1108 memcpy(out
, cp
, end
+ 1 - cp
);
1109 out
+= (end
+ 1 - cp
);
1112 } else if ((more
= utf8_open(&ud
, *cp
)) == UTF8_MORE
) {
1113 while (*++cp
!= '\0' && more
== UTF8_MORE
)
1114 more
= utf8_append(&ud
, *cp
);
1115 if (more
== UTF8_DONE
) {
1116 if (width
+ ud
.width
<= limit
) {
1117 memcpy(out
, ud
.data
, ud
.size
);
1125 } else if (*cp
> 0x1f && *cp
< 0x7f) {
1126 if (width
+ 1 <= limit
)
1137 /* Trim on the right, taking #[] into account. */
1139 format_trim_right(const char *expanded
, u_int limit
)
1142 const char *cp
= expanded
, *end
;
1143 u_int width
= 0, total_width
, skip
, n
;
1144 u_int leading_width
, copy_width
;
1145 struct utf8_data ud
;
1146 enum utf8_state more
;
1148 total_width
= format_width(expanded
);
1149 if (total_width
<= limit
)
1150 return (xstrdup(expanded
));
1151 skip
= total_width
- limit
;
1153 out
= copy
= xcalloc(2, strlen(expanded
) + 1);
1154 while (*cp
!= '\0') {
1156 end
= format_leading_hashes(cp
, &n
, &leading_width
);
1157 copy_width
= leading_width
;
1158 if (width
<= skip
) {
1159 if (skip
- width
>= copy_width
)
1162 copy_width
-= (skip
- width
);
1164 if (copy_width
!= 0) {
1168 memset(out
, '#', 2 * copy_width
);
1169 out
+= 2 * copy_width
;
1172 width
+= leading_width
;
1175 end
= format_skip(cp
+ 2, "]");
1178 memcpy(out
, cp
, end
+ 1 - cp
);
1179 out
+= (end
+ 1 - cp
);
1182 } else if ((more
= utf8_open(&ud
, *cp
)) == UTF8_MORE
) {
1183 while (*++cp
!= '\0' && more
== UTF8_MORE
)
1184 more
= utf8_append(&ud
, *cp
);
1185 if (more
== UTF8_DONE
) {
1186 if (width
>= skip
) {
1187 memcpy(out
, ud
.data
, ud
.size
);
1195 } else if (*cp
> 0x1f && *cp
< 0x7f) {