/* * Reflow lines from src grid into dst grid of width new_x. Returns number of * lines fewer in the visible area. The source grid is destroyed. */ u_int grid_reflow(struct grid *dst, struct grid *src, u_int new_x) { u_int py, sy, line; int previous_wrapped; struct grid_line *src_gl; py = 0; sy = src->sy; previous_wrapped = 0; for (line = 0; line < sy + src->hsize; line++) { src_gl = src->linedata + line; if (!previous_wrapped) { /* Wasn't wrapped. If smaller, move to destination. */ if (src_gl->cellsize <= new_x) grid_reflow_move(dst, &py, src_gl); else grid_reflow_split(dst, &py, src_gl, new_x, 0); } else { /* Previous was wrapped. Try to join. */ grid_reflow_join(dst, &py, src_gl, new_x); } previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED; } grid_destroy(src); if (py > sy) return (0); return (sy - py); }
/* 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); }
/* Join line below onto this one. */ static void grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, u_int width, int already) { struct grid_line *gl, *from = NULL; struct grid_cell gc; u_int lines, left, i, to, line, want = 0; u_int at; int wrapped = 1; /* * Add a new target line. */ if (!already) { to = target->sy; gl = grid_reflow_move(target, &gd->linedata[yy]); } else { to = target->sy - 1; gl = &target->linedata[to]; } at = gl->cellused; /* * Loop until no more to consume or the target line is full. */ lines = 0; for (;;) { /* * If this is now the last line, there is nothing more to be * done. */ if (yy + 1 + lines == gd->hsize + gd->sy) break; line = yy + 1 + lines; /* If the next line is empty, skip it. */ if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) wrapped = 0; if (gd->linedata[line].cellused == 0) { if (!wrapped) break; lines++; continue; } /* * Is the destination line now full? Copy the first character * separately because we need to leave "from" set to the last * line if this line is full. */ grid_get_cell1(&gd->linedata[line], 0, &gc); if (width + gc.data.width > sx) break; width += gc.data.width; grid_set_cell(target, at, to, &gc); at++; /* Join as much more as possible onto the current line. */ from = &gd->linedata[line]; for (want = 1; want < from->cellused; want++) { grid_get_cell1(from, want, &gc); if (width + gc.data.width > sx) break; width += gc.data.width; grid_set_cell(target, at, to, &gc); at++; } lines++; /* * If this line wasn't wrapped or we didn't consume the entire * line, don't try to join any further lines. */ if (!wrapped || want != from->cellused || width == sx) break; } if (lines == 0) return; /* * If we didn't consume the entire final line, then remove what we did * consume. If we consumed the entire line and it wasn't wrapped, * remove the wrap flag from this line. */ left = from->cellused - want; if (left != 0) { grid_move_cells(gd, 0, want, yy + lines, left, 8); from->cellsize = from->cellused = left; lines--; } else if (!wrapped) gl->flags &= ~GRID_LINE_WRAPPED; /* Remove the lines that were completely consumed. */ for (i = yy + 1; i < yy + 1 + lines; i++) { free(gd->linedata[i].celldata); free(gd->linedata[i].extddata); grid_reflow_dead(&gd->linedata[i]); } /* Adjust scroll position. */ if (gd->hscrolled > to + lines) gd->hscrolled -= lines; else if (gd->hscrolled > to) gd->hscrolled = to; }
/* 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); }