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