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.7 wiz 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.7 wiz memcpy(&base_default, base, sizeof base_default); 733 1.1.1.3 christos memcpy(¤t_default, base, sizeof current_default); 734 1.1.1.7 wiz 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.7 wiz } else if (sy.default_type == STYLE_DEFAULT_SET) { 853 1.1.1.7 wiz memcpy(&base_default, &saved_sy.gc, 854 1.1.1.7 wiz sizeof base_default); 855 1.1.1.7 wiz memcpy(¤t_default, &saved_sy.gc, 856 1.1.1.7 wiz sizeof current_default); 857 1.1.1.7 wiz 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