/*
 * $Id: wvscreen.c 1.24 1997/02/14 20:12:31 dumoulin Exp $
 */

/*-- First Line of WVSCREEN.C -- Contains Screen-related functions.  */
#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop

/*--- 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 (long nLines, long LinesOnScreen, TypBlock far **BlockPtr, TypLine far **LinePtr, long *LinesAdvanced)
{
  TypBlock far *MyBlock = *BlockPtr;
  TypLine far *MyLine = *LinePtr;
  long LinesGone, 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;) {
	if (!NextLine (&MyBlock, &MyLine))
	  break;
	/* Count only active lines. */
	if (MyLine->active)
	  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);
	if ((*LinePtr)->active)
	  nLines--;
  }
}

/*--- 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 (long nLines, TypBlock far **BlockPtr, TypLine far **LinePtr, long *LinesBackedUp)
{
  *LinesBackedUp = 0;

  while (nLines && PrevLine (BlockPtr, LinePtr)) {
	if ((*LinePtr)->active) {
	  nLines--;
	  (*LinesBackedUp)++;
	}
  }
}

/*--- function NewScrollIt ----------------------------------------------
 *
 *  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.
 *
 *   except in WIN32 ...
 *
 *          wParam   LOWORD: SB_code, HIWORD: position.
 */
void
NewScrollIt (TypDoc *Document, WPARAM wParam, LPARAM lParam)
{
  long LinesGone;
  unsigned int LineOrd;
  RECT Rect;      
  int window_lineheight, window_topspace;

#ifdef _WIN32  //MDS proportional scroll
  SCROLLINFO scrollInfo;
  memset(&scrollInfo, 0, sizeof(SCROLLINFO));
  scrollInfo.cbSize = sizeof(SCROLLINFO);
  scrollInfo.fMask = SIF_ALL;
  
  GetScrollInfo(Document->hDocWnd, SB_VERT, &scrollInfo);  
#endif


  if (Document->DocType == DOCTYPE_ARTICLE) {
	window_lineheight = ArtLineHeight;
	window_topspace = ArtTopSpace;
  }
  else {
	window_lineheight = LineHeight;
	window_topspace = TopSpace;
  }

  switch (LOWORD (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, (int) (LinesGone * window_lineheight), NULL, NULL);

	  Rect.top = window_topspace;
	  Rect.bottom = (int) (window_topspace + LinesGone * window_lineheight);

	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);

	  /* Make sure garbage at bottom is erased in WM_PAINT processing */
	  Rect.top = window_topspace + Document->ScYLines * window_lineheight;
	  Rect.bottom = Document->ScYHeight;
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
	break;

  case SB_LINEDOWN:
	/* Should change TotalLines below to ActiveLines for 
	 * suppression of already-read articles, but it's not that simple.
	 *  /mrr
	 */
	if (Document->TopLineOrd < (Document->ActiveLines - Document->ScYLines)) {
	  LinesGone = 1;
	}
	else
	  LinesGone = 0;
	/*
	 * If ActiveLines happens to be less than ScYLines, LinesGone will now
	 * be one, and we will GPF for running off the end of the data.  Check
	 * and fix this condition. -gkt
	 */
	if ((LinesGone) && (Document->ActiveLines < Document->ScYLines))
	  LinesGone = 0;

  doscrolldown:;
	if (LinesGone) {
	  Document->TopLineOrd += LinesGone;
	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
	  ScrollWindow (Document->hDocWnd, 0, (int) (-LinesGone * window_lineheight), NULL, NULL);
	  Rect.top = (int) (window_topspace + (Document->ScYLines - LinesGone) * window_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 = window_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->ActiveLines > Document->ScYLines) {	/* do we even need to scroll? */
	  if ((Document->ActiveLines -
		   (Document->TopLineOrd + Document->ScYLines)) > Document->ScYLines)
		LinesGone = Document->ScYLines - 1;
	  else
		LinesGone = Document->ActiveLines - (Document->TopLineOrd + Document->ScYLines);

	  /*
	   * If ActiveLines happens to be less than TopLineOrd + ScYLines,
	   * Adding LinesGone to TopLineOrd would take us beyond the end
	   * of data and would cause a GPF.  Correct this condition by
	   * making TopLineOrd either 1 (if ActiveLines < ScYLines )
	   * or ActiveLines - ScYLines (the last "page" of data).  -gkt
	   */
	  if (Document->TopLineOrd + Document->ScYLines > Document->ActiveLines) {
		if (Document->ActiveLines > Document->ScYLines)
		  Document->TopLineOrd = Document->ActiveLines - Document->ScYLines;
		else
		  Document->TopLineOrd = 1;
	  }
	  else {
		Document->TopLineOrd += LinesGone;
	  }

	  InvalidateRect (Document->hDocWnd, NULL, FALSE);
	}
	break;

  case SB_THUMBPOSITION:
#ifdef _WIN32 //MDS proportional scroll
  LineOrd = scrollInfo.nTrackPos;//(int) HIWORD (wParam);
    scrollInfo.nPos = LineOrd;
    scrollInfo.fMask = SIF_POS;
    SetScrollInfo(Document->hDocWnd, SB_VERT, &scrollInfo, TRUE);
#else
  LineOrd = LOWORD (lParam);
    SetScrollPos (Document->hDocWnd, SB_VERT, LineOrd, TRUE);
#endif

  //Document->ThumbTracking = FALSE;
  ClearFlags(Document, ThumbTracking);
  doposition:;

#ifdef _WIN32 //MDS proportional scroll
  Document->TopLineOrd = LineOrd;
#else
    Document->TopLineOrd = ScrollToOrd(Document->ActiveLines, LineOrd);
#endif
  InvalidateRect (Document->hDocWnd, NULL, FALSE);
  break;


  case SB_THUMBTRACK:
#ifdef _WIN32
  	LineOrd = scrollInfo.nTrackPos; //(int) HIWORD (wParam);
    	LinesGone = LineOrd - Document->TopLineOrd;

#else
  	LineOrd = LOWORD (lParam);
    	LinesGone = ScrollToOrd(Document->ActiveLines,LineOrd) - Document->TopLineOrd;
#endif

  	SetFlags(Document, ThumbTracking);
	//Document->ThumbTracking = TRUE;
  	if (LinesGone > 0) {
    		if (LinesGone >= (long) (Document->ScYLines - 1)) {
    			goto doposition;
    		} else {
    			goto doscrolldown;
    		}
  	} else if (LinesGone < 0) {
    		LinesGone = -LinesGone;
    		if (LinesGone >= (long) (Document->ScYLines - 1)) {
    			goto doposition;
    		} else {
    			goto doscrollup;
    		}
  	}

  	break;


  default:
	return;
	break;
  }
}  

/*--- 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 (TypDoc *Document, WPARAM wParam, LPARAM lParam)
{
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;
  long LinesGone;
  unsigned int LineOrd;
  RECT Rect;
  int window_lineheight, window_topspace;

#ifdef _WIN32
  SCROLLINFO scrollInfo;  //MDS proportional scroll

  memset(&scrollInfo, 0, sizeof(SCROLLINFO));
  scrollInfo.cbSize = sizeof(SCROLLINFO);
  scrollInfo.fMask = SIF_ALL;
  
  GetScrollInfo(Document->hDocWnd, SB_VERT, &scrollInfo);  
#endif

  if (Document->DocType == DOCTYPE_ARTICLE) {
	window_lineheight = ArtLineHeight;
	window_topspace = ArtTopSpace;
  }
  else {
	window_lineheight = LineHeight;
	window_topspace = TopSpace;
  }

  switch (LOWORD (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);
	AdvanceToActive (&BlockPtr, &LinePtr);
	ScreenUp (1, &BlockPtr, &LinePtr, &LinesGone);
  doscrollup:;
	if (LinesGone) {
	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
	  Document->TopLineOrd -= LinesGone;
	  ScrollWindow (Document->hDocWnd, 0, (int) (LinesGone * (window_lineheight)), NULL, NULL);

	  Rect.top = window_topspace;
	  Rect.bottom = (int) (window_topspace + LinesGone * window_lineheight);

	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	  /* Make sure garbage at bottom is erased in WM_PAINT processing */
	  Rect.top = window_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);
	AdvanceToActive (&BlockPtr, &LinePtr);
	ScreenDown (1, Document->ScYLines, &BlockPtr, &LinePtr, &LinesGone);
  doscrolldown:;
	if (LinesGone) {
	  Document->TopLineOrd += LinesGone;
	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
	  ScrollWindow (Document->hDocWnd, 0, (int) (-LinesGone * (window_lineheight)), NULL, NULL);
	  Rect.top = (int) (window_topspace + (Document->ScYLines - LinesGone) * window_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 = window_topspace;
	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
	break;

  case SB_PAGEUP:
	LockLine (Document->hCurTopScBlock, Document->TopScOffset, Document->TopScLineID, &BlockPtr, &LinePtr);
	AdvanceToActive (&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);
	AdvanceToActive (&BlockPtr, &LinePtr);     // shimomai
	if (Document->NextTopLine > 0 && Document->NextTopLine < Document->TotalLines) {
	  LinesGone = Document->NextTopLine - Document->TopLineOrd;
	  while (LinesGone--) 
	  	NextLine (&BlockPtr, &LinePtr);
	  Document->TopLineOrd = Document->NextTopLine;
	}
	else {
		ScreenDown (Document->ScYLines - 1, Document->ScYLines,
				&BlockPtr, &LinePtr, &LinesGone);
		Document->TopLineOrd += LinesGone;
	}
	InvalidateRect (Document->hDocWnd, NULL, FALSE);
	break;
  case SB_TOP:
    LineOrd = 0;
    if (!FindLineOrd (Document, ScrollToOrd(Document->ActiveLines,LineOrd), &BlockPtr, &LinePtr)) {
	    return;
    }
      Document->TopLineOrd = ScrollToOrd(Document->ActiveLines,LineOrd);
    InvalidateRect (Document->hDocWnd, NULL, FALSE);
	break;

  case SB_BOTTOM:
    LineOrd = Document->TotalLines - Document->ScYLines;
    if (!FindLineOrd (Document, ScrollToOrd(Document->ActiveLines,LineOrd), &BlockPtr, &LinePtr)) {
	    return;
    }
      Document->TopLineOrd = ScrollToOrd(Document->ActiveLines,LineOrd);
    InvalidateRect (Document->hDocWnd, NULL, FALSE);
	break;


  case SB_THUMBPOSITION:
#ifdef _WIN32 //MDS proportional scroll
    LineOrd = scrollInfo. nTrackPos; //(int) HIWORD (wParam);
    if (!FindLineOrd (Document, LineOrd, &BlockPtr, &LinePtr)) {
    return;
  }
    scrollInfo.nPos = LineOrd;
    scrollInfo.fMask = SIF_POS;
    SetScrollInfo(Document->hDocWnd, SB_VERT, &scrollInfo, TRUE);

#else
  LineOrd = LOWORD (lParam);
    if (!FindLineOrd (Document, ScrollToOrd(Document->ActiveLines,LineOrd), &BlockPtr, &LinePtr)) {
    return;
  }
    SetScrollPos (Document->hDocWnd, SB_VERT, LineOrd, TRUE);
#endif


  //Document->ThumbTracking = FALSE;
  ClearFlags(Document, ThumbTracking);
  doposition:;
//  Document->TopLineOrd = LineOrd;

#ifdef _WIN32 //MDS proportional scroll
    Document->TopLineOrd = LineOrd;
#else
    Document->TopLineOrd = ScrollToOrd(Document->ActiveLines,LineOrd);
#endif
  InvalidateRect (Document->hDocWnd, NULL, FALSE);
  break;

  case SB_THUMBTRACK:

#ifdef _WIN32
  LineOrd = scrollInfo. nTrackPos; //(int) HIWORD (wParam);
  if (!FindLineOrd (Document, LineOrd, &BlockPtr, &LinePtr)) {
    return;
  }
    LinesGone = LineOrd - Document->TopLineOrd;     
#else
  LineOrd = LOWORD (lParam);
  if (!FindLineOrd (Document, ScrollToOrd(Document->ActiveLines,LineOrd), &BlockPtr, &LinePtr)) {
    return;
  }     
  LinesGone = ScrollToOrd(Document->ActiveLines,LineOrd) - Document->TopLineOrd;
#endif


  SetFlags(Document, ThumbTracking);
  //Document->ThumbTracking = TRUE;
  
  if (LinesGone > 0) {
    if (LinesGone >= (int) (Document->ScYLines - 1)) {
    goto doposition;
    }
    else {
    goto doscrolldown;
    }
  }
  else if (LinesGone < 0) {
    LinesGone = -LinesGone;
    if (LinesGone >= (int) (Document->ScYLines - 1)) {
    goto doposition;
    }
    else {
    goto doscrollup;
    }
  }

  break;

  default:
	return;
	break;
  }
  UnlockLine (BlockPtr, LinePtr, &(Document->hCurTopScBlock),
			  &(Document->TopScOffset), &(Document->TopScLineID));

}



/*--- function HScrollIt ----------------------------------------------
 *
 *  Perform a horizontal scrolling action.
 *
 *    Entry    Document points to a document
 *             wParam   is the wParam argument given from a WM_HSCROLL
 *                      window message.  (One of the SB_ symbols.)
 *             lParam   is the lParam argument (scroll box position).
 */
void
HScrollIt (TypDoc *Document, WPARAM wParam, LPARAM lParam)
{
#ifdef _WIN32  //MDS proportional scroll

  SCROLLINFO scrollInfo;
  memset(&scrollInfo, 0, sizeof(SCROLLINFO));
  scrollInfo.cbSize = sizeof(SCROLLINFO);
  scrollInfo.fMask = SIF_ALL;
  
  GetScrollInfo(Document->hDocWnd, SB_HORZ, &scrollInfo);  
#endif

  switch (LOWORD (wParam)) {

  case SB_LINEUP:
	if (Document->ScXOffset > 0) {
	  RECT Rect;

	  Document->ScXOffset--;
	  Rect.top = 0;
	  Rect.bottom = Document->ScYHeight;

	  if (Document->DocType == DOCTYPE_ARTICLE) {
		Rect.left = -((int) ArtCharWidth + 1);
	  }
	  else {
		Rect.left = -((int) CharWidth + 1);
	  }
	  Rect.right = Document->ScXWidth - Rect.left;
	  ScrollWindow (Document->hDocWnd, -Rect.left, 0, &Rect, NULL);
//shimomai	  Rect.right = -Rect.left;
	  Rect.right = -Rect.left + 1;

	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
	break;

  case SB_LINEDOWN:
	if (Document->LongestLine > Document->ScXChars &&
		Document->ScXOffset < Document->LongestLine - Document->ScXChars) {
	  RECT Rect;

	  Document->ScXOffset++;
	  Rect.top = 0;
	  Rect.bottom = Document->ScYHeight;

	  if (Document->DocType == DOCTYPE_ARTICLE) {
		Rect.left = ArtCharWidth + 1;
	  }
	  else {
		Rect.left = CharWidth + 1;
	  }
	  Rect.right = Document->ScXWidth + Rect.left;
	  ScrollWindow (Document->hDocWnd, -Rect.left, 0, &Rect, NULL);
//shiomai	  Rect.right = Rect.left;
//	  Rect.left = 0;
	  Rect.right = Document->ScXWidth;
	  Rect.left = Rect.right - Rect.left -1;

	  InvalidateRect (Document->hDocWnd, &Rect, FALSE);
	}
	break;

  case SB_PAGEUP:
#ifdef _WIN32  //MDS proportional scroll
  if (Document->ScXOffset > (unsigned int)scrollInfo.nMin)
    {
        if (scrollInfo.nPage > Document->ScXOffset)
            Document->ScXOffset =  scrollInfo.nMin;
        else
            Document->ScXOffset -= (unsigned int)scrollInfo.nPage;
    }
    if (Document->ScXOffset < (unsigned int)scrollInfo.nMin)
        Document->ScXOffset = (unsigned int)scrollInfo.nMin;
#else
  if (((int) Document->ScXOffset - (int) Document->ScXChars) > 0) {
    Document->ScXOffset = Document->ScXOffset -
    Document->ScXChars + 5;
  }
  else {
    Document->ScXOffset = 0;
  }
#endif

  InvalidateRect (Document->hDocWnd, NULL, FALSE);
  break;

  case SB_PAGEDOWN:
#ifdef _WIN32
    if (Document->ScXOffset < (unsigned int)scrollInfo.nMax) {
        Document->ScXOffset += (unsigned int)scrollInfo.nPage;
    }
    if ((Document->ScXOffset + Document->ScXChars) > (unsigned int)scrollInfo.nMax  )
        Document->ScXOffset = (unsigned int)scrollInfo.nMax - Document->ScXChars;
#else
  if ((Document->ScXOffset + Document->ScXChars) <
    (Document->LongestLine - Document->ScXChars)) {
    Document->ScXOffset = Document->ScXOffset +
    Document->ScXChars - 5;
  }
  else {
    Document->ScXOffset = (int) (Document->LongestLine - Document->ScXChars);
  }

#endif

  InvalidateRect (Document->hDocWnd, NULL, FALSE);
  break;


  case SB_THUMBPOSITION:
	ClearFlags(Document, ThumbTracking);
  	//Document->ThumbTracking = FALSE;
#ifdef _WIN32  //MDS proportional scroll
  	Document->ScXOffset =  scrollInfo.nPos; //(int) HIWORD (wParam);
#else
  	Document->ScXOffset = LOWORD (lParam);
#endif

  	InvalidateRect (Document->hDocWnd, NULL, FALSE);
  	break;

	
  case SB_THUMBTRACK:
#ifdef _WIN32 //MDS proportional scroll
  	Document->ScXOffset = scrollInfo.nTrackPos; //(int) HIWORD (wParam);
#else
  	Document->ScXOffset = LOWORD (lParam);
#endif
    	SetFlags(Document, ThumbTracking);
		//Document->ThumbTracking = TRUE;
    	InvalidateRect (Document->hDocWnd, NULL, FALSE);
  	break;


  default:
	return;
	break;

  }
}




/*--- 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 (TypDoc *Doc, HANDLE hTargBlock, unsigned int TargOffset, TypLineID TargLineID)
{
  TypBlock far *CurBlockPtr, far * TargBlockPtr;
  TypLine far *CurLinePtr, far * TargLinePtr;
  int iline;
  BOOL found = FALSE, gotnext = TRUE;
  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; gotnext && !found && (unsigned) iline < Doc->ScYLines;) 
  {
	found = (TargLinePtr == CurLinePtr);
	if (CurLinePtr->active)
	  iline++;
	if (!found) {
	  gotnext = 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 (HWND hCurWnd, int docType)
{
  register int i;
  int tryWndType;
  HWND hNewWnd = NULL;
  BOOL foundNext = FALSE;

  /* First, locate the current window in our data structures.       */
  switch (docType) {
  case DOCTYPE_GROUP:
	for (i = 0; i < MAXGROUPWNDS && hCurWnd != GroupDocs[i].hWndFrame; i++);
	if (i >= MAXGROUPWNDS) {
	  WVMessageBox (hCurWnd, "Error finding next window", "System error", MB_ICONHAND | MB_OK);
	}
	tryWndType = DOCTYPE_GROUP;
	i++;
	break;

  case DOCTYPE_ARTICLE:
	for (i = 0; i < MAXARTICLEWNDS && hCurWnd != ArticleDocs[i].hWndFrame; i++);
	if (i >= MAXARTICLEWNDS) {
	  WVMessageBox (hCurWnd, "Error finding next window", "System error", MB_ICONHAND | MB_OK);
	}
	tryWndType = DOCTYPE_ARTICLE;
	i++;
	break;

  case DOCTYPE_MAIL:
  case DOCTYPE_FORWARD:
	for (i = 0; i < MAXMAILWNDS && hCurWnd != WndMails[i].hWnd; i++);
	if (i >= MAXMAILWNDS) {
	  WVMessageBox (hCurWnd, "Error finding next window", "System error", MB_ICONHAND | MB_OK);
	}
	tryWndType = DOCTYPE_MAIL;
	i++;
	break;

  case DOCTYPE_POSTING:
  case DOCTYPE_SUPERSEDE:
	for (i = 0; i < MAXPOSTWNDS && hCurWnd != WndPosts[i].hWnd; i++);
	if (i >= MAXPOSTWNDS) {
	  WVMessageBox (hCurWnd, "Error finding next window", "System error", MB_ICONHAND | MB_OK);
	}
	tryWndType = DOCTYPE_POSTING;
	i++;
	break;

  case DOCTYPE_NET:
  default:
	i = 0;
	tryWndType = DOCTYPE_GROUP;
	break;
  }

  /* Now, find the next window in the sequence.                     */
  if (!foundNext && tryWndType == DOCTYPE_GROUP) {
	for (; i < MAXGROUPWNDS && !foundNext; i++) {
	  if (GroupDocs[i].InUse) {
		ActiveGroupDoc = &(GroupDocs[i]);
		hNewWnd = ActiveGroupDoc->hWndFrame;
		foundNext = TRUE;
	  }
	}
	i = 0;
	tryWndType = DOCTYPE_ARTICLE;
  }

  /* Not found yet--try to find an Article doc.                     */
  if (!foundNext && tryWndType == DOCTYPE_ARTICLE) {
	for (; i < MAXARTICLEWNDS && !foundNext; i++) {
	  if (ArticleDocs[i].InUse) {
		ActiveArticleDoc = &(ArticleDocs[i]);
		hNewWnd = ActiveArticleDoc->hWndFrame;
		foundNext = TRUE;
	  }
	}
	i = 0;
	tryWndType = DOCTYPE_MAIL;
  }

  if (!foundNext && tryWndType == DOCTYPE_MAIL) {
	for (; i < MAXMAILWNDS && !foundNext; i++) {
	  if (WndMails[i].hWnd) {
		hNewWnd = WndMails[i].hWnd;
		foundNext = TRUE;
	  }
	}
	i = 0;
	tryWndType = DOCTYPE_POSTING;
  }

  if (!foundNext && tryWndType == DOCTYPE_POSTING) {
	for (; i < MAXPOSTWNDS && !foundNext; i++) {
	  if (WndPosts[i].hWnd) {
		hNewWnd = WndPosts[i].hWnd;
		foundNext = TRUE;
	  }
	}
  }

  /* Still not found--just make the Net document the next one.      */
  if (!foundNext) {
	hNewWnd = NetDoc.hWndFrame;
  }

  SetActiveWindow (hNewWnd);
  SetFocus (hNewWnd);
}

/*--- 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 (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)
	//if (Doc->ActiveLines > Doc->ScYLines)  //??
  {
	lineord = WhatLine (BlockPtr, LinePtr);

	while (lineord > Doc->TotalLines - Doc->ScYLines)
	  //while (lineord > Doc->ActiveLines - Doc->ScYLines)  //??
	{
	  PrevLine (&BlockPtr, &LinePtr);
	  lineord--;
	}
	Doc->TopLineOrd = lineord;
	ClearFlags(Doc, ThumbTracking);
	//Doc->ThumbTracking = FALSE;	/* if thumb tracking, release it */

	UnlockLine (BlockPtr, LinePtr, &(Doc->hCurTopScBlock),
				&(Doc->TopScOffset), &(Doc->TopScLineID));
  }
  else 
  {
	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  }
}

void AdjustMidSc (TypBlock far * BlockPtr, TypLine far * LinePtr)
{
  unsigned int lineord;
  TypDoc *Doc;
  HANDLE hBlock;
  unsigned int Offset;
  TypLineID MyLineID;
  unsigned int i;

  Doc = BlockPtr->OwnerDoc;

  if (Doc->ActiveLines > Doc->ScYLines) 
  {
	lineord = WhatLine (BlockPtr, LinePtr);

	if (lineord < Doc->TopLineOrd || lineord >= (Doc->TopLineOrd + Doc->ScYLines - 1)) 
	{
	  if (lineord > Doc->ActiveLines - Doc->ScYLines) 
	  {
		while (lineord > (Doc->ActiveLines - Doc->ScYLines) + 1)	//??
		 {
		  PrevActiveLine (&BlockPtr, &LinePtr);
		  lineord--;
		}
	  }
	  else 
	  {
		if (lineord >= Doc->ScYLines) 
		{
		  for (i = 0; i < Doc->ScYLines / 2; i++) 
		  {
			PrevActiveLine (&BlockPtr, &LinePtr);
			lineord--;
		  }
		}
		else 
		{
		  while (lineord) 
		  {
			PrevActiveLine (&BlockPtr, &LinePtr);
			lineord--;
		  }
		}
	  }
	  Doc->TopLineOrd = lineord;
	  ClearFlags(Doc, ThumbTracking);
	  //Doc->ThumbTracking = FALSE;	/* if thumb tracking, release it */
	  UnlockLine (BlockPtr, LinePtr, &(Doc->hCurTopScBlock),
				  &(Doc->TopScOffset), &(Doc->TopScLineID));
	}
  }
  else 
  {
	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  }
  InvalidateRect (Doc->hDocWnd, NULL, FALSE);
  UpdateWindow (Doc->hDocWnd);
}

/*
 * This is a Doc/line-index version of AdjustScTop
 * Don't you wish we could overload the function?!
 * JSC 8/13/94
 */
void AdjustTopScByDoc (TypDoc * aDoc, TypLineID index)
{
  if (aDoc->TotalLines < aDoc->ScYLines ||
	  index >= (TypLineID) aDoc->TopLineOrd && index < (TypLineID) (aDoc->TopLineOrd + aDoc->ScYLines))
	return;						// already on screen

  if (index <= (TypLineID) aDoc->ScYLines / 2)
	aDoc->TopLineOrd = 0;
  else
  {
	aDoc->TopLineOrd = (unsigned int) (index - (TypLineID) aDoc->ScYLines / 2);		// center it
    if ((TypLineID) (aDoc->TopLineOrd + aDoc->ScYLines) > aDoc->TotalLines)
		aDoc->TopLineOrd = aDoc->TotalLines - aDoc->ScYLines;  	
  }

  ClearFlags(aDoc, ThumbTracking);
  //aDoc->ThumbTracking = FALSE;	/* if thumb tracking, release it */
  InvalidateRect (aDoc->hDocWnd, NULL, FALSE);
}

/*--- 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)
{
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;

  Doc->hCurTopScBlock = Doc->hFirstBlock;
  Doc->TopScOffset = sizeof (TypBlock);
  Doc->TopScLineID = 0;
  Doc->TopLineOrd = 0;
  //Doc->ThumbTracking = FALSE;	/* if thumb tracking, release it */
  ClearFlags(Doc, ThumbTracking);

  LockLine (Doc->hFirstBlock, Doc->TopScOffset, Doc->TopScLineID, &BlockPtr, &LinePtr);
  AdvanceToActive (&BlockPtr, &LinePtr);
  UnlockLine (BlockPtr, LinePtr, &(Doc->hFirstBlock), &(Doc->TopScOffset), &(Doc->TopScLineID));
}

/*--- function AdvanceToActive ---------------------------------------
 *
 *  Advances a block/line pointer pair to an active line.
 *  The pointers are unchanged if the current line is active.
 *
 *  Entry:  BlockPtr, LinePtr points to a line.
 *
 *  Exit:   BlockPtr,LinePtr  points to an active line, if possible.
 *          Returns TRUE if successful, else FALSE if no line from
 *            this point to the end of the document was active.
 */
BOOL AdvanceToActive (TypBlock far **BlockPtr, TypLine far **LinePtr)
{
  BOOL ok = TRUE;

  while (!((*LinePtr)->active)) 
  {
	if (!NextLine (BlockPtr, LinePtr)) 
	{
	  ok = FALSE;
	  break;
	}
  }

  return ok;
}


/*--- function SetHandleBkBrush ---------------------------------------
 *
 *  Sets a new handle of a background brush.
 *  This uses SetClassWord in a portable way and makes code look tidier.
 *
 *  Entry:  hwnd  Identifies window whose brush is to be changed.
 *       handle   Handle to the new brush.
 *
 */
HBRUSH SetHandleBkBrush (HWND hwnd, HBRUSH handle)
{

  return (HBRUSH) SetClassLongPtr (hwnd, GCLP_HBRBACKGROUND, (LONG_PTR) handle);

}

/*--- Function ScrollToOrd ---------------------------------------
 *
 *  Converts the position on the Vertical or Horizontal Scroll Bar from a 
 *  relative screen position into a line location of the newsgroup in the 
 *  window, the article in the group window or the line of an article.  This 
 *  conversion is necessary because Windows scroll bars can't exceed 32K 
 *  elements but the number of newsgroups, article numbers or lines in a 
 *  message can. Hopefully, the Horizontal Scroll bar doesn't need this but
 *  we use it anyway to be safe. (JD 1/9/97) */ 

unsigned long ScrollToOrd (unsigned long MaxLinesOrChars, int ScrollPos) 
{
	if (MaxLinesOrChars <= 10000) 
	{ 
    	return((unsigned long) ScrollPos);
    }
    else 
	{
        return (min (MaxLinesOrChars, (unsigned long) (MaxLinesOrChars * (float) ScrollPos/10000)));
    }
 }   
 
/*--- Function OrdToScroll ---------------------------------------
 *
 *  Converts the active newsgroup index in the newsgroup list, article index
 *  in the Group window or line index in the article window into its relative
 *  position on the Vertical or Horizontal Scroll Bar.  This conversion is necessary
 *  because Windows scroll bars can't exceed 32K elements but the number of
 *  newsgroups, article numbers or lines in a message can. (JD 1/9/97) */

int	OrdToScroll (unsigned long MaxLinesOrChars, unsigned long LineOrd) 
{
	if (MaxLinesOrChars <= 10000) 
	{ 
    	return((int) LineOrd);
    }
    else 
	{
        return (min (9999, (int) (LineOrd * (float) 10000/MaxLinesOrChars)));
    }
}

/*-- Last Line of WVSCREEN.C ----------------- */
