head	1.76;
access;
symbols
	V0-99-2:1.72
	V0-99-1:1.71
	V0-93-14:1.70
	V0-93-13:1.70
	V0-93-12:1.69
	V0-93-11:1.66
	V0-93-10:1.63
	V0-93-9:1.62
	V0-93-8:1.61
	V0-93-7:1.58
	V0-93-5:1.57
	test:1.51
	V80:1.11
	V76d:1.2;
locks; strict;
comment	@ * @;


1.76
date	97.02.06.17.29.17;	author dumoulin;	state Exp;
branches;
next	1.75;

1.75
date	95.11.07.22.29.12;	author dumoulin;	state Exp;
branches;
next	1.74;

1.74
date	95.04.12.20.54.46;	author dumoulin;	state Exp;
branches;
next	1.73;

1.73
date	95.03.29.01.53.28;	author dumoulin;	state Exp;
branches;
next	1.72;

1.72
date	95.03.29.00.33.26;	author dumoulin;	state Exp;
branches;
next	1.71;

1.71
date	95.03.16.17.12.10;	author dumoulin;	state Exp;
branches;
next	1.70;

1.70
date	95.02.07.23.02.16;	author jcooper;	state Exp;
branches;
next	1.69;

1.69
date	95.01.25.00.27.37;	author dumoulin;	state Exp;
branches;
next	1.68;

1.68
date	95.01.19.02.47.25;	author jglasser;	state Exp;
branches;
next	1.67;

1.67
date	95.01.03.19.00.30;	author jcooper;	state Exp;
branches;
next	1.66;

1.66
date	94.12.19.17.08.24;	author rushing;	state Exp;
branches;
next	1.65;

1.65
date	94.12.19.15.02.10;	author rushing;	state Exp;
branches;
next	1.64;

1.64
date	94.12.19.15.01.26;	author rushing;	state Exp;
branches;
next	1.63;

1.63
date	94.12.13.12.57.41;	author jcooper;	state Exp;
branches;
next	1.62;

1.62
date	94.11.30.22.22.08;	author jcooper;	state Exp;
branches;
next	1.61;

1.61
date	94.11.30.03.04.36;	author jpiraino;	state Exp;
branches;
next	1.60;

1.60
date	94.11.30.02.54.22;	author jpiraino;	state Exp;
branches;
next	1.59;

1.59
date	94.11.28.22.01.06;	author jcooper;	state Exp;
branches;
next	1.58;

1.58
date	94.11.21.21.20.46;	author jcooper;	state Exp;
branches;
next	1.57;

1.57
date	94.11.11.01.39.21;	author jglasser;	state Exp;
branches;
next	1.56;

1.56
date	94.11.10.02.46.17;	author rushing;	state Exp;
branches;
next	;


desc
@winvn version 0.76 placed into RCS
@


1.76
log
@stub for deleted file.  wvusenet.c is now wvusenet.cpp
@
text
@This file (WVUSENET.C) has been superceded by WVUSENET.CPP.  This 
file is being kept around so that the WinVN source control system can 
continue to build older versions of WinVN. 

This text should generate a compile error to any known C/C++ compiler.
@


1.75
log
@formatting change
@
text
@d1 5
@


1.74
log
@Allow PageUP, PageDown, Home and End keys to work regardless
of state of the Multi-Selection option
@
text
@a0 2752
/********************************************************************
 *                                                                  *
 *  MODULE    :  WVUSENET.C                                         *
 *                                                                  *
 *  PURPOSE   : This file contains the window procedure and support *
 *              routines for WinVn's main window                    *
 *                                                                  *
 ********************************************************************/

/*
 * $Id: wvusenet.c 1.73 1995/03/29 01:53:28 dumoulin Exp $
 *
 */
#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <stdlib.h>
#include <assert.h>
#include "wvtb\wvtb.h"			/* for toolbar  */
#ifdef USE_3D_CONTROLS
#include "ctl3d.h"
#endif

/* This is for the special case where the user invoked the */
/* program with dolist=0, then turned it on.  Without checking */
/* this condition, the newsrc will be truncated to only those */
/* groups read.  SMR 930224 */

int started_with_no_dolist;
void show_version_strings (HWND);
void SetGroupActiveLines (void);
BOOL WarnColors (COLORREF c1, COLORREF c2, char *warnMsg);
BOOL SelectGroup (TypDoc far * Doc, TypGroup far * group, BOOL value);

static char groupbuf[MAXHEADERLINE];

/*--- FUNCTION: WinVnConfWndProc ---------------------------------------
 *
 *    Window procedure for the "Net" window.
 *    This window displays the names of the newsgroups, one per line.
 *
 *    Entry    The usual parameters for any window function.
 *
 *    Note:    We don't do anything until "Initialized" is TRUE.
 *             This way, the user won't try to perform functions
 *             while the communications are still being set up.
 */

long WINAPI
WinVnConfWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 unsigned message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  PAINTSTRUCT ps;				/* paint structure          */
//  POINT ptCursor;

  HDC hDC;						/* handle to display context */
  HWND hWndView;
  RECT myRect;					/* selection rectangle                */

  int j;
  unsigned int i;
  int found;
  char far *lpsz;
  TypLine far *LinePtr, far * GroupLinePtr;
  TypBlock far *BlockPtr, far * GroupBlockPtr;
  HANDLE hBlock;
  TypDoc *MyDoc;
  unsigned int Offset;
  BOOL looping = TRUE;
  TypGroup far *MyGroup;

  int X, Y;
  int docnum;
  int newdoc;
  char mybuf[MAXINTERNALLINE];
  int OldSel = FALSE;
  int CtrlState;
  TypLineID MyLineID;

  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
    return(0);

  switch (message) {

/*
   case WM_DESTROY:
   SetHandleBkBrush (hWnd, GetStockObject (WHITE_BRUSH));
   break;
 */

  case WM_CREATE:

	MailInit (hWnd);

	/* Get handle to the hourglass cursor */

	hHourGlass = LoadCursor (NULL, IDC_WAIT);


	/* Fill in the Net document's window handle that we
	 * were unable to fill in in the call to InitDoc.
	 */
	NetDoc.hDocWnd = hWnd;
	InitBitmaps ();
	break;

  case WM_CHAR:
	/* Hitting CR on a group name is the same as double-clicking
	 * on the name.
	 */
	if (!Initializing) {
	  if (wParam == '\r') {

	//	if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &GroupBlockPtr, &GroupLinePtr))
			goto getgroup1;
		  else
			break;

	//	}
	//	else {
	//	  GetCursorPos (&ptCursor);
	//	  ScreenToClient (hWnd, &ptCursor);
	//	  X = ptCursor.x;
	//	  Y = ptCursor.y;
	//	  goto getgroup;
	//	}
	  }
	}
	break;

  case WM_KEYDOWN:
	/* See if this key should be mapped to a scrolling event
	 * for which we have programmed the mouse.  If so,
	 * construct the appropriate mouse call and call the mouse code.
	 * Also, F6 means switch to next window.
	 * (This latter should also be table-driven.)
	 */
	if (wParam == VK_F6) {
	  NextWindow (NetDoc.hWndFrame, NetDoc.DocType);
	}               				
	else if (!Initializing) {
	  switch (wParam) {
	  case VK_DOWN:
	//	if (GroupListMultiSelect) {
		  /* move one line down from Active line */
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			if (NextActiveLine (&BlockPtr, &LinePtr)) {
			  SetSelections (&NetDoc, LinePtr);
			  AdjustMidSc (BlockPtr, LinePtr);
			}
		  }
	//	}
		break;

	  case VK_UP:
	//	if (GroupListMultiSelect) {
		  /* move one line up from Active line */
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			if (PrevActiveLine (&BlockPtr, &LinePtr)) {
			  SetSelections (&NetDoc, LinePtr);
			  AdjustMidSc (BlockPtr, LinePtr);
			}
		  }
	//	}
		break;

	  case VK_HOME:
	//	if (GroupListMultiSelect) {
		  TopOfDoc (&NetDoc, &BlockPtr, &LinePtr);
		  SetSelections (&NetDoc, LinePtr);
		  AdjustMidSc (BlockPtr, LinePtr);
	//	}
		break;

	  case VK_END:
	//	if (GroupListMultiSelect) {
		  BottomOfDoc (&NetDoc, &BlockPtr, &LinePtr);
		  SetSelections (&NetDoc, LinePtr);
		  AdjustMidSc (BlockPtr, LinePtr);
	//	}
		break;

	  case VK_NEXT:
	//	if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			for (i = 0; i < NetDoc.ScYLines; i++) {
			  if (!NextActiveLine (&BlockPtr, &LinePtr))
				break;
			}
			SetSelections (&NetDoc, LinePtr);
			AdjustMidSc (BlockPtr, LinePtr);
		  }
	//	}
		break;

	  case VK_PRIOR:
	//	if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			for (i = 0; i < NetDoc.ScYLines; i++) {
			  if (!PrevActiveLine (&BlockPtr, &LinePtr))
				break;
			}
			SetSelections (&NetDoc, LinePtr);
			AdjustMidSc (BlockPtr, LinePtr);
		  }
	//	}
		break;

	  default:
		/* Based on the state of the Control key and the value
		 * of the key just pressed, look up in the array
		 * key2scroll to see if we should process this keystroke
		 * as if it were a mouse event.  If so, simulate the
		 * mouse event by sending the appropriate message.
		 */

		CtrlState = GetKeyState (VK_CONTROL) < 0;
		for (j = 0; j < NUMKEYS; j++) {
		  if (wParam == key2scroll[j].wVirtKey &&
			  CtrlState == key2scroll[j].CtlState) {
			SendMessage (hWnd, key2scroll[j].iMessage,
						 key2scroll[j].wRequest, 0L);
			break;
		  }
		}

	  }
	}
	break;

  case WM_LBUTTONDOWN:
	/*  Clicking the left button on a group name toggles the
	 *  selected/not selected status of that group.
	 *  Currently selected groups are displayed in reverse video.
	 */

	DragMouseAction = DRAG_NONE;
	if (!Initializing) {
	  BOOL status;
	  TypGroup *aGroup;

	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  /* make this behave more like a multi-select listbox (jlg) */
	  /* if click,       make the line the only one selected */
	  /* if ctrl-click,  add the line to the selection */
	  /* if shift-click, select from previously selected to current */
	  if (GroupListMultiSelect) {
		if (!(wParam & MK_CONTROL) && !(wParam & MK_SHIFT)) {
		  if (NetDoc.SelectedLines == 1) {
			LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr);
			SelectGroup (&NetDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)), FALSE);
		  }
		  else {
			ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
		  }
		}
	  }
	  if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) {
		aGroup = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
		status = SelectGroup (&NetDoc, aGroup, aGroup->Selected ? FALSE : TRUE);
		if (GroupListMultiSelect) {
		  NetDoc.ActiveLineID = LinePtr->LineID;
		  if (wParam & MK_SHIFT) {
			ForAllLines (&NetDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
		  }
		  else
			NetDoc.AnchorLineID = LinePtr->LineID;

		  if (!(wParam & MK_CONTROL) && !(wParam & MK_SHIFT))
			DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
		  else
			DragMouseAction = DRAG_NONE;
		}
		else
		  DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;

		GlobalUnlock (BlockPtr->hCurBlock);
		SetCapture (hWnd);		/* release capture on button up */

	  }

	  InvalidateRect (hWnd, NULL, FALSE);
	}
	break;

  case WM_LBUTTONUP:
	/*  Letting up on the left button on a group name 
	 *  gets us out of Dragging mode   */

	ReleaseCapture ();
	DragMouseAction = DRAG_NONE;
	break;

  case WM_MOUSEMOVE:
	/*  Code to drag the mouse and change the select/not selected
	 *  status of that group.
	 */

	if ((!Initializing) && (DragMouseAction != DRAG_NONE)) {
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  GetClientRect (hWnd, &myRect);
	  if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) {
		if (GroupListMultiSelect) {
		  NetDoc.ActiveLineID = LinePtr->LineID;
		  ForAllLinesOnScreen (&NetDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
		  GlobalUnlock (BlockPtr->hCurBlock);
		}
		else {
		  SelectGroup (&NetDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)),
					   (DragMouseAction == DRAG_SELECT) ? TRUE : FALSE);
		  GlobalUnlock (BlockPtr->hCurBlock);
		}
	  }
	  InvalidateRect (hWnd, NULL, FALSE);
	}
	break;

  case WM_LBUTTONDBLCLK:
	/*  Double-clicking on a group name creates a "Group"
	 *  window, whose purpose is to display the subjects
	 *  of the articles in the group.
	 */
	if (!Initializing) {
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

//	getgroup:;

	  if (CursorToTextLine (X, Y, &NetDoc, &GroupBlockPtr, &GroupLinePtr)) {

	  getgroup1:;

		if (MyDoc = (((TypGroup far *)
			  (((char far *) GroupLinePtr) + sizeof (TypLine)))->SubjDoc)) {

		  /* We already have a document containing the subjects */
		  /* of this group, so just activate it.                */

		  ShowWindow (MyDoc->hWndFrame, SW_SHOWNORMAL);
		  SetActiveWindow (MyDoc->hWndFrame);
		  SetFocus (MyDoc->hDocWnd);
		  GlobalUnlock (GroupBlockPtr->hCurBlock);
		  break;
		}
		if (TestCommBusy (hWnd, "Can't Retrieve Group")) {
		  GlobalUnlock (GroupBlockPtr->hCurBlock);
		  break;
		}

		/* At this point, we've determined that the subjects for
		 * this newsgroup are not in memory, and that it's OK
		 * to fetch them from the server.  (Comm line not busy.)
		 * Figure out whether a new window/document should be
		 * created for this group & set "newdoc" accordingly.
		 */
		newdoc = FALSE;
		if (ViewNew || !ActiveGroupDoc || !(ActiveGroupDoc->InUse)) {
		  found = FALSE;
		  for (docnum = 0; docnum < MAXGROUPWNDS; docnum++) {
			if (!GroupDocs[docnum].InUse) {
			  found = TRUE;
			  newdoc = TRUE;
			  CommDoc = &(GroupDocs[docnum]);
			  CommDoc->LongestLine = 0;
			  CommDoc->ScXOffset = 0;
			  break;
			}
		  }
		  if (!found) {
			MessageBox (hWnd, "You have too many group windows active.\n"
							  "Close one or deselect 'New window for each group'.", 
							  "Can't open new window", MB_OK | MB_ICONASTERISK);
			UnlockLine (GroupBlockPtr, GroupLinePtr, &hBlock, &Offset, &MyLineID);
			break;
		  }
		}
		else {
		  /* Must reuse old window for this group.           */
		  ActiveGroupDoc->LongestLine = 0;
		  ActiveGroupDoc->ScXOffset = 0;
		  CommDoc = ActiveGroupDoc;

		  LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset, CommDoc->ParentLineID, &BlockPtr, &LinePtr);
		  MyGroup = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
		  MyGroup->SubjDoc = (TypDoc *) NULL;

		  /* make the 'n' in the group-list go away */
		  MyGroup->HighestPrevSeen = MyGroup->ServerLast;
		  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);

		  UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
		  
		  UpdateSeenArts (CommDoc);
		  UnlinkArtsInGroup (CommDoc);
		  /* clear out contents of this document for reuse */
		  FreeDoc (CommDoc);
		}

		/* Create window title indicating we are retrieving text. */

		CommDoc->InUse = TRUE;
		lpsz = (char far *) (((char far *) GroupLinePtr) +
							 sizeof (TypLine) + sizeof (TypGroup));
		strcpy (mybuf, "Retrieving ");
		lstrcat (mybuf, lpsz);

		/* If necessary, create a new window.              */

		if (newdoc) {
		  int x, y, width, height;
		  char poschars[MAXINTERNALLINE];

		  /* Compute default screen position. */
		  x = 1;
		  y = 0;
		  width = (int) xScreen;
		  height = (int) (yScreen * 1 / 2);

		  /* If the screen position has been saved, use that instead. */
		  GetPrivateProfileString (ARTLIST, "GroupWindowPos", "!",
								   poschars, MAXINTERNALLINE, szAppProFile);
		  if (poschars[0] != '!') {
			sscanf (poschars, "%d,%d,%d,%d", &x, &y, &width, &height);
		  }
		  hWndView = CreateWindow ("WinVnViewFrame",
								   mybuf,
								   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
								   x + (int) docnum * CharWidth,	/* Initial X pos */
								   y + (int) docnum * LineHeight,
								   width,	/* Initial X Width */
								   height,
								   NULL,
								   NULL,
								   hInst,
								   NULL);

		  if (!hWndView)
			return (0);			/* ??? */

/*    SetHandleBkBrush (hWndView, hListBackgroundBrush); */
		  ShowWindow (hWndView, SW_SHOWNORMAL);
		}
		else {
		  hWndView = CommDoc->hWndFrame;
		  SetWindowText (hWndView, mybuf);
		  SetGroupMenus (CommDoc, DISABLE);
		}
		RcvLineCount = 0;

		/* Do some housekeeping:  Set group as selected,
		 * initialize empty document to hold subject lines,
		 * set focus to this document, set pointers linking
		 * this document and the subject line.
		 */
		SelectGroup (&NetDoc, (TypGroup far *) (((char far *) GroupLinePtr) +
												sizeof (TypLine)), TRUE);
		InitDoc (CommDoc, hWndView, &NetDoc, DOCTYPE_GROUP);
		PtrToOffset (GroupBlockPtr, GroupLinePtr, &(CommDoc->hParentBlock),
					 &(CommDoc->ParentOffset), &(CommDoc->ParentLineID));
		SetActiveWindow (CommDoc->hWndFrame);
		SetFocus (CommDoc->hDocWnd);

		((TypGroup far *) (((char far *) GroupLinePtr) + sizeof (TypLine)))
		  ->SubjDoc = CommDoc;
		GlobalUnlock (GroupBlockPtr->hCurBlock);
		InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);

		UpdateWindow (CommDoc->hDocWnd);
		InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);

		/* Deal with Comm-related stuff:  set FSA variables,
		 * send the GROUP command to NNTP.
		 */

		CommLinePtr = CommLineIn;
		CommBusy = TRUE;
		CommState = ST_GROUP_RESP;
		hSaveCursor = SetCursor (hHourGlass);
		ShowCursor (TRUE);


/* capture the mouse to the usenet window, so that we keep the hourglass */

/*       SetCapture (hWnd); */
		strcpy (mybuf, "GROUP ");
		lpsz = (char far *) GroupLinePtr + sizeof (TypLine) + sizeof (TypGroup);
		lstrcat (mybuf, lpsz);
		mylstrncpy (CurrentGroup, lpsz, MAXFINDSTRING);
		PutCommLine (mybuf);
	  }
	}
	break;

/* If we've lost the connection for some reason, kill the timer */
	/* that's banging on the socket. */
  case WM_TIMER:
	if ((CommState == ST_CLOSED_COMM) && (!Initializing)) {
	  KillTimer (hWnd, ID_TIMER);
	}
	else {
	  if (CommState != ST_CLOSED_COMM)
		DoCommInput ();
	};
	break;

  case WM_SIZE:
	GetClientRect (hWnd, &myRect);
	NetDoc.ScXWidth = myRect.right;
	NetDoc.ScYHeight = myRect.bottom;
	NetDoc.ScYLines = (RectHeight (myRect) - TopSpace) / LineHeight;
	NetDoc.ScXChars = (RectWidth (myRect) - SideSpace) / CharWidth;
	break;

  case WM_VSCROLL:
	if (!Initializing) {
	  ScrollIt (&NetDoc, wParam, lParam);
	}
	break;

  case WM_HSCROLL:
	if (!Initializing) {
	  HScrollIt (&NetDoc, wParam, lParam);
	}
	break;

  case WM_PAINT:
	{
	  HANDLE hBlock;
	  SIZE sz;
	  unsigned int Offset, MyLen, indicatorwidth;
	  int VertLines, HorzChars;
	  int EndofDoc = FALSE;
	  int RangeHigh, CurPos;
	  int Xtext;
	  char far *textptr;
	  char *cptr, *cptr2;
	  char indicator;
	  char indcptr[128];
	  char group_name[MAXINTERNALLINE];
	  char init_msg[60];
	  TypGroup far *MyGroup;
	  COLORREF MyColors[4], MyBack[4];
/* DWORD Rop; */
	  RECT myRect, aRect;
	  int MyColorMask = 1, PrevColorMask = MyColorMask;
	  HBRUSH hOldBrush;
	  HANDLE hOldFont;
#define SUBSCR_MASK 1
#define SELECT_MASK 2

	  hDC = BeginPaint (hWnd, &ps);
	  GetClientRect (hWnd, &myRect);

	  /* If still initializing comm link, put out a message    */
	  /* to that effect.                                       */

	  switch (Initializing) {
	  case INIT_NOT_CONNECTED:
		cptr = "Not connected";
		cptr2 = "to news server";

		goto display_msg;

	  case INIT_ESTAB_CONN:
		cptr = "Establishing link";
		cptr2 = "to news server...";

		goto display_msg;

	  case INIT_READING_NEWSRC:
		cptr = "Reading your log";
		cptr2 = "(\"newsrc\")...";
		goto display_msg;

	  case INIT_SCANNING_NETDOC:
		cptr = "Creating hash table";
		cptr2 = "from current groups...";
		goto display_msg;

	  case INIT_GETTING_LIST:
		sprintf (mybuf, "Receiving %uth newsgroup", RcvLineCount);
		cptr = mybuf;
		cptr2 = "name from server...";

	  display_msg:;
		hOldFont = SelectObject (hDC, hWinVnFont);
		SetBkColor (hDC, ListBackgroundColor);
		if (IsDark(ListBackgroundColor))
		  SetTextColor (hDC, RGB (200, 200, 200));	/* if background is black, make text white */

		else
		  SetTextColor (hDC, RGB (0, 0, 0));	/* otherwise text is black */
/*
		hOldBrush = SelectObject (hDC, hListBackgroundBrush);
		PatBlt (hDC, 0, 0, myRect.right, myRect.bottom, PATCOPY);
		SelectObject (hDC, hOldBrush);
*/
		X = SideSpace + CharWidth;
		Y = LineHeight;
		strcpy (init_msg, cptr);
		strcat (init_msg, "    ");
		j = strlen (init_msg);
		TextOut (hDC, X, Y, init_msg, j);
		strcpy (init_msg, cptr2);
		strcat (init_msg, "    ");
		j = strlen (init_msg);
		TextOut (hDC, X, Y + WinVnLineHeight, init_msg, j);
		SelectObject (hDC, hOldFont);
		break;

	  case INIT_READY:
		/* this state happens when all connection prep is finished 
		 * and we are ready to begin normal WinVn user operation
		 */
		SetUserMenus (&NetDoc, ENABLE);
		Initializing = INIT_DONE;
		/* fall into INIT_DONE */

	  case INIT_DONE:
		VertLines = NetDoc.ScYLines;
		HorzChars = NetDoc.ScXChars;

		MyColors[0] = NetUnSubscribedColor;		/* unsubscribed/unselected     */

		MyBack[0] = ListBackgroundColor;
		MyColors[1] = NetSubscribedColor;	/* subscribed/unselected */

		MyBack[1] = ListBackgroundColor;

		if (UseInverseSelections) {
		  MyColors[2] = ListBackgroundColor;	/* unsubscribed/selected */

		  MyBack[2] = NetUnSubscribedColor;
		  MyColors[3] = ListBackgroundColor;	/* subscribed/selected */

		  MyBack[3] = NetSubscribedColor;
		  MyColors[2] = ListBackgroundColor;	/* unsubscribed/selected */

		  MyBack[2] = NetUnSubscribedColor;
		  MyColors[3] = ListBackgroundColor;	/* subscribed/selected */

		  MyBack[3] = NetSubscribedColor;
		}
		else {
		  if (NetUnSubscribedColor == RGB (0, 0, 0) ||	/* unsub/selected */
			   ListBackgroundColor == RGB (0, 0, 0) && IsBright (NetUnSubscribedColor))
			MyColors[2] = ListBackgroundColor;
		  else
			MyColors[2] = NetUnSubscribedColor;

		  if (NetSubscribedColor == RGB (0, 0, 0) ||	/* sub/selected */
			   ListBackgroundColor == RGB (0, 0, 0) && IsBright (NetSubscribedColor))
			MyColors[3] = ListBackgroundColor;
		  else
			MyColors[3] = NetSubscribedColor;

		  if (ListBackgroundColor == RGB (0, 0, 0))
			MyBack[2] = RGB (200, 200, 200);
		  else
			MyBack[2] = RGB (0, 0, 0);
		  MyBack[3] = MyBack[2];
		}

		SetTextColor (hDC, MyColors[MyColorMask]);
		SetBkColor (hDC, MyBack[MyColorMask]);

		hOldFont = SelectObject (hDC, hListFont);

		GetTextExtentPoint (hDC, "n", 2, &sz);	/* was a '*', now will be 'u' or 'n' */
		indicatorwidth = sz.cx * 7 * 7;

		/* Update the scroll bar thumb position.                 */

		RangeHigh = NetDoc.ActiveLines - VertLines;
		if (RangeHigh < 0) {
		  RangeHigh = 0;
		  NetDoc.TopLineOrd = 0;
		  NetDoc.TopScLineID = 0;
		  NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
		  NetDoc.TopScOffset = sizeof (TypBlock);
		  LockLine (NetDoc.hFirstBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
					&BlockPtr, &LinePtr);
  		  AdvanceToActive (&BlockPtr, &LinePtr);
		  VertLines = NetDoc.ActiveLines;
		} else {
		  LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
					&BlockPtr, &LinePtr);
		}

		CurPos = NetDoc.TopLineOrd;
		if (CurPos < 0)
		  CurPos = 0;

		SetScrollRange (hWnd, SB_VERT, 0, RangeHigh,
		NetDoc.ThumbTracking ? FALSE : TRUE);
		/* thumb pos is updated at end of thumb track */
		if (!NetDoc.ThumbTracking) {
			SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
		}
        
		RangeHigh = NetDoc.LongestLine - NetDoc.ScXChars;
		if (RangeHigh < 0) {
		  RangeHigh = 0;
		  NetDoc.ScXOffset = 0;
		}
		SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
		SetScrollPos (hWnd, SB_HORZ, NetDoc.ScXOffset, TRUE);

		/* Loop through the lines, painting them on the screen. */

		X = SideSpace - NetDoc.ScXOffset * (CharWidth + 1);
		Xtext = X + indicatorwidth;
		Y = StartPen;
		if (LinePtr->length != END_OF_BLOCK)
		  do {
			MyGroup = ((TypGroup far *)
					   ((char far *) LinePtr + sizeof (TypLine)));
			MyLen = MyGroup->NameLen;
			textptr = (char far *) LinePtr + sizeof (TypLine) +
			  sizeof (TypGroup);

			/* Display this line only if it is active. */
			if (LinePtr->active) {

			  /* Figure out the color of this line.                 */

			  if (MyGroup->Subscribed) {
				if (MyGroup->Selected)
				  MyColorMask = 3;
				else
				  MyColorMask = 1;
			  }
			  else {
				if (MyGroup->Selected)
				  MyColorMask = 2;
				else
				  MyColorMask = 0;
			  }

			  if (MyColorMask != PrevColorMask) {
				SetTextColor (hDC, MyColors[MyColorMask]);
				SetBkColor (hDC, MyBack[MyColorMask]);
				PrevColorMask = MyColorMask;
			  }

			  /* Figure out what indicator character to use. */

			  indicator = ' ';
			  if (NetDoc.FindLineID == LinePtr->LineID) {
				indicator = '>';
			  }
			  else if (MyGroup->HighestPrevSeen) {
				if (MyGroup->ServerLast > MyGroup->HighestPrevSeen) {
				  indicator = 'n';	/* was a '*' */
				}
				else {
				  /* check if there are unread articles - jlg 4/9/94 */
#if 0
				  RangePtr = (TypRange far *) ((char far *) MyGroup + RangeOffset (MyGroup->NameLen));
				  if (MyGroup->nRanges) {
					HighestRead = RangePtr[MyGroup->nRanges - 1].Last;
					if (MyGroup->ServerLast > HighestRead && MyGroup->ServerEstNum > 0)
					  indicator = 'u';	/* was a '*' */
				  }
#endif
				  if (MyGroup->NumUnread && MyGroup->ServerEstNum > 0) {
					  indicator = 'u';
				  }
				}
			  }

			  mylstrncpy (group_name, textptr, MyGroup->NameLen + 1);

			  if (MyGroup->Determined == FALSE)
				strcpy (str, "-");
			  else
				sprintf (str, "%5lu", MyGroup->ServerEstNum);

			  sprintf (indcptr, "%c %5s %s ", indicator, str, group_name);

			  /* Now write out the line.                            */

			  MyLen = strlen (indcptr);

			  SetRect (&aRect, 0, Y, myRect.right, Y + LineHeight);

			  ExtTextOut (hDC, X, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
						  indcptr, MyLen, (LPINT) NULL);

			  if (NetDoc.ActiveLineID == -1L)
				NetDoc.ActiveLineID = LinePtr->LineID;
			  if (NetDoc.ActiveLineID == LinePtr->LineID && GroupListMultiSelect)
				DrawFocusRect (hDC, &aRect);

			  Y += LineHeight;
			  --VertLines;
			}					/* end if LinePtr->active */
		  }
		  while (VertLines > 0 && NextLine (&BlockPtr, &LinePtr));

		SelectObject (hDC, hOldFont);

/*       SetTextColor (hDC, MyColors[1]); */
		/*       SetBkColor (hDC, MyBack[1]); */
		/*       SelectObject (hDC, UnSelectedBrush); */

		/* Blank out bottom and top of screen */
		hOldBrush = SelectObject (hDC, hListBackgroundBrush);
		PatBlt (hDC, 0, Y, myRect.right - 1, myRect.bottom - Y, PATCOPY);
		PatBlt (hDC, 0, 0, myRect.right - 1, TopSpace, PATCOPY);
		SelectObject (hDC, hOldBrush);
		UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	  }
	  EndPaint (hWnd, &ps);
	  break;
	}

  case WM_ENDSESSION:

	WinHelp (NetDoc.hDocWnd, LFN_HELP, HELP_QUIT, 0L);
	/*CloseComm (CommDevice); */

	break;

  case WM_COMMAND:
	return (SendMessage (NetDoc.hWndFrame, message, wParam, lParam));


  default:
	return (DefWindowProc (hWnd, message, wParam, lParam));
  }
  return (0);
}

void
SetSelections (TypDoc * MyDoc, TypLine far * LinePtr)
{
  TypLine far *deselPtr;
  TypBlock far *BlockPtr;
  TypLineID prevActive;

  if (GetKeyState (VK_SHIFT) >= 0) {	/* shift is NOT pressed  */
	prevActive = MyDoc->ActiveLineID;
	MyDoc->ActiveLineID = LinePtr->LineID;
	/* clear select status of all lines */
	if (MyDoc->SelectedLines == 1) {
	  LineIDtoLinePtr (prevActive, MyDoc, &BlockPtr, &deselPtr);
	  SelectGroup (MyDoc, (TypGroup far *) (((char far *) deselPtr) + sizeof (TypLine)), FALSE);
	}
	else {
	  ForAllLines (MyDoc, SetLineFlag, LINE_FLAG_SET, FALSE);
	}
	/* set only current line selected */
	SelectGroup (MyDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)), TRUE);
	/* set the current line to be the anchor */
	MyDoc->AnchorLineID = LinePtr->LineID;
  }
  else
	/* shift is pressed  */
  {
	/* select all lines between ActiveLineID and AnchorLineID inclusive */
	MyDoc->ActiveLineID = LinePtr->LineID;
	ForAllLines (MyDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
  }
}

/* if enable is true then enable connect, disable disconnect */
/* disconnect is managed in INTITMENU */
void
SetConnectMenuItems (TypDoc FAR * DocPtr, int enable)
{
  HMENU hMenu, hSubMenu;
  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */

  EnableMenuItem (hSubMenu, IDM_CONNECT,  enable ? ENABLE_MENU : DISABLE_MENU);
  EnableMenuItem (hSubMenu, IDM_RESET, ENABLE_MENU);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, TRUE);
  SendMessage (DocPtr->hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_CONNECT, enable ? FALSE : TRUE);

  /* updates the exit menus/button */
  SendMessage (NetDoc.hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
}

void
SetMainMailMenu (TypDoc FAR * DocPtr)
{
  HMENU hMenu, hSubMenu;

  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */

  EnableMenuItem (hSubMenu, IDM_LOGOUT, MailCtrl.enableLogout);

  hSubMenu = GetSubMenu (hMenu, 2);		/* utilities menu */

  EnableMenuItem (hSubMenu, IDM_MAIL, MailCtrl.enableMail);

  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_MAIL,
			   (MailCtrl.enableMail == MF_ENABLED));
}

void
SetUserMenus (TypDoc FAR * DocPtr, int enable)
{
  HMENU hMenu, hSubMenu;
  UINT mode;

  assert (DocPtr->hWndFrame != 0);

  mode = (enable == ENABLE) ? ENABLE_MENU : DISABLE_MENU;

  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 1);		/* group menu */

  EnableMenuItem (hSubMenu, IDM_FIND, mode);
  EnableMenuItem (hSubMenu, IDM_FIND_NEXT_SAME, mode);

  hSubMenu = GetSubMenu (hMenu, 2);		/* utilities menu */
  EnableMenuItem (hSubMenu, IDM_POST, mode);

  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */
  EnableMenuItem (hSubMenu, IDM_RESET, mode);

  /* connect item is enabled when all else is disabled and v.v. */
  SetConnectMenuItems (DocPtr, !enable);

  SetMainMailMenu (DocPtr);

  /* set toolbar buttons */
  mode = (enable == ENABLE) ? TRUE : FALSE;

  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_RESET, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND_NEXT_SAME, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_POST, mode);
}

/*---  FUNCTION: About ---------------------------------------------------
 *
 *  Process messages for "About" dialog box
 */

BOOL WINAPI
About (hDlg, message, wParam, lParam)
	 HWND hDlg;
	 unsigned message;
	 WORD wParam;
	 LONG lParam;
{
  switch (message) {
  case WM_INITDIALOG:
	SetDlgItemText (hDlg, IDD_VERSION_NUMBER, (LPSTR)WINVN_VERSION);
	return (TRUE);

  case WM_COMMAND:
	if (wParam == IDOK) {
	  EndDialog (hDlg, TRUE);
	  return (TRUE);
	}
	break;
  }
  return (FALSE);
}

/* ----------------------------------------------------
 * Warn user if colors overlap
 * jsc 9/30/94
 */
BOOL
WarnColors (COLORREF c1, COLORREF c2, char *warnMsg)
{
  if (c1 == c2) {
	sprintf (str, "This color is the same as the %s color.\nSome displays may become unreadable.", warnMsg);
	MessageBox (NetDoc.hDocWnd, str, "Duplicate Color", MB_OK | MB_ICONINFORMATION);
	return TRUE;
  }
  return FALSE;
}

BOOL
SelectGroup (TypDoc far * Doc, TypGroup far * group, BOOL value)
{
  if (group->Selected != value) {
	group->Selected = value;
	if (value)
	  (Doc->SelectedLines)++;
	else
	  (Doc->SelectedLines)--;
  }
  return value;
}

/* --- function CrackGroupLine --------------------------------------

 *  Given a line from a .newsrc file, create a text line containing
 *  a structure of type TypGroup containing the same information.
 *  The line is zero-terminated upon input.
 *
 *    Entry    buf      points to a zero-terminated line from .newsrc.
 *             lineptr  points to a place to put the converted textline.
 *
 *    Exit     The line pointed to by "lineptr" now is a TypLine textline
 *             containing a structure of type TypGroup, containing the
 *             information in the input line from .newsrc.
 *
 *  Returns TRUE iff group was subscribed to.
 */
BOOL
CrackGroupLine (buf, lineptr)
	 char *buf;
	 TypLine *lineptr;
{
  char *grname = (char *) lineptr + sizeof (TypLine) + sizeof (TypGroup);
  TypGroup *group = (TypGroup *) ((char *) lineptr + sizeof (TypLine));
  TypRange *RangePtr;
  BOOL MoreNums;
  unsigned int MyLength;

  group->Subscribed = FALSE;
  group->Selected = FALSE;
  group->Determined = FALSE;
  group->NameLen = 0;
  group->SubjDoc = (TypDoc *) NULL;
  group->ServerEstNum = 0;
  group->ServerFirst = 0;
  group->ServerLast = 0;
  group->HighestPrevSeen = 0;
  group->nRanges = 0;
  group->header_handle = (HANDLE) NULL;
  group->NumUnread = 0;

  /* Copy group name to output line.                               */

  while (*buf && *buf != ':' && *buf != '!') {
	*(grname++) = *(buf++);
	(group->NameLen)++;
  }
  *(grname++) = '\0';

  if (!(*buf)) {
	/* Ran off end of line without seeing ':' or '!'.  Error.      */
  }
  else {
	if (*buf == ':') {
	  group->Subscribed = TRUE;
	}
	buf++;
  }

  /* Look for the highest article number previously seen, in an
   * entry of form "s" followed by a number.
   */

  while (*buf && *buf == ' ' && *buf != '\n')
	buf++;
  if (*buf == 's') {
	buf++;
	GetNum (&buf, &(group->HighestPrevSeen));
  }

  /* Convert the article number ranges to the internal numeric
   * form we use in WinVN.
   */

  RangePtr = (TypRange *) ((char *) lineptr + sizeof (TypLine) +
						   RangeOffset (group->NameLen));

  RangePtr->Last = RangePtr->First = 0;

  /* allow lines like this, with no range data: */
  /* news.group:  */
  /* */
  MoreNums = ((*buf) == '\n' ? FALSE : TRUE);
  while (MoreNums) {
	while (*buf && (*buf == ' ' || *buf == ','))
	  buf++;
	if (GetNum (&buf, &(RangePtr->First))) {
	  /* We have the first number in a range.                     */
	  (group->nRanges)++;
	  RangePtr->Last = RangePtr->First;
	  if (*buf == '-') {
		buf++;
		if (GetNum (&buf, &(RangePtr->Last))) {
		  RangePtr++;
		  /* at this point, we are positioned just after a range */
		}
		else {
		  RangePtr->Last = RangePtr->First;
		  MoreNums = FALSE;
		}
	  }
	  else if (*buf == ',') {
		/* We have a single number "n"; interpret as the range "n-n".
		 */
		RangePtr++;
	  }
	  else {
		/* That must have been the last number.                  */
		MoreNums = FALSE;
	  }
	}
	else {
	  MoreNums = FALSE;
	}
  }
  if (group->nRanges == 0)
	(group->nRanges)++;

  MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) +
	sizeof (TypRange) * (group->nRanges) + sizeof (int);

  lineptr->length = MyLength;
  lineptr->LineID = NextLineID++;
  *(int *) ((char *) lineptr + MyLength - sizeof (int)) = MyLength;

  return (group->Subscribed);
}

/*-- function CursorToTextLine ----------------------------------------
 *
 *   Routine to locate a text line in a document, based on the
 *   cursor position.  Used to figure out which line is being selected
 *   when a user clicks a mouse button.
 *
 *   Entry    X, Y    are the position of the cursor.
 *            DocPtr  points to the current document.
 *
 *   Exit     *LinePtr points to the current line, if one was found.
 *            *BlockPtr points to the current block, if found.
 *            Function returns TRUE iff a line was found that corresponds
 *              to the cursor position.
 */
BOOL
CursorToTextLine (X, Y, DocPtr, BlockPtr, LinePtr)
	 int X;
	 int Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int found;
  int SelLine;

  if (Y < TopSpace || (unsigned) Y > TopSpace + DocPtr->ScYLines * LineHeight ||
	  X < SideSpace) {
	/* Cursor is in no-man's-land at edge of window.               */
	found = FALSE;
  }
  else {
	found = TRUE;
	SelLine = (Y - TopSpace) / LineHeight;
	if ((unsigned int) SelLine >= DocPtr->ScYLines)		/* double-check */
	  return (FALSE);

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);
	AdvanceToActive (BlockPtr, LinePtr);

	for (found = TRUE, il = 0; il < SelLine;) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		found = FALSE;			/* ran off end of document */
		break;
	  }
	  else if ((*LinePtr)->active) {
		il++;
	  }
	}
  }
  return (found);
}

extern char * GetFileExtension (char *ext, char *fileName);

/*-- function ReadNewsrc ----------------------------------------------
 *
 *    Reads NEWSRC into the Net document.
 *    This routine opens NEWSRC, reads & parses the lines into the NetDoc
 *    document, and closes the file.  One call does it all.
 *
 *    Entry    The NetDoc document is assumed to be initialized.
 *
 *    Exit     Returns TRUE if all went well, else zero.
 */
int
ReadNewsrc ()
{
  TypBlock far *BlockPtr;
  TypLine *LinePtr, far * GroupPtr;
  HANDLE hBlock;
  HFILE hRetCode;
  unsigned int Offset;
  TypLineID MyLineID;
  char mybuf[TEMPBUFSIZE];
  TypMRRFile *MRRFile;
  int returned;

  LockLine (NetDoc.hCurAddBlock, NetDoc.AddOffset, NetDoc.AddLineID, &BlockPtr, &GroupPtr);
/*  NetDoc.hDocWnd = NetDoc.hDocWnd; */

  if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, TEMPBUFSIZE)) == NULL) {
	return 0;
  }

#if 0
  env_var = getenv ("WINVN");	/* get path to winvn.ini */
  if (lstrlen (env_var)) {
	lstrcpy (newsrc_filename, env_var);
	if (*(newsrc_filename + lstrlen (newsrc_filename) - 1) == '\\')
	  lstrcat (newsrc_filename, "newsrc");
	else
	  lstrcat (newsrc_filename, "\\newsrc");
  }
  else {
	MessageBox (NetDoc.hDocWnd, "Environment variable WINVN not set.", "Fatal Error", MB_OK | MB_ICONEXCLAMATION);
	return (0);
  }

#endif
  
  hRetCode = MRROpenFile (szNewsSrc, OF_READ, &MRRFile);
  if ((int) hRetCode <= 0) {
	return FALSE;
  }
  else {
	/* Loop to read lines, convert them to internal format, and
	 * insert them into the NetDoc document.
	 */

	LinesInRC = 0;
	while ((returned = MRRReadLine (MRRFile, mybuf, TEMPBUFSIZE)) > (-1)) {
	  mybuf[returned] = '\0';
	  if (CrackGroupLine (mybuf, LinePtr)) {
		NetDoc.CountedLines++;
	  }
	  AddLine (LinePtr, &BlockPtr, &GroupPtr);
	  LinesInRC++;
	}
	MRRCloseFile (MRRFile);
  }

  /* Change the title of the Net document.  I suppose that,
   * strictly speaking, this oughtn't be done in this routine.
   */
  SetNetDocTitle ();
  UnlockLine (BlockPtr, GroupPtr, &hBlock, &Offset, &MyLineID);

  NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
  NetDoc.TopScOffset = sizeof (TypBlock);
  NetDoc.TopScLineID = 0L;
  NetDoc.LongestLine = 0;
  /* Mark lines active or inactive according to ShowUnsubscribed. */
  SetGroupActiveLines ();
  /* we'll check this during WriteNewsrc(). SMR 930224 */
  started_with_no_dolist = !DoList;

  GlobalFreePtr (LinePtr);

  return (TRUE);
}

char *ltoa ();

/*--- function WriteNewsrc ---------------------------------------------
 *
 *  Write out a NEWSRC file, based on the information in the
 *  NetDoc document.  Use the standard Unix "rn" format for .newsrc.
 *
 *    Entry    no parameters
 *             NetDoc   has the group information.
 *
 *    Exit     The NEWSRC file has been written.
 */
void
WriteNewsrc ()
{
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;
  HANDLE hBlock;
  unsigned int Offset;
  TypLineID MyLineID;
  char far *fromptr;
  char *toptr;
  char *NewsLine;
  HFILE hRetCode;
  TypMRRFile *MRRFile;
  int nranges;
  TypGroup far *Group;
  TypRange far *RangePtr;
  BOOL firstrange;
  char newsrc_temp[MAXFILENAME];
  char newsrc_extension[MAXFILENAME];
  int extension_length;

  /* conditions in which we do not want to write it out */
  if (Initializing == INIT_READING_NEWSRC ||
	  Initializing == INIT_SCANNING_NETDOC ||
	  Initializing == INIT_GETTING_LIST) {
	return;
  }

  LockLine (NetDoc.hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr);

  if ((NewsLine = (char *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) == NULL) {
	MessageBox (NetDoc.hDocWnd, "Memory allocation failure.  Newsrc not saved.", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
	return;
  }


  strcpy (newsrc_temp, szNewsSrc);
  GetFileExtension (newsrc_extension, szNewsSrc);
  extension_length = strlen (newsrc_extension);

  if (extension_length != 0) {
	strcpy (newsrc_temp + (strlen(newsrc_temp) - (extension_length+1)), ".tmp");
  } else {
	strcpy (newsrc_temp + strlen(newsrc_temp), ".tmp");
  }

 /*  hRetCode = MRROpenFile (szNewsSrc, OF_CREATE, &MRRFile);  */
 /*  hRetCode = MRROpenFile (szNewsSrc, OF_SHARE_EXCLUSIVE, &MRRFile); */
  hRetCode = MRROpenFile (newsrc_temp, OF_WRITE, &MRRFile); 
  if ((int) hRetCode < 0) {
	MessageBox (NetDoc.hDocWnd, "Could not write to newsrc file", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
  }
  else {
	do {
	  toptr = NewsLine;
	  Group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

	  /* Remove groups only if we performed a LIST command and */
	  /* the server did not have them.  Otherwise if !did_list, the */
	  /* newsrc will be practically empty! */

	  /* Jeeeeez!!  I'm fixing this AGAIN! */
	  /* DoList is not a boolean. SMR 930610 */

	  if ((Group->ServerFirst || Group->ServerEstNum) || !did_list) {
		/* Copy group name                                          */
		fromptr = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
		while (*(toptr++) = *(fromptr++));
		toptr--;

		/* Affix : or ! depending upon whether subscribed.          */
		*(toptr++) = (char) (Group->Subscribed ? ':' : '!');
		*(toptr++) = ' ';

		/* If we know the highest article number on the server,
		 * output it preceded by an "s".
		 */
		if (Group->ServerLast || Group->HighestPrevSeen) {
		  *(toptr++) = 's';
		  if (Group->ServerLast)
			ltoa ((unsigned long) Group->ServerLast, toptr, 10);
		  else
			ltoa ((unsigned long) Group->HighestPrevSeen, toptr, 10);
		  while (*toptr)
			toptr++;
		  *(toptr++) = ' ';
		}

		/* Affix ranges of articles read.                          */
		firstrange = TRUE;
		nranges = Group->nRanges;
		RangePtr = (TypRange far *) ((char far *) Group + RangeOffset (Group->NameLen));

		while ((nranges--) > 0) {
		  /* Write out ',' if not first range of articles.         */

		  if (!firstrange) {
			*(toptr++) = ',';
		  }
		  else {
			firstrange = FALSE;
		  }
		  /* Write out first article in a range.                   */

		  ltoa ((unsigned long) RangePtr->First, toptr, 10);
		  while (*toptr)
			toptr++;

		  /* If the range is of form "n-n", just put out "n"       */

		  if (RangePtr->First != RangePtr->Last) {
			/* Put out the hyphen in middle of range.                */
			*(toptr++) = '-';
			/* Put out the last article in a range.                  */
			ltoa ((unsigned long) RangePtr->Last, toptr, 10);
			while (*toptr)
			  toptr++;
		  }
		  RangePtr++;
		}
		MRRWriteLine (MRRFile, NewsLine, toptr - NewsLine);
	  }
	}
	while (NextLine (&BlockPtr, &LinePtr));
	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	MRRCloseFile (MRRFile);
	unlink (szNewsSrc);
	if (rename (newsrc_temp, szNewsSrc)) {
	  char error_string[256];
	  sprintf (error_string, "Could not rename temporary newsrc file '%s'", newsrc_temp);
	  MessageBox (NetDoc.hDocWnd, error_string, "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
	}
  }
  GlobalFreePtr (NewsLine);
}

/*--- function SetNetDocTitle -------------------------------------------
 *
 */
void
SetNetDocTitle ()
{
  char mybuf[120];

  sprintf (mybuf, "WinVN:  %u groups; %u subscribed", NetDoc.TotalLines,
		   NetDoc.CountedLines);
  SetWindowText (NetDoc.hWndFrame, mybuf);

}

/*--- function SetLineFlag --------------------------------------------
 *
 *  Set some flag in a line in a document.
 *
 *  Entry   Doc      points to the document.
 *          LinePtr  points to th line.
 *
 *  Exit    lFlag    says what to do.
 */
void
SetLineFlag (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  switch (wFlag) {
  case LINE_FLAG_SET:
	SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), wValue);
	break;
  }
}

void
SetExLineFlag (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  static BOOL extending = FALSE;

  switch (wFlag) {
  case LINE_FLAG_SET:
	if ((*LinePtr)->LineID == Doc->ActiveLineID ||
		(*LinePtr)->LineID == Doc->AnchorLineID) {
	  if (extending == TRUE) {
		SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), TRUE);
		extending = FALSE;
	  }
	  else {
		SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), TRUE);
		if (Doc->ActiveLineID == Doc->AnchorLineID)
		  extending = FALSE;
		else
		  extending = TRUE;
	  }
	}
	else
	  SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), extending);

	break;
  }
}

/*--- function GroupAction --------------------------------------------
 *
 *  Perform some action on a group that is specified by a pointer
 *  to a line in the Net document.
 *  Typically called for each line in the Net document by
 *  ForAllLines.
 *
 *  Entry   Doc      points to NetDoc
 *          LinePtr  points to a line in that document.
 *          lFlag    indicates what to do with that line.
 */
void
GroupAction (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  static char mybuf[MAXINTERNALLINE];

  switch (wFlag) {
  case GROUP_ACTION_SUBSCRIBE:
  case GROUP_ACTION_UNSUBSCRIBE:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Selected) {
	  ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Subscribed = wValue;
	  AddGroupToTable ((char far *) *LinePtr);
	  DeleteLine (BlockPtr, LinePtr);
	}
	break;

  case GROUP_ACTION_CHECK_ACTIVE:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Subscribed || ShowUnsubscribed) {
	  (*LinePtr)->active = TRUE;
	  (*BlockPtr)->NumActiveLines++;
	  NetDoc.ActiveLines++;
	  NetDoc.LongestLine = max (NetDoc.LongestLine, (unsigned)
			 ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
								->NameLen + GROUP_NAME_OFFSET);
	  if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		  ->Subscribed)
		NetDoc.CountedLines++;
	}
	else {
	  (*LinePtr)->active = FALSE;
	}
	break;

  case GROUP_ACTION_GET_GROUPNAMES:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Selected) {
	  ExtractTextLine (&NetDoc, *LinePtr,
					   mybuf, sizeof(mybuf));
	  if (strlen(groupbuf) + strlen(mybuf) + 2 < sizeof(groupbuf)) {
	    if (*groupbuf)
	      strcat(groupbuf, ",");
  	    strcat(groupbuf, mybuf);
	  }
	}
	break;
  }
}

/****************************************************************************

   FUNCTION:   MakeHelpPathName

   PURPOSE:    HelpEx assumes that the .HLP help file is in the same
          directory as the HelpEx executable.This function derives
          the full path name of the help file from the path of the
          executable.

   Taken from HELPEX.C, from the MS Windows SDK.

****************************************************************************/

void
MakeHelpPathName (szFileName, maxchars)
	 char *szFileName;
	 int maxchars;
{
  char *pcFileName;
  int nFileNameLen;

  nFileNameLen = GetModuleFileName (hInst, szFileName, maxchars);
  pcFileName = szFileName + nFileNameLen;

  while (pcFileName > szFileName) {
	if (*pcFileName == '\\' || *pcFileName == ':') {
	  *(++pcFileName) = '\0';
	  break;
	}
	nFileNameLen--;
	pcFileName--;
  }

  if ((nFileNameLen + 13) < maxchars) {
	lstrcat (szFileName, LFN_HELP);
  }

  else {
	lstrcat (szFileName, "?");
  }

  return;
}


int
cursor_to_char_number (X, Y, DocPtr, BlockPtr, LinePtr)
	 int X;
	 int Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int SelLine;
  int charnum = -1;
  SIZE extent;

  char far *textptr;
  int textlen;
  HDC display_context;
  int iTopSpace, iSideSpace, iLineHeight, iCharWidth;
  HANDLE hOldFont;

  if (DocPtr->DocType == DOCTYPE_ARTICLE) {
	iTopSpace = ArtTopSpace;
	iSideSpace = ArtSideSpace;
	iLineHeight = ArtLineHeight;
	iCharWidth = ArtCharWidth;
  }
  else {
	iTopSpace = TopSpace;
	iSideSpace = SideSpace;
	iLineHeight = LineHeight;
	iCharWidth = CharWidth;
  }

  if (Y < iTopSpace || (unsigned) Y > iTopSpace + DocPtr->ScYLines * iLineHeight ||
	  X < iSideSpace) {
	return (-1);
  }
  else {
	SelLine = (Y - iTopSpace) / iLineHeight;

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);

	for (il = 0; il < SelLine; il++) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		return (-1);
		break;
	  }
	}
  }

  /* find the right character on the text line */
  textlen = ((TypText far *) ((char far *) (*LinePtr) +
							  sizeof (TypLine)))->NameLen;


  if (textlen) {
	textptr = (char far *) ((char far *) (*LinePtr) + sizeof (TypLine) + sizeof (TypText));
	display_context = GetDC (DocPtr->hDocWnd);

	if (isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
	  hOldFont = SelectObject (display_context, hFontArtQuote);
	}
	else {						/* prepare to print a normal line */
	  hOldFont = SelectObject (display_context, hFontArtNormal);
	}

	for (charnum = 1; charnum < textlen; charnum++) {
	  GetTextExtentPoint (display_context, (LPSTR) textptr, charnum, &extent);
	  if (extent.cx > (X + (int) DocPtr->ScXOffset * (iCharWidth + 1) - iSideSpace))
		break;
	}
	SelectObject (display_context, hOldFont);
	ReleaseDC (DocPtr->hDocWnd, display_context);
	return (charnum - 1);
  }

}
  long
lcursor_to_char_number (X, Y, DocPtr, BlockPtr, LinePtr)
	 long X;
	 long Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int SelLine;
  long charnum = -1;
  SIZE extent;

  char far *textptr;
  int textlen;
  HDC display_context;
  int iTopSpace, iSideSpace, iLineHeight, iCharWidth;
  HANDLE hOldFont;

  if (DocPtr->DocType == DOCTYPE_ARTICLE) {
	iTopSpace = ArtTopSpace;
	iSideSpace = ArtSideSpace;
	iLineHeight = ArtLineHeight;
	iCharWidth = ArtCharWidth;
  }
  else {
	iTopSpace = TopSpace;
	iSideSpace = SideSpace;
	iLineHeight = LineHeight;
	iCharWidth = CharWidth;
  }

  if (Y < iTopSpace || (unsigned) Y > iTopSpace + DocPtr->ScYLines * iLineHeight ||
	  X < iSideSpace) {
	return (-1);
  }
  else {
	SelLine = (int) (Y - iTopSpace) / iLineHeight;

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);

	for (il = 0; il < SelLine; il++) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		return (-1);
		break;
	  }
	}
  }

  /* find the right character on the text line */
  textlen = ((TypText far *) ((char far *) (*LinePtr) +
							  sizeof (TypLine)))->NameLen;


  if (textlen) {
	textptr = (char far *) ((char far *) (*LinePtr) + sizeof (TypLine) + sizeof (TypText));
	display_context = GetDC (DocPtr->hDocWnd);

	if (isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
	  hOldFont = SelectObject (display_context, hFontArtQuote);
	}
	else {						/* prepare to print a normal line */
	  hOldFont = SelectObject (display_context, hFontArtNormal);
	}

	for (charnum = 1; charnum < textlen; charnum++) {
	  GetTextExtentPoint (display_context, (LPSTR) textptr, (int) charnum, &extent);
	  if (extent.cx > (X + (int) DocPtr->ScXOffset * (iCharWidth + 1) - iSideSpace))
		break;
	}
	SelectObject (display_context, hOldFont);
	ReleaseDC (DocPtr->hDocWnd, display_context);
	return (charnum - 1);
  }

}

/*--- function SetGroupActiveLines --------------------------------------
 *
 *  Go through all the lines in the Net document, marking each
 *  as active or inactive according to whether the corresponding
 *  group is subscribed and whether we are displaying unsubscribed
 *  groups.
 *
 *  Entry:  NetDoc and ShowUnsubscribed are set properly.
 *
 *  Exit:   Each of the lines in NetDoc has had its "active" field
 *          set properly.
 */
void
SetGroupActiveLines ()
{
  NetDoc.ActiveLines = 0;
  NetDoc.CountedLines = 0;
  ForAllBlocks (&NetDoc, SetForBlock, BLOCK_ACTION_SET_ACTIVE, 0);
  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_CHECK_ACTIVE, 0);
}


/* stuff for showing version numbers of the files */

#include "version.h"
#include "version.c"

/*-- function VerListDlgProc ---------------------------------------
 *
 *  Dialog function to handle selection of Version List
 *                                  JD 8/3/94
 */

LRESULT CALLBACK
VerListDlgProc (HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  int j;
  char far *cptr;
  char szVersion[64];

  switch (iMessage) {
  case WM_INITDIALOG:
	sprintf (szVersion, "Version %s", (LPSTR) WINVN_VERSION);
	SetDlgItemText ((HWND) hDlg, IDD_VERSION_NUMBER, (LPSTR) szVersion);
	hVerDlgList = GetDlgItem (hDlg, IDD_VERSION_LISTBOX);
	SendMessage (hVerDlgList, WM_SETREDRAW, FALSE, 0L);

	cptr = 0;
	for (j = 0; j < (sizeof (version_string) / sizeof version_string[0]); j++) {
	  cptr = version_string[j];
	  SendMessage (hVerDlgList, LB_ADDSTRING, 0, (LONG) cptr);
	}
	SendMessage (hVerDlgList, WM_SETREDRAW, TRUE, 0L);
	return TRUE;
	break;

  case WM_COMMAND:
	switch (wParam) {
	case IDOK:
	  EndDialog (hDlg, TRUE);
	  break;

	case IDCANCEL:
	  EndDialog (hDlg, FALSE);
	  break;

	default:
	  return FALSE;
	}
	break;

  case WM_DESTROY:
	break;

  default:
	return FALSE;
	break;
  }
  return TRUE;
}

/*  Display Dialog Box to show version info   JD 8/4/94 */
void
show_version_strings (HWND hWnd)
{
  lpfnWinVnVersionListDlg = (DLGPROC) MakeProcInstance ((FARPROC) VerListDlgProc, (HINSTANCE) hInst);
  DialogBox (hInst, "WINVNVERSIONLIST", hWnd, lpfnWinVnVersionListDlg);
  FreeProcInstance ((FARPROC) lpfnWinVnVersionListDlg);

}


long WINAPI
WinVnConfFrameWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 unsigned message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  DLGPROC lpProcAbout;
  RECT myRect;					/* selection rectangle                */
  static HANDLE ToolBarLib;
  char mybuf[MAXINTERNALLINE];
  int found;
  PAINTSTRUCT ps;				/* paint structure          */
  HDC hDC;						/* handle to display context */
  int i, j, numComposeWnds;
  TBBUTTON tbButton[MAXTBBUTTONS];
  HMENU hMenu, hSubMenu;
  BOOL continueFind;
  TypLine far *LinePtr;
  TypBlock far *BlockPtr;

  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
    return 0;

  switch (message) {
#ifdef USE_3D_CONTROLS
  case WM_SYSCOLORCHANGE:
	Ctl3dColorChange ();
	break;
#endif

  case WM_SETFOCUS:
   SetCapsLockText(hWnd);
   SetNumLockText(hWnd);
   return (DefWindowProc (hWnd, message, wParam, lParam));
	break;

  case WM_SYSCOMMAND:
	switch (wParam) {
		case ID_ABOUT:
			lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
			DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
			FreeProcInstance ((FARPROC) lpProcAbout);
			break;

		case IDM_RESTORE_ALL:
			RestoreWindows();
			break;

		default:
			return (DefWindowProc (hWnd, message, wParam, lParam));
	}
	break;

  case WM_CREATE:
	GetClientRect (hWnd, &myRect);

/* add common controls for toolbar (jlg) */

	i = j = 0;
	tbButton[j].iBitmap = 0;	/* connect */

	tbButton[j].idCommand = IDB_TOGGLE_CONNECT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* reset */

	tbButton[j].idCommand = IDM_RESET;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* find */

	tbButton[j].idCommand = IDM_FIND;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* find next */

	tbButton[j].idCommand = IDM_FIND_NEXT_SAME;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* New Article */

	tbButton[j].idCommand = IDM_POST;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* New Mail */

	tbButton[j].idCommand = IDM_MAIL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* Save Configuration */

	tbButton[j].idCommand = IDM_SAVE_CONFIG;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* Toggle View Unsubscribed */

	tbButton[j].idCommand = IDB_TOGGLE_VIEW_UNSUB;
	if (ShowUnsubscribed == TRUE)
	  tbButton[j].fsState = TBSTATE_ENABLED | TBSTATE_CHECKED;
	else
	  tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* Exit */

	tbButton[j].idCommand = IDB_FASTEXIT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* About */

	tbButton[j].idCommand = ID_ABOUT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	NetDoc.hWndTB = CreateToolbar (hWnd,
								   WS_BORDER | WS_VISIBLE,
								   (WORD) GetMenu (hWnd),
								   i + 1,
								   hInst,
								   IDB_TOOLBAR,
								   tbButton,
								   j + 1);

	assert (NetDoc.hWndTB != NULL);

	NetDoc.hDocWnd = CreateWindow ("WinVn",
								   "WinVN -- Usenet News Reader",
								   WS_CHILD | WS_VSCROLL | WS_HSCROLL,
								   0,	/* Initial X position */
								   TOOLBARHEIGHT,	/* Initial Y position */
								   myRect.right - myRect.left,	/* Initial X width */
	(myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,	/* Initial Y height */
								   hWnd,
								   NULL,
								   hInst,
								   NULL);

	assert (NetDoc.hDocWnd != NULL);

	SetHandleBkBrush (NetDoc.hDocWnd, hListBackgroundBrush);
/*         ShowWindow (NetDoc.hDocWnd, SW_SHOWNORMAL); */

	/* Add the "About" and "Restore All" options to the system menu.            */
	hMenu = GetSystemMenu (hWnd, FALSE);
	AppendMenu (hMenu, MF_SEPARATOR, 0, (LPCSTR) NULL);
	AppendMenu (hMenu, MF_STRING, IDM_RESTORE_ALL, (LPCSTR) "Restore &All");
	AppendMenu (hMenu, MF_STRING, ID_ABOUT, (LPCSTR) "A&bout WinVn...");

#ifdef USE_SPLASH
	/* Add the Splash option */
	hMenu = GetMenu(hWnd);
	hSubMenu = GetSubMenu (hMenu, 3);	/* Config menu */
	InsertMenu (hMenu, IDM_SAVE_CONFIG, MF_STRING|MF_BYCOMMAND, 
					IDM_USE_SPLASH, (LPCSTR) "Show Splash Screen");
	InsertMenu (hMenu, IDM_SAVE_CONFIG, MF_SEPARATOR|MF_BYCOMMAND, 0, (LPCSTR) NULL);
#endif
	break;

  case WM_MYINITMENU:
  case WM_INITMENU:
	hMenu = GetMenu (hWnd);

	hSubMenu = GetSubMenu (hMenu, 0);	/* network menu */
	
	/* don't allow disconnect until connection is established */
	i = (Initializing == INIT_ESTAB_CONN || Initializing == INIT_NOT_CONNECTED);
	EnableMenuItem (hSubMenu, IDM_RECONNECT,  i ? DISABLE_MENU : ENABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_DISCONNECT, i ? DISABLE_MENU : ENABLE_MENU);

	i = (Initializing != INIT_ESTAB_CONN);
	EnableMenuItem (hSubMenu, IDM_EXIT,  i ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_QUIT,  i ? ENABLE_MENU : DISABLE_MENU);
	SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_FASTEXIT, i ? TRUE : FALSE);

	hSubMenu = GetSubMenu (hMenu, 1);	/* group menu */
	EnableMenuItem (hSubMenu, IDM_SUBSCRIBE, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNSUBSCRIBE, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_GROUP_TOP, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNSEL_ALL, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);

	hSubMenu = GetSubMenu (hMenu, 2);	/* Utilities menu */
	EnableMenuItem (hMenu, IDM_DECODE_FILE,
					(CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hMenu, IDM_ENCODE_FILE,
					(CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_POST,
					NumPostWnds ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_MAIL,
					NumMailWnds ? ENABLE_MENU : DISABLE_MENU);

	hSubMenu = GetSubMenu (hMenu, 3);	/* Config menu */
	CheckMenuItem (GetSubMenu (hSubMenu, 14), IDM_TRUE_INVERSE_SELECTIONS,
		 MF_BYCOMMAND | (UseInverseSelections ? MF_CHECKED : MF_UNCHECKED));
#ifdef USE_SPLASH
	CheckMenuItem (hSubMenu, IDM_USE_SPLASH,
		 MF_BYCOMMAND | (ShowSplashScreen ? MF_CHECKED : MF_UNCHECKED));
#endif

	hSubMenu = GetSubMenu (hMenu, 4);	/* Window menu */
	numComposeWnds = NumPostWnds + NumMailWnds;
	/* close submenu */
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_ARTICLE,
					NumArticleWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_GROUP,
					NumGroupWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_COMPOSE,
					numComposeWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_STATUS,
					NumStatusTexts ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL,
					(NumArticleWnds || NumGroupWnds || NumStatusTexts || numComposeWnds) ? MF_ENABLED : MF_GRAYED);
	/* minimize submenu */
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_ARTICLE,
					NumArticleWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_GROUP,
					NumGroupWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_COMPOSE,
					numComposeWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_STATUS,
					NumStatusTexts ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	/* can always minimize all - at least the main window! */
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL, MF_ENABLED);
	break;

  case WM_SIZE:
	GetClientRect (hWnd, &myRect);
	ShowWindow (NetDoc.hDocWnd, SW_SHOWNORMAL);
	MoveWindow (NetDoc.hDocWnd, 0, TOOLBARHEIGHT,
				myRect.right - myRect.left,
	(myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
				TRUE);
	MoveWindow (NetDoc.hWndTB, 0, 0,
				myRect.right - myRect.left,
				TOOLBARHEIGHT,
				TRUE);
	break;

  case WM_CLOSE:
	if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	}
	if (!ConfirmSaveOnExit) {
	  SaveNewsrc = SaveConfig = TRUE;
	}
	else {
	  /* WinVnExitDlg sets SaveNewsrc and SaveConfig */
	  if (!DialogBox (hInst, "WinVnExit", hWnd, lpfnWinVnExitDlg)) {
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		break;
	  }
	}
	SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	DestroyWindow (hWnd);
	return (0);
	break;

  case WM_DESTROY:
	if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	}
	if (Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) {
	  PutCommData ("QUIT\r\n", 6);
	}

	MailClose (hWnd);
	Disconnect ();

	if (SaveNewsrc)
	  WriteNewsrc ();

	if (SaveConfig)
	  WriteWinvnProfile ();

	/* Remove resources before exiting program */
	FreeTextBlock (Signature);
	if (MailList)
	  FreeTextBlock (MailList);

	DestroyFonts ();
	DestroyBitmaps ();

	if (hListBackgroundBrush)
	  DeleteObject (hListBackgroundBrush);
	if (hArticleBackgroundBrush)
	  DeleteObject (hArticleBackgroundBrush);
	if (hStatusBackgroundBrush)
	  DeleteObject (hStatusBackgroundBrush);
//	if (StatbarPntData.hFontStatbar)
//	  DeleteObject (StatbarPntData.hFontStatbar);
	PostQuitMessage (0);
	return (0);
	break;

  case WM_VSCROLL:
  case WM_HSCROLL:
	break;
  case WM_PAINT:
/*	if (Initializing == INIT_READY || Initializing == INIT_DONE) */
/*	  SetUserMenus (&NetDoc, ENABLE); */

	/* paint status bar  */
	hDC = BeginPaint (hWnd, &ps);
	PaintStatbar (hWnd, hDC, &NetDoc);
	EndPaint (hWnd, &ps);
	if (GetFocus () == hWnd)
	  SetFocus (NetDoc.hDocWnd);
	return (DefWindowProc (hWnd, message, wParam, lParam));
	break;

  case WM_COMMAND:
	switch (LOWORD (wParam)) {
	case IDM_QUIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }  
	  SaveNewsrc = SaveConfig = FALSE;
	  SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	  DestroyWindow (hWnd);
	  return (0);
	  break;
	case IDM_EXIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }  
	  SendMessage (hWnd, WM_CLOSE, 0, 0L);
	  break;

	case IDM_CONNECT:
	  Connect ();				/* menus are enabled in WM_PAINT once connection is established */
	  break;
    
    case IDM_SAVE_NEWSRC:
      WriteNewsrc ();
      MessageBox (NetDoc.hDocWnd, "WinVN NEWSRC Saved", "WinVN Resource File", MB_OK);
	  break;
	  
	case IDB_TOGGLE_VIEW_UNSUB:
	  if (ShowUnsubscribed == FALSE)
		ShowUnsubscribed = TRUE;
	  else
		ShowUnsubscribed = FALSE;
	  SendMessage (NetDoc.hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_VIEW_UNSUB, ShowUnsubscribed);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDB_FASTEXIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }
	  WriteNewsrc ();
	  WriteWinvnProfile ();
	  SaveNewsrc = SaveConfig = FALSE;	/* we've already done them */
	  SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	  DestroyWindow (hWnd);
	  return (0);
	  break;

	case IDB_TOGGLE_CONNECT:
	  if (Initializing == INIT_NOT_CONNECTED) {
		SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
		Connect ();
		break;
	  }
	  /* else fall into disconnect */
	  SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
	case IDM_DISCONNECT:
	  /* If the NNTP server disconnected, then Initializing == INIT_NOT_CONNECTED */
	  if ((Initializing != INIT_NOT_CONNECTED && ConfirmDisconnect &&
		   (MessageBox (NetDoc.hDocWnd,
						"Are you sure you wish to disconnect from the server?\n"
						"All active windows will be closed.",
						"Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO))) {
		SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, TRUE);
		SendMessage (NetDoc.hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_CONNECT, TRUE);
		break;
	  }

	  if (Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) {
		PutCommData ("QUIT\r\n", 6);
	  }
	  Disconnect ();
	  break;


	case IDM_VIEW_SEL_GROUP:
	  break;

	case IDM_SHOW_SUBSCR:
	case IDM_SHOW_ALL_GROUP:
	case IDM_SEL_SUBSCR:
	case IDM_SELECT_ALL:
	  MessageBox (hWnd, "Command not implemented","Sorry", MB_OK);
	  break;

	case IDM_UNSEL_ALL:
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_SUBSCRIBE:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
	  BuildPtrList ();
	  MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  SetNetDocTitle ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_UNSUBSCRIBE:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_UNSUBSCRIBE, FALSE);
	  BuildPtrList ();
	  ShellSort (NewGroupTable,
				 (size_t) nNewGroups,
				 (size_t) sizeof (void far *),
				 GroupCompare);
	  MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  SetNetDocTitle ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_GROUP_TOP:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
	  BuildPtrList ();
	  MergeGroups (ADD_SUBSCRIBED_TOP_OF_DOC);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  ScreenToTop (&NetDoc);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;


	case IDM_COMMOPTIONS:
	  DialogBox (hInst, "WinVnComm", hWnd, lpfnWinVnCommDlg);
	  break;

	case IDM_CONFIG_PERSONAL:
	  DialogBox (hInst, "WinVnPersonal", hWnd, lpfnWinVnPersonalInfoDlg);
	  break;
	  
	case IDM_CONFIG_CONFIRMATIONS:
	  DialogBox (hInst, "WinVnConfirmations", hWnd, lpfnWinVnConfirmationDlg);
	  break;

	case IDM_CONFIG_LOG:
	  DialogBox (hInst, "WinVnLogOpts", hWnd, lpfnWinVnLogOptDlg);
	  break;

	case IDM_CONFIG_SMART_FILER:
	  DialogBox (hInst, "WinvnSmartFiler", NetDoc.hDocWnd, lpfnWinVnSmartFilerDlg);
	  break;

	case IDM_CONFIG_ARTLIST:
	  DialogBox (hInst, "WinVnArticleListPrefs", hWnd, lpfnWinVnArtListDlg);
	  break;

	case IDM_CONFIG_ARTICLE:
	  DialogBox (hInst, "WinVnArticlePrefs", hWnd, lpfnWinVnArticleDlg);
	  break;

	case IDM_CONFIG_GROUPLIST:
	  DialogBox (hInst, "WinVnGroupListPrefs", hWnd, lpfnWinVnGroupListDlg);
	  break;

	case IDM_COMPOSE_PREFS:
	  DialogBox (hInst, "WinVnComposePrefs", hWnd, lpfnWinVnComposePrefsDlg);
	  break;

	case IDM_ATTACH_PREFS:
	  DialogBox (hInst, "WinVnAttachPrefs", hWnd, lpfnWinVnAttachPrefsDlg);
	  break;

	case IDM_CONFIG_CODING:
	  DialogBox (hInst, "WinVnCodingPrefs", hWnd, lpfnWinVnCodingPrefsDlg);
	  break;

	case IDM_FONT_WINVNSYSTEM:
	  if (AskForFont (NetDoc.hDocWnd, WinVnFontFace, &WinVnFontSize, WinVnFontStyle))
		break;
	  InitWinVnFonts ();
	  InitStatBar (&StatbarPntData);
	  SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
	  InvalidateRect (NetDoc.hWndFrame, NULL, TRUE);
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshArticleWnds ();
	  RefreshGroupWnds ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_COMPOSITION:
	  if (AskForFont (NetDoc.hDocWnd, CompositionFontFace, &CompositionFontSize, CompositionFontStyle))
		break;
	  InitCompositionFonts ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_GROUPLIST:
	  if (AskForFont (NetDoc.hDocWnd, ListFontFace, &ListFontSize, ListFontStyle))
		break;
	  InitListFonts ();
	  SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshGroupWnds ();
	  break;

	case IDM_FONT_ARTICLE_TEXT:
	  if (AskForFont (NetDoc.hDocWnd, ArticleFontFace, &ArticleFontSize, ArticleFontStyle))
		break;
	  InitArticleFonts ();
	  RefreshArticleWnds ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_STATUS_TEXT:
	askStatusFont:;
	  if (AskForFont (NetDoc.hDocWnd, StatusFontFace, &StatusFontSize, StatusFontStyle))
		break;
	  InitStatusFonts ();
	  if (STATUSWIDTH > xScreen) {
		MessageBox (NetDoc.hDocWnd, "The status window will not fit on your screen with this font.\nPlease select a smaller font",
					"Font too big", MB_OK | MB_ICONSTOP);
		goto askStatusFont;
	  }
	  RefreshStatusWnds ();
	  break;

	case IDM_FONT_PRINT_TEXT:
	  if (AskForFont (NetDoc.hDocWnd, PrintFontFace, &PrintFontSize, "Printer"))
		break;
	  InitPrintFonts ();
	  break;

	case IDM_TRUE_INVERSE_SELECTIONS:
	  UseInverseSelections = !UseInverseSelections;
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshGroupWnds ();
	  break;

#ifdef USE_SPLASH
	case IDM_USE_SPLASH:
	  ShowSplashScreen = !ShowSplashScreen;
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;
#endif

	case IDM_COLOR_SUBSCRIBED:
	  if (AskForColor (NetDoc.hDocWnd, &NetSubscribedColor))
		break;
	  WarnColors (NetSubscribedColor, ListBackgroundColor, "List Background");
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_UNSUBSCRIBED:
	  if (AskForColor (NetDoc.hDocWnd, &NetUnSubscribedColor))
		break;
	  WarnColors (NetUnSubscribedColor, ListBackgroundColor, "List Background");
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_SEEN:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleSeenColor))
		break;
	  WarnColors (ArticleSeenColor, ListBackgroundColor, "List Background");
	  RefreshGroupWnds ();
	  break;

	case IDM_COLOR_UNSEEN:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleUnSeenColor))
		break;
	  WarnColors (ArticleUnSeenColor, ListBackgroundColor, "List Background");
	  RefreshGroupWnds ();
	  break;

	case IDM_COLOR_ARTICLE_TEXT:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleTextColor))
		break;
	  WarnColors (ArticleTextColor, ArticleBackgroundColor, "Article Background");
	  RefreshArticleWnds ();
	  break;

	case IDM_COLOR_STATUS_TEXT:
	  if (AskForColor (NetDoc.hDocWnd, &StatusTextColor))
		break;
	  WarnColors (StatusTextColor, StatusBackgroundColor, "Status Background");
	  RefreshStatusWnds ();
	  break;

	case IDM_COLOR_LIST_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &ListBackgroundColor))
		break;
	  DeleteObject (hListBackgroundBrush);
	  hListBackgroundBrush = CreateSolidBrush (ListBackgroundColor);
	  SetHandleBkBrush (hWnd, hListBackgroundBrush);

	  WarnColors (ArticleSeenColor, ListBackgroundColor, "Seen Article") ||
		WarnColors (ArticleUnSeenColor, ListBackgroundColor, "Unseen Article") ||
		WarnColors (NetSubscribedColor, ListBackgroundColor, "Subscribed Group") ||
		WarnColors (NetUnSubscribedColor, ListBackgroundColor, "Unsubscribed Group");

	  RefreshGroupWnds ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_ARTICLE_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleBackgroundColor))
		break;
	  DeleteObject (hArticleBackgroundBrush);
	  hArticleBackgroundBrush = CreateSolidBrush (ArticleBackgroundColor);

	  WarnColors (ArticleTextColor, ArticleBackgroundColor, "Article Text");
	  RefreshArticleWnds ();
	  break;

	case IDM_COLOR_STATUS_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &StatusBackgroundColor))
		break;
	  DeleteObject (hStatusBackgroundBrush);
	  hStatusBackgroundBrush = CreateSolidBrush (StatusBackgroundColor);
	  RefreshStatusWnds ();

	  WarnColors (StatusTextColor, StatusBackgroundColor, "Status Text");
	  break;

	case IDM_WINDOW_CASCADE:
	  CascadeWindows ();
	  break;

	case IDM_CLOSE_ALL:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseWindows ();
	  break;

	case IDM_CLOSE_ALL_ARTICLE:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open article windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseArticleWnds ();
	  break;

	case IDM_CLOSE_ALL_GROUP:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open group windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseGroupWnds ();
	  break;

	case IDM_CLOSE_ALL_COMPOSE:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open composition windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseComposeWnds ();
	  break;

	case IDM_CLOSE_ALL_STATUS:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open status windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseStatusWnds ();
	  break;

	case IDM_MINIMIZE_ALL:
	  MinimizeWindows ();
	  break;

	case IDM_MINIMIZE_ALL_ARTICLE:
	  MinimizeArticleWnds ();
	  break;

	case IDM_MINIMIZE_ALL_GROUP:
	  MinimizeGroupWnds ();
	  break;

	case IDM_MINIMIZE_ALL_COMPOSE:
	  MinimizeComposeWnds ();
	  break;

	case IDM_MINIMIZE_ALL_STATUS:
	  MinimizeStatusWnds ();
	  break;

	case IDM_RESTORE_ALL:
	  RestoreWindows ();
	  break;

	case IDM_SEND_ALL_POST:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to send all open post windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  BatchSend (DOCTYPE_POSTING);
	  break;

	case IDM_SEND_ALL_MAIL:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to send all open mail windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  BatchSend (DOCTYPE_MAIL);
	  break;

	case IDM_DECODE_FILE:
	  if (TestCodingBusy (NetDoc.hDocWnd, "Can't decode file"))
		break;

	  if (!DialogBoxParam (hInst, "WinVnDecodeArts", NetDoc.hDocWnd, lpfnWinVnDecodeArtsDlg, 0))
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  else
		DecodeFile (hWnd);
	  break;

	case IDM_ENCODE_FILE:
	  {
		TypAttachment attach;
		if (TestCodingBusy (NetDoc.hDocWnd, "Can't encode file"))
		  break;
		if (!DialogBoxParam (hInst, "WinVnEncode", NetDoc.hDocWnd, lpfnWinVnEncodeDlg, (LPARAM) & attach))
		  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		else
		  EncodeToFile (NetDoc.hDocWnd, &attach);
	  }
	  break;

	case IDM_RESET:
	  if (!CommBusy || SendingMail || SendingPost) {
		  if (MessageBox (NetDoc.hDocWnd,
				  "Are you sure you wish to reset the server protocol?\n"
				   "Any active communications and coding sessions will be aborted.",
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
			AbortAllComm ();
			hMenu = GetMenu (hWnd);
		    hSubMenu = GetSubMenu (hMenu, 1);
			EnableMenuItem (hSubMenu, IDM_CONNECT, ENABLE_MENU);
			
		  }
	  } else {	
		  if (MessageBox (NetDoc.hDocWnd,
				"Cannot reset server protocol while receiving server information.\n"
				"Do you wish to disconnect and reconnect?\n"
				"All active windows will be closed.",
				"Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
			Reconnect();
		  }
	  }
	  break;

	case IDM_RECONNECT:
	  if ((Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) &&
	  	  (!ConfirmDisconnect ||
		  (MessageBox (NetDoc.hDocWnd,
					"Are you sure you wish to disconnect and reconnect to the server?\n"
					   "All active windows will be closed.",
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES))) {
			Reconnect();
		}
		break;
		
	case IDM_SAVE_CONFIG:
	  WriteWinvnProfile ();
	  MessageBox (NetDoc.hDocWnd, "WinVN Configuration Saved", "WinVN Configuration", MB_OK);
	  break;

	case IDM_SAVE_WINDOW:
	  SaveWindowPositions ();
	  break;

	case IDM_FIND:
	  FindDoc = &NetDoc;
	  if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0])
		strcpy (FindDoc->SearchStr, LastGroupNameFind);

	  if (DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg)) {
		found = DoFind (TRUE, GroupListMultiSelect);
		if (!found) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, NetDoc.SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "Not found", MB_OK);
		}
		else {
		  strcpy (LastGroupNameFind, FindDoc->SearchStr);
		  if (LineIDtoLinePtr (FindDoc->FindLineID, FindDoc, &BlockPtr, &LinePtr)) {
			SetSelections (FindDoc, LinePtr);
			if (GroupListMultiSelect) {
			  /* set the active and anchor line IDs = FindLineID */
			  FindDoc->AnchorLineID = FindDoc->FindLineID;
			}
		  }
		}
	  }
	  break;

	case IDM_FIND_NEXT_SAME:
	  FindDoc = &NetDoc;
	  continueFind = TRUE;
	  if (!FindDoc->SearchStr[0]) {
		if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0])
		  strcpy (FindDoc->SearchStr, LastGroupNameFind);
		continueFind = DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
	  }

	  if (continueFind && FindDoc->SearchStr[0]) {
		found = DoFind (!FindDoc->hFindBlock && !FindDoc->FindLineID, GroupListMultiSelect);
		if (!found) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, NetDoc.SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "No more occurrences", MB_OK);
		}
		else {
		  if (LineIDtoLinePtr (FindDoc->FindLineID, FindDoc, &BlockPtr, &LinePtr)) {
			SetSelections (FindDoc, LinePtr);
			if (GroupListMultiSelect) {
			  /* set the active and anchor line IDs = FindLineID */
			  FindDoc->AnchorLineID = FindDoc->FindLineID;
			}
		  }
		}
	  }
	  break;

	case ID_ABOUT:
	  lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
	  DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
	  FreeProcInstance ((FARPROC) lpProcAbout);
	  break;

	case IDM_HELP:
	  MakeHelpPathName (mybuf, MAXINTERNALLINE);
	  WinHelp (NetDoc.hDocWnd, mybuf, HELP_INDEX, 0L);
	  break;

	case IDM_MAIL:
	  (MailCtrl.fnMlWinCreate) (hWnd, (TypDoc *) NULL, DOCTYPE_MAIL);
	  break;

	case IDM_LOGOUT:
	  (MailCtrl.fnMlLogout) (hWnd);
	  break;

	case IDM_SHOW_VERSION:
	  show_version_strings (hWnd);
	  break;

	case IDM_POST:
	  /*
	   *  DialogBoxParam (hInst, "WinVnGeneric", hWnd,
	   *                  lpfnWinVnGenericDlg, (LPARAM) (char far *) "Newsgroup(s):");
	   */

	  /* NewsgroupsPtr = (char *) NULL; */
	  *groupbuf = '\0';
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_GET_GROUPNAMES, TRUE);
	  NewsgroupsPtr = &groupbuf[0];
	  CreateComposeWnd (hWnd, (TypDoc *) NULL, DOCTYPE_POSTING);
	  break;
	}

  default:
	if (GetFocus () == hWnd)
	  SetFocus (NetDoc.hDocWnd);
	return (DefWindowProc (hWnd, message, wParam, lParam));
	break;
  }

  if (GetFocus () == hWnd)
	SetFocus (NetDoc.hDocWnd);
  return (0);
}

/*
 * Local Variables:
 * tab-width: 4
 * end:
 */

/* Last line of WVUSENET.C */
@


1.73
log
@Fix bug with unitialized HMENU object during Reset
@
text
@d1 2752
a2752 2752
/********************************************************************
 *                                                                  *
 *  MODULE    :  WVUSENET.C                                         *
 *                                                                  *
 *  PURPOSE   : This file contains the window procedure and support *
 *              routines for WinVn's main window                    *
 *                                                                  *
 ********************************************************************/

/*
 * $Id: wvusenet.c 1.72 1995/03/29 00:33:26 dumoulin Exp $
 *
 */
#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <stdlib.h>
#include <assert.h>
#include "wvtb\wvtb.h"			/* for toolbar  */
#ifdef USE_3D_CONTROLS
#include "ctl3d.h"
#endif

/* This is for the special case where the user invoked the */
/* program with dolist=0, then turned it on.  Without checking */
/* this condition, the newsrc will be truncated to only those */
/* groups read.  SMR 930224 */

int started_with_no_dolist;
void show_version_strings (HWND);
void SetGroupActiveLines (void);
BOOL WarnColors (COLORREF c1, COLORREF c2, char *warnMsg);
BOOL SelectGroup (TypDoc far * Doc, TypGroup far * group, BOOL value);

static char groupbuf[MAXHEADERLINE];

/*--- FUNCTION: WinVnConfWndProc ---------------------------------------
 *
 *    Window procedure for the "Net" window.
 *    This window displays the names of the newsgroups, one per line.
 *
 *    Entry    The usual parameters for any window function.
 *
 *    Note:    We don't do anything until "Initialized" is TRUE.
 *             This way, the user won't try to perform functions
 *             while the communications are still being set up.
 */

long WINAPI
WinVnConfWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 unsigned message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  PAINTSTRUCT ps;				/* paint structure          */
  POINT ptCursor;

  HDC hDC;						/* handle to display context */
  HWND hWndView;
  RECT myRect;					/* selection rectangle                */

  int j;
  unsigned int i;
  int found;
  char far *lpsz;
  TypLine far *LinePtr, far * GroupLinePtr;
  TypBlock far *BlockPtr, far * GroupBlockPtr;
  HANDLE hBlock;
  TypDoc *MyDoc;
  unsigned int Offset;
  BOOL looping = TRUE;
  TypGroup far *MyGroup;

  int X, Y;
  int docnum;
  int newdoc;
  char mybuf[MAXINTERNALLINE];
  int OldSel = FALSE;
  int CtrlState;
  TypLineID MyLineID;

  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
    return(0);

  switch (message) {

/*
   case WM_DESTROY:
   SetHandleBkBrush (hWnd, GetStockObject (WHITE_BRUSH));
   break;
 */

  case WM_CREATE:

	MailInit (hWnd);

	/* Get handle to the hourglass cursor */

	hHourGlass = LoadCursor (NULL, IDC_WAIT);


	/* Fill in the Net document's window handle that we
	 * were unable to fill in in the call to InitDoc.
	 */
	NetDoc.hDocWnd = hWnd;
	InitBitmaps ();
	break;

  case WM_CHAR:
	/* Hitting CR on a group name is the same as double-clicking
	 * on the name.
	 */
	if (!Initializing) {
	  if (wParam == '\r') {

		if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &GroupBlockPtr, &GroupLinePtr))
			goto getgroup1;
		  else
			break;

		}
		else {
		  GetCursorPos (&ptCursor);
		  ScreenToClient (hWnd, &ptCursor);
		  X = ptCursor.x;
		  Y = ptCursor.y;
		  goto getgroup;
		}
	  }
	}
	break;

  case WM_KEYDOWN:
	/* See if this key should be mapped to a scrolling event
	 * for which we have programmed the mouse.  If so,
	 * construct the appropriate mouse call and call the mouse code.
	 * Also, F6 means switch to next window.
	 * (This latter should also be table-driven.)
	 */
	if (wParam == VK_F6) {
	  NextWindow (NetDoc.hWndFrame, NetDoc.DocType);
	}               				
	else if (!Initializing) {
	  switch (wParam) {
	  case VK_DOWN:
		if (GroupListMultiSelect) {
		  /* move one line down from Active line */
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			if (NextActiveLine (&BlockPtr, &LinePtr)) {
			  SetSelections (&NetDoc, LinePtr);
			  AdjustMidSc (BlockPtr, LinePtr);
			}
		  }
		}
		break;

	  case VK_UP:
		if (GroupListMultiSelect) {
		  /* move one line up from Active line */
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			if (PrevActiveLine (&BlockPtr, &LinePtr)) {
			  SetSelections (&NetDoc, LinePtr);
			  AdjustMidSc (BlockPtr, LinePtr);
			}
		  }
		}
		break;

	  case VK_HOME:
		if (GroupListMultiSelect) {
		  TopOfDoc (&NetDoc, &BlockPtr, &LinePtr);
		  SetSelections (&NetDoc, LinePtr);
		  AdjustMidSc (BlockPtr, LinePtr);
		}
		break;

	  case VK_END:
		if (GroupListMultiSelect) {
		  BottomOfDoc (&NetDoc, &BlockPtr, &LinePtr);
		  SetSelections (&NetDoc, LinePtr);
		  AdjustMidSc (BlockPtr, LinePtr);
		}
		break;

	  case VK_NEXT:
		if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			for (i = 0; i < NetDoc.ScYLines; i++) {
			  if (!NextActiveLine (&BlockPtr, &LinePtr))
				break;
			}
			SetSelections (&NetDoc, LinePtr);
			AdjustMidSc (BlockPtr, LinePtr);
		  }
		}
		break;

	  case VK_PRIOR:
		if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			for (i = 0; i < NetDoc.ScYLines; i++) {
			  if (!PrevActiveLine (&BlockPtr, &LinePtr))
				break;
			}
			SetSelections (&NetDoc, LinePtr);
			AdjustMidSc (BlockPtr, LinePtr);
		  }
		}
		break;

	  default:
		/* Based on the state of the Control key and the value
		 * of the key just pressed, look up in the array
		 * key2scroll to see if we should process this keystroke
		 * as if it were a mouse event.  If so, simulate the
		 * mouse event by sending the appropriate message.
		 */

		CtrlState = GetKeyState (VK_CONTROL) < 0;
		for (j = 0; j < NUMKEYS; j++) {
		  if (wParam == key2scroll[j].wVirtKey &&
			  CtrlState == key2scroll[j].CtlState) {
			SendMessage (hWnd, key2scroll[j].iMessage,
						 key2scroll[j].wRequest, 0L);
			break;
		  }
		}

	  }
	}
	break;

  case WM_LBUTTONDOWN:
	/*  Clicking the left button on a group name toggles the
	 *  selected/not selected status of that group.
	 *  Currently selected groups are displayed in reverse video.
	 */

	DragMouseAction = DRAG_NONE;
	if (!Initializing) {
	  BOOL status;
	  TypGroup *aGroup;

	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  /* make this behave more like a multi-select listbox (jlg) */
	  /* if click,       make the line the only one selected */
	  /* if ctrl-click,  add the line to the selection */
	  /* if shift-click, select from previously selected to current */
	  if (GroupListMultiSelect) {
		if (!(wParam & MK_CONTROL) && !(wParam & MK_SHIFT)) {
		  if (NetDoc.SelectedLines == 1) {
			LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr);
			SelectGroup (&NetDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)), FALSE);
		  }
		  else {
			ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
		  }
		}
	  }
	  if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) {
		aGroup = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
		status = SelectGroup (&NetDoc, aGroup, aGroup->Selected ? FALSE : TRUE);
		if (GroupListMultiSelect) {
		  NetDoc.ActiveLineID = LinePtr->LineID;
		  if (wParam & MK_SHIFT) {
			ForAllLines (&NetDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
		  }
		  else
			NetDoc.AnchorLineID = LinePtr->LineID;

		  if (!(wParam & MK_CONTROL) && !(wParam & MK_SHIFT))
			DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
		  else
			DragMouseAction = DRAG_NONE;
		}
		else
		  DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;

		GlobalUnlock (BlockPtr->hCurBlock);
		SetCapture (hWnd);		/* release capture on button up */

	  }

	  InvalidateRect (hWnd, NULL, FALSE);
	}
	break;

  case WM_LBUTTONUP:
	/*  Letting up on the left button on a group name 
	 *  gets us out of Dragging mode   */

	ReleaseCapture ();
	DragMouseAction = DRAG_NONE;
	break;

  case WM_MOUSEMOVE:
	/*  Code to drag the mouse and change the select/not selected
	 *  status of that group.
	 */

	if ((!Initializing) && (DragMouseAction != DRAG_NONE)) {
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  GetClientRect (hWnd, &myRect);
	  if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) {
		if (GroupListMultiSelect) {
		  NetDoc.ActiveLineID = LinePtr->LineID;
		  ForAllLinesOnScreen (&NetDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
		  GlobalUnlock (BlockPtr->hCurBlock);
		}
		else {
		  SelectGroup (&NetDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)),
					   (DragMouseAction == DRAG_SELECT) ? TRUE : FALSE);
		  GlobalUnlock (BlockPtr->hCurBlock);
		}
	  }
	  InvalidateRect (hWnd, NULL, FALSE);
	}
	break;

  case WM_LBUTTONDBLCLK:
	/*  Double-clicking on a group name creates a "Group"
	 *  window, whose purpose is to display the subjects
	 *  of the articles in the group.
	 */
	if (!Initializing) {
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	getgroup:;

	  if (CursorToTextLine (X, Y, &NetDoc, &GroupBlockPtr, &GroupLinePtr)) {

	  getgroup1:;

		if (MyDoc = (((TypGroup far *)
			  (((char far *) GroupLinePtr) + sizeof (TypLine)))->SubjDoc)) {

		  /* We already have a document containing the subjects */
		  /* of this group, so just activate it.                */

		  ShowWindow (MyDoc->hWndFrame, SW_SHOWNORMAL);
		  SetActiveWindow (MyDoc->hWndFrame);
		  SetFocus (MyDoc->hDocWnd);
		  GlobalUnlock (GroupBlockPtr->hCurBlock);
		  break;
		}
		if (TestCommBusy (hWnd, "Can't Retrieve Group")) {
		  GlobalUnlock (GroupBlockPtr->hCurBlock);
		  break;
		}

		/* At this point, we've determined that the subjects for
		 * this newsgroup are not in memory, and that it's OK
		 * to fetch them from the server.  (Comm line not busy.)
		 * Figure out whether a new window/document should be
		 * created for this group & set "newdoc" accordingly.
		 */
		newdoc = FALSE;
		if (ViewNew || !ActiveGroupDoc || !(ActiveGroupDoc->InUse)) {
		  found = FALSE;
		  for (docnum = 0; docnum < MAXGROUPWNDS; docnum++) {
			if (!GroupDocs[docnum].InUse) {
			  found = TRUE;
			  newdoc = TRUE;
			  CommDoc = &(GroupDocs[docnum]);
			  CommDoc->LongestLine = 0;
			  CommDoc->ScXOffset = 0;
			  break;
			}
		  }
		  if (!found) {
			MessageBox (hWnd, "You have too many group windows active.\n"
							  "Close one or deselect 'New window for each group'.", 
							  "Can't open new window", MB_OK | MB_ICONASTERISK);
			UnlockLine (GroupBlockPtr, GroupLinePtr, &hBlock, &Offset, &MyLineID);
			break;
		  }
		}
		else {
		  /* Must reuse old window for this group.           */
		  ActiveGroupDoc->LongestLine = 0;
		  ActiveGroupDoc->ScXOffset = 0;
		  CommDoc = ActiveGroupDoc;

		  LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset, CommDoc->ParentLineID, &BlockPtr, &LinePtr);
		  MyGroup = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
		  MyGroup->SubjDoc = (TypDoc *) NULL;

		  /* make the 'n' in the group-list go away */
		  MyGroup->HighestPrevSeen = MyGroup->ServerLast;
		  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);

		  UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
		  
		  UpdateSeenArts (CommDoc);
		  UnlinkArtsInGroup (CommDoc);
		  /* clear out contents of this document for reuse */
		  FreeDoc (CommDoc);
		}

		/* Create window title indicating we are retrieving text. */

		CommDoc->InUse = TRUE;
		lpsz = (char far *) (((char far *) GroupLinePtr) +
							 sizeof (TypLine) + sizeof (TypGroup));
		strcpy (mybuf, "Retrieving ");
		lstrcat (mybuf, lpsz);

		/* If necessary, create a new window.              */

		if (newdoc) {
		  int x, y, width, height;
		  char poschars[MAXINTERNALLINE];

		  /* Compute default screen position. */
		  x = 1;
		  y = 0;
		  width = (int) xScreen;
		  height = (int) (yScreen * 1 / 2);

		  /* If the screen position has been saved, use that instead. */
		  GetPrivateProfileString (ARTLIST, "GroupWindowPos", "!",
								   poschars, MAXINTERNALLINE, szAppProFile);
		  if (poschars[0] != '!') {
			sscanf (poschars, "%d,%d,%d,%d", &x, &y, &width, &height);
		  }
		  hWndView = CreateWindow ("WinVnViewFrame",
								   mybuf,
								   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
								   x + (int) docnum * CharWidth,	/* Initial X pos */
								   y + (int) docnum * LineHeight,
								   width,	/* Initial X Width */
								   height,
								   NULL,
								   NULL,
								   hInst,
								   NULL);

		  if (!hWndView)
			return (0);			/* ??? */

/*    SetHandleBkBrush (hWndView, hListBackgroundBrush); */
		  ShowWindow (hWndView, SW_SHOWNORMAL);
		}
		else {
		  hWndView = CommDoc->hWndFrame;
		  SetWindowText (hWndView, mybuf);
		  SetGroupMenus (CommDoc, DISABLE);
		}
		RcvLineCount = 0;

		/* Do some housekeeping:  Set group as selected,
		 * initialize empty document to hold subject lines,
		 * set focus to this document, set pointers linking
		 * this document and the subject line.
		 */
		SelectGroup (&NetDoc, (TypGroup far *) (((char far *) GroupLinePtr) +
												sizeof (TypLine)), TRUE);
		InitDoc (CommDoc, hWndView, &NetDoc, DOCTYPE_GROUP);
		PtrToOffset (GroupBlockPtr, GroupLinePtr, &(CommDoc->hParentBlock),
					 &(CommDoc->ParentOffset), &(CommDoc->ParentLineID));
		SetActiveWindow (CommDoc->hWndFrame);
		SetFocus (CommDoc->hDocWnd);

		((TypGroup far *) (((char far *) GroupLinePtr) + sizeof (TypLine)))
		  ->SubjDoc = CommDoc;
		GlobalUnlock (GroupBlockPtr->hCurBlock);
		InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);

		UpdateWindow (CommDoc->hDocWnd);
		InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);

		/* Deal with Comm-related stuff:  set FSA variables,
		 * send the GROUP command to NNTP.
		 */

		CommLinePtr = CommLineIn;
		CommBusy = TRUE;
		CommState = ST_GROUP_RESP;
		hSaveCursor = SetCursor (hHourGlass);
		ShowCursor (TRUE);


/* capture the mouse to the usenet window, so that we keep the hourglass */

/*       SetCapture (hWnd); */
		strcpy (mybuf, "GROUP ");
		lpsz = (char far *) GroupLinePtr + sizeof (TypLine) + sizeof (TypGroup);
		lstrcat (mybuf, lpsz);
		mylstrncpy (CurrentGroup, lpsz, MAXFINDSTRING);
		PutCommLine (mybuf);
	  }
	}
	break;

/* If we've lost the connection for some reason, kill the timer */
	/* that's banging on the socket. */
  case WM_TIMER:
	if ((CommState == ST_CLOSED_COMM) && (!Initializing)) {
	  KillTimer (hWnd, ID_TIMER);
	}
	else {
	  if (CommState != ST_CLOSED_COMM)
		DoCommInput ();
	};
	break;

  case WM_SIZE:
	GetClientRect (hWnd, &myRect);
	NetDoc.ScXWidth = myRect.right;
	NetDoc.ScYHeight = myRect.bottom;
	NetDoc.ScYLines = (RectHeight (myRect) - TopSpace) / LineHeight;
	NetDoc.ScXChars = (RectWidth (myRect) - SideSpace) / CharWidth;
	break;

  case WM_VSCROLL:
	if (!Initializing) {
	  ScrollIt (&NetDoc, wParam, lParam);
	}
	break;

  case WM_HSCROLL:
	if (!Initializing) {
	  HScrollIt (&NetDoc, wParam, lParam);
	}
	break;

  case WM_PAINT:
	{
	  HANDLE hBlock;
	  SIZE sz;
	  unsigned int Offset, MyLen, indicatorwidth;
	  int VertLines, HorzChars;
	  int EndofDoc = FALSE;
	  int RangeHigh, CurPos;
	  int Xtext;
	  char far *textptr;
	  char *cptr, *cptr2;
	  char indicator;
	  char indcptr[128];
	  char group_name[MAXINTERNALLINE];
	  char init_msg[60];
	  TypGroup far *MyGroup;
	  COLORREF MyColors[4], MyBack[4];
/* DWORD Rop; */
	  RECT myRect, aRect;
	  int MyColorMask = 1, PrevColorMask = MyColorMask;
	  HBRUSH hOldBrush;
	  HANDLE hOldFont;
#define SUBSCR_MASK 1
#define SELECT_MASK 2

	  hDC = BeginPaint (hWnd, &ps);
	  GetClientRect (hWnd, &myRect);

	  /* If still initializing comm link, put out a message    */
	  /* to that effect.                                       */

	  switch (Initializing) {
	  case INIT_NOT_CONNECTED:
		cptr = "Not connected";
		cptr2 = "to news server";

		goto display_msg;

	  case INIT_ESTAB_CONN:
		cptr = "Establishing link";
		cptr2 = "to news server...";

		goto display_msg;

	  case INIT_READING_NEWSRC:
		cptr = "Reading your log";
		cptr2 = "(\"newsrc\")...";
		goto display_msg;

	  case INIT_SCANNING_NETDOC:
		cptr = "Creating hash table";
		cptr2 = "from current groups...";
		goto display_msg;

	  case INIT_GETTING_LIST:
		sprintf (mybuf, "Receiving %uth newsgroup", RcvLineCount);
		cptr = mybuf;
		cptr2 = "name from server...";

	  display_msg:;
		hOldFont = SelectObject (hDC, hWinVnFont);
		SetBkColor (hDC, ListBackgroundColor);
		if (IsDark(ListBackgroundColor))
		  SetTextColor (hDC, RGB (200, 200, 200));	/* if background is black, make text white */

		else
		  SetTextColor (hDC, RGB (0, 0, 0));	/* otherwise text is black */
/*
		hOldBrush = SelectObject (hDC, hListBackgroundBrush);
		PatBlt (hDC, 0, 0, myRect.right, myRect.bottom, PATCOPY);
		SelectObject (hDC, hOldBrush);
*/
		X = SideSpace + CharWidth;
		Y = LineHeight;
		strcpy (init_msg, cptr);
		strcat (init_msg, "    ");
		j = strlen (init_msg);
		TextOut (hDC, X, Y, init_msg, j);
		strcpy (init_msg, cptr2);
		strcat (init_msg, "    ");
		j = strlen (init_msg);
		TextOut (hDC, X, Y + WinVnLineHeight, init_msg, j);
		SelectObject (hDC, hOldFont);
		break;

	  case INIT_READY:
		/* this state happens when all connection prep is finished 
		 * and we are ready to begin normal WinVn user operation
		 */
		SetUserMenus (&NetDoc, ENABLE);
		Initializing = INIT_DONE;
		/* fall into INIT_DONE */

	  case INIT_DONE:
		VertLines = NetDoc.ScYLines;
		HorzChars = NetDoc.ScXChars;

		MyColors[0] = NetUnSubscribedColor;		/* unsubscribed/unselected     */

		MyBack[0] = ListBackgroundColor;
		MyColors[1] = NetSubscribedColor;	/* subscribed/unselected */

		MyBack[1] = ListBackgroundColor;

		if (UseInverseSelections) {
		  MyColors[2] = ListBackgroundColor;	/* unsubscribed/selected */

		  MyBack[2] = NetUnSubscribedColor;
		  MyColors[3] = ListBackgroundColor;	/* subscribed/selected */

		  MyBack[3] = NetSubscribedColor;
		  MyColors[2] = ListBackgroundColor;	/* unsubscribed/selected */

		  MyBack[2] = NetUnSubscribedColor;
		  MyColors[3] = ListBackgroundColor;	/* subscribed/selected */

		  MyBack[3] = NetSubscribedColor;
		}
		else {
		  if (NetUnSubscribedColor == RGB (0, 0, 0) ||	/* unsub/selected */
			   ListBackgroundColor == RGB (0, 0, 0) && IsBright (NetUnSubscribedColor))
			MyColors[2] = ListBackgroundColor;
		  else
			MyColors[2] = NetUnSubscribedColor;

		  if (NetSubscribedColor == RGB (0, 0, 0) ||	/* sub/selected */
			   ListBackgroundColor == RGB (0, 0, 0) && IsBright (NetSubscribedColor))
			MyColors[3] = ListBackgroundColor;
		  else
			MyColors[3] = NetSubscribedColor;

		  if (ListBackgroundColor == RGB (0, 0, 0))
			MyBack[2] = RGB (200, 200, 200);
		  else
			MyBack[2] = RGB (0, 0, 0);
		  MyBack[3] = MyBack[2];
		}

		SetTextColor (hDC, MyColors[MyColorMask]);
		SetBkColor (hDC, MyBack[MyColorMask]);

		hOldFont = SelectObject (hDC, hListFont);

		GetTextExtentPoint (hDC, "n", 2, &sz);	/* was a '*', now will be 'u' or 'n' */
		indicatorwidth = sz.cx * 7 * 7;

		/* Update the scroll bar thumb position.                 */

		RangeHigh = NetDoc.ActiveLines - VertLines;
		if (RangeHigh < 0) {
		  RangeHigh = 0;
		  NetDoc.TopLineOrd = 0;
		  NetDoc.TopScLineID = 0;
		  NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
		  NetDoc.TopScOffset = sizeof (TypBlock);
		  LockLine (NetDoc.hFirstBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
					&BlockPtr, &LinePtr);
  		  AdvanceToActive (&BlockPtr, &LinePtr);
		  VertLines = NetDoc.ActiveLines;
		} else {
		  LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
					&BlockPtr, &LinePtr);
		}

		CurPos = NetDoc.TopLineOrd;
		if (CurPos < 0)
		  CurPos = 0;

		SetScrollRange (hWnd, SB_VERT, 0, RangeHigh,
		NetDoc.ThumbTracking ? FALSE : TRUE);
		/* thumb pos is updated at end of thumb track */
		if (!NetDoc.ThumbTracking) {
			SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
		}
        
		RangeHigh = NetDoc.LongestLine - NetDoc.ScXChars;
		if (RangeHigh < 0) {
		  RangeHigh = 0;
		  NetDoc.ScXOffset = 0;
		}
		SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
		SetScrollPos (hWnd, SB_HORZ, NetDoc.ScXOffset, TRUE);

		/* Loop through the lines, painting them on the screen. */

		X = SideSpace - NetDoc.ScXOffset * (CharWidth + 1);
		Xtext = X + indicatorwidth;
		Y = StartPen;
		if (LinePtr->length != END_OF_BLOCK)
		  do {
			MyGroup = ((TypGroup far *)
					   ((char far *) LinePtr + sizeof (TypLine)));
			MyLen = MyGroup->NameLen;
			textptr = (char far *) LinePtr + sizeof (TypLine) +
			  sizeof (TypGroup);

			/* Display this line only if it is active. */
			if (LinePtr->active) {

			  /* Figure out the color of this line.                 */

			  if (MyGroup->Subscribed) {
				if (MyGroup->Selected)
				  MyColorMask = 3;
				else
				  MyColorMask = 1;
			  }
			  else {
				if (MyGroup->Selected)
				  MyColorMask = 2;
				else
				  MyColorMask = 0;
			  }

			  if (MyColorMask != PrevColorMask) {
				SetTextColor (hDC, MyColors[MyColorMask]);
				SetBkColor (hDC, MyBack[MyColorMask]);
				PrevColorMask = MyColorMask;
			  }

			  /* Figure out what indicator character to use. */

			  indicator = ' ';
			  if (NetDoc.FindLineID == LinePtr->LineID) {
				indicator = '>';
			  }
			  else if (MyGroup->HighestPrevSeen) {
				if (MyGroup->ServerLast > MyGroup->HighestPrevSeen) {
				  indicator = 'n';	/* was a '*' */
				}
				else {
				  /* check if there are unread articles - jlg 4/9/94 */
#if 0
				  RangePtr = (TypRange far *) ((char far *) MyGroup + RangeOffset (MyGroup->NameLen));
				  if (MyGroup->nRanges) {
					HighestRead = RangePtr[MyGroup->nRanges - 1].Last;
					if (MyGroup->ServerLast > HighestRead && MyGroup->ServerEstNum > 0)
					  indicator = 'u';	/* was a '*' */
				  }
#endif
				  if (MyGroup->NumUnread && MyGroup->ServerEstNum > 0) {
					  indicator = 'u';
				  }
				}
			  }

			  mylstrncpy (group_name, textptr, MyGroup->NameLen + 1);

			  if (MyGroup->Determined == FALSE)
				strcpy (str, "-");
			  else
				sprintf (str, "%5lu", MyGroup->ServerEstNum);

			  sprintf (indcptr, "%c %5s %s ", indicator, str, group_name);

			  /* Now write out the line.                            */

			  MyLen = strlen (indcptr);

			  SetRect (&aRect, 0, Y, myRect.right, Y + LineHeight);

			  ExtTextOut (hDC, X, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
						  indcptr, MyLen, (LPINT) NULL);

			  if (NetDoc.ActiveLineID == -1L)
				NetDoc.ActiveLineID = LinePtr->LineID;
			  if (NetDoc.ActiveLineID == LinePtr->LineID && GroupListMultiSelect)
				DrawFocusRect (hDC, &aRect);

			  Y += LineHeight;
			  --VertLines;
			}					/* end if LinePtr->active */
		  }
		  while (VertLines > 0 && NextLine (&BlockPtr, &LinePtr));

		SelectObject (hDC, hOldFont);

/*       SetTextColor (hDC, MyColors[1]); */
		/*       SetBkColor (hDC, MyBack[1]); */
		/*       SelectObject (hDC, UnSelectedBrush); */

		/* Blank out bottom and top of screen */
		hOldBrush = SelectObject (hDC, hListBackgroundBrush);
		PatBlt (hDC, 0, Y, myRect.right - 1, myRect.bottom - Y, PATCOPY);
		PatBlt (hDC, 0, 0, myRect.right - 1, TopSpace, PATCOPY);
		SelectObject (hDC, hOldBrush);
		UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	  }
	  EndPaint (hWnd, &ps);
	  break;
	}

  case WM_ENDSESSION:

	WinHelp (NetDoc.hDocWnd, LFN_HELP, HELP_QUIT, 0L);
	/*CloseComm (CommDevice); */

	break;

  case WM_COMMAND:
	return (SendMessage (NetDoc.hWndFrame, message, wParam, lParam));


  default:
	return (DefWindowProc (hWnd, message, wParam, lParam));
  }
  return (0);
}

void
SetSelections (TypDoc * MyDoc, TypLine far * LinePtr)
{
  TypLine far *deselPtr;
  TypBlock far *BlockPtr;
  TypLineID prevActive;

  if (GetKeyState (VK_SHIFT) >= 0) {	/* shift is NOT pressed  */
	prevActive = MyDoc->ActiveLineID;
	MyDoc->ActiveLineID = LinePtr->LineID;
	/* clear select status of all lines */
	if (MyDoc->SelectedLines == 1) {
	  LineIDtoLinePtr (prevActive, MyDoc, &BlockPtr, &deselPtr);
	  SelectGroup (MyDoc, (TypGroup far *) (((char far *) deselPtr) + sizeof (TypLine)), FALSE);
	}
	else {
	  ForAllLines (MyDoc, SetLineFlag, LINE_FLAG_SET, FALSE);
	}
	/* set only current line selected */
	SelectGroup (MyDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)), TRUE);
	/* set the current line to be the anchor */
	MyDoc->AnchorLineID = LinePtr->LineID;
  }
  else
	/* shift is pressed  */
  {
	/* select all lines between ActiveLineID and AnchorLineID inclusive */
	MyDoc->ActiveLineID = LinePtr->LineID;
	ForAllLines (MyDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
  }
}

/* if enable is true then enable connect, disable disconnect */
/* disconnect is managed in INTITMENU */
void
SetConnectMenuItems (TypDoc FAR * DocPtr, int enable)
{
  HMENU hMenu, hSubMenu;
  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */

  EnableMenuItem (hSubMenu, IDM_CONNECT,  enable ? ENABLE_MENU : DISABLE_MENU);
  EnableMenuItem (hSubMenu, IDM_RESET, ENABLE_MENU);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, TRUE);
  SendMessage (DocPtr->hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_CONNECT, enable ? FALSE : TRUE);

  /* updates the exit menus/button */
  SendMessage (NetDoc.hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
}

void
SetMainMailMenu (TypDoc FAR * DocPtr)
{
  HMENU hMenu, hSubMenu;

  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */

  EnableMenuItem (hSubMenu, IDM_LOGOUT, MailCtrl.enableLogout);

  hSubMenu = GetSubMenu (hMenu, 2);		/* utilities menu */

  EnableMenuItem (hSubMenu, IDM_MAIL, MailCtrl.enableMail);

  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_MAIL,
			   (MailCtrl.enableMail == MF_ENABLED));
}

void
SetUserMenus (TypDoc FAR * DocPtr, int enable)
{
  HMENU hMenu, hSubMenu;
  UINT mode;

  assert (DocPtr->hWndFrame != 0);

  mode = (enable == ENABLE) ? ENABLE_MENU : DISABLE_MENU;

  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 1);		/* group menu */

  EnableMenuItem (hSubMenu, IDM_FIND, mode);
  EnableMenuItem (hSubMenu, IDM_FIND_NEXT_SAME, mode);

  hSubMenu = GetSubMenu (hMenu, 2);		/* utilities menu */
  EnableMenuItem (hSubMenu, IDM_POST, mode);

  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */
  EnableMenuItem (hSubMenu, IDM_RESET, mode);

  /* connect item is enabled when all else is disabled and v.v. */
  SetConnectMenuItems (DocPtr, !enable);

  SetMainMailMenu (DocPtr);

  /* set toolbar buttons */
  mode = (enable == ENABLE) ? TRUE : FALSE;

  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_RESET, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND_NEXT_SAME, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_POST, mode);
}

/*---  FUNCTION: About ---------------------------------------------------
 *
 *  Process messages for "About" dialog box
 */

BOOL WINAPI
About (hDlg, message, wParam, lParam)
	 HWND hDlg;
	 unsigned message;
	 WORD wParam;
	 LONG lParam;
{
  switch (message) {
  case WM_INITDIALOG:
	SetDlgItemText (hDlg, IDD_VERSION_NUMBER, (LPSTR)WINVN_VERSION);
	return (TRUE);

  case WM_COMMAND:
	if (wParam == IDOK) {
	  EndDialog (hDlg, TRUE);
	  return (TRUE);
	}
	break;
  }
  return (FALSE);
}

/* ----------------------------------------------------
 * Warn user if colors overlap
 * jsc 9/30/94
 */
BOOL
WarnColors (COLORREF c1, COLORREF c2, char *warnMsg)
{
  if (c1 == c2) {
	sprintf (str, "This color is the same as the %s color.\nSome displays may become unreadable.", warnMsg);
	MessageBox (NetDoc.hDocWnd, str, "Duplicate Color", MB_OK | MB_ICONINFORMATION);
	return TRUE;
  }
  return FALSE;
}

BOOL
SelectGroup (TypDoc far * Doc, TypGroup far * group, BOOL value)
{
  if (group->Selected != value) {
	group->Selected = value;
	if (value)
	  (Doc->SelectedLines)++;
	else
	  (Doc->SelectedLines)--;
  }
  return value;
}

/* --- function CrackGroupLine --------------------------------------

 *  Given a line from a .newsrc file, create a text line containing
 *  a structure of type TypGroup containing the same information.
 *  The line is zero-terminated upon input.
 *
 *    Entry    buf      points to a zero-terminated line from .newsrc.
 *             lineptr  points to a place to put the converted textline.
 *
 *    Exit     The line pointed to by "lineptr" now is a TypLine textline
 *             containing a structure of type TypGroup, containing the
 *             information in the input line from .newsrc.
 *
 *  Returns TRUE iff group was subscribed to.
 */
BOOL
CrackGroupLine (buf, lineptr)
	 char *buf;
	 TypLine *lineptr;
{
  char *grname = (char *) lineptr + sizeof (TypLine) + sizeof (TypGroup);
  TypGroup *group = (TypGroup *) ((char *) lineptr + sizeof (TypLine));
  TypRange *RangePtr;
  BOOL MoreNums;
  unsigned int MyLength;

  group->Subscribed = FALSE;
  group->Selected = FALSE;
  group->Determined = FALSE;
  group->NameLen = 0;
  group->SubjDoc = (TypDoc *) NULL;
  group->ServerEstNum = 0;
  group->ServerFirst = 0;
  group->ServerLast = 0;
  group->HighestPrevSeen = 0;
  group->nRanges = 0;
  group->header_handle = (HANDLE) NULL;
  group->NumUnread = 0;

  /* Copy group name to output line.                               */

  while (*buf && *buf != ':' && *buf != '!') {
	*(grname++) = *(buf++);
	(group->NameLen)++;
  }
  *(grname++) = '\0';

  if (!(*buf)) {
	/* Ran off end of line without seeing ':' or '!'.  Error.      */
  }
  else {
	if (*buf == ':') {
	  group->Subscribed = TRUE;
	}
	buf++;
  }

  /* Look for the highest article number previously seen, in an
   * entry of form "s" followed by a number.
   */

  while (*buf && *buf == ' ' && *buf != '\n')
	buf++;
  if (*buf == 's') {
	buf++;
	GetNum (&buf, &(group->HighestPrevSeen));
  }

  /* Convert the article number ranges to the internal numeric
   * form we use in WinVN.
   */

  RangePtr = (TypRange *) ((char *) lineptr + sizeof (TypLine) +
						   RangeOffset (group->NameLen));

  RangePtr->Last = RangePtr->First = 0;

  /* allow lines like this, with no range data: */
  /* news.group:  */
  /* */
  MoreNums = ((*buf) == '\n' ? FALSE : TRUE);
  while (MoreNums) {
	while (*buf && (*buf == ' ' || *buf == ','))
	  buf++;
	if (GetNum (&buf, &(RangePtr->First))) {
	  /* We have the first number in a range.                     */
	  (group->nRanges)++;
	  RangePtr->Last = RangePtr->First;
	  if (*buf == '-') {
		buf++;
		if (GetNum (&buf, &(RangePtr->Last))) {
		  RangePtr++;
		  /* at this point, we are positioned just after a range */
		}
		else {
		  RangePtr->Last = RangePtr->First;
		  MoreNums = FALSE;
		}
	  }
	  else if (*buf == ',') {
		/* We have a single number "n"; interpret as the range "n-n".
		 */
		RangePtr++;
	  }
	  else {
		/* That must have been the last number.                  */
		MoreNums = FALSE;
	  }
	}
	else {
	  MoreNums = FALSE;
	}
  }
  if (group->nRanges == 0)
	(group->nRanges)++;

  MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) +
	sizeof (TypRange) * (group->nRanges) + sizeof (int);

  lineptr->length = MyLength;
  lineptr->LineID = NextLineID++;
  *(int *) ((char *) lineptr + MyLength - sizeof (int)) = MyLength;

  return (group->Subscribed);
}

/*-- function CursorToTextLine ----------------------------------------
 *
 *   Routine to locate a text line in a document, based on the
 *   cursor position.  Used to figure out which line is being selected
 *   when a user clicks a mouse button.
 *
 *   Entry    X, Y    are the position of the cursor.
 *            DocPtr  points to the current document.
 *
 *   Exit     *LinePtr points to the current line, if one was found.
 *            *BlockPtr points to the current block, if found.
 *            Function returns TRUE iff a line was found that corresponds
 *              to the cursor position.
 */
BOOL
CursorToTextLine (X, Y, DocPtr, BlockPtr, LinePtr)
	 int X;
	 int Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int found;
  int SelLine;

  if (Y < TopSpace || (unsigned) Y > TopSpace + DocPtr->ScYLines * LineHeight ||
	  X < SideSpace) {
	/* Cursor is in no-man's-land at edge of window.               */
	found = FALSE;
  }
  else {
	found = TRUE;
	SelLine = (Y - TopSpace) / LineHeight;
	if ((unsigned int) SelLine >= DocPtr->ScYLines)		/* double-check */
	  return (FALSE);

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);
	AdvanceToActive (BlockPtr, LinePtr);

	for (found = TRUE, il = 0; il < SelLine;) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		found = FALSE;			/* ran off end of document */
		break;
	  }
	  else if ((*LinePtr)->active) {
		il++;
	  }
	}
  }
  return (found);
}

extern char * GetFileExtension (char *ext, char *fileName);

/*-- function ReadNewsrc ----------------------------------------------
 *
 *    Reads NEWSRC into the Net document.
 *    This routine opens NEWSRC, reads & parses the lines into the NetDoc
 *    document, and closes the file.  One call does it all.
 *
 *    Entry    The NetDoc document is assumed to be initialized.
 *
 *    Exit     Returns TRUE if all went well, else zero.
 */
int
ReadNewsrc ()
{
  TypBlock far *BlockPtr;
  TypLine *LinePtr, far * GroupPtr;
  HANDLE hBlock;
  HFILE hRetCode;
  unsigned int Offset;
  TypLineID MyLineID;
  char mybuf[TEMPBUFSIZE];
  TypMRRFile *MRRFile;
  int returned;

  LockLine (NetDoc.hCurAddBlock, NetDoc.AddOffset, NetDoc.AddLineID, &BlockPtr, &GroupPtr);
/*  NetDoc.hDocWnd = NetDoc.hDocWnd; */

  if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, TEMPBUFSIZE)) == NULL) {
	return 0;
  }

#if 0
  env_var = getenv ("WINVN");	/* get path to winvn.ini */
  if (lstrlen (env_var)) {
	lstrcpy (newsrc_filename, env_var);
	if (*(newsrc_filename + lstrlen (newsrc_filename) - 1) == '\\')
	  lstrcat (newsrc_filename, "newsrc");
	else
	  lstrcat (newsrc_filename, "\\newsrc");
  }
  else {
	MessageBox (NetDoc.hDocWnd, "Environment variable WINVN not set.", "Fatal Error", MB_OK | MB_ICONEXCLAMATION);
	return (0);
  }

#endif
  
  hRetCode = MRROpenFile (szNewsSrc, OF_READ, &MRRFile);
  if ((int) hRetCode <= 0) {
	return FALSE;
  }
  else {
	/* Loop to read lines, convert them to internal format, and
	 * insert them into the NetDoc document.
	 */

	LinesInRC = 0;
	while ((returned = MRRReadLine (MRRFile, mybuf, TEMPBUFSIZE)) > (-1)) {
	  mybuf[returned] = '\0';
	  if (CrackGroupLine (mybuf, LinePtr)) {
		NetDoc.CountedLines++;
	  }
	  AddLine (LinePtr, &BlockPtr, &GroupPtr);
	  LinesInRC++;
	}
	MRRCloseFile (MRRFile);
  }

  /* Change the title of the Net document.  I suppose that,
   * strictly speaking, this oughtn't be done in this routine.
   */
  SetNetDocTitle ();
  UnlockLine (BlockPtr, GroupPtr, &hBlock, &Offset, &MyLineID);

  NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
  NetDoc.TopScOffset = sizeof (TypBlock);
  NetDoc.TopScLineID = 0L;
  NetDoc.LongestLine = 0;
  /* Mark lines active or inactive according to ShowUnsubscribed. */
  SetGroupActiveLines ();
  /* we'll check this during WriteNewsrc(). SMR 930224 */
  started_with_no_dolist = !DoList;

  GlobalFreePtr (LinePtr);

  return (TRUE);
}

char *ltoa ();

/*--- function WriteNewsrc ---------------------------------------------
 *
 *  Write out a NEWSRC file, based on the information in the
 *  NetDoc document.  Use the standard Unix "rn" format for .newsrc.
 *
 *    Entry    no parameters
 *             NetDoc   has the group information.
 *
 *    Exit     The NEWSRC file has been written.
 */
void
WriteNewsrc ()
{
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;
  HANDLE hBlock;
  unsigned int Offset;
  TypLineID MyLineID;
  char far *fromptr;
  char *toptr;
  char *NewsLine;
  HFILE hRetCode;
  TypMRRFile *MRRFile;
  int nranges;
  TypGroup far *Group;
  TypRange far *RangePtr;
  BOOL firstrange;
  char newsrc_temp[MAXFILENAME];
  char newsrc_extension[MAXFILENAME];
  int extension_length;

  /* conditions in which we do not want to write it out */
  if (Initializing == INIT_READING_NEWSRC ||
	  Initializing == INIT_SCANNING_NETDOC ||
	  Initializing == INIT_GETTING_LIST) {
	return;
  }

  LockLine (NetDoc.hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr);

  if ((NewsLine = (char *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) == NULL) {
	MessageBox (NetDoc.hDocWnd, "Memory allocation failure.  Newsrc not saved.", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
	return;
  }


  strcpy (newsrc_temp, szNewsSrc);
  GetFileExtension (newsrc_extension, szNewsSrc);
  extension_length = strlen (newsrc_extension);

  if (extension_length != 0) {
	strcpy (newsrc_temp + (strlen(newsrc_temp) - (extension_length+1)), ".tmp");
  } else {
	strcpy (newsrc_temp + strlen(newsrc_temp), ".tmp");
  }

 /*  hRetCode = MRROpenFile (szNewsSrc, OF_CREATE, &MRRFile);  */
 /*  hRetCode = MRROpenFile (szNewsSrc, OF_SHARE_EXCLUSIVE, &MRRFile); */
  hRetCode = MRROpenFile (newsrc_temp, OF_WRITE, &MRRFile); 
  if ((int) hRetCode < 0) {
	MessageBox (NetDoc.hDocWnd, "Could not write to newsrc file", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
  }
  else {
	do {
	  toptr = NewsLine;
	  Group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

	  /* Remove groups only if we performed a LIST command and */
	  /* the server did not have them.  Otherwise if !did_list, the */
	  /* newsrc will be practically empty! */

	  /* Jeeeeez!!  I'm fixing this AGAIN! */
	  /* DoList is not a boolean. SMR 930610 */

	  if ((Group->ServerFirst || Group->ServerEstNum) || !did_list) {
		/* Copy group name                                          */
		fromptr = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
		while (*(toptr++) = *(fromptr++));
		toptr--;

		/* Affix : or ! depending upon whether subscribed.          */
		*(toptr++) = (char) (Group->Subscribed ? ':' : '!');
		*(toptr++) = ' ';

		/* If we know the highest article number on the server,
		 * output it preceded by an "s".
		 */
		if (Group->ServerLast || Group->HighestPrevSeen) {
		  *(toptr++) = 's';
		  if (Group->ServerLast)
			ltoa ((unsigned long) Group->ServerLast, toptr, 10);
		  else
			ltoa ((unsigned long) Group->HighestPrevSeen, toptr, 10);
		  while (*toptr)
			toptr++;
		  *(toptr++) = ' ';
		}

		/* Affix ranges of articles read.                          */
		firstrange = TRUE;
		nranges = Group->nRanges;
		RangePtr = (TypRange far *) ((char far *) Group + RangeOffset (Group->NameLen));

		while ((nranges--) > 0) {
		  /* Write out ',' if not first range of articles.         */

		  if (!firstrange) {
			*(toptr++) = ',';
		  }
		  else {
			firstrange = FALSE;
		  }
		  /* Write out first article in a range.                   */

		  ltoa ((unsigned long) RangePtr->First, toptr, 10);
		  while (*toptr)
			toptr++;

		  /* If the range is of form "n-n", just put out "n"       */

		  if (RangePtr->First != RangePtr->Last) {
			/* Put out the hyphen in middle of range.                */
			*(toptr++) = '-';
			/* Put out the last article in a range.                  */
			ltoa ((unsigned long) RangePtr->Last, toptr, 10);
			while (*toptr)
			  toptr++;
		  }
		  RangePtr++;
		}
		MRRWriteLine (MRRFile, NewsLine, toptr - NewsLine);
	  }
	}
	while (NextLine (&BlockPtr, &LinePtr));
	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	MRRCloseFile (MRRFile);
	unlink (szNewsSrc);
	if (rename (newsrc_temp, szNewsSrc)) {
	  char error_string[256];
	  sprintf (error_string, "Could not rename temporary newsrc file '%s'", newsrc_temp);
	  MessageBox (NetDoc.hDocWnd, error_string, "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
	}
  }
  GlobalFreePtr (NewsLine);
}

/*--- function SetNetDocTitle -------------------------------------------
 *
 */
void
SetNetDocTitle ()
{
  char mybuf[120];

  sprintf (mybuf, "WinVN:  %u groups; %u subscribed", NetDoc.TotalLines,
		   NetDoc.CountedLines);
  SetWindowText (NetDoc.hWndFrame, mybuf);

}

/*--- function SetLineFlag --------------------------------------------
 *
 *  Set some flag in a line in a document.
 *
 *  Entry   Doc      points to the document.
 *          LinePtr  points to th line.
 *
 *  Exit    lFlag    says what to do.
 */
void
SetLineFlag (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  switch (wFlag) {
  case LINE_FLAG_SET:
	SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), wValue);
	break;
  }
}

void
SetExLineFlag (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  static BOOL extending = FALSE;

  switch (wFlag) {
  case LINE_FLAG_SET:
	if ((*LinePtr)->LineID == Doc->ActiveLineID ||
		(*LinePtr)->LineID == Doc->AnchorLineID) {
	  if (extending == TRUE) {
		SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), TRUE);
		extending = FALSE;
	  }
	  else {
		SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), TRUE);
		if (Doc->ActiveLineID == Doc->AnchorLineID)
		  extending = FALSE;
		else
		  extending = TRUE;
	  }
	}
	else
	  SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), extending);

	break;
  }
}

/*--- function GroupAction --------------------------------------------
 *
 *  Perform some action on a group that is specified by a pointer
 *  to a line in the Net document.
 *  Typically called for each line in the Net document by
 *  ForAllLines.
 *
 *  Entry   Doc      points to NetDoc
 *          LinePtr  points to a line in that document.
 *          lFlag    indicates what to do with that line.
 */
void
GroupAction (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  static char mybuf[MAXINTERNALLINE];

  switch (wFlag) {
  case GROUP_ACTION_SUBSCRIBE:
  case GROUP_ACTION_UNSUBSCRIBE:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Selected) {
	  ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Subscribed = wValue;
	  AddGroupToTable ((char far *) *LinePtr);
	  DeleteLine (BlockPtr, LinePtr);
	}
	break;

  case GROUP_ACTION_CHECK_ACTIVE:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Subscribed || ShowUnsubscribed) {
	  (*LinePtr)->active = TRUE;
	  (*BlockPtr)->NumActiveLines++;
	  NetDoc.ActiveLines++;
	  NetDoc.LongestLine = max (NetDoc.LongestLine, (unsigned)
			 ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
								->NameLen + GROUP_NAME_OFFSET);
	  if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		  ->Subscribed)
		NetDoc.CountedLines++;
	}
	else {
	  (*LinePtr)->active = FALSE;
	}
	break;

  case GROUP_ACTION_GET_GROUPNAMES:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Selected) {
	  ExtractTextLine (&NetDoc, *LinePtr,
					   mybuf, sizeof(mybuf));
	  if (strlen(groupbuf) + strlen(mybuf) + 2 < sizeof(groupbuf)) {
	    if (*groupbuf)
	      strcat(groupbuf, ",");
  	    strcat(groupbuf, mybuf);
	  }
	}
	break;
  }
}

/****************************************************************************

   FUNCTION:   MakeHelpPathName

   PURPOSE:    HelpEx assumes that the .HLP help file is in the same
          directory as the HelpEx executable.This function derives
          the full path name of the help file from the path of the
          executable.

   Taken from HELPEX.C, from the MS Windows SDK.

****************************************************************************/

void
MakeHelpPathName (szFileName, maxchars)
	 char *szFileName;
	 int maxchars;
{
  char *pcFileName;
  int nFileNameLen;

  nFileNameLen = GetModuleFileName (hInst, szFileName, maxchars);
  pcFileName = szFileName + nFileNameLen;

  while (pcFileName > szFileName) {
	if (*pcFileName == '\\' || *pcFileName == ':') {
	  *(++pcFileName) = '\0';
	  break;
	}
	nFileNameLen--;
	pcFileName--;
  }

  if ((nFileNameLen + 13) < maxchars) {
	lstrcat (szFileName, LFN_HELP);
  }

  else {
	lstrcat (szFileName, "?");
  }

  return;
}


int
cursor_to_char_number (X, Y, DocPtr, BlockPtr, LinePtr)
	 int X;
	 int Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int SelLine;
  int charnum = -1;
  SIZE extent;

  char far *textptr;
  int textlen;
  HDC display_context;
  int iTopSpace, iSideSpace, iLineHeight, iCharWidth;
  HANDLE hOldFont;

  if (DocPtr->DocType == DOCTYPE_ARTICLE) {
	iTopSpace = ArtTopSpace;
	iSideSpace = ArtSideSpace;
	iLineHeight = ArtLineHeight;
	iCharWidth = ArtCharWidth;
  }
  else {
	iTopSpace = TopSpace;
	iSideSpace = SideSpace;
	iLineHeight = LineHeight;
	iCharWidth = CharWidth;
  }

  if (Y < iTopSpace || (unsigned) Y > iTopSpace + DocPtr->ScYLines * iLineHeight ||
	  X < iSideSpace) {
	return (-1);
  }
  else {
	SelLine = (Y - iTopSpace) / iLineHeight;

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);

	for (il = 0; il < SelLine; il++) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		return (-1);
		break;
	  }
	}
  }

  /* find the right character on the text line */
  textlen = ((TypText far *) ((char far *) (*LinePtr) +
							  sizeof (TypLine)))->NameLen;


  if (textlen) {
	textptr = (char far *) ((char far *) (*LinePtr) + sizeof (TypLine) + sizeof (TypText));
	display_context = GetDC (DocPtr->hDocWnd);

	if (isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
	  hOldFont = SelectObject (display_context, hFontArtQuote);
	}
	else {						/* prepare to print a normal line */
	  hOldFont = SelectObject (display_context, hFontArtNormal);
	}

	for (charnum = 1; charnum < textlen; charnum++) {
	  GetTextExtentPoint (display_context, (LPSTR) textptr, charnum, &extent);
	  if (extent.cx > (X + (int) DocPtr->ScXOffset * (iCharWidth + 1) - iSideSpace))
		break;
	}
	SelectObject (display_context, hOldFont);
	ReleaseDC (DocPtr->hDocWnd, display_context);
	return (charnum - 1);
  }

}
  long
lcursor_to_char_number (X, Y, DocPtr, BlockPtr, LinePtr)
	 long X;
	 long Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int SelLine;
  long charnum = -1;
  SIZE extent;

  char far *textptr;
  int textlen;
  HDC display_context;
  int iTopSpace, iSideSpace, iLineHeight, iCharWidth;
  HANDLE hOldFont;

  if (DocPtr->DocType == DOCTYPE_ARTICLE) {
	iTopSpace = ArtTopSpace;
	iSideSpace = ArtSideSpace;
	iLineHeight = ArtLineHeight;
	iCharWidth = ArtCharWidth;
  }
  else {
	iTopSpace = TopSpace;
	iSideSpace = SideSpace;
	iLineHeight = LineHeight;
	iCharWidth = CharWidth;
  }

  if (Y < iTopSpace || (unsigned) Y > iTopSpace + DocPtr->ScYLines * iLineHeight ||
	  X < iSideSpace) {
	return (-1);
  }
  else {
	SelLine = (int) (Y - iTopSpace) / iLineHeight;

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);

	for (il = 0; il < SelLine; il++) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		return (-1);
		break;
	  }
	}
  }

  /* find the right character on the text line */
  textlen = ((TypText far *) ((char far *) (*LinePtr) +
							  sizeof (TypLine)))->NameLen;


  if (textlen) {
	textptr = (char far *) ((char far *) (*LinePtr) + sizeof (TypLine) + sizeof (TypText));
	display_context = GetDC (DocPtr->hDocWnd);

	if (isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
	  hOldFont = SelectObject (display_context, hFontArtQuote);
	}
	else {						/* prepare to print a normal line */
	  hOldFont = SelectObject (display_context, hFontArtNormal);
	}

	for (charnum = 1; charnum < textlen; charnum++) {
	  GetTextExtentPoint (display_context, (LPSTR) textptr, (int) charnum, &extent);
	  if (extent.cx > (X + (int) DocPtr->ScXOffset * (iCharWidth + 1) - iSideSpace))
		break;
	}
	SelectObject (display_context, hOldFont);
	ReleaseDC (DocPtr->hDocWnd, display_context);
	return (charnum - 1);
  }

}

/*--- function SetGroupActiveLines --------------------------------------
 *
 *  Go through all the lines in the Net document, marking each
 *  as active or inactive according to whether the corresponding
 *  group is subscribed and whether we are displaying unsubscribed
 *  groups.
 *
 *  Entry:  NetDoc and ShowUnsubscribed are set properly.
 *
 *  Exit:   Each of the lines in NetDoc has had its "active" field
 *          set properly.
 */
void
SetGroupActiveLines ()
{
  NetDoc.ActiveLines = 0;
  NetDoc.CountedLines = 0;
  ForAllBlocks (&NetDoc, SetForBlock, BLOCK_ACTION_SET_ACTIVE, 0);
  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_CHECK_ACTIVE, 0);
}


/* stuff for showing version numbers of the files */

#include "version.h"
#include "version.c"

/*-- function VerListDlgProc ---------------------------------------
 *
 *  Dialog function to handle selection of Version List
 *                                  JD 8/3/94
 */

LRESULT CALLBACK
VerListDlgProc (HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  int j;
  char far *cptr;
  char szVersion[64];

  switch (iMessage) {
  case WM_INITDIALOG:
	sprintf (szVersion, "Version %s", (LPSTR) WINVN_VERSION);
	SetDlgItemText ((HWND) hDlg, IDD_VERSION_NUMBER, (LPSTR) szVersion);
	hVerDlgList = GetDlgItem (hDlg, IDD_VERSION_LISTBOX);
	SendMessage (hVerDlgList, WM_SETREDRAW, FALSE, 0L);

	cptr = 0;
	for (j = 0; j < (sizeof (version_string) / sizeof version_string[0]); j++) {
	  cptr = version_string[j];
	  SendMessage (hVerDlgList, LB_ADDSTRING, 0, (LONG) cptr);
	}
	SendMessage (hVerDlgList, WM_SETREDRAW, TRUE, 0L);
	return TRUE;
	break;

  case WM_COMMAND:
	switch (wParam) {
	case IDOK:
	  EndDialog (hDlg, TRUE);
	  break;

	case IDCANCEL:
	  EndDialog (hDlg, FALSE);
	  break;

	default:
	  return FALSE;
	}
	break;

  case WM_DESTROY:
	break;

  default:
	return FALSE;
	break;
  }
  return TRUE;
}

/*  Display Dialog Box to show version info   JD 8/4/94 */
void
show_version_strings (HWND hWnd)
{
  lpfnWinVnVersionListDlg = (DLGPROC) MakeProcInstance ((FARPROC) VerListDlgProc, (HINSTANCE) hInst);
  DialogBox (hInst, "WINVNVERSIONLIST", hWnd, lpfnWinVnVersionListDlg);
  FreeProcInstance ((FARPROC) lpfnWinVnVersionListDlg);

}


long WINAPI
WinVnConfFrameWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 unsigned message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  DLGPROC lpProcAbout;
  RECT myRect;					/* selection rectangle                */
  static HANDLE ToolBarLib;
  char mybuf[MAXINTERNALLINE];
  int found;
  PAINTSTRUCT ps;				/* paint structure          */
  HDC hDC;						/* handle to display context */
  int i, j, numComposeWnds;
  TBBUTTON tbButton[MAXTBBUTTONS];
  HMENU hMenu, hSubMenu;
  BOOL continueFind;
  TypLine far *LinePtr;
  TypBlock far *BlockPtr;

  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
    return 0;

  switch (message) {
#ifdef USE_3D_CONTROLS
  case WM_SYSCOLORCHANGE:
	Ctl3dColorChange ();
	break;
#endif

  case WM_SETFOCUS:
   SetCapsLockText(hWnd);
   SetNumLockText(hWnd);
   return (DefWindowProc (hWnd, message, wParam, lParam));
	break;

  case WM_SYSCOMMAND:
	switch (wParam) {
		case ID_ABOUT:
			lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
			DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
			FreeProcInstance ((FARPROC) lpProcAbout);
			break;

		case IDM_RESTORE_ALL:
			RestoreWindows();
			break;

		default:
			return (DefWindowProc (hWnd, message, wParam, lParam));
	}
	break;

  case WM_CREATE:
	GetClientRect (hWnd, &myRect);

/* add common controls for toolbar (jlg) */

	i = j = 0;
	tbButton[j].iBitmap = 0;	/* connect */

	tbButton[j].idCommand = IDB_TOGGLE_CONNECT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* reset */

	tbButton[j].idCommand = IDM_RESET;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* find */

	tbButton[j].idCommand = IDM_FIND;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* find next */

	tbButton[j].idCommand = IDM_FIND_NEXT_SAME;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* New Article */

	tbButton[j].idCommand = IDM_POST;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* New Mail */

	tbButton[j].idCommand = IDM_MAIL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* Save Configuration */

	tbButton[j].idCommand = IDM_SAVE_CONFIG;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* Toggle View Unsubscribed */

	tbButton[j].idCommand = IDB_TOGGLE_VIEW_UNSUB;
	if (ShowUnsubscribed == TRUE)
	  tbButton[j].fsState = TBSTATE_ENABLED | TBSTATE_CHECKED;
	else
	  tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* Exit */

	tbButton[j].idCommand = IDB_FASTEXIT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* About */

	tbButton[j].idCommand = ID_ABOUT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	NetDoc.hWndTB = CreateToolbar (hWnd,
								   WS_BORDER | WS_VISIBLE,
								   (WORD) GetMenu (hWnd),
								   i + 1,
								   hInst,
								   IDB_TOOLBAR,
								   tbButton,
								   j + 1);

	assert (NetDoc.hWndTB != NULL);

	NetDoc.hDocWnd = CreateWindow ("WinVn",
								   "WinVN -- Usenet News Reader",
								   WS_CHILD | WS_VSCROLL | WS_HSCROLL,
								   0,	/* Initial X position */
								   TOOLBARHEIGHT,	/* Initial Y position */
								   myRect.right - myRect.left,	/* Initial X width */
	(myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,	/* Initial Y height */
								   hWnd,
								   NULL,
								   hInst,
								   NULL);

	assert (NetDoc.hDocWnd != NULL);

	SetHandleBkBrush (NetDoc.hDocWnd, hListBackgroundBrush);
/*         ShowWindow (NetDoc.hDocWnd, SW_SHOWNORMAL); */

	/* Add the "About" and "Restore All" options to the system menu.            */
	hMenu = GetSystemMenu (hWnd, FALSE);
	AppendMenu (hMenu, MF_SEPARATOR, 0, (LPCSTR) NULL);
	AppendMenu (hMenu, MF_STRING, IDM_RESTORE_ALL, (LPCSTR) "Restore &All");
	AppendMenu (hMenu, MF_STRING, ID_ABOUT, (LPCSTR) "A&bout WinVn...");

#ifdef USE_SPLASH
	/* Add the Splash option */
	hMenu = GetMenu(hWnd);
	hSubMenu = GetSubMenu (hMenu, 3);	/* Config menu */
	InsertMenu (hMenu, IDM_SAVE_CONFIG, MF_STRING|MF_BYCOMMAND, 
					IDM_USE_SPLASH, (LPCSTR) "Show Splash Screen");
	InsertMenu (hMenu, IDM_SAVE_CONFIG, MF_SEPARATOR|MF_BYCOMMAND, 0, (LPCSTR) NULL);
#endif
	break;

  case WM_MYINITMENU:
  case WM_INITMENU:
	hMenu = GetMenu (hWnd);

	hSubMenu = GetSubMenu (hMenu, 0);	/* network menu */
	
	/* don't allow disconnect until connection is established */
	i = (Initializing == INIT_ESTAB_CONN || Initializing == INIT_NOT_CONNECTED);
	EnableMenuItem (hSubMenu, IDM_RECONNECT,  i ? DISABLE_MENU : ENABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_DISCONNECT, i ? DISABLE_MENU : ENABLE_MENU);

	i = (Initializing != INIT_ESTAB_CONN);
	EnableMenuItem (hSubMenu, IDM_EXIT,  i ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_QUIT,  i ? ENABLE_MENU : DISABLE_MENU);
	SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_FASTEXIT, i ? TRUE : FALSE);

	hSubMenu = GetSubMenu (hMenu, 1);	/* group menu */
	EnableMenuItem (hSubMenu, IDM_SUBSCRIBE, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNSUBSCRIBE, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_GROUP_TOP, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNSEL_ALL, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);

	hSubMenu = GetSubMenu (hMenu, 2);	/* Utilities menu */
	EnableMenuItem (hMenu, IDM_DECODE_FILE,
					(CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hMenu, IDM_ENCODE_FILE,
					(CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_POST,
					NumPostWnds ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_MAIL,
					NumMailWnds ? ENABLE_MENU : DISABLE_MENU);

	hSubMenu = GetSubMenu (hMenu, 3);	/* Config menu */
	CheckMenuItem (GetSubMenu (hSubMenu, 14), IDM_TRUE_INVERSE_SELECTIONS,
		 MF_BYCOMMAND | (UseInverseSelections ? MF_CHECKED : MF_UNCHECKED));
#ifdef USE_SPLASH
	CheckMenuItem (hSubMenu, IDM_USE_SPLASH,
		 MF_BYCOMMAND | (ShowSplashScreen ? MF_CHECKED : MF_UNCHECKED));
#endif

	hSubMenu = GetSubMenu (hMenu, 4);	/* Window menu */
	numComposeWnds = NumPostWnds + NumMailWnds;
	/* close submenu */
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_ARTICLE,
					NumArticleWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_GROUP,
					NumGroupWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_COMPOSE,
					numComposeWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_STATUS,
					NumStatusTexts ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL,
					(NumArticleWnds || NumGroupWnds || NumStatusTexts || numComposeWnds) ? MF_ENABLED : MF_GRAYED);
	/* minimize submenu */
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_ARTICLE,
					NumArticleWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_GROUP,
					NumGroupWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_COMPOSE,
					numComposeWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_STATUS,
					NumStatusTexts ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	/* can always minimize all - at least the main window! */
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL, MF_ENABLED);
	break;

  case WM_SIZE:
	GetClientRect (hWnd, &myRect);
	ShowWindow (NetDoc.hDocWnd, SW_SHOWNORMAL);
	MoveWindow (NetDoc.hDocWnd, 0, TOOLBARHEIGHT,
				myRect.right - myRect.left,
	(myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
				TRUE);
	MoveWindow (NetDoc.hWndTB, 0, 0,
				myRect.right - myRect.left,
				TOOLBARHEIGHT,
				TRUE);
	break;

  case WM_CLOSE:
	if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	}
	if (!ConfirmSaveOnExit) {
	  SaveNewsrc = SaveConfig = TRUE;
	}
	else {
	  /* WinVnExitDlg sets SaveNewsrc and SaveConfig */
	  if (!DialogBox (hInst, "WinVnExit", hWnd, lpfnWinVnExitDlg)) {
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		break;
	  }
	}
	SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	DestroyWindow (hWnd);
	return (0);
	break;

  case WM_DESTROY:
	if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	}
	if (Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) {
	  PutCommData ("QUIT\r\n", 6);
	}

	MailClose (hWnd);
	Disconnect ();

	if (SaveNewsrc)
	  WriteNewsrc ();

	if (SaveConfig)
	  WriteWinvnProfile ();

	/* Remove resources before exiting program */
	FreeTextBlock (Signature);
	if (MailList)
	  FreeTextBlock (MailList);

	DestroyFonts ();
	DestroyBitmaps ();

	if (hListBackgroundBrush)
	  DeleteObject (hListBackgroundBrush);
	if (hArticleBackgroundBrush)
	  DeleteObject (hArticleBackgroundBrush);
	if (hStatusBackgroundBrush)
	  DeleteObject (hStatusBackgroundBrush);
	if (StatbarPntData.hFontStatbar)
	  DeleteObject (StatbarPntData.hFontStatbar);
	PostQuitMessage (0);
	return (0);
	break;

  case WM_VSCROLL:
  case WM_HSCROLL:
	break;
  case WM_PAINT:
/*	if (Initializing == INIT_READY || Initializing == INIT_DONE) */
/*	  SetUserMenus (&NetDoc, ENABLE); */

	/* paint status bar  */
	hDC = BeginPaint (hWnd, &ps);
	PaintStatbar (hWnd, hDC, &NetDoc);
	EndPaint (hWnd, &ps);
	if (GetFocus () == hWnd)
	  SetFocus (NetDoc.hDocWnd);
	return (DefWindowProc (hWnd, message, wParam, lParam));
	break;

  case WM_COMMAND:
	switch (LOWORD (wParam)) {
	case IDM_QUIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }  
	  SaveNewsrc = SaveConfig = FALSE;
	  SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	  DestroyWindow (hWnd);
	  return (0);
	  break;
	case IDM_EXIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }  
	  SendMessage (hWnd, WM_CLOSE, 0, 0L);
	  break;

	case IDM_CONNECT:
	  Connect ();				/* menus are enabled in WM_PAINT once connection is established */
	  break;
    
    case IDM_SAVE_NEWSRC:
      WriteNewsrc ();
      MessageBox (NetDoc.hDocWnd, "WinVN NEWSRC Saved", "WinVN Resource File", MB_OK);
	  break;
	  
	case IDB_TOGGLE_VIEW_UNSUB:
	  if (ShowUnsubscribed == FALSE)
		ShowUnsubscribed = TRUE;
	  else
		ShowUnsubscribed = FALSE;
	  SendMessage (NetDoc.hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_VIEW_UNSUB, ShowUnsubscribed);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDB_FASTEXIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }
	  WriteNewsrc ();
	  WriteWinvnProfile ();
	  SaveNewsrc = SaveConfig = FALSE;	/* we've already done them */
	  SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	  DestroyWindow (hWnd);
	  return (0);
	  break;

	case IDB_TOGGLE_CONNECT:
	  if (Initializing == INIT_NOT_CONNECTED) {
		SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
		Connect ();
		break;
	  }
	  /* else fall into disconnect */
	  SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
	case IDM_DISCONNECT:
	  /* If the NNTP server disconnected, then Initializing == INIT_NOT_CONNECTED */
	  if ((Initializing != INIT_NOT_CONNECTED && ConfirmDisconnect &&
		   (MessageBox (NetDoc.hDocWnd,
						"Are you sure you wish to disconnect from the server?\n"
						"All active windows will be closed.",
						"Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO))) {
		SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, TRUE);
		SendMessage (NetDoc.hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_CONNECT, TRUE);
		break;
	  }

	  if (Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) {
		PutCommData ("QUIT\r\n", 6);
	  }
	  Disconnect ();
	  break;


	case IDM_VIEW_SEL_GROUP:
	  break;

	case IDM_SHOW_SUBSCR:
	case IDM_SHOW_ALL_GROUP:
	case IDM_SEL_SUBSCR:
	case IDM_SELECT_ALL:
	  MessageBox (hWnd, "Command not implemented","Sorry", MB_OK);
	  break;

	case IDM_UNSEL_ALL:
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_SUBSCRIBE:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
	  BuildPtrList ();
	  MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  SetNetDocTitle ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_UNSUBSCRIBE:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_UNSUBSCRIBE, FALSE);
	  BuildPtrList ();
	  ShellSort (NewGroupTable,
				 (size_t) nNewGroups,
				 (size_t) sizeof (void far *),
				 GroupCompare);
	  MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  SetNetDocTitle ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_GROUP_TOP:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
	  BuildPtrList ();
	  MergeGroups (ADD_SUBSCRIBED_TOP_OF_DOC);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  ScreenToTop (&NetDoc);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;


	case IDM_COMMOPTIONS:
	  DialogBox (hInst, "WinVnComm", hWnd, lpfnWinVnCommDlg);
	  break;

	case IDM_CONFIG_PERSONAL:
	  DialogBox (hInst, "WinVnPersonal", hWnd, lpfnWinVnPersonalInfoDlg);
	  break;
	  
	case IDM_CONFIG_CONFIRMATIONS:
	  DialogBox (hInst, "WinVnConfirmations", hWnd, lpfnWinVnConfirmationDlg);
	  break;

	case IDM_CONFIG_LOG:
	  DialogBox (hInst, "WinVnLogOpts", hWnd, lpfnWinVnLogOptDlg);
	  break;

	case IDM_CONFIG_SMART_FILER:
	  DialogBox (hInst, "WinvnSmartFiler", NetDoc.hDocWnd, lpfnWinVnSmartFilerDlg);
	  break;

	case IDM_CONFIG_ARTLIST:
	  DialogBox (hInst, "WinVnArticleListPrefs", hWnd, lpfnWinVnArtListDlg);
	  break;

	case IDM_CONFIG_ARTICLE:
	  DialogBox (hInst, "WinVnArticlePrefs", hWnd, lpfnWinVnArticleDlg);
	  break;

	case IDM_CONFIG_GROUPLIST:
	  DialogBox (hInst, "WinVnGroupListPrefs", hWnd, lpfnWinVnGroupListDlg);
	  break;

	case IDM_COMPOSE_PREFS:
	  DialogBox (hInst, "WinVnComposePrefs", hWnd, lpfnWinVnComposePrefsDlg);
	  break;

	case IDM_ATTACH_PREFS:
	  DialogBox (hInst, "WinVnAttachPrefs", hWnd, lpfnWinVnAttachPrefsDlg);
	  break;

	case IDM_CONFIG_CODING:
	  DialogBox (hInst, "WinVnCodingPrefs", hWnd, lpfnWinVnCodingPrefsDlg);
	  break;

	case IDM_FONT_WINVNSYSTEM:
	  if (AskForFont (NetDoc.hDocWnd, WinVnFontFace, &WinVnFontSize, WinVnFontStyle))
		break;
	  InitWinVnFonts ();
	  InitStatBar (&StatbarPntData);
	  SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
	  InvalidateRect (NetDoc.hWndFrame, NULL, TRUE);
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshArticleWnds ();
	  RefreshGroupWnds ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_COMPOSITION:
	  if (AskForFont (NetDoc.hDocWnd, CompositionFontFace, &CompositionFontSize, CompositionFontStyle))
		break;
	  InitCompositionFonts ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_GROUPLIST:
	  if (AskForFont (NetDoc.hDocWnd, ListFontFace, &ListFontSize, ListFontStyle))
		break;
	  InitListFonts ();
	  SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshGroupWnds ();
	  break;

	case IDM_FONT_ARTICLE_TEXT:
	  if (AskForFont (NetDoc.hDocWnd, ArticleFontFace, &ArticleFontSize, ArticleFontStyle))
		break;
	  InitArticleFonts ();
	  RefreshArticleWnds ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_STATUS_TEXT:
	askStatusFont:;
	  if (AskForFont (NetDoc.hDocWnd, StatusFontFace, &StatusFontSize, StatusFontStyle))
		break;
	  InitStatusFonts ();
	  if (STATUSWIDTH > xScreen) {
		MessageBox (NetDoc.hDocWnd, "The status window will not fit on your screen with this font.\nPlease select a smaller font",
					"Font too big", MB_OK | MB_ICONSTOP);
		goto askStatusFont;
	  }
	  RefreshStatusWnds ();
	  break;

	case IDM_FONT_PRINT_TEXT:
	  if (AskForFont (NetDoc.hDocWnd, PrintFontFace, &PrintFontSize, "Printer"))
		break;
	  InitPrintFonts ();
	  break;

	case IDM_TRUE_INVERSE_SELECTIONS:
	  UseInverseSelections = !UseInverseSelections;
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshGroupWnds ();
	  break;

#ifdef USE_SPLASH
	case IDM_USE_SPLASH:
	  ShowSplashScreen = !ShowSplashScreen;
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;
#endif

	case IDM_COLOR_SUBSCRIBED:
	  if (AskForColor (NetDoc.hDocWnd, &NetSubscribedColor))
		break;
	  WarnColors (NetSubscribedColor, ListBackgroundColor, "List Background");
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_UNSUBSCRIBED:
	  if (AskForColor (NetDoc.hDocWnd, &NetUnSubscribedColor))
		break;
	  WarnColors (NetUnSubscribedColor, ListBackgroundColor, "List Background");
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_SEEN:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleSeenColor))
		break;
	  WarnColors (ArticleSeenColor, ListBackgroundColor, "List Background");
	  RefreshGroupWnds ();
	  break;

	case IDM_COLOR_UNSEEN:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleUnSeenColor))
		break;
	  WarnColors (ArticleUnSeenColor, ListBackgroundColor, "List Background");
	  RefreshGroupWnds ();
	  break;

	case IDM_COLOR_ARTICLE_TEXT:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleTextColor))
		break;
	  WarnColors (ArticleTextColor, ArticleBackgroundColor, "Article Background");
	  RefreshArticleWnds ();
	  break;

	case IDM_COLOR_STATUS_TEXT:
	  if (AskForColor (NetDoc.hDocWnd, &StatusTextColor))
		break;
	  WarnColors (StatusTextColor, StatusBackgroundColor, "Status Background");
	  RefreshStatusWnds ();
	  break;

	case IDM_COLOR_LIST_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &ListBackgroundColor))
		break;
	  DeleteObject (hListBackgroundBrush);
	  hListBackgroundBrush = CreateSolidBrush (ListBackgroundColor);
	  SetHandleBkBrush (hWnd, hListBackgroundBrush);

	  WarnColors (ArticleSeenColor, ListBackgroundColor, "Seen Article") ||
		WarnColors (ArticleUnSeenColor, ListBackgroundColor, "Unseen Article") ||
		WarnColors (NetSubscribedColor, ListBackgroundColor, "Subscribed Group") ||
		WarnColors (NetUnSubscribedColor, ListBackgroundColor, "Unsubscribed Group");

	  RefreshGroupWnds ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_ARTICLE_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleBackgroundColor))
		break;
	  DeleteObject (hArticleBackgroundBrush);
	  hArticleBackgroundBrush = CreateSolidBrush (ArticleBackgroundColor);

	  WarnColors (ArticleTextColor, ArticleBackgroundColor, "Article Text");
	  RefreshArticleWnds ();
	  break;

	case IDM_COLOR_STATUS_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &StatusBackgroundColor))
		break;
	  DeleteObject (hStatusBackgroundBrush);
	  hStatusBackgroundBrush = CreateSolidBrush (StatusBackgroundColor);
	  RefreshStatusWnds ();

	  WarnColors (StatusTextColor, StatusBackgroundColor, "Status Text");
	  break;

	case IDM_WINDOW_CASCADE:
	  CascadeWindows ();
	  break;

	case IDM_CLOSE_ALL:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseWindows ();
	  break;

	case IDM_CLOSE_ALL_ARTICLE:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open article windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseArticleWnds ();
	  break;

	case IDM_CLOSE_ALL_GROUP:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open group windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseGroupWnds ();
	  break;

	case IDM_CLOSE_ALL_COMPOSE:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open composition windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseComposeWnds ();
	  break;

	case IDM_CLOSE_ALL_STATUS:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open status windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseStatusWnds ();
	  break;

	case IDM_MINIMIZE_ALL:
	  MinimizeWindows ();
	  break;

	case IDM_MINIMIZE_ALL_ARTICLE:
	  MinimizeArticleWnds ();
	  break;

	case IDM_MINIMIZE_ALL_GROUP:
	  MinimizeGroupWnds ();
	  break;

	case IDM_MINIMIZE_ALL_COMPOSE:
	  MinimizeComposeWnds ();
	  break;

	case IDM_MINIMIZE_ALL_STATUS:
	  MinimizeStatusWnds ();
	  break;

	case IDM_RESTORE_ALL:
	  RestoreWindows ();
	  break;

	case IDM_SEND_ALL_POST:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to send all open post windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  BatchSend (DOCTYPE_POSTING);
	  break;

	case IDM_SEND_ALL_MAIL:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to send all open mail windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  BatchSend (DOCTYPE_MAIL);
	  break;

	case IDM_DECODE_FILE:
	  if (TestCodingBusy (NetDoc.hDocWnd, "Can't decode file"))
		break;

	  if (!DialogBoxParam (hInst, "WinVnDecodeArts", NetDoc.hDocWnd, lpfnWinVnDecodeArtsDlg, 0))
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  else
		DecodeFile (hWnd);
	  break;

	case IDM_ENCODE_FILE:
	  {
		TypAttachment attach;
		if (TestCodingBusy (NetDoc.hDocWnd, "Can't encode file"))
		  break;
		if (!DialogBoxParam (hInst, "WinVnEncode", NetDoc.hDocWnd, lpfnWinVnEncodeDlg, (LPARAM) & attach))
		  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		else
		  EncodeToFile (NetDoc.hDocWnd, &attach);
	  }
	  break;

	case IDM_RESET:
	  if (!CommBusy || SendingMail || SendingPost) {
		  if (MessageBox (NetDoc.hDocWnd,
				  "Are you sure you wish to reset the server protocol?\n"
				   "Any active communications and coding sessions will be aborted.",
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
			AbortAllComm ();
			hMenu = GetMenu (hWnd);
		    hSubMenu = GetSubMenu (hMenu, 1);
			EnableMenuItem (hSubMenu, IDM_CONNECT, ENABLE_MENU);
			
		  }
	  } else {	
		  if (MessageBox (NetDoc.hDocWnd,
				"Cannot reset server protocol while receiving server information.\n"
				"Do you wish to disconnect and reconnect?\n"
				"All active windows will be closed.",
				"Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
			Reconnect();
		  }
	  }
	  break;

	case IDM_RECONNECT:
	  if ((Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) &&
	  	  (!ConfirmDisconnect ||
		  (MessageBox (NetDoc.hDocWnd,
					"Are you sure you wish to disconnect and reconnect to the server?\n"
					   "All active windows will be closed.",
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES))) {
			Reconnect();
		}
		break;
		
	case IDM_SAVE_CONFIG:
	  WriteWinvnProfile ();
	  MessageBox (NetDoc.hDocWnd, "WinVN Configuration Saved", "WinVN Configuration", MB_OK);
	  break;

	case IDM_SAVE_WINDOW:
	  SaveWindowPositions ();
	  break;

	case IDM_FIND:
	  FindDoc = &NetDoc;
	  if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0])
		strcpy (FindDoc->SearchStr, LastGroupNameFind);

	  if (DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg)) {
		found = DoFind (TRUE, GroupListMultiSelect);
		if (!found) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, NetDoc.SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "Not found", MB_OK);
		}
		else {
		  strcpy (LastGroupNameFind, FindDoc->SearchStr);
		  if (LineIDtoLinePtr (FindDoc->FindLineID, FindDoc, &BlockPtr, &LinePtr)) {
			SetSelections (FindDoc, LinePtr);
			if (GroupListMultiSelect) {
			  /* set the active and anchor line IDs = FindLineID */
			  FindDoc->AnchorLineID = FindDoc->FindLineID;
			}
		  }
		}
	  }
	  break;

	case IDM_FIND_NEXT_SAME:
	  FindDoc = &NetDoc;
	  continueFind = TRUE;
	  if (!FindDoc->SearchStr[0]) {
		if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0])
		  strcpy (FindDoc->SearchStr, LastGroupNameFind);
		continueFind = DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
	  }

	  if (continueFind && FindDoc->SearchStr[0]) {
		found = DoFind (!FindDoc->hFindBlock && !FindDoc->FindLineID, GroupListMultiSelect);
		if (!found) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, NetDoc.SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "No more occurrences", MB_OK);
		}
		else {
		  if (LineIDtoLinePtr (FindDoc->FindLineID, FindDoc, &BlockPtr, &LinePtr)) {
			SetSelections (FindDoc, LinePtr);
			if (GroupListMultiSelect) {
			  /* set the active and anchor line IDs = FindLineID */
			  FindDoc->AnchorLineID = FindDoc->FindLineID;
			}
		  }
		}
	  }
	  break;

	case ID_ABOUT:
	  lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
	  DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
	  FreeProcInstance ((FARPROC) lpProcAbout);
	  break;

	case IDM_HELP:
	  MakeHelpPathName (mybuf, MAXINTERNALLINE);
	  WinHelp (NetDoc.hDocWnd, mybuf, HELP_INDEX, 0L);
	  break;

	case IDM_MAIL:
	  (MailCtrl.fnMlWinCreate) (hWnd, (TypDoc *) NULL, DOCTYPE_MAIL);
	  break;

	case IDM_LOGOUT:
	  (MailCtrl.fnMlLogout) (hWnd);
	  break;

	case IDM_SHOW_VERSION:
	  show_version_strings (hWnd);
	  break;

	case IDM_POST:
	  /*
	   *  DialogBoxParam (hInst, "WinVnGeneric", hWnd,
	   *                  lpfnWinVnGenericDlg, (LPARAM) (char far *) "Newsgroup(s):");
	   */

	  /* NewsgroupsPtr = (char *) NULL; */
	  *groupbuf = '\0';
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_GET_GROUPNAMES, TRUE);
	  NewsgroupsPtr = &groupbuf[0];
	  CreateComposeWnd (hWnd, (TypDoc *) NULL, DOCTYPE_POSTING);
	  break;
	}

  default:
	if (GetFocus () == hWnd)
	  SetFocus (NetDoc.hDocWnd);
	return (DefWindowProc (hWnd, message, wParam, lParam));
	break;
  }

  if (GetFocus () == hWnd)
	SetFocus (NetDoc.hDocWnd);
  return (0);
}

/*
 * Local Variables:
 * tab-width: 4
 * end:
 */

/* Last line of WVUSENET.C */
@


1.72
log
@enable reset server protocol in instances where it wasn't previously
possible to recover from a hung server connection
@
text
@d1 2752
a2752 2750
/********************************************************************
 *                                                                  *
 *  MODULE    :  WVUSENET.C                                         *
 *                                                                  *
 *  PURPOSE   : This file contains the window procedure and support *
 *              routines for WinVn's main window                    *
 *                                                                  *
 ********************************************************************/

/*
 * $Id: wvusenet.c 1.71 1995/03/16 17:12:10 dumoulin Exp $
 *
 */
#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <stdlib.h>
#include <assert.h>
#include "wvtb\wvtb.h"			/* for toolbar  */
#ifdef USE_3D_CONTROLS
#include "ctl3d.h"
#endif

/* This is for the special case where the user invoked the */
/* program with dolist=0, then turned it on.  Without checking */
/* this condition, the newsrc will be truncated to only those */
/* groups read.  SMR 930224 */

int started_with_no_dolist;
void show_version_strings (HWND);
void SetGroupActiveLines (void);
BOOL WarnColors (COLORREF c1, COLORREF c2, char *warnMsg);
BOOL SelectGroup (TypDoc far * Doc, TypGroup far * group, BOOL value);

static char groupbuf[MAXHEADERLINE];

/*--- FUNCTION: WinVnConfWndProc ---------------------------------------
 *
 *    Window procedure for the "Net" window.
 *    This window displays the names of the newsgroups, one per line.
 *
 *    Entry    The usual parameters for any window function.
 *
 *    Note:    We don't do anything until "Initialized" is TRUE.
 *             This way, the user won't try to perform functions
 *             while the communications are still being set up.
 */

long WINAPI
WinVnConfWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 unsigned message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  PAINTSTRUCT ps;				/* paint structure          */
  POINT ptCursor;

  HDC hDC;						/* handle to display context */
  HWND hWndView;
  RECT myRect;					/* selection rectangle                */

  int j;
  unsigned int i;
  int found;
  char far *lpsz;
  TypLine far *LinePtr, far * GroupLinePtr;
  TypBlock far *BlockPtr, far * GroupBlockPtr;
  HANDLE hBlock;
  TypDoc *MyDoc;
  unsigned int Offset;
  BOOL looping = TRUE;
  TypGroup far *MyGroup;

  int X, Y;
  int docnum;
  int newdoc;
  char mybuf[MAXINTERNALLINE];
  int OldSel = FALSE;
  int CtrlState;
  TypLineID MyLineID;

  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
    return(0);

  switch (message) {

/*
   case WM_DESTROY:
   SetHandleBkBrush (hWnd, GetStockObject (WHITE_BRUSH));
   break;
 */

  case WM_CREATE:

	MailInit (hWnd);

	/* Get handle to the hourglass cursor */

	hHourGlass = LoadCursor (NULL, IDC_WAIT);


	/* Fill in the Net document's window handle that we
	 * were unable to fill in in the call to InitDoc.
	 */
	NetDoc.hDocWnd = hWnd;
	InitBitmaps ();
	break;

  case WM_CHAR:
	/* Hitting CR on a group name is the same as double-clicking
	 * on the name.
	 */
	if (!Initializing) {
	  if (wParam == '\r') {

		if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &GroupBlockPtr, &GroupLinePtr))
			goto getgroup1;
		  else
			break;

		}
		else {
		  GetCursorPos (&ptCursor);
		  ScreenToClient (hWnd, &ptCursor);
		  X = ptCursor.x;
		  Y = ptCursor.y;
		  goto getgroup;
		}
	  }
	}
	break;

  case WM_KEYDOWN:
	/* See if this key should be mapped to a scrolling event
	 * for which we have programmed the mouse.  If so,
	 * construct the appropriate mouse call and call the mouse code.
	 * Also, F6 means switch to next window.
	 * (This latter should also be table-driven.)
	 */
	if (wParam == VK_F6) {
	  NextWindow (NetDoc.hWndFrame, NetDoc.DocType);
	}               				
	else if (!Initializing) {
	  switch (wParam) {
	  case VK_DOWN:
		if (GroupListMultiSelect) {
		  /* move one line down from Active line */
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			if (NextActiveLine (&BlockPtr, &LinePtr)) {
			  SetSelections (&NetDoc, LinePtr);
			  AdjustMidSc (BlockPtr, LinePtr);
			}
		  }
		}
		break;

	  case VK_UP:
		if (GroupListMultiSelect) {
		  /* move one line up from Active line */
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			if (PrevActiveLine (&BlockPtr, &LinePtr)) {
			  SetSelections (&NetDoc, LinePtr);
			  AdjustMidSc (BlockPtr, LinePtr);
			}
		  }
		}
		break;

	  case VK_HOME:
		if (GroupListMultiSelect) {
		  TopOfDoc (&NetDoc, &BlockPtr, &LinePtr);
		  SetSelections (&NetDoc, LinePtr);
		  AdjustMidSc (BlockPtr, LinePtr);
		}
		break;

	  case VK_END:
		if (GroupListMultiSelect) {
		  BottomOfDoc (&NetDoc, &BlockPtr, &LinePtr);
		  SetSelections (&NetDoc, LinePtr);
		  AdjustMidSc (BlockPtr, LinePtr);
		}
		break;

	  case VK_NEXT:
		if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			for (i = 0; i < NetDoc.ScYLines; i++) {
			  if (!NextActiveLine (&BlockPtr, &LinePtr))
				break;
			}
			SetSelections (&NetDoc, LinePtr);
			AdjustMidSc (BlockPtr, LinePtr);
		  }
		}
		break;

	  case VK_PRIOR:
		if (GroupListMultiSelect) {
		  if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) {
			for (i = 0; i < NetDoc.ScYLines; i++) {
			  if (!PrevActiveLine (&BlockPtr, &LinePtr))
				break;
			}
			SetSelections (&NetDoc, LinePtr);
			AdjustMidSc (BlockPtr, LinePtr);
		  }
		}
		break;

	  default:
		/* Based on the state of the Control key and the value
		 * of the key just pressed, look up in the array
		 * key2scroll to see if we should process this keystroke
		 * as if it were a mouse event.  If so, simulate the
		 * mouse event by sending the appropriate message.
		 */

		CtrlState = GetKeyState (VK_CONTROL) < 0;
		for (j = 0; j < NUMKEYS; j++) {
		  if (wParam == key2scroll[j].wVirtKey &&
			  CtrlState == key2scroll[j].CtlState) {
			SendMessage (hWnd, key2scroll[j].iMessage,
						 key2scroll[j].wRequest, 0L);
			break;
		  }
		}

	  }
	}
	break;

  case WM_LBUTTONDOWN:
	/*  Clicking the left button on a group name toggles the
	 *  selected/not selected status of that group.
	 *  Currently selected groups are displayed in reverse video.
	 */

	DragMouseAction = DRAG_NONE;
	if (!Initializing) {
	  BOOL status;
	  TypGroup *aGroup;

	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  /* make this behave more like a multi-select listbox (jlg) */
	  /* if click,       make the line the only one selected */
	  /* if ctrl-click,  add the line to the selection */
	  /* if shift-click, select from previously selected to current */
	  if (GroupListMultiSelect) {
		if (!(wParam & MK_CONTROL) && !(wParam & MK_SHIFT)) {
		  if (NetDoc.SelectedLines == 1) {
			LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr);
			SelectGroup (&NetDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)), FALSE);
		  }
		  else {
			ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
		  }
		}
	  }
	  if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) {
		aGroup = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
		status = SelectGroup (&NetDoc, aGroup, aGroup->Selected ? FALSE : TRUE);
		if (GroupListMultiSelect) {
		  NetDoc.ActiveLineID = LinePtr->LineID;
		  if (wParam & MK_SHIFT) {
			ForAllLines (&NetDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
		  }
		  else
			NetDoc.AnchorLineID = LinePtr->LineID;

		  if (!(wParam & MK_CONTROL) && !(wParam & MK_SHIFT))
			DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
		  else
			DragMouseAction = DRAG_NONE;
		}
		else
		  DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;

		GlobalUnlock (BlockPtr->hCurBlock);
		SetCapture (hWnd);		/* release capture on button up */

	  }

	  InvalidateRect (hWnd, NULL, FALSE);
	}
	break;

  case WM_LBUTTONUP:
	/*  Letting up on the left button on a group name 
	 *  gets us out of Dragging mode   */

	ReleaseCapture ();
	DragMouseAction = DRAG_NONE;
	break;

  case WM_MOUSEMOVE:
	/*  Code to drag the mouse and change the select/not selected
	 *  status of that group.
	 */

	if ((!Initializing) && (DragMouseAction != DRAG_NONE)) {
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  GetClientRect (hWnd, &myRect);
	  if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) {
		if (GroupListMultiSelect) {
		  NetDoc.ActiveLineID = LinePtr->LineID;
		  ForAllLinesOnScreen (&NetDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
		  GlobalUnlock (BlockPtr->hCurBlock);
		}
		else {
		  SelectGroup (&NetDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)),
					   (DragMouseAction == DRAG_SELECT) ? TRUE : FALSE);
		  GlobalUnlock (BlockPtr->hCurBlock);
		}
	  }
	  InvalidateRect (hWnd, NULL, FALSE);
	}
	break;

  case WM_LBUTTONDBLCLK:
	/*  Double-clicking on a group name creates a "Group"
	 *  window, whose purpose is to display the subjects
	 *  of the articles in the group.
	 */
	if (!Initializing) {
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	getgroup:;

	  if (CursorToTextLine (X, Y, &NetDoc, &GroupBlockPtr, &GroupLinePtr)) {

	  getgroup1:;

		if (MyDoc = (((TypGroup far *)
			  (((char far *) GroupLinePtr) + sizeof (TypLine)))->SubjDoc)) {

		  /* We already have a document containing the subjects */
		  /* of this group, so just activate it.                */

		  ShowWindow (MyDoc->hWndFrame, SW_SHOWNORMAL);
		  SetActiveWindow (MyDoc->hWndFrame);
		  SetFocus (MyDoc->hDocWnd);
		  GlobalUnlock (GroupBlockPtr->hCurBlock);
		  break;
		}
		if (TestCommBusy (hWnd, "Can't Retrieve Group")) {
		  GlobalUnlock (GroupBlockPtr->hCurBlock);
		  break;
		}

		/* At this point, we've determined that the subjects for
		 * this newsgroup are not in memory, and that it's OK
		 * to fetch them from the server.  (Comm line not busy.)
		 * Figure out whether a new window/document should be
		 * created for this group & set "newdoc" accordingly.
		 */
		newdoc = FALSE;
		if (ViewNew || !ActiveGroupDoc || !(ActiveGroupDoc->InUse)) {
		  found = FALSE;
		  for (docnum = 0; docnum < MAXGROUPWNDS; docnum++) {
			if (!GroupDocs[docnum].InUse) {
			  found = TRUE;
			  newdoc = TRUE;
			  CommDoc = &(GroupDocs[docnum]);
			  CommDoc->LongestLine = 0;
			  CommDoc->ScXOffset = 0;
			  break;
			}
		  }
		  if (!found) {
			MessageBox (hWnd, "You have too many group windows active.\n"
							  "Close one or deselect 'New window for each group'.", 
							  "Can't open new window", MB_OK | MB_ICONASTERISK);
			UnlockLine (GroupBlockPtr, GroupLinePtr, &hBlock, &Offset, &MyLineID);
			break;
		  }
		}
		else {
		  /* Must reuse old window for this group.           */
		  ActiveGroupDoc->LongestLine = 0;
		  ActiveGroupDoc->ScXOffset = 0;
		  CommDoc = ActiveGroupDoc;

		  LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset, CommDoc->ParentLineID, &BlockPtr, &LinePtr);
		  MyGroup = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
		  MyGroup->SubjDoc = (TypDoc *) NULL;

		  /* make the 'n' in the group-list go away */
		  MyGroup->HighestPrevSeen = MyGroup->ServerLast;
		  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);

		  UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
		  
		  UpdateSeenArts (CommDoc);
		  UnlinkArtsInGroup (CommDoc);
		  /* clear out contents of this document for reuse */
		  FreeDoc (CommDoc);
		}

		/* Create window title indicating we are retrieving text. */

		CommDoc->InUse = TRUE;
		lpsz = (char far *) (((char far *) GroupLinePtr) +
							 sizeof (TypLine) + sizeof (TypGroup));
		strcpy (mybuf, "Retrieving ");
		lstrcat (mybuf, lpsz);

		/* If necessary, create a new window.              */

		if (newdoc) {
		  int x, y, width, height;
		  char poschars[MAXINTERNALLINE];

		  /* Compute default screen position. */
		  x = 1;
		  y = 0;
		  width = (int) xScreen;
		  height = (int) (yScreen * 1 / 2);

		  /* If the screen position has been saved, use that instead. */
		  GetPrivateProfileString (ARTLIST, "GroupWindowPos", "!",
								   poschars, MAXINTERNALLINE, szAppProFile);
		  if (poschars[0] != '!') {
			sscanf (poschars, "%d,%d,%d,%d", &x, &y, &width, &height);
		  }
		  hWndView = CreateWindow ("WinVnViewFrame",
								   mybuf,
								   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
								   x + (int) docnum * CharWidth,	/* Initial X pos */
								   y + (int) docnum * LineHeight,
								   width,	/* Initial X Width */
								   height,
								   NULL,
								   NULL,
								   hInst,
								   NULL);

		  if (!hWndView)
			return (0);			/* ??? */

/*    SetHandleBkBrush (hWndView, hListBackgroundBrush); */
		  ShowWindow (hWndView, SW_SHOWNORMAL);
		}
		else {
		  hWndView = CommDoc->hWndFrame;
		  SetWindowText (hWndView, mybuf);
		  SetGroupMenus (CommDoc, DISABLE);
		}
		RcvLineCount = 0;

		/* Do some housekeeping:  Set group as selected,
		 * initialize empty document to hold subject lines,
		 * set focus to this document, set pointers linking
		 * this document and the subject line.
		 */
		SelectGroup (&NetDoc, (TypGroup far *) (((char far *) GroupLinePtr) +
												sizeof (TypLine)), TRUE);
		InitDoc (CommDoc, hWndView, &NetDoc, DOCTYPE_GROUP);
		PtrToOffset (GroupBlockPtr, GroupLinePtr, &(CommDoc->hParentBlock),
					 &(CommDoc->ParentOffset), &(CommDoc->ParentLineID));
		SetActiveWindow (CommDoc->hWndFrame);
		SetFocus (CommDoc->hDocWnd);

		((TypGroup far *) (((char far *) GroupLinePtr) + sizeof (TypLine)))
		  ->SubjDoc = CommDoc;
		GlobalUnlock (GroupBlockPtr->hCurBlock);
		InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);

		UpdateWindow (CommDoc->hDocWnd);
		InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);

		/* Deal with Comm-related stuff:  set FSA variables,
		 * send the GROUP command to NNTP.
		 */

		CommLinePtr = CommLineIn;
		CommBusy = TRUE;
		CommState = ST_GROUP_RESP;
		hSaveCursor = SetCursor (hHourGlass);
		ShowCursor (TRUE);


/* capture the mouse to the usenet window, so that we keep the hourglass */

/*       SetCapture (hWnd); */
		strcpy (mybuf, "GROUP ");
		lpsz = (char far *) GroupLinePtr + sizeof (TypLine) + sizeof (TypGroup);
		lstrcat (mybuf, lpsz);
		mylstrncpy (CurrentGroup, lpsz, MAXFINDSTRING);
		PutCommLine (mybuf);
	  }
	}
	break;

/* If we've lost the connection for some reason, kill the timer */
	/* that's banging on the socket. */
  case WM_TIMER:
	if ((CommState == ST_CLOSED_COMM) && (!Initializing)) {
	  KillTimer (hWnd, ID_TIMER);
	}
	else {
	  if (CommState != ST_CLOSED_COMM)
		DoCommInput ();
	};
	break;

  case WM_SIZE:
	GetClientRect (hWnd, &myRect);
	NetDoc.ScXWidth = myRect.right;
	NetDoc.ScYHeight = myRect.bottom;
	NetDoc.ScYLines = (RectHeight (myRect) - TopSpace) / LineHeight;
	NetDoc.ScXChars = (RectWidth (myRect) - SideSpace) / CharWidth;
	break;

  case WM_VSCROLL:
	if (!Initializing) {
	  ScrollIt (&NetDoc, wParam, lParam);
	}
	break;

  case WM_HSCROLL:
	if (!Initializing) {
	  HScrollIt (&NetDoc, wParam, lParam);
	}
	break;

  case WM_PAINT:
	{
	  HANDLE hBlock;
	  SIZE sz;
	  unsigned int Offset, MyLen, indicatorwidth;
	  int VertLines, HorzChars;
	  int EndofDoc = FALSE;
	  int RangeHigh, CurPos;
	  int Xtext;
	  char far *textptr;
	  char *cptr, *cptr2;
	  char indicator;
	  char indcptr[128];
	  char group_name[MAXINTERNALLINE];
	  char init_msg[60];
	  TypGroup far *MyGroup;
	  COLORREF MyColors[4], MyBack[4];
/* DWORD Rop; */
	  RECT myRect, aRect;
	  int MyColorMask = 1, PrevColorMask = MyColorMask;
	  HBRUSH hOldBrush;
	  HANDLE hOldFont;
#define SUBSCR_MASK 1
#define SELECT_MASK 2

	  hDC = BeginPaint (hWnd, &ps);
	  GetClientRect (hWnd, &myRect);

	  /* If still initializing comm link, put out a message    */
	  /* to that effect.                                       */

	  switch (Initializing) {
	  case INIT_NOT_CONNECTED:
		cptr = "Not connected";
		cptr2 = "to news server";

		goto display_msg;

	  case INIT_ESTAB_CONN:
		cptr = "Establishing link";
		cptr2 = "to news server...";

		goto display_msg;

	  case INIT_READING_NEWSRC:
		cptr = "Reading your log";
		cptr2 = "(\"newsrc\")...";
		goto display_msg;

	  case INIT_SCANNING_NETDOC:
		cptr = "Creating hash table";
		cptr2 = "from current groups...";
		goto display_msg;

	  case INIT_GETTING_LIST:
		sprintf (mybuf, "Receiving %uth newsgroup", RcvLineCount);
		cptr = mybuf;
		cptr2 = "name from server...";

	  display_msg:;
		hOldFont = SelectObject (hDC, hWinVnFont);
		SetBkColor (hDC, ListBackgroundColor);
		if (IsDark(ListBackgroundColor))
		  SetTextColor (hDC, RGB (200, 200, 200));	/* if background is black, make text white */

		else
		  SetTextColor (hDC, RGB (0, 0, 0));	/* otherwise text is black */
/*
		hOldBrush = SelectObject (hDC, hListBackgroundBrush);
		PatBlt (hDC, 0, 0, myRect.right, myRect.bottom, PATCOPY);
		SelectObject (hDC, hOldBrush);
*/
		X = SideSpace + CharWidth;
		Y = LineHeight;
		strcpy (init_msg, cptr);
		strcat (init_msg, "    ");
		j = strlen (init_msg);
		TextOut (hDC, X, Y, init_msg, j);
		strcpy (init_msg, cptr2);
		strcat (init_msg, "    ");
		j = strlen (init_msg);
		TextOut (hDC, X, Y + WinVnLineHeight, init_msg, j);
		SelectObject (hDC, hOldFont);
		break;

	  case INIT_READY:
		/* this state happens when all connection prep is finished 
		 * and we are ready to begin normal WinVn user operation
		 */
		SetUserMenus (&NetDoc, ENABLE);
		Initializing = INIT_DONE;
		/* fall into INIT_DONE */

	  case INIT_DONE:
		VertLines = NetDoc.ScYLines;
		HorzChars = NetDoc.ScXChars;

		MyColors[0] = NetUnSubscribedColor;		/* unsubscribed/unselected     */

		MyBack[0] = ListBackgroundColor;
		MyColors[1] = NetSubscribedColor;	/* subscribed/unselected */

		MyBack[1] = ListBackgroundColor;

		if (UseInverseSelections) {
		  MyColors[2] = ListBackgroundColor;	/* unsubscribed/selected */

		  MyBack[2] = NetUnSubscribedColor;
		  MyColors[3] = ListBackgroundColor;	/* subscribed/selected */

		  MyBack[3] = NetSubscribedColor;
		  MyColors[2] = ListBackgroundColor;	/* unsubscribed/selected */

		  MyBack[2] = NetUnSubscribedColor;
		  MyColors[3] = ListBackgroundColor;	/* subscribed/selected */

		  MyBack[3] = NetSubscribedColor;
		}
		else {
		  if (NetUnSubscribedColor == RGB (0, 0, 0) ||	/* unsub/selected */
			   ListBackgroundColor == RGB (0, 0, 0) && IsBright (NetUnSubscribedColor))
			MyColors[2] = ListBackgroundColor;
		  else
			MyColors[2] = NetUnSubscribedColor;

		  if (NetSubscribedColor == RGB (0, 0, 0) ||	/* sub/selected */
			   ListBackgroundColor == RGB (0, 0, 0) && IsBright (NetSubscribedColor))
			MyColors[3] = ListBackgroundColor;
		  else
			MyColors[3] = NetSubscribedColor;

		  if (ListBackgroundColor == RGB (0, 0, 0))
			MyBack[2] = RGB (200, 200, 200);
		  else
			MyBack[2] = RGB (0, 0, 0);
		  MyBack[3] = MyBack[2];
		}

		SetTextColor (hDC, MyColors[MyColorMask]);
		SetBkColor (hDC, MyBack[MyColorMask]);

		hOldFont = SelectObject (hDC, hListFont);

		GetTextExtentPoint (hDC, "n", 2, &sz);	/* was a '*', now will be 'u' or 'n' */
		indicatorwidth = sz.cx * 7 * 7;

		/* Update the scroll bar thumb position.                 */

		RangeHigh = NetDoc.ActiveLines - VertLines;
		if (RangeHigh < 0) {
		  RangeHigh = 0;
		  NetDoc.TopLineOrd = 0;
		  NetDoc.TopScLineID = 0;
		  NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
		  NetDoc.TopScOffset = sizeof (TypBlock);
		  LockLine (NetDoc.hFirstBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
					&BlockPtr, &LinePtr);
  		  AdvanceToActive (&BlockPtr, &LinePtr);
		  VertLines = NetDoc.ActiveLines;
		} else {
		  LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
					&BlockPtr, &LinePtr);
		}

		CurPos = NetDoc.TopLineOrd;
		if (CurPos < 0)
		  CurPos = 0;

		SetScrollRange (hWnd, SB_VERT, 0, RangeHigh,
		NetDoc.ThumbTracking ? FALSE : TRUE);
		/* thumb pos is updated at end of thumb track */
		if (!NetDoc.ThumbTracking) {
			SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
		}
        
		RangeHigh = NetDoc.LongestLine - NetDoc.ScXChars;
		if (RangeHigh < 0) {
		  RangeHigh = 0;
		  NetDoc.ScXOffset = 0;
		}
		SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
		SetScrollPos (hWnd, SB_HORZ, NetDoc.ScXOffset, TRUE);

		/* Loop through the lines, painting them on the screen. */

		X = SideSpace - NetDoc.ScXOffset * (CharWidth + 1);
		Xtext = X + indicatorwidth;
		Y = StartPen;
		if (LinePtr->length != END_OF_BLOCK)
		  do {
			MyGroup = ((TypGroup far *)
					   ((char far *) LinePtr + sizeof (TypLine)));
			MyLen = MyGroup->NameLen;
			textptr = (char far *) LinePtr + sizeof (TypLine) +
			  sizeof (TypGroup);

			/* Display this line only if it is active. */
			if (LinePtr->active) {

			  /* Figure out the color of this line.                 */

			  if (MyGroup->Subscribed) {
				if (MyGroup->Selected)
				  MyColorMask = 3;
				else
				  MyColorMask = 1;
			  }
			  else {
				if (MyGroup->Selected)
				  MyColorMask = 2;
				else
				  MyColorMask = 0;
			  }

			  if (MyColorMask != PrevColorMask) {
				SetTextColor (hDC, MyColors[MyColorMask]);
				SetBkColor (hDC, MyBack[MyColorMask]);
				PrevColorMask = MyColorMask;
			  }

			  /* Figure out what indicator character to use. */

			  indicator = ' ';
			  if (NetDoc.FindLineID == LinePtr->LineID) {
				indicator = '>';
			  }
			  else if (MyGroup->HighestPrevSeen) {
				if (MyGroup->ServerLast > MyGroup->HighestPrevSeen) {
				  indicator = 'n';	/* was a '*' */
				}
				else {
				  /* check if there are unread articles - jlg 4/9/94 */
#if 0
				  RangePtr = (TypRange far *) ((char far *) MyGroup + RangeOffset (MyGroup->NameLen));
				  if (MyGroup->nRanges) {
					HighestRead = RangePtr[MyGroup->nRanges - 1].Last;
					if (MyGroup->ServerLast > HighestRead && MyGroup->ServerEstNum > 0)
					  indicator = 'u';	/* was a '*' */
				  }
#endif
				  if (MyGroup->NumUnread && MyGroup->ServerEstNum > 0) {
					  indicator = 'u';
				  }
				}
			  }

			  mylstrncpy (group_name, textptr, MyGroup->NameLen + 1);

			  if (MyGroup->Determined == FALSE)
				strcpy (str, "-");
			  else
				sprintf (str, "%5lu", MyGroup->ServerEstNum);

			  sprintf (indcptr, "%c %5s %s ", indicator, str, group_name);

			  /* Now write out the line.                            */

			  MyLen = strlen (indcptr);

			  SetRect (&aRect, 0, Y, myRect.right, Y + LineHeight);

			  ExtTextOut (hDC, X, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
						  indcptr, MyLen, (LPINT) NULL);

			  if (NetDoc.ActiveLineID == -1L)
				NetDoc.ActiveLineID = LinePtr->LineID;
			  if (NetDoc.ActiveLineID == LinePtr->LineID && GroupListMultiSelect)
				DrawFocusRect (hDC, &aRect);

			  Y += LineHeight;
			  --VertLines;
			}					/* end if LinePtr->active */
		  }
		  while (VertLines > 0 && NextLine (&BlockPtr, &LinePtr));

		SelectObject (hDC, hOldFont);

/*       SetTextColor (hDC, MyColors[1]); */
		/*       SetBkColor (hDC, MyBack[1]); */
		/*       SelectObject (hDC, UnSelectedBrush); */

		/* Blank out bottom and top of screen */
		hOldBrush = SelectObject (hDC, hListBackgroundBrush);
		PatBlt (hDC, 0, Y, myRect.right - 1, myRect.bottom - Y, PATCOPY);
		PatBlt (hDC, 0, 0, myRect.right - 1, TopSpace, PATCOPY);
		SelectObject (hDC, hOldBrush);
		UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	  }
	  EndPaint (hWnd, &ps);
	  break;
	}

  case WM_ENDSESSION:

	WinHelp (NetDoc.hDocWnd, LFN_HELP, HELP_QUIT, 0L);
	/*CloseComm (CommDevice); */

	break;

  case WM_COMMAND:
	return (SendMessage (NetDoc.hWndFrame, message, wParam, lParam));


  default:
	return (DefWindowProc (hWnd, message, wParam, lParam));
  }
  return (0);
}

void
SetSelections (TypDoc * MyDoc, TypLine far * LinePtr)
{
  TypLine far *deselPtr;
  TypBlock far *BlockPtr;
  TypLineID prevActive;

  if (GetKeyState (VK_SHIFT) >= 0) {	/* shift is NOT pressed  */
	prevActive = MyDoc->ActiveLineID;
	MyDoc->ActiveLineID = LinePtr->LineID;
	/* clear select status of all lines */
	if (MyDoc->SelectedLines == 1) {
	  LineIDtoLinePtr (prevActive, MyDoc, &BlockPtr, &deselPtr);
	  SelectGroup (MyDoc, (TypGroup far *) (((char far *) deselPtr) + sizeof (TypLine)), FALSE);
	}
	else {
	  ForAllLines (MyDoc, SetLineFlag, LINE_FLAG_SET, FALSE);
	}
	/* set only current line selected */
	SelectGroup (MyDoc, (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)), TRUE);
	/* set the current line to be the anchor */
	MyDoc->AnchorLineID = LinePtr->LineID;
  }
  else
	/* shift is pressed  */
  {
	/* select all lines between ActiveLineID and AnchorLineID inclusive */
	MyDoc->ActiveLineID = LinePtr->LineID;
	ForAllLines (MyDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
  }
}

/* if enable is true then enable connect, disable disconnect */
/* disconnect is managed in INTITMENU */
void
SetConnectMenuItems (TypDoc FAR * DocPtr, int enable)
{
  HMENU hMenu, hSubMenu;
  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */

  EnableMenuItem (hSubMenu, IDM_CONNECT,  enable ? ENABLE_MENU : DISABLE_MENU);
  EnableMenuItem (hSubMenu, IDM_RESET, ENABLE_MENU);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, TRUE);
  SendMessage (DocPtr->hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_CONNECT, enable ? FALSE : TRUE);

  /* updates the exit menus/button */
  SendMessage (NetDoc.hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
}

void
SetMainMailMenu (TypDoc FAR * DocPtr)
{
  HMENU hMenu, hSubMenu;

  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */

  EnableMenuItem (hSubMenu, IDM_LOGOUT, MailCtrl.enableLogout);

  hSubMenu = GetSubMenu (hMenu, 2);		/* utilities menu */

  EnableMenuItem (hSubMenu, IDM_MAIL, MailCtrl.enableMail);

  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_MAIL,
			   (MailCtrl.enableMail == MF_ENABLED));
}

void
SetUserMenus (TypDoc FAR * DocPtr, int enable)
{
  HMENU hMenu, hSubMenu;
  UINT mode;

  assert (DocPtr->hWndFrame != 0);

  mode = (enable == ENABLE) ? ENABLE_MENU : DISABLE_MENU;

  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 1);		/* group menu */

  EnableMenuItem (hSubMenu, IDM_FIND, mode);
  EnableMenuItem (hSubMenu, IDM_FIND_NEXT_SAME, mode);

  hSubMenu = GetSubMenu (hMenu, 2);		/* utilities menu */
  EnableMenuItem (hSubMenu, IDM_POST, mode);

  hSubMenu = GetSubMenu (hMenu, 0);		/* network menu */
  EnableMenuItem (hSubMenu, IDM_RESET, mode);

  /* connect item is enabled when all else is disabled and v.v. */
  SetConnectMenuItems (DocPtr, !enable);

  SetMainMailMenu (DocPtr);

  /* set toolbar buttons */
  mode = (enable == ENABLE) ? TRUE : FALSE;

  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_RESET, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND_NEXT_SAME, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_POST, mode);
}

/*---  FUNCTION: About ---------------------------------------------------
 *
 *  Process messages for "About" dialog box
 */

BOOL WINAPI
About (hDlg, message, wParam, lParam)
	 HWND hDlg;
	 unsigned message;
	 WORD wParam;
	 LONG lParam;
{
  switch (message) {
  case WM_INITDIALOG:
	SetDlgItemText (hDlg, IDD_VERSION_NUMBER, (LPSTR)WINVN_VERSION);
	return (TRUE);

  case WM_COMMAND:
	if (wParam == IDOK) {
	  EndDialog (hDlg, TRUE);
	  return (TRUE);
	}
	break;
  }
  return (FALSE);
}

/* ----------------------------------------------------
 * Warn user if colors overlap
 * jsc 9/30/94
 */
BOOL
WarnColors (COLORREF c1, COLORREF c2, char *warnMsg)
{
  if (c1 == c2) {
	sprintf (str, "This color is the same as the %s color.\nSome displays may become unreadable.", warnMsg);
	MessageBox (NetDoc.hDocWnd, str, "Duplicate Color", MB_OK | MB_ICONINFORMATION);
	return TRUE;
  }
  return FALSE;
}

BOOL
SelectGroup (TypDoc far * Doc, TypGroup far * group, BOOL value)
{
  if (group->Selected != value) {
	group->Selected = value;
	if (value)
	  (Doc->SelectedLines)++;
	else
	  (Doc->SelectedLines)--;
  }
  return value;
}

/* --- function CrackGroupLine --------------------------------------

 *  Given a line from a .newsrc file, create a text line containing
 *  a structure of type TypGroup containing the same information.
 *  The line is zero-terminated upon input.
 *
 *    Entry    buf      points to a zero-terminated line from .newsrc.
 *             lineptr  points to a place to put the converted textline.
 *
 *    Exit     The line pointed to by "lineptr" now is a TypLine textline
 *             containing a structure of type TypGroup, containing the
 *             information in the input line from .newsrc.
 *
 *  Returns TRUE iff group was subscribed to.
 */
BOOL
CrackGroupLine (buf, lineptr)
	 char *buf;
	 TypLine *lineptr;
{
  char *grname = (char *) lineptr + sizeof (TypLine) + sizeof (TypGroup);
  TypGroup *group = (TypGroup *) ((char *) lineptr + sizeof (TypLine));
  TypRange *RangePtr;
  BOOL MoreNums;
  unsigned int MyLength;

  group->Subscribed = FALSE;
  group->Selected = FALSE;
  group->Determined = FALSE;
  group->NameLen = 0;
  group->SubjDoc = (TypDoc *) NULL;
  group->ServerEstNum = 0;
  group->ServerFirst = 0;
  group->ServerLast = 0;
  group->HighestPrevSeen = 0;
  group->nRanges = 0;
  group->header_handle = (HANDLE) NULL;
  group->NumUnread = 0;

  /* Copy group name to output line.                               */

  while (*buf && *buf != ':' && *buf != '!') {
	*(grname++) = *(buf++);
	(group->NameLen)++;
  }
  *(grname++) = '\0';

  if (!(*buf)) {
	/* Ran off end of line without seeing ':' or '!'.  Error.      */
  }
  else {
	if (*buf == ':') {
	  group->Subscribed = TRUE;
	}
	buf++;
  }

  /* Look for the highest article number previously seen, in an
   * entry of form "s" followed by a number.
   */

  while (*buf && *buf == ' ' && *buf != '\n')
	buf++;
  if (*buf == 's') {
	buf++;
	GetNum (&buf, &(group->HighestPrevSeen));
  }

  /* Convert the article number ranges to the internal numeric
   * form we use in WinVN.
   */

  RangePtr = (TypRange *) ((char *) lineptr + sizeof (TypLine) +
						   RangeOffset (group->NameLen));

  RangePtr->Last = RangePtr->First = 0;

  /* allow lines like this, with no range data: */
  /* news.group:  */
  /* */
  MoreNums = ((*buf) == '\n' ? FALSE : TRUE);
  while (MoreNums) {
	while (*buf && (*buf == ' ' || *buf == ','))
	  buf++;
	if (GetNum (&buf, &(RangePtr->First))) {
	  /* We have the first number in a range.                     */
	  (group->nRanges)++;
	  RangePtr->Last = RangePtr->First;
	  if (*buf == '-') {
		buf++;
		if (GetNum (&buf, &(RangePtr->Last))) {
		  RangePtr++;
		  /* at this point, we are positioned just after a range */
		}
		else {
		  RangePtr->Last = RangePtr->First;
		  MoreNums = FALSE;
		}
	  }
	  else if (*buf == ',') {
		/* We have a single number "n"; interpret as the range "n-n".
		 */
		RangePtr++;
	  }
	  else {
		/* That must have been the last number.                  */
		MoreNums = FALSE;
	  }
	}
	else {
	  MoreNums = FALSE;
	}
  }
  if (group->nRanges == 0)
	(group->nRanges)++;

  MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) +
	sizeof (TypRange) * (group->nRanges) + sizeof (int);

  lineptr->length = MyLength;
  lineptr->LineID = NextLineID++;
  *(int *) ((char *) lineptr + MyLength - sizeof (int)) = MyLength;

  return (group->Subscribed);
}

/*-- function CursorToTextLine ----------------------------------------
 *
 *   Routine to locate a text line in a document, based on the
 *   cursor position.  Used to figure out which line is being selected
 *   when a user clicks a mouse button.
 *
 *   Entry    X, Y    are the position of the cursor.
 *            DocPtr  points to the current document.
 *
 *   Exit     *LinePtr points to the current line, if one was found.
 *            *BlockPtr points to the current block, if found.
 *            Function returns TRUE iff a line was found that corresponds
 *              to the cursor position.
 */
BOOL
CursorToTextLine (X, Y, DocPtr, BlockPtr, LinePtr)
	 int X;
	 int Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int found;
  int SelLine;

  if (Y < TopSpace || (unsigned) Y > TopSpace + DocPtr->ScYLines * LineHeight ||
	  X < SideSpace) {
	/* Cursor is in no-man's-land at edge of window.               */
	found = FALSE;
  }
  else {
	found = TRUE;
	SelLine = (Y - TopSpace) / LineHeight;
	if ((unsigned int) SelLine >= DocPtr->ScYLines)		/* double-check */
	  return (FALSE);

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);
	AdvanceToActive (BlockPtr, LinePtr);

	for (found = TRUE, il = 0; il < SelLine;) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		found = FALSE;			/* ran off end of document */
		break;
	  }
	  else if ((*LinePtr)->active) {
		il++;
	  }
	}
  }
  return (found);
}

extern char * GetFileExtension (char *ext, char *fileName);

/*-- function ReadNewsrc ----------------------------------------------
 *
 *    Reads NEWSRC into the Net document.
 *    This routine opens NEWSRC, reads & parses the lines into the NetDoc
 *    document, and closes the file.  One call does it all.
 *
 *    Entry    The NetDoc document is assumed to be initialized.
 *
 *    Exit     Returns TRUE if all went well, else zero.
 */
int
ReadNewsrc ()
{
  TypBlock far *BlockPtr;
  TypLine *LinePtr, far * GroupPtr;
  HANDLE hBlock;
  HFILE hRetCode;
  unsigned int Offset;
  TypLineID MyLineID;
  char mybuf[TEMPBUFSIZE];
  TypMRRFile *MRRFile;
  int returned;

  LockLine (NetDoc.hCurAddBlock, NetDoc.AddOffset, NetDoc.AddLineID, &BlockPtr, &GroupPtr);
/*  NetDoc.hDocWnd = NetDoc.hDocWnd; */

  if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, TEMPBUFSIZE)) == NULL) {
	return 0;
  }

#if 0
  env_var = getenv ("WINVN");	/* get path to winvn.ini */
  if (lstrlen (env_var)) {
	lstrcpy (newsrc_filename, env_var);
	if (*(newsrc_filename + lstrlen (newsrc_filename) - 1) == '\\')
	  lstrcat (newsrc_filename, "newsrc");
	else
	  lstrcat (newsrc_filename, "\\newsrc");
  }
  else {
	MessageBox (NetDoc.hDocWnd, "Environment variable WINVN not set.", "Fatal Error", MB_OK | MB_ICONEXCLAMATION);
	return (0);
  }

#endif
  
  hRetCode = MRROpenFile (szNewsSrc, OF_READ, &MRRFile);
  if ((int) hRetCode <= 0) {
	return FALSE;
  }
  else {
	/* Loop to read lines, convert them to internal format, and
	 * insert them into the NetDoc document.
	 */

	LinesInRC = 0;
	while ((returned = MRRReadLine (MRRFile, mybuf, TEMPBUFSIZE)) > (-1)) {
	  mybuf[returned] = '\0';
	  if (CrackGroupLine (mybuf, LinePtr)) {
		NetDoc.CountedLines++;
	  }
	  AddLine (LinePtr, &BlockPtr, &GroupPtr);
	  LinesInRC++;
	}
	MRRCloseFile (MRRFile);
  }

  /* Change the title of the Net document.  I suppose that,
   * strictly speaking, this oughtn't be done in this routine.
   */
  SetNetDocTitle ();
  UnlockLine (BlockPtr, GroupPtr, &hBlock, &Offset, &MyLineID);

  NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
  NetDoc.TopScOffset = sizeof (TypBlock);
  NetDoc.TopScLineID = 0L;
  NetDoc.LongestLine = 0;
  /* Mark lines active or inactive according to ShowUnsubscribed. */
  SetGroupActiveLines ();
  /* we'll check this during WriteNewsrc(). SMR 930224 */
  started_with_no_dolist = !DoList;

  GlobalFreePtr (LinePtr);

  return (TRUE);
}

char *ltoa ();

/*--- function WriteNewsrc ---------------------------------------------
 *
 *  Write out a NEWSRC file, based on the information in the
 *  NetDoc document.  Use the standard Unix "rn" format for .newsrc.
 *
 *    Entry    no parameters
 *             NetDoc   has the group information.
 *
 *    Exit     The NEWSRC file has been written.
 */
void
WriteNewsrc ()
{
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;
  HANDLE hBlock;
  unsigned int Offset;
  TypLineID MyLineID;
  char far *fromptr;
  char *toptr;
  char *NewsLine;
  HFILE hRetCode;
  TypMRRFile *MRRFile;
  int nranges;
  TypGroup far *Group;
  TypRange far *RangePtr;
  BOOL firstrange;
  char newsrc_temp[MAXFILENAME];
  char newsrc_extension[MAXFILENAME];
  int extension_length;

  /* conditions in which we do not want to write it out */
  if (Initializing == INIT_READING_NEWSRC ||
	  Initializing == INIT_SCANNING_NETDOC ||
	  Initializing == INIT_GETTING_LIST) {
	return;
  }

  LockLine (NetDoc.hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr);

  if ((NewsLine = (char *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) == NULL) {
	MessageBox (NetDoc.hDocWnd, "Memory allocation failure.  Newsrc not saved.", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
	return;
  }


  strcpy (newsrc_temp, szNewsSrc);
  GetFileExtension (newsrc_extension, szNewsSrc);
  extension_length = strlen (newsrc_extension);

  if (extension_length != 0) {
	strcpy (newsrc_temp + (strlen(newsrc_temp) - (extension_length+1)), ".tmp");
  } else {
	strcpy (newsrc_temp + strlen(newsrc_temp), ".tmp");
  }

 /*  hRetCode = MRROpenFile (szNewsSrc, OF_CREATE, &MRRFile);  */
 /*  hRetCode = MRROpenFile (szNewsSrc, OF_SHARE_EXCLUSIVE, &MRRFile); */
  hRetCode = MRROpenFile (newsrc_temp, OF_WRITE, &MRRFile); 
  if ((int) hRetCode < 0) {
	MessageBox (NetDoc.hDocWnd, "Could not write to newsrc file", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
  }
  else {
	do {
	  toptr = NewsLine;
	  Group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

	  /* Remove groups only if we performed a LIST command and */
	  /* the server did not have them.  Otherwise if !did_list, the */
	  /* newsrc will be practically empty! */

	  /* Jeeeeez!!  I'm fixing this AGAIN! */
	  /* DoList is not a boolean. SMR 930610 */

	  if ((Group->ServerFirst || Group->ServerEstNum) || !did_list) {
		/* Copy group name                                          */
		fromptr = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
		while (*(toptr++) = *(fromptr++));
		toptr--;

		/* Affix : or ! depending upon whether subscribed.          */
		*(toptr++) = (char) (Group->Subscribed ? ':' : '!');
		*(toptr++) = ' ';

		/* If we know the highest article number on the server,
		 * output it preceded by an "s".
		 */
		if (Group->ServerLast || Group->HighestPrevSeen) {
		  *(toptr++) = 's';
		  if (Group->ServerLast)
			ltoa ((unsigned long) Group->ServerLast, toptr, 10);
		  else
			ltoa ((unsigned long) Group->HighestPrevSeen, toptr, 10);
		  while (*toptr)
			toptr++;
		  *(toptr++) = ' ';
		}

		/* Affix ranges of articles read.                          */
		firstrange = TRUE;
		nranges = Group->nRanges;
		RangePtr = (TypRange far *) ((char far *) Group + RangeOffset (Group->NameLen));

		while ((nranges--) > 0) {
		  /* Write out ',' if not first range of articles.         */

		  if (!firstrange) {
			*(toptr++) = ',';
		  }
		  else {
			firstrange = FALSE;
		  }
		  /* Write out first article in a range.                   */

		  ltoa ((unsigned long) RangePtr->First, toptr, 10);
		  while (*toptr)
			toptr++;

		  /* If the range is of form "n-n", just put out "n"       */

		  if (RangePtr->First != RangePtr->Last) {
			/* Put out the hyphen in middle of range.                */
			*(toptr++) = '-';
			/* Put out the last article in a range.                  */
			ltoa ((unsigned long) RangePtr->Last, toptr, 10);
			while (*toptr)
			  toptr++;
		  }
		  RangePtr++;
		}
		MRRWriteLine (MRRFile, NewsLine, toptr - NewsLine);
	  }
	}
	while (NextLine (&BlockPtr, &LinePtr));
	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	MRRCloseFile (MRRFile);
	unlink (szNewsSrc);
	if (rename (newsrc_temp, szNewsSrc)) {
	  char error_string[256];
	  sprintf (error_string, "Could not rename temporary newsrc file '%s'", newsrc_temp);
	  MessageBox (NetDoc.hDocWnd, error_string, "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
	}
  }
  GlobalFreePtr (NewsLine);
}

/*--- function SetNetDocTitle -------------------------------------------
 *
 */
void
SetNetDocTitle ()
{
  char mybuf[120];

  sprintf (mybuf, "WinVN:  %u groups; %u subscribed", NetDoc.TotalLines,
		   NetDoc.CountedLines);
  SetWindowText (NetDoc.hWndFrame, mybuf);

}

/*--- function SetLineFlag --------------------------------------------
 *
 *  Set some flag in a line in a document.
 *
 *  Entry   Doc      points to the document.
 *          LinePtr  points to th line.
 *
 *  Exit    lFlag    says what to do.
 */
void
SetLineFlag (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  switch (wFlag) {
  case LINE_FLAG_SET:
	SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), wValue);
	break;
  }
}

void
SetExLineFlag (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  static BOOL extending = FALSE;

  switch (wFlag) {
  case LINE_FLAG_SET:
	if ((*LinePtr)->LineID == Doc->ActiveLineID ||
		(*LinePtr)->LineID == Doc->AnchorLineID) {
	  if (extending == TRUE) {
		SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), TRUE);
		extending = FALSE;
	  }
	  else {
		SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), TRUE);
		if (Doc->ActiveLineID == Doc->AnchorLineID)
		  extending = FALSE;
		else
		  extending = TRUE;
	  }
	}
	else
	  SelectGroup (Doc, (TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)), extending);

	break;
  }
}

/*--- function GroupAction --------------------------------------------
 *
 *  Perform some action on a group that is specified by a pointer
 *  to a line in the Net document.
 *  Typically called for each line in the Net document by
 *  ForAllLines.
 *
 *  Entry   Doc      points to NetDoc
 *          LinePtr  points to a line in that document.
 *          lFlag    indicates what to do with that line.
 */
void
GroupAction (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
{
  static char mybuf[MAXINTERNALLINE];

  switch (wFlag) {
  case GROUP_ACTION_SUBSCRIBE:
  case GROUP_ACTION_UNSUBSCRIBE:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Selected) {
	  ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Subscribed = wValue;
	  AddGroupToTable ((char far *) *LinePtr);
	  DeleteLine (BlockPtr, LinePtr);
	}
	break;

  case GROUP_ACTION_CHECK_ACTIVE:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Subscribed || ShowUnsubscribed) {
	  (*LinePtr)->active = TRUE;
	  (*BlockPtr)->NumActiveLines++;
	  NetDoc.ActiveLines++;
	  NetDoc.LongestLine = max (NetDoc.LongestLine, (unsigned)
			 ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
								->NameLen + GROUP_NAME_OFFSET);
	  if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		  ->Subscribed)
		NetDoc.CountedLines++;
	}
	else {
	  (*LinePtr)->active = FALSE;
	}
	break;

  case GROUP_ACTION_GET_GROUPNAMES:
	if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
		->Selected) {
	  ExtractTextLine (&NetDoc, *LinePtr,
					   mybuf, sizeof(mybuf));
	  if (strlen(groupbuf) + strlen(mybuf) + 2 < sizeof(groupbuf)) {
	    if (*groupbuf)
	      strcat(groupbuf, ",");
  	    strcat(groupbuf, mybuf);
	  }
	}
	break;
  }
}

/****************************************************************************

   FUNCTION:   MakeHelpPathName

   PURPOSE:    HelpEx assumes that the .HLP help file is in the same
          directory as the HelpEx executable.This function derives
          the full path name of the help file from the path of the
          executable.

   Taken from HELPEX.C, from the MS Windows SDK.

****************************************************************************/

void
MakeHelpPathName (szFileName, maxchars)
	 char *szFileName;
	 int maxchars;
{
  char *pcFileName;
  int nFileNameLen;

  nFileNameLen = GetModuleFileName (hInst, szFileName, maxchars);
  pcFileName = szFileName + nFileNameLen;

  while (pcFileName > szFileName) {
	if (*pcFileName == '\\' || *pcFileName == ':') {
	  *(++pcFileName) = '\0';
	  break;
	}
	nFileNameLen--;
	pcFileName--;
  }

  if ((nFileNameLen + 13) < maxchars) {
	lstrcat (szFileName, LFN_HELP);
  }

  else {
	lstrcat (szFileName, "?");
  }

  return;
}


int
cursor_to_char_number (X, Y, DocPtr, BlockPtr, LinePtr)
	 int X;
	 int Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int SelLine;
  int charnum = -1;
  SIZE extent;

  char far *textptr;
  int textlen;
  HDC display_context;
  int iTopSpace, iSideSpace, iLineHeight, iCharWidth;
  HANDLE hOldFont;

  if (DocPtr->DocType == DOCTYPE_ARTICLE) {
	iTopSpace = ArtTopSpace;
	iSideSpace = ArtSideSpace;
	iLineHeight = ArtLineHeight;
	iCharWidth = ArtCharWidth;
  }
  else {
	iTopSpace = TopSpace;
	iSideSpace = SideSpace;
	iLineHeight = LineHeight;
	iCharWidth = CharWidth;
  }

  if (Y < iTopSpace || (unsigned) Y > iTopSpace + DocPtr->ScYLines * iLineHeight ||
	  X < iSideSpace) {
	return (-1);
  }
  else {
	SelLine = (Y - iTopSpace) / iLineHeight;

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);

	for (il = 0; il < SelLine; il++) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		return (-1);
		break;
	  }
	}
  }

  /* find the right character on the text line */
  textlen = ((TypText far *) ((char far *) (*LinePtr) +
							  sizeof (TypLine)))->NameLen;


  if (textlen) {
	textptr = (char far *) ((char far *) (*LinePtr) + sizeof (TypLine) + sizeof (TypText));
	display_context = GetDC (DocPtr->hDocWnd);

	if (isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
	  hOldFont = SelectObject (display_context, hFontArtQuote);
	}
	else {						/* prepare to print a normal line */
	  hOldFont = SelectObject (display_context, hFontArtNormal);
	}

	for (charnum = 1; charnum < textlen; charnum++) {
	  GetTextExtentPoint (display_context, (LPSTR) textptr, charnum, &extent);
	  if (extent.cx > (X + (int) DocPtr->ScXOffset * (iCharWidth + 1) - iSideSpace))
		break;
	}
	SelectObject (display_context, hOldFont);
	ReleaseDC (DocPtr->hDocWnd, display_context);
	return (charnum - 1);
  }

}
  long
lcursor_to_char_number (X, Y, DocPtr, BlockPtr, LinePtr)
	 long X;
	 long Y;
	 TypDoc *DocPtr;
	 TypBlock far **BlockPtr;
	 TypLine far **LinePtr;
{
  int SelLine;
  long charnum = -1;
  SIZE extent;

  char far *textptr;
  int textlen;
  HDC display_context;
  int iTopSpace, iSideSpace, iLineHeight, iCharWidth;
  HANDLE hOldFont;

  if (DocPtr->DocType == DOCTYPE_ARTICLE) {
	iTopSpace = ArtTopSpace;
	iSideSpace = ArtSideSpace;
	iLineHeight = ArtLineHeight;
	iCharWidth = ArtCharWidth;
  }
  else {
	iTopSpace = TopSpace;
	iSideSpace = SideSpace;
	iLineHeight = LineHeight;
	iCharWidth = CharWidth;
  }

  if (Y < iTopSpace || (unsigned) Y > iTopSpace + DocPtr->ScYLines * iLineHeight ||
	  X < iSideSpace) {
	return (-1);
  }
  else {
	SelLine = (int) (Y - iTopSpace) / iLineHeight;

	LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
			  BlockPtr, LinePtr);

	for (il = 0; il < SelLine; il++) {
	  if (!NextLine (BlockPtr, LinePtr)) {
		return (-1);
		break;
	  }
	}
  }

  /* find the right character on the text line */
  textlen = ((TypText far *) ((char far *) (*LinePtr) +
							  sizeof (TypLine)))->NameLen;


  if (textlen) {
	textptr = (char far *) ((char far *) (*LinePtr) + sizeof (TypLine) + sizeof (TypText));
	display_context = GetDC (DocPtr->hDocWnd);

	if (isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
	  hOldFont = SelectObject (display_context, hFontArtQuote);
	}
	else {						/* prepare to print a normal line */
	  hOldFont = SelectObject (display_context, hFontArtNormal);
	}

	for (charnum = 1; charnum < textlen; charnum++) {
	  GetTextExtentPoint (display_context, (LPSTR) textptr, (int) charnum, &extent);
	  if (extent.cx > (X + (int) DocPtr->ScXOffset * (iCharWidth + 1) - iSideSpace))
		break;
	}
	SelectObject (display_context, hOldFont);
	ReleaseDC (DocPtr->hDocWnd, display_context);
	return (charnum - 1);
  }

}

/*--- function SetGroupActiveLines --------------------------------------
 *
 *  Go through all the lines in the Net document, marking each
 *  as active or inactive according to whether the corresponding
 *  group is subscribed and whether we are displaying unsubscribed
 *  groups.
 *
 *  Entry:  NetDoc and ShowUnsubscribed are set properly.
 *
 *  Exit:   Each of the lines in NetDoc has had its "active" field
 *          set properly.
 */
void
SetGroupActiveLines ()
{
  NetDoc.ActiveLines = 0;
  NetDoc.CountedLines = 0;
  ForAllBlocks (&NetDoc, SetForBlock, BLOCK_ACTION_SET_ACTIVE, 0);
  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_CHECK_ACTIVE, 0);
}


/* stuff for showing version numbers of the files */

#include "version.h"
#include "version.c"

/*-- function VerListDlgProc ---------------------------------------
 *
 *  Dialog function to handle selection of Version List
 *                                  JD 8/3/94
 */

LRESULT CALLBACK
VerListDlgProc (HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  int j;
  char far *cptr;
  char szVersion[64];

  switch (iMessage) {
  case WM_INITDIALOG:
	sprintf (szVersion, "Version %s", (LPSTR) WINVN_VERSION);
	SetDlgItemText ((HWND) hDlg, IDD_VERSION_NUMBER, (LPSTR) szVersion);
	hVerDlgList = GetDlgItem (hDlg, IDD_VERSION_LISTBOX);
	SendMessage (hVerDlgList, WM_SETREDRAW, FALSE, 0L);

	cptr = 0;
	for (j = 0; j < (sizeof (version_string) / sizeof version_string[0]); j++) {
	  cptr = version_string[j];
	  SendMessage (hVerDlgList, LB_ADDSTRING, 0, (LONG) cptr);
	}
	SendMessage (hVerDlgList, WM_SETREDRAW, TRUE, 0L);
	return TRUE;
	break;

  case WM_COMMAND:
	switch (wParam) {
	case IDOK:
	  EndDialog (hDlg, TRUE);
	  break;

	case IDCANCEL:
	  EndDialog (hDlg, FALSE);
	  break;

	default:
	  return FALSE;
	}
	break;

  case WM_DESTROY:
	break;

  default:
	return FALSE;
	break;
  }
  return TRUE;
}

/*  Display Dialog Box to show version info   JD 8/4/94 */
void
show_version_strings (HWND hWnd)
{
  lpfnWinVnVersionListDlg = (DLGPROC) MakeProcInstance ((FARPROC) VerListDlgProc, (HINSTANCE) hInst);
  DialogBox (hInst, "WINVNVERSIONLIST", hWnd, lpfnWinVnVersionListDlg);
  FreeProcInstance ((FARPROC) lpfnWinVnVersionListDlg);

}


long WINAPI
WinVnConfFrameWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 unsigned message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  DLGPROC lpProcAbout;
  RECT myRect;					/* selection rectangle                */
  static HANDLE ToolBarLib;
  char mybuf[MAXINTERNALLINE];
  int found;
  PAINTSTRUCT ps;				/* paint structure          */
  HDC hDC;						/* handle to display context */
  int i, j, numComposeWnds;
  TBBUTTON tbButton[MAXTBBUTTONS];
  HMENU hMenu, hSubMenu;
  BOOL continueFind;
  TypLine far *LinePtr;
  TypBlock far *BlockPtr;

  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
    return 0;

  switch (message) {
#ifdef USE_3D_CONTROLS
  case WM_SYSCOLORCHANGE:
	Ctl3dColorChange ();
	break;
#endif

  case WM_SETFOCUS:
   SetCapsLockText(hWnd);
   SetNumLockText(hWnd);
   return (DefWindowProc (hWnd, message, wParam, lParam));
	break;

  case WM_SYSCOMMAND:
	switch (wParam) {
		case ID_ABOUT:
			lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
			DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
			FreeProcInstance ((FARPROC) lpProcAbout);
			break;

		case IDM_RESTORE_ALL:
			RestoreWindows();
			break;

		default:
			return (DefWindowProc (hWnd, message, wParam, lParam));
	}
	break;

  case WM_CREATE:
	GetClientRect (hWnd, &myRect);

/* add common controls for toolbar (jlg) */

	i = j = 0;
	tbButton[j].iBitmap = 0;	/* connect */

	tbButton[j].idCommand = IDB_TOGGLE_CONNECT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* reset */

	tbButton[j].idCommand = IDM_RESET;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* find */

	tbButton[j].idCommand = IDM_FIND;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* find next */

	tbButton[j].idCommand = IDM_FIND_NEXT_SAME;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* New Article */

	tbButton[j].idCommand = IDM_POST;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* New Mail */

	tbButton[j].idCommand = IDM_MAIL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* Save Configuration */

	tbButton[j].idCommand = IDM_SAVE_CONFIG;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* Toggle View Unsubscribed */

	tbButton[j].idCommand = IDB_TOGGLE_VIEW_UNSUB;
	if (ShowUnsubscribed == TRUE)
	  tbButton[j].fsState = TBSTATE_ENABLED | TBSTATE_CHECKED;
	else
	  tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	/* Exit */

	tbButton[j].idCommand = IDB_FASTEXIT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	tbButton[++j].iBitmap = 8;	/* separator */

	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

	tbButton[++j].iBitmap = ++i;	/* About */

	tbButton[j].idCommand = ID_ABOUT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;

	NetDoc.hWndTB = CreateToolbar (hWnd,
								   WS_BORDER | WS_VISIBLE,
								   (WORD) GetMenu (hWnd),
								   i + 1,
								   hInst,
								   IDB_TOOLBAR,
								   tbButton,
								   j + 1);

	assert (NetDoc.hWndTB != NULL);

	NetDoc.hDocWnd = CreateWindow ("WinVn",
								   "WinVN -- Usenet News Reader",
								   WS_CHILD | WS_VSCROLL | WS_HSCROLL,
								   0,	/* Initial X position */
								   TOOLBARHEIGHT,	/* Initial Y position */
								   myRect.right - myRect.left,	/* Initial X width */
	(myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,	/* Initial Y height */
								   hWnd,
								   NULL,
								   hInst,
								   NULL);

	assert (NetDoc.hDocWnd != NULL);

	SetHandleBkBrush (NetDoc.hDocWnd, hListBackgroundBrush);
/*         ShowWindow (NetDoc.hDocWnd, SW_SHOWNORMAL); */

	/* Add the "About" and "Restore All" options to the system menu.            */
	hMenu = GetSystemMenu (hWnd, FALSE);
	AppendMenu (hMenu, MF_SEPARATOR, 0, (LPCSTR) NULL);
	AppendMenu (hMenu, MF_STRING, IDM_RESTORE_ALL, (LPCSTR) "Restore &All");
	AppendMenu (hMenu, MF_STRING, ID_ABOUT, (LPCSTR) "A&bout WinVn...");

#ifdef USE_SPLASH
	/* Add the Splash option */
	hMenu = GetMenu(hWnd);
	hSubMenu = GetSubMenu (hMenu, 3);	/* Config menu */
	InsertMenu (hMenu, IDM_SAVE_CONFIG, MF_STRING|MF_BYCOMMAND, 
					IDM_USE_SPLASH, (LPCSTR) "Show Splash Screen");
	InsertMenu (hMenu, IDM_SAVE_CONFIG, MF_SEPARATOR|MF_BYCOMMAND, 0, (LPCSTR) NULL);
#endif
	break;

  case WM_MYINITMENU:
  case WM_INITMENU:
	hMenu = GetMenu (hWnd);

	hSubMenu = GetSubMenu (hMenu, 0);	/* network menu */
	
	/* don't allow disconnect until connection is established */
	i = (Initializing == INIT_ESTAB_CONN || Initializing == INIT_NOT_CONNECTED);
	EnableMenuItem (hSubMenu, IDM_RECONNECT,  i ? DISABLE_MENU : ENABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_DISCONNECT, i ? DISABLE_MENU : ENABLE_MENU);

	i = (Initializing != INIT_ESTAB_CONN);
	EnableMenuItem (hSubMenu, IDM_EXIT,  i ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_QUIT,  i ? ENABLE_MENU : DISABLE_MENU);
	SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_FASTEXIT, i ? TRUE : FALSE);

	hSubMenu = GetSubMenu (hMenu, 1);	/* group menu */
	EnableMenuItem (hSubMenu, IDM_SUBSCRIBE, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNSUBSCRIBE, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_GROUP_TOP, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNSEL_ALL, NetDoc.SelectedLines ? ENABLE_MENU : DISABLE_MENU);

	hSubMenu = GetSubMenu (hMenu, 2);	/* Utilities menu */
	EnableMenuItem (hMenu, IDM_DECODE_FILE,
					(CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hMenu, IDM_ENCODE_FILE,
					(CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_POST,
					NumPostWnds ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_MAIL,
					NumMailWnds ? ENABLE_MENU : DISABLE_MENU);

	hSubMenu = GetSubMenu (hMenu, 3);	/* Config menu */
	CheckMenuItem (GetSubMenu (hSubMenu, 14), IDM_TRUE_INVERSE_SELECTIONS,
		 MF_BYCOMMAND | (UseInverseSelections ? MF_CHECKED : MF_UNCHECKED));
#ifdef USE_SPLASH
	CheckMenuItem (hSubMenu, IDM_USE_SPLASH,
		 MF_BYCOMMAND | (ShowSplashScreen ? MF_CHECKED : MF_UNCHECKED));
#endif

	hSubMenu = GetSubMenu (hMenu, 4);	/* Window menu */
	numComposeWnds = NumPostWnds + NumMailWnds;
	/* close submenu */
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_ARTICLE,
					NumArticleWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_GROUP,
					NumGroupWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_COMPOSE,
					numComposeWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_STATUS,
					NumStatusTexts ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL,
					(NumArticleWnds || NumGroupWnds || NumStatusTexts || numComposeWnds) ? MF_ENABLED : MF_GRAYED);
	/* minimize submenu */
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_ARTICLE,
					NumArticleWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_GROUP,
					NumGroupWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_COMPOSE,
					numComposeWnds ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL_STATUS,
					NumStatusTexts ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
	/* can always minimize all - at least the main window! */
	EnableMenuItem (GetSubMenu (hSubMenu, 2), IDM_MINIMIZE_ALL, MF_ENABLED);
	break;

  case WM_SIZE:
	GetClientRect (hWnd, &myRect);
	ShowWindow (NetDoc.hDocWnd, SW_SHOWNORMAL);
	MoveWindow (NetDoc.hDocWnd, 0, TOOLBARHEIGHT,
				myRect.right - myRect.left,
	(myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
				TRUE);
	MoveWindow (NetDoc.hWndTB, 0, 0,
				myRect.right - myRect.left,
				TOOLBARHEIGHT,
				TRUE);
	break;

  case WM_CLOSE:
	if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	}
	if (!ConfirmSaveOnExit) {
	  SaveNewsrc = SaveConfig = TRUE;
	}
	else {
	  /* WinVnExitDlg sets SaveNewsrc and SaveConfig */
	  if (!DialogBox (hInst, "WinVnExit", hWnd, lpfnWinVnExitDlg)) {
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		break;
	  }
	}
	SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	DestroyWindow (hWnd);
	return (0);
	break;

  case WM_DESTROY:
	if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	}
	if (Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) {
	  PutCommData ("QUIT\r\n", 6);
	}

	MailClose (hWnd);
	Disconnect ();

	if (SaveNewsrc)
	  WriteNewsrc ();

	if (SaveConfig)
	  WriteWinvnProfile ();

	/* Remove resources before exiting program */
	FreeTextBlock (Signature);
	if (MailList)
	  FreeTextBlock (MailList);

	DestroyFonts ();
	DestroyBitmaps ();

	if (hListBackgroundBrush)
	  DeleteObject (hListBackgroundBrush);
	if (hArticleBackgroundBrush)
	  DeleteObject (hArticleBackgroundBrush);
	if (hStatusBackgroundBrush)
	  DeleteObject (hStatusBackgroundBrush);
	if (StatbarPntData.hFontStatbar)
	  DeleteObject (StatbarPntData.hFontStatbar);
	PostQuitMessage (0);
	return (0);
	break;

  case WM_VSCROLL:
  case WM_HSCROLL:
	break;
  case WM_PAINT:
/*	if (Initializing == INIT_READY || Initializing == INIT_DONE) */
/*	  SetUserMenus (&NetDoc, ENABLE); */

	/* paint status bar  */
	hDC = BeginPaint (hWnd, &ps);
	PaintStatbar (hWnd, hDC, &NetDoc);
	EndPaint (hWnd, &ps);
	if (GetFocus () == hWnd)
	  SetFocus (NetDoc.hDocWnd);
	return (DefWindowProc (hWnd, message, wParam, lParam));
	break;

  case WM_COMMAND:
	switch (LOWORD (wParam)) {
	case IDM_QUIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }  
	  SaveNewsrc = SaveConfig = FALSE;
	  SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	  DestroyWindow (hWnd);
	  return (0);
	  break;
	case IDM_EXIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }  
	  SendMessage (hWnd, WM_CLOSE, 0, 0L);
	  break;

	case IDM_CONNECT:
	  Connect ();				/* menus are enabled in WM_PAINT once connection is established */
	  break;
    
    case IDM_SAVE_NEWSRC:
      WriteNewsrc ();
      MessageBox (NetDoc.hDocWnd, "WinVN NEWSRC Saved", "WinVN Resource File", MB_OK);
	  break;
	  
	case IDB_TOGGLE_VIEW_UNSUB:
	  if (ShowUnsubscribed == FALSE)
		ShowUnsubscribed = TRUE;
	  else
		ShowUnsubscribed = FALSE;
	  SendMessage (NetDoc.hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_VIEW_UNSUB, ShowUnsubscribed);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDB_FASTEXIT:
	  if (Initializing == INIT_ESTAB_CONN) {
		break;		/* can't quit if in process of connecting (will GPF) */
	  }
	  WriteNewsrc ();
	  WriteWinvnProfile ();
	  SaveNewsrc = SaveConfig = FALSE;	/* we've already done them */
	  SetHandleBkBrush (NetDoc.hDocWnd, GetStockObject (WHITE_BRUSH));
	  DestroyWindow (hWnd);
	  return (0);
	  break;

	case IDB_TOGGLE_CONNECT:
	  if (Initializing == INIT_NOT_CONNECTED) {
		SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
		Connect ();
		break;
	  }
	  /* else fall into disconnect */
	  SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
	case IDM_DISCONNECT:
	  /* If the NNTP server disconnected, then Initializing == INIT_NOT_CONNECTED */
	  if ((Initializing != INIT_NOT_CONNECTED && ConfirmDisconnect &&
		   (MessageBox (NetDoc.hDocWnd,
						"Are you sure you wish to disconnect from the server?\n"
						"All active windows will be closed.",
						"Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO))) {
		SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, TRUE);
		SendMessage (NetDoc.hWndTB, TB_CHECKBUTTON, IDB_TOGGLE_CONNECT, TRUE);
		break;
	  }

	  if (Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) {
		PutCommData ("QUIT\r\n", 6);
	  }
	  Disconnect ();
	  break;


	case IDM_VIEW_SEL_GROUP:
	  break;

	case IDM_SHOW_SUBSCR:
	case IDM_SHOW_ALL_GROUP:
	case IDM_SEL_SUBSCR:
	case IDM_SELECT_ALL:
	  MessageBox (hWnd, "Command not implemented","Sorry", MB_OK);
	  break;

	case IDM_UNSEL_ALL:
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_SUBSCRIBE:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
	  BuildPtrList ();
	  MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  SetNetDocTitle ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_UNSUBSCRIBE:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_UNSUBSCRIBE, FALSE);
	  BuildPtrList ();
	  ShellSort (NewGroupTable,
				 (size_t) nNewGroups,
				 (size_t) sizeof (void far *),
				 GroupCompare);
	  MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  NetDoc.LongestLine = 0;
	  SetGroupActiveLines ();
	  ScreenToTop (&NetDoc);
	  SetNetDocTitle ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;

	case IDM_GROUP_TOP:
	  InitGroupTable ();
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
	  BuildPtrList ();
	  MergeGroups (ADD_SUBSCRIBED_TOP_OF_DOC);
	  CleanUpGroupTable ();
	  ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
	  ScreenToTop (&NetDoc);
	  InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  break;


	case IDM_COMMOPTIONS:
	  DialogBox (hInst, "WinVnComm", hWnd, lpfnWinVnCommDlg);
	  break;

	case IDM_CONFIG_PERSONAL:
	  DialogBox (hInst, "WinVnPersonal", hWnd, lpfnWinVnPersonalInfoDlg);
	  break;
	  
	case IDM_CONFIG_CONFIRMATIONS:
	  DialogBox (hInst, "WinVnConfirmations", hWnd, lpfnWinVnConfirmationDlg);
	  break;

	case IDM_CONFIG_LOG:
	  DialogBox (hInst, "WinVnLogOpts", hWnd, lpfnWinVnLogOptDlg);
	  break;

	case IDM_CONFIG_SMART_FILER:
	  DialogBox (hInst, "WinvnSmartFiler", NetDoc.hDocWnd, lpfnWinVnSmartFilerDlg);
	  break;

	case IDM_CONFIG_ARTLIST:
	  DialogBox (hInst, "WinVnArticleListPrefs", hWnd, lpfnWinVnArtListDlg);
	  break;

	case IDM_CONFIG_ARTICLE:
	  DialogBox (hInst, "WinVnArticlePrefs", hWnd, lpfnWinVnArticleDlg);
	  break;

	case IDM_CONFIG_GROUPLIST:
	  DialogBox (hInst, "WinVnGroupListPrefs", hWnd, lpfnWinVnGroupListDlg);
	  break;

	case IDM_COMPOSE_PREFS:
	  DialogBox (hInst, "WinVnComposePrefs", hWnd, lpfnWinVnComposePrefsDlg);
	  break;

	case IDM_ATTACH_PREFS:
	  DialogBox (hInst, "WinVnAttachPrefs", hWnd, lpfnWinVnAttachPrefsDlg);
	  break;

	case IDM_CONFIG_CODING:
	  DialogBox (hInst, "WinVnCodingPrefs", hWnd, lpfnWinVnCodingPrefsDlg);
	  break;

	case IDM_FONT_WINVNSYSTEM:
	  if (AskForFont (NetDoc.hDocWnd, WinVnFontFace, &WinVnFontSize, WinVnFontStyle))
		break;
	  InitWinVnFonts ();
	  InitStatBar (&StatbarPntData);
	  SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
	  InvalidateRect (NetDoc.hWndFrame, NULL, TRUE);
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshArticleWnds ();
	  RefreshGroupWnds ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_COMPOSITION:
	  if (AskForFont (NetDoc.hDocWnd, CompositionFontFace, &CompositionFontSize, CompositionFontStyle))
		break;
	  InitCompositionFonts ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_GROUPLIST:
	  if (AskForFont (NetDoc.hDocWnd, ListFontFace, &ListFontSize, ListFontStyle))
		break;
	  InitListFonts ();
	  SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshGroupWnds ();
	  break;

	case IDM_FONT_ARTICLE_TEXT:
	  if (AskForFont (NetDoc.hDocWnd, ArticleFontFace, &ArticleFontSize, ArticleFontStyle))
		break;
	  InitArticleFonts ();
	  RefreshArticleWnds ();
	  RefreshComposeWnds ();
	  break;

	case IDM_FONT_STATUS_TEXT:
	askStatusFont:;
	  if (AskForFont (NetDoc.hDocWnd, StatusFontFace, &StatusFontSize, StatusFontStyle))
		break;
	  InitStatusFonts ();
	  if (STATUSWIDTH > xScreen) {
		MessageBox (NetDoc.hDocWnd, "The status window will not fit on your screen with this font.\nPlease select a smaller font",
					"Font too big", MB_OK | MB_ICONSTOP);
		goto askStatusFont;
	  }
	  RefreshStatusWnds ();
	  break;

	case IDM_FONT_PRINT_TEXT:
	  if (AskForFont (NetDoc.hDocWnd, PrintFontFace, &PrintFontSize, "Printer"))
		break;
	  InitPrintFonts ();
	  break;

	case IDM_TRUE_INVERSE_SELECTIONS:
	  UseInverseSelections = !UseInverseSelections;
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  RefreshGroupWnds ();
	  break;

#ifdef USE_SPLASH
	case IDM_USE_SPLASH:
	  ShowSplashScreen = !ShowSplashScreen;
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;
#endif

	case IDM_COLOR_SUBSCRIBED:
	  if (AskForColor (NetDoc.hDocWnd, &NetSubscribedColor))
		break;
	  WarnColors (NetSubscribedColor, ListBackgroundColor, "List Background");
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_UNSUBSCRIBED:
	  if (AskForColor (NetDoc.hDocWnd, &NetUnSubscribedColor))
		break;
	  WarnColors (NetUnSubscribedColor, ListBackgroundColor, "List Background");
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_SEEN:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleSeenColor))
		break;
	  WarnColors (ArticleSeenColor, ListBackgroundColor, "List Background");
	  RefreshGroupWnds ();
	  break;

	case IDM_COLOR_UNSEEN:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleUnSeenColor))
		break;
	  WarnColors (ArticleUnSeenColor, ListBackgroundColor, "List Background");
	  RefreshGroupWnds ();
	  break;

	case IDM_COLOR_ARTICLE_TEXT:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleTextColor))
		break;
	  WarnColors (ArticleTextColor, ArticleBackgroundColor, "Article Background");
	  RefreshArticleWnds ();
	  break;

	case IDM_COLOR_STATUS_TEXT:
	  if (AskForColor (NetDoc.hDocWnd, &StatusTextColor))
		break;
	  WarnColors (StatusTextColor, StatusBackgroundColor, "Status Background");
	  RefreshStatusWnds ();
	  break;

	case IDM_COLOR_LIST_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &ListBackgroundColor))
		break;
	  DeleteObject (hListBackgroundBrush);
	  hListBackgroundBrush = CreateSolidBrush (ListBackgroundColor);
	  SetHandleBkBrush (hWnd, hListBackgroundBrush);

	  WarnColors (ArticleSeenColor, ListBackgroundColor, "Seen Article") ||
		WarnColors (ArticleUnSeenColor, ListBackgroundColor, "Unseen Article") ||
		WarnColors (NetSubscribedColor, ListBackgroundColor, "Subscribed Group") ||
		WarnColors (NetUnSubscribedColor, ListBackgroundColor, "Unsubscribed Group");

	  RefreshGroupWnds ();
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  break;

	case IDM_COLOR_ARTICLE_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &ArticleBackgroundColor))
		break;
	  DeleteObject (hArticleBackgroundBrush);
	  hArticleBackgroundBrush = CreateSolidBrush (ArticleBackgroundColor);

	  WarnColors (ArticleTextColor, ArticleBackgroundColor, "Article Text");
	  RefreshArticleWnds ();
	  break;

	case IDM_COLOR_STATUS_BACKGROUND:
	  if (AskForColor (NetDoc.hDocWnd, &StatusBackgroundColor))
		break;
	  DeleteObject (hStatusBackgroundBrush);
	  hStatusBackgroundBrush = CreateSolidBrush (StatusBackgroundColor);
	  RefreshStatusWnds ();

	  WarnColors (StatusTextColor, StatusBackgroundColor, "Status Text");
	  break;

	case IDM_WINDOW_CASCADE:
	  CascadeWindows ();
	  break;

	case IDM_CLOSE_ALL:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseWindows ();
	  break;

	case IDM_CLOSE_ALL_ARTICLE:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open article windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseArticleWnds ();
	  break;

	case IDM_CLOSE_ALL_GROUP:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open group windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseGroupWnds ();
	  break;

	case IDM_CLOSE_ALL_COMPOSE:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open composition windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseComposeWnds ();
	  break;

	case IDM_CLOSE_ALL_STATUS:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to close all open status windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  CloseStatusWnds ();
	  break;

	case IDM_MINIMIZE_ALL:
	  MinimizeWindows ();
	  break;

	case IDM_MINIMIZE_ALL_ARTICLE:
	  MinimizeArticleWnds ();
	  break;

	case IDM_MINIMIZE_ALL_GROUP:
	  MinimizeGroupWnds ();
	  break;

	case IDM_MINIMIZE_ALL_COMPOSE:
	  MinimizeComposeWnds ();
	  break;

	case IDM_MINIMIZE_ALL_STATUS:
	  MinimizeStatusWnds ();
	  break;

	case IDM_RESTORE_ALL:
	  RestoreWindows ();
	  break;

	case IDM_SEND_ALL_POST:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to send all open post windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  BatchSend (DOCTYPE_POSTING);
	  break;

	case IDM_SEND_ALL_MAIL:
	  if (ConfirmBatchOps)
		if (MessageBox (NetDoc.hDocWnd, "Are you sure you wish to send all open mail windows?",
					  "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)
		  break;
	  BatchSend (DOCTYPE_MAIL);
	  break;

	case IDM_DECODE_FILE:
	  if (TestCodingBusy (NetDoc.hDocWnd, "Can't decode file"))
		break;

	  if (!DialogBoxParam (hInst, "WinVnDecodeArts", NetDoc.hDocWnd, lpfnWinVnDecodeArtsDlg, 0))
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  else
		DecodeFile (hWnd);
	  break;

	case IDM_ENCODE_FILE:
	  {
		TypAttachment attach;
		if (TestCodingBusy (NetDoc.hDocWnd, "Can't encode file"))
		  break;
		if (!DialogBoxParam (hInst, "WinVnEncode", NetDoc.hDocWnd, lpfnWinVnEncodeDlg, (LPARAM) & attach))
		  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		else
		  EncodeToFile (NetDoc.hDocWnd, &attach);
	  }
	  break;

	case IDM_RESET:
	  if (!CommBusy || SendingMail || SendingPost) {
		  if (MessageBox (NetDoc.hDocWnd,
				  "Are you sure you wish to reset the server protocol?\n"
				   "Any active communications and coding sessions will be aborted.",
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
			AbortAllComm ();
			EnableMenuItem (hSubMenu, IDM_CONNECT, ENABLE_MENU);
			
		  }
	  } else {	
		  if (MessageBox (NetDoc.hDocWnd,
				"Cannot reset server protocol while receiving server information.\n"
				"Do you wish to disconnect and reconnect?\n"
				"All active windows will be closed.",
				"Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
			Reconnect();
		  }
	  }
	  break;

	case IDM_RECONNECT:
	  if ((Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) &&
	  	  (!ConfirmDisconnect ||
		  (MessageBox (NetDoc.hDocWnd,
					"Are you sure you wish to disconnect and reconnect to the server?\n"
					   "All active windows will be closed.",
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES))) {
			Reconnect();
		}
		break;
		
	case IDM_SAVE_CONFIG:
	  WriteWinvnProfile ();
	  MessageBox (NetDoc.hDocWnd, "WinVN Configuration Saved", "WinVN Configuration", MB_OK);
	  break;

	case IDM_SAVE_WINDOW:
	  SaveWindowPositions ();
	  break;

	case IDM_FIND:
	  FindDoc = &NetDoc;
	  if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0])
		strcpy (FindDoc->SearchStr, LastGroupNameFind);

	  if (DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg)) {
		found = DoFind (TRUE, GroupListMultiSelect);
		if (!found) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, NetDoc.SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "Not found", MB_OK);
		}
		else {
		  strcpy (LastGroupNameFind, FindDoc->SearchStr);
		  if (LineIDtoLinePtr (FindDoc->FindLineID, FindDoc, &BlockPtr, &LinePtr)) {
			SetSelections (FindDoc, LinePtr);
			if (GroupListMultiSelect) {
			  /* set the active and anchor line IDs = FindLineID */
			  FindDoc->AnchorLineID = FindDoc->FindLineID;
			}
		  }
		}
	  }
	  break;

	case IDM_FIND_NEXT_SAME:
	  FindDoc = &NetDoc;
	  continueFind = TRUE;
	  if (!FindDoc->SearchStr[0]) {
		if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0])
		  strcpy (FindDoc->SearchStr, LastGroupNameFind);
		continueFind = DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
	  }

	  if (continueFind && FindDoc->SearchStr[0]) {
		found = DoFind (!FindDoc->hFindBlock && !FindDoc->FindLineID, GroupListMultiSelect);
		if (!found) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, NetDoc.SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "No more occurrences", MB_OK);
		}
		else {
		  if (LineIDtoLinePtr (FindDoc->FindLineID, FindDoc, &BlockPtr, &LinePtr)) {
			SetSelections (FindDoc, LinePtr);
			if (GroupListMultiSelect) {
			  /* set the active and anchor line IDs = FindLineID */
			  FindDoc->AnchorLineID = FindDoc->FindLineID;
			}
		  }
		}
	  }
	  break;

	case ID_ABOUT:
	  lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
	  DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
	  FreeProcInstance ((FARPROC) lpProcAbout);
	  break;

	case IDM_HELP:
	  MakeHelpPathName (mybuf, MAXINTERNALLINE);
	  WinHelp (NetDoc.hDocWnd, mybuf, HELP_INDEX, 0L);
	  break;

	case IDM_MAIL:
	  (MailCtrl.fnMlWinCreate) (hWnd, (TypDoc *) NULL, DOCTYPE_MAIL);
	  break;

	case IDM_LOGOUT:
	  (MailCtrl.fnMlLogout) (hWnd);
	  break;

	case IDM_SHOW_VERSION:
	  show_version_strings (hWnd);
	  break;

	case IDM_POST:
	  /*
	   *  DialogBoxParam (hInst, "WinVnGeneric", hWnd,
	   *                  lpfnWinVnGenericDlg, (LPARAM) (char far *) "Newsgroup(s):");
	   */

	  /* NewsgroupsPtr = (char *) NULL; */
	  *groupbuf = '\0';
	  ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_GET_GROUPNAMES, TRUE);
	  NewsgroupsPtr = &groupbuf[0];
	  CreateComposeWnd (hWnd, (TypDoc *) NULL, DOCTYPE_POSTING);
	  break;
	}

  default:
	if (GetFocus () == hWnd)
	  SetFocus (NetDoc.hDocWnd);
	return (DefWindowProc (hWnd, message, wParam, lParam));
	break;
  }

  if (GetFocus () == hWnd)
	SetFocus (NetDoc.hDocWnd);
  return (0);
}

/*
 * Local Variables:
 * tab-width: 4
 * end:
 */

/* Last line of WVUSENET.C */
@


1.71
log
@Add a simple Save NewsRC option to Network menu
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.70 1995/02/07 23:02:16 jcooper Exp $
d146 1
a146 1
	}
d887 1
d2123 1
a2123 1
	if (SaveNewsrc)				/* do we need  && !Initializing? */
d2250 1
a2250 2
	  MessageBox (hWnd, "Command not implemented",
				  "Sorry", MB_OK);
d2309 1
d2606 2
@


1.70
log
@About box shows IDD_VERSION_NUMBER in static text
Added Restore All command to Window menu and to main window Sys menu
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.69 1995/01/25 00:27:37 dumoulin Exp $
d1662 74
d1737 2
d2185 6
a2190 1

@


1.69
log
@use a temporary file when writing newsrc [in case of disaster]
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.68 1995/01/19 02:47:25 jglasser Exp root $
d962 1
d1793 13
a1805 5
	if (wParam == ID_ABOUT) {
	  lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
	  DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
	  FreeProcInstance ((FARPROC) lpProcAbout);
	  break;
d1807 1
a1807 2
	else
	  return (DefWindowProc (hWnd, message, wParam, lParam));
d1921 1
a1921 1
	/* Add the "About" option to the system menu.            */
d1924 1
d2475 4
@


1.68
log
@Jody Glasser's changes of 950113
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.67 1995/01/03 19:00:30 jcooper Exp $
d1181 2
d1228 1
a1228 2


d1299 3
d1317 14
a1330 1
  hRetCode = MRROpenFile (szNewsSrc, OF_CREATE, &MRRFile);
d1408 6
@


1.67
log
@don't write newsrc under certain conditions
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.66 1994/12/19 17:08:24 rushing Exp jcooper $
d603 1
a603 1

d607 1
a607 1

d1762 6
@


1.66
log
@changed multi-line comment to #if 0/#endif
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.65 1994/12/19 15:02:10 rushing Exp rushing $
d1299 7
@


1.65
log
@change c++ comments to c
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.64 1994/12/19 15:01:26 rushing Exp rushing $
d768 2
a769 1
/*				  RangePtr = (TypRange far *) ((char far *) MyGroup + RangeOffset (MyGroup->NameLen));
d775 1
a775 1
*/
@


1.64
log
@disconnect logic fix (jglasser)
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.63 1994/12/13 12:57:41 jcooper Exp rushing $
d26 4
a29 4
// This is for the special case where the user invoked the
// program with dolist=0, then turned it on.  Without checking
// this condition, the newsrc will be truncated to only those
// groups read.  SMR 930224
d251 4
a254 4
	  // make this behave more like a multi-select listbox (jlg)
	  // if click,       make the line the only one selected
	  // if ctrl-click,  add the line to the selection
	  // if shift-click, select from previously selected to current
d286 1
a286 1
		SetCapture (hWnd);		// release capture on button up
d450 1
a450 1
//    SetHandleBkBrush (hWndView, hListBackgroundBrush);
d494 1
a494 1
//       SetCapture (hWnd);
d504 2
a505 2
// If we've lost the connection for some reason, kill the timer
	// that's banging on the socket.
d553 1
a553 1
// DWORD Rop;
d599 1
a599 1
		  SetTextColor (hDC, RGB (200, 200, 200));	// if background is black, make text white
d602 1
a602 1
		  SetTextColor (hDC, RGB (0, 0, 0));	// otherwise text is black
d627 1
a627 1
		// fall into INIT_DONE
d633 1
a633 1
		MyColors[0] = NetUnSubscribedColor;		// unsubscribed/unselected    
d636 1
a636 1
		MyColors[1] = NetSubscribedColor;	// subscribed/unselected
d641 1
a641 1
		  MyColors[2] = ListBackgroundColor;	// unsubscribed/selected
d644 1
a644 1
		  MyColors[3] = ListBackgroundColor;	// subscribed/selected
d647 1
a647 1
		  MyColors[2] = ListBackgroundColor;	// unsubscribed/selected
d650 1
a650 1
		  MyColors[3] = ListBackgroundColor;	// subscribed/selected
d655 1
a655 1
		  if (NetUnSubscribedColor == RGB (0, 0, 0) ||	// unsub/selected
d661 1
a661 1
		  if (NetSubscribedColor == RGB (0, 0, 0) ||	// sub/selected
d772 1
a772 1
					  indicator = 'u';	// was a '*'
d812 3
a814 3
//       SetTextColor (hDC, MyColors[1]);
		//       SetBkColor (hDC, MyBack[1]);
		//       SelectObject (hDC, UnSelectedBrush);
d830 1
a830 1
	//CloseComm (CommDevice);
d876 2
a877 2
// if enable is true then enable connect, disable disconnect
// disconnect is managed in INTITMENU
d883 1
a883 1
  hSubMenu = GetSubMenu (hMenu, 0);		// network menu
d889 1
a889 1
  // updates the exit menus/button
d899 1
a899 1
  hSubMenu = GetSubMenu (hMenu, 0);		// network menu
d903 1
a903 1
  hSubMenu = GetSubMenu (hMenu, 2);		// utilities menu
d922 1
a922 1
  hSubMenu = GetSubMenu (hMenu, 1);		// group menu
d927 1
a927 1
  hSubMenu = GetSubMenu (hMenu, 2);		// utilities menu
d930 1
a930 1
  hSubMenu = GetSubMenu (hMenu, 0);		// network menu
d933 1
a933 1
  // connect item is enabled when all else is disabled and v.v.
d938 1
a938 1
  // set toolbar buttons
d1078 3
a1080 3
  // allow lines like this, with no range data:
  // news.group: 
  //
d1204 1
a1204 1
//  NetDoc.hDocWnd = NetDoc.hDocWnd;
d1260 1
a1260 1
  // we'll check this during WriteNewsrc(). SMR 930224
d1767 1
a1767 1
// add common controls for toolbar (jlg)
d1770 1
a1770 1
	tbButton[j].iBitmap = 0;	// connect
d1775 1
a1775 1
	tbButton[++j].iBitmap = ++i;	// reset
d1781 1
a1781 1
	tbButton[++j].iBitmap = 8;	// separator
d1786 1
a1786 1
	tbButton[++j].iBitmap = ++i;	// find
d1791 1
a1791 1
	tbButton[++j].iBitmap = ++i;	// find next
d1797 1
a1797 1
	tbButton[++j].iBitmap = 8;	// separator
d1802 1
a1802 1
	tbButton[++j].iBitmap = ++i;	// New Article
d1807 1
a1807 1
	tbButton[++j].iBitmap = ++i;	// New Mail
d1813 1
a1813 1
	tbButton[++j].iBitmap = 8;	// separator
d1818 1
a1818 1
	tbButton[++j].iBitmap = ++i;	// Save Configuration
d1823 1
a1823 1
	tbButton[++j].iBitmap = ++i;	// Toggle View Unsubscribed
d1831 1
a1831 1
	tbButton[++j].iBitmap = ++i;	// Exit
d1837 1
a1837 1
	tbButton[++j].iBitmap = 8;	// separator
d1842 1
a1842 1
	tbButton[++j].iBitmap = ++i;	// About
d1874 1
a1874 1
//         ShowWindow (NetDoc.hDocWnd, SW_SHOWNORMAL);
d1897 1
a1897 1
	// don't allow disconnect until connection is established
d1972 1
a1972 1
		break;		// can't quit if in process of connecting (will GPF)
d1978 1
a1978 1
	  // WinVnExitDlg sets SaveNewsrc and SaveConfig
d1991 1
a1991 1
		break;		// can't quit if in process of connecting (will GPF)
d2030 2
a2031 2
//	if (Initializing == INIT_READY || Initializing == INIT_DONE)
//	  SetUserMenus (&NetDoc, ENABLE);
d2046 1
a2046 1
		break;		// can't quit if in process of connecting (will GPF)
d2055 1
a2055 1
		break;		// can't quit if in process of connecting (will GPF)
d2061 1
a2061 1
	  Connect ();				// menus are enabled in WM_PAINT once connection is established
d2078 1
a2078 1
		break;		// can't quit if in process of connecting (will GPF)
d2094 1
a2094 1
	  // else fall into disconnect
@


1.63
log
@93.10 update (take two)
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.62 1994/11/30 22:22:08 jcooper Exp $
d2098 5
a2102 5
	  if ((Initializing != INIT_NOT_CONNECTED) && (!ConfirmDisconnect ||
		  (MessageBox (NetDoc.hDocWnd,
					"Are you sure you wish to disconnect from the server?\n"
					   "All active windows will be closed.",
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO))) {
@


1.62
log
@93.9 changes
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.61 1994/11/30 03:04:36 jpiraino Exp $
d37 2
d75 1
a75 1
//  TypGroup far *MyGroup;
d380 3
a382 2
			MessageBox (hWnd, "You have too many group windows \
active;\nClose one or select 'Reuse Old Window'.", "Can't open new window", MB_OK | MB_ICONASTERISK);
d392 11
a404 4
		  LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset, CommDoc->ParentLineID, &BlockPtr, &LinePtr);
		  ((TypGroup far *)
		   (((char far *) LinePtr) + sizeof (TypLine)))->SubjDoc = (TypDoc *) NULL;
		  UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
d598 1
a598 1
		if (ListBackgroundColor == RGB (0, 0, 0))
d604 4
d877 1
a877 1
// if enable is false then disable connect, enable disconnect
a884 1
  // connect and disconnect are always oppositely enabled
a885 3
  EnableMenuItem (hSubMenu, IDM_RECONNECT,  enable ? DISABLE_MENU : ENABLE_MENU);
  EnableMenuItem (hSubMenu, IDM_DISCONNECT, enable ? DISABLE_MENU : ENABLE_MENU);

d888 3
a925 4
// EnableMenuItem (hSubMenu, IDM_SUBSCRIBE, mode);
  // EnableMenuItem (hSubMenu, IDM_UNSUBSCRIBE, mode);
  // EnableMenuItem (hSubMenu, IDM_GROUP_TOP, mode);
  // EnableMenuItem (hSubMenu, IDM_UNSEL_ALL, mode);
a927 1

a928 2
// EnableMenuItem (hSubMenu, 3, mode|MF_BYPOSITION ) ;     // batch submenu

a930 1

d935 1
d1462 1
d1494 12
d1891 1
d1894 13
d1971 3
d1990 3
d2045 3
d2054 3
a2061 1

d2077 3
d2231 7
d2591 3
a2593 1
	  memset(&NewsgroupsPtr, 0x00, sizeof(NewsgroupsPtr)); /* jpiraino */
@


1.61
log
@an hwnd was getting stomped on.
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.60 1994/11/30 02:54:22 jpiraino Exp jpiraino $
d2537 1
a2537 1
	  memset((char *)NewsgroupsPtr, 0x00, sizeof(NewsgroupsPtr)); /* jpiraino */
@


1.60
log
@hard-coded array size, use MAXTBBUTTONS
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.59 1994/11/28 22:01:06 jcooper Exp jpiraino $
d2531 7
a2537 3
//               DialogBoxParam (hInst, "WinVnGeneric", hWnd,
	  //                     lpfnWinVnGenericDlg, (LPARAM) (char far *) "Newsgroup(s):");
	  NewsgroupsPtr = (char *) NULL;
@


1.59
log
@93.8 update from jcooper
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.58 1994/11/21 21:20:46 jcooper Exp $
d1719 1
a1719 1
  TBBUTTON tbButton[20];
@


1.58
log
@93.7 update
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.57 1994/11/11 01:39:21 jglasser Exp $
a448 1
		TimesWndUpdated = 0;
d1954 1
a1954 1
	if (Initializing != INIT_NOT_CONNECTED) {
d2051 1
a2051 1
	  if ((Initializing != INIT_NOT_CONNECTED) && ConfirmDisconnect &&
d2055 1
a2055 1
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDNO)) {
d2061 1
a2061 1
	  if (Initializing != INIT_NOT_CONNECTED) {
d2433 2
a2434 1
	  if ((Initializing != INIT_NOT_CONNECTED) && ConfirmDisconnect &&
d2438 1
a2438 1
				   "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES)) {
d2549 6
@


1.57
log
@toolbar fixes
@
text
@d11 1
a11 1
 * $Id: wvusenet.c 1.56 1994/11/10 02:46:17 rushing Exp $
a55 3

  DLGPROC lpProcAbout;

a93 10
  case WM_SYSCOMMAND:
	if (wParam == ID_ABOUT) {
	  lpProcAbout = (DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
	  DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
	  FreeProcInstance ((FARPROC) lpProcAbout);
	  break;
	}
	else
	  return (DefWindowProc (hWnd, message, wParam, lParam));

a542 2
	  TypRange far *RangePtr;
	  unsigned long int HighestRead;
a546 1
	  HWND hParentWnd;
d612 1
a612 4
		// get parent window handle and invalidate it
		// so parent WM_PAINT can change menus
		hParentWnd = GetParent (hWnd);
		InvalidateRect (hParentWnd, NULL, FALSE);
a616 1

d669 1
a669 2
		LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
				  &BlockPtr, &LinePtr);
d671 15
a685 1
		/* Update the scroll bar thumb position.                 */
a689 5
		RangeHigh = NetDoc.ActiveLines - VertLines;
		if (RangeHigh < 0)
		  RangeHigh = 0;
		SetScrollRange (hWnd, SB_VERT, 0, RangeHigh, FALSE);
		SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
d691 7
d755 1
a755 1
				  RangePtr = (TypRange far *) ((char far *) MyGroup + RangeOffset (MyGroup->NameLen));
d759 5
a763 1
					  indicator = 'u';	/* was a '*' */
d863 2
a864 2
// if enable is true the enable connect, disable disconnect
// if enable is false the disable connect, enable disconnect
d870 1
a870 1
  hSubMenu = GetSubMenu (hMenu, 0);		// config menu
d873 2
a874 1
  EnableMenuItem (hSubMenu, IDM_CONNECT, enable ? ENABLE_MENU : DISABLE_MENU);
d1033 1
d1726 2
a1727 1
  StatusBarProc (hWnd, message, wParam, lParam, &NetDoc);
d1735 11
d1862 9
d1894 4
d1992 2
a1993 2
	if (Initializing == INIT_READY || Initializing == INIT_DONE)
	  SetUserMenus (&NetDoc, ENABLE);
d2043 1
a2043 1
	  if ((Initializing == INIT_NOT_CONNECTED)) {
d2227 7
d2415 4
a2418 3
	  if (MessageBox (NetDoc.hDocWnd,
					  "Are you sure you wish to reset the server protocol?\n"
		   "Any active communications and coding sessions will be aborted.",
d2420 10
a2429 1
		AbortAllComm ();
d2433 10
@


1.56
log
@restart
@
text
@d86 2
a87 1
  StatusBarProc (hWnd, message, wParam, lParam, &NetDoc);
d878 1
a878 1
  SendMessage (DocPtr->hWndTB, TB_PRESSBUTTON, IDB_TOGGLE_CONNECT, enable ? FALSE : TRUE);
d2032 1
a2032 1
		SendMessage (NetDoc.hWndTB, TB_PRESSBUTTON, IDB_TOGGLE_CONNECT, TRUE);
@
