#if	!defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: basic.c 831 2007-11-27 01:04:19Z hubert@u.washington.edu $";
#endif

/*
 * ========================================================================
 * Copyright 2006-2007 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:	Cursor manipulation functions
 */

/*
 * The routines in this file move the cursor around on the screen. They
 * compute a new value for the cursor, then adjust ".". The display code
 * always updates the cursor location, so only moves between lines, or
 * functions that adjust the top line in the window and invalidate the
 * framing, are hard.
 */
#include        "headers.h"
#include	"../pith/osdep/color.h"
#include "osdep/terminal.h"

int	indent_match(char **, LINE *, char *, int, int);

/*
 * Move the cursor to the
 * beginning of the current line.
 * Trivial.
 */
int
gotobol(int f, int n)
{
    curwp->w_doto  = 0;
    return (TRUE);
}

/*
 * Move the cursor backwards by "n" characters. If "n" is less than zero call
 * "forwchar" to actually do the move. Otherwise compute the new cursor
 * location. Error if you try and move out of the buffer. Set the flag if the
 * line pointer for dot changes.
 */
int
backchar(int f, int n)
{
    register LINE   *lp;

    if (n < 0)
      return (forwchar(f, -n));

    while (n--) {
	if (curwp->w_doto == 0) {
	    if ((lp=lback(curwp->w_dotp)) == curbp->b_linep){
		if(Pmaster && Pmaster->headents)
		    /*
		     * go up into editing the mail header if on 
		     * the top line and the user hits the left arrow!!!
		     *
		     * if the editor returns anything except -1, the 
		     * user requested something special, so let 
		     * pico know...
		     */
		  return(HeaderEditor(2, 1));
		else
		  return (FALSE);
	    }

	    curwp->w_dotp  = lp;
	    curwp->w_doto  = llength(lp);
	    curwp->w_flag |= WFMOVE;
	} else
	  curwp->w_doto--;
    }

    return (TRUE);
}

/*
 * Move the cursor backwards by "n" characters. If "n" is less than zero call
 * "forwchar" to actually do the move. Otherwise compute the new cursor
 * location. Error if you try and move out of the buffer. Set the flag if the
 * line pointer for dot changes.
 *
 * This routine does _not_ do the header editor checks. It is used by
 * backword() in pico\word.c which gets stuck in a loop trying to go
 * back if you've jumped into a header.
 */
int
backchar_no_header_editor(int f, int n)
{
    register LINE   *lp;

    if (n < 0)
      return (forwchar(f, -n));

    while (n--) {
	if (curwp->w_doto == 0) {
	    if ((lp=lback(curwp->w_dotp)) == curbp->b_linep){
		  return (FALSE);
	    }

	    curwp->w_dotp  = lp;
	    curwp->w_doto  = llength(lp);
	    curwp->w_flag |= WFMOVE;
	} else
	  curwp->w_doto--;
    }

    return (TRUE);
}

/*
 * Move the cursor to the end of the current line. Trivial. No errors.
 */
int
gotoeol(int f, int n)
{
    curwp->w_doto  = llength(curwp->w_dotp);
    return (TRUE);
}


/*
 * Move the cursor forwwards by "n" characters. If "n" is less than zero call
 * "backchar" to actually do the move. Otherwise compute the new cursor
 * location, and move ".". Error if you try and move off the end of the
 * buffer. Set the flag if the line pointer for dot changes.
 */
int
forwchar(int f, int n)
{
    if (n < 0)
      return (backchar(f, -n));

    while (n--) {
	if (curwp->w_doto == llength(curwp->w_dotp)) {
	    if (curwp->w_dotp == curbp->b_linep)
	      return (FALSE);

	    curwp->w_dotp  = lforw(curwp->w_dotp);
	    curwp->w_doto  = 0;
	    curwp->w_flag |= WFMOVE;
	}
	else
	  curwp->w_doto++;
    }
    
    return (TRUE);
}


/*
 * move to a particular line.
 * argument (n) must be a positive integer for
 * this to actually do anything
 */
int
gotoline(int f, int n)
{
    if (n < 1)		/* if a bogus argument...then leave */
      return(FALSE);

    /* first, we go to the start of the buffer */
    curwp->w_dotp  = lforw(curbp->b_linep);
    curwp->w_doto  = 0;
    return(forwline(f, n-1));
}


/*
 * Goto the beginning of the buffer. Massive adjustment of dot. This is
 * considered to be hard motion; it really isn't if the original value of dot
 * is the same as the new value of dot. Normally bound to "M-<".
 */
int
gotobob(int f, int n)
{
    curwp->w_dotp  = lforw(curbp->b_linep);
    curwp->w_doto  = 0;
    curwp->w_flag |= WFHARD;
    return (TRUE);
}


/*
 * Move to the end of the buffer. Dot is always put at the end of the file
 * (ZJ). The standard screen code does most of the hard parts of update.
 * Bound to "M->".
 */
int
gotoeob(int f, int n)
{
    curwp->w_dotp  = curbp->b_linep;
    curwp->w_doto  = 0;
    curwp->w_flag |= WFHARD;
    return (TRUE);
}


/*
 * Move forward by full lines. If the number of lines to move is less than
 * zero, call the backward line function to actually do it. The last command
 * controls how the goal column is set. Bound to "C-N". No errors are
 * possible.
 */
int
forwline(int f, int n)
{
    register LINE   *dlp;

    if (n < 0)
      return (backline(f, -n));

    if ((lastflag&CFCPCN) == 0)             /* Reset goal if last   */
      curgoal = getccol(FALSE);       /* not C-P or C-N       */

    thisflag |= CFCPCN;
    dlp = curwp->w_dotp;
    while (n-- && dlp!=curbp->b_linep)
      dlp = lforw(dlp);

    curwp->w_dotp  = dlp;
    curwp->w_doto  = getgoal(dlp);
    curwp->w_flag |= WFMOVE;
    return (TRUE);
}


/*
 * This function is like "forwline", but goes backwards. The scheme is exactly
 * the same. Check for arguments that are less than zero and call your
 * alternate. Figure out the new line and call "movedot" to perform the
 * motion. No errors are possible. Bound to "C-P".
 */
int
backline(int f, int n)
{
    register LINE   *dlp;

    if (n < 0)
      return (forwline(f, -n));

    if(Pmaster && Pmaster->headents){
	/*
	 * go up into editing the mail header if on the top line
	 * and the user hits the up arrow!!!
	 */
	if (lback(curwp->w_dotp) == curbp->b_linep)
	  /*
	   * if the editor returns anything except -1 then the user
	   * has requested something special, so let pico know...
	   */
	  return(HeaderEditor(1, 1));
    }

    if ((lastflag&CFCPCN) == 0)             /* Reset goal if the    */
      curgoal = getccol(FALSE);       /* last isn't C-P, C-N  */

    thisflag |= CFCPCN;
    dlp = curwp->w_dotp;
    while (n-- && lback(dlp)!=curbp->b_linep)
      dlp = lback(dlp);

    curwp->w_dotp  = dlp;
    curwp->w_doto  = getgoal(dlp);
    curwp->w_flag |= WFMOVE;
    return (TRUE);
}


/*
 * go back to the begining of the current paragraph
 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
 * combination to delimit the begining of a paragraph	
 */
int
gotobop(int f, int n)
{
    int quoted, qlen;
    char qstr[NLINE], qstr2[NLINE], ind_str[NLINE], pqstr[NLINE];;

    if (n < 0)	/* the other way...*/
      return(gotoeop(f, -n));

    while (n-- > 0) {	/* for each one asked for */

	while(lisblank(curwp->w_dotp)
	      && lback(curwp->w_dotp) != curbp->b_linep){
	    curwp->w_dotp = lback(curwp->w_dotp);
	    curwp->w_doto = 0;
	}

	if (indent_match(default_qstr(glo_quote_str, 1), curwp->w_dotp,ind_str, NLINE, 0)){
	   if (n){ /* look for another paragraph ? */
	      curwp->w_dotp = lback(curwp->w_dotp);
	      continue;
	   }
	   break;
	}
	
	/* scan line by line until we come to a line ending with
	 * a <NL><NL> or <NL><TAB> or <NL><SPACE>
	 *
	 * PLUS: if there's a quote string, a quoted-to-non-quoted
	 *	 line transition.
	 */
	quoted = quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 0);
	qlen   = quoted ? strlen(qstr) : 0;
	while(lback(curwp->w_dotp) != curbp->b_linep
	      && llength(lback(curwp->w_dotp)) > qlen
	      && (quoted == quote_match(default_qstr(glo_quote_str, 1),
			lback(curwp->w_dotp), qstr2, NLINE, 0))
	      && !strcmp(qstr, qstr2)   /* processed string */
	      && (quoted == quote_match(default_qstr(glo_quote_str, 1),
			lback(curwp->w_dotp), qstr2, NLINE, 1))
	      && !strcmp(qstr, qstr2)   /* raw string */
	      && !indent_match(default_qstr(glo_quote_str, 1),
			lback(curwp->w_dotp),ind_str, NLINE, 0)
	      && !ISspace(lgetc(curwp->w_dotp, qlen).c))
	  curwp->w_dotp = lback(curwp->w_dotp);

	 /*
	  * Ok, we made it here and we assume that we are at the begining
	  * of the paragraph. Let's double check this now. In order to do
	  * so we shell check if the first line was indented in a special
	  * way.
	  */
	if(lback(curwp->w_dotp) == curbp->b_linep)
	    break;
	else{
	     int i, j;

	   /*
	    * First we test if the preceding line is indented.
	    * for the following test we need to have the raw values,
	    * not the processed values
	    */
	   quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 1);
	   quote_match(default_qstr(glo_quote_str, 1), lback(curwp->w_dotp), qstr2, NLINE, 1);
	   for (i = 0, j = 0;
	        qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]); i++, j++);
	   for (; ISspace(qstr2[i]); i++);
	   for (; ISspace(qstr[j]); j++);
	   if ((indent_match(default_qstr(glo_quote_str, 1), lback(curwp->w_dotp),
						ind_str, NLINE, 1)
	       && (strlenis(qstr2) 
			+ strlenis(ind_str) >= strlenis(qstr)))
	      || (lback(curwp->w_dotp) != curbp->b_linep
	         && llength(lback(curwp->w_dotp)) > qlen
	         && (quoted == quote_match(default_qstr(glo_quote_str, 1),
				lback(curwp->w_dotp), pqstr, NLINE, 0))
		 && !strcmp(qstr, pqstr)
		 && !ISspace(lgetc(curwp->w_dotp, qlen).c)
		 && (strlenis(qstr2) > strlenis(qstr)))
	         && !qstr2[i] && !qstr[j])
		curwp->w_dotp = lback(curwp->w_dotp);
	}

	if(n){
	    /* keep looking */
	    if(lback(curwp->w_dotp) == curbp->b_linep)
	      break;
	    else
	      curwp->w_dotp = lback(curwp->w_dotp);

	    curwp->w_doto = 0;
	}
	else{
	  /* leave cursor on first word in para */
	    curwp->w_doto = 0;
	    while(ISspace(lgetc(curwp->w_dotp, curwp->w_doto).c))
	      if(++curwp->w_doto >= llength(curwp->w_dotp)){
		  curwp->w_doto = 0;
		  curwp->w_dotp = lforw(curwp->w_dotp);
		  if(curwp->w_dotp == curbp->b_linep)
		    break;
	      }
	}
    }

    curwp->w_flag |= WFMOVE;	/* force screen update */
    return(TRUE);
}

unsigned char 
GetAccent(void)
{
  UCS c,d;
    c = GetKey();
    if ((c == '?') || (c == '!')) {
        d = c;
        c = '\\';
    }
    else if ((c == 's') || (c == 'S'))
	 c =  d = 's';
    else if ((c == 'l') || (c == 'L'))
	   c = d = 'l';
    else d = GetKey();
    return accent(c,d);
}

int 
pineaccent(int f, int n)
{ 
   unsigned char e;
   if (e = GetAccent())
      execute(e, 0, 1);
   return 1;
}

#define SWAP(X, Y) do { UCS _e;  _e = (X); (X) = (Y); (Y) = _e; } while (0)

unsigned char 
accent(UCS c, UCS d)
{
   switch(c){
     case 'a': case 'A':
     case 'e': case 'E':
     case 'i': case 'I':
     case 'o': case 'O':
     case 'u': case 'U':
	switch(d){
	    case '~'   : if(c == 'a' || c == 'A' || c == 'o' || c == 'O')
			    SWAP(c, d);
			 break;
	    case '\047': 
	    case '`'   : 
	    case '"'   : 
	    case '^'   : SWAP(c, d);
			 break;
	}
	break;
     case 'y': case 'Y': if (d == '\047') SWAP(c, d); break;
     case 'n': case 'N': if (d == '~') SWAP(c, d); break;
     case 'c': case 'C': SWAP(c,d); break; 
   }

   switch(c){
	case '~' :  
            switch(d){
		case 'a' : return '\343';
		case 'n' : return '\361';
		case 'o' : return '\365';
		case 'A' : return '\303';
		case 'N' : return '\321';
		case 'O' : return '\325';
	    }
	    break;
	case '\047' :
	    switch(d){
		case 'a' : return '\341';
		case 'e' : return '\351';
		case 'i' : return '\355';
		case 'o' : return '\363';
		case 'u' : return '\372';
		case 'y' : return '\375';
		case 'A' : return '\301';
		case 'E' : return '\311';
		case 'I' : return '\315';
		case 'O' : return '\323';
		case 'U' : return '\332';
		case 'Y' : return '\335';
	     }
	     break;
	case '"' :
	     switch(d){
		case 'a' : return '\344';
		case 'e' : return '\353';
		case 'i' : return '\357';
		case 'o' : return '\366';
		case 'u' : return '\374';
		case 'y' : return '\377';
		case 'A' : return '\304';
		case 'E' : return '\313';
		case 'I' : return '\317';
		case 'O' : return '\326';
		case 'U' : return '\334';
	     }
	     break;
	case '^' :
	     switch(d){
		case 'a' : return '\342';
		case 'e' : return '\352';
		case 'i' : return '\356';
		case 'o' : return '\364';
		case 'u' : return '\373';
		case 'A' : return '\302';
		case 'E' : return '\312';
		case 'I' : return '\316';
		case 'O' : return '\324';
		case 'U' : return '\333';
		case '0' : return '\260';
		case '1' : return '\271';
		case '2' : return '\262';
		case '3' : return '\263';
	     }
	     break;
	case '`' :
	     switch(d){
		case 'a' : return '\340';
		case 'e' : return '\350';
		case 'i' : return '\354';
		case 'o' : return '\362';
		case 'u' : return '\371';
		case 'A' : return '\300';
		case 'E' : return '\310';
		case 'I' : return '\314';
		case 'O' : return '\322';
		case 'U' : return '\331';
	      }
	      break;
	case 'o' :
	      switch(d){
		case 'a' : return '\345';
		case 'A' : return '\305';
		case '/' : return '\370';
		case 'r' : return '\256';
		case 'R' : return '\256';
		case 'c' : return '\251';
		case 'C' : return '\251';
	      }
	      break;
	case '-' :
	      switch(d){
		case 'o' : return '\272';
		case 'O' : return '\272';
		case '0' : return '\272';
		case 'a' : return '\252';
		case 'A' : return '\252';
		case 'l' : return '\243';
		case 'L' : return '\243';
	      }
	      break;
	case 'O' :
	      switch(d){
		case '/' : return '\330';
		case 'r' : return '\256';
		case 'R' : return '\256';
		case 'c' : return '\251';
		case 'C' : return '\251';
	      }
	      break;
	case '/' :
	      switch(d){
		case 'o' : return '\370';
		case 'O' : return '\330';
	      }
	      break;
	case 'a' :
	      switch(d){
		case 'e' : return '\346';
		case 'E' : return '\346';
	      }
	      break;
	case 'A' :
	      switch(d){
		case 'E' : return '\306';
		case 'e' : return '\306';
	      }
	      break;
	case ',' :
	      switch(d){
		case 'c' : return '\347';
		case 'C' : return '\307';
	      }
	      break;
	case '\\' :
	      switch(d){
		case '?' : return '\277';
		case '!' : return '\241';
	      }
	      break;
	case 's' :
	      switch(d){
		case 's' : return '\337';
	      }
	      break;
	case 'l' :
	      switch(d){
		case 'l' : return '\243';
	      }
	      break;
   }
   return '\0';
}

/* 
 * go forword to the end of the current paragraph
 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
 * combination to delimit the begining of a paragraph
 */
int
gotoeop(int f, int n)
{
    int quoted, qlen, indented, changeqstr = 0;
    int i,j, fli = 0; /* fli = first line indented a boolean variable */
    char qstr[NLINE], qstr2[NLINE], ind_str[NLINE];

    if (n < 0)	/* the other way...*/
      return(gotobop(f, -n));

    while (n-- > 0) {	/* for each one asked for */

	while(lisblank(curwp->w_dotp)){
	    curwp->w_doto = 0;
	    if((curwp->w_dotp = lforw(curwp->w_dotp)) == curbp->b_linep)
	      break;
	}

	/*
	 * We need to figure out if this line is the first line of
	 * a paragraph that has been indented in a special way. If this
	 * is the case, we advance one more line before we use the
	 * algorithm below
	 */

	if(curwp->w_dotp != curbp->b_linep){
	   quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 1);
	   quote_match(default_qstr(glo_quote_str, 1), lforw(curwp->w_dotp), qstr2, NLINE, 1);
	   indented = indent_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, ind_str,
							NLINE, 1);
	   if (strlenis(qstr) 
		+ strlenis(ind_str) < strlenis(qstr2)){
		curwp->w_doto = llength(curwp->w_dotp);
		if(n){    /* this line is a paragraph by itself */
		   curwp->w_dotp = lforw(curwp->w_dotp);
		   continue;
		}
		break;
	   }
	   for (i=0,j=0; qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]);i++,j++);
	   for (; ISspace(qstr[i]); i++);
	   for (; ISspace(qstr2[j]); j++);
	   if (!qstr[i] && !qstr2[j] && indented){
		fli++;
		if (indent_match(default_qstr(glo_quote_str, 1), lforw(curwp->w_dotp),
					ind_str, NLINE, 0)){
		    if (n){ /* look for another paragraph ? */
		      curwp->w_dotp = lforw(curwp->w_dotp);
		      continue;
		    }
		}
		else{
		  if (!lisblank(lforw(curwp->w_dotp)))
		     curwp->w_dotp = lforw(curwp->w_dotp);
		}
	   }
	}

	/* scan line by line until we come to a line ending with
	 * a <NL><NL> or <NL><TAB> or <NL><SPACE>
	 *
	 * PLUS: if there's a quote string, a quoted-to-non-quoted
	 *	 line transition.
	 */
	/* if the first line is indented (fli == 1), then the test below
	   is on the second line, and in that case we will need the raw
	   string, not the processed string
	 */
	quoted = quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, fli);
	qlen   = quoted ? strlen(qstr) : 0;
	
	while(curwp->w_dotp != curbp->b_linep
	      && llength(lforw(curwp->w_dotp)) > qlen
	      && (quoted == quote_match(default_qstr(glo_quote_str, 1),
				lforw(curwp->w_dotp), qstr2, NLINE, fli))
	      && !strcmp(qstr, qstr2)
	      && (quoted == quote_match(default_qstr(glo_quote_str, 1),
				lforw(curwp->w_dotp), qstr2, NLINE, 1))
	      && !strcmp(qstr, qstr2)
	      && !indent_match(default_qstr(glo_quote_str, 1),
				lforw(curwp->w_dotp), ind_str, NLINE, 0)
	      && !ISspace(lgetc(lforw(curwp->w_dotp), qlen).c))
	  curwp->w_dotp = lforw(curwp->w_dotp);

	curwp->w_doto = llength(curwp->w_dotp);

	/* still looking? */
	if(n){
	    if(curwp->w_dotp == curbp->b_linep)
	      break;
	    else
	      curwp->w_dotp = lforw(curwp->w_dotp);

	    curwp->w_doto = 0;
	}
    }

    curwp->w_flag |= WFMOVE;	/* force screen update */
    return(curwp->w_dotp != curbp->b_linep);
}

/*
 * This routine, given a pointer to a LINE, and the current cursor goal
 * column, return the best choice for the offset. The offset is returned.
 * Used by "C-N" and "C-P".
 */
int
getgoal(LINE *dlp)
{
    UCS    c;
    register int    col;
    register int    newcol;
    register int    dbo;

    col = 0;
    dbo = 0;
    while (dbo != llength(dlp)) {
	c = lgetc(dlp, dbo).c;
	newcol = col;

	if (c == '\t'){
	    newcol |= 0x07;
	    ++newcol;
	}
	else if (ISCONTROL(c)){
	    newcol += 2;
	}
	else{
	    int w;

	    w = wcellwidth(c);
	    newcol += (w >= 0 ? w : 1);
	}

	if (newcol > curgoal)
	  break;

	col = newcol;
	++dbo;
    }

    return (dbo);
}


/*
 * Scroll the display forward (up) n lines.
 */
int
scrollforw(int n, int movedot)
{
    register LINE   *lp;
    LINE	    *lp2;
    register int    nl;
    int		    i;

    nl = n;
    lp = curwp->w_linep;
    while (n-- && lp!=curbp->b_linep)
      lp = lforw(lp);

    if (movedot) {			/* Move dot to top of page. */
	curwp->w_dotp  = lp;
	curwp->w_doto  = 0;
    }

    curwp->w_flag |= WFHARD;
    if(lp == curbp->b_linep)
      return(TRUE);
    else
      curwp->w_linep = lp;

    /*
     * if the header is open, close it ...
     */
    if(Pmaster && Pmaster->headents && ComposerTopLine != COMPOSER_TOP_LINE){
	n -= ComposerTopLine - COMPOSER_TOP_LINE;
	ToggleHeader(0);
    }

    /*
     * scroll down from the top the same number of lines we've moved 
     * forward
     */
    if(TERM_OPTIMIZE)
      scrollup(curwp, -1, nl-n-1);

    if(!movedot){
	/* Requested to not move the dot.  Look for the dot in the current
	 * window.  loop through all lines, stop when at end of window
	 * or endof buffer.  If the dot is found, it can stay where it
	 * is, otherwise we do need to move it.
	 */
	movedot = TRUE;
	for (	lp2 = lp, i = 0; 
		lp2 != curbp->b_linep && i < curwp->w_ntrows;  
		lp2 = lforw(lp2), ++i) {
	    if (curwp->w_dotp == lp2) {
		 movedot = FALSE;
		 break;
	    }
        }
	if (movedot) {
	    /* Dot not found in window.  Move to first line of window. */
	    curwp->w_dotp  = lp;
	    curwp->w_doto  = 0;
        }
    }

    return (TRUE);
}


/*
 * Scroll forward by a specified number of lines, or by a full page if no
 * argument. Bound to "C-V". The "2" in the arithmetic on the window size is
 * the overlap; this value is the default overlap value in ITS EMACS. Because
 * this zaps the top line in the display window, we have to do a hard update.
 */
int
forwpage(int f, int n)
{

    if (f == FALSE) {
	n = curwp->w_ntrows - 2;        /* Default scroll.      */
	if (n <= 0)                     /* Forget the overlap   */
	  n = 1;                  /* if tiny window.      */
    } else if (n < 0)
      return (backpage(f, -n));
#if     CVMVAS
    else                                    /* Convert from pages   */
      n *= curwp->w_ntrows;           /* to lines.            */
#endif
    return (scrollforw (n, TRUE));
}


/*
 * Scroll back (down) number of lines.  
 */
int
scrollback(int n, int movedot)
{
    register LINE   *lp, *tp;
    register int    nl;
    int		    i;

    if(Pmaster && Pmaster->headents){
	/*
	 * go up into editing the mail header if on the top line
	 * and the user hits the up arrow!!!
	 */
	if (lback(curwp->w_dotp) == curbp->b_linep){
	    /*
	     * if the editor returns anything except -1 then the user
	     * has requested something special, so let pico know...
	     */
	    return(HeaderEditor(1, 1));
	}
    }

    /*
     * Count back the number of lines requested.
     */
    nl = n;
    lp = curwp->w_linep;
    while (n-- && lback(lp)!=curbp->b_linep)
      lp = lback(lp);

    curwp->w_linep = lp;
    curwp->w_flag |= WFHARD;

    /*
     * scroll down from the top the same number of lines we've moved 
     * forward
     *
     * This isn't too cool, but it has to be this way so we can 
     * gracefully scroll in the message header
     */
    if(Pmaster && Pmaster->headents){
	if((lback(lp)==curbp->b_linep) && (ComposerTopLine==COMPOSER_TOP_LINE))
	  n -= entry_line(1000, TRUE); /* never more than 1000 headers */
	if(nl-n-1 < curwp->w_ntrows)
	  if(TERM_OPTIMIZE)
	    scrolldown(curwp, -1, nl-n-1);
    }
    else
      if(TERM_OPTIMIZE)
	scrolldown(curwp, -1, nl-n-1);

    if(Pmaster && Pmaster->headents){
	/*
	 * if we're at the top of the page, and the header is closed, 
	 * open it ...
	 */
	if((lback(lp) == curbp->b_linep) 
	   && (ComposerTopLine == COMPOSER_TOP_LINE)){
	    ToggleHeader(1);
	    movecursor(ComposerTopLine, 0);
	}
    }
    
    /*
     * Decide if we move the dot or not.  Calculation done AFTER deciding
     * if we display the header because that will change the number of
     * lines on the screen.
     */
    if (movedot) {
	/* Dot gets put at top of window. */
	curwp->w_dotp  = curwp->w_linep;
	curwp->w_doto  = 0;
    }
    else {
	/* Requested not to move dot, but we do need to keep in on
	 * the screen.  Verify that it is still in the range of lines
	 * visable in the window.  Loop from the first line to the
	 * last line, until we reach the end of the buffer or the end
	 * of the window.  If we find the dot, then we don't need
	 * to move it. */
	movedot = TRUE;
	for (	tp = curwp->w_linep, i = 0; 
		tp != curbp->b_linep && i < curwp->w_ntrows;  
		tp = lforw(tp), ++i) {
	    if (curwp->w_dotp == tp) {
		 movedot = FALSE;
		 break;
	    }
        }
	if (movedot) {
	    /* Dot not found in window.  Move to last line of window. */
	    curwp->w_dotp  = lback (tp);
	    curwp->w_doto  = 0;
        }
    }

    return (TRUE);
}




/*
 * This command is like "forwpage", but it goes backwards. The "2", like
 * above, is the overlap between the two windows. The value is from the ITS
 * EMACS manual. Bound to "M-V". We do a hard update for exactly the same
 * reason.
 */
int
backpage(int f, int n)
{

    if (f == FALSE) {
	n = curwp->w_ntrows - 2;        /* Default scroll.      */
	if (n <= 0)                     /* Don't blow up if the */
	  n = 1;                  /* window is tiny.      */
    } else if (n < 0)
      return (forwpage(f, -n));
#if     CVMVAS
    else                                    /* Convert from pages   */
      n *= curwp->w_ntrows;           /* to lines.            */
#endif
    return (scrollback (n, TRUE));
}


int
scrollupline(int f, int n)
{
    return (scrollback (1, FALSE));
}


int
scrolldownline(int f, int n)
{
    return (scrollforw (1, FALSE));
}

/* deltext deletes from the specified position until the end of the file
 * or until the signature (when called from Pine), whichever comes first.
 */

int
deltext (f,n)
int f,n;
{               
  LINE *currline = curwp->w_dotp;
  static int firsttime = 0;

  if ((lastflag&CFKILL) == 0)
     kdelete();
  
  curwp->w_markp = curwp->w_dotp;
  curwp->w_marko = curwp->w_doto;
  
  while (curwp->w_dotp != curbp->b_linep){
     if ((Pmaster) 
    	&& (llength(curwp->w_dotp) == 3) 
	&& (lgetc(curwp->w_dotp, 0).c == '-') 
	&& (lgetc(curwp->w_dotp, 1).c == '-') 
	&& (lgetc(curwp->w_dotp, 2).c == ' ')){
	  if (curwp->w_dotp == currline){
	     if (curwp->w_doto)
		curwp->w_dotp = lforw(curwp->w_dotp);
	     else
	   	break;
     	  }
     	  else{
	     curwp->w_dotp = lback(curwp->w_dotp);
	     curwp->w_doto = llength(curwp->w_dotp);
	     break;
          }
     }
     else{
	if(lforw(curwp->w_dotp) != curbp->b_linep)
	 curwp->w_dotp = lforw(curwp->w_dotp);
	else{
	 curwp->w_doto = llength(curwp->w_dotp);
	 break;
	}
     }
  }         
  killregion(FALSE,1);
  lastflag |= CFKILL;
  if(firsttime == 0)
     emlwrite("Deleted text can be recovered with the ^U command", NULL);
  firsttime = 1;
  return TRUE;
}

/*
 * Scroll to a position.
 */
int
scrollto(int f, int n)
{
#ifdef _WINDOWS
    long	scrollLine;
    LINE	*lp;
    int		i;
    
    scrollLine = mswin_getscrollto ();
    
    /*
     * Starting at the first data line in the buffer, step forward
     * 'scrollLine' lines to find the new top line.  It is a circular
     * list of buffers, so watch for the first line to reappear.  if
     * it does, we have some sort of internal error, abort scroll
     * operation.  Also watch for NULL, just in case.
     */
    lp = lforw (curbp->b_linep);
    for (i = 0; i < scrollLine && lp != curbp->b_linep && lp != NULL; ++i)
	lp = lforw(lp);

    if (lp == curbp->b_linep || lp == NULL)
	return (FALSE);					/* Whoops! */
    

    /* Set the new top line for the window and flag a redraw. */
    curwp->w_linep = lp;
    curwp->w_dotp  = lp;
    curwp->w_doto  = 0;
    curwp->w_flag |= WFHARD;
    
    if(Pmaster && Pmaster->headents){
	/*
	 * If we are at the top of the page and header not open, open it.
	 * If we are not at the top of the page and the header is open,
	 * close it.
	 */
	if((lback(lp) == curbp->b_linep) 
	   && (ComposerTopLine == COMPOSER_TOP_LINE)){
	    ToggleHeader(1);
	    movecursor(ComposerTopLine, 0);
	}
	else if((lback(lp) != curbp->b_linep) 
	   && (ComposerTopLine != COMPOSER_TOP_LINE)){
	   ToggleHeader (0);
        }
    }
#endif

    return (TRUE);
}



/*
 * Set the mark in the current window to the value of "." in the window. No
 * errors are possible. Bound to "M-.".  If told to set an already set mark
 * unset it.
 */
int
setmark(int f, int n)
{
    if(!curwp->w_markp){
        curwp->w_markp = curwp->w_dotp;
        curwp->w_marko = curwp->w_doto;
	if(n)
	  emlwrite("Mark Set", NULL);
    }
    else{
	/* clear inverse chars between here and dot */
	markregion(0);
	curwp->w_markp = NULL;
	if(n)
	  emlwrite("Mark UNset", NULL);
    }

#ifdef	_WINDOWS
    mswin_allowcopycut(curwp->w_markp ? kremove : NULL);
#endif
    return (TRUE);
}


/*
 * Swap the values of "." and "mark" in the current window. This is pretty
 * easy, bacause all of the hard work gets done by the standard routine
 * that moves the mark about. The only possible error is "no mark". Bound to
 * "C-X C-X".
 */
int
swapmark(int f, int n)
{
    register LINE   *odotp;
    register int    odoto;

    if (curwp->w_markp == NULL) {
	if(Pmaster == NULL)
	  emlwrite("No mark in this window", NULL);
	return (FALSE);
    }

    odotp = curwp->w_dotp;
    odoto = curwp->w_doto;
    curwp->w_dotp  = curwp->w_markp;
    curwp->w_doto  = curwp->w_marko;
    curwp->w_markp = odotp;
    curwp->w_marko = odoto;
    curwp->w_flag |= WFMOVE;
    return (TRUE);
}


/*
 * Set the mark in the current window to the value of "." in the window. No
 * errors are possible. Bound to "M-.".  If told to set an already set mark
 * unset it.
 */
int
setimark(int f, int n)
{
    curwp->w_imarkp = curwp->w_dotp;
    curwp->w_imarko = curwp->w_doto;
    return(TRUE);
}


/*
 * Swap the values of "." and "mark" in the current window. This is pretty
 * easy, bacause all of the hard work gets done by the standard routine
 * that moves the mark about. The only possible error is "no mark". Bound to
 * "C-X C-X".
 */
int
swapimark(int f, int n)
{
    register LINE   *odotp;
    register int    odoto;

    if (curwp->w_imarkp == NULL) {
	if(Pmaster == NULL)
	  emlwrite("Programmer botch! No mark in this window", NULL);
	return (FALSE);
    }

    odotp = curwp->w_dotp;
    odoto = curwp->w_doto;
    curwp->w_dotp  = curwp->w_imarkp;
    curwp->w_doto  = curwp->w_imarko;
    curwp->w_imarkp = odotp;
    curwp->w_imarko = odoto;
    curwp->w_flag |= WFMOVE;
    return (TRUE);
}


/*
 * If dot comes before mark, do nothing.
 * If mark comes before dot, swap them.
 */
void
swap_mark_and_dot_if_mark_comes_first(void)
{
    LINE *blp, *flp;

    if(!(curwp && curwp->w_dotp && curwp->w_markp))
      return;

    if(curwp->w_dotp == curwp->w_markp){	/* they are in the same line */
	if(curwp->w_doto > curwp->w_marko)
	  swapmark(0,1);

	return;
    }

    /*
     * Search forward and backward from dot to see if mark
     * is less than or greater than dot.
     */
    flp = blp = curwp->w_dotp;
    while(flp != curbp->b_linep || lback(blp) != curbp->b_linep){
	if(flp != curbp->b_linep){
	    flp = lforw(flp);
	    if(flp == curwp->w_markp)		/* dot already less than mark */
	      return;
	}

	if(lback(blp) != curbp->b_linep){
	    blp = lback(blp);
	    if(blp == curwp->w_markp){
		swapmark(0, 1);
		return;
	    }
	}
    }
}


#ifdef MOUSE

/*
 * Handle a mouse down.
 */
int
mousepress(int f, int n)
{
    MOUSEPRESS	mp;
    LINE	*lp;
    int    i;


    mouse_get_last (NULL, &mp);


    lp = curwp->w_linep;
    i = mp.row - ((Pmaster && Pmaster->headents) ? ComposerTopLine : 2);
    if (i < 0) {
	if (Pmaster) {
	    /* Clear existing region. */
	    if (curwp->w_markp)
		setmark(0,1);	

	    /* Move to top of document before editing header. */
	    curwp->w_dotp = curwp->w_linep;
	    curwp->w_doto = 0;
	    curwp->w_flag |= WFMOVE;
	    update ();				/* And update. */

	    return (HeaderEditor (1, 1));
        }
    }
    else {
	while(i-- && lp != curbp->b_linep)
	  lp = lforw(lp);

	curgoal = mp.col;
	curwp->w_dotp = lp;
	curwp->w_doto = getgoal(lp);
	curwp->w_flag |= WFMOVE;

	if(mp.doubleclick)
	    setmark(0, 1);
    }

    return(FALSE);
}


int
toggle_xterm_mouse(int f, int n)
{
#ifndef _WINDOWS
    int e;

    (e=mouseexist()) ? end_mouse() : (void) init_mouse();
    if(e != mouseexist()){
	mouseexist() ? emlwrite(_("Xterm mouse tracking on!"), NULL)
		     : emlwrite(_("Xterm mouse tracking off!"), NULL);
    }
    else if(!e)
      emlwrite(_("Xterm mouse tracking still off ($DISPLAY variable set?)"), NULL);
#endif
    return(TRUE);
}
#endif
