/********************************************************************
 *                                                                  *
 *  MODULE    :  WVUSENET.CPP                                       *
 *                                                                  *
 *  PURPOSE   : This file contains the window procedure and support *
 *              routines for WinVn's main window                    *
 *                                                                  *
 ********************************************************************/

/*
 * $Id: wvusenet.cpp 1.8 1995/11/10 06:45:49 dumoulin Exp $
 *
 */
#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#include <errno.h>

#pragma hdrstop
#include <stdlib.h>
#include <assert.h>
#ifdef COMMCTRL
#include <commctrl.h>
#else
extern "C"
{
#include "wvtb\wvtb.h"          /* for toolbar  */
}
#endif
#include "WVClass.h"
#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 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:                      /* 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:                       /* 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 (TopOfDoc (&NetDoc, &BlockPtr, &LinePtr)) {
          	SetSelections (&NetDoc, LinePtr);
          	AdjustMidSc (BlockPtr, LinePtr);
		  }
          break;

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

      case VK_NEXT:
          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 (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, GetGroup(LinePtr), FALSE);
          }
          else {
            ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
          }
        }
      }
      if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) {
        aGroup = GetGroup(LinePtr);
        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, GetGroup(LinePtr),(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 = (GetGroup(GroupLinePtr))->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 = GetGroup(LinePtr);
          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);
          
          /* Update range info for Newsrc and clear out contents of document for reuse */
          UpdateSeenArts (CommDoc);
          UnlinkArtsInGroup (CommDoc);
          FreeDoc (CommDoc);
        }

        /* Create window title indicating we are retrieving text. */
        CommDoc->InUse = TRUE;
        lpsz = GetGroupName(GroupLinePtr);
        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, GetGroup(GroupLinePtr), TRUE);
        InitDoc (CommDoc, hWndView, &NetDoc, DOCTYPE_GROUP);
        PtrToOffset (GroupBlockPtr, GroupLinePtr, &(CommDoc->hParentBlock),
                     &(CommDoc->ParentOffset), &(CommDoc->ParentLineID));
        SetActiveWindow (CommDoc->hWndFrame);
        SetFocus (CommDoc->hDocWnd);
        (GetGroup(GroupLinePtr))->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);
        strcpy (mybuf, "GROUP ");
        lpsz = GetGroupName(GroupLinePtr);
        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];
      RECT myRect, aRect;
      int MyColorMask = 1, PrevColorMask = MyColorMask;
      HBRUSH hOldBrush;
      HANDLE hOldFont;
	  BOOL GotLock = FALSE;
//#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 */

        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);
          GotLock = LockLine (NetDoc.hFirstBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
                              &BlockPtr, &LinePtr);
		  if (GotLock) {
             AdvanceToActive (&BlockPtr, &LinePtr);
             VertLines = NetDoc.ActiveLines;
			 }
         }
         else {
          GotLock = LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
                              &BlockPtr, &LinePtr);
        }

		if (GotLock == FALSE) return(0);  /* JD 6/6/95 */

        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 = GetGroup(LinePtr);
            MyLen = MyGroup->NameLen;
            textptr = GetGroupName(LinePtr);

            /* 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 unseen articles */
                  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);

        /* Blank out bottom and top of screen */
        hOldBrush = (HBRUSH) 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, GetGroup(deselPtr), FALSE);
    }
    else {
      ForAllLines (MyDoc, SetLineFlag, LINE_FLAG_SET, FALSE);
    }
    /* set only current line selected and make it the anchor */
    SelectGroup (MyDoc, GetGroup(LinePtr), TRUE);
    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 (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.\n"
                  "Some 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 (char *buf, TypLine *lineptr, void*pEndLine)
{
  char *grname = GetGroupName(lineptr);
  TypGroup *group = GetGroup(lineptr);
  TypRange *RangePtr;
  BOOL MoreNums;
  unsigned int MyLength;

  group->Subscribed = FALSE;
  group->Selected = FALSE;
  group->Determined = FALSE;
/* group->Threaded skipped */
  group->NameLen = 0;
  group->SubjDoc = (TypDoc *) NULL;
  group->ServerEstNum = 0;
  group->ServerFirst = 0;
  group->ServerLast = 0;
  group->HighestPrevSeen = 0;
  group->header_handle = (HANDLE) NULL;
/* group->thread_handle skipped */
/* group->total_headers skipped */
  group->NumUnread = 0;
  group->nRanges = 0;
  group->nSpareRanges = 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. */
 //   DEBUG_BREAK; // new group on server
  }
  else {
    if (*buf == ':') {
      group->Subscribed = TRUE;
	  group->nSpareRanges = SpareRanges;
    }
    buf++;
  }

  /* if we're allowed to mark crossposted articles, create some room
     in the Group Data structure for the Range Array to grow.  This
     will keep memory from getting to fragmented while reading articles
     and updating the ranges of other articles on the fly   (JD 5/12/95)  */
  if ((TrackSubscribedCrossposts && (group->Subscribed == TRUE)) ||
      (TrackUnsubscribedCrossposts && (group->Subscribed = FALSE))) {
    group->nSpareRanges = SpareRanges;
  }  

  /* Look for the highest article number previously available on the server,
   * 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 = GetRangePtr(group);
  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(RangePtr < (TypRange *)(char*)pEndLine - sizeof(TypRange))
    {
    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;
    }
    }
    else
    {
      while(*buf && (*buf) != '\n')
        buf++;
      MoreNums = FALSE;
    }
  }
  if (group->nRanges == 0) {
    (group->nRanges)++;
	RangePtr->Last = RangePtr->First = 1;
	 }

  MyLength = CalcGroupLen(group);
  lineptr->length = MyLength;
  lineptr->LineID = NextLineID++;
  GroupLenPtr(lineptr) = 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 (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;
  int iRet = 0;

  LockLine (NetDoc.hCurAddBlock, NetDoc.AddOffset, NetDoc.AddLineID, &BlockPtr, &GroupPtr);
  if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, TEMPBUFSIZE)) == NULL) {
    return 0;
  }
 
  hRetCode = MRROpenFile (szNewsSrc, OF_READ, &MRRFile);
  if ((int) hRetCode <= 0) {
    iRet = 0;
  }
  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, (char far*)LinePtr + (TEMPBUFSIZE-1))) {
        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;
  iRet = 1;
  }
  GlobalFreePtr (LinePtr); 
  NewsrcDirty = FALSE;

  return iRet;
}

char *ltoa ();

#define WNEWSRC_INIT       5
#define WNEWSRC_OPEN       9
#define WNEWSRC_BEGINSAVE 10
#define WNEWSRC_ENDSAVE   90
#define WNEWSRC_CLOSE     92
#define WNEWSRC_WAIT      94
#define WNEWSRC_RENAME    96
#define WNEWSRC_FINISH    98

/*--- 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,percent,opercent,errcode;
  TypGroup far *Group;
  TypRange far *RangePtr;
  BOOL firstrange;
  char newsrc_temp[MAXFILENAME];
  char newsrc_extension[MAXFILENAME];
  char szString[MAXFILENAME+20];
  int extension_length, filename_length;
  uint32 loopctr = 0;
  uint32 loopend = max(1,LinesInRC);

  /* conditions in which we do not want to write it out */
  if (Initializing == INIT_READING_NEWSRC ||
      Initializing == INIT_SCANNING_NETDOC ||
      Initializing == INIT_GETTING_LIST) {
	MessageBox (NetDoc.hDocWnd, "Newsrc was busy initializing.  Newsrc not saved.", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);  
    return;
  }
  
  sprintf(szString,"Saving NewsRC File %s",szNewsSrc); 
  SetStatbarText(NetDoc.hWndFrame,szString,&NetDoc, TRUE, TRUE);
  SetStatbarPercent(NetDoc.hWndFrame,(percent = 0),&NetDoc,TRUE);
  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);
  filename_length = strlen (newsrc_temp);

  if ((extension_length != 0) || (newsrc_temp[filename_length] == '.')) {
    strcpy (newsrc_temp + (filename_length - (extension_length+1)), ".tmp");
   } 
  else {
    strcpy (newsrc_temp + filename_length, ".tmp");
  }

  sprintf(szString,"Opening NewsRC File %s",szNewsSrc); 
  SetStatbarText(NetDoc.hWndFrame,szString,&NetDoc, TRUE, TRUE);  
  hRetCode = MRROpenFile (newsrc_temp, OF_WRITE, &MRRFile);
  sprintf(szString,"Writing NewsRC File %s",szNewsSrc); 
  SetStatbarText(NetDoc.hWndFrame,szString,&NetDoc, TRUE, TRUE);
  opercent = percent;
  
  if ((int) hRetCode <= 0) {
    MessageBox (NetDoc.hDocWnd, "Could not write to newsrc file", "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
  }
  else {
    do {
     percent = (int) (((float) loopctr++ / (float) loopend) * 100);
     if (percent > opercent + 5) {  
         SetStatbarPercent(NetDoc.hWndFrame,percent,&NetDoc,TRUE);
         opercent = percent;
     } 
     toptr = NewsLine;
     Group = GetGroup(LinePtr);

      /* 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! */

      if ((Group->ServerFirst || Group->ServerEstNum) || !did_list) {
        /* Copy group name                                          */
        fromptr = GetGroupName(LinePtr);
        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 = GetRangePtr(Group);

        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->Last > RangePtr->First) {
            /* 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); 
    sprintf(szString,"Closing NewsRC File %s",szNewsSrc); 
    SetStatbarText(NetDoc.hWndFrame,szString,&NetDoc, TRUE, TRUE);
    SetStatbarPercent(NetDoc.hWndFrame,100,&NetDoc,TRUE);
    
    MRRCloseFile (MRRFile);
    unlink (szNewsSrc);
    SetStatbarPercent(NetDoc.hWndFrame,0,&NetDoc,TRUE);
    SetStatbarText(NetDoc.hWndFrame,"",&NetDoc, TRUE, TRUE);
    
  //  errcode = rename (newsrc_temp,szNewsSrc);
  	errcode = EXDEV;  /* debug */
    if (errcode != 0) {
      char error_string[256];

      switch (errcode) {
      case EACCES:
      	sprintf (error_string, "Could not rename temporary newsrc file '%s'.\n File specified by '%s' still exists after being deleted or could not be created", newsrc_temp,szNewsSrc);
      	MessageBox (NetDoc.hDocWnd, error_string, "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
      	break;
      case ENOENT:
      	sprintf (error_string, "Could not rename temporary newsrc file '%s'. \n File Not Found", newsrc_temp);
      	MessageBox (NetDoc.hDocWnd, error_string, "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
        break;
      case EXDEV:
        /* Failed because Rename doesn't work across some networks or file systems
           try a slower block by block copy instead   JD 4/16/96  */
        sprintf(szString,"Making temporary NewsRC File %s permanent",szNewsSrc); 
        SetStatbarText(NetDoc.hWndFrame,szString,&NetDoc, TRUE, TRUE);
        errcode = CopyFile(newsrc_temp,szNewsSrc,FALSE);
        if (errcode = 0) {
        	sprintf (error_string, "Could not rename temporary newsrc file '%s'.\n Error Code = '%n'", newsrc_temp,errcode);
      		MessageBox (NetDoc.hDocWnd, error_string, "Save Newsrc Failure", MB_OK | MB_ICONSTOP);
      		}
      	else
      		unlink (newsrc_temp);
      		SetStatbarText(NetDoc.hWndFrame,"",&NetDoc, TRUE, TRUE);
      	break;
      }
    }
  }
  
  NewsrcDirty = FALSE;
  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, GetGroup(*LinePtr), 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, GetGroup(*LinePtr), TRUE);
        extending = FALSE;
      }
      else {
        SelectGroup (Doc, GetGroup(*LinePtr), TRUE);
        if (Doc->ActiveLineID == Doc->AnchorLineID)
          extending = FALSE;
        else
          extending = TRUE;
      }
    }
    else
      SelectGroup (Doc, GetGroup(*LinePtr), 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 ((GetGroup(*LinePtr))->Selected) {
	  (GetGroup(*LinePtr))->Subscribed = wValue;
      AddGroupToTable ((char far *) *LinePtr);
      DeleteLine (BlockPtr, LinePtr);
    }
    break;

  case GROUP_ACTION_SELECTED:	 
     if ((GetGroup(*LinePtr))->Selected) {
      AddGroupToTable ((char far *) *LinePtr);
  //    DeleteLine (BlockPtr, LinePtr);
    }
    break;

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

  case GROUP_ACTION_GET_GROUPNAMES:
    if ((GetGroup(*LinePtr))->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 (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;
}

long
cursor_to_char_number (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 = GetTextLen(*LinePtr);
  
  if (textlen) {
    textptr = GetTextPtr(*LinePtr);
    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);
  }
  return(0);
}

/*--- 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 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;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    /* reset */
    tbButton[j].idCommand = IDM_RESET;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = 8;  /* separator */
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_SEP;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    /* find */
    tbButton[j].idCommand = IDM_FIND;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    /* find next */
    tbButton[j].idCommand = IDM_FIND_NEXT_SAME;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = 8;  /* separator */
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_SEP;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    /* New Article */
    tbButton[j].idCommand = IDM_POST;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    /* New Mail */
    tbButton[j].idCommand = IDM_MAIL;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = 8;  /* separator */
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_SEP;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    /* Save Configuration */
    tbButton[j].idCommand = IDM_SAVE_CONFIG;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    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;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

	tbButton[++j].iBitmap = 8;  /* separator */
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_SEP;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    /* Exit */
    tbButton[j].idCommand = IDB_FASTEXIT;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = 8;  /* separator */
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_SEP;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

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

  //  tbButton[j].idCommand = ID_ABOUT;
  //  Help is more useful than About    (JD 5/16/95)

    tbButton[j].idCommand = IDM_HELP; 	 
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;

    NetDoc.hWndTB = CreateToolbarEx(hWnd,	
                                    CCS_TOP | WS_VISIBLE | TBSTYLE_TOOLTIPS,	
                                    IDB_TOOLBAR, 	
                                    i + 1, 	
                                    hInst, 	
                                    IDB_TOOLBAR, 	
                                    tbButton, 	
                                    j + 1, 	
                                    16, 	
                                    16, 	
                                    16, 	
                                    16, 	
                                    sizeof(TBBUTTON));	
#else

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

    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);

    /* 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...");

    /* Get the global Action items */
    g_action.ReadActions(NULL);

#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_SORT_SELECTED, 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, 15), 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)) {
	     if (NetDoc.hDocWnd){
            InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		 }
        break;
      }
    }
    SetHandleBkBrush (NetDoc.hDocWnd, (HBRUSH) GetStockObject (WHITE_BRUSH));    
    MailClose (hWnd);

    if (SaveConfig) WriteWinvnProfile ();     
    if (SaveNewsrc && NewsrcDirty) WriteNewsrc ();
      
    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);
    }
   
    Disconnect ();
       
    /* 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, (HBRUSH) 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 ();
      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) */
      }
      if (NewsrcDirty) WriteNewsrc ();
      WriteWinvnProfile ();
      SaveNewsrc = SaveConfig = FALSE;  /* we've already done them */
      SetHandleBkBrush (NetDoc.hDocWnd, (HBRUSH) 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:   
      MessageBox (hWnd, "Command not implemented","Sorry", MB_OK);
      break;

	case IDM_SELECT_ALL:
	  ForAllLines (&NetDoc, SetLineFlag, 0, TRUE);
      InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
      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);
	  NewsrcDirty = TRUE;
      break;

    case IDM_UNSUBSCRIBE:
      InitGroupTable ();
      ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_UNSUBSCRIBE, FALSE);
      BuildPtrList ();
      ShellSort (NewGroupTable,
                 (size_t) nNewGroups,
                 (size_t) sizeof (void far *),
#ifdef WIN32  // TODO: fixme...            
                 (int(__cdecl *)(const void*,const void*)) GroupCompare);
#else
                 (int (__far __cdecl *)(const void huge *,const void huge *)) GroupCompare);
#endif                 
      MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
      CleanUpGroupTable ();
      ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
      NetDoc.LongestLine = 0;
      SetGroupActiveLines ();
      ScreenToTop (&NetDoc);
      SetNetDocTitle ();
      InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  NewsrcDirty = TRUE;
      break;

 case IDM_SORT_SELECTED:
      InitGroupTable ();
      ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SELECTED, TRUE);
      BuildPtrList ();
      ShellSort (NewGroupTable,
                 (size_t) nNewGroups,
                 (size_t) sizeof (void far *),
#ifdef WIN32           
                 (int(__cdecl *)(const void*,const void*)) GroupCompare);
#else
                 (int (__far __cdecl *)(const void huge *,const void huge *)) GroupCompare);
#endif                 
      MergeGroups (ADD_FIRST_SELECTED);
      CleanUpGroupTable ();
      ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
      NetDoc.LongestLine = 0;
      SetGroupActiveLines ();
      ScreenToTop (&NetDoc);
      SetNetDocTitle ();
      InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
	  NewsrcDirty = TRUE;
      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);
	  NewsrcDirty = TRUE;
      break;


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

    case IDM_CONFIG_PERSONAL:
      DialogBox (hInst, "WinVnPersonal", hWnd, lpfnWinVnPersonalInfoDlg);
      break;
    
    case IDM_CONFIG_EXECUTE:
      DialogBox (hInst, "WinVnExecute", hWnd, lpfnWinVnExecuteInfoDlg);
      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_KILLED:
      if (AskForColor (NetDoc.hDocWnd, &ArticleKilledColor))
        break;
      WarnColors (ArticleKilledColor, 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 (ArticleKilledColor, ListBackgroundColor, "Killed 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:
      WinVNCascadeWindows ();
      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,
                "It is not usually safe to reset server protocol while receiving \n"
                "information from your server information.  This can leave WinVN \n"
                "in an unstable state.  Use only as a last resort.  Are you \n"
                "sure you wish to continue. \n",
                "Please Confirm", MB_YESNO | MB_ICONQUESTION) == IDYES) {
                
              CommDoc = (TypDoc *) NULL;
              CommBusy = FALSE;
              CommState = ST_NONE;
              hMenu = GetMenu (hWnd);
              hSubMenu = GetSubMenu (hMenu, 1);
              EnableMenuItem (hSubMenu, IDM_CONNECT, ENABLE_MENU);
    //        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 ();
	  WriteWinvnProfile ();
      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:
      /* 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: 2
 * end:
 */

/* Last line of WVUSENET.CPP */
