Home | History | Annotate | Line # | Download | only in dist
format-draw.c revision 1.1.1.3.4.2
      1  1.1.1.3.4.2  martin /* $OpenBSD$ */
      2  1.1.1.3.4.2  martin 
      3  1.1.1.3.4.2  martin /*
      4  1.1.1.3.4.2  martin  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  1.1.1.3.4.2  martin  *
      6  1.1.1.3.4.2  martin  * Permission to use, copy, modify, and distribute this software for any
      7  1.1.1.3.4.2  martin  * purpose with or without fee is hereby granted, provided that the above
      8  1.1.1.3.4.2  martin  * copyright notice and this permission notice appear in all copies.
      9  1.1.1.3.4.2  martin  *
     10  1.1.1.3.4.2  martin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  1.1.1.3.4.2  martin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  1.1.1.3.4.2  martin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  1.1.1.3.4.2  martin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  1.1.1.3.4.2  martin  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  1.1.1.3.4.2  martin  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  1.1.1.3.4.2  martin  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  1.1.1.3.4.2  martin  */
     18  1.1.1.3.4.2  martin 
     19  1.1.1.3.4.2  martin #include <sys/types.h>
     20  1.1.1.3.4.2  martin 
     21  1.1.1.3.4.2  martin #include <stdlib.h>
     22  1.1.1.3.4.2  martin #include <string.h>
     23  1.1.1.3.4.2  martin 
     24  1.1.1.3.4.2  martin #include "tmux.h"
     25  1.1.1.3.4.2  martin 
     26  1.1.1.3.4.2  martin /* Format range. */
     27  1.1.1.3.4.2  martin struct format_range {
     28  1.1.1.3.4.2  martin 	u_int				 index;
     29  1.1.1.3.4.2  martin 	struct screen			*s;
     30  1.1.1.3.4.2  martin 
     31  1.1.1.3.4.2  martin 	u_int				 start;
     32  1.1.1.3.4.2  martin 	u_int				 end;
     33  1.1.1.3.4.2  martin 
     34  1.1.1.3.4.2  martin 	enum style_range_type		 type;
     35  1.1.1.3.4.2  martin 	u_int				 argument;
     36  1.1.1.3.4.2  martin 
     37  1.1.1.3.4.2  martin 	TAILQ_ENTRY(format_range)	 entry;
     38  1.1.1.3.4.2  martin };
     39  1.1.1.3.4.2  martin TAILQ_HEAD(format_ranges, format_range);
     40  1.1.1.3.4.2  martin 
     41  1.1.1.3.4.2  martin /* Does this range match this style? */
     42  1.1.1.3.4.2  martin static int
     43  1.1.1.3.4.2  martin format_is_type(struct format_range *fr, struct style *sy)
     44  1.1.1.3.4.2  martin {
     45  1.1.1.3.4.2  martin 	if (fr->type != sy->range_type)
     46  1.1.1.3.4.2  martin 		return (0);
     47  1.1.1.3.4.2  martin 	if (fr->type == STYLE_RANGE_WINDOW &&
     48  1.1.1.3.4.2  martin 	    fr->argument != sy->range_argument)
     49  1.1.1.3.4.2  martin 		return (0);
     50  1.1.1.3.4.2  martin 	return (1);
     51  1.1.1.3.4.2  martin }
     52  1.1.1.3.4.2  martin 
     53  1.1.1.3.4.2  martin /* Free a range. */
     54  1.1.1.3.4.2  martin static void
     55  1.1.1.3.4.2  martin format_free_range(struct format_ranges *frs, struct format_range *fr)
     56  1.1.1.3.4.2  martin {
     57  1.1.1.3.4.2  martin 	TAILQ_REMOVE(frs, fr, entry);
     58  1.1.1.3.4.2  martin 	free(fr);
     59  1.1.1.3.4.2  martin }
     60  1.1.1.3.4.2  martin 
     61  1.1.1.3.4.2  martin /* Fix range positions. */
     62  1.1.1.3.4.2  martin static void
     63  1.1.1.3.4.2  martin format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
     64  1.1.1.3.4.2  martin     u_int start, u_int width)
     65  1.1.1.3.4.2  martin {
     66  1.1.1.3.4.2  martin 	struct format_range	*fr, *fr1;
     67  1.1.1.3.4.2  martin 
     68  1.1.1.3.4.2  martin 	if (frs == NULL)
     69  1.1.1.3.4.2  martin 		return;
     70  1.1.1.3.4.2  martin 
     71  1.1.1.3.4.2  martin 	TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
     72  1.1.1.3.4.2  martin 		if (fr->s != s)
     73  1.1.1.3.4.2  martin 			continue;
     74  1.1.1.3.4.2  martin 
     75  1.1.1.3.4.2  martin 		if (fr->end <= start || fr->start >= start + width) {
     76  1.1.1.3.4.2  martin 			format_free_range(frs, fr);
     77  1.1.1.3.4.2  martin 			continue;
     78  1.1.1.3.4.2  martin 		}
     79  1.1.1.3.4.2  martin 
     80  1.1.1.3.4.2  martin 		if (fr->start < start)
     81  1.1.1.3.4.2  martin 			fr->start = start;
     82  1.1.1.3.4.2  martin 		if (fr->end > start + width)
     83  1.1.1.3.4.2  martin 			fr->end = start + width;
     84  1.1.1.3.4.2  martin 		if (fr->start == fr->end) {
     85  1.1.1.3.4.2  martin 			format_free_range(frs, fr);
     86  1.1.1.3.4.2  martin 			continue;
     87  1.1.1.3.4.2  martin 		}
     88  1.1.1.3.4.2  martin 
     89  1.1.1.3.4.2  martin 		fr->start -= start;
     90  1.1.1.3.4.2  martin 		fr->end -= start;
     91  1.1.1.3.4.2  martin 
     92  1.1.1.3.4.2  martin 		fr->start += offset;
     93  1.1.1.3.4.2  martin 		fr->end += offset;
     94  1.1.1.3.4.2  martin 	}
     95  1.1.1.3.4.2  martin }
     96  1.1.1.3.4.2  martin 
     97  1.1.1.3.4.2  martin /* Draw a part of the format. */
     98  1.1.1.3.4.2  martin static void
     99  1.1.1.3.4.2  martin format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
    100  1.1.1.3.4.2  martin     struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
    101  1.1.1.3.4.2  martin     u_int width)
    102  1.1.1.3.4.2  martin {
    103  1.1.1.3.4.2  martin 	/*
    104  1.1.1.3.4.2  martin 	 * The offset is how far from the cursor on the target screen; start
    105  1.1.1.3.4.2  martin 	 * and width how much to copy from the source screen.
    106  1.1.1.3.4.2  martin 	 */
    107  1.1.1.3.4.2  martin 	screen_write_cursormove(octx, ocx + offset, ocy, 0);
    108  1.1.1.3.4.2  martin 	screen_write_fast_copy(octx, s, start, 0, width, 1);
    109  1.1.1.3.4.2  martin 	format_update_ranges(frs, s, offset, start, width);
    110  1.1.1.3.4.2  martin }
    111  1.1.1.3.4.2  martin 
    112  1.1.1.3.4.2  martin /* Draw list part of format. */
    113  1.1.1.3.4.2  martin static void
    114  1.1.1.3.4.2  martin format_draw_put_list(struct screen_write_ctx *octx,
    115  1.1.1.3.4.2  martin     u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
    116  1.1.1.3.4.2  martin     struct screen *list_left, struct screen *list_right, int focus_start,
    117  1.1.1.3.4.2  martin     int focus_end, struct format_ranges *frs)
    118  1.1.1.3.4.2  martin {
    119  1.1.1.3.4.2  martin 	u_int	start, focus_centre;
    120  1.1.1.3.4.2  martin 
    121  1.1.1.3.4.2  martin 	/* If there is enough space for the list, draw it entirely. */
    122  1.1.1.3.4.2  martin 	if (width >= list->cx) {
    123  1.1.1.3.4.2  martin 		format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
    124  1.1.1.3.4.2  martin 		return;
    125  1.1.1.3.4.2  martin 	}
    126  1.1.1.3.4.2  martin 
    127  1.1.1.3.4.2  martin 	/* The list needs to be trimmed. Try to keep the focus visible. */
    128  1.1.1.3.4.2  martin 	focus_centre = focus_start + (focus_end - focus_start) / 2;
    129  1.1.1.3.4.2  martin 	if (focus_centre < width / 2)
    130  1.1.1.3.4.2  martin 		start = 0;
    131  1.1.1.3.4.2  martin 	else
    132  1.1.1.3.4.2  martin 		start = focus_centre - width / 2;
    133  1.1.1.3.4.2  martin 	if (start + width > list->cx)
    134  1.1.1.3.4.2  martin 		start = list->cx - width;
    135  1.1.1.3.4.2  martin 
    136  1.1.1.3.4.2  martin 	/* Draw <> markers at either side if needed. */
    137  1.1.1.3.4.2  martin 	if (start != 0 && width > list_left->cx) {
    138  1.1.1.3.4.2  martin 		screen_write_cursormove(octx, ocx + offset, ocy, 0);
    139  1.1.1.3.4.2  martin 		screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
    140  1.1.1.3.4.2  martin 		offset += list_left->cx;
    141  1.1.1.3.4.2  martin 		start += list_left->cx;
    142  1.1.1.3.4.2  martin 		width -= list_left->cx;
    143  1.1.1.3.4.2  martin 	}
    144  1.1.1.3.4.2  martin 	if (start + width < list->cx && width > list_right->cx) {
    145  1.1.1.3.4.2  martin 		screen_write_cursormove(octx, ocx + offset + width -
    146  1.1.1.3.4.2  martin 		    list_right->cx, ocy, 0);
    147  1.1.1.3.4.2  martin 		screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
    148  1.1.1.3.4.2  martin 		    1);
    149  1.1.1.3.4.2  martin 		width -= list_right->cx;
    150  1.1.1.3.4.2  martin 	}
    151  1.1.1.3.4.2  martin 
    152  1.1.1.3.4.2  martin 	/* Draw the list screen itself. */
    153  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
    154  1.1.1.3.4.2  martin }
    155  1.1.1.3.4.2  martin 
    156  1.1.1.3.4.2  martin /* Draw format with no list. */
    157  1.1.1.3.4.2  martin static void
    158  1.1.1.3.4.2  martin format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
    159  1.1.1.3.4.2  martin     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    160  1.1.1.3.4.2  martin     struct format_ranges *frs)
    161  1.1.1.3.4.2  martin {
    162  1.1.1.3.4.2  martin 	u_int	width_left, width_centre, width_right;
    163  1.1.1.3.4.2  martin 
    164  1.1.1.3.4.2  martin 	width_left = left->cx;
    165  1.1.1.3.4.2  martin 	width_centre = centre->cx;
    166  1.1.1.3.4.2  martin 	width_right = right->cx;
    167  1.1.1.3.4.2  martin 
    168  1.1.1.3.4.2  martin 	/*
    169  1.1.1.3.4.2  martin 	 * Try to keep as much of the left and right as possible at the expense
    170  1.1.1.3.4.2  martin 	 * of the centre.
    171  1.1.1.3.4.2  martin 	 */
    172  1.1.1.3.4.2  martin 	while (width_left + width_centre + width_right > available) {
    173  1.1.1.3.4.2  martin 		if (width_centre > 0)
    174  1.1.1.3.4.2  martin 			width_centre--;
    175  1.1.1.3.4.2  martin 		else if (width_right > 0)
    176  1.1.1.3.4.2  martin 			width_right--;
    177  1.1.1.3.4.2  martin 		else
    178  1.1.1.3.4.2  martin 			width_left--;
    179  1.1.1.3.4.2  martin 	}
    180  1.1.1.3.4.2  martin 
    181  1.1.1.3.4.2  martin 	/* Write left. */
    182  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    183  1.1.1.3.4.2  martin 
    184  1.1.1.3.4.2  martin 	/* Write right at available - width_right. */
    185  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, right, frs,
    186  1.1.1.3.4.2  martin 	    available - width_right,
    187  1.1.1.3.4.2  martin 	    right->cx - width_right,
    188  1.1.1.3.4.2  martin 	    width_right);
    189  1.1.1.3.4.2  martin 
    190  1.1.1.3.4.2  martin 	/*
    191  1.1.1.3.4.2  martin 	 * Write centre halfway between
    192  1.1.1.3.4.2  martin 	 *     width_left
    193  1.1.1.3.4.2  martin 	 * and
    194  1.1.1.3.4.2  martin 	 *     available - width_right.
    195  1.1.1.3.4.2  martin 	 */
    196  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, centre, frs,
    197  1.1.1.3.4.2  martin 	    width_left
    198  1.1.1.3.4.2  martin 	    + ((available - width_right) - width_left) / 2
    199  1.1.1.3.4.2  martin 	    - width_centre / 2,
    200  1.1.1.3.4.2  martin 	    centre->cx / 2 - width_centre / 2,
    201  1.1.1.3.4.2  martin 	    width_centre);
    202  1.1.1.3.4.2  martin }
    203  1.1.1.3.4.2  martin 
    204  1.1.1.3.4.2  martin /* Draw format with list on the left. */
    205  1.1.1.3.4.2  martin static void
    206  1.1.1.3.4.2  martin format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
    207  1.1.1.3.4.2  martin     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    208  1.1.1.3.4.2  martin     struct screen *list, struct screen *list_left, struct screen *list_right,
    209  1.1.1.3.4.2  martin     struct screen *after, int focus_start, int focus_end,
    210  1.1.1.3.4.2  martin     struct format_ranges *frs)
    211  1.1.1.3.4.2  martin {
    212  1.1.1.3.4.2  martin 	u_int			width_left, width_centre, width_right;
    213  1.1.1.3.4.2  martin 	u_int			width_list, width_after;
    214  1.1.1.3.4.2  martin 	struct screen_write_ctx	ctx;
    215  1.1.1.3.4.2  martin 
    216  1.1.1.3.4.2  martin 	width_left = left->cx;
    217  1.1.1.3.4.2  martin 	width_centre = centre->cx;
    218  1.1.1.3.4.2  martin 	width_right = right->cx;
    219  1.1.1.3.4.2  martin 	width_list = list->cx;
    220  1.1.1.3.4.2  martin 	width_after = after->cx;
    221  1.1.1.3.4.2  martin 
    222  1.1.1.3.4.2  martin 	/*
    223  1.1.1.3.4.2  martin 	 * Trim first the centre, then the list, then the right, then after the
    224  1.1.1.3.4.2  martin 	 * list, then the left.
    225  1.1.1.3.4.2  martin 	 */
    226  1.1.1.3.4.2  martin 	while (width_left +
    227  1.1.1.3.4.2  martin 	    width_centre +
    228  1.1.1.3.4.2  martin 	    width_right +
    229  1.1.1.3.4.2  martin 	    width_list +
    230  1.1.1.3.4.2  martin 	    width_after > available) {
    231  1.1.1.3.4.2  martin 		if (width_centre > 0)
    232  1.1.1.3.4.2  martin 			width_centre--;
    233  1.1.1.3.4.2  martin 		else if (width_list > 0)
    234  1.1.1.3.4.2  martin 			width_list--;
    235  1.1.1.3.4.2  martin 		else if (width_right > 0)
    236  1.1.1.3.4.2  martin 			width_right--;
    237  1.1.1.3.4.2  martin 		else if (width_after > 0)
    238  1.1.1.3.4.2  martin 			width_after--;
    239  1.1.1.3.4.2  martin 		else
    240  1.1.1.3.4.2  martin 			width_left--;
    241  1.1.1.3.4.2  martin 	}
    242  1.1.1.3.4.2  martin 
    243  1.1.1.3.4.2  martin 	/* If there is no list left, pass off to the no list function. */
    244  1.1.1.3.4.2  martin 	if (width_list == 0) {
    245  1.1.1.3.4.2  martin 		screen_write_start(&ctx, NULL, left);
    246  1.1.1.3.4.2  martin 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
    247  1.1.1.3.4.2  martin 		screen_write_stop(&ctx);
    248  1.1.1.3.4.2  martin 
    249  1.1.1.3.4.2  martin 		format_draw_none(octx, available, ocx, ocy, left, centre,
    250  1.1.1.3.4.2  martin 		    right, frs);
    251  1.1.1.3.4.2  martin 		return;
    252  1.1.1.3.4.2  martin 	}
    253  1.1.1.3.4.2  martin 
    254  1.1.1.3.4.2  martin 	/* Write left at 0. */
    255  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    256  1.1.1.3.4.2  martin 
    257  1.1.1.3.4.2  martin 	/* Write right at available - width_right. */
    258  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, right, frs,
    259  1.1.1.3.4.2  martin 	    available - width_right,
    260  1.1.1.3.4.2  martin 	    right->cx - width_right,
    261  1.1.1.3.4.2  martin 	    width_right);
    262  1.1.1.3.4.2  martin 
    263  1.1.1.3.4.2  martin 	/* Write after at width_left + width_list. */
    264  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, after, frs,
    265  1.1.1.3.4.2  martin 	    width_left + width_list,
    266  1.1.1.3.4.2  martin 	    0,
    267  1.1.1.3.4.2  martin 	    width_after);
    268  1.1.1.3.4.2  martin 
    269  1.1.1.3.4.2  martin 	/*
    270  1.1.1.3.4.2  martin 	 * Write centre halfway between
    271  1.1.1.3.4.2  martin 	 *     width_left + width_list + width_after
    272  1.1.1.3.4.2  martin 	 * and
    273  1.1.1.3.4.2  martin 	 *     available - width_right.
    274  1.1.1.3.4.2  martin 	 */
    275  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, centre, frs,
    276  1.1.1.3.4.2  martin 	    (width_left + width_list + width_after)
    277  1.1.1.3.4.2  martin 	    + ((available - width_right)
    278  1.1.1.3.4.2  martin 		- (width_left + width_list + width_after)) / 2
    279  1.1.1.3.4.2  martin 	    - width_centre / 2,
    280  1.1.1.3.4.2  martin 	    centre->cx / 2 - width_centre / 2,
    281  1.1.1.3.4.2  martin 	    width_centre);
    282  1.1.1.3.4.2  martin 
    283  1.1.1.3.4.2  martin 	/*
    284  1.1.1.3.4.2  martin 	 * The list now goes from
    285  1.1.1.3.4.2  martin 	 *     width_left
    286  1.1.1.3.4.2  martin 	 * to
    287  1.1.1.3.4.2  martin 	 *     width_left + width_list.
    288  1.1.1.3.4.2  martin 	 * If there is no focus given, keep the left in focus.
    289  1.1.1.3.4.2  martin 	 */
    290  1.1.1.3.4.2  martin 	if (focus_start == -1 || focus_end == -1)
    291  1.1.1.3.4.2  martin 		focus_start = focus_end = 0;
    292  1.1.1.3.4.2  martin 	format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
    293  1.1.1.3.4.2  martin 	    list_left, list_right, focus_start, focus_end, frs);
    294  1.1.1.3.4.2  martin }
    295  1.1.1.3.4.2  martin 
    296  1.1.1.3.4.2  martin /* Draw format with list in the centre. */
    297  1.1.1.3.4.2  martin static void
    298  1.1.1.3.4.2  martin format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
    299  1.1.1.3.4.2  martin     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    300  1.1.1.3.4.2  martin     struct screen *list, struct screen *list_left, struct screen *list_right,
    301  1.1.1.3.4.2  martin     struct screen *after, int focus_start, int focus_end,
    302  1.1.1.3.4.2  martin     struct format_ranges *frs)
    303  1.1.1.3.4.2  martin {
    304  1.1.1.3.4.2  martin 	u_int			width_left, width_centre, width_right;
    305  1.1.1.3.4.2  martin 	u_int			width_list, width_after, middle;
    306  1.1.1.3.4.2  martin 	struct screen_write_ctx	ctx;
    307  1.1.1.3.4.2  martin 
    308  1.1.1.3.4.2  martin 	width_left = left->cx;
    309  1.1.1.3.4.2  martin 	width_centre = centre->cx;
    310  1.1.1.3.4.2  martin 	width_right = right->cx;
    311  1.1.1.3.4.2  martin 	width_list = list->cx;
    312  1.1.1.3.4.2  martin 	width_after = after->cx;
    313  1.1.1.3.4.2  martin 
    314  1.1.1.3.4.2  martin 	/*
    315  1.1.1.3.4.2  martin 	 * Trim first the list, then after the list, then the centre, then the
    316  1.1.1.3.4.2  martin 	 * right, then the left.
    317  1.1.1.3.4.2  martin 	 */
    318  1.1.1.3.4.2  martin 	while (width_left +
    319  1.1.1.3.4.2  martin 	    width_centre +
    320  1.1.1.3.4.2  martin 	    width_right +
    321  1.1.1.3.4.2  martin 	    width_list +
    322  1.1.1.3.4.2  martin 	    width_after > available) {
    323  1.1.1.3.4.2  martin 		if (width_list > 0)
    324  1.1.1.3.4.2  martin 			width_list--;
    325  1.1.1.3.4.2  martin 		else if (width_after > 0)
    326  1.1.1.3.4.2  martin 			width_after--;
    327  1.1.1.3.4.2  martin 		else if (width_centre > 0)
    328  1.1.1.3.4.2  martin 			width_centre--;
    329  1.1.1.3.4.2  martin 		else if (width_right > 0)
    330  1.1.1.3.4.2  martin 			width_right--;
    331  1.1.1.3.4.2  martin 		else
    332  1.1.1.3.4.2  martin 			width_left--;
    333  1.1.1.3.4.2  martin 	}
    334  1.1.1.3.4.2  martin 
    335  1.1.1.3.4.2  martin 	/* If there is no list left, pass off to the no list function. */
    336  1.1.1.3.4.2  martin 	if (width_list == 0) {
    337  1.1.1.3.4.2  martin 		screen_write_start(&ctx, NULL, centre);
    338  1.1.1.3.4.2  martin 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
    339  1.1.1.3.4.2  martin 		screen_write_stop(&ctx);
    340  1.1.1.3.4.2  martin 
    341  1.1.1.3.4.2  martin 		format_draw_none(octx, available, ocx, ocy, left, centre,
    342  1.1.1.3.4.2  martin 		    right, frs);
    343  1.1.1.3.4.2  martin 		return;
    344  1.1.1.3.4.2  martin 	}
    345  1.1.1.3.4.2  martin 
    346  1.1.1.3.4.2  martin 	/* Write left at 0. */
    347  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    348  1.1.1.3.4.2  martin 
    349  1.1.1.3.4.2  martin 	/* Write right at available - width_right. */
    350  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, right, frs,
    351  1.1.1.3.4.2  martin 	    available - width_right,
    352  1.1.1.3.4.2  martin 	    right->cx - width_right,
    353  1.1.1.3.4.2  martin 	    width_right);
    354  1.1.1.3.4.2  martin 
    355  1.1.1.3.4.2  martin 	/*
    356  1.1.1.3.4.2  martin 	 * All three centre sections are offset from the middle of the
    357  1.1.1.3.4.2  martin 	 * available space.
    358  1.1.1.3.4.2  martin 	 */
    359  1.1.1.3.4.2  martin 	middle = (width_left + ((available - width_right) - width_left) / 2);
    360  1.1.1.3.4.2  martin 
    361  1.1.1.3.4.2  martin 	/*
    362  1.1.1.3.4.2  martin 	 * Write centre at
    363  1.1.1.3.4.2  martin 	 *     middle - width_list / 2 - width_centre.
    364  1.1.1.3.4.2  martin 	 */
    365  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, centre, frs,
    366  1.1.1.3.4.2  martin 	    middle - width_list / 2 - width_centre,
    367  1.1.1.3.4.2  martin 	    0,
    368  1.1.1.3.4.2  martin 	    width_centre);
    369  1.1.1.3.4.2  martin 
    370  1.1.1.3.4.2  martin 	/*
    371  1.1.1.3.4.2  martin 	 * Write after at
    372  1.1.1.3.4.2  martin 	 *     middle - width_list / 2 + width_list
    373  1.1.1.3.4.2  martin 	 */
    374  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, after, frs,
    375  1.1.1.3.4.2  martin 	    middle - width_list / 2 + width_list,
    376  1.1.1.3.4.2  martin 	    0,
    377  1.1.1.3.4.2  martin 	    width_after);
    378  1.1.1.3.4.2  martin 
    379  1.1.1.3.4.2  martin 	/*
    380  1.1.1.3.4.2  martin 	 * The list now goes from
    381  1.1.1.3.4.2  martin 	 *     middle - width_list / 2
    382  1.1.1.3.4.2  martin 	 * to
    383  1.1.1.3.4.2  martin 	 *     middle + width_list / 2
    384  1.1.1.3.4.2  martin 	 * If there is no focus given, keep the centre in focus.
    385  1.1.1.3.4.2  martin 	 */
    386  1.1.1.3.4.2  martin 	if (focus_start == -1 || focus_end == -1)
    387  1.1.1.3.4.2  martin 		focus_start = focus_end = list->cx / 2;
    388  1.1.1.3.4.2  martin 	format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
    389  1.1.1.3.4.2  martin 	    width_list, list, list_left, list_right, focus_start, focus_end,
    390  1.1.1.3.4.2  martin 	    frs);
    391  1.1.1.3.4.2  martin }
    392  1.1.1.3.4.2  martin 
    393  1.1.1.3.4.2  martin /* Draw format with list on the right. */
    394  1.1.1.3.4.2  martin static void
    395  1.1.1.3.4.2  martin format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
    396  1.1.1.3.4.2  martin     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    397  1.1.1.3.4.2  martin     struct screen *list, struct screen *list_left, struct screen *list_right,
    398  1.1.1.3.4.2  martin     struct screen *after, int focus_start, int focus_end,
    399  1.1.1.3.4.2  martin     struct format_ranges *frs)
    400  1.1.1.3.4.2  martin {
    401  1.1.1.3.4.2  martin 	u_int			width_left, width_centre, width_right;
    402  1.1.1.3.4.2  martin 	u_int			width_list, width_after;
    403  1.1.1.3.4.2  martin 	struct screen_write_ctx	ctx;
    404  1.1.1.3.4.2  martin 
    405  1.1.1.3.4.2  martin 	width_left = left->cx;
    406  1.1.1.3.4.2  martin 	width_centre = centre->cx;
    407  1.1.1.3.4.2  martin 	width_right = right->cx;
    408  1.1.1.3.4.2  martin 	width_list = list->cx;
    409  1.1.1.3.4.2  martin 	width_after = after->cx;
    410  1.1.1.3.4.2  martin 
    411  1.1.1.3.4.2  martin 	/*
    412  1.1.1.3.4.2  martin 	 * Trim first the centre, then the list, then the right, then
    413  1.1.1.3.4.2  martin 	 * after the list, then the left.
    414  1.1.1.3.4.2  martin 	 */
    415  1.1.1.3.4.2  martin 	while (width_left +
    416  1.1.1.3.4.2  martin 	    width_centre +
    417  1.1.1.3.4.2  martin 	    width_right +
    418  1.1.1.3.4.2  martin 	    width_list +
    419  1.1.1.3.4.2  martin 	    width_after > available) {
    420  1.1.1.3.4.2  martin 		if (width_centre > 0)
    421  1.1.1.3.4.2  martin 			width_centre--;
    422  1.1.1.3.4.2  martin 		else if (width_list > 0)
    423  1.1.1.3.4.2  martin 			width_list--;
    424  1.1.1.3.4.2  martin 		else if (width_right > 0)
    425  1.1.1.3.4.2  martin 			width_right--;
    426  1.1.1.3.4.2  martin 		else if (width_after > 0)
    427  1.1.1.3.4.2  martin 			width_after--;
    428  1.1.1.3.4.2  martin 		else
    429  1.1.1.3.4.2  martin 			width_left--;
    430  1.1.1.3.4.2  martin 	}
    431  1.1.1.3.4.2  martin 
    432  1.1.1.3.4.2  martin 	/* If there is no list left, pass off to the no list function. */
    433  1.1.1.3.4.2  martin 	if (width_list == 0) {
    434  1.1.1.3.4.2  martin 		screen_write_start(&ctx, NULL, right);
    435  1.1.1.3.4.2  martin 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
    436  1.1.1.3.4.2  martin 		screen_write_stop(&ctx);
    437  1.1.1.3.4.2  martin 
    438  1.1.1.3.4.2  martin 		format_draw_none(octx, available, ocx, ocy, left, centre,
    439  1.1.1.3.4.2  martin 		    right, frs);
    440  1.1.1.3.4.2  martin 		return;
    441  1.1.1.3.4.2  martin 	}
    442  1.1.1.3.4.2  martin 
    443  1.1.1.3.4.2  martin 	/* Write left at 0. */
    444  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    445  1.1.1.3.4.2  martin 
    446  1.1.1.3.4.2  martin 	/* Write after at available - width_after. */
    447  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, after, frs,
    448  1.1.1.3.4.2  martin 	    available - width_after,
    449  1.1.1.3.4.2  martin 	    after->cx - width_after,
    450  1.1.1.3.4.2  martin 	    width_after);
    451  1.1.1.3.4.2  martin 
    452  1.1.1.3.4.2  martin 	/*
    453  1.1.1.3.4.2  martin 	 * Write right at
    454  1.1.1.3.4.2  martin 	 *     available - width_right - width_list - width_after.
    455  1.1.1.3.4.2  martin 	 */
    456  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, right, frs,
    457  1.1.1.3.4.2  martin 	    available - width_right - width_list - width_after,
    458  1.1.1.3.4.2  martin 	    0,
    459  1.1.1.3.4.2  martin 	    width_right);
    460  1.1.1.3.4.2  martin 
    461  1.1.1.3.4.2  martin 	/*
    462  1.1.1.3.4.2  martin 	 * Write centre halfway between
    463  1.1.1.3.4.2  martin 	 *     width_left
    464  1.1.1.3.4.2  martin 	 * and
    465  1.1.1.3.4.2  martin 	 *     available - width_right - width_list - width_after.
    466  1.1.1.3.4.2  martin 	 */
    467  1.1.1.3.4.2  martin 	format_draw_put(octx, ocx, ocy, centre, frs,
    468  1.1.1.3.4.2  martin 	    width_left
    469  1.1.1.3.4.2  martin 	    + ((available - width_right - width_list - width_after)
    470  1.1.1.3.4.2  martin 		- width_left) / 2
    471  1.1.1.3.4.2  martin 	    - width_centre / 2,
    472  1.1.1.3.4.2  martin 	    centre->cx / 2 - width_centre / 2,
    473  1.1.1.3.4.2  martin 	    width_centre);
    474  1.1.1.3.4.2  martin 
    475  1.1.1.3.4.2  martin 	/*
    476  1.1.1.3.4.2  martin 	 * The list now goes from
    477  1.1.1.3.4.2  martin 	 *     available - width_list - width_after
    478  1.1.1.3.4.2  martin 	 * to
    479  1.1.1.3.4.2  martin 	 *     available - width_after
    480  1.1.1.3.4.2  martin 	 * If there is no focus given, keep the right in focus.
    481  1.1.1.3.4.2  martin 	 */
    482  1.1.1.3.4.2  martin 	if (focus_start == -1 || focus_end == -1)
    483  1.1.1.3.4.2  martin 		focus_start = focus_end = 0;
    484  1.1.1.3.4.2  martin 	format_draw_put_list(octx, ocx, ocy, available - width_list -
    485  1.1.1.3.4.2  martin 	    width_after, width_list, list, list_left, list_right, focus_start,
    486  1.1.1.3.4.2  martin 	    focus_end, frs);
    487  1.1.1.3.4.2  martin }
    488  1.1.1.3.4.2  martin 
    489  1.1.1.3.4.2  martin /* Draw a format to a screen. */
    490  1.1.1.3.4.2  martin void
    491  1.1.1.3.4.2  martin format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
    492  1.1.1.3.4.2  martin     u_int available, const char *expanded, struct style_ranges *srs)
    493  1.1.1.3.4.2  martin {
    494  1.1.1.3.4.2  martin 	enum { LEFT,
    495  1.1.1.3.4.2  martin 	       CENTRE,
    496  1.1.1.3.4.2  martin 	       RIGHT,
    497  1.1.1.3.4.2  martin 	       LIST,
    498  1.1.1.3.4.2  martin 	       LIST_LEFT,
    499  1.1.1.3.4.2  martin 	       LIST_RIGHT,
    500  1.1.1.3.4.2  martin 	       AFTER,
    501  1.1.1.3.4.2  martin 	       TOTAL } current = LEFT, last = LEFT;
    502  1.1.1.3.4.2  martin 	const char	        *names[] = { "LEFT",
    503  1.1.1.3.4.2  martin 					     "CENTRE",
    504  1.1.1.3.4.2  martin 					     "RIGHT",
    505  1.1.1.3.4.2  martin 					     "LIST",
    506  1.1.1.3.4.2  martin 					     "LIST_LEFT",
    507  1.1.1.3.4.2  martin 					     "LIST_RIGHT",
    508  1.1.1.3.4.2  martin 					     "AFTER" };
    509  1.1.1.3.4.2  martin 	size_t			 size = strlen(expanded);
    510  1.1.1.3.4.2  martin 	struct screen		*os = octx->s, s[TOTAL];
    511  1.1.1.3.4.2  martin 	struct screen_write_ctx	 ctx[TOTAL];
    512  1.1.1.3.4.2  martin 	u_int			 ocx = os->cx, ocy = os->cy, i, width[TOTAL];
    513  1.1.1.3.4.2  martin 	u_int			 map[] = { LEFT, LEFT, CENTRE, RIGHT };
    514  1.1.1.3.4.2  martin 	int			 focus_start = -1, focus_end = -1;
    515  1.1.1.3.4.2  martin 	int			 list_state = -1, fill = -1;
    516  1.1.1.3.4.2  martin 	enum style_align	 list_align = STYLE_ALIGN_DEFAULT;
    517  1.1.1.3.4.2  martin 	struct grid_cell	 gc, current_default;
    518  1.1.1.3.4.2  martin 	struct style		 sy, saved_sy;
    519  1.1.1.3.4.2  martin 	struct utf8_data	*ud = &sy.gc.data;
    520  1.1.1.3.4.2  martin 	const char		*cp, *end;
    521  1.1.1.3.4.2  martin 	enum utf8_state		 more;
    522  1.1.1.3.4.2  martin 	char			*tmp;
    523  1.1.1.3.4.2  martin 	struct format_range	*fr = NULL, *fr1;
    524  1.1.1.3.4.2  martin 	struct format_ranges	 frs;
    525  1.1.1.3.4.2  martin 	struct style_range	*sr;
    526  1.1.1.3.4.2  martin 
    527  1.1.1.3.4.2  martin 	memcpy(&current_default, base, sizeof current_default);
    528  1.1.1.3.4.2  martin 	style_set(&sy, &current_default);
    529  1.1.1.3.4.2  martin 	TAILQ_INIT(&frs);
    530  1.1.1.3.4.2  martin 	log_debug("%s: %s", __func__, expanded);
    531  1.1.1.3.4.2  martin 
    532  1.1.1.3.4.2  martin 	/*
    533  1.1.1.3.4.2  martin 	 * We build three screens for left, right, centre alignment, one for
    534  1.1.1.3.4.2  martin 	 * the list, one for anything after the list and two for the list left
    535  1.1.1.3.4.2  martin 	 * and right markers.
    536  1.1.1.3.4.2  martin 	 */
    537  1.1.1.3.4.2  martin 	for (i = 0; i < TOTAL; i++) {
    538  1.1.1.3.4.2  martin 		screen_init(&s[i], size, 1, 0);
    539  1.1.1.3.4.2  martin 		screen_write_start(&ctx[i], NULL, &s[i]);
    540  1.1.1.3.4.2  martin 		screen_write_clearendofline(&ctx[i], current_default.bg);
    541  1.1.1.3.4.2  martin 		width[i] = 0;
    542  1.1.1.3.4.2  martin 	}
    543  1.1.1.3.4.2  martin 
    544  1.1.1.3.4.2  martin 	/*
    545  1.1.1.3.4.2  martin 	 * Walk the string and add to the corresponding screens,
    546  1.1.1.3.4.2  martin 	 * parsing styles as we go.
    547  1.1.1.3.4.2  martin 	 */
    548  1.1.1.3.4.2  martin 	cp = expanded;
    549  1.1.1.3.4.2  martin 	while (*cp != '\0') {
    550  1.1.1.3.4.2  martin 		if (cp[0] != '#' || cp[1] != '[') {
    551  1.1.1.3.4.2  martin 			/* See if this is a UTF-8 character. */
    552  1.1.1.3.4.2  martin 			if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
    553  1.1.1.3.4.2  martin 				while (*++cp != '\0' && more == UTF8_MORE)
    554  1.1.1.3.4.2  martin 					more = utf8_append(ud, *cp);
    555  1.1.1.3.4.2  martin 				if (more != UTF8_DONE)
    556  1.1.1.3.4.2  martin 					cp -= ud->have;
    557  1.1.1.3.4.2  martin 			}
    558  1.1.1.3.4.2  martin 
    559  1.1.1.3.4.2  martin 			/* Not a UTF-8 character - ASCII or not valid. */
    560  1.1.1.3.4.2  martin 			if (more != UTF8_DONE) {
    561  1.1.1.3.4.2  martin 				if (*cp < 0x20 || *cp > 0x7e) {
    562  1.1.1.3.4.2  martin 					/* Ignore nonprintable characters. */
    563  1.1.1.3.4.2  martin 					cp++;
    564  1.1.1.3.4.2  martin 					continue;
    565  1.1.1.3.4.2  martin 				}
    566  1.1.1.3.4.2  martin 				utf8_set(ud, *cp);
    567  1.1.1.3.4.2  martin 				cp++;
    568  1.1.1.3.4.2  martin 			}
    569  1.1.1.3.4.2  martin 
    570  1.1.1.3.4.2  martin 			/* Draw the cell to the current screen. */
    571  1.1.1.3.4.2  martin 			screen_write_cell(&ctx[current], &sy.gc);
    572  1.1.1.3.4.2  martin 			width[current] += ud->width;
    573  1.1.1.3.4.2  martin 			continue;
    574  1.1.1.3.4.2  martin 		}
    575  1.1.1.3.4.2  martin 
    576  1.1.1.3.4.2  martin 		/* This is a style. Work out where the end is and parse it. */
    577  1.1.1.3.4.2  martin 		end = format_skip(cp + 2, "]");
    578  1.1.1.3.4.2  martin 		if (end == NULL) {
    579  1.1.1.3.4.2  martin 			log_debug("%s: no terminating ] at '%s'", __func__,
    580  1.1.1.3.4.2  martin 			    cp + 2);
    581  1.1.1.3.4.2  martin 			TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
    582  1.1.1.3.4.2  martin 			    format_free_range(&frs, fr);
    583  1.1.1.3.4.2  martin 			goto out;
    584  1.1.1.3.4.2  martin 		}
    585  1.1.1.3.4.2  martin 		tmp = xstrndup(cp + 2, end - (cp + 2));
    586  1.1.1.3.4.2  martin 		style_copy(&saved_sy, &sy);
    587  1.1.1.3.4.2  martin 		if (style_parse(&sy, &current_default, tmp) != 0) {
    588  1.1.1.3.4.2  martin 			log_debug("%s: invalid style '%s'", __func__, tmp);
    589  1.1.1.3.4.2  martin 			free(tmp);
    590  1.1.1.3.4.2  martin 			cp = end + 1;
    591  1.1.1.3.4.2  martin 			continue;
    592  1.1.1.3.4.2  martin 		}
    593  1.1.1.3.4.2  martin 		log_debug("%s: style '%s' -> '%s'", __func__, tmp,
    594  1.1.1.3.4.2  martin 		    style_tostring(&sy));
    595  1.1.1.3.4.2  martin 		free(tmp);
    596  1.1.1.3.4.2  martin 
    597  1.1.1.3.4.2  martin 		/* If this style has a fill colour, store it for later. */
    598  1.1.1.3.4.2  martin 		if (sy.fill != 8)
    599  1.1.1.3.4.2  martin 			fill = sy.fill;
    600  1.1.1.3.4.2  martin 
    601  1.1.1.3.4.2  martin 		/* If this style pushed or popped the default, update it. */
    602  1.1.1.3.4.2  martin 		if (sy.default_type == STYLE_DEFAULT_PUSH) {
    603  1.1.1.3.4.2  martin 			memcpy(&current_default, &saved_sy.gc, sizeof current_default);
    604  1.1.1.3.4.2  martin 			sy.default_type = STYLE_DEFAULT_BASE;
    605  1.1.1.3.4.2  martin 		} else if (sy.default_type == STYLE_DEFAULT_POP) {
    606  1.1.1.3.4.2  martin 			memcpy(&current_default, base, sizeof current_default);
    607  1.1.1.3.4.2  martin 			sy.default_type = STYLE_DEFAULT_BASE;
    608  1.1.1.3.4.2  martin 		}
    609  1.1.1.3.4.2  martin 
    610  1.1.1.3.4.2  martin 		/* Check the list state. */
    611  1.1.1.3.4.2  martin 		switch (sy.list) {
    612  1.1.1.3.4.2  martin 		case STYLE_LIST_ON:
    613  1.1.1.3.4.2  martin 			/*
    614  1.1.1.3.4.2  martin 			 * Entering the list, exiting a marker, or exiting the
    615  1.1.1.3.4.2  martin 			 * focus.
    616  1.1.1.3.4.2  martin 			 */
    617  1.1.1.3.4.2  martin 			if (list_state != 0) {
    618  1.1.1.3.4.2  martin 				if (fr != NULL) { /* abort any region */
    619  1.1.1.3.4.2  martin 					free(fr);
    620  1.1.1.3.4.2  martin 					fr = NULL;
    621  1.1.1.3.4.2  martin 				}
    622  1.1.1.3.4.2  martin 				list_state = 0;
    623  1.1.1.3.4.2  martin 				list_align = sy.align;
    624  1.1.1.3.4.2  martin 			}
    625  1.1.1.3.4.2  martin 
    626  1.1.1.3.4.2  martin 			/* End the focus if started. */
    627  1.1.1.3.4.2  martin 			if (focus_start != -1 && focus_end == -1)
    628  1.1.1.3.4.2  martin 				focus_end = s[LIST].cx;
    629  1.1.1.3.4.2  martin 
    630  1.1.1.3.4.2  martin 			current = LIST;
    631  1.1.1.3.4.2  martin 			break;
    632  1.1.1.3.4.2  martin 		case STYLE_LIST_FOCUS:
    633  1.1.1.3.4.2  martin 			/* Entering the focus. */
    634  1.1.1.3.4.2  martin 			if (list_state != 0) /* not inside the list */
    635  1.1.1.3.4.2  martin 				break;
    636  1.1.1.3.4.2  martin 			if (focus_start == -1) /* focus already started */
    637  1.1.1.3.4.2  martin 				focus_start = s[LIST].cx;
    638  1.1.1.3.4.2  martin 			break;
    639  1.1.1.3.4.2  martin 		case STYLE_LIST_OFF:
    640  1.1.1.3.4.2  martin 			/* Exiting or outside the list. */
    641  1.1.1.3.4.2  martin 			if (list_state == 0) {
    642  1.1.1.3.4.2  martin 				if (fr != NULL) { /* abort any region */
    643  1.1.1.3.4.2  martin 					free(fr);
    644  1.1.1.3.4.2  martin 					fr = NULL;
    645  1.1.1.3.4.2  martin 				}
    646  1.1.1.3.4.2  martin 				if (focus_start != -1 && focus_end == -1)
    647  1.1.1.3.4.2  martin 					focus_end = s[LIST].cx;
    648  1.1.1.3.4.2  martin 
    649  1.1.1.3.4.2  martin 				map[list_align] = AFTER;
    650  1.1.1.3.4.2  martin 				if (list_align == STYLE_ALIGN_LEFT)
    651  1.1.1.3.4.2  martin 					map[STYLE_ALIGN_DEFAULT] = AFTER;
    652  1.1.1.3.4.2  martin 				list_state = 1;
    653  1.1.1.3.4.2  martin 			}
    654  1.1.1.3.4.2  martin 			current = map[sy.align];
    655  1.1.1.3.4.2  martin 			break;
    656  1.1.1.3.4.2  martin 		case STYLE_LIST_LEFT_MARKER:
    657  1.1.1.3.4.2  martin 			/* Entering left marker. */
    658  1.1.1.3.4.2  martin 			if (list_state != 0) /* not inside the list */
    659  1.1.1.3.4.2  martin 				break;
    660  1.1.1.3.4.2  martin 			if (s[LIST_LEFT].cx != 0) /* already have marker */
    661  1.1.1.3.4.2  martin 				break;
    662  1.1.1.3.4.2  martin 			if (fr != NULL) { /* abort any region */
    663  1.1.1.3.4.2  martin 				free(fr);
    664  1.1.1.3.4.2  martin 				fr = NULL;
    665  1.1.1.3.4.2  martin 			}
    666  1.1.1.3.4.2  martin 			if (focus_start != -1 && focus_end == -1)
    667  1.1.1.3.4.2  martin 				focus_start = focus_end = -1;
    668  1.1.1.3.4.2  martin 			current = LIST_LEFT;
    669  1.1.1.3.4.2  martin 			break;
    670  1.1.1.3.4.2  martin 		case STYLE_LIST_RIGHT_MARKER:
    671  1.1.1.3.4.2  martin 			/* Entering right marker. */
    672  1.1.1.3.4.2  martin 			if (list_state != 0) /* not inside the list */
    673  1.1.1.3.4.2  martin 				break;
    674  1.1.1.3.4.2  martin 			if (s[LIST_RIGHT].cx != 0) /* already have marker */
    675  1.1.1.3.4.2  martin 				break;
    676  1.1.1.3.4.2  martin 			if (fr != NULL) { /* abort any region */
    677  1.1.1.3.4.2  martin 				free(fr);
    678  1.1.1.3.4.2  martin 				fr = NULL;
    679  1.1.1.3.4.2  martin 			}
    680  1.1.1.3.4.2  martin 			if (focus_start != -1 && focus_end == -1)
    681  1.1.1.3.4.2  martin 				focus_start = focus_end = -1;
    682  1.1.1.3.4.2  martin 			current = LIST_RIGHT;
    683  1.1.1.3.4.2  martin 			break;
    684  1.1.1.3.4.2  martin 		}
    685  1.1.1.3.4.2  martin 		if (current != last) {
    686  1.1.1.3.4.2  martin 			log_debug("%s: change %s -> %s", __func__,
    687  1.1.1.3.4.2  martin 			    names[last], names[current]);
    688  1.1.1.3.4.2  martin 			last = current;
    689  1.1.1.3.4.2  martin 		}
    690  1.1.1.3.4.2  martin 
    691  1.1.1.3.4.2  martin 		/*
    692  1.1.1.3.4.2  martin 		 * Check if the range style has changed and if so end the
    693  1.1.1.3.4.2  martin 		 * current range and start a new one if needed.
    694  1.1.1.3.4.2  martin 		 */
    695  1.1.1.3.4.2  martin 		if (srs != NULL) {
    696  1.1.1.3.4.2  martin 			if (fr != NULL && !format_is_type(fr, &sy)) {
    697  1.1.1.3.4.2  martin 				if (s[current].cx != fr->start) {
    698  1.1.1.3.4.2  martin 					fr->end = s[current].cx + 1;
    699  1.1.1.3.4.2  martin 					TAILQ_INSERT_TAIL(&frs, fr, entry);
    700  1.1.1.3.4.2  martin 				} else
    701  1.1.1.3.4.2  martin 					free(fr);
    702  1.1.1.3.4.2  martin 				fr = NULL;
    703  1.1.1.3.4.2  martin 			}
    704  1.1.1.3.4.2  martin 			if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
    705  1.1.1.3.4.2  martin 				fr = xcalloc(1, sizeof *fr);
    706  1.1.1.3.4.2  martin 				fr->index = current;
    707  1.1.1.3.4.2  martin 
    708  1.1.1.3.4.2  martin 				fr->s = &s[current];
    709  1.1.1.3.4.2  martin 				fr->start = s[current].cx;
    710  1.1.1.3.4.2  martin 
    711  1.1.1.3.4.2  martin 				fr->type = sy.range_type;
    712  1.1.1.3.4.2  martin 				fr->argument = sy.range_argument;
    713  1.1.1.3.4.2  martin 			}
    714  1.1.1.3.4.2  martin 		}
    715  1.1.1.3.4.2  martin 
    716  1.1.1.3.4.2  martin 		cp = end + 1;
    717  1.1.1.3.4.2  martin 	}
    718  1.1.1.3.4.2  martin 	free(fr);
    719  1.1.1.3.4.2  martin 
    720  1.1.1.3.4.2  martin 	for (i = 0; i < TOTAL; i++) {
    721  1.1.1.3.4.2  martin 		screen_write_stop(&ctx[i]);
    722  1.1.1.3.4.2  martin 		log_debug("%s: width %s is %u", __func__, names[i], width[i]);
    723  1.1.1.3.4.2  martin 	}
    724  1.1.1.3.4.2  martin 	if (focus_start != -1 && focus_end != -1)
    725  1.1.1.3.4.2  martin 		log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
    726  1.1.1.3.4.2  martin 	TAILQ_FOREACH(fr, &frs, entry) {
    727  1.1.1.3.4.2  martin 		log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
    728  1.1.1.3.4.2  martin 		    fr->argument, names[fr->index], fr->start, fr->end);
    729  1.1.1.3.4.2  martin 	}
    730  1.1.1.3.4.2  martin 
    731  1.1.1.3.4.2  martin 	/* Clear the available area. */
    732  1.1.1.3.4.2  martin 	if (fill != -1) {
    733  1.1.1.3.4.2  martin 		memcpy(&gc, &grid_default_cell, sizeof gc);
    734  1.1.1.3.4.2  martin 		gc.bg = fill;
    735  1.1.1.3.4.2  martin 		for (i = 0; i < available; i++)
    736  1.1.1.3.4.2  martin 			screen_write_putc(octx, &gc, ' ');
    737  1.1.1.3.4.2  martin 	}
    738  1.1.1.3.4.2  martin 
    739  1.1.1.3.4.2  martin 	/*
    740  1.1.1.3.4.2  martin 	 * Draw the screens. How they are arranged depends on where the list
    741  1.1.1.3.4.2  martin 	 * appearsq.
    742  1.1.1.3.4.2  martin 	 */
    743  1.1.1.3.4.2  martin 	switch (list_align) {
    744  1.1.1.3.4.2  martin 	case STYLE_ALIGN_DEFAULT:
    745  1.1.1.3.4.2  martin 		/* No list. */
    746  1.1.1.3.4.2  martin 		format_draw_none(octx, available, ocx, ocy, &s[LEFT],
    747  1.1.1.3.4.2  martin 		    &s[CENTRE], &s[RIGHT], &frs);
    748  1.1.1.3.4.2  martin 		break;
    749  1.1.1.3.4.2  martin 	case STYLE_ALIGN_LEFT:
    750  1.1.1.3.4.2  martin 		/* List is part of the left. */
    751  1.1.1.3.4.2  martin 		format_draw_left(octx, available, ocx, ocy, &s[LEFT],
    752  1.1.1.3.4.2  martin 		    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
    753  1.1.1.3.4.2  martin 		    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
    754  1.1.1.3.4.2  martin 		break;
    755  1.1.1.3.4.2  martin 	case STYLE_ALIGN_CENTRE:
    756  1.1.1.3.4.2  martin 		/* List is part of the centre. */
    757  1.1.1.3.4.2  martin 		format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
    758  1.1.1.3.4.2  martin 		    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
    759  1.1.1.3.4.2  martin 		    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
    760  1.1.1.3.4.2  martin 		break;
    761  1.1.1.3.4.2  martin 	case STYLE_ALIGN_RIGHT:
    762  1.1.1.3.4.2  martin 		/* List is part of the right. */
    763  1.1.1.3.4.2  martin 		format_draw_right(octx, available, ocx, ocy, &s[LEFT],
    764  1.1.1.3.4.2  martin 		    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
    765  1.1.1.3.4.2  martin 		    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
    766  1.1.1.3.4.2  martin 		break;
    767  1.1.1.3.4.2  martin 	}
    768  1.1.1.3.4.2  martin 
    769  1.1.1.3.4.2  martin 	/* Create ranges to return. */
    770  1.1.1.3.4.2  martin 	TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
    771  1.1.1.3.4.2  martin 		sr = xcalloc(1, sizeof *sr);
    772  1.1.1.3.4.2  martin 		sr->type = fr->type;
    773  1.1.1.3.4.2  martin 		sr->argument = fr->argument;
    774  1.1.1.3.4.2  martin 		sr->start = fr->start;
    775  1.1.1.3.4.2  martin 		sr->end = fr->end;
    776  1.1.1.3.4.2  martin 		TAILQ_INSERT_TAIL(srs, sr, entry);
    777  1.1.1.3.4.2  martin 
    778  1.1.1.3.4.2  martin 		log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
    779  1.1.1.3.4.2  martin 		    sr->argument, sr->start, sr->end);
    780  1.1.1.3.4.2  martin 
    781  1.1.1.3.4.2  martin 		format_free_range(&frs, fr);
    782  1.1.1.3.4.2  martin 	}
    783  1.1.1.3.4.2  martin 
    784  1.1.1.3.4.2  martin out:
    785  1.1.1.3.4.2  martin 	/* Free the screens. */
    786  1.1.1.3.4.2  martin 	for (i = 0; i < TOTAL; i++)
    787  1.1.1.3.4.2  martin 		screen_free(&s[i]);
    788  1.1.1.3.4.2  martin 
    789  1.1.1.3.4.2  martin 	/* Restore the original cursor position. */
    790  1.1.1.3.4.2  martin 	screen_write_cursormove(octx, ocx, ocy, 0);
    791  1.1.1.3.4.2  martin }
    792  1.1.1.3.4.2  martin 
    793  1.1.1.3.4.2  martin /* Get width, taking #[] into account. */
    794  1.1.1.3.4.2  martin u_int
    795  1.1.1.3.4.2  martin format_width(const char *expanded)
    796  1.1.1.3.4.2  martin {
    797  1.1.1.3.4.2  martin 	const char		*cp, *end;
    798  1.1.1.3.4.2  martin 	u_int			 width = 0;
    799  1.1.1.3.4.2  martin 	struct utf8_data	 ud;
    800  1.1.1.3.4.2  martin 	enum utf8_state		 more;
    801  1.1.1.3.4.2  martin 
    802  1.1.1.3.4.2  martin 	cp = expanded;
    803  1.1.1.3.4.2  martin 	while (*cp != '\0') {
    804  1.1.1.3.4.2  martin 		if (cp[0] == '#' && cp[1] == '[') {
    805  1.1.1.3.4.2  martin 			end = format_skip(cp + 2, "]");
    806  1.1.1.3.4.2  martin 			if (end == NULL)
    807  1.1.1.3.4.2  martin 				return 0;
    808  1.1.1.3.4.2  martin 			cp = end + 1;
    809  1.1.1.3.4.2  martin 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
    810  1.1.1.3.4.2  martin 			while (*++cp != '\0' && more == UTF8_MORE)
    811  1.1.1.3.4.2  martin 				more = utf8_append(&ud, *cp);
    812  1.1.1.3.4.2  martin 			if (more == UTF8_DONE)
    813  1.1.1.3.4.2  martin 				width += ud.width;
    814  1.1.1.3.4.2  martin 			else
    815  1.1.1.3.4.2  martin 				cp -= ud.have;
    816  1.1.1.3.4.2  martin 		} else if (*cp > 0x1f && *cp < 0x7f) {
    817  1.1.1.3.4.2  martin 			width++;
    818  1.1.1.3.4.2  martin 			cp++;
    819  1.1.1.3.4.2  martin 		} else
    820  1.1.1.3.4.2  martin 			cp++;
    821  1.1.1.3.4.2  martin 	}
    822  1.1.1.3.4.2  martin 	return (width);
    823  1.1.1.3.4.2  martin }
    824  1.1.1.3.4.2  martin 
    825  1.1.1.3.4.2  martin /* Trim on the left, taking #[] into account. */
    826  1.1.1.3.4.2  martin char *
    827  1.1.1.3.4.2  martin format_trim_left(const char *expanded, u_int limit)
    828  1.1.1.3.4.2  martin {
    829  1.1.1.3.4.2  martin 	char			*copy, *out;
    830  1.1.1.3.4.2  martin 	const char		*cp = expanded, *end;
    831  1.1.1.3.4.2  martin 	u_int			 width = 0;
    832  1.1.1.3.4.2  martin 	struct utf8_data	 ud;
    833  1.1.1.3.4.2  martin 	enum utf8_state		 more;
    834  1.1.1.3.4.2  martin 
    835  1.1.1.3.4.2  martin 	out = copy = xmalloc(strlen(expanded) + 1);
    836  1.1.1.3.4.2  martin 	while (*cp != '\0') {
    837  1.1.1.3.4.2  martin 		if (cp[0] == '#' && cp[1] == '[') {
    838  1.1.1.3.4.2  martin 			end = format_skip(cp + 2, "]");
    839  1.1.1.3.4.2  martin 			if (end == NULL)
    840  1.1.1.3.4.2  martin 				break;
    841  1.1.1.3.4.2  martin 			memcpy(out, cp, end + 1 - cp);
    842  1.1.1.3.4.2  martin 			out += (end + 1 - cp);
    843  1.1.1.3.4.2  martin 			cp = end + 1;
    844  1.1.1.3.4.2  martin 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
    845  1.1.1.3.4.2  martin 			while (*++cp != '\0' && more == UTF8_MORE)
    846  1.1.1.3.4.2  martin 				more = utf8_append(&ud, *cp);
    847  1.1.1.3.4.2  martin 			if (more == UTF8_DONE) {
    848  1.1.1.3.4.2  martin 				if (width + ud.width <= limit) {
    849  1.1.1.3.4.2  martin 					memcpy(out, ud.data, ud.size);
    850  1.1.1.3.4.2  martin 					out += ud.size;
    851  1.1.1.3.4.2  martin 				}
    852  1.1.1.3.4.2  martin 				width += ud.width;
    853  1.1.1.3.4.2  martin 			} else {
    854  1.1.1.3.4.2  martin 				cp -= ud.have;
    855  1.1.1.3.4.2  martin 				cp++;
    856  1.1.1.3.4.2  martin 			}
    857  1.1.1.3.4.2  martin 		} else if (*cp > 0x1f && *cp < 0x7f) {
    858  1.1.1.3.4.2  martin 			if (width + 1 <= limit)
    859  1.1.1.3.4.2  martin 				*out++ = *cp;
    860  1.1.1.3.4.2  martin 			width++;
    861  1.1.1.3.4.2  martin 			cp++;
    862  1.1.1.3.4.2  martin 		} else
    863  1.1.1.3.4.2  martin 			cp++;
    864  1.1.1.3.4.2  martin 	}
    865  1.1.1.3.4.2  martin 	*out = '\0';
    866  1.1.1.3.4.2  martin 	return (copy);
    867  1.1.1.3.4.2  martin }
    868  1.1.1.3.4.2  martin 
    869  1.1.1.3.4.2  martin /* Trim on the right, taking #[] into account. */
    870  1.1.1.3.4.2  martin char *
    871  1.1.1.3.4.2  martin format_trim_right(const char *expanded, u_int limit)
    872  1.1.1.3.4.2  martin {
    873  1.1.1.3.4.2  martin 	char			*copy, *out;
    874  1.1.1.3.4.2  martin 	const char		*cp = expanded, *end;
    875  1.1.1.3.4.2  martin 	u_int			 width = 0, total_width, skip;
    876  1.1.1.3.4.2  martin 	struct utf8_data	 ud;
    877  1.1.1.3.4.2  martin 	enum utf8_state		 more;
    878  1.1.1.3.4.2  martin 
    879  1.1.1.3.4.2  martin 	total_width = format_width(expanded);
    880  1.1.1.3.4.2  martin 	if (total_width <= limit)
    881  1.1.1.3.4.2  martin 		return (xstrdup(expanded));
    882  1.1.1.3.4.2  martin 	skip = total_width - limit;
    883  1.1.1.3.4.2  martin 
    884  1.1.1.3.4.2  martin 	out = copy = xmalloc(strlen(expanded) + 1);
    885  1.1.1.3.4.2  martin 	while (*cp != '\0') {
    886  1.1.1.3.4.2  martin 		if (cp[0] == '#' && cp[1] == '[') {
    887  1.1.1.3.4.2  martin 			end = format_skip(cp + 2, "]");
    888  1.1.1.3.4.2  martin 			if (end == NULL)
    889  1.1.1.3.4.2  martin 				break;
    890  1.1.1.3.4.2  martin 			memcpy(out, cp, end + 1 - cp);
    891  1.1.1.3.4.2  martin 			out += (end + 1 - cp);
    892  1.1.1.3.4.2  martin 			cp = end + 1;
    893  1.1.1.3.4.2  martin 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
    894  1.1.1.3.4.2  martin 			while (*++cp != '\0' && more == UTF8_MORE)
    895  1.1.1.3.4.2  martin 				more = utf8_append(&ud, *cp);
    896  1.1.1.3.4.2  martin 			if (more == UTF8_DONE) {
    897  1.1.1.3.4.2  martin 				if (width >= skip) {
    898  1.1.1.3.4.2  martin 					memcpy(out, ud.data, ud.size);
    899  1.1.1.3.4.2  martin 					out += ud.size;
    900  1.1.1.3.4.2  martin 				}
    901  1.1.1.3.4.2  martin 				width += ud.width;
    902  1.1.1.3.4.2  martin 			} else {
    903  1.1.1.3.4.2  martin 				cp -= ud.have;
    904  1.1.1.3.4.2  martin 				cp++;
    905  1.1.1.3.4.2  martin 			}
    906  1.1.1.3.4.2  martin 		} else if (*cp > 0x1f && *cp < 0x7f) {
    907  1.1.1.3.4.2  martin 			if (width >= skip)
    908  1.1.1.3.4.2  martin 				*out++ = *cp;
    909  1.1.1.3.4.2  martin 			width++;
    910  1.1.1.3.4.2  martin 			cp++;
    911  1.1.1.3.4.2  martin 		} else
    912  1.1.1.3.4.2  martin 			cp++;
    913  1.1.1.3.4.2  martin 	}
    914  1.1.1.3.4.2  martin 	*out = '\0';
    915  1.1.1.3.4.2  martin 	return (copy);
    916  1.1.1.3.4.2  martin }
    917