/*
 * 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 <stdio.h>
#include "ed.h"

/*
 * Move the cursor to the beginning of the current line.
 */
gotobol(f, 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.
 */
backchar(f, n)
register int n;
{
 register LINE  *lp;

 if (n < 0) {
  return (forwchar(f, -n));
 }
 while (n--) {
  if (curwp->w_doto == 0) {
  /*
   * 29.7.89/ms stripLine() eingefügt.
   */
   stripLine(curwp->w_dotp);
   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.
 */
gotoeol(f, 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.
 */
forwchar(f, n)
register 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);
  /*
   * 29.7.89/ms stripLine() eingefügt.
   */
   stripLine(curwp->w_dotp);
   curwp->w_dotp = lforw(curwp->w_dotp);
   curwp->w_doto = 0;
   curwp->w_flag |= WFMOVE;
  }
  else
   curwp->w_doto++;
 }
 return (TRUE);
}

/*
 * 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-<".
 */
gotobob(f, 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->".
 */
gotoeob(f, 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.
 */
forwline(f, 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;
 /*
  * 29.7.89/ms stripLine sobald die Zeile verlassen wird.
  */
  stripLine(dlp);
  if (kbdmop) update();
  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".
 */
backline(f, n)
{
  register LINE  *dlp;

  if (n < 0)
    return (forwline(f, -n));
  if ((lastflag&CFCPCN) == 0)   /* Reset goal if the */
    curgoal = getccol(FALSE);/* last isn't C-P, C-N */
  thisflag |= CFCPCN;
  dlp = curwp->w_dotp;
 /*
  * 29.7.89/ms stripLine sobald die Zeile verlassen wird.
  */
  stripLine(dlp);
  if (kbdmop) update();
  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);
}

/*
 * 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".
 */
getgoal(dlp)
register LINE  *dlp;
{
  register int c;
  register int col;
  register int newcol;
  register int dbo;

  col = 0;
  dbo = 0;
  while (dbo != llength(dlp)) {
    c = lgetc(dlp, dbo);
    newcol = col;
    if (c == '\t')
      newcol |= 0x07;
    else if (c<0x20 || c==0x7F)
      ++newcol;
    ++newcol;
    if (newcol > curgoal)
      break;
    col = newcol;
    ++dbo;
  }
  return (dbo);
}

/*
 * 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.
 */
forwpage(f, n)
register int n;
{
 register LINE  *lp;

 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
 lp = curwp->w_linep;
 while (n-- && lp!=curbp->b_linep)
  lp = lforw(lp);
 if (curwp->w_linep != lp) {
  curwp->w_linep = lp;
  curwp->w_flag |= WFHARD;
 }
 else
  curwp->w_flag |= WFMOVE;

 curwp->w_dotp = lp;
 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.
 */
backpage(f, n)
register int n;
{
 register LINE  *lp;

 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
 lp = curwp->w_linep;
 while (n-- && lback(lp)!=curbp->b_linep)
  lp = lback(lp);
 if (lp != curwp->w_linep) {
  curwp->w_linep = lp;
  curwp->w_flag |= WFHARD;
 }
 else
  curwp->w_flag |= WFMOVE;

 curwp->w_dotp = lp;
 curwp->w_doto = 0;

 return (TRUE);
}

/*
 * Set the mark in the current window to the value of "." in the window. No
 * errors are possible. Bound to "M-.".
 */
setmark(f, n)
{
 curwp->w_markp = curwp->w_dotp;
 curwp->w_marko = curwp->w_doto;
#ifdef DEUTSCH
 mlwrite("[Marke gesetzt]");
#else
 mlwrite("[Mark set]");
#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".
 */
swapmark(f, n)
{
 register LINE  *odotp;
 register int odoto;

 if (curwp->w_markp == NULL) {
#ifdef DEUTSCH
  mlwrite("Keine Marke in diesem Fenster");
#else
  mlwrite("No mark in this window");
#endif
  return (FALSE);
 }
 odotp = curwp->w_dotp;
 odoto = curwp->w_doto;
 curwp->w_dotp = curwp->w_markp;
#if 0
/*
 * 20.2.92/ms
 * Dieser Block scheint falsch weil der offset doto das Zeichen im
 * Buffer betrifft und getgoal die Cursor Position benötigt.
 * Neu wird stripLine bei jedem strip überlegen ob die Marke noch
 * gültig ist.
 */
/*
 * 7.10.90/ms
 * ClearLines hat die Position der Marke eventuell ungültig
 * gemacht. Wir müssen hier 'getgoal()' zu Rate ziehen.
 */
 ogoal = curgoal;
 curgoal = curwp->w_marko;
 curwp->w_doto = getgoal(curwp->w_dotp);
 curgoal = ogoal;
#else
 curwp->w_doto = curwp->w_marko;
#endif
 curwp->w_markp = odotp;
 curwp->w_marko = odoto;
 curwp->w_flag |= WFMOVE;
 return (TRUE);
}


moveline(f, n)  /* move to n lines from the top of buffer */
{
#ifdef  DEUTSCH
#define GOTOLINE "Gehe zur Zeile: "
#else
#define GOTOLINE "Move to line: "
#endif

  register int c;
  char buf[24];

  f = TRUE;
  n = 1;

  buf[0] = '\0';

  if ( (c = mlreply(GOTOLINE, buf, sizeof(buf))) == ABORT) {
    return ABORT;
  }
  else {
    mlerase();
    if ( buf[0] == '$')
      return( gotoeob(FALSE, 1) );
    else
      n = atoi(buf);
    gotobob(FALSE, 1);
    if (n) {
      n--;
    }
    return( forwline(f, n));
  }
}
