/*-- First Line of WVSCREEN.C -- Contains Screen-related functions.  */

#include "windows.h"
#include "wvglob.h"
#include "winvn.h"

/*--- function ScreenDown ----------------------------------------------
 *
 *  Advance a pointer "nLines" lines through the textlines.
 *  However, do not go so far that less than LinesOnScreen lines
 *  are left past the pointer.
 *
 *  This is used to implement "PageDown" and "LineDown" functions.
 *
 *    Entry    nLines         Number of lines to move the pointer.
 *             LinesOnScreen  is the number of lines on the screen.
 *             BlockPtr       Pointer to block containing line.
 *             LinePtr        Pointer to the given line.
 *
 *    Exit     BlockPtr and LinePtr (may) have been moved to a new line.
 *             LinesAdvanced  is the number of lines actually moved--can be
 *                            less than nLines if we hit the top of the
 *                            last screen.
 */
void
ScreenDown (nLines, LinesOnScreen, BlockPtr, LinePtr, LinesAdvanced)
     int nLines;
     int LinesOnScreen;
     TypBlock far **BlockPtr;
     TypLine far **LinePtr;
     int *LinesAdvanced;
{
  TypBlock far *MyBlock = *BlockPtr;
  TypLine far *MyLine = *LinePtr;
  int LinesGone;
  int TestAdvance;
  HANDLE hBlock, hBlockGarbage;
  unsigned int Offset, OffsetGarbage;
  TypLineID LineIDGarbage, MyLineID;

  /* Skip forward nLines, plus one screen's worth, just to see      */
  /* if there are enough lines ahead of us in the document.         */

  PtrToOffset (MyBlock, MyLine, &hBlock, &Offset, &MyLineID);
  TestAdvance = nLines + LinesOnScreen - 1;
  for (LinesGone = TestAdvance; LinesGone && NextLine (&MyBlock, &MyLine);
       LinesGone--);
  UnlockLine (MyBlock, MyLine, &hBlockGarbage, &OffsetGarbage, &LineIDGarbage);

  nLines -= LinesGone;
  if (nLines < 0)
    nLines = 0;
  *LinesAdvanced = nLines;

  LockLine (hBlock, Offset, MyLineID, &MyBlock, &MyLine);
  while (nLines--)
    {
      NextLine (BlockPtr, LinePtr);
    }
}

/*--- function ScreenUp ----------------------------------------------
 *
 *  Back up a pointer "nLines" lines through the textlines.
 *  However, do not go past the beginning of the document.
 *
 *  This is used to implement "PageUp" and "LineUp" functions.
 *
 *    Entry    nLines         Number of lines to move the pointer.
 *             BlockPtr       Pointer to block containing line.
 *             LinePtr        Pointer to the given line.
 *
 *    Exit     BlockPtr and LinePtr (may) have been moved to a new line.
 *             LinesBackedUp  is the number of lines actually moved--can be
 *                            less than nLines if we hit beginning of doc.
 */
void
ScreenUp (nLines, BlockPtr, LinePtr, LinesBackedUp)
     int nLines;
     TypBlock far **BlockPtr;
     TypLine far **LinePtr;
     int *LinesBackedUp;
{
  *LinesBackedUp = 0;

  while (nLines-- && PrevLine (BlockPtr, LinePtr))
    {
      (*LinesBackedUp)++;
    }
}

/*--- function ScrollIt ----------------------------------------------
 *
 *  Perform a scrolling action.
 *
 *    Entry    Document points to a document
 *             wParam   is the wParam argument given from a WM_VSCROLL
 *                      window message.  (One of the SB_ symbols.)
 *             lParam   is the lParam argument.
 */
void
NewScrollIt (Document, wParam, lParam)
     TypDoc *Document;
     WORD wParam;
     DWORD lParam;
{
  TypBlock far *BlockPtr, far * NewBlockPtr;
  TypLine far *LinePtr, far * NewLinePtr;
  int LinesGone;
  unsigned int LineOrd;
  RECT Rect;

  switch (wParam)
    {
    case SB_LINEUP:
      /* Move up a line by scrolling the window down one line     */
      /* and introducing 1 new line at the top.                   */

   if (Document->TopLineOrd > 0) {
      LinesGone = 1;
    }
    else LinesGone = 0;

    doscrollup:;
      if (LinesGone)
	{
	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
	  Document->TopLineOrd -= LinesGone;

	  ScrollWindow (Document->hDocWnd, 0, LinesGone * LineHeight, NULL, NULL);

	  Rect.top = TopSpace;
	  Rect.bottom = TopSpace + LinesGone * LineHeight;

	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);

	  /* Make sure garbage at bottom is erased in WM_PAINT processing */
	  Rect.top = TopSpace + Document->ScYLines * LineHeight;
	  Rect.bottom = Document->ScYHeight;
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
      break;

    case SB_LINEDOWN:

   if (Document->TopLineOrd < (Document->TotalLines - Document->ScYLines)) {
      LinesGone = 1;
    }
   else LinesGone = 0;

    doscrolldown:;
      if (LinesGone)
	{
	  Document->TopLineOrd += LinesGone;
	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
	  ScrollWindow (Document->hDocWnd, 0, -LinesGone * LineHeight, NULL, NULL);
	  Rect.top = TopSpace + (Document->ScYLines - LinesGone) * LineHeight;
	  Rect.bottom = Document->ScYHeight;
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	  /* Make sure garbage at top is erased in WM_PAINT processing */
	  Rect.top = 0;
	  Rect.bottom = TopSpace;
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
      break;

    case SB_PAGEUP:
/*      LockLine (Document->hCurTopScBlock, Document->TopScOffset, Document->TopScLineID, &BlockPtr, &LinePtr); */
/*      ScreenUp (Document->ScYLines - 1, &BlockPtr, &LinePtr, &LinesGone); */
      LinesGone= Document->TopLineOrd - (Document->ScYLines - 1);
      if (LinesGone > 0) {
	LinesGone = Document->ScYLines - 1;
      }
	else {
	LinesGone = Document->TopLineOrd;
	}

      Document->TopLineOrd -= LinesGone;
      InvalidateRect (Document->hDocWnd, NULL, FALSE);
      break;

    case SB_PAGEDOWN:

      if ((Document->TotalLines - (Document->TopLineOrd + Document->ScYLines)) > Document->ScYLines)
	LinesGone = Document->ScYLines - 1;
      else
        LinesGone = Document->TotalLines
                    - (Document->TopLineOrd + Document->ScYLines );

      Document->TopLineOrd += LinesGone;
      InvalidateRect (Document->hDocWnd, NULL, FALSE);
      break;

    case SB_THUMBPOSITION:
      LineOrd = LOWORD (lParam);
/*       if (!FindLineOrd (Document, LineOrd, &BlockPtr, &LinePtr))
	{
	  return;
	}
*/
    doposition:;
      Document->TopLineOrd = LineOrd;
      InvalidateRect (Document->hDocWnd, NULL, FALSE);
      break;

    case SB_THUMBTRACK:

      LineOrd = LOWORD (lParam);
/*      if (!FindLineOrd (Document, LineOrd, &BlockPtr, &LinePtr))
	{
	  return;
	}
*/
      LinesGone = LineOrd - Document->TopLineOrd;
      if (LinesGone > 0)
	{
	  if (LinesGone >= (Document->ScYLines - 1))
	    {
	      goto doposition;
	    }
	  else
	    {
	      goto doscrolldown;
	    }
	}
      else if (LinesGone < 0)
	{
	  LinesGone = -LinesGone;
	  if (LinesGone >= (Document->ScYLines - 1))
	    {
	      goto doposition;
	    }
	  else
	    {
	      goto doscrollup;
	    }
	}

      break;

    default:
      return;
      break;
    }

/*  UnlockLine (BlockPtr, LinePtr, &(Document->hCurTopScBlock),
	      &(Document->TopScOffset), &(Document->TopScLineID));
*/

}
/*--- function ScrollIt ----------------------------------------------
 *
 *  Perform a scrolling action.
 *
 *    Entry    Document points to a document
 *             wParam   is the wParam argument given from a WM_VSCROLL
 *                      window message.  (One of the SB_ symbols.)
 *             lParam   is the lParam argument.
 */
void
ScrollIt (Document, wParam, lParam)
     TypDoc *Document;
     WORD wParam;
     DWORD lParam;
{
  TypBlock far *BlockPtr, far * NewBlockPtr;
  TypLine far *LinePtr, far * NewLinePtr;
  int LinesGone;
  unsigned int LineOrd;
  RECT Rect;
  int window_lineheight;

  if (Document->DocType == DOCTYPE_ARTICLE)
   window_lineheight = ArtLineHeight;
  else
   window_lineheight = LineHeight;

  switch (wParam)
    {
    case SB_LINEUP:
      /* Move up a line by scrolling the window down one line     */
      /* and introducing 1 new line at the top.                   */
      LockLine (Document->hCurTopScBlock, Document->TopScOffset, Document->TopScLineID, &BlockPtr, &LinePtr); 

      ScreenUp (1, &BlockPtr, &LinePtr, &LinesGone);
    doscrollup:;
      if (LinesGone)
	{
	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
	  Document->TopLineOrd -= LinesGone;
#if 0
	  Rect.top = TopSpace;
	  Rect.bottom = Document->ScYHeight;
	  ScrollWindow (Document->hDocWnd, 0, LinesGone * window_lineheight, &Rect, &Rect);
#endif
	  ScrollWindow (Document->hDocWnd, 0, LinesGone * window_lineheight, NULL, NULL);
#ifdef MAC
	  Rect.top = 0;
	  Rect.bottom = LinesGone * window_lineheight;
#else
	  Rect.top = TopSpace;
	  Rect.bottom = TopSpace + LinesGone * window_lineheight;
#endif
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	  /* Make sure garbage at bottom is erased in WM_PAINT processing */
	  Rect.top = TopSpace + Document->ScYLines * window_lineheight;
	  Rect.bottom = Document->ScYHeight;
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
      break;

    case SB_LINEDOWN:
      LockLine (Document->hCurTopScBlock, Document->TopScOffset, Document->TopScLineID, &BlockPtr, &LinePtr);
      ScreenDown (1, Document->ScYLines, &BlockPtr, &LinePtr, &LinesGone);
    doscrolldown:;
      if (LinesGone)
	{
	  Document->TopLineOrd += LinesGone;
	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
#if 0
	  Rect.top = TopSpace;
	  Rect.bottom = Document->ScYHeight;
	  ScrollWindow (Document->hDocWnd, 0, -LinesGone * window_lineheight, &Rect, &Rect);
#endif
	  ScrollWindow (Document->hDocWnd, 0, -LinesGone * window_lineheight, NULL, NULL);
#ifdef MAC
	  Rect.top = (Document->ScYLines - LinesGone) * window_lineheight;
	  Rect.bottom = Document->ScYHeight;
#else
	  Rect.top = TopSpace + (Document->ScYLines - LinesGone) * window_lineheight;
	  Rect.bottom = Document->ScYHeight;
#endif
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	  /* Make sure garbage at top is erased in WM_PAINT processing */
	  Rect.top = 0;
	  Rect.bottom = TopSpace;
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
      break;

    case SB_PAGEUP:
      LockLine (Document->hCurTopScBlock, Document->TopScOffset, Document->TopScLineID, &BlockPtr, &LinePtr);
      ScreenUp (Document->ScYLines - 1, &BlockPtr, &LinePtr, &LinesGone);
      Document->TopLineOrd -= LinesGone;
      InvalidateRect (Document->hDocWnd, NULL, FALSE);
      break;

    case SB_PAGEDOWN:
      LockLine (Document->hCurTopScBlock, Document->TopScOffset, Document->TopScLineID, &BlockPtr, &LinePtr);
      ScreenDown (Document->ScYLines - 1, Document->ScYLines,
		  &BlockPtr, &LinePtr, &LinesGone);
      Document->TopLineOrd += LinesGone;
      InvalidateRect (Document->hDocWnd, NULL, FALSE);
      break;

    case SB_THUMBPOSITION:
      LineOrd = LOWORD (lParam);
      if (!FindLineOrd (Document, LineOrd, &BlockPtr, &LinePtr))
	{
	  return;
	}
    doposition:;
      Document->TopLineOrd = LineOrd;
      InvalidateRect (Document->hDocWnd, NULL, FALSE);
      break;

    case SB_THUMBTRACK:

      LineOrd = LOWORD (lParam);
      if (!FindLineOrd (Document, LineOrd, &BlockPtr, &LinePtr))
	{
	  return;
	}
      LinesGone = LineOrd - Document->TopLineOrd;
      if (LinesGone > 0)
	{
	  if (LinesGone >= (Document->ScYLines - 1))
	    {
	      goto doposition;
	    }
	  else
	    {
	      goto doscrolldown;
	    }
	}
      else if (LinesGone < 0)
	{
	  LinesGone = -LinesGone;
	  if (LinesGone >= (Document->ScYLines - 1))
	    {
	      goto doposition;
	    }
	  else
	    {
	      goto doscrollup;
	    }
	}

      break;

    default:
      return;
      break;
    }

  UnlockLine (BlockPtr, LinePtr, &(Document->hCurTopScBlock),
	      &(Document->TopScOffset), &(Document->TopScLineID));
#ifdef MAC
/* SendMessage(Document->hDocWnd,WM_PAINT,0,0L); */
  MyUpdateWindow (Document->hDocWnd);
#endif

}


/*--- function LineOnScreen ----------------------------------------
 *
 *  Determine whether a given line is displayed on the screen.
 *  If it is, give the ordinal line number on the screen.
 *
 *  Entry   Doc         points to the document.
 *          hTargBlock  is the handle of the block containing the line.
 *          TargOffset  is the offset of the target line.
 *          TargLineID  is the line's ID.
 *
 *  Exit    returns -1 if the line is not on the screen, else
 *          the ordinal line number (0 = top line, etc.)
 */
int
LineOnScreen (Doc, hTargBlock, TargOffset, TargLineID)
     TypDoc *Doc;
     HANDLE hTargBlock;
     unsigned int TargOffset;
     TypLineID TargLineID;
{
  TypBlock far *CurBlockPtr, far * TargBlockPtr;
  TypLine far *CurLinePtr, far * TargLinePtr;
  int iline;
  BOOL found = FALSE;
  HANDLE hBlock;
  unsigned int Offset;
  TypLineID MyLineID;

  LockLine (Doc->hCurTopScBlock, Doc->TopScOffset, Doc->TopScLineID, &CurBlockPtr, &CurLinePtr);
  LockLine (hTargBlock, TargOffset, TargLineID, &TargBlockPtr, &TargLinePtr);

  for (iline = 0; !found && iline < Doc->ScYLines; iline++)
    {
      found = (TargLinePtr == CurLinePtr);
      if (!found)
	{
	  NextLine (&CurBlockPtr, &CurLinePtr);
	}
    }

  UnlockLine (CurBlockPtr, CurLinePtr, &hBlock, &Offset, &MyLineID);
  UnlockLine (TargBlockPtr, TargLinePtr, &hBlock, &Offset, &MyLineID);

  if (!found)
    iline = 0;
  return (iline - 1);
}

/*--- function NextWindow ----------------------------------------------
 *
 *  Makes another window the active window with input focus.
 *  This function would typically be used to implement a keystroke
 *  (usually F6) that switches windows.
 *
 *    Entry    Doc   points to the current document.
 */
void
NextWindow (Doc)
     TypDoc *Doc;
{
  int idoc;
  int trydoctype;
  TypDoc *NewDoc;

  /* First, locate the current window in our data structures.       */

  switch (Doc->DocType)
    {
    case DOCTYPE_NET:
      idoc = 0;
      trydoctype = DOCTYPE_GROUP;
      break;
    case DOCTYPE_GROUP:
      for (idoc = 0; idoc < MAXGROUPWNDS && Doc != &(GroupDocs[idoc]); idoc++);
      if (idoc >= MAXGROUPWNDS)
	{
	  MessageBox (Doc->hDocWnd, "Error finding next window", "System error", MB_ICONHAND | MB_OK);
	}
      trydoctype = DOCTYPE_GROUP;
      idoc++;
      break;
    case DOCTYPE_ARTICLE:
      for (idoc = 0; idoc < MAXARTICLEWNDS && Doc != &(ArticleDocs[idoc]); idoc++);
      if (idoc >= MAXARTICLEWNDS)
	{
	  MessageBox (Doc->hDocWnd, "Error finding next window", "System error", MB_ICONHAND | MB_OK);
	}
      trydoctype = DOCTYPE_ARTICLE;
      idoc++;
      break;
    }

  /* Now, find the next window in the sequence.                     */

  if (trydoctype == DOCTYPE_GROUP)
    {
      for (; idoc < MAXGROUPWNDS; idoc++)
	{
	  if (GroupDocs[idoc].InUse)
	    {
	      NewDoc = ActiveGroupDoc = &(GroupDocs[idoc]);
	      goto foundit;
	    }
	}
      idoc = 0;
    }

  /* Not found yet--try to find an Article doc.                     */

  for (; idoc < MAXARTICLEWNDS; idoc++)
    {
      if (ArticleDocs[idoc].InUse)
	{
	  NewDoc = ActiveArticleDoc = &(ArticleDocs[idoc]);
	  goto foundit;
	}
    }

  /* Still not found--just make the Net document the next one.      */

  NewDoc = &NetDoc;

foundit:;

  SetActiveWindow (NewDoc->hDocWnd);
  SetFocus (NewDoc->hDocWnd);

}

/*--- function AdjustScTop -----------------------------------
 *
 *  Adjust the top line of the screen so that a given line is sure
 *  to appear on the screen.
 *  Don't do anything if the document is one screen's length or smaller.
 *
 *    Entry
 *             BlockPtr and LinePtr  point to the line we want to
 *                      make sure is visible.
 *
 *    Exit     Doc's TopSc fields have been set to ensure that
 *               the line will appear on the screen.
 *             The line has been unlocked.
 */

void
AdjustTopSc (BlockPtr, LinePtr)
     TypBlock far *BlockPtr;
     TypLine far *LinePtr;
{
  unsigned int lineord;
  TypDoc *Doc;
  HANDLE hBlock;
  unsigned int Offset;
  TypLineID MyLineID;

  Doc = BlockPtr->OwnerDoc;
  if (Doc->TotalLines > Doc->ScYLines)
    {
      lineord = WhatLine (BlockPtr, LinePtr);

      while (lineord > Doc->TotalLines - Doc->ScYLines)
	{
	  PrevLine (&BlockPtr, &LinePtr);
	  lineord--;
	}
      Doc->TopLineOrd = lineord;
      UnlockLine (BlockPtr, LinePtr, &(Doc->hCurTopScBlock),
		  &(Doc->TopScOffset), &(Doc->TopScLineID));
    }
  else
    {
      UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
    }
}

/*--- function ScreenToTop -------------------------------------
 *
 *  Sets a document so that the screen is scrolled to the top.
 *
 *  Entry   Doc     points to the document.
 *
 *  Exit 	The document has been set to display starting at
 * 			  the first line.
 */

void
ScreenToTop (TypDoc * Doc)
{
  Doc->hCurTopScBlock = Doc->hFirstBlock;
  Doc->TopScOffset = sizeof (TypBlock);
  Doc->TopScLineID = 0;
  Doc->TopLineOrd = 0;

}

/*-- Last Line of WVSCREEN.C ----------------- */
