summaryrefslogtreecommitdiff
path: root/pico/display.c
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pico/display.c
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'pico/display.c')
-rw-r--r--pico/display.c3306
1 files changed, 3306 insertions, 0 deletions
diff --git a/pico/display.c b/pico/display.c
new file mode 100644
index 00000000..2741be3e
--- /dev/null
+++ b/pico/display.c
@@ -0,0 +1,3306 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: display.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Display functions
+ *
+ */
+
+/*
+ * The functions in this file handle redisplay. There are two halves, the
+ * ones that update the virtual display screen, and the ones that make the
+ * physical display screen the same as the virtual display screen. These
+ * functions use hints that are left in the windows by the commands.
+ *
+ */
+
+#include "../c-client/mail.h"
+#include "../c-client/utf8.h"
+
+#ifdef _WINDOWS
+/* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
+#undef ERROR
+#endif
+
+#include "headers.h"
+#include "../pith/charconv/filesys.h"
+#include "../pith/charconv/utf8.h"
+
+
+void vtmove(int, int);
+void vtputc(CELL);
+void vteeol(void);
+void updateline(int, CELL *, CELL *, short *);
+void updext(void);
+void mlputi(int, int);
+void pprints(int, int);
+void mlputli(long, int);
+void showCompTitle(void);
+int nlforw(void);
+int dumbroot(int, int);
+int dumblroot(long, int);
+unsigned cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend);
+unsigned vcellwidth_a_to_b(int row, int a, int b);
+#ifdef _WINDOWS
+void pico_config_menu_items (KEYMENU *);
+int update_scroll (void);
+#endif /* _WINDOWS */
+
+
+/*
+ * Standard pico keymenus...
+ */
+static KEYMENU menu_pico[] = {
+ {"^G", N_("Get Help"), KS_SCREENHELP}, {"^O", N_("WriteOut"), KS_SAVEFILE},
+ {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
+ {"^K", N_("Cut Text"), KS_NONE}, {"^C", N_("Cur Pos"), KS_CURPOSITION},
+ {"^X", N_("Exit"), KS_EXIT}, {"^J", N_("Justify"), KS_JUSTIFY},
+ {"^W", N_("Where is"), KS_WHEREIS}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
+ {"^U", NULL, KS_NONE},
+#ifdef SPELLER
+ {"^T", N_("To Spell"), KS_SPELLCHK}
+#else
+ {"^D", N_("Del Char"), KS_NONE}
+#endif
+};
+#define UNCUT_KEY 10
+
+
+static KEYMENU menu_compose[] = {
+ {"^G", N_("Get Help"), KS_SCREENHELP}, {"^X", NULL, KS_SEND},
+ {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
+ {"^K", N_("Cut Text"), KS_NONE}, {"^O", N_("Postpone"), KS_POSTPONE},
+ /* TRANSLATORS: Justify is to reformat a paragraph automatically */
+ {"^C", N_("Cancel"), KS_CANCEL}, {"^J", N_("Justify"), KS_JUSTIFY},
+ {NULL, NULL, KS_NONE}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
+ {"^U", NULL, KS_NONE},
+#ifdef SPELLER
+ {"^T", N_("To Spell"), KS_SPELLCHK}
+#else
+ {"^D", N_("Del Char"), KS_NONE}
+#endif
+};
+#define EXIT_KEY 1
+#define PSTPN_KEY 5
+#define WHERE_KEY 8
+
+
+/*
+ * Definition's for pico's modeline
+ */
+#define PICO_TITLE " UW PICO %s"
+#define PICO_MOD_MSG "Modified"
+#define PICO_NEWBUF_MSG "New Buffer"
+
+#define WFDEBUG 0 /* Window flag debug. */
+
+#define VFCHG 0x0001 /* Changed flag */
+#define VFEXT 0x0002 /* extended (beyond column 80) */
+#define VFREV 0x0004 /* reverse video status */
+#define VFREQ 0x0008 /* reverse video request */
+
+int vtrow = 0; /* Row location of SW cursor */
+int vtcol = 0; /* Column location of SW cursor */
+int vtind = 0; /* Index into row array of SW cursor */
+int ttrow = FARAWAY; /* Row location of HW cursor */
+int ttcol = FARAWAY; /* Column location of HW cursor */
+int lbound = 0; /* leftmost column of current line
+ being displayed */
+
+VIDEO **vscreen; /* Virtual screen. */
+VIDEO **pscreen; /* Physical screen. */
+
+
+/*
+ * Initialize the data structures used by the display code. The edge vectors
+ * used to access the screens are set up. The operating system's terminal I/O
+ * channel is set up. All the other things get initialized at compile time.
+ * The original window has "WFCHG" set, so that it will get completely
+ * redrawn on the first call to "update".
+ */
+int
+vtinit(void)
+{
+ int i, j;
+ VIDEO *vp;
+ CELL ac;
+
+ ac.c = ' ';
+ ac.a = 0;
+
+ if(Pmaster == NULL)
+ vtterminalinfo(gmode & MDTCAPWINS);
+
+ (*term.t_open)();
+
+ (*term.t_rev)(FALSE);
+ vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
+ memset(vscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
+ if (vscreen == NULL){
+ emlwrite("Allocating memory for virtual display failed.", NULL);
+ return(FALSE);
+ }
+
+ pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
+ memset(pscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
+ if (pscreen == NULL){
+ free((void *)vscreen);
+ emlwrite("Allocating memory for physical display failed.", NULL);
+ return(FALSE);
+ }
+
+
+ for (i = 0; i <= term.t_nrow; ++i) {
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL){
+ free((void *)vscreen);
+ free((void *)pscreen);
+ emlwrite("Allocating memory for virtual display lines failed.",
+ NULL);
+ return(FALSE);
+ }
+ else
+ for(j = 0; j < term.t_ncol; j++)
+ vp->v_text[j] = ac;
+
+ vp->v_flag = 0;
+ vscreen[i] = vp;
+
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL){
+ free((void *)vscreen[i]);
+ while(--i >= 0){
+ free((void *)vscreen[i]);
+ free((void *)pscreen[i]);
+ }
+
+ free((void *)vscreen);
+ free((void *)pscreen);
+ emlwrite("Allocating memory for physical display lines failed.",
+ NULL);
+ return(FALSE);
+ }
+ else
+ for(j = 0; j < term.t_ncol; j++)
+ vp->v_text[j] = ac;
+
+ vp->v_flag = 0;
+ pscreen[i] = vp;
+ }
+
+ return(TRUE);
+}
+
+int
+vtterminalinfo(int termcap_wins)
+{
+ return((term.t_terminalinfo) ? (*term.t_terminalinfo)(termcap_wins)
+ : (Pmaster ? 0 : TRUE));
+}
+
+
+/*
+ * Clean up the virtual terminal system, in anticipation for a return to the
+ * operating system. Move down to the last line and clear it out (the next
+ * system prompt will be written in the line). Shut down the channel to the
+ * terminal.
+ */
+void
+vttidy(void)
+{
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ (*term.t_close)();
+}
+
+
+/*
+ * Set the virtual cursor to the specified row and column on the virtual
+ * screen. There is no checking for nonsense values; this might be a good
+ * idea during the early stages.
+ */
+void
+vtmove(int row, int col)
+{
+ vtrow = row;
+ vtcol = col;
+
+ if(vtcol < 0)
+ vtind = -1;
+ else if(vtcol == 0)
+ vtind = vtcol;
+ else{
+ /*
+ * This is unused so don't worry about it.
+ */
+ assert(0);
+ }
+}
+
+
+/*
+ * Write a character to the virtual screen. The virtual row and column are
+ * updated. If the line is too long put a "$" in the last column. This routine
+ * only puts printing characters into the virtual terminal buffers. Only
+ * column overflow is checked.
+ */
+void
+vtputc(CELL c)
+{
+ VIDEO *vp;
+ CELL ac;
+ int w;
+
+ vp = vscreen[vtrow];
+ ac.c = ' ';
+ ac.a = c.a;
+
+ if (vtcol >= term.t_ncol) {
+ /*
+ * What's this supposed to be doing? This sets vtcol
+ * to the even multiple of 8 >= vtcol. Why are we doing that?
+ * Must make tab work correctly.
+ * 24 -> 24
+ * 25 -> 32
+ * ...
+ * 31 -> 32
+ * 32 -> 32
+ * 33 -> 40
+ */
+ vtcol = (vtcol + 0x07) & ~0x07;
+ ac.c = '$';
+
+ /*
+ * If we get to here that means that there must be characters
+ * past the right hand edge, so we want to put a $ character
+ * in the last visible character. It would be nice to replace
+ * the last visible character by a double-$ if it is double-width
+ * but we aren't doing that because we'd have to add up the widths
+ * starting at the left hand margin each time through.
+ */
+ if(vtind > 0 && vtind <= term.t_ncol)
+ vp->v_text[vtind-1] = ac;
+ }
+ else if (c.c == '\t') {
+ do {
+ vtputc(ac);
+ }
+ while (((vtcol + (vtrow==currow ? lbound : 0)) & 0x07) != 0 && vtcol < term.t_ncol);
+ }
+ else if (ISCONTROL(c.c)){
+ ac.c = '^';
+ vtputc(ac);
+ ac.c = ((c.c & 0x7f) | 0x40);
+ vtputc(ac);
+ }
+ else{
+
+ /*
+ * Have to worry about what happens if we skip over 0
+ * with a double-width character. There may be a better
+ * place to be setting vtind, or maybe we could make do
+ * without it.
+ */
+
+ w = wcellwidth((UCS) c.c);
+ w = (w >= 0 ? w : 1);
+
+ if(vtcol == 0 || (vtcol < 0 && vtcol + w == 1)){
+ vtind = 0;
+ if(vtcol < 0)
+ vtcol = 0;
+ }
+
+ /*
+ * Double-width character overlaps right edge.
+ * Replace it with a $.
+ */
+ if(vtcol + w > term.t_ncol){
+ ac.c = '$';
+ c = ac;
+ w = 1;
+ }
+
+ if(vtind >= 0 && vtind < term.t_ncol)
+ vp->v_text[vtind++] = c;
+
+ vtcol += w;
+ }
+}
+
+
+/*
+ * Erase from the end of the software cursor to the end of the line on which
+ * the software cursor is located.
+ */
+void
+vteeol(void)
+{
+ register VIDEO *vp;
+ CELL c;
+
+ c.c = ' ';
+ c.a = 0;
+ vp = vscreen[vtrow];
+
+ if(vtind >= 0)
+ while (vtind < term.t_ncol)
+ vp->v_text[vtind++] = c;
+
+ vtcol = term.t_ncol;
+}
+
+
+/*
+ * Make sure that the display is right. This is a three part process. First,
+ * scan through all of the windows looking for dirty ones. Check the framing,
+ * and refresh the screen. Second, make sure that "currow" and "curcol" are
+ * correct for the current window. Third, make the virtual and physical
+ * screens the same.
+ */
+void
+update(void)
+{
+ LINE *lp;
+ WINDOW *wp;
+ VIDEO *vp1;
+ VIDEO *vp2;
+ int i;
+ int j;
+ int scroll = 0;
+ CELL c;
+
+#if TYPEAH
+ if (typahead())
+ return;
+#endif
+
+#ifdef _WINDOWS
+ /* This tells our MS Windows module to not bother updating the
+ * cursor position while a massive screen update is in progress.
+ */
+ mswin_beginupdate ();
+#endif
+
+/*
+ * BUG: setting and unsetting whole region at a time is dumb. fix this.
+ */
+ if(curwp->w_markp){
+ unmarkbuffer();
+ markregion(1);
+ }
+
+ wp = wheadp;
+
+ while (wp != NULL){
+ /* Look at any window with update flags set on. */
+
+ if (wp->w_flag != 0){
+ /* If not force reframe, check the framing. */
+
+ if ((wp->w_flag & WFFORCE) == 0){
+ lp = wp->w_linep;
+
+ for (i = 0; i < wp->w_ntrows; ++i){
+ if (lp == wp->w_dotp)
+ goto out;
+
+ if (lp == wp->w_bufp->b_linep)
+ break;
+
+ lp = lforw(lp);
+ }
+ }
+
+ /* Not acceptable, better compute a new value for the line at the
+ * top of the window. Then set the "WFHARD" flag to force full
+ * redraw.
+ */
+ i = wp->w_force;
+
+ if (i > 0){
+ --i;
+
+ if (i >= wp->w_ntrows)
+ i = wp->w_ntrows-1;
+ }
+ else if (i < 0){
+ i += wp->w_ntrows;
+
+ if (i < 0)
+ i = 0;
+ }
+ else if(TERM_OPTIMIZE){
+ /*
+ * find dotp, if its been moved just above or below the
+ * window, use scrollxxx() to facilitate quick redisplay...
+ */
+ lp = lforw(wp->w_dotp);
+ if(lp != wp->w_dotp){
+ if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
+ scroll = 1;
+ }
+ else {
+ lp = wp->w_linep;
+ for(j=0;j < wp->w_ntrows; ++j){
+ if(lp != wp->w_bufp->b_linep)
+ lp = lforw(lp);
+ else
+ break;
+ }
+ if(lp == wp->w_dotp && j == wp->w_ntrows)
+ scroll = 2;
+ }
+ }
+ j = i = wp->w_ntrows/2;
+ }
+ else
+ i = wp->w_ntrows/2;
+
+ lp = wp->w_dotp;
+
+ while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
+ --i;
+ lp = lback(lp);
+ }
+
+ /*
+ * this is supposed to speed things up by using tcap sequences
+ * to efficiently scroll the terminal screen. the thinking here
+ * is that its much faster to update pscreen[] than to actually
+ * write the stuff to the screen...
+ */
+ if(TERM_OPTIMIZE){
+ switch(scroll){
+ case 1: /* scroll text down */
+ j = j-i+1; /* add one for dot line */
+ /*
+ * do we scroll down the header as well? Well, only
+ * if we're not editing the header, we've backed up
+ * to the top, and the composer is not being
+ * displayed...
+ */
+ if(Pmaster && Pmaster->headents && !ComposerEditing
+ && (lback(lp) == wp->w_bufp->b_linep)
+ && (ComposerTopLine == COMPOSER_TOP_LINE))
+ j += entry_line(1000, TRUE); /* Never > 1000 headers */
+
+ scrolldown(wp, -1, j);
+ break;
+ case 2: /* scroll text up */
+ j = wp->w_ntrows - (j-i); /* we chose new top line! */
+ if(Pmaster && j){
+ /*
+ * do we scroll down the header as well? Well, only
+ * if we're not editing the header, we've backed up
+ * to the top, and the composer is not being
+ * displayed...
+ */
+ if(!ComposerEditing
+ && (ComposerTopLine != COMPOSER_TOP_LINE))
+ scrollup(wp, COMPOSER_TOP_LINE,
+ j+entry_line(1000, TRUE));
+ else
+ scrollup(wp, -1, j);
+ }
+ else
+ scrollup(wp, -1, j);
+ break;
+ default :
+ break;
+ }
+ }
+
+ wp->w_linep = lp;
+ wp->w_flag |= WFHARD; /* Force full. */
+out:
+ /*
+ * if the line at the top of the page is the top line
+ * in the body, show the header...
+ */
+ if(Pmaster && Pmaster->headents && !ComposerEditing){
+ if(lback(wp->w_linep) == wp->w_bufp->b_linep){
+ if(ComposerTopLine == COMPOSER_TOP_LINE){
+ i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
+ if(i > 0 && nlforw() >= i) { /* room for header ? */
+ if((i = nlforw()/2) == 0 && term.t_nrow&1)
+ i = 1;
+ while(wp->w_linep != wp->w_bufp->b_linep && i--)
+ wp->w_linep = lforw(wp->w_linep);
+
+ }
+ else
+ ToggleHeader(1);
+ }
+ }
+ else{
+ if(ComposerTopLine != COMPOSER_TOP_LINE)
+ ToggleHeader(0); /* hide it ! */
+ }
+ }
+
+ /* Try to use reduced update. Mode line update has its own special
+ * flag. The fast update is used if the only thing to do is within
+ * the line editing.
+ */
+ lp = wp->w_linep;
+ i = wp->w_toprow;
+
+ if ((wp->w_flag & ~WFMODE) == WFEDIT){
+ while (lp != wp->w_dotp){
+ ++i;
+ lp = lforw(lp);
+ }
+
+ vscreen[i]->v_flag |= VFCHG;
+ vtmove(i, 0);
+
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+
+ vteeol();
+ }
+ else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
+ while (i < wp->w_toprow+wp->w_ntrows){
+ vscreen[i]->v_flag |= VFCHG;
+ vtmove(i, 0);
+
+ /* if line has been changed */
+ if (lp != wp->w_bufp->b_linep){
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+
+ lp = lforw(lp);
+ }
+
+ vteeol();
+ ++i;
+ }
+ }
+#if ~WFDEBUG
+ if ((wp->w_flag&WFMODE) != 0)
+ modeline(wp);
+
+ wp->w_flag = 0;
+ wp->w_force = 0;
+#endif
+ }
+#if WFDEBUG
+ modeline(wp);
+ wp->w_flag = 0;
+ wp->w_force = 0;
+#endif
+
+ /* and onward to the next window */
+ wp = wp->w_wndp;
+ }
+
+ /* Always recompute the row and column number of the hardware cursor. This
+ * is the only update for simple moves.
+ */
+ lp = curwp->w_linep;
+ currow = curwp->w_toprow;
+
+ while (lp != curwp->w_dotp){
+ ++currow;
+ lp = lforw(lp);
+ }
+
+ curcol = 0;
+ i = 0;
+
+ while (i < curwp->w_doto){
+ c = lgetc(lp, i++);
+
+ if(c.c == '\t'){
+ curcol |= 0x07;
+ ++curcol;
+ }
+ else if(ISCONTROL(c.c)){
+ curcol += 2;
+ }
+ else{
+ int w;
+
+ w = wcellwidth((UCS) c.c);
+ curcol += (w >= 0 ? w : 1);
+ }
+ }
+
+ if (curcol >= term.t_ncol) { /* extended line. */
+ /* flag we are extended and changed */
+ vscreen[currow]->v_flag |= VFEXT | VFCHG;
+ updext(); /* and output extended line */
+ } else
+ lbound = 0; /* not extended line */
+
+ /* make sure no lines need to be de-extended because the cursor is
+ * no longer on them
+ */
+
+ wp = wheadp;
+
+ while (wp != NULL) {
+ lp = wp->w_linep;
+ i = wp->w_toprow;
+
+ while (i < wp->w_toprow + wp->w_ntrows) {
+ if (vscreen[i]->v_flag & VFEXT) {
+ /* always flag extended lines as changed */
+ vscreen[i]->v_flag |= VFCHG;
+ if ((wp != curwp) || (lp != wp->w_dotp) ||
+ (curcol < term.t_ncol)) {
+ vtmove(i, 0);
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+ vteeol();
+
+ /* this line no longer is extended */
+ vscreen[i]->v_flag &= ~VFEXT;
+ }
+ }
+ lp = lforw(lp);
+ ++i;
+ }
+ /* and onward to the next window */
+ wp = wp->w_wndp;
+ }
+
+ /* Special hacking if the screen is garbage. Clear the hardware screen,
+ * and update your copy to agree with it. Set all the virtual screen
+ * change bits, to force a full update.
+ */
+
+ if (sgarbf != FALSE){
+ if(Pmaster){
+ int rv;
+
+ showCompTitle();
+
+ if(ComposerTopLine != COMPOSER_TOP_LINE){
+ UpdateHeader(0); /* arrange things */
+ PaintHeader(COMPOSER_TOP_LINE, TRUE);
+ }
+
+ /*
+ * since we're using only a portion of the screen and only
+ * one buffer, only clear enough screen for the current window
+ * which is to say the *only* window.
+ */
+ for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
+ movecursor(i, 0);
+ peeol();
+ vscreen[i]->v_flag |= VFCHG;
+ }
+ rv = (*Pmaster->showmsg)('X' & 0x1f); /* ctrl-L */
+ ttresize();
+ picosigs(); /* restore altered handlers */
+ if(rv) /* Did showmsg corrupt the display? */
+ PaintBody(0); /* Yes, repaint */
+ movecursor(wheadp->w_toprow, 0);
+ }
+ else{
+ for (i = 0; i < term.t_nrow-term.t_mrow; ++i){
+ vscreen[i]->v_flag |= VFCHG;
+ vp1 = pscreen[i];
+ c.c = ' ';
+ c.a = 0;
+ for (j = 0; j < term.t_ncol; ++j)
+ vp1->v_text[j] = c;
+ }
+
+ movecursor(0, 0); /* Erase the screen. */
+ (*term.t_eeop)();
+
+ }
+
+ sgarbf = FALSE; /* Erase-page clears */
+ mpresf = FALSE; /* the message area. */
+
+ if(Pmaster)
+ modeline(curwp);
+ else
+ sgarbk = TRUE; /* fix the keyhelp as well...*/
+ }
+
+ /* Make sure that the physical and virtual displays agree. Unlike before,
+ * the "updateline" code is only called with a line that has been updated
+ * for sure.
+ */
+ if(Pmaster)
+ i = curwp->w_toprow;
+ else
+ i = 0;
+
+ if (term.t_nrow > term.t_mrow)
+ c.c = term.t_nrow - term.t_mrow;
+ else
+ c.c = 0;
+
+ for (; i < (int)c.c; ++i){
+
+ vp1 = vscreen[i];
+
+ /* for each line that needs to be updated, or that needs its
+ reverse video status changed, call the line updater */
+ j = vp1->v_flag;
+ if (j & VFCHG){
+
+#if TYPEAH
+ if (typahead()){
+#ifdef _WINDOWS
+ mswin_endupdate ();
+#endif
+ return;
+ }
+#endif
+ vp2 = pscreen[i];
+
+ updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
+ }
+ }
+
+ if(Pmaster == NULL){
+
+ if(sgarbk != FALSE){
+ if(term.t_mrow > 0){
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ }
+
+ if(lastflag&CFFILL){
+ /* TRANSLATORS: UnJustify means undo the previous
+ Justify command. */
+ menu_pico[UNCUT_KEY].label = N_("UnJustify");
+ if(!(lastflag&CFFLBF)){
+ emlwrite(_("Can now UnJustify!"), NULL);
+ mpresf = FARAWAY; /* remove this after next keystroke! */
+ }
+ }
+ else
+ menu_pico[UNCUT_KEY].label = N_("UnCut Text");
+
+ wkeyhelp(menu_pico);
+ sgarbk = FALSE;
+ }
+ }
+
+ if(lastflag&CFFLBF){
+ emlwrite(_("Can now UnJustify!"), NULL);
+ mpresf = FARAWAY; /* remove this after next keystroke! */
+ }
+
+ /* Finally, update the hardware cursor and flush out buffers. */
+
+ movecursor(currow, curcol - lbound);
+#ifdef _WINDOWS
+ mswin_endupdate ();
+
+ /*
+ * Update the scroll bars. This function is where curbp->b_linecnt
+ * is really managed. See update_scroll.
+ */
+ update_scroll ();
+#endif
+ (*term.t_flush)();
+}
+
+
+/* updext - update the extended line which the cursor is currently
+ * on at a column greater than the terminal width. The line
+ * will be scrolled right or left to let the user see where
+ * the cursor is
+ */
+void
+updext(void)
+{
+ int rcursor; /* real cursor location */
+ LINE *lp; /* pointer to current line */
+ int j; /* index into line */
+ int w = 0;
+ int ww;
+
+ /*
+ * Calculate what column the real cursor will end up in.
+ * The cursor will be in the rcursor'th column. So if we're
+ * counting columns 0 1 2 3 and rcursor is 8, then rcursor
+ * will be over cell 7.
+ *
+ * What this effectively does is to scroll the screen as we're
+ * moving to the right when the cursor first passes off the
+ * screen's right edge. It would be nice if it did the same
+ * thing coming back to the left. Instead, in order that the
+ * screen's display depends only on the curcol and not on
+ * how we got there, the screen scrolls when we pass the
+ * t_margin column. It's also kind of funky that you can't
+ * see the character under the $ but you can delete it.
+ */
+ rcursor = ((curcol - term.t_ncol) % (term.t_ncol - term.t_margin + 1)) + term.t_margin;
+ lbound = curcol - rcursor + 1;
+
+ /*
+ * Make sure lbound is set so that a double-width character does
+ * not straddle the boundary. If it does, move over one cell.
+ */
+ lp = curwp->w_dotp; /* line to output */
+ for (j=0; j<llength(lp) && w < lbound; ++j){
+ ww = wcellwidth((UCS) lgetc(lp, j).c);
+ w += (ww >= 0 ? ww : 1);
+ }
+
+ if(w > lbound)
+ lbound = w;
+
+
+ /* scan through the line outputing characters to the virtual screen
+ * once we reach the left edge
+ */
+ vtmove(currow, -lbound); /* start scanning offscreen */
+ for (j=0; j<llength(lp); ++j) /* until the end-of-line */
+ vtputc(lgetc(lp, j));
+
+ /* truncate the virtual line */
+ vteeol();
+
+ /* and put a '$' in column 1, may have to adjust curcol */
+ w = wcellwidth((UCS) vscreen[currow]->v_text[0].c);
+ vscreen[currow]->v_text[0].c = '$';
+ vscreen[currow]->v_text[0].a = 0;
+ if(w == 2){
+ /*
+ * We want to put $ in the first two columns so that it
+ * takes up the right amount of space, but that means we
+ * have to scoot the real characters over one slot.
+ */
+ for (j = term.t_ncol-1; j >= 2; --j)
+ vscreen[currow]->v_text[j] = vscreen[currow]->v_text[j-1];
+
+ vscreen[currow]->v_text[1].c = '$';
+ vscreen[currow]->v_text[1].a = 0;
+ }
+}
+
+
+/*
+ * Update a single line. This does not know how to use insert or delete
+ * character sequences; we are using VT52 functionality. Update the physical
+ * row and column variables.
+ */
+void
+updateline(int row, /* row on screen */
+ CELL vline[], /* what we want it to end up as */
+ CELL pline[], /* what it looks like now */
+ short *flags)
+{
+ CELL *cp1, *cp2, *cp3, *cp4, *cp5, *cp6, *cp7;
+ int display = TRUE;
+ int nbflag; /* non-blanks to the right flag? */
+ int cleartoeol = 0;
+
+ if(row < 0 || row > term.t_nrow)
+ return;
+
+ /* set up pointers to virtual and physical lines */
+ cp1 = &vline[0];
+ cp2 = &pline[0];
+ cp3 = &vline[term.t_ncol];
+
+ /* advance past any common chars at the left */
+ while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
+ ++cp1;
+ ++cp2;
+ }
+
+/* This can still happen, even though we only call this routine on changed
+ * lines. A hard update is always done when a line splits, a massive
+ * change is done, or a buffer is displayed twice. This optimizes out most
+ * of the excess updating. A lot of computes are used, but these tend to
+ * be hard operations that do a lot of update, so I don't really care.
+ */
+ /* if both lines are the same, no update needs to be done */
+ if (cp1 == cp3){
+ *flags &= ~VFCHG; /* mark it clean */
+ return;
+ }
+
+ /* find out if there is a match on the right */
+ nbflag = FALSE;
+ cp3 = &vline[term.t_ncol];
+ cp4 = &pline[term.t_ncol];
+
+ if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
+ while (cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
+ --cp3;
+ --cp4;
+ if (cp3[0].c != ' ' || cp3[0].a != 0) /* Note if any nonblank */
+ nbflag = TRUE; /* in right match. */
+ }
+
+ cp5 = cp3;
+
+ if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
+ while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
+ --cp5;
+
+ if (cp3-cp5 <= 3) /* Use only if erase is */
+ cp5 = cp3; /* fewer characters. */
+ }
+
+ /* go to start of differences */
+ movecursor(row, cellwidth_ptr_to_ptr(&vline[0], cp1));
+
+ if (!nbflag) { /* use insert or del char? */
+ cp6 = cp3;
+ cp7 = cp4;
+
+ if(TERM_INSCHAR
+ &&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
+ while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
+ --cp7;
+ --cp6;
+ }
+
+ if (cp7==cp2 && cp4-cp2 > 3){
+ int ww;
+
+ (*term.t_rev)(cp1->a); /* set inverse for this char */
+ o_insert((UCS) cp1->c); /* insert the char */
+ ww = wcellwidth((UCS) cp1->c);
+ ttcol += (ww >= 0 ? ww : 1);
+ display = FALSE; /* only do it once!! */
+ }
+ }
+ else if(TERM_DELCHAR && cp3 != cp1 && cp7[0].c == cp6[-1].c
+ && cp7[0].a == cp6[-1].a){
+ while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
+ --cp7;
+ --cp6;
+ }
+
+ if (cp6==cp1 && cp5-cp6 > 3){
+ int w;
+
+ w = wcellwidth((UCS) cp7[0].c);
+ w = (w >= 0 ? w : 1);
+ while(w-- > 0) /* in case double-width char */
+ o_delete(); /* delete the char */
+ display = FALSE; /* only do it once!! */
+ }
+ }
+ }
+
+ if(cp1 != cp5 && display){
+ int w1, w2;
+
+ /*
+ * If we need to copy characters from cp1 to cp2 and
+ * we need to display them, then we have to worry about
+ * the characters that we are replacing being of a different
+ * width than the new characters, else the display may be
+ * messed up.
+ *
+ * If the new width (w1) is less than the old width, that means
+ * we will leave behind some old remnants if we aren't careful.
+ * If the new width is larger than the old width, we have to
+ * make sure we draw the characters all the way to the end
+ * in order to get it right. Take advantage of clear to end
+ * of line if we have it.
+ */
+ w1 = cellwidth_ptr_to_ptr(cp1, cp3);
+ w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
+
+ if(w1 < w2 || (nbflag && w1 != w2)){
+ if(TERM_EOLEXIST){
+ if(nbflag){
+ /*
+ * Draw all of the characters starting with cp1
+ * until we get to all spaces, then clear to the end of
+ * line from there. Watch out we don't run over the
+ * right hand edge, which shouldn't happen.
+ *
+ * Set cp5 to the first of the repeating spaces.
+ */
+ cp5 = &vline[term.t_ncol];
+ while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
+ --cp5;
+ }
+
+ /*
+ * In the !nbflag case we want spaces from cp5 on.
+ * Setting cp3 to something different from cp5 triggers
+ * the clear to end of line below.
+ */
+ if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
+ cleartoeol++;
+ }
+ else{
+ int w;
+
+ /*
+ * No peeol so draw all the way to the edge whether they
+ * are spaces or not.
+ */
+ cp3 = &vline[0];
+ for(w = 0; w < term.t_ncol; cp3++){
+ int ww;
+
+ ww = wcellwidth((UCS) cp3->c);
+ w += (ww >= 0 ? ww : 1);
+ }
+
+ cp5 = cp3;
+ }
+ }
+ }
+
+ while (cp1 != cp5) { /* Ordinary. */
+ int ww;
+
+ if(display){
+ (*term.t_rev)(cp1->a); /* set inverse for this char */
+ (*term.t_putchar)(cp1->c);
+ }
+
+ ww = wcellwidth((UCS) cp1->c);
+ ttcol += (ww >= 0 ? ww : 1);
+
+ *cp2++ = *cp1++;
+ }
+
+ (*term.t_rev)(0); /* turn off inverse anyway! */
+
+ if (cp5 != cp3 || cleartoeol) { /* Erase. */
+ if(display)
+ peeol();
+ else
+ while (cp1 != cp3)
+ *cp2++ = *cp1++;
+ }
+
+ *flags &= ~VFCHG; /* flag this line is changed */
+}
+
+
+/*
+ * Redisplay the mode line for the window pointed to by the "wp". This is the
+ * only routine that has any idea of how the modeline is formatted. You can
+ * change the modeline format by hacking at this routine. Called by "update"
+ * any time there is a dirty window.
+ */
+void
+modeline(WINDOW *wp)
+{
+ if(Pmaster){
+ if(ComposerEditing)
+ ShowPrompt();
+ else{
+ menu_compose[EXIT_KEY].label = (Pmaster->headents)
+ ? N_("Send") :N_("Exit");
+ menu_compose[PSTPN_KEY].name = (Pmaster->headents)
+ ? "^O" : NULL;
+ menu_compose[PSTPN_KEY].label = (Pmaster->headents)
+ ? N_("Postpone") : NULL;
+ menu_compose[WHERE_KEY].name = (Pmaster->alt_ed) ? "^_" : "^W";
+ menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? N_("Alt Edit")
+ : N_("Where is");
+ KS_OSDATASET(&menu_compose[WHERE_KEY],
+ (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
+ menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? N_("UnJustify")
+ : N_("UnCut Text");
+ wkeyhelp(menu_compose);
+ }
+ }
+ else{
+ BUFFER *bp;
+ char t1[NLINE], t2[NLINE], t3[NLINE], tline[NLINE];
+ int w1, w2, w3, w1_to_2, w2_to_3, w3_to_r;
+ UCS *ucs;
+
+ vtmove(1, 0);
+ vteeol();
+ vscreen[0]->v_flag |= VFCHG; /* Redraw next time. */
+ vtmove(0, 0); /* Seek to right line. */
+
+ snprintf(t1, sizeof(t1), PICO_TITLE, version); /* write version */
+
+ bp = wp->w_bufp;
+ if(bp->b_fname[0]) /* File name? */
+ snprintf(t2, sizeof(t2), "File: %s", bp->b_fname);
+ else{
+ strncpy(t2, PICO_NEWBUF_MSG, sizeof(t2));
+ t2[sizeof(t2)-1] = '\0';
+ }
+
+ if(bp->b_flag&BFCHG){ /* "MOD" if changed. */
+ strncpy(t3, PICO_MOD_MSG, sizeof(t3));
+ t3[sizeof(t3)-1] = '\0';
+ }
+ else
+ t3[0] = '\0';
+
+#define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
+#define ALLBUTSPACE (w1+w2+w3+w3_to_r)
+
+ w1 = utf8_width(t1);
+ w2 = utf8_width(t2);
+ w3 = utf8_width(t3);
+ w1_to_2 = w2_to_3 = 1; /* min values for separation */
+ w3_to_r = 2;
+
+ if(ALLOFTHEM <= term.t_ncol){ /* everything fits */
+ w1_to_2 = (term.t_ncol - ALLBUTSPACE)/2;
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ }
+ else{
+ w1 = 2;
+ w1_to_2 = 0;
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ w1 = w1_to_2 = w3_to_r = 0;
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ if(bp->b_fname[0]){
+ snprintf(t2, sizeof(t2), "%s", bp->b_fname);
+ w2 = utf8_width(t2);
+ }
+
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ w2 = 8;
+ if(bp->b_fname[0] && ALLOFTHEM <= term.t_ncol){
+ /* reduce size of file */
+ w2 = term.t_ncol - (ALLOFTHEM - w2);
+ t2[0] = t2[1] = t2[2] = '.';
+ utf8_to_width_rhs(t2+3, bp->b_fname, sizeof(t2)-3, w2-3);
+ w2 = utf8_width(t2);
+ }
+ else
+ w2 = utf8_width(t2);
+
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ w1 = w1_to_2 = w2 = w2_to_3 = w3_to_r = 0;
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - ALLBUTSPACE;
+ else
+ w3 = 0;
+ }
+ }
+ }
+ }
+ }
+
+ utf8_snprintf(tline, sizeof(tline),
+ "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
+ w1, w1, t1,
+ w1_to_2, w1_to_2, "",
+ w2, w2, t2,
+ w2_to_3, w2_to_3, "",
+ w3, w3, t3,
+ w3_to_r, w3_to_r, "");
+
+ ucs = NULL;
+ if(utf8_width(tline) <= term.t_ncol)
+ ucs = utf8_to_ucs4_cpystr(tline);
+
+ if(ucs){
+ UCS *ucsp;
+ CELL c;
+
+ c.a = 1;
+ ucsp = ucs;
+ while((c.c = CELLMASK & *ucsp++))
+ vtputc(c);
+
+ fs_give((void **) &ucs);
+ }
+ }
+}
+
+
+
+/*
+ * Send a command to the terminal to move the hardware cursor to row "row"
+ * and column "col". The row and column arguments are origin 0. Optimize out
+ * random calls. Update "ttrow" and "ttcol".
+ */
+void
+movecursor(int row, int col)
+{
+ if (row!=ttrow || col!=ttcol) {
+ ttrow = row;
+ ttcol = col;
+ (*term.t_move)(MIN(MAX(row,0),term.t_nrow), MIN(MAX(col,0),term.t_ncol-1));
+ }
+}
+
+
+/*
+ * Erase any sense we have of the cursor's HW location...
+ */
+void
+clearcursor(void)
+{
+ ttrow = ttcol = FARAWAY;
+}
+
+void
+get_cursor(int *row, int *col)
+{
+ if(row)
+ *row = ttrow;
+ if(col)
+ *col = ttcol;
+}
+
+
+/*
+ * Erase the message line. This is a special routine because the message line
+ * is not considered to be part of the virtual screen. It always works
+ * immediately; the terminal buffer is flushed via a call to the flusher.
+ */
+void
+mlerase(void)
+{
+ if (term.t_nrow < term.t_mrow)
+ return;
+
+ movecursor(term.t_nrow - term.t_mrow, 0);
+ (*term.t_rev)(0);
+ if (TERM_EOLEXIST == TRUE)
+ peeol();
+ else{
+ if(ttrow == term.t_nrow){
+ while(ttcol++ < term.t_ncol-1)
+ (*term.t_putchar)(' ');
+ }
+ else{
+ while(ttcol++ < term.t_ncol) /* track's ttcol */
+ (*term.t_putchar)(' ');
+ }
+ }
+
+ (*term.t_flush)();
+ mpresf = FALSE;
+}
+
+
+int
+mlyesno_utf8(char *utf8prompt, int dflt)
+{
+ int ret;
+ UCS *prompt;
+
+ prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
+
+ ret = mlyesno(prompt, dflt);
+
+ if(prompt)
+ fs_give((void **) &prompt);
+
+ return(ret);
+}
+
+
+/*
+ * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
+ * ABORT. The ABORT status is returned if the user bumps out of the question
+ * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
+ * is no default.
+ */
+int
+mlyesno(UCS *prompt, int dflt)
+{
+ int rv;
+ UCS buf[NLINE], lbuf[10];
+ KEYMENU menu_yesno[12];
+ COLOR_PAIR *lastc = NULL;
+
+#ifdef _WINDOWS
+ if (mswin_usedialog ())
+ switch (mswin_yesno (prompt)) {
+ default:
+ case 0: return (ABORT);
+ case 1: return (TRUE);
+ case 2: return (FALSE);
+ }
+#endif
+
+ for(rv = 0; rv < 12; rv++){
+ menu_yesno[rv].name = NULL;
+ KS_OSDATASET(&menu_yesno[rv], KS_NONE);
+ }
+
+ menu_yesno[1].name = "Y";
+ menu_yesno[1].label = (dflt == TRUE) ? "[" N_("Yes") "]" : N_("Yes");
+ menu_yesno[6].name = "^C";
+ menu_yesno[6].label = N_("Cancel");
+ menu_yesno[7].name = "N";
+ menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No");
+ wkeyhelp(menu_yesno); /* paint generic menu */
+ sgarbk = TRUE; /* mark menu dirty */
+ if(Pmaster && curwp)
+ curwp->w_flag |= WFMODE;
+
+ ucs4_strncpy(buf, prompt, NLINE);
+ buf[NLINE-1] = '\0';
+ lbuf[0] = ' '; lbuf[1] = '?'; lbuf[2] = ' '; lbuf[3] = '\0';
+ ucs4_strncat(buf, lbuf, NLINE - ucs4_strlen(buf) - 1);
+ buf[NLINE-1] = '\0';
+ mlwrite(buf, NULL);
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ rv = -1;
+ while(1){
+ switch(GetKey()){
+ case (CTRL|'M') : /* default */
+ if(dflt >= 0){
+ pputs_utf8((dflt) ? _("Yes") : _("No"), 1);
+ rv = dflt;
+ }
+ else
+ (*term.t_beep)();
+
+ break;
+
+ case (CTRL|'C') : /* Bail out! */
+ case F2 :
+ pputs_utf8(_("ABORT"), 1);
+ rv = ABORT;
+ break;
+
+ case 'y' :
+ case 'Y' :
+ case F3 :
+ pputs_utf8(_("Yes"), 1);
+ rv = TRUE;
+ break;
+
+ case 'n' :
+ case 'N' :
+ case F4 :
+ pputs_utf8(_("No"), 1);
+ rv = FALSE;
+ break;
+
+ case (CTRL|'G') :
+ if(term.t_mrow == 0 && km_popped == 0){
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ term.t_mrow = 2;
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ wkeyhelp(menu_yesno); /* paint generic menu */
+ mlwrite(buf, NULL);
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ sgarbk = TRUE; /* mark menu dirty */
+ km_popped++;
+ break;
+ }
+ /* else fall through */
+
+ default:
+ (*term.t_beep)();
+ case NODATA :
+ break;
+ }
+
+ (*term.t_flush)();
+ if(rv != -1){
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ if(km_popped){
+ term.t_mrow = 0;
+ movecursor(term.t_nrow, 0);
+ peeol();
+ sgarbf = 1;
+ km_popped = 0;
+ }
+
+ return(rv);
+ }
+ }
+}
+
+
+/*
+ * Write a prompt into the message line, then read back a response. Keep
+ * track of the physical position of the cursor. If we are in a keyboard
+ * macro throw the prompt away, and return the remembered response. This
+ * lets macros run at full speed. The reply is always terminated by a carriage
+ * return. Handle erase, kill, and abort keys.
+ */
+int
+mlreply_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ return(mlreplyd_utf8(utf8prompt, utf8buf, nbuf, flg|QDEFLT, extras));
+}
+
+
+int
+mlreply(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
+}
+
+
+/*
+ * function key mappings
+ */
+static UCS rfkm[12][2] = {
+ { F1, (CTRL|'G')},
+ { F2, (CTRL|'C')},
+ { F3, 0 },
+ { F4, 0 },
+ { F5, 0 },
+ { F6, 0 },
+ { F7, 0 },
+ { F8, 0 },
+ { F9, 0 },
+ { F10, 0 },
+ { F11, 0 },
+ { F12, 0 }
+};
+
+
+int
+mlreplyd_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ int ret;
+ UCS *b, *buf;
+ char *utf8;
+ UCS *prompt;
+
+ buf = (UCS *) fs_get(nbuf * sizeof(*b));
+ b = utf8_to_ucs4_cpystr(utf8buf);
+ if(b){
+ ucs4_strncpy(buf, b, nbuf);
+ buf[nbuf-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
+
+ ret = mlreplyd(prompt, buf, nbuf, flg, extras);
+
+ utf8 = ucs4_to_utf8_cpystr(buf);
+ if(utf8){
+ strncpy(utf8buf, utf8, nbuf);
+ utf8buf[nbuf-1] = '\0';
+ fs_give((void **) &utf8);
+ }
+
+ if(buf)
+ fs_give((void **) &buf);
+
+ if(prompt)
+ fs_give((void **) &prompt);
+
+ return(ret);
+}
+
+
+void
+writeachar(UCS ucs)
+{
+ pputc(ucs, 0);
+}
+
+
+/*
+ * mlreplyd - write the prompt to the message line along with a default
+ * answer already typed in. Carriage return accepts the
+ * default. answer returned in buf which also holds the initial
+ * default, nbuf is its length, def set means use default value.
+ */
+int
+mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ UCS c; /* current char */
+ UCS *b; /* pointer in buf */
+ int i, j;
+ int plen;
+ int changed = FALSE;
+ int return_val = 0;
+ KEYMENU menu_mlreply[12];
+ UCS extra_v[12];
+ struct display_line dline;
+ COLOR_PAIR *lastc = NULL;
+
+#ifdef _WINDOWS
+ if(mswin_usedialog()){
+ MDlgButton btn_list[12];
+ LPTSTR free_names[12];
+ LPTSTR free_labels[12];
+ int i, j;
+
+ memset(&free_names, 0, sizeof(LPTSTR) * 12);
+ memset(&free_labels, 0, sizeof(LPTSTR) * 12);
+ memset(&btn_list, 0, sizeof (MDlgButton) * 12);
+ j = 0;
+ for(i = 0; extras && extras[i].name != NULL; ++i) {
+ if(extras[i].label[0] != '\0') {
+ if((extras[i].key & CTRL) == CTRL)
+ btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
+ else
+ btn_list[j].ch = extras[i].key;
+
+ btn_list[j].rval = extras[i].key;
+ free_names[j] = utf8_to_lptstr(extras[i].name);
+ btn_list[j].name = free_names[j];
+ free_labels[j] = utf8_to_lptstr(extras[i].label);
+ btn_list[j].label = free_labels[j];
+ j++;
+ }
+ }
+
+ btn_list[j].ch = -1;
+
+ return_val = mswin_dialog(prompt, buf, nbuf, ((flg&QDEFLT) > 0),
+ FALSE, btn_list, NULL, 0);
+
+ if(return_val == 3)
+ return_val = HELPCH;
+
+ for(i = 0; i < 12; i++){
+ if(free_names[i])
+ fs_give((void **) &free_names[i]);
+ if(free_labels[i])
+ fs_give((void **) &free_labels[i]);
+ }
+
+ return(return_val);
+ }
+#endif
+
+ menu_mlreply[0].name = "^G";
+ menu_mlreply[0].label = N_("Get Help");
+ KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
+ for(j = 0, i = 1; i < 6; i++){ /* insert odd extras */
+ menu_mlreply[i].name = NULL;
+ KS_OSDATASET(&menu_mlreply[i], KS_NONE);
+ rfkm[2*i][1] = 0;
+ if(extras){
+ for(; extras[j].name && j != 2*(i-1); j++)
+ ;
+
+ if(extras[j].name){
+ rfkm[2*i][1] = extras[j].key;
+ menu_mlreply[i].name = extras[j].name;
+ menu_mlreply[i].label = extras[j].label;
+ KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
+ }
+ }
+ }
+
+ menu_mlreply[6].name = "^C";
+ menu_mlreply[6].label = N_("Cancel");
+ KS_OSDATASET(&menu_mlreply[6], KS_NONE);
+ for(j = 0, i = 7; i < 12; i++){ /* insert even extras */
+ menu_mlreply[i].name = NULL;
+ rfkm[2*(i-6)+1][1] = 0;
+ if(extras){
+ for(; extras[j].name && j != (2*(i-6)) - 1; j++)
+ ;
+
+ if(extras[j].name){
+ rfkm[2*(i-6)+1][1] = extras[j].key;
+ menu_mlreply[i].name = extras[j].name;
+ menu_mlreply[i].label = extras[j].label;
+ KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
+ }
+ }
+ }
+
+ /* set up what to watch for and return values */
+ memset(extra_v, 0, sizeof(extra_v));
+ for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
+ extra_v[j++] = extras[i].key;
+
+ plen = mlwrite(prompt, NULL); /* paint prompt */
+
+ if(!(flg&QDEFLT))
+ *buf = '\0';
+
+ dline.vused = ucs4_strlen(buf);
+ dline.dwid = term.t_ncol - plen;
+ dline.row = term.t_nrow - term.t_mrow;
+ dline.col = plen;
+
+ dline.dlen = 2 * dline.dwid + 100;
+
+ dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
+ dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
+ memset(dline.dl, 0, dline.dlen * sizeof(UCS));
+ memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
+
+ dline.movecursor = movecursor;
+ dline.writechar = writeachar;
+
+ dline.vl = buf;
+ dline.vlen = nbuf-1;
+ dline.vbase = 0;
+
+ b = &buf[(flg & QBOBUF) ? 0 : ucs4_strlen(buf)];
+
+ wkeyhelp(menu_mlreply); /* paint generic menu */
+
+ sgarbk = 1; /* mark menu dirty */
+
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ for(;;){
+
+ line_paint(b-buf, &dline, NULL);
+ (*term.t_flush)();
+
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
+ register_mfunc(mouse_in_content,
+ term.t_nrow - term.t_mrow, plen,
+ term.t_nrow - term.t_mrow, term.t_ncol-1);
+#endif
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_LINE);
+#endif
+ while((c = GetKey()) == NODATA)
+ ;
+
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+#endif
+
+ switch(c = normalize_cmd(c, rfkm, 1)){
+ case (CTRL|'A') : /* CTRL-A beginning */
+ case KEY_HOME :
+ b = buf;
+ continue;
+
+ case (CTRL|'B') : /* CTRL-B back a char */
+ case KEY_LEFT:
+ if(b <= buf)
+ (*term.t_beep)();
+ else
+ b--;
+
+ continue;
+
+ case (CTRL|'C') : /* CTRL-C abort */
+ pputs_utf8(_("ABORT"), 1);
+ ctrlg(FALSE, 0);
+ return_val = ABORT;
+ goto ret;
+
+ case (CTRL|'E') : /* CTRL-E end of line */
+ case KEY_END :
+ b = &buf[ucs4_strlen(buf)];
+ continue;
+
+ case (CTRL|'F') : /* CTRL-F forward a char*/
+ case KEY_RIGHT :
+ if(*b == '\0')
+ (*term.t_beep)();
+ else
+ b++;
+
+ continue;
+
+ case (CTRL|'G') : /* CTRL-G help */
+ if(term.t_mrow == 0 && km_popped == 0){
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ sgarbk = 1; /* mark menu dirty */
+ km_popped++;
+ term.t_mrow = 2;
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ wkeyhelp(menu_mlreply); /* paint generic menu */
+ plen = mlwrite(prompt, NULL); /* paint prompt */
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ pputs(buf, 1);
+ break;
+ }
+
+ pputs_utf8(_("HELP"), 1);
+ return_val = HELPCH;
+ goto ret;
+
+ case (CTRL|'H') : /* CTRL-H backspace */
+ case 0x7f : /* rubout */
+ if (b <= buf){
+ (*term.t_beep)();
+ break;
+ }
+ else
+ b--;
+
+ case (CTRL|'D') : /* CTRL-D delete char */
+ case KEY_DEL :
+ if (!*b){
+ (*term.t_beep)();
+ break;
+ }
+
+ changed=TRUE;
+ i = 0;
+ dline.vused--;
+ do /* blat out left char */
+ b[i] = b[i+1];
+ while(b[i++] != '\0');
+ break;
+
+ case (CTRL|'L') : /* CTRL-L redraw */
+ return_val = (CTRL|'L');
+ goto ret;
+
+ case (CTRL|'K') : /* CTRL-K kill line */
+ changed=TRUE;
+ buf[0] = '\0';
+ dline.vused = 0;
+ b = buf;
+ break;
+
+ case F1 : /* sort of same thing */
+ return_val = HELPCH;
+ goto ret;
+
+ case (CTRL|'M') : /* newline */
+ return_val = changed;
+ goto ret;
+
+#ifdef MOUSE
+ case KEY_MOUSE :
+ {
+ MOUSEPRESS mp;
+
+ mouse_get_last (NULL, &mp);
+
+ /* The clicked line have anything special on it? */
+ switch(mp.button){
+ case M_BUTTON_LEFT : /* position cursor */
+ mp.col -= plen; /* normalize column */
+ if(mp.col >= 0 && mp.col <= ucs4_strlen(buf))
+ b = buf + mp.col;
+
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_LINE);
+ mswin_paste_popup();
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+ break;
+#endif
+
+ case M_BUTTON_MIDDLE : /* NO-OP for now */
+ default: /* just ignore */
+ break;
+ }
+ }
+
+ continue;
+#endif
+
+ default :
+
+ /* look for match in extra_v */
+ for(i = 0; i < 12; i++)
+ if(c && c == extra_v[i]){
+ return_val = c;
+ goto ret;
+ }
+
+ changed=TRUE;
+
+ if(c & (CTRL | FUNC)){ /* bag ctrl_special chars */
+ (*term.t_beep)();
+ }
+ else{
+ i = ucs4_strlen(b);
+ if(flg&QNODQT){ /* reject double quotes? */
+ if(c == '"'){
+ (*term.t_beep)();
+ continue;
+ }
+ }
+
+ if(dline.vused >= nbuf-1){
+ (*term.t_beep)();
+ continue;
+ }
+
+ do /* blat out left char */
+ b[i+1] = b[i];
+ while(i-- > 0);
+
+ dline.vused++;
+ *b++ = c;
+ }
+ }
+ }
+
+ret:
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ (*term.t_flush)();
+
+ if(km_popped){
+ term.t_mrow = 0;
+ movecursor(term.t_nrow, 0);
+ peeol();
+ sgarbf = 1;
+ km_popped = 0;
+ }
+
+ if(dline.dl)
+ fs_give((void **) &dline.dl);
+
+ if(dline.olddl)
+ fs_give((void **) &dline.olddl);
+
+ return(return_val);
+}
+
+
+void
+emlwrite(char *utf8message, EML *eml)
+{
+ UCS *message;
+
+ message = utf8_to_ucs4_cpystr(utf8message ? utf8message : "");
+
+ emlwrite_ucs4(message, eml);
+
+ if(message)
+ fs_give((void **) &message);
+}
+
+
+/*
+ * emlwrite() - write the message string to the error half of the screen
+ * center justified. much like mlwrite (which is still used
+ * to paint the line for prompts and such), except it center
+ * the text.
+ */
+void
+emlwrite_ucs4(UCS *message, EML *eml)
+{
+ UCS *bufp, *ap;
+ int width;
+ COLOR_PAIR *lastc = NULL;
+
+ mlerase();
+
+ if(!(message && *message) || term.t_nrow < 2)
+ return; /* nothing to write or no space to write, bag it */
+
+ bufp = message;
+
+ width = ucs4_str_width(message);
+
+ /*
+ * next, figure out where the to move the cursor so the message
+ * comes out centered
+ */
+ if((ap=ucs4_strchr(message, '%')) != NULL){
+ width -= 2;
+ switch(ap[1]){
+ case '%':
+ case 'c':
+ width += (eml && eml->c) ? wcellwidth(eml->c) : 1;
+ break;
+ case 'd':
+ width += dumbroot(eml ? eml->d : 0, 10);
+ break;
+ case 'D':
+ width += dumblroot(eml ? eml->l : 0L, 10);
+ break;
+ case 'o':
+ width += dumbroot(eml ? eml->d : 0, 8);
+ break;
+ case 'x':
+ width += dumbroot(eml ? eml->d : 0, 16);
+ break;
+ case 's': /* string arg is UTF-8 */
+ width += (eml && eml->s) ? utf8_width(eml->s) : 2;
+ break;
+ }
+ }
+
+ if(width+4 <= term.t_ncol)
+ movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (width + 4))/2);
+ else
+ movecursor(term.t_nrow-term.t_mrow, 0);
+
+ if(Pmaster && Pmaster->colors && Pmaster->colors->stcp
+ && pico_is_good_colorpair(Pmaster->colors->stcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->stcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ pputs_utf8("[ ", 1);
+ while (*bufp != '\0' && ttcol < term.t_ncol-2){
+ if(*bufp == '\007')
+ (*term.t_beep)();
+ else if(*bufp == '%'){
+ switch(*++bufp){
+ case 'c':
+ if(eml && eml->c)
+ pputc(eml->c, 0);
+ else {
+ pputs_utf8("%c", 0);
+ }
+ break;
+ case 'd':
+ mlputi(eml ? eml->d : 0, 10);
+ break;
+ case 'D':
+ mlputli(eml ? eml->l : 0L, 10);
+ break;
+ case 'o':
+ mlputi(eml ? eml->d : 0, 16);
+ break;
+ case 'x':
+ mlputi(eml ? eml->d : 0, 8);
+ break;
+ case 's':
+ pputs_utf8((eml && eml->s) ? eml->s : "%s", 0);
+ break;
+ case '%':
+ default:
+ pputc(*bufp, 0);
+ break;
+ }
+ }
+ else
+ pputc(*bufp, 0);
+ bufp++;
+ }
+
+ pputs_utf8(" ]", 1);
+
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ (*term.t_flush)();
+
+ mpresf = TRUE;
+}
+
+
+int
+mlwrite_utf8(char *utf8fmt, void *arg)
+{
+ UCS *fmt;
+ int ret;
+
+ fmt = utf8_to_ucs4_cpystr(utf8fmt ? utf8fmt : "");
+ ret = mlwrite(fmt, arg);
+ if(fmt)
+ fs_give((void **) &fmt);
+
+ return(ret);
+}
+
+
+/*
+ * Write a message into the message line. Keep track of the physical cursor
+ * position. A small class of printf like format items is handled. Assumes the
+ * stack grows down; this assumption is made by the "++" in the argument scan
+ * loop. Set the "message line" flag TRUE.
+ */
+int
+mlwrite(UCS *fmt, void *arg)
+{
+ int ret, ww;
+ UCS c;
+ char *ap;
+ COLOR_PAIR *lastc = NULL;
+
+ /*
+ * the idea is to only highlight if there is something to show
+ */
+ mlerase();
+ movecursor(ttrow, 0);
+
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ ap = (char *) &arg;
+
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ pputc(c, 1);
+ }
+ else {
+ c = *fmt++;
+ switch (c){
+ case 'd':
+ mlputi(*(int *)ap, 10);
+ ap += sizeof(int);
+ break;
+
+ case 'o':
+ mlputi(*(int *)ap, 8);
+ ap += sizeof(int);
+ break;
+
+ case 'x':
+ mlputi(*(int *)ap, 16);
+ ap += sizeof(int);
+ break;
+
+ case 'D':
+ mlputli(*(long *)ap, 10);
+ ap += sizeof(long);
+ break;
+
+ case 's':
+ pputs_utf8(*(char **)ap, 1);
+ ap += sizeof(char *);
+ break;
+
+ default:
+ pputc(c, 1);
+ ww = wcellwidth(c);
+ ttcol += (ww >= 0 ? ww : 1);
+ }
+ }
+ }
+
+ ret = ttcol;
+ while(ttcol < term.t_ncol)
+ pputc(' ', 0);
+
+ movecursor(term.t_nrow - term.t_mrow, ret);
+
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ (*term.t_flush)();
+ mpresf = TRUE;
+
+ return(ret);
+}
+
+
+/*
+ * Write out an integer, in the specified radix. Update the physical cursor
+ * position. This will not handle any negative numbers; maybe it should.
+ */
+void
+mlputi(int i, int r)
+{
+ register int q;
+ static char hexdigits[] = "0123456789ABCDEF";
+
+ if (i < 0){
+ i = -i;
+ pputc('-', 1);
+ }
+
+ q = i/r;
+
+ if (q != 0)
+ mlputi(q, r);
+
+ pputc(hexdigits[i%r], 1);
+}
+
+
+/*
+ * do the same except as a long integer.
+ */
+void
+mlputli(long l, int r)
+{
+ register long q;
+
+ if (l < 0){
+ l = -l;
+ pputc('-', 1);
+ }
+
+ q = l/r;
+
+ if (q != 0)
+ mlputli(q, r);
+
+ pputc((int)(l%r)+'0', 1);
+}
+
+
+void
+unknown_command(UCS c)
+{
+ char buf[10], ch, *s;
+ EML eml;
+
+ buf[0] = '\0';
+ s = buf;
+
+ if(!c){
+ /* fall through */
+ }
+ else if(c & CTRL && c >= (CTRL|'@') && c <= (CTRL|'_')){
+ ch = c - (CTRL|'@') + '@';
+ snprintf(s, sizeof(buf), "^%c", ch);
+ }
+ else
+ switch(c){
+ case ' ' : s = "SPACE"; break;
+ case '\033' : s = "ESC"; break;
+ case '\177' : s = "DEL"; break;
+ case ctrl('I') : s = "TAB"; break;
+ case ctrl('J') : s = "LINEFEED"; break;
+ case ctrl('M') : s = "RETURN"; break;
+ case ctrl('Q') : s = "XON"; break;
+ case ctrl('S') : s = "XOFF"; break;
+ case KEY_UP : s = "Up Arrow"; break;
+ case KEY_DOWN : s = "Down Arrow"; break;
+ case KEY_RIGHT : s = "Right Arrow"; break;
+ case KEY_LEFT : s = "Left Arrow"; break;
+ case CTRL|KEY_UP : s = "Ctrl-Up Arrow"; break;
+ case CTRL|KEY_DOWN : s = "Ctrl-Down Arrow"; break;
+ case CTRL|KEY_RIGHT : s = "Ctrl-Right Arrow"; break;
+ case CTRL|KEY_LEFT : s = "Ctrl-Left Arrow"; break;
+ case KEY_PGUP : s = "Prev Page"; break;
+ case KEY_PGDN : s = "Next Page"; break;
+ case KEY_HOME : s = "Home"; break;
+ case KEY_END : s = "End"; break;
+ case KEY_DEL : s = "Delete"; break; /* Not necessary DEL! */
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ snprintf(s, sizeof(buf), "F%ld", (long) (c - PF1 + 1));
+ break;
+
+ default:
+ if(c < CTRL)
+ utf8_put((unsigned char *) s, (unsigned long) c);
+
+ break;
+ }
+
+ eml.s = s;
+ emlwrite("Unknown Command: %s", &eml);
+ (*term.t_beep)();
+}
+
+
+/*
+ * scrolldown - use stuff to efficiently move blocks of text on the
+ * display, and update the pscreen array to reflect those
+ * moves...
+ *
+ * wp is the window to move in
+ * r is the row at which to begin scrolling
+ * n is the number of lines to scrol
+ */
+void
+scrolldown(WINDOW *wp, int r, int n)
+{
+#ifdef TERMCAP
+ register int i;
+ register int l;
+ register VIDEO *vp1;
+ register VIDEO *vp2;
+
+ if(!n)
+ return;
+
+ if(r < 0){
+ r = wp->w_toprow;
+ l = wp->w_ntrows;
+ }
+ else{
+ if(r > wp->w_toprow)
+ vscreen[r-1]->v_flag |= VFCHG;
+ l = wp->w_toprow+wp->w_ntrows-r;
+ }
+
+ o_scrolldown(r, n);
+
+ for(i=l-n-1; i >= 0; i--){
+ vp1 = pscreen[r+i];
+ vp2 = pscreen[r+i+n];
+ memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
+ }
+ pprints(r+n-1, r);
+ ttrow = FARAWAY;
+ ttcol = FARAWAY;
+#endif /* TERMCAP */
+}
+
+
+/*
+ * scrollup - use tcap stuff to efficiently move blocks of text on the
+ * display, and update the pscreen array to reflect those
+ * moves...
+ */
+void
+scrollup(WINDOW *wp, int r, int n)
+{
+#ifdef TERMCAP
+ register int i;
+ register VIDEO *vp1;
+ register VIDEO *vp2;
+
+ if(!n)
+ return;
+
+ if(r < 0)
+ r = wp->w_toprow;
+
+ o_scrollup(r, n);
+
+ i = 0;
+ while(1){
+ if(Pmaster){
+ if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
+ break;
+ }
+ else{
+ if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
+ break;
+ }
+ vp1 = pscreen[r+i+n];
+ vp2 = pscreen[r+i];
+ memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
+ i++;
+ }
+ pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
+ ttrow = FARAWAY;
+ ttcol = FARAWAY;
+#endif /* TERMCAP */
+}
+
+
+/*
+ * print spaces in the physical screen starting from row abs(n) working in
+ * either the positive or negative direction (depending on sign of n).
+ */
+void
+pprints(int x, int y)
+{
+ register int i;
+ register int j;
+
+ if(x < y){
+ for(i = x;i <= y; ++i){
+ for(j = 0; j < term.t_ncol; j++){
+ pscreen[i]->v_text[j].c = ' ';
+ pscreen[i]->v_text[j].a = 0;
+ }
+ }
+ }
+ else{
+ for(i = x;i >= y; --i){
+ for(j = 0; j < term.t_ncol; j++){
+ pscreen[i]->v_text[j].c = ' ';
+ pscreen[i]->v_text[j].a = 0;
+ }
+ }
+ }
+ ttrow = y;
+ ttcol = 0;
+}
+
+
+/*
+ * doton - return the physical line number that the dot is on in the
+ * current window, and by side effect the number of lines remaining
+ */
+int
+doton(int *r, unsigned *chs)
+{
+ register int i = 0;
+ register LINE *lp = curwp->w_linep;
+ int l = -1;
+
+ assert(r != NULL && chs != NULL);
+
+ *chs = 0;
+ while(i++ < curwp->w_ntrows){
+ if(lp == curwp->w_dotp)
+ l = i-1;
+ lp = lforw(lp);
+ if(lp == curwp->w_bufp->b_linep){
+ i++;
+ break;
+ }
+ if(l >= 0)
+ (*chs) += llength(lp);
+ }
+ *r = i - l - term.t_mrow;
+ return(l+curwp->w_toprow);
+}
+
+
+
+/*
+ * resize_pico - given new window dimensions, allocate new resources
+ */
+int
+resize_pico(int row, int col)
+{
+ int old_nrow, old_ncol;
+ register int i;
+ register VIDEO *vp;
+
+ old_nrow = term.t_nrow;
+ old_ncol = term.t_ncol;
+
+ term.t_nrow = row;
+ term.t_ncol = col;
+
+ if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
+ return(TRUE);
+
+ if(curwp){
+ curwp->w_toprow = 2;
+ curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
+ }
+
+ if(Pmaster){
+ fillcol = Pmaster->fillcolumn;
+ (*Pmaster->resize)();
+ }
+ else if(userfillcol > 0)
+ fillcol = userfillcol;
+ else
+ fillcol = term.t_ncol - 6; /* we control the fill column */
+
+ /*
+ * free unused screen space ...
+ */
+ for(i=term.t_nrow+1; i <= old_nrow; ++i){
+ free((char *) vscreen[i]);
+ free((char *) pscreen[i]);
+ }
+
+ /*
+ * realloc new space for screen ...
+ */
+ if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
+ if(Pmaster)
+ return(-1);
+ else
+ exit(1);
+ }
+
+ if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
+ if(Pmaster)
+ return(-1);
+ else
+ exit(1);
+ }
+
+ for (i = 0; i <= term.t_nrow; ++i) {
+ if(i <= old_nrow)
+ vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+ else
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL)
+ exit(1);
+ vp->v_flag = VFCHG;
+ vscreen[i] = vp;
+ if(old_ncol < term.t_ncol){ /* don't let any garbage in */
+ vtrow = i;
+ vtcol = (i < old_nrow) ? old_ncol : 0;
+ vteeol();
+ }
+
+ if(i <= old_nrow)
+ vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+ else
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL)
+ exit(1);
+
+ vp->v_flag = VFCHG;
+ pscreen[i] = vp;
+ }
+
+ if(!ResizeBrowser()){
+ if(Pmaster && Pmaster->headents){
+ ResizeHeader();
+ }
+ else{
+ curwp->w_flag |= (WFHARD | WFMODE);
+ pico_refresh(0, 1); /* redraw whole enchilada. */
+ update(); /* do it */
+ }
+ }
+
+ return(TRUE);
+}
+
+void
+redraw_pico_for_callback(void)
+{
+ pico_refresh(0, 1);
+ update();
+}
+
+
+/*
+ * showCompTitle - display the anchor line passed in from pine
+ */
+void
+showCompTitle(void)
+{
+ if(Pmaster){
+ UCS *bufp;
+ extern UCS *pico_anchor;
+ COLOR_PAIR *lastc = NULL;
+
+ if((bufp = pico_anchor) == NULL)
+ return;
+
+ movecursor(COMPOSER_TITLE_LINE, 0);
+ if (Pmaster->colors && Pmaster->colors->tbcp &&
+ pico_is_good_colorpair(Pmaster->colors->tbcp)){
+ lastc = pico_get_cur_color();
+ (void)pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ while (ttcol < term.t_ncol)
+ if(*bufp != '\0')
+ pputc(*bufp++, 1);
+ else
+ pputc(' ', 1);
+
+ if (lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ movecursor(COMPOSER_TITLE_LINE + 1, 0);
+ peeol();
+ }
+}
+
+
+
+/*
+ * zotdisplay - blast malloc'd space created for display maps
+ */
+void
+zotdisplay(void)
+{
+ register int i;
+
+ for (i = 0; i <= term.t_nrow; ++i){ /* free screens */
+ free((char *) vscreen[i]);
+ free((char *) pscreen[i]);
+ }
+
+ free((char *) vscreen);
+ free((char *) pscreen);
+}
+
+
+
+/*
+ * nlforw() - returns the number of lines from the top to the dot
+ */
+int
+nlforw(void)
+{
+ register int i = 0;
+ register LINE *lp = curwp->w_linep;
+
+ while(lp != curwp->w_dotp){
+ lp = lforw(lp);
+ i++;
+ }
+ return(i);
+}
+
+
+
+/*
+ * pputc - output the given char, keep track of it on the physical screen
+ * array, and keep track of the cursor
+ */
+void
+pputc(UCS c, /* char to write */
+ int a) /* and its attribute */
+{
+ int ind, width, printable_ascii = 0;
+
+ /*
+ * This is necessary but not sufficient to allow us to draw. Note that
+ * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
+ * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
+ */
+ if((ttcol >= 0 && ttcol < term.t_ncol) && (ttrow >= 0 && ttrow <= term.t_nrow)){
+
+ /*
+ * Width is the number of screen columns a character will occupy.
+ */
+ if(c < 0x80 && isprint(c)){
+ printable_ascii++;
+ width = 1;
+ }
+ else
+ width = wcellwidth(c);
+
+ if(width < 0)
+ width = 1; /* will be a '?' */
+
+ if(ttcol + width <= term.t_ncol){ /* it fits */
+ /*
+ * Some terminals scroll when you write in the lower right corner
+ * of the screen, so don't write there.
+ */
+ if(!(ttrow == term.t_nrow && ttcol+width == term.t_ncol)){
+ (*term.t_putchar)(c); /* write it */
+ ind = index_from_col(ttrow, ttcol);
+ pscreen[ttrow]->v_text[ind].c = c; /* keep track of it */
+ pscreen[ttrow]->v_text[ind].a = a; /* keep track of it */
+ }
+ }
+ else{
+ /*
+ * Character overlaps right edge of screen. Hopefully the higher
+ * layers will prevent this but we're making sure.
+ *
+ * We may want to do something like writing a space character
+ * into the cells that are on the screen. We'll see.
+ */
+ }
+
+ ttcol = MIN(term.t_ncol, ttcol+width);
+ }
+}
+
+
+/*
+ * pputs - print a string and keep track of the cursor
+ */
+void
+pputs(UCS *s, /* string to write */
+ int a) /* and its attribute */
+{
+ while (*s != '\0')
+ pputc(*s++, a);
+}
+
+
+void
+pputs_utf8(char *s, int a)
+{
+ UCS *ucsstr = NULL;
+
+ if(s && *s){
+ ucsstr = utf8_to_ucs4_cpystr(s);
+ if(ucsstr){
+ pputs(ucsstr, a);
+ fs_give((void **) &ucsstr);
+ }
+ }
+}
+
+
+/*
+ * peeol - physical screen array erase to end of the line. remember to
+ * track the cursor.
+ */
+void
+peeol(void)
+{
+ int i, width = 0, ww;
+ CELL cl;
+
+ if(ttrow < 0 || ttrow > term.t_nrow)
+ return;
+
+ cl.c = ' ';
+ cl.a = 0;
+
+ /*
+ * Don't clear if we think we are sitting past the last column,
+ * that erases the last column if we just wrote it.
+ */
+ if(ttcol < term.t_ncol)
+ (*term.t_eeol)();
+
+ /*
+ * Because the characters are variable width it's a little tricky
+ * to erase the rest of the line. What we do is add up the
+ * widths of the characters until we reach ttcol
+ * then set the rest to the space character.
+ */
+ for(i = 0; i < term.t_ncol && width < ttcol; i++){
+ ww = wcellwidth((UCS) pscreen[ttrow]->v_text[i].c);
+ width += (ww >= 0 ? ww : 1);
+ }
+
+ while(i < term.t_ncol)
+ pscreen[ttrow]->v_text[i++] = cl;
+}
+
+
+/*
+ * pscr - return the character cell on the physical screen map on the
+ * given line, l, and offset, o.
+ */
+CELL *
+pscr(int l, int o)
+{
+ if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
+ return(&(pscreen[l]->v_text[o]));
+ else
+ return(NULL);
+}
+
+
+/*
+ * pclear() - clear the physical screen from row x through row y (inclusive)
+ * row is zero origin, min row = 0 max row = t_nrow
+ * Clear whole screen -- pclear(0, term.t_nrow)
+ * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
+ * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
+ */
+void
+pclear(int x, int y)
+{
+ register int i;
+
+ x = MIN(MAX(0, x), term.t_nrow);
+ y = MIN(MAX(0, y), term.t_nrow);
+
+ for(i=x; i <= y; i++){
+ movecursor(i, 0);
+ peeol();
+ }
+}
+
+
+/*
+ * dumbroot - just get close
+ */
+int
+dumbroot(int x, int b)
+{
+ if(x < b)
+ return(1);
+ else
+ return(dumbroot(x/b, b) + 1);
+}
+
+
+/*
+ * dumblroot - just get close
+ */
+int
+dumblroot(long x, int b)
+{
+ if(x < b)
+ return(1);
+ else
+ return(dumblroot(x/b, b) + 1);
+}
+
+
+/*
+ * pinsertc - use optimized insert, fixing physical screen map.
+ * returns true if char written, false otherwise
+ */
+int
+pinsert(CELL c)
+{
+ int i, ind = 0, ww;
+ CELL *p;
+
+ if(ttrow < 0 || ttrow > term.t_nrow)
+ return(0);
+
+ if(o_insert((UCS) c.c)){ /* if we've got it, use it! */
+ p = pscreen[ttrow]->v_text; /* then clean up physical screen */
+
+ ind = index_from_col(ttrow, ttcol);
+
+ for(i = term.t_ncol-1; i > ind; i--)
+ p[i] = p[i-1]; /* shift right */
+
+ p[ind] = c; /* insert new char */
+
+ ww = wcellwidth((UCS) c.c);
+ ttcol += (ww >= 0 ? ww : 1);
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * pdel - use optimized delete to rub out the current char and
+ * fix the physical screen array.
+ * returns true if optimized the delete, false otherwise
+ */
+int
+pdel(void)
+{
+ int i, ind = 0, w;
+ CELL *p;
+
+ if(ttrow < 0 || ttrow > term.t_nrow)
+ return(0);
+
+ if(TERM_DELCHAR){ /* if we've got it, use it! */
+ p = pscreen[ttrow]->v_text;
+ ind = index_from_col(ttrow, ttcol);
+
+ if(ind > 0){
+ --ind;
+ w = wcellwidth((UCS) p[ind].c);
+ w = (w >= 0 ? w : 1);
+ ttcol -= w;
+
+ for(i = 0; i < w; i++){
+ (*term.t_putchar)('\b'); /* move left a char */
+ o_delete(); /* and delete it */
+ }
+
+ /* then clean up physical screen */
+ for(i=ind; i < term.t_ncol-1; i++)
+ p[i] = p[i+1];
+
+ p[i].c = ' ';
+ p[i].a = 0;
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+
+/*
+ * wstripe - write out the given string at the given location, and reverse
+ * video on flagged characters. Does the same thing as pine's
+ * stripe.
+ *
+ * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
+ * only if you want to put the tildes before multi-byte chars.
+ */
+void
+wstripe(int line, int column, char *utf8pmt, int key)
+{
+ UCS *ucs4pmt, *u;
+ int i = 0, col = 0;
+ int j = 0;
+ int l, ww;
+ COLOR_PAIR *lastc = NULL;
+ COLOR_PAIR *kncp = NULL;
+ COLOR_PAIR *klcp = NULL;
+
+ if(line < 0 || line > term.t_nrow)
+ return;
+
+ if (Pmaster && Pmaster->colors){
+ if(pico_is_good_colorpair(Pmaster->colors->klcp))
+ klcp = Pmaster->colors->klcp;
+
+ if(klcp && pico_is_good_colorpair(Pmaster->colors->kncp))
+ kncp = Pmaster->colors->kncp;
+
+ lastc = pico_get_cur_color();
+ }
+
+ ucs4pmt = utf8_to_ucs4_cpystr(utf8pmt);
+ l = ucs4_strlen(ucs4pmt);
+ while(1){
+ if(i >= term.t_ncol || col >= term.t_ncol || j >= l)
+ return; /* equal strings */
+
+ if(ucs4pmt[j] == (UCS) key)
+ j++;
+
+ if (pscr(line, i) == NULL)
+ return;
+
+ if(pscr(line, i)->c != ucs4pmt[j]){
+ if(j >= 1 && ucs4pmt[j-1] == (UCS) key)
+ j--;
+ break;
+ }
+
+ ww = wcellwidth((UCS) pscr(line, i)->c);
+ col += (ww >= 0 ? ww : 1);
+ j++;
+ i++;
+ }
+
+ movecursor(line, column+col);
+ if(klcp) (void)pico_set_colorp(klcp, PSC_NONE);
+ u = &ucs4pmt[j];
+ do{
+ if(*u == (UCS) key){
+ u++;
+ if(kncp)
+ (void)pico_set_colorp(kncp, PSC_NONE);
+ else
+ (void)(*term.t_rev)(1);
+
+ pputc(*u, 1);
+ if(kncp)
+ (void)pico_set_colorp(klcp, PSC_NONE);
+ else
+ (void)(*term.t_rev)(0);
+ }
+ else{
+ pputc(*u, 0);
+ }
+ }
+ while(*++u != '\0');
+
+ if(ucs4pmt)
+ fs_give((void **) &ucs4pmt);
+
+ peeol();
+ if (lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ (*term.t_flush)();
+}
+
+
+
+/*
+ * wkeyhelp - paint list of possible commands on the bottom
+ * of the display (yet another pine clone)
+ * NOTE: function key mode is handled here since all the labels
+ * are the same...
+ *
+ * The KEYMENU definitions have names and labels defined as UTF-8 strings,
+ * and wstripe expects UTF-8.
+ */
+void
+wkeyhelp(KEYMENU *keymenu)
+{
+ char *obufp, *p, fkey[4];
+ char linebuf[2*NLINE]; /* "2" is for space for invert tokens */
+ int row, slot, tspace, adjusted_tspace, nspace[6], index, n;
+#ifdef MOUSE
+ char nbuf[NLINE];
+#endif
+
+#ifdef _WINDOWS
+ pico_config_menu_items (keymenu);
+#endif
+
+ if(term.t_mrow == 0)
+ return;
+
+ if(term.t_nrow < 1)
+ return;
+
+ /*
+ * Calculate amount of space for the names column by column...
+ */
+ for(index = 0; index < 6; index++)
+ if(!(gmode&MDFKEY)){
+ nspace[index] = (keymenu[index].name)
+ ? utf8_width(keymenu[index].name) : 0;
+ if(keymenu[index+6].name
+ && (n = utf8_width(keymenu[index+6].name)) > nspace[index])
+ nspace[index] = n;
+
+ nspace[index]++;
+ }
+ else
+ nspace[index] = (index < 4) ? 3 : 4;
+
+ tspace = term.t_ncol/6; /* total space for each item */
+
+ /*
+ * Avoid writing in bottom right corner so we won't scroll screens that
+ * scroll when you do that. The way this is setup, we won't do that
+ * unless the number of columns is evenly divisible by 6.
+ */
+ adjusted_tspace = (6 * tspace == term.t_ncol) ? tspace - 1 : tspace;
+
+ index = 0;
+ for(row = 0; row <= 1; row++){
+ linebuf[0] = '\0';
+ obufp = &linebuf[0];
+ for(slot = 0; slot < 6; slot++){
+ if(keymenu[index].name && keymenu[index].label){
+ size_t l;
+ char this_label[200], tmp_label[200];
+
+ if(keymenu[index].label[0] == '[' && keymenu[index].label[(l=strlen(keymenu[index].label))-1] == ']' && l > 2){
+ strncpy(tmp_label, &keymenu[index].label[1], MIN(sizeof(tmp_label),l-2));
+ tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
+ snprintf(this_label, sizeof(this_label), "[%s]", _(tmp_label));
+ }
+ else
+ strncpy(this_label, _(keymenu[index].label), sizeof(this_label));
+
+ this_label[sizeof(this_label)-1] = '\0';
+
+ if(gmode&MDFKEY){
+ p = fkey;
+ snprintf(fkey, sizeof(fkey), "F%d", (2 * slot) + row + 1);
+ }
+ else
+ p = keymenu[index].name;
+#ifdef MOUSE
+ snprintf(nbuf, sizeof(nbuf), "%.*s %s", nspace[slot], p, this_label);
+ register_key(index,
+ (gmode&MDFKEY) ? F1 + (2 * slot) + row:
+ (keymenu[index].name[0] == '^')
+ ? (CTRL | keymenu[index].name[1])
+ : (keymenu[index].name[0] == 'S'
+ && !strcmp(keymenu[index].name, "Spc"))
+ ? ' '
+ : keymenu[index].name[0],
+ nbuf, invert_label,
+ term.t_nrow - 1 + row, (slot * tspace),
+ strlen(nbuf),
+ (Pmaster && Pmaster->colors)
+ ? Pmaster->colors->kncp: NULL,
+ (Pmaster && Pmaster->colors)
+ ? Pmaster->colors->klcp: NULL);
+#endif
+
+ n = nspace[slot];
+ while(p && *p && n--){
+ *obufp++ = '~'; /* insert "invert" token */
+ *obufp++ = *p++;
+ }
+
+ while(n-- > 0)
+ *obufp++ = ' ';
+
+ p = this_label;
+ n = ((slot == 5 && row == 1) ? adjusted_tspace
+ : tspace) - nspace[slot];
+ while(p && *p && n-- > 0)
+ *obufp++ = *p++;
+
+ while(n-- > 0)
+ *obufp++ = ' ';
+ }
+ else{
+ n = (slot == 5 && row == 1) ? adjusted_tspace : tspace;
+ while(n--)
+ *obufp++ = ' ';
+
+#ifdef MOUSE
+ register_key(index, NODATA, "", NULL, 0, 0, 0, NULL, NULL);
+#endif
+ }
+
+ *obufp = '\0';
+ index++;
+ }
+
+ wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
+ }
+}
+
+
+/*
+ * This returns the screen width between pstart (inclusive) and
+ * pend (exclusive) where the pointers point into an array of CELLs.
+ */
+unsigned
+cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend)
+{
+ CELL *p;
+ unsigned width = 0;
+ int ww;
+
+ if(pstart)
+ for(p = pstart; p < pend; p++){
+ ww = wcellwidth((UCS) p->c);
+ width += (ww >= 0 ? ww : 1);
+ }
+
+ return(width);
+}
+
+
+/*
+ * This returns the virtual screen width in row from index a to b (exclusive).
+ */
+unsigned
+vcellwidth_a_to_b(int row, int a, int b)
+{
+ CELL *pstart, *pend;
+ VIDEO *vp;
+
+ if(row < 0 || row > term.t_nrow)
+ return 0;
+
+ if(a >= b)
+ return 0;
+
+ a = MIN(MAX(0, a), term.t_ncol-1);
+ b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
+
+ vp = vscreen[row];
+ pstart = &vp->v_text[a];
+ pend = &vp->v_text[b];
+
+ return(cellwidth_ptr_to_ptr(pstart, pend));
+}
+
+
+/*
+ * This returns the physical screen width in row from index a to b (exclusive).
+ */
+unsigned
+pcellwidth_a_to_b(int row, int a, int b)
+{
+ CELL *pstart, *pend;
+ VIDEO *vp;
+
+ if(row < 0 || row > term.t_nrow)
+ return 0;
+
+ if(a >= b)
+ return 0;
+
+ a = MIN(MAX(0, a), term.t_ncol-1);
+ b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
+
+ vp = pscreen[row];
+ pstart = &vp->v_text[a];
+ pend = &vp->v_text[b];
+
+ return(cellwidth_ptr_to_ptr(pstart, pend));
+}
+
+
+int
+index_from_col(int row, int col)
+{
+ CELL *p_start, *p_end, *p_limit;
+ int w_consumed = 0, w, done = 0;
+
+ if(row < 0 || row > term.t_nrow)
+ return 0;
+
+ p_end = p_start = pscreen[row]->v_text;
+ p_limit = p_start + term.t_ncol;
+
+ if(p_start)
+ while(!done && p_end < p_limit && p_end->c && w_consumed <= col){
+ w = wcellwidth((UCS) p_end->c);
+ w = (w >= 0 ? w : 1);
+ if(w_consumed + w <= col){
+ w_consumed += w;
+ ++p_end;
+ }
+ else
+ ++done;
+ }
+
+ /* MIN and MAX just to be sure */
+ return(MIN(MAX(0, p_end - p_start), term.t_ncol-1));
+}
+
+#ifdef _WINDOWS
+
+void
+pico_config_menu_items (KEYMENU *keymenu)
+{
+ int i;
+ KEYMENU *k;
+ UCS key;
+
+ mswin_menuitemclear ();
+
+ /* keymenu's seem to be hardcoded at 12 entries. */
+ for (i = 0, k = keymenu; i < 12; ++i, ++k) {
+ if (k->name != NULL && k->label != NULL &&
+ k->menuitem != KS_NONE) {
+
+ if (k->name[0] == '^')
+ key = CTRL | k->name[1];
+ else if (strcmp(k->name, "Ret") == 0)
+ key = '\r';
+ else
+ key = k->name[0];
+
+ mswin_menuitemadd (key, k->label, k->menuitem, 0);
+ }
+ }
+}
+
+/*
+ * Update the scroll range and position. (exported)
+ *
+ * This is where curbp->b_linecnt is really managed. With out this function
+ * to count the number of lines when needed curbp->b_linecnt will never
+ * really be correct. BUT, this function is only compiled into the
+ * windows version, so b_linecnt will only ever be right in the windows
+ * version. OK for now because that is the only version that
+ * looks at b_linecnt.
+ */
+int
+update_scroll (void)
+{
+ long scr_pos;
+ long scr_range;
+ LINE *lp;
+ static LINE *last_top_line = NULL;
+ static long last_scroll_pos = -1;
+
+
+ if (ComposerEditing) {
+ /* Editing header - don't allow scroll bars. */
+ mswin_setscrollrange (0, 0);
+ return(0);
+ }
+
+
+ /*
+ * Count the number of lines in the current bufer. Done when:
+ *
+ * when told to recount: curbp->b_linecnt == -1
+ * when the top line changed: curwp->w_linep != last_top_line
+ * when we don't know the scroll pos: last_scroll_pos == -1
+ *
+ * The first line in the list is a "place holder" line and is not
+ * counted. The list is circular, when we return the to place
+ * holder we have reached the end.
+ */
+ if(curbp->b_linecnt == -1 || curwp->w_linep != last_top_line
+ || last_scroll_pos == -1) {
+ scr_range = 0;
+ scr_pos = 0;
+ for (lp = lforw (curbp->b_linep); lp != curbp->b_linep;
+ lp = lforw (lp)) {
+ if (lp == curwp->w_linep)
+ scr_pos = scr_range;
+
+ ++scr_range;
+ }
+
+ curbp->b_linecnt = scr_range;
+ last_scroll_pos = scr_pos;
+ last_top_line = curwp->w_linep;
+ }
+
+ /*
+ * Set new scroll range and position.
+ */
+ mswin_setscrollrange (curwp->w_ntrows - 2, curbp->b_linecnt - 1);
+ mswin_setscrollpos (last_scroll_pos);
+ return (0);
+}
+#endif /* _WINDOWS */