This is draw.c in view mode; [Download] [Up]
/* draw.c */ /* Copyright 1995 by Steve Kirkendall */ char id_draw[] = "$Id: draw.c,v 2.65 1996/09/26 01:05:52 steve Exp $"; #include "elvis.h" #if defined (GUI_WIN32) # define SLOPPY_ITALICS #else # undef SLOPPY_ITALICS #endif #if USE_PROTOTYPES static void insimage(CHAR *ch, char *font, int qty, int extent); static void delimage(CHAR *ch, char *font, int qty, int extent); static void fillcell(_CHAR_ ch, _char_ font, long offset); static void drawchar(CHAR *p, long qty, _char_ font, long offset); static void compareimage(WINDOW win); static void updateimage(WINDOW win); static void genlastrow(WINDOW win); static BOOLEAN drawquick(WINDOW win); static void opentextline(WINDOW win, CHAR *text, int len); static void openchar(CHAR *p, long qty, _char_ font, long offset); static void openmove(WINDOW win, long oldcol, long newcol, CHAR *image, long len); #endif /* allocate a drawinfo struct, including the related arrays */ DRAWINFO *drawalloc(rows, columns) int rows; /* height of new window image */ int columns;/* width of new window image */ { DRAWINFO *newp; int i; /* allocate the stuff */ newp = (DRAWINFO *)safealloc(1, sizeof *newp); newp->newrow = (DRAWROW *)safealloc(rows, sizeof(DRAWROW)); newp->newline = (DRAWLINE *)safealloc(rows + 1, sizeof(DRAWLINE)); newp->curline = (DRAWLINE *)safealloc(rows, sizeof(DRAWLINE)); newp->newchar = (CHAR *)safealloc(rows * columns, sizeof(CHAR)); newp->newfont = (char *)safealloc(rows * columns, sizeof(char)); newp->curchar = (CHAR *)safealloc(rows * columns, sizeof(CHAR)); newp->curfont = (char *)safealloc(rows * columns, sizeof(char)); newp->offsets = (long *)safealloc(rows * columns, sizeof(long)); /* clear the current image and the "new" image */ for (i = rows * columns; --i >= 0; ) { newp->newchar[i] = newp->curchar[i] = ' '; newp->newfont[i] = 'n'; newp->curfont[i] = '?'; } /* initialize the other variables */ newp->rows = rows; newp->columns = columns; newp->logic = DRAW_SCRATCH; return newp; } /* free a drawinfo struct, including the related arrays */ void drawfree(di) DRAWINFO *di; /* window image to destroy */ { /* free the stuff */ safefree(di->newrow); safefree(di->newline); safefree(di->curline); safefree(di->newchar); safefree(di->newfont); safefree(di->curchar); safefree(di->curfont); safefree(di->offsets); if (di->openimage) { safefree(di->openimage); } if (di->openline) { markfree(di->openline); } safefree(di); } /* re-output a rectangular part of a window's current image. The top-left * corner of the window is (0,0). * * The redrawn area includes the interior and edges of the window. This means, * for example, that if top==bottom, a single row will be (partially) redrawn. */ void drawexpose(win, top, left, bottom, right) WINDOW win; /* window that was partially exposed */ int top; /* top edge to be redrawn */ int left; /* left edge to be redrawn */ int bottom; /* bottom edge to be redrawn */ int right; /* right edge to be redrawn */ { int row, column, same, base, nonblank; MARKBUF m; long firstline, lastline; assert(win != NULL && top >= 0 && left >= 0 && bottom < o_lines(win) && right < o_columns(win) && top <= bottom && left <= right); #if 0 /* if we must redraw anyway, then do nothing */ if (win->di->logic == DRAW_SCRATCH) { return; } #endif /* if this GUI has no moveto() function, then do nothing */ if (!gui->moveto) { return; } /* for each row in the rectangle... */ for (row = top; row <= bottom; row++) { #if 1 /* find the width of this row, ignoring trailing blanks */ for (nonblank = o_columns(win), base = o_columns(win) * row; nonblank > left && win->di->curfont[base + nonblank - 1] == 'n' && win->di->curchar[base + nonblank - 1] == ' '; nonblank--) { } /* move to the first character we'll be redrawing in this row */ guimoveto(win, left, row); /* for each segment of the row which shares the same font... */ for (column = left, base += column; column <= right && column < nonblank; base += same, column += same) { /* find the width of the segment */ for (same = 1; column + same <= right && column + same < nonblank && win->di->curfont[base + same] == win->di->curfont[base]; same++) { } /* output the segment */ guidraw(win, win->di->curfont[base], &win->di->curchar[base], same); } /* if necessary, do a clrtoeol */ if (nonblank < right) { guiclrtoeol(win); } #else /* for each column in the row */ for (base = o_columns(win) * row + left, column = left; column <= right; base++, column++) { /* blot out elvis' idea of the current image. This * will cause elvis to re-output it the next time * the image is updated. */ win->di->curfont[base] = '\0'; } #endif } #if 1 /* leave the cursor in the right place */ guimoveto(win, win->di->curscol, win->di->cursrow); #endif /* update the scrollbar, too */ if (gui->scrollbar) { if (win->md->move == dmnormal.move) { /* find line numbers of first and last lines shown */ firstline = markline(marktmp(m, markbuffer(win->cursor), win->di->topline)) - 1; lastline = markline(marktmp(m, markbuffer(win->cursor), win->di->bottomline)) - 1; /* Some scrolling commands temporarily set bottomline * to the end of the buffer. Ordinarily this causes * no problems, but if an expose event occurs before * the screen is redrawn, the the scrollbar's "thumb" * will briefly extend to the bottom of the scrollbar. * If we seem to be in that situation, then make a more * reasonable guess about the screen bottom line number. */ if (lastline - firstline >= o_lines(win)) { lastline = firstline + o_lines(win) - 1; } /* update the scrollbar */ (*gui->scrollbar)(win->gw, firstline, lastline, o_buflines(markbuffer(win->cursor))); } else { (*gui->scrollbar)(win->gw, win->di->topline, win->di->bottomline, win->di->curnbytes); } } guiflush(); } /*----------------------------------------------------------------------------*/ /* This marks the end of the easy stuff! The remainder of this file consists * of the drawimage() function and its support functions and variables. */ static WINDOW thiswin; /* The window being drawn */ static long thisline; /* the line being drawn */ static char thislnumstr[16];/* line number, as a string */ static int linesshown; /* number of different lines visible */ static int thiscell; /* index of next cell to draw */ static int thiscol; /* logical column number */ static int leftcol; /* leftmost column to actually draw */ static int rightcol; /* rightmost column to actully draw, plus 1 */ static int maxrow; /* number of rows to be drawn */ static int wantcurs; /* largest acceptable cursor row */ static int thisscroll; /* scroll distance while drawing this line */ static int scrollrows; /* #rows (i.e., area) scrolled by gui->scroll */ static int maxcell; /* number of cells to be drawn */ static int seloffset; /* offset of character, during selection */ static char selfont; /* font of character, during selection */ /* insert some blanks into an image */ static void insimage(ch, font, qty, extent) CHAR *ch; /* where character insertion should begin */ char *font; /* parallel array of font codes for "ch" characters */ int qty; /* number of blanks to be inserted */ int extent; /* number of characters after the insertion point */ { register int i; /* shift the characters and font codes */ for (i = extent; --i >= qty; ) { ch[i] = ch[i - qty]; font[i] = font[i - qty]; } /* initialize the newly inserted cells */ if (gui->newblank) { /* the newly inserted characters are all normal spaces */ for (i = 0; i < qty; i++) { ch[i] = ' '; font[i] = 'n'; } } else { /* the new cells' contents are undefined */ for (i = 0; i < qty; i++) { font[i] = '?'; } } } /* delete some characters from an image, and add blanks to the end. */ static void delimage(ch, font, qty, extent) CHAR *ch; /* where character deletion should begin */ char *font; /* parallel array of font codes for "ch" characters */ int qty; /* number of characters to delete */ int extent; /* number of characters after the deletion point */ { register int i; /* shift the characters */ for (i = 0; i < extent - qty; i++) { ch[i] = ch[i + qty]; font[i] = font[i + qty]; } /* we've dragged some normal characters onto the edge of the extent */ if (gui->newblank) { /* the new cells are filled with normal blanks */ for ( ; i < extent; i++) { ch[i] = ' '; font[i] = 'n'; } } else { /* the new cells' contents are undefined */ for ( ; i < extent; i++) { font[i] = '?'; } } } /* Fill in a character cell. This function is called by drawchar(), which * in turn is called by an edit mode's image() function. */ static void fillcell(ch, font, offset) _CHAR_ ch; /* new character to place in the next cell */ _char_ font; /* font code of new character */ long offset; /* buffer offset, or -1 if not from buffer */ { register int i; register DRAWINFO *di = thiswin->di; assert(thiscell <= maxcell); assert(maxcell <= maxrow * di->columns); /* if we've reached the end of the screen without finding the cursor, * then we'll need to do a little slop scrolling. */ if (thiscell == maxcell && (di->cursrow < 0 || di->cursrow > wantcurs)) { /* scroll up 1 row */ delimage(di->newchar, di->newfont, (int)o_columns(thiswin), (int)(maxcell + o_columns(thiswin))); for (i = 0; i < maxcell - o_columns(thiswin); i++) { di->offsets[i] = di->offsets[i + o_columns(thiswin)]; } thisscroll++; /* scroll the statistics, too */ di->newrow[1].insrows += di->newrow[0].insrows; for (i = 0; i < maxrow - 1; i++) { di->newrow[i] = di->newrow[i + 1]; } di->newrow[i].insrows = di->newrow[i].shiftright = di->newrow[i].inschars = 0; for (i = 0; i < linesshown; i++) { di->newline[i].startrow--; } assert(di->cursrow != 0); if (di->cursrow > 0) di->cursrow--; /* if the top /line/ has scrolled off screen, then scroll * the line statistics, too. */ if (linesshown > 1 && di->newline[1].startrow == 0) { linesshown--; for (i = 0; i < linesshown; i++) { di->newline[i] = di->newline[i + 1]; } } /* adjust limits, taking o_sidescroll into consideration */ thiscell -= o_columns(thiswin); if (o_wrap(thiswin)) { rightcol += o_columns(thiswin); } } assert(thiscell < maxcell || di->cursrow >= 0); /* if this is the cursor character, then the cursor belongs here */ if (offset == markoffset(thiswin->cursor)) { di->cursrow = thiscell / o_columns(thiswin); } /* if this cell really should be drawn, then draw it */ if (thiscol >= leftcol && thiscol < rightcol && thiscell < maxcell) { di->newchar[thiscell] = ch; di->newfont[thiscell] = font; di->offsets[thiscell] = offset; thiscell++; } /* increment the column number */ thiscol++; } /* Add consecutive characters to the new image. This function is called by * an edit mode's image() function to generate a screen image. In addition * to displayable characters, this function gives special processing to * the following: * '\013' (VTAB) is interpretted as two '\n' characters. * '\f' is interpretted as two '\n' characters. * '\n' moves to the start of the next row. * Note that tabs and backspaces are not given special treatment here; * that's the responsibility of the edit mode's image() function. */ static void drawchar(p, qty, font, offset) CHAR *p; /* the characters to be added */ long qty; /* number of characters to add */ _char_ font; /* font code of characters */ long offset; /* buffer offset of character, or -1 if not from buffer */ { register CHAR ch; /* a character from *p */ register DRAWINFO *di = thiswin->di; /* window drawing info */ long delta; /* value to add to "offset" */ CHAR hifont; /* possibly highlighted font */ CHAR tmpch; int i; assert(offset >= -1 && strchr("bungifeBUNGIFE", font) != NULL); /* A negative qty value indicates that all characters have the same * offset. Otherwise the characters will have consecutive offsets. */ if (qty < 0) { qty = -qty; delta = 0; } else { delta = 1; } /* for each character... */ for ( ; qty > 0; qty--, p += delta, offset += delta) { /* copy the next char into a register variable */ ch = *p; /* Treat '\f' and '\013' (VTAB) as two '\n's in a row * (one of them recursively) */ if (ch == '\f' || ch == '\013') { tmpch = ch = '\n'; drawchar(&tmpch, 1, font, offset); } /* Handle non-ascii characters */ else if (ch >= 0x80) { switch (o_nonascii) { case 's': /* strip */ ch &= 0x7f; if (ch < ' ' || ch == 0x7f) ch = '.'; break; case 'n': /* none */ ch = '.'; break; /* !!it B4: to make cc happy */ case 'm': /* most */ if (ch <= 0x9f) ch = '.'; break; /* !!it B4: to make cc happy */ default: /* all */ /* no conversion necessary */; } } /* If we're showing line numbers, and this character is supposed * to be displayed in column 0, then output the line number * before this character. */ if (o_number(thiswin) && thiscol == leftcol) { for (i = 0; i < 8; i++) { fillcell((CHAR)thislnumstr[i], 'n', -1); } } /* If the offset is in the selected region for this window, * then make the font letter uppercase so that it will be * drawn highlighted. Also, make wide characters be either * totally highlighted or totally unhighlighted. */ hifont = font; if (thiswin->seltop) { if (offset == seloffset && seloffset != -1) { hifont = selfont; } else { i = thiscol; if (o_number(thiswin)) i -= 8; if (markoffset(thiswin->seltop) <= offset && offset <= markoffset(thiswin->selbottom) && thiswin->selleft <= i && i <= thiswin->selright && (ch != '\n' || thiswin->seltype != 'r')) { hifont = toupper(font); } } selfont = hifont; seloffset = offset; } else if (thiswin->match == offset) { hifont = toupper(font); } /* remember where this line started */ if (linesshown == 0 || di->newline[linesshown - 1].start != thisline) { assert(linesshown <= maxrow); assert((thiscell % o_columns(thiswin)) == (o_number(thiswin) && leftcol == 0 ? 8 : 0)); di->newline[linesshown].start = thisline; di->newline[linesshown++].startrow = thiscell / o_columns(thiswin); } /* if this is a newline, then treat it as a bunch of spaces */ if (ch == '\n') { /* remember this line's width. If a single "line" * involves multiple newlines, then the width of the * last non-empty phsyical line is kept. */ if (thiscol != 0) { di->newline[linesshown].width = thiscol; } /* "leftcol" has no effect on newlines */ if (thiscol < leftcol) { thiscol = leftcol; } /* Output a bunch of spaces. Note that the looping * condition is sensitive to both the column number and * the cell number, so it'll correctly handle lines * whose width==o_columns(win). It emulates the :xw: * brain-damaged newline, like a VT-100. * * This gets a bit tricky when you're appending to the * end of a line, and you've just extended the line to * reach edge of the screen. In this case, an extra * blank line *should* appear on the screen; the cursor * will be there. This test must be made outside the * loop. */ #if 0 if (offset == markoffset(thiswin->state->cursor) && thiscol % o_columns(thiswin) == 0) { thiscol = leftcol; } while (thiscol == leftcol || thiscell % o_columns(thiswin) != 0) { fillcell(' ', hifont, offset); } #else i = o_columns(thiswin); if ((offset == markoffset(thiswin->state->cursor) && thiscol % i == 0) || thiscol == leftcol) { fillcell(' ', hifont, offset); } /* Note: thiscell may've changed since previous "if" */ if (thiscell % i != 0) { for (i -= thiscell % i; i > 0; i--) { di->newchar[thiscell] = ' '; di->newfont[thiscell] = hifont; di->offsets[thiscell] = offset; thiscol++, thiscell++; } } #endif /* reset the virtual column number */ thiscol = 0; } else /* normal character */ { assert(ch >= ' '); /* draw the character */ fillcell(ch, hifont, offset); } } } /* This function compares old lines to new lines, and determines how much * insert/deleting we should do, and approximately where we should do it. */ static void compareimage(win) WINDOW win; /* window to be processed */ { int i, j, k, diff, totdiff; struct prevmatch_s { DRAWLINE *line; /* ptr to new line's current info */ enum {NEW, BEFORE, AFTER} type; /* line position, relative to any change */ } *prev; /* if we're supposed to redraw from scratch, then we won't be doing * any inserting/deleting. */ if (win->di->logic == DRAW_SCRATCH) { for (i = 0; i < maxrow; i++) { win->di->newrow[i].shiftright = 0; assert(win->di->newrow[i].insrows == 0 && win->di->newrow[i].inschars == 0); } /* also, ignore anything in current image */ memset(win->di->curchar, 0, maxcell * sizeof(CHAR)); memset(win->di->curfont, 0, maxcell * sizeof(char)); return; } /* allocate an array for matching new lines to old lines */ prev = (struct prevmatch_s *)safealloc(linesshown, sizeof *prev); /* Try to match each new line against current lines */ /* try to match top lines as being BEFORE a change */ i = 0; for (j = 0; j < win->di->nlines && win->di->newline[0].start != win->di->curline[j].start; j++) { } if (j < win->di->nlines) { do { prev[i].line = &win->di->curline[j]; prev[i].type = BEFORE; } while (++i < linesshown && ++j < win->di->nlines && win->di->newline[i].start == win->di->curline[j].start); } /* try to match bottom lines as being AFTER a change */ for (k = linesshown - 1; k >= i; k--) { for (j = win->di->nlines - 1; j >= 0 && o_bufchars(markbuffer(win->cursor)) - win->di->newline[k].start != win->di->curnbytes - win->di->curline[j].start; j--) { } if (j >= 0) { do { prev[k].line = &win->di->curline[j]; prev[k].type = AFTER; } while (--k >= i && --j >= i && o_bufchars(markbuffer(win->cursor)) - win->di->newline[k].start == win->di->curnbytes - win->di->curline[j].start); break; } prev[k].type = NEW; } /* any intervening lines are NEW */ for (; i < k; i++) { prev[i].type = NEW; } /* Compute character shifts/inserts/deletes for each row... */ for (i = 0; i < maxrow && win->di->newrow[i].lineoffset < o_bufchars(markbuffer(win->cursor)); i++) { /* find the line shown there (actually its index into newline[] and prev[]) */ for (j = 0; win->di->newrow[i].lineoffset != win->di->newline[j].start; j++) { assert(j + 1 < linesshown); } /* if it is a new line, then don't shift it */ if (prev[j].type == NEW) { win->di->newrow[i].shiftright = 0; assert(win->di->newrow[i].inschars == 0); } else /* recognized old line, maybe insert/delete chars or rows */ { /* number of chars to insert/delete is computed by change in length */ win->di->newrow[i].inschars = win->di->newline[j].width - prev[j].line->width; } } for ( ; i < maxrow; i++) { /* lines after the end of the buffer will show a tilde */ win->di->newrow[i].shiftright = 0; assert(win->di->newrow[i].inschars == 0 && win->di->newrow[i].inschars == 0); } /* Compute row insertion/deletion for each line... */ for (i = totdiff = 0; i < linesshown; i++, totdiff += diff) { /* We never need to insert/delete rows to make a NEW line * appear in the correct position. */ if (prev[i].type == NEW) { diff = 0; continue; } /* Compute the positional difference for this line. Also the * number of lines inserted/delete for preceding lines should * be taken into consideration. */ diff = win->di->newline[i].startrow - prev[i].line->startrow - totdiff; /* If inserting, we'll insert at the line's old starting row. * If deleting, we'll delete on a row above the line's starting * row so the line's current image ends up on the intended line */ j = prev[i].line->startrow + totdiff; if (diff < 0) { j += diff; } /* logical lines may start on negative rows, due to slop scrolling, * but physically we can only address lines >= 0. Any insertions * or deletions for a negative row should be applied to row 0. */ if (j < 0) { j = 0; } /* add the insertions/deletions for the appropriate row. */ win->di->newrow[j].insrows += diff; } #if 0 for (i = 0; i < maxrow; i++) printf("newrow[%d] = {lineoffset=%ld, insrows=%d, shiftright=%d, inschars=%d}, type=%s\n", i, win->di->newrow[i].lineoffset, win->di->newrow[i].insrows, win->di->newrow[i].shiftright, win->di->newrow[i].inschars, prev[i].type==BEFORE?"BEFORE":prev[i].type==NEW?"NEW":prev[i].type==AFTER?"AFTER":"?"); printf("---------------------------------------------------------------------\n"); #endif /* we don't need the prev array anymore */ safefree(prev); } /* after we've collected the image and some update hints, copy the new image to * both the "current" image and also the GUI. */ static void updateimage(win) WINDOW win; /* window to be updated */ { int row, column, same, used; int ncols = o_columns(win); register int i, base; register DRAWINFO *di = win->di; register int rowXncols; /* for each row... */ for (row = rowXncols = 0; row < o_lines(win); row++, rowXncols += ncols) { assert(di->newrow[row].insrows == 0 || di->logic != DRAW_SCRATCH); /* do we want to insert/delete rows? */ if (di->newrow[row].insrows != 0) { /* try to perform the insert/delete for the GUI */ guimoveto(win, 0, row); if (guiscroll(win, di->newrow[row].insrows, (BOOLEAN)(maxrow < o_lines(win)))) { /* perform the insert/delete on the "current" image */ if (di->newrow[row].insrows > 0) { /* insert some rows */ insimage(&di->curchar[rowXncols], &di->curfont[rowXncols], di->newrow[row].insrows * ncols, (scrollrows - row) * ncols); /* shift amount of new lines is always 0 */ for (i = row; i < row + di->newrow[row].insrows; i++) { di->newrow[i].shiftright = di->newrow[i].inschars = 0; } } else { /* delete some rows */ delimage(&di->curchar[rowXncols], &di->curfont[rowXncols], -di->newrow[row].insrows * ncols, (scrollrows - row) * ncols); /* shift amount of new lines is always 0 */ for (i = maxrow + di->newrow[row].insrows; i < maxrow; i++) { di->newrow[i].shiftright = di->newrow[i].inschars = 0; } } } else /* either we can't scroll, or have a huge insert/delete amount */ { /* don't do any more scrolling after this */ for (i = row; i < o_lines(win); i++) { di->newrow[i].insrows = 0; } } } /* perform shifting, if any */ if (di->newrow[row].shiftright != 0) { /* see how many following rows have the same shift */ for (same = 1; gui->shiftrows && row + same < di->rows - 1 && di->newrow[row].shiftright == di->newrow[row + same].shiftright; same++) { } /* make the GUI do its shift */ guimoveto(win, 0, row); if (guishift(win, di->newrow[row].shiftright, same)) { /* shift the current image, too */ for (i = row; i < row + same; i++) { if (di->newrow[i].shiftright > 0) { /* shift right by inserting */ insimage(&di->curchar[i * ncols], &di->curfont[i * ncols], di->newrow[i].shiftright, ncols); } else { /* shift left by deleting */ delimage(&di->curchar[i * ncols], &di->curfont[i * ncols], -di->newrow[i].shiftright, ncols); } di->newrow[i].shiftright = 0; } } } /* figure out how much of the line we'll be using */ base = ncols * row; for (used = ncols; used > 0 && di->newchar[base + used - 1] == ' ' && di->newfont[base + used - 1] == 'n'; used--) { } #ifdef SLOPPY_ITALICS /* If the line ends with an italic character, then pretend * we're using one additional character. This is so the last * italic character's right edge isn't clipped off. */ if (used < ncols && used >= 1 && di->newfont[base + used - 1] == 'i') { used++; } #endif /* look for mismatched segments */ for (column = 0; column < used; base += same, column += same) { /* if this cell matches, then skip to next */ if (di->newchar[base] == di->curchar[base] && di->newfont[base] == di->curfont[base]) { same = 1; continue; } /* move the GUI cursor to the point of interest */ guimoveto(win, column, row); /* perform this row's character insert/delete now, * unless we just shifted this line to the right, and * we're still on the inserted blanks at the start * of the row. */ if (column >= di->newrow[row].shiftright && di->newrow[row].inschars != 0) { if (guishift(win, di->newrow[row].inschars, 1)) { /* shift the current image, too */ if (di->newrow[row].inschars > 0) { /* shift right by inserting */ insimage(&di->curchar[rowXncols + column], &di->curfont[rowXncols + column], di->newrow[row].inschars, ncols - column); } else { /* shift left by deleting */ delimage(&di->curchar[rowXncols + column], &di->curfont[rowXncols + column], -di->newrow[row].inschars, ncols - column); } /* for other rows of the same line, insertions * will now appear to be shifts. */ for (i = row + 1; i < maxrow && di->newrow[i].lineoffset == di->newrow[row].lineoffset; i++) { di->newrow[i].shiftright += di->newrow[i].inschars; di->newrow[i].inschars = 0; } } /* we have now done all the inserting/deleting * that we're ever going to do for this row. */ di->newrow[row].inschars = 0; } /* See how many mismatched cells we can find with same * font. Allow small spans of matched characters, if * it costs less to redraw unchanged characters than * moving the cursor would cost. */ for (same = 1, i = 0; column + same < used && (di->newchar[base + same] != di->curchar[base + same] || di->newfont[base + same] != di->curfont[base + same] || i < gui->movecost) && (di->newfont[base] == di->newfont[base + same] #ifdef SLOPPY_ITALICS || (di->newfont[base + same - 1] == 'i' && di->newfont[base + same] == 'n' && di->newchar[base + same] == ' ') #endif ); same++) { if (di->newchar[base + same] != di->curchar[base + same] || di->newfont[base + same] != di->curfont[base + same]) { i = 0; } else { i++; } } same -= i; #ifdef SLOPPY_ITALICS /* if the change is in italics, and the preceding * characters were also italic, then include those * preceding characters in the redrawn span; otherwise * they would be distorted by the new text. */ while (column > 0 && di->newfont[base] == 'i' && di->newfont[base - 1] == 'i' && di->newchar[base - 1] != ' ') { base--; column--; same++; } guimoveto(win, column, row); #endif /* write the cells to the GUI */ guidraw(win, di->newfont[base], &di->newchar[base], same); /* write the cells to the "current" image */ for (i = 0; i < same; i++) { di->curchar[base + i] = di->newchar[base + i]; di->curfont[base + i] = di->newfont[base + i]; } } /* Do we need to clear to the end of the row? */ base = ncols * row; for (i = used; i < ncols && di->curchar[base + i] == ' ' && di->curfont[base + i] == 'n'; i++) { } if (i < ncols) { /* Yes, we need to do a clrtoeol */ /* We must clear at least from column "i", but the * cursor is probably already at column "used". * If the GUI doesn't say we should move to "i", * then move to "used" instead. */ if (!gui->minimizeclr) { i = used; } guimoveto(win, i, row); /* perform the clrtoeol */ guiclrtoeol(win); while (i < ncols) { di->curchar[base + i] = ' '; di->curfont[base + i] = 'n'; i++; } } } } /* This function generates the image of the last row. This is where "showmode" * and "ruler" come into play. Also, part of the previous line may be retained */ static void genlastrow(win) WINDOW win; /* window whose last row is to be generated */ { int i, j, base; char *scan; char buf[25]; /* were there any rows inserted or deleted? */ for (i = 0; !win->di->newmsg && i < o_lines(win) - 1 && win->di->newrow[i].insrows == 0; i++) { } base = (o_lines(win) - 1) * o_columns(win); if (!win->di->newmsg && i < o_lines(win) - 1) { /* Some rows were inserted/deleted, and there are no unread * messages on the last row; Assume the last row should be * wiped out. */ for (i = o_columns(win); --i >= 0; ) { win->di->newchar[base + i] = ' '; win->di->newfont[base + i] = 'n'; } } else { /* No unread messages, and no rows inserted/deleted; * Assume most of the last row is unchanged. */ for (i = o_columns(win); --i >= 0; ) { win->di->newchar[base + i] = win->di->curchar[base + i]; win->di->newfont[base + i] = win->di->curfont[base + i]; } win->di->newmsg = False; } /* does the GUI have a status function? */ if (gui->status) { /* yes, call it with status info */ (*gui->status)(win->gw, win->cmdchars, markline(win->state->cursor), (*win->md->mark2col)(win, win->state->cursor, viiscmd(win)) + 1, maplrnchar(o_modified(markbuffer(win->cursor)) ? '*' : ','), win->state->modename); } else { /* no, but maybe show status on the window's bottom row */ /* if "showmode", then show it */ if (o_showmode(win)) { scan = win->state->modename; for (i = base + o_columns(win) - 10; i < base + o_columns(win) - 3; i++) { win->di->newchar[i] = *scan++; win->di->newfont[i] = 'E'; /* !!it E3: I prefer it emphasized */ if (!*scan) { scan = " "; } } } /* if "ruler", then show it */ if (o_ruler(win) && o_bufchars(markbuffer(win->cursor)) > 0) { sprintf(buf, " %6ld%c%-4ld", markline(win->state->cursor), maplrnchar(o_modified(markbuffer(win->cursor)) ? '*' : ','), (*win->md->mark2col)(win, win->state->cursor, viiscmd(win)) + 1); scan = buf; for (i = base + o_columns(win) - 10 - strlen(buf); *scan; i++) { win->di->newchar[i] = *scan++; win->di->newfont[i] = 'n'; } } else { /* show the maplearn character anyway */ i = base + o_columns(win) - 15; win->di->newchar[i] = maplrnchar(o_modified(markbuffer(win->cursor)) ? '*' : ','); if (win->di->newchar[i] == ',') win->di->newchar[i] = ' '; win->di->newfont[i] = 'n'; } /* if "showcmd" then show it */ if (o_showcmd(win)) { i = base + o_columns(win) - 22 - QTY(win->cmdchars); for (j = 0; win->cmdchars[j]; j++, i++) { win->di->newchar[i] = win->cmdchars[j]; win->di->newfont[i] = 'n'; } for ( ; j < QTY(win->cmdchars) - 1; j++, i++) { win->di->newchar[i] = ' '; win->di->newfont[i] = 'n'; } } } /* if "showstack", then show it */ if (o_showstack(win)) { #if 0 for (state = win->state, i = base + o_columns(win) - 30; state; state = state->pop) { win->di->newchar[i] = *state->modename; win->di->newfont[i++] = 'b'; if (state->acton == state->pop) { win->di->newchar[i] = '.'; win->di->newfont[i++] = 'b'; } } win->di->newchar[i] = ' '; win->di->newfont[i++] = 'n'; win->di->newchar[i] = ' '; win->di->newfont[i++] = 'n'; win->di->newchar[i] = ' '; win->di->newfont[i++] = 'n'; #else sprintf(buf, " %ld/%ld/%ld", markoffset(win->state->top), markoffset(win->state->cursor), markoffset(win->state->bottom)); scan = buf; for (i = base + o_columns(win) - 20 - strlen(buf); *scan; i++) { win->di->newchar[i] = *scan++; win->di->newfont[i] = 'n'; } #endif } /* the last row is never inserted/deleted/shifted */ win->di->newrow[o_lines(win) - 1].insrows = 0; win->di->newrow[o_lines(win) - 1].inschars = 0; win->di->newrow[o_lines(win) - 1].shiftright = 0; } /* This function checks to see if we can skip regenerating a window image. * If not, then it returns False without doing anything... * * But if we can, it updates the bottom row (status line) and moves the cursor * to where it should be, and then returns True. */ static BOOLEAN drawquick(win) WINDOW win; /* window to be updated */ { DRAWINFO *di = win->di; long cursoff = markoffset(win->cursor); int i, j, col, base; /* if long lines wrap onto multiple rows, adjust curscol accordingly */ col = di->curscol; if (o_number(win)) col += 8; if (o_wrap(win)) col %= di->columns; else col -= di->skipped; /* check for obvious reasons that we might need to redraw */ if (!o_optimize || /* user doesn't want to optimize */ !win->md->canopt || /* display mode doesn't support optimization */ di->logic != DRAW_NORMAL || /* last command made a subtle change */ di->drawstate != DRAW_VISUAL || /* screen doesn't already show image */ di->curchgs != markbuffer(win->cursor)->changes || /* last command made a normal change */ cursoff < di->topline || /* cursor off top of screen */ cursoff >= di->bottomline || /* cursor off bottom of screen */ col < 0 || /* cursor off left edge of screen */ col >= di->columns || /* cursor off right edge of screen */ win->seltop) /* visually selecting text */ { return False; } /* scan all rows at that column, looking for the cursor's offset */ for (i = 0, j = col; di->offsets[j] != cursoff; i++, j += di->columns) { if (i >= di->rows - 2) return False; } /* Hooray! We can skip generating a new image */ di->cursrow = i; di->curscol = col; /* Except for the last line */ genlastrow(win); for (col = 0, i = base = (di->rows - 1) * di->columns, j = -1; col < di->columns - 1; col++, i++) { if (di->newchar[i] != di->curchar[i] || di->newfont[i] != di->curfont[i]) { if (j < 0) { j = i; } else if (di->newfont[j] != di->newfont[i]) { guimoveto(win, j - base, di->rows - 1); guidraw(win, di->newfont[j], &di->newchar[j], i - j); j = i; } } else if (j > 0) { guimoveto(win, j - base, di->rows - 1); guidraw(win, di->newfont[j], &di->newchar[j], i - j); j = -1; } } if (j > 0) { guimoveto(win, j - base, di->rows - 1); guidraw(win, di->newfont[j], &di->newchar[j], i - j); j = -1; } memcpy(&di->curfont[base], &di->newfont[base], di->columns * sizeof(*di->curfont)); memcpy(&di->curchar[base], &di->newchar[base], di->columns * sizeof(*di->curchar)); /* leave the cursor in the right place */ guimoveto(win, di->curscol, di->cursrow); /* That should do it */ return True; } /* update the image of a window to reflect changes to its buffer */ void drawimage(win) WINDOW win; /* window to be updated */ { MARKBUF first, last; MARK next; int i, row; CHAR tmpch; DRAWLOGIC nextlogic = DRAW_NORMAL; assert(gui->moveto && gui->draw); /* unavoidable setup performed here */ if (o_bufchars(markbuffer(win->cursor)) > 0) { win->di->curscol = (*win->md->mark2col)(win, marktmp(first, markbuffer(win->cursor), markoffset(win->cursor)), viiscmd(win)); } else { win->di->curscol = 0; } if (win->di->curbuf != markbuffer(win->cursor)) { /* place the cursor's line at the center of the screen */ win->di->topline = markoffset(dispmove(win, -(o_lines(win) / 2), 0)); win->di->bottomline = o_bufchars(markbuffer(win->cursor)); win->di->logic = DRAW_CENTER; win->di->curbuf = markbuffer(win->cursor); } /* see if maybe everything else *is* avoidable*/ if (drawquick(win)) { return; } /* setup, and choose a starting point for the drawing */ next = (*win->md->setup)(marktmp(first, markbuffer(win->cursor), win->di->topline), markoffset(win->cursor), marktmp(last, markbuffer(win->cursor), win->di->bottomline), win->mi); thiswin = win; thiscell = 0; thiscol = 0; maxrow = o_lines(win) - 1; maxcell = maxrow * o_columns(win); scrollrows = (gui->scrolllast ? maxrow + 1 : maxrow); linesshown = 0; if (o_number(win)) { win->di->curscol += 8; /* since line numbers will push text to the right */ } if (o_wrap(win)) { /* we're doing traditional vi line wrapping */ leftcol = 0; for (i = 0; i < maxrow; i++) { thiswin->di->newrow[i].insrows = thiswin->di->newrow[i].shiftright = thiswin->di->newrow[i].inschars = 0; } win->di->curscol %= o_columns(win); } else { /* we're doing side-scroll. Adjust leftcol, if necessary, to * make sure the cursor will be visible after drawing. */ leftcol = win->di->skipped; i = win->di->curscol; while (i >= leftcol + o_columns(win)) { leftcol += o_sidescroll(win); } if (o_number(win)) i -= 8; /* because line number pushed it over 8 cols */ while (i < leftcol) { leftcol -= o_sidescroll(win); } if (leftcol < 0) { leftcol = 0; } for (i = 0; i < maxrow; i++) { thiswin->di->newrow[i].insrows = thiswin->di->newrow[i].inschars = 0; thiswin->di->newrow[i].shiftright = win->di->skipped - leftcol; } win->di->skipped = leftcol; win->di->curscol -= leftcol; } win->di->cursrow = -1; seloffset = -1; /* are we going to try and center the cursor? */ if (win->di->logic == DRAW_CENTER) wantcurs = win->di->rows / 2; else wantcurs = win->di->rows; /* draw each line until we reach the last row */ win->di->topline = markoffset(next); for (row = 0; thiscell < maxcell || win->di->cursrow < 0 || (win->di->cursrow > wantcurs && markoffset(next) < o_bufchars(markbuffer(win->cursor))); ) { /* set column limits, taking o_sidescroll into consideration */ if (o_wrap(win)) { rightcol = maxcell - thiscell; } else { rightcol = leftcol + o_columns(win); } /* if we're at the end of the buffer, pad with tildes */ if (markoffset(next) >= o_bufchars(markbuffer(win->cursor))) { /* never show numbers on ~ lines */ if (o_number(win)) strcpy(thislnumstr, " "); thiscol = leftcol; if (row != 0) { tmpch = '~'; drawchar(&tmpch, 1, 'n', -1); } tmpch = '\n'; drawchar(&tmpch, 1, 'n', -1); if (win->di->cursrow < 0) { win->di->cursrow = row; } win->di->newrow[row++].lineoffset = o_bufchars(markbuffer(win->cursor)); } else { /* if we show line numbers, then convert this line's * offset into a line number in ASCII format. */ if (o_number(win)) sprintf(thislnumstr, "%6ld ", markline(next)); /* draw the line & remember its width */ thisline = markoffset(next); thiswin->di->newline[linesshown].width = 0; thisscroll = 0; next = (*win->md->image)(win, next, win->mi, drawchar); /* remember where each row started. */ row -= thisscroll; if (row < 0) { row = 0; nextlogic = DRAW_CHANGED; } for (row = (row > thisscroll) ? row - thisscroll : 0; row < thiscell / o_columns(win); row++) { win->di->newrow[row].lineoffset = thisline; } /* If the cursor probably should have been on this line, * then set cursrow to the row we just finished, even if * we didn't see the exact character where it belongs. */ if (thiswin->di->cursrow < 0 && markoffset(thiswin->cursor) < markoffset(next)) { assert(row > 0); thiswin->di->cursrow = row - 1; } } } win->di->bottomline = markoffset(next); assert(linesshown > 0 && linesshown <= maxrow); /* update the scrollbar */ if (gui->scrollbar) { if (win->md->move == dmnormal.move) { (*gui->scrollbar)(win->gw, markline(marktmp(first, markbuffer(win->cursor), win->di->newline[0].start)) - 1, markline(marktmp(last, markbuffer(win->cursor), markoffset(next))) - 1, o_buflines(markbuffer(win->cursor))); } else { (*gui->scrollbar)(win->gw, win->di->newline[0].start, markoffset(next), o_bufchars(markbuffer(next))); } guiflush(); } /* compute insert/delete info */ compareimage(win); /* figure out what the last row looks like */ genlastrow(win); /* copy the image to the window */ updateimage(win); /* remember some final info about this image, to help us update it * efficiently next time. */ win->di->logic = nextlogic; win->di->topline = win->di->newline[0].start; win->di->bottomline = markoffset(next); win->di->nlines = linesshown; win->di->curnbytes = o_bufchars(markbuffer(next)); win->di->curchgs = markbuffer(next)->changes; for (i = 0; i < linesshown; i++) { win->di->curline[i] = win->di->newline[i]; } /* we're left in visual mode */ win->di->drawstate = DRAW_VISUAL; /* leave the cursor in the right place */ guimoveto(win, win->di->curscol, win->di->cursrow); } /*----------------------------------------------------------------------------*/ /* The follow are line-oriented output functions */ /* This function either calls gui->textline, or simulates it via the usual * screen update functions. */ static void opentextline(win, text, len) WINDOW win; /* window where text line is to be output */ CHAR *text; /* text to be output */ int len; /* length of text */ { int i, j; /* if the GUI has a textline function, great! */ if (gui->textline) { (*gui->textline)(win->gw, text, len); return; } /* else we have to simulate textline */ /* for each character... */ for (i = 0; i < len; i++) { /* process this character */ switch (text[i]) { case '\b': if (win->di->opencell > 0) win->di->opencell--; thiscol = win->di->opencell % win->di->columns; break; case '\t': do { win->di->newchar[win->di->opencell] = ' '; win->di->newfont[win->di->opencell] = 'n'; win->di->opencell++; thiscol++; } while (thiscol < win->di->columns && thiscol % 8 != 0); break; case '\r': win->di->opencell -= thiscol; thiscol = 0; break; case '\n': win->di->opencell += win->di->columns; break; case '\f': case '\013': /* VTAB character */ /* ignore */ break; default: win->di->newchar[win->di->opencell] = text[i]; win->di->newfont[win->di->opencell] = 'n'; win->di->opencell++; thiscol++; } /* protect thiscol against wrap-around */ if (thiscol >= win->di->columns) { thiscol %= win->di->columns; } /* if reached end of window, then scroll */ while (win->di->opencell >= win->di->rows * win->di->columns) { delimage(win->di->newchar, win->di->newfont, win->di->columns, win->di->rows * win->di->columns); if (win->di->logic != DRAW_SCRATCH) win->di->newrow[0].insrows--; win->di->opencell -= win->di->columns; assert((win->di->opencell - thiscol) % win->di->columns == 0); for (j = win->di->opencell - thiscol; j < win->di->rows * win->di->columns; j++) { win->di->newchar[j] = ' '; win->di->newfont[j] = 'n'; } } } /* set the cursor coordinates */ win->di->curscol = thiscol; win->di->cursrow = win->di->opencell / win->di->columns; } /* This function is called before outputting a different line. If the screen * is in open-edit state, then the current line is output along with a newline. * If the screen is in visual state, then the cursor is moved to the bottom of * the screen and its line is cleared. Otherwise it has no effect. */ void drawopencomplete(win) WINDOW win; /* window where output will take place */ { int len, i; switch (win->di->drawstate) { case DRAW_VISUAL: /* SET UP FOR SCROLLING */ /* row hints initially say rows won't change */ for (i = 0; i < win->di->rows; i++) { /* win->di->newrow[i].lineoffset remains unchanged */ win->di->newrow[i].insrows = win->di->newrow[i].shiftright = win->di->newrow[i].inschars = 0; } /* move to the bottom row */ win->di->opencell = win->di->rows * win->di->columns - win->di->columns; thiscol = 0; /* clear the bottom row */ for (i = win->di->opencell; i < win->di->rows * win->di->columns; i++) { win->di->newchar[i] = ' '; win->di->newfont[i] = 'n'; } break; case DRAW_OPENEDIT: thiscol = win->di->opencell % win->di->columns; len = win->di->opencnt - win->di->opencursor; if (len > 0) { opentextline(win, win->di->openimage + win->di->opencursor, len); } opentextline(win, toCHAR("\r\n"), 2); break; case DRAW_OPENOUTPUT: thiscol = win->di->opencell % win->di->columns; break; case DRAW_VMSG: /* scroll upward, and leave cursor on bottom */ win->di->opencell = (win->di->rows - 1) * win->di->columns; thiscol = 0; win->di->newrow[0].insrows = 0; opentextline(win, toCHAR("\r\n"), 2); break; } /* either way, the current image is gone */ if (win->di->openimage) { safefree(win->di->openimage); win->di->openimage = NULL; } if (win->di->openline) { markfree(win->di->openline); win->di->openline = NULL; } win->di->drawstate = DRAW_OPENOUTPUT; } /* Draw a message on the bottom row of a window. If the previous message's * importance is MSG_STATUS, then overwrite; else append. Handle "[more]" * prompts. */ void drawmsg(win, imp, verbose, len) WINDOW win; /* window that the message pertains to */ MSGIMP imp; /* type of message */ CHAR *verbose; /* text of message */ int len; /* length of text */ { int i, base; /* if the window is in open mode, then just write the message with * a newline. */ if (win->di->drawstate != DRAW_VISUAL) { drawextext(win, verbose, len); drawextext(win, toCHAR("\n"), 1); } else /* full-screen */ { /* draw the message on the bottom row */ /* for now, any message overwrites any other */ base = (o_lines(win) - 1) * o_columns(win); for (i = 0; i < o_columns(win); i++) { if (i < len) { win->di->newchar[base + i] = win->di->curchar[base + i] = verbose[i]; } else { win->di->newchar[base + i] = win->di->curchar[base + i] = ' '; } win->di->newfont[base + i] = win->di->curfont[base + i] = 'n'; } drawexpose(win, (int)(o_lines(win)-1), 0, (int)(o_lines(win)-1), (int)(o_columns(win)-1)); win->di->newmsg = True; } /* non-status messages force us out of DRAW_VISUAL state */ if (imp != MSG_STATUS && win->di->logic != DRAW_SCRATCH && win->di->drawstate == DRAW_VISUAL) { win->di->drawstate = DRAW_VMSG; } } /* these are used while generating a line of text */ static long opencnt; /* column where next image character goes */ static long openoffsetcurs; /* offset of cursor */ static CHAR *openimage; /* buffer, holds new line image */ static long opensize; /* size of openimage */ static BOOLEAN opencursfound; /* has the cursor's cell been found yet? */ static BOOLEAN openskipping; /* has the line containing the cursor been completed? */ static void openchar(p, qty, font, offset) CHAR *p; /* characters to be output */ long qty; /* number of characters */ _char_ font; /* font code of that character */ long offset; /* buffer offset of the character, or -1 if not from buffer */ { register CHAR ch; CHAR *newp; long delta; /* negative qty indicates that the same character is repeated */ if (qty < 0) { delta = 0; qty = -qty; } else { delta = 1; } /* for each character */ for ( ; qty > 0; qty--, p += delta, offset += delta) { /* copy *p into a register variable */ ch = *p; /* ignore formfeed and VT, and extra lines */ if (ch == '\f' || ch == '\013' || openskipping) { continue; } /* is the cursor in this line? */ if (offset >= openoffsetcurs) { opencursfound = True; } /* newline is generally ignored */ if (ch == '\n') { if (opencursfound) { openskipping = True; } else { opencnt = 0; } continue; } /* expand the size of the buffer, if necessary */ if (opencnt >= opensize) { newp = (CHAR *)safealloc((int)(opensize + 80), sizeof(CHAR)); memcpy(newp, openimage, (size_t)opensize); safefree(openimage); openimage = newp; opensize += 80; } /* store the character */ openimage[opencnt++] = ch; } } /* output a bunch of backspace characters or characters to move the cursor * one way or the other on the current line. */ static void openmove(win, oldcol, newcol, image, len) WINDOW win; /* window whose cursor should be moved */ long oldcol; /* old position of the cursor, relative to image */ long newcol; /* new position of the cursor, relative to image */ CHAR *image; /* image of the line being edited */ long len; /* length of the image */ { static CHAR tenbs[10] = {'\b', '\b', '\b', '\b', '\b', '\b', '\b', '\b', '\b', '\b'}; static CHAR tensp[10] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; /* left or right? */ if (oldcol < newcol) { /* move right by writing characters from image */ if (len < newcol) { /* we'll need to pad the image with blanks */ if (oldcol < len) { opentextline(win, &image[oldcol], (int)(len - oldcol)); } while (len + 10 < newcol) { opentextline(win, tensp, 10); len += 10; } if (len < newcol) { opentextline(win, tensp, (int)(newcol - len)); } } else { /* don't pad with blanks; just write characters */ opentextline(win, &image[oldcol], (int)(newcol - oldcol)); } } else if (newcol < oldcol) { /* move right by outputing backspaces */ while (newcol + 10 < oldcol) { opentextline(win, tenbs, 10); oldcol -= 10; } if (newcol < oldcol) { opentextline(win, tenbs, (int)(oldcol - newcol)); } } /* else no movement was necessary */ } /* draw the window's current line from scratch, or just update it, in open mode. */ void drawopenedit(win) WINDOW win; /* window whose current line is to be output */ { MARK curline; long curcol; long first, last; /* differences */ long i, max; /* find the start of the cursor's line & its column */ curline = dispmove(win, 0, 0); curcol = dispmark2col(win); /* if we were editing a different line before, then end it now */ if (win->di->drawstate != DRAW_OPENEDIT || !win->di->openline || markbuffer(win->di->openline) != markbuffer(curline) || (markoffset(win->di->openline) != markoffset(curline) && !win->state->acton)) { drawopencomplete(win); curline = win->di->openline = markdup(curline); } /* else we're editing the same line as last time */ /* generate the new line image */ opencursfound = openskipping = False; opencnt = 0; openoffsetcurs = markoffset(win->state->cursor); openimage = (CHAR *)safealloc(80, sizeof(CHAR)); opensize = 80; if (win->state->acton) { curline = (*dmnormal.setup)(curline, openoffsetcurs, win->state->cursor, win->mi); curline = (*dmnormal.image)(win, curline, win->mi, openchar); } else { curline = (*win->md->setup)(curline, openoffsetcurs, win->state->cursor, win->mi); curline = (*win->md->image)(win, curline, win->mi, openchar); } /* do we have an old image? */ if (win->di->openimage) { /* find the differences */ first = last = -1; max = (opencnt > win->di->opencnt) ? opencnt : win->di->opencnt; for (i = 0; i < max; i++) { if (i < opencnt && win->di->openimage[i] == openimage[i]) { continue; } if (first < 0) { first = i; } last = i; } } else { /* the whole line is different */ first = 0; last = opencnt - 1; win->di->opencursor = 0; } /* output the different parts of the line */ if (first >= 0) { openmove(win, win->di->opencursor, first, openimage, opencnt); openmove(win, first, last + 1, openimage, opencnt); win->di->opencursor = last + 1; } /* move the cursor back where it wants to be */ openmove(win, win->di->opencursor, curcol, openimage, opencnt); win->di->opencursor = curcol; /* remember this line */ if (win->di->openimage) { safefree(win->di->openimage); } win->di->openimage = openimage; win->di->opencnt = opencnt; /* this leaves us in open+edit state */ win->di->drawstate = DRAW_OPENEDIT; /* if we're simulating the textline function, then we need to flush * the image to the window. */ if (!gui->textline) { updateimage(win); win->di->curscol = win->di->opencell % win->di->columns; win->di->cursrow = win->di->opencell / win->di->columns; guimoveto(win, win->di->opencell % win->di->columns, win->di->cursrow); win->di->newrow[0].insrows = 0; } } /* ex commands use this function to output information */ void drawextext(win, text, len) WINDOW win; /* window where ex output should be drawn */ CHAR *text; /* text to be output */ int len; /* length of text */ { int i, width; /* if no window specified, then throw away the text */ if (!win || eventcounter <= 1) { fwrite(tochar8(text), len, sizeof(char), stdout); if (text[len - 1] == '\n') { fwrite("\r", 1, sizeof(char), stdout); } return; } /* if we were editing a line before, finish it now */ drawopencomplete(win); /* output the text one line at a time, inserting CR before each LF */ for (i = 0; i < len; i += width + 1) { for (width = 0; i + width < len && text[i + width] != '\n'; width++) { } if (width > 0) { opentextline(win, text + i, width); } if (i + width < len) { opentextline(win, toCHAR("\r\n"), 2); } } /* flush the image to the screen */ if (o_exrefresh) { updateimage(win); win->di->curscol = win->di->opencell % win->di->columns; win->di->cursrow = win->di->opencell / win->di->columns; guimoveto(win, win->di->curscol, win->di->cursrow); win->di->newrow[0].insrows = 0; if (gui->flush) { (*gui->flush)(); } } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.