/* Move a line across. */ static struct grid_line * grid_reflow_move(struct grid *gd, struct grid_line *from) { struct grid_line *to; to = grid_reflow_add(gd, 1); memcpy(to, from, sizeof *to); grid_reflow_dead(from); return (to); }
/* Reflow lines on grid to new width. */ void grid_reflow(struct grid *gd, u_int sx) { struct grid *target; struct grid_line *gl; struct grid_cell gc; u_int yy, width, i, at, first; /* * Create a destination grid. This is just used as a container for the * line data and may not be fully valid. */ target = grid_create(gd->sx, 0, 0); /* * Loop over each source line. */ for (yy = 0; yy < gd->hsize + gd->sy; yy++) { gl = &gd->linedata[yy]; if (gl->flags & GRID_LINE_DEAD) continue; /* * Work out the width of this line. first is the width of the * first character, at is the point at which the available * width is hit, and width is the full line width. */ first = at = width = 0; if (~gl->flags & GRID_LINE_EXTENDED) { first = 1; width = gl->cellused; if (width > sx) at = sx; else at = width; } else { for (i = 0; i < gl->cellused; i++) { grid_get_cell1(gl, i, &gc); if (i == 0) first = gc.data.width; if (at == 0 && width + gc.data.width > sx) at = i; width += gc.data.width; } } /* * If the line is exactly right or the first character is wider * than the targe width, just move it across unchanged. */ if (width == sx || first > sx) { grid_reflow_move(target, gl); continue; } /* * If the line is too big, it needs to be split, whether or not * it was previously wrapped. */ if (width > sx) { grid_reflow_split(target, gd, sx, yy, at); continue; } /* * If the line was previously wrapped, join as much as possible * of the next line. */ if (gl->flags & GRID_LINE_WRAPPED) grid_reflow_join(target, gd, sx, yy, width, 0); else grid_reflow_move(target, gl); } /* * Replace the old grid with the new. */ if (target->sy < gd->sy) grid_reflow_add(target, gd->sy - target->sy); gd->hsize = target->sy - gd->sy; if (gd->hscrolled > gd->hsize) gd->hscrolled = gd->hsize; free(gd->linedata); gd->linedata = target->linedata; free(target); }
/* Split this line into several new ones */ static void grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, u_int at) { struct grid_line *gl = &gd->linedata[yy], *first; struct grid_cell gc; u_int line, lines, width, i, xx; u_int used = gl->cellused; int flags = gl->flags; /* How many lines do we need to insert? We know we need at least two. */ if (~gl->flags & GRID_LINE_EXTENDED) lines = 1 + (gl->cellused - 1) / sx; else { lines = 2; width = 0; for (i = at; i < used; i++) { grid_get_cell1(gl, i, &gc); if (width + gc.data.width > sx) { lines++; width = 0; } width += gc.data.width; } } /* Insert new lines. */ line = target->sy + 1; first = grid_reflow_add(target, lines); /* Copy sections from the original line. */ width = 0; xx = 0; for (i = at; i < used; i++) { grid_get_cell1(gl, i, &gc); if (width + gc.data.width > sx) { target->linedata[line].flags |= GRID_LINE_WRAPPED; line++; width = 0; xx = 0; } width += gc.data.width; grid_set_cell(target, xx, line, &gc); xx++; } if (flags & GRID_LINE_WRAPPED) target->linedata[line].flags |= GRID_LINE_WRAPPED; /* Move the remainder of the original line. */ gl->cellsize = gl->cellused = at; gl->flags |= GRID_LINE_WRAPPED; memcpy(first, gl, sizeof *first); grid_reflow_dead(gl); /* Adjust the scroll position. */ if (yy <= gd->hscrolled) gd->hscrolled += lines - 1; /* * If the original line had the wrapped flag and there is still space * in the last new line, try to join with the next lines. */ if (width < sx && (flags & GRID_LINE_WRAPPED)) grid_reflow_join(target, gd, sx, yy, width, 1); }
/* Reflow lines on grid to new width. */ void grid_reflow(struct grid *gd, u_int sx, u_int *cursor) { struct grid *target; struct grid_line *gl; struct grid_cell gc; u_int yy, cy, width, i, at, first; struct timeval start, tv; gettimeofday(&start, NULL); log_debug("%s: %u lines, new width %u", __func__, gd->hsize + gd->sy, sx); cy = gd->hsize + (*cursor); /* * Create a destination grid. This is just used as a container for the * line data and may not be fully valid. */ target = grid_create(gd->sx, 0, 0); /* * Loop over each source line. */ for (yy = 0; yy < gd->hsize + gd->sy; yy++) { gl = &gd->linedata[yy]; if (gl->flags & GRID_LINE_DEAD) continue; /* * Work out the width of this line. first is the width of the * first character, at is the point at which the available * width is hit, and width is the full line width. */ first = at = width = 0; if (~gl->flags & GRID_LINE_EXTENDED) { first = 1; width = gl->cellused; if (width > sx) at = sx; else at = width; } else { for (i = 0; i < gl->cellused; i++) { grid_get_cell1(gl, i, &gc); if (i == 0) first = gc.data.width; if (at == 0 && width + gc.data.width > sx) at = i; width += gc.data.width; } } /* * If the line is exactly right or the first character is wider * than the targe width, just move it across unchanged. */ if (width == sx || first > sx) { grid_reflow_move(target, gl); continue; } /* * If the line is too big, it needs to be split, whether or not * it was previously wrapped. */ if (width > sx) { grid_reflow_split(target, gd, sx, yy, at, &cy); continue; } /* * If the line was previously wrapped, join as much as possible * of the next line. */ if (gl->flags & GRID_LINE_WRAPPED) grid_reflow_join(target, gd, sx, yy, width, &cy, 0); else grid_reflow_move(target, gl); } /* * Replace the old grid with the new. */ if (target->sy < gd->sy) grid_reflow_add(target, gd->sy - target->sy); gd->hsize = target->sy - gd->sy; free(gd->linedata); gd->linedata = target->linedata; free(target); /* * Update scrolled and cursor positions. */ if (gd->hscrolled > gd->hsize) gd->hscrolled = gd->hsize; if (cy < gd->hsize) *cursor = 0; else *cursor = cy - gd->hsize; gettimeofday(&tv, NULL); timersub(&tv, &start, &tv); log_debug("%s: now %u lines (in %llu.%06u seconds)", __func__, gd->hsize + gd->sy, (unsigned long long)tv.tv_sec, (u_int)tv.tv_usec); }