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

/*
 * $Id: wvusenet.cpp 1.14 1997/03/18 19:19:19 dumoulin Exp $
 *
 */
#include <windows.h>
#include <windowsx.h>
#include <sys/stat.h>
#include <errno.h>
#include "wvglob.h"
#include "winvn.h"
#include "version.h"
#include "wvnzb.h"
#ifdef _WIN32
#include "inifile.h"    //replacement ini file handling
#include "htmlhelp.h"
#endif
#include "wvnzb.h"
#pragma hdrstop

//#include <errno.h>
#include "wvclass.h"
//#include <stdlib.h>
#include <assert.h>
//#include <ctype.h>
//#include <io.h>                                 // for unlink
//#include <stdio.h>
#include <commctrl.h>
#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 SetGroupActiveLines (void);
BOOL WarnColors (COLORREF c1, COLORREF c2, char *warnMsg);
BOOL SelectGroup (TypDoc far * Doc, TypGroup far * group, BOOL value);


static char groupbuf[MAXHEADERLINE];

/* added by shimomai */
extern "C" {
HCURSOR hArrowCursor;
HCURSOR hSizeArrow;
}
static BOOL WidthCursor = FALSE;
static int orgPos;
#define DRAG_ON     5

void DrawDragingCursor(HWND hWnd, long oldPosition, long newPosition, BYTE flag);


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

LRESULT CALLBACK  WinVnConfWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT ps;               /* paint structure          */
  HDC hDC;                      /* handle to display context */
  HWND hWndView;
  RECT myRect;                  /* selection rectangle                */

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


  int X, Y;
  int docnum = 0;
  int newdoc;
  char mybuf[MAXINTERNALLINE];
  int CtrlState;
  TypLineID MyLineID;
  static long DragCursorPosition = 0;

#ifdef OFFLINE  // TANAKA Goh
  char* pGenSockDLL;
  static int PrevState;
#endif

#ifdef _WIN32
  SCROLLINFO scrollInfo;  //MDS proportional scroll
#endif


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

    /* load cursor for dragging */
    hArrowCursor = LoadCursor (NULL, IDC_ARROW);
    hSizeArrow = LoadCursor (hInst, MAKEINTRESOURCE (IDC_SIZECURSOR));

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

    /* Group List Separator (shimomai)*/
    if (!GroupListSeparator)
      GroupListSeparator = CharWidth * 10;

    break;

  case WM_CHAR:
    /* Hitting CR on a group name is the same as double-clicking
     * on the name. */
    if (!Initializing || Reconnecting == 1) 
    {
      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 || Reconnecting == 1) 
    {
      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:
      {
        //MDS scroll by 1 page based on current top of page
        //not by current active line because you skip lines otherwise

        long lineord = 0;
        LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
                        &BlockPtr, &LinePtr);

        lineord = WhatLine (BlockPtr, LinePtr);
        UnlockLine (BlockPtr, LinePtr, &NetDoc.hCurTopScBlock, &NetDoc.TopScOffset, &NetDoc.TopScLineID);
        if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) 
        {
          long currline =  WhatLine (BlockPtr, LinePtr);
          long target = lineord + NetDoc.ScYLines + NetDoc.ScYLines/2 -1;
          for (long i = currline; i < target; i++) 
          {
            if (!NextActiveLine (&BlockPtr, &LinePtr))
              break;
          }
          SetSelections (&NetDoc, LinePtr);
          AdjustMidSc (BlockPtr, LinePtr);
        }
       }
        break;

      case VK_PRIOR:
      {
        //MDS scroll by 1 page based on current top of page
        //not by current active line because you skip lines otherwise
        long lineord = 0;
        LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
                        &BlockPtr, &LinePtr);

        lineord = WhatLine (BlockPtr, LinePtr);
        UnlockLine (BlockPtr, LinePtr, &NetDoc.hCurTopScBlock, &NetDoc.TopScOffset, &NetDoc.TopScLineID);
        
        if (LineIDtoLinePtr (NetDoc.ActiveLineID, &NetDoc, &BlockPtr, &LinePtr)) 
        {
          long currline =  WhatLine (BlockPtr, LinePtr);
          long target = max((long)(lineord - NetDoc.ScYLines + NetDoc.ScYLines/2 +1), (long)0);
          for (long i = target; i < currline; i++) 
          {
            if (!PrevActiveLine (&BlockPtr, &LinePtr))
              break;
          }
          SetSelections (&NetDoc, LinePtr);
          AdjustMidSc (BlockPtr, LinePtr);
        }
      }
          break;

    case VK_ESCAPE:
      if (DragMouseAction == DRAG_ON) 
      {
        DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
        ReleaseCapture ();
        DragMouseAction = DRAG_NONE;
        WidthCursor = FALSE;
        SetCursor (hArrowCursor);
        InvalidateRect(hWnd, NULL, FALSE);
      }
      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_MBUTTONDOWN:
    MiddleButton_Is_Down = TRUE;
    if (UScrollMode == WHEELSCROLL_CLICK) 
    {
        endless = FALSE; /* first loop assuemd as save */
        do 
        {
            UPosScrollByWheel++;
            if ( UPosScrollByWheel >= MAX_WHEEL_ENTRIES ) 
            {
                    UPosScrollByWheel = 0;
                    if (endless) break; /* prevent a endless loop in case this occures twice */
                    endless = TRUE; /* remember first loop just encountered, now it could be endless */
            }
        } while (UScrollByWheel[UPosScrollByWheel] == 0);
    }
    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;
// Add support to dynamically size the width of fields (shimomai)
    if (WidthCursor) 
    {
      orgPos = GET_X_LPARAM (lParam);
      DragCursorPosition = orgPos;
      DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
      SetCapture (hWnd);      // release capture on button up
      DragMouseAction = DRAG_ON;
    }
    else if (!Initializing || Reconnecting == 1) 
    {
      BOOL status;
      TypGroup *aGroup;
      static BOOL bUSel = FALSE;

      X = GET_X_LPARAM (lParam);
      Y = GET_Y_LPARAM (lParam);
	  NetDoc.KilledLines = Y;
      /* 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);
        NetDoc.ActiveLineID = LinePtr->LineID;
        if (GroupListMultiSelect) 
        {
          
          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
        {
          if (wParam & MK_SHIFT)
          {
			//additive shift, like article list
            BOOL bDir = CompareLineIDPos(NetDoc.AnchorLineID, LinePtr->LineID) > 0;
            
            SelectGroup (&NetDoc, GetGroup(LinePtr),bUSel ? TRUE : FALSE);

            while (NetDoc.AnchorLineID != LinePtr->LineID && LinePtr->LineID)
            {

                if (bDir)
                {
                    if (!NextLine (&BlockPtr, &LinePtr))
                        break;
                }
                else
                {
                    if (!PrevLine (&BlockPtr, &LinePtr))
                        break;
                }
                SelectGroup (&NetDoc, GetGroup(LinePtr),bUSel ? TRUE : FALSE);
            }   

          }
          else
          {
            NetDoc.AnchorLineID = LinePtr->LineID;
            DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
            bUSel = status;

          }
        }

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

      InvalidateRect (hWnd, NULL, FALSE);
    }
    break;
    
  case WM_MBUTTONUP:
    MiddleButton_Is_Down = FALSE;
    break;
    
  case WM_KILLFOCUS:
  case WM_ACTIVATEAPP:
  case WM_LBUTTONUP:
    /*  Letting up on the left button on a group name 
     *  gets us out of Dragging mode   */

    if (DragMouseAction == DRAG_ON) 
    {
      DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
      ReleaseCapture ();
      GroupListSeparator += GET_X_LPARAM (lParam) - orgPos;
      if (GroupListSeparator < CharWidth)
        GroupListSeparator = CharWidth * 10;
      InvalidateRect (hWnd, NULL, FALSE);
    }
    ReleaseCapture ();
    DragMouseAction = DRAG_NONE;
    break;

  case WM_SETCURSOR:
    if ((!Initializing || Reconnecting == 1) && WidthCursor)
      SetCursor (hSizeArrow);
    else if (Initializing && Initializing != INIT_NOT_CONNECTED)
      SetCursor (hHourGlass);
    else
      SetCursor (hArrowCursor);
    break;

  case WM_MOUSEMOVE:
    /*  Code to drag the mouse and change the select/not selected
     *  status of that group.*/
// edited by shimomai
    if (!Initializing || Reconnecting == 1) 
    {
//    if ((!Initializing) && (DragMouseAction != DRAG_NONE)) {
      X = GET_X_LPARAM (lParam);
      Y = GET_Y_LPARAM (lParam);

      switch (DragMouseAction) 
      {
      case DRAG_NONE:
        X -= SideSpace - NetDoc.ScXOffset * (CharWidth + 1);
        if (Y < LineHeight
            && X >= GroupListSeparator && X < GroupListSeparator + CharWidth) 
        {
          WidthCursor = TRUE;
        }
        else 
        {
          WidthCursor = FALSE;
        }
        break;

      case DRAG_ON:
        if (!(wParam & MK_LBUTTON)) // changed from && to & JD 12/31/96
        {  
            DragMouseAction = DRAG_NONE;
            DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
                        ReleaseCapture ();
          } 
          else 
          {
            DrawDragingCursor(hWnd, DragCursorPosition, X, 2);
            DragCursorPosition = X;
          }
          break;
        
      default:
        GetClientRect (hWnd, &myRect);
        //if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr)) 
        {
          if (GroupListMultiSelect) 
          {
            if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr))
			{
            NetDoc.ActiveLineID = LinePtr->LineID;
            ForAllLinesOnScreen (&NetDoc, SetExLineFlag, LINE_FLAG_SET, FALSE);
            GlobalUnlock (BlockPtr->hCurBlock);
			}
          }
          else 
          {
                     
			BOOL bDir = NetDoc.KilledLines  > Y;

			int yTop = min(Y, NetDoc.KilledLines);
			int yBottom = max(Y, NetDoc.KilledLines);
			if (yBottom >  TopSpace + NetDoc.ScYLines * LineHeight)
				yBottom = TopSpace + NetDoc.ScYLines * LineHeight -1;
			if (yTop < TopSpace)
				yTop = TopSpace;
			int yCur = yTop;
			BOOL bSet = FALSE;
			

			while (yCur < yBottom)
			{
				if (CursorToTextLine (X, yCur, &NetDoc, &BlockPtr, &LinePtr))
				{
					if (!bDir && !bSet)
					{
						NetDoc.ActiveLineID = LinePtr->LineID;
						bSet = TRUE;
					}
					if (bDir)
						NetDoc.ActiveLineID = LinePtr->LineID;

					SelectGroup (&NetDoc, GetGroup(LinePtr),(DragMouseAction == DRAG_SELECT) ? TRUE : FALSE);
					GlobalUnlock (BlockPtr->hCurBlock);
				}
				yCur +=	LineHeight;
			}
          
			NetDoc.KilledLines = Y;
          }
        }
        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 || Reconnecting == 1) 
    {
      X = GET_X_LPARAM (lParam);
      Y = GET_Y_LPARAM (lParam);

      getgroup:;
      if (CursorToTextLine (X, Y, &NetDoc, &GroupBlockPtr, &GroupLinePtr)) 
      {
        NetDoc.ActiveLineID = GroupLinePtr->LineID;
      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, IDS_TEST_RETRIEVE_GROUP)) 
        {
          GlobalUnlock (GroupBlockPtr->hCurBlock);
          break;
        }

#ifdef OFFLINE  // TANAKA Goh
		if (SaveSOUPOnClose) 
		{
			PrevState = CommState;

			if ((pGenSockDLL = strrchr(GenSockDLL, '\\')) == NULL) 
			{
				pGenSockDLL = GenSockDLL;
			} 
			else 
			{
				pGenSockDLL++;
			}

			if (!stricmp(pGenSockDLL, "gwssoup.dll") || !stricmp(pGenSockDLL, "gsoup32.dll")) 
			{
				if (ActiveGroupDoc && CommState == ST_DELETE) 
				{
					GlobalUnlock(GroupBlockPtr->hCurBlock);
					CommDoc = ActiveGroupDoc;
					CommState = ST_WRITE;
					CommBusy = TRUE;
					PutCommLine("WRITE");
					break;
				}
			}
		}
#endif

        /* 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) 
		  {
            WVMessageBox (hWnd, IDS_WUNVN_GROUP_WINDOWS, IDS_WINVN_GROUP_OPEN,   
                          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;

          if(LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
              CommDoc->ParentLineID, &BlockPtr, &LinePtr))
          {
              /* Clear the pointer in the line for this group in the   */
              /* NetDoc document.  This pointer currently points       */
              /* to the current document, which we are wiping out      */
              /* with the destruction of this window.                  */

              MyGroup = GetGroup(LinePtr);
              MyGroup->SubjDoc = (TypDoc far *) NULL;

              /* make the 'n' in the group-list go away */
              MyGroup->HighestPrevSeen = MyGroup->uLoadLast;
              InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
      
              if (CommDoc->TotalLines != 0) 
              {
                  UpdateSeenArts (CommDoc);
                  UnlinkArtsInGroup (CommDoc);
                  if (MyGroup->header_handle) 
                  {
                      long i;
                      header_p headers;
          
                      headers = lock_headers (MyGroup->header_handle, MyGroup->thread_handle);
                      for(i = 0; i < MyGroup->alloc_headers; i++) 
                      {
                            delete headers[i].pHD;
                            headers[i].pHD = NULL;
                      }
                      unlock_headers (MyGroup->header_handle, MyGroup->thread_handle);
                      free_headers (MyGroup->header_handle, MyGroup->thread_handle);
                  }
                  MyGroup->header_handle = (HANDLE) NULL;
                  MyGroup->thread_handle = (HANDLE) NULL;
                  MyGroup->total_headers = 0;
                  MyGroup->alloc_headers = 0;
                  delete MyGroup->pAA;
                  MyGroup->pAA = NULL;
					if (MyGroup->pFile_sort)
					{
						FreeFileSortHeaders((HEADERLIST*)MyGroup->pFile_sort);
						delete (HEADERLIST*) MyGroup->pFile_sort;
						MyGroup->pFile_sort = NULL;
					}

                  delete MyGroup->pAddHdr;
                  MyGroup->pAddHdr = NULL;
                  GlobalFreePtr(MyGroup->Description);
                  MyGroup->Description = NULL;
                  //jana
                  delete MyGroup->pSeendb;
                  MyGroup->pSeendb = NULL;
              }
          }

          UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
          /* Clear document                                        */
          FreeDoc (CommDoc);

#ifndef OFFLINE
          if (!LineIDtoLinePtr(NetDoc.ActiveLineID, &NetDoc, &GroupBlockPtr, &GroupLinePtr))
            break;
#endif
#ifdef OFFLINE  // TANAKA Goh
          if (message == WM_CHAR && wParam == '\r') 
          {
                if (!LineIDtoLinePtr(NetDoc.ActiveLineID, &NetDoc, &GroupBlockPtr, &GroupLinePtr))
                  break;
          } 
          else 
          {
                if (!CursorToTextLine(X, Y, &NetDoc, &GroupBlockPtr, &GroupLinePtr))
                  break;
          }
#endif
        }

        /* Create window title indicating we are retrieving text. */
        CommDoc->InUse = TRUE;
        lpsz = GetGroupName(GroupLinePtr);


        /* 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);
            RECT r;
            r.top = y;
            r.left = x;
            r.right = r.left+width;
            r.bottom = r.top + height;
            MakeRectVisible(r);
            x=r.left;
            y=r.top;
            width = r.right-r.left;
            height=r.bottom-r.top;
          }
          hWndView = CreateWindow ("WinVnViewFrame",
                                   "",
                                   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);         /* ??? */
      WVArgXSetWindowText(hWndView, IDS_GROUP_RETRIEVING, lpsz);

/*    SetHandleBkBrush (hWndView, hListBackgroundBrush); */
          ShowWindow (hWndView, SW_SHOWNORMAL);
        }
        else 
        {
          hWndView = CommDoc->hWndFrame;
          WVArgXSetWindowText (hWndView, IDS_GROUP_RETRIEVING, lpsz);
          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;
          if (ShowDescription) 
          {
            CommState = ST_GROUP_DESCRIPTION_RESP;
            strcpy (mybuf, "LIST NEWSGROUPS ");
          } 
          else  
          {
              CommState = ST_GROUP_RESP;
              strcpy (mybuf, "GROUP ");
          }
        lpsz = GetGroupName(GroupLinePtr);
        lstrcat (mybuf, lpsz);
        strntcpy (CurrentGroup, lpsz, MAXGROUPNAME);
          CurrentDescription[0] = '\0';

        hSaveCursor = SetCursor (hHourGlass);
        ShowCursor (TRUE);
        PutCommLine (mybuf);
      }
    }
    break;


    /* If we've lost the connection for some reason, kill the timer */
    /* that's banging on the socket. */

  case WM_TIMER:
#ifndef OFFLINE // TANAKA Goh
    if (wParam == idTimer)
	{
		if ((CommState == ST_CLOSED_COMM) && (!Initializing)) 
		{
		  KillTimer (hWnd, ID_TIMER);
		}
		else 
		{
		  if ((CommState != ST_CLOSED_COMM) && (CommState != ST_WAIT_COMM))
			DoCommInput ();
		  if (ExecAsynMessage) 
		  {
			ExecAsynMessage = FALSE;
			/* maybe set to TRUE during executing of message, because we has still to wait */
			SendMessage(AsynMsg.hwnd,AsynMsg.uMsg,AsynMsg.wParam,AsynMsg.lParam);
		  }
		}
	}
	else if (wParam == AppCheckId)
	{ 
	
		if (AutoDeleteList)
			CheckMonitoredFiles(FALSE);
	}
	else
	{
		TRACE("Unknown timer\r\n");
	}

    break;
#else
    switch (wParam) 
    {
    case ID_TIMER:

    if ((CommState == ST_CLOSED_COMM) && (!Initializing)) 
    {
      KillTimer (hWnd, ID_TIMER);
    }
    else 
    {
      if ((CommState != ST_CLOSED_COMM) && (CommState != ST_WAIT_COMM))
        DoCommInput ();
      if (ExecAsynMessage) 
      {
        ExecAsynMessage = FALSE;
        /* maybe set to TRUE during executing of message, because we has still to wait */
        SendMessage(AsynMsg.hwnd,AsynMsg.uMsg,AsynMsg.wParam,AsynMsg.lParam);
      }
    };

    break;

default:
    if (PrevState == ST_DELETE) 
    {
          PostMessage(hWnd, WM_CHAR, '\r', 0);
    }
    break;
    }
break;
#endif

  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;




   /*******************************************************/
   /*** This code handles the Microsoft Intellimouse    ***/
   /*** wheel rotation.                                 ***/
   /*******************************************************/

   case WM_MYMOUSEWHEEL:
   case WM_MOUSEWHEEL:
    {
      int   zDelta;

      if (message == WM_MOUSEWHEEL) 
      {
          zDelta = (short) HIWORD(wParam);
      } 
      else 
      {
        zDelta = (int) wParam;
      }

      UTotalDelta += zDelta;
      if ((UScrollMode != WHEELSCROLL_DONT)  && (abs(UTotalDelta) >= WHEEL_DELTA)) 
      {
          UTotalDelta = 0;
        if ( zDelta > 0 )
        {
          if ((UScrollMode == WHEELSCROLL_SCROLL) && (MiddleButton_Is_Down)) 
          {
            if ((UPosScrollByWheel > 0) && (UScrollByWheel[UPosScrollByWheel-1]))
              UPosScrollByWheel--;
          } 
          else 
          {
            if ( UScrollByWheel[UPosScrollByWheel] == (UINT) -1)
			{
				//system want page scroll
				ScrollIt (&NetDoc, (WPARAM) SB_PAGEUP, lParam);

			}
			else
			{
				for (i = 0 ; i < UScrollByWheel[UPosScrollByWheel] ; i++)
					ScrollIt (&NetDoc, (WPARAM) SB_LINEUP, lParam);
			}
          }
        }
        else
        {
          if ((UScrollMode == WHEELSCROLL_SCROLL) && (MiddleButton_Is_Down)) 
          {
            if ((UPosScrollByWheel < (MAX_WHEEL_ENTRIES-1)) && (UScrollByWheel[UPosScrollByWheel+1]))
              UPosScrollByWheel++;
          } 
          else 
          { 
            if (UScrollByWheel[UPosScrollByWheel] == (UINT) -1)
			{
				ScrollIt (&NetDoc, (WPARAM) SB_PAGEDOWN, lParam);
			}
			else 
			{
				for (i = 0 ; i < UScrollByWheel[UPosScrollByWheel] ; i++ )
					ScrollIt (&NetDoc, (WPARAM) SB_LINEDOWN, lParam);
			}
          }
        }
      }
    }
    break;

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

  case WM_HSCROLL:
    if (!Initializing || Reconnecting == 1) 
    {
      HScrollIt (&NetDoc, wParam, lParam);
    }
    break;
  case WM_QUERYNEWPALETTE:
	{
		if (g_hPalette)
		{
			HDC hDC = GetDC(hWnd);
			if (hDC)
			{
				SelectPalette(hDC, g_hPalette, FALSE);
				RealizePalette(hDC);
				ReleaseDC(hWnd, hDC);
				return TRUE;
			}
			return 0;

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

	}
    case WM_PALETTECHANGED:
          if (!g_hPalette || (HWND) wParam == hWnd)
               break ;

          {
			HDC hdc = GetDC (hWnd) ;
			  if (hdc)
			  {
				SelectPalette (hdc, g_hPalette, FALSE) ;
				RealizePalette (hdc) ;
				UpdateColors (hdc) ;
  				//TODO send to decendents
				ReleaseDC (hWnd, hdc) ;
			}
		  }
          break ;
  case WM_ERASEBKGND:

        {
			HDC hDC = (HDC) wParam;
			if (g_hPalette && hListBackgroundBrush)
			{
				
				if (g_hPalette)
				{
					SelectPalette (hDC, g_hPalette, TRUE) ;
					RealizePalette (hDC) ;				
				}
				
				HBRUSH hOldBrush = (HBRUSH) SelectObject(hDC, hListBackgroundBrush);
		
        
				RECT rect;
				GetClientRect(hWnd, &rect );
				PatBlt( hDC,  rect.left, rect.top, 
						 RectWidth(rect), RectHeight(rect),PATCOPY);
				SelectObject(hDC, hOldBrush);
				return 1;
			}
			return (DefWindowProc (hWnd, message, wParam, lParam));

		}

	break;

  case WM_PAINT:
    {
      HANDLE hBlock;
      SIZE sz;
      unsigned int Offset, MyLen, indicatorwidth;
      long VertLines, RangeHigh, CurPos;
      long HorzChars;
      int Xtext;
      char far *textptr;
      char *cptr, *cptr2;
      char indicator;
      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 (g_hPalette)
	  {
		SelectPalette(hDC, g_hPalette, FALSE);
		RealizePalette(hDC);
	  }


      /* If still initializing comm link, put out a message    */
      /* to that effect.                                       */
      switch (Initializing) 
      {
      case INIT_NOT_CONNECTED:
	    if (Autoconnect && Reconnecting)
			goto display_usenet;
        cptr = "Ready to connect";
        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 local";
        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 %luth 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;
        SetCursor (hArrowCursor);   //shimomai
        /* fall into INIT_DONE */

      case INIT_DONE:
display_usenet:
        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 ( NOPALETTERGB(NetUnSubscribedColor) == RGB (0, 0, 0) ||  /* unsub/selected */
               NOPALETTERGB(ListBackgroundColor) == RGB (0, 0, 0) && IsBright (NetUnSubscribedColor))
            MyColors[2] = ListBackgroundColor;
          else
            MyColors[2] = NetUnSubscribedColor;

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

          if (NOPALETTERGB(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;
#ifdef _WIN32 //MDS proportional scroll     
          scrollInfo.cbSize = sizeof(SCROLLINFO);
          scrollInfo.fMask = SIF_RANGE | SIF_PAGE;
          scrollInfo.nMin = 0;
          //scrollInfo.nMax = RangeHigh + NetDoc.ScYLines-1;
            scrollInfo.nMax = RangeHigh + VertLines -1;
          scrollInfo.nPage = NetDoc.ScYLines;
          if (!IsFlagSet(&NetDoc, ThumbTracking))//;NetDoc.ThumbTracking)
          {
            scrollInfo.fMask |= SIF_POS;
            scrollInfo.nPos = CurPos;
          }

          SetScrollInfo( hWnd, SB_VERT, &scrollInfo, TRUE);
#else


     //   SetScrollRange (hWnd, SB_VERT, 0, RangeHigh,       // JD 1/8/97
          SetScrollRange (hWnd, SB_VERT, 0, OrdToScroll(RangeHigh,RangeHigh),
                        IsFlagSet( &NetDoc,ThumbTracking) ? FALSE : TRUE);
        /* thumb pos is updated at end of thumb track */
        if (!IsFlagSet(&NetDoc,ThumbTracking) )
        {
     //       SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);   // JD 1/8/97
              SetScrollPos (hWnd, SB_VERT, OrdToScroll(RangeHigh,CurPos), TRUE);
        }
#endif
        
        RangeHigh = NetDoc.LongestLine - NetDoc.ScXChars;
        if (RangeHigh < 0) 
        {
          RangeHigh = 0;
          NetDoc.ScXOffset = 0;
        }
#ifdef _WIN32 //MDS proportional scroll
        scrollInfo.cbSize = sizeof(SCROLLINFO);
        scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
        scrollInfo.nMin = 0;
        scrollInfo.nMax = NetDoc.LongestLine;
        scrollInfo.nPage = NetDoc.ScXChars;
        scrollInfo.nPos = NetDoc.ScXOffset;
        SetScrollInfo( hWnd, SB_HORZ, &scrollInfo, TRUE);
#else

        SetScrollRange (hWnd, SB_HORZ, 0, OrdToScroll(RangeHigh,RangeHigh), FALSE);
        SetScrollPos (hWnd, SB_HORZ, OrdToScroll(RangeHigh,NetDoc.ScXOffset), TRUE);

#endif

        /* 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->ServerEstNum > 0 && MyGroup->pSeendb->NumUnseen()  ) 
                  {
                      indicator = 'u';
                  }
                }
              }

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

              if (MyGroup->Determined == FALSE)
			  {
                if (did_list)
					strcpy (str, "X");
				else
					strcpy (str, "-");
			  }
              else
			  {
				
				if (ShowUnreadCount)
				{
					BIGINT ServerEstNum =  MyGroup->NumUnread;
					if (ServerEstNum < 0)
						ServerEstNum = 0;
                
					sprintf (str, "%5I64u", ServerEstNum);
				}
				else
				{
					sprintf (str, "%5I64u", MyGroup->ServerEstNum);

				}
			  }
              SetRect (&aRect, 0, Y, X + GroupListSeparator, Y + LineHeight);
              SetTextAlign (hDC, TA_TOP | TA_RIGHT);
              ExtTextOut (hDC, X + GroupListSeparator, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
                          str, strlen(str), (LPINT) NULL);
              SetTextAlign (hDC, TA_TOP | TA_LEFT);
              TextOut (hDC, X, Y, &indicator, 1);
              SetRect (&aRect, X + GroupListSeparator, Y, myRect.right, Y + LineHeight);
              ExtTextOut (hDC, X + GroupListSeparator+CharWidth, Y, 
                          ETO_OPAQUE | ETO_CLIPPED, &aRect,
                          group_name, strlen(group_name), (LPINT) NULL);
              SetRect (&aRect, 0, Y, myRect.right, Y + LineHeight);

              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:

    DoHelp (NetDoc.hDocWnd, 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);
	EnableMenuItem (hSubMenu,IDM_FIND_PREV_SAME, mode);
	EnableMenuItem (hSubMenu,IDM_SELECT_ALL, 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);
	//   Keep reset Menu enabled (JD 3/8/97)
	//  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_RESET, TRUE);
	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
 */
static TCHAR szAuthors[] = \
	" Matt Bretherton\tMBretherton@labyrinth.net.au\r\n"\
	" Jody Glasser\tjglasser@netcom.com\r\n" \
	" Harvey Brydon\thab@slb.com\r\n" \
	" Michael Finken\tfinken@conware.de\r\n" \
	" Masaki Ishido\tishidou@jpn.hp.com\r\n" \
	" Conor Nolan   \tcnolan@tcd.ie\r\n" \
	" Mitsuo Shimomai\tsim@aae.kagoshima-u.ac.jp\r\n" \
	" Tanaka Goh\tgoh@yamato.ibm.com.jp\r\n" \
	" Vince Vielhaber\tvev@michvhf.com\r\n" \
	" Don Gardner   \tDon.Gardner-1@ksc.nasa.gov\r\n" \
	" Michael Downs\tdowns@titan.ksc.nasa.gov\r\n" \
	" Roger Pearse\trpearse@tertullian.net\r\n" \
	" Tom Menshik  \ttmenshik@hpcvitlm.cv.hp.com\r\n" \
	" Jim Martin  \tjim@noc.rutgers.edu\r\n" \
	" Peter Heirich\tpeter@heirich.in-berlin.de\r\n" \
	" Mark Spankus\tmark@marks-lab.com\r\n"\
	" Ray Dunn \traydunn@canada.com\r\n";

INT_PTR CALLBACK About (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) 
	{
	case WM_INITDIALOG:
		SetDlgItemText (hDlg, IDD_VERSION_NUMBER, (LPSTR)WINVN_VERSION);
		SetDlgItemText(hDlg, IDC_EB_AUTHORS, szAuthors);
		return (TRUE);

	case WM_COMMAND:
		if (GET_WM_COMMAND_ID(wParam, lParam) == 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) 
	{
		WVArgMessageBox (NetDoc.hDocWnd, IDS_WINVN_SAME_COLOR, IDS_WINVN_COLOR, MB_OK | MB_ICONINFORMATION, warnMsg);
		return TRUE;
	}
	return FALSE;
}

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

/*--- function GetNumFromBuf --------------------------------------------
 *
 *  Cracks off a positive integer number from a string in an MRRFile buffer.
 *
 *  Entry    MRRFile->buf[MRRFile->bufptr]  is the character position to start scanning
 *                 for an integer
 *
 *  Exit     MRRFile->buf[MRRFile->bufptr]  is the character position at which we stopped
 *                 scanning (because of a non-digit).
 */
UBIGINT GetNumFromBuf(TypMRRFile *MRRFile)
{
	UBIGINT uRetval = 0;

	while(MRRFile->buf[MRRFile->bufidx] == ' ') // Skip initial spaces

	MRRFile->bufidx++;

	while(isdigit(MRRFile->buf[MRRFile->bufidx]))
	{
		uRetval = 10 * uRetval + (MRRFile->buf[MRRFile->bufidx] - '0');
		MRRFile->bufidx++;
	}
	return uRetval;
}


int BuildGroup(const char *sLine, TypLine *lineptr)
{
	const char *sTmp = sLine;
	char *grname = GetGroupName(lineptr);
	TypGroup *group = GetGroup(lineptr);

	group->Subscribed = FALSE;
	group->Selected = FALSE;
	group->Determined = FALSE;
	group->Threaded = FALSE;
	group->NameLen = 0;
	group->SubjDoc = (TypDoc *) NULL;
	group->ServerEstNum = 0;
	group->ServerFirst = 0;
	group->ServerLast = 0;
	group->uLoadFirst = 0;
	group->uLoadLast = 0;
	group->HighestPrevSeen = 0;
	group->header_handle = (HANDLE) NULL;
	group->thread_handle = (HANDLE) NULL;
	group->total_headers = 0;
	group->max_headers = 0;
	group->alloc_headers = 0;
	group->NumUnread = 0;
	group->pFile_sort = NULL;
	group->pAA = NULL;
	group->pSeendb = NULL;
	group->pAddHdr = NULL;
	group->Description = NULL;
#ifdef OFFLINE
	group->pSME = NULL;
#endif

	// Copy group name to output line.
	while (*sTmp && *sTmp != ':' && *sTmp != '!')
	{
		*(grname++) = *(sTmp++);
		(group->NameLen)++;
		if (group->NameLen == BUFSIZE -1)
			break; //MDS avoid buffer overrun on long group
	}
	*(grname++) = '\0';

	//TRACE("BuildGroup(): Built group named <%s>\n", GetGroupName(lineptr));
	lineptr->length = CalcGroupLen(group);
	lineptr->LineID = NextLineID++;
	GroupLenPtr(lineptr) = lineptr->length;

	return (int) (sTmp - sLine);
}





/* --- 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(TypMRRFile *MRRFile, TypLine *lineptr)
{
  TypGroup *group = GetGroup(lineptr);
  UBIGINT uTemp;
  TypRange rTmp;
  char ch;
  BOOL bFail = FALSE;
  
  do
  {
    
	//MDS hack to prevent extra long group lines from crashing winvn
    //If the group name hits limit, keep reading until EOL & get the next
    //line. This will prune offending groups from the newsrc 
    MRRFile->bufidx += BuildGroup(&MRRFile->buf[MRRFile->bufidx], lineptr);
  
    if (group->NameLen >= MAXGROUPNAME)
    {
      //This group name is too long, ignore it! 
      MRRReadInternalBuff(MRRFile);
      // Scan for end of line (or EOF)
      while((ch = MRRFile->buf[MRRFile->bufidx]) && ch != '\n' && ch != '\r' )
      {
        MRRFile->bufidx++;
        if(MRRFile->bufidx > sizeof(MRRFile->buf)/2)
        {
          MRRReadInternalBuff(MRRFile);
        }
      }
      //scan for start of line
      while((ch = MRRFile->buf[MRRFile->bufidx]) && (ch == '\n' || ch == '\r'))
        MRRFile->bufidx++;

    } 
    else if (ch = MRRFile->buf[MRRFile->bufidx])
    {
      if(ch == ':')
      {
        group->Subscribed = TRUE;
      }
      MRRFile->bufidx++;
    }
  } while (group->NameLen >= MAXGROUPNAME);  

  // Start looking for seen article info.
  // The following line syntax must be accomodated:
  // news.group:
  // news.group: 1
  // news.group: s123 1
  // news.group: s123 1-100
  // news.group: s123 1,105-110, ...
  // news.group: s123 1-100,105-110, ...
  // news.group: s123 1-100,103,105-110, ...
  
  // Look for the highest article number previously available on the server,
  // in an entry of form "s" followed by a number.
  while(MRRFile->buf[MRRFile->bufidx] == ' ')
    MRRFile->bufidx++;
#if NEWSRC_MAKE_COMPAT
  if((ch = MRRFile->buf[MRRFile->bufidx]) && ch == 's')
  {
    MRRFile->bufidx++; // skip over the 's'
    group->ServerFirst = 1;
    group->ServerLast = // Heirich
      group->HighestPrevSeen = GetNumFromBuf(MRRFile);
    if ((ch = MRRPeek(MRRFile)) != -1 && ch != LF) {
      MRRFile->bufidx ++;
    }
  }
  if(ch != '\n')
  {
    long lPrevSeen = 0;
    group->pSeendb = NULL;
    while ( 0 < (rTmp.First = GetNumFromBuf(MRRFile)) ) 
    {
      rTmp.Last = rTmp.First;
      if( MRRPeek(MRRFile) == '-' ) 
      {
        MRRFile->bufidx ++;
        rTmp.Last = GetNumFromBuf(MRRFile);
      }
      if( rTmp.First > rTmp.Last ) 
      {
        /* Invalid line */
      } 
      else 
      {
        if( lPrevSeen + 1 == rTmp.First ) 
        {
          lPrevSeen = rTmp.Last;
        } 
        else 
        {
         if( !group->pSeendb ) 
         {
           
           if (group->HighestPrevSeen > lPrevSeen &&  group->HighestPrevSeen - lPrevSeen > MaxDBSize)
           {
                //MDS fix f*d up max seen
                group->HighestPrevSeen = lPrevSeen + MaxDBSize;
           }
           group->pSeendb = new WVSeenDB( lPrevSeen + 1, group -> HighestPrevSeen );
           if( group->pSeendb == NULL ) 
           {
             break;
           }
         }
         group->pSeendb->MarkSeen(rTmp.First, rTmp.Last);
        }
      }
      if( MRRPeek(MRRFile) != ',' ) 
      {
        break;
      }
      MRRFile->bufidx ++;
    }
    if( 0 < lPrevSeen && group->pSeendb == NULL ) 
    {
      
       if (group->HighestPrevSeen > lPrevSeen &&  group->HighestPrevSeen - lPrevSeen > MaxDBSize)
       {
            //MDS fix f*d up max seen
            group->HighestPrevSeen = lPrevSeen + MaxDBSize;
       }
      
      group->pSeendb = new WVSeenDB( lSeen + 1, group -> HighestPrevSeen );
    }
  }
#else
  if((ch = MRRFile->buf[MRRFile->bufidx]) && ch != 's') // not an 's', we're done with this line...
  {
    while((ch = MRRFile->buf[MRRFile->bufidx]) && ch != '\n')
      MRRFile->bufidx++;
  }
  else
  {
/* Ray Dunn:99/12:  "Corrupt" newsrc files can play havoc - one instance is when a "seen"
                     number is higher than HighestPrevSeen - in Threshold dialog, all
                     articles show as being unseen, but load as being seen - that is a bug
                     elsewhere, but ensuring the numbers are OK here is a better fix.
                     
                     Don't know what causes this corruption, but I've seen it several
                     times, and saw it just now when subscribing to a group that didn't
                     exist before.  (Later: Found that problem, it is an error in the
                     tracking of crosspostings (see mark_article_seen), but will leave this
                     check in, too).

                     Fix is to ignore any values that are greater than HighestPrevSeen.

                     There are also serious problems if you carry a newsrc from one server
                     to another - definitely not a good idea!

                     The fact that winvn does not check the integrity of the newsrc file
                     both internally and with the info it gets from the server, is a serious
                     deficiency, particularly because the newsrc is an editable file (users
                     will try to do "clever" things!)

                     A partial fix for this has been added in DoCommState in WvUtils.cpp
*/

    if (MRRFile->buf[MRRFile->bufidx])
		MRRFile->bufidx++; // skip over the 's'
    group->ServerFirst = 1;
    
    //MDS fix for incorrectly written signed seen value
    while(MRRFile->buf[MRRFile->bufidx] == ' ') // Skip initial spaces
      MRRFile->bufidx++;
    
    if ((ch = MRRFile->buf[MRRFile->bufidx]) && !isdigit(ch))
    {
        //MDS seen list may have negative # due to signed long wrap
        if (ch == '-')
        {
            MRRFile->bufidx++;
            BIGINT signed_num = GetNumFromBuf(MRRFile);

            group->ServerLast = // Heirich
                group->HighestPrevSeen = (UBIGINT) signed_num;
        }

    }
    else
    {

        group->ServerLast = // Heirich
            group->HighestPrevSeen = GetNumFromBuf(MRRFile);
    }

    if((ch = MRRFile->buf[MRRFile->bufidx]) && ch != '\n')
    {
      MRRFile->bufidx++;
      rTmp.Last = rTmp.First = GetNumFromBuf(MRRFile); // usually '1'
      if((ch = MRRFile->buf[MRRFile->bufidx]) && ((ch == '-') || (ch == ',')))
      {
        if(ch == '-')
        {
          MRRFile->bufidx++;
          rTmp.Last = GetNumFromBuf(MRRFile);
        }
/* Ray Dunn:99/12: fix here */
        if (rTmp.Last > group->HighestPrevSeen)
        {
          NewsrcDirty = TRUE;
          rTmp.Last = group->HighestPrevSeen;
        }
        
        if ( group->HighestPrevSeen - rTmp.Last > MaxDBSize)
        {
            //there are more than 500K msgs in this active group.
            //Screw this, we don't support that many
			TRACE("Extreme group range in %s\r\n", GetGroupName(lineptr));
            rTmp.Last = group->HighestPrevSeen-1;// - MaxDBSize;
            
        }
        group->pSeendb = new WVSeenDB(rTmp.Last+1, group->HighestPrevSeen); // This can fail...
		if(!group->pSeendb) // Nope? Skip to end of line
        {
          MRRReadInternalBuff(MRRFile);
          while((ch = MRRFile->buf[MRRFile->bufidx]) && ch != '\n')
          {
            MRRFile->bufidx++;
          }
        }
        else
        {
          group->pSeendb->MarkSeen(rTmp.First, rTmp.Last);
          while(MRRFile->buf[MRRFile->bufidx] == ',')
          {
            MRRFile->bufidx++;
            uTemp = GetNumFromBuf(MRRFile);
            if(MRRFile->buf[MRRFile->bufidx] == '-')
            {
              MRRFile->bufidx++;

/* Ray Dunn:99/12: and fix here */
              UBIGINT uTemp1 = GetNumFromBuf(MRRFile);
              if (uTemp > group->HighestPrevSeen || uTemp1 > group->HighestPrevSeen)
                NewsrcDirty = TRUE;   // ensure fix is written back to newsrc
              else
                group->pSeendb->MarkSeen(uTemp, uTemp1);
            }
            else
            {
/* Ray Dunn:99/12: and fix here */
              if (uTemp > group->HighestPrevSeen)
                NewsrcDirty = TRUE;   // ensure fix is written back to newsrc
              else
                group->pSeendb->MarkSeen(uTemp);
            }
            if(MRRFile->bufidx > sizeof(MRRFile->buf)/2)
            {
              MRRReadInternalBuff(MRRFile);
            }
          }
        }
      }
    }
  }
#endif /* NEWSRC_MAKE_COMPAT */
  // Scan for the first character of a line (or EOF)
  while((ch = MRRFile->buf[MRRFile->bufidx]) && (ch < 0x20 )) //MDS ignore whitespace, ^Z etc.
  {
    MRRFile->bufidx++;
    if(MRRFile->bufidx > sizeof(MRRFile->buf)/2)
    {
      MRRReadInternalBuff(MRRFile);
    }
  }
  
  MRRReadInternalBuff(MRRFile);
  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;
  TypMRRFile *MRRFile;
  char szString[MAXFILENAME+50];
  int iRet = 0;
  
  if(LockLine(NetDoc.hCurAddBlock, NetDoc.AddOffset, NetDoc.AddLineID, &BlockPtr, &GroupPtr) &&
    (LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, TEMPBUFSIZE)))
  {
    WVGetFileWriteTime(szNewsSrc, ftNewsSrc); 
	hRetCode = MRROpenFile (szNewsSrc, OF_READ, &MRRFile);
    if((int) hRetCode <= 0)
    {
      iRet = 0;
    }
    else
    {
	  LinesInRC = 0;
      MRRReadInternalBuff(MRRFile);
      while(MRRFile->buf[MRRFile->bufidx])
      { 
        if (LinesInRC%1000 == 0) 
		{
                sprintf(szString,"%lu",LinesInRC);
                WVArg2SetStatbarText(NetDoc.hWndFrame, IDS_STBAR_LOADING_GROUP ,&NetDoc, TRUE, TRUE, szString, szNewsSrc);
        }
        // This is seperated from the code above so we can get feedback and
        // have a place to put breakpoints when troubleshooting problems
        // with WinVN blowing up when given a large number of newsgroups
  
        // if (LinesInRC >= 32000) {
        //      j = sprintf(szString,"Loading %luth Newsgroup",LinesInRC);
        //      sprintf(szString+j," from file %s",szNewsSrc); 
        //      SetStatbarText(NetDoc.hWndFrame,szString,&NetDoc, TRUE, TRUE);
        //}
        if(CrackGroupLine(MRRFile, LinePtr))
        {
          NetDoc.CountedLines++;
        }
        /*df 2000-04-28 - Clear uninit read */
        LinePtr->active = FALSE;
        AddLine(LinePtr, &BlockPtr, &GroupPtr);
        LinesInRC++;
      }
      MRRCloseFile(MRRFile);
      sprintf(szString,"%lu",LinesInRC);
      WVArg2SetStatbarText(NetDoc.hWndFrame, IDS_STBAR_LOADED_GROUP, &NetDoc, TRUE, TRUE, szString, szNewsSrc);
       
      WVArgSetStatbarText(NetDoc.hWndFrame,IDS_STBAR_FINISH_GROUP,&NetDoc, TRUE, TRUE, szNewsSrc);
#ifdef MULTIGROUP
	  BuildGroup("MULTIGROUP!", LinePtr);
	  LinePtr->active = FALSE;
      AddLine(LinePtr, &BlockPtr, &GroupPtr);
      LinesInRC++;


#endif      
      // 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();
      
      started_with_no_dolist = !DoList;
      iRet = 1;
    }
    GlobalFreePtr(LinePtr); 
    NewsrcDirty = FALSE;
  }
  return iRet;
}


// Given a pointer to two group (of the same name) 
// Update the destination group seen ranges with the seen ranges from the source group
BOOL UpdateRanges( TypGroup *DestGroup, TypGroup *SrcGroup)
{

	TypRange r;

	if (SrcGroup->ServerLast > DestGroup->ServerLast)
	{
		DestGroup->ServerLast = SrcGroup->ServerLast;
	}
	if (SrcGroup->Subscribed != DestGroup->Subscribed)
		DestGroup->Subscribed = SrcGroup->Subscribed;
		 
	if (!DestGroup->pSeendb)
		DestGroup->pSeendb = new WVSeenDB( 1, DestGroup->HighestPrevSeen );

	

	if(SrcGroup->ServerFirst)
    {
        /* Affix ranges of articles seen. */
        if(SrcGroup->pSeendb)
        {
            r.First = r.Last = 0;
            SrcGroup->pSeendb->GetRange(r);
            if (r.Last > SrcGroup->ServerLast)
                r.Last = SrcGroup->ServerLast; //MDS last can not be more than what server has, maybe user is switching servers & not newsrc
            r.Last = max(r.Last, SrcGroup->ServerFirst - 1); // quicker but can cause 'extra range' error
		 	
			BOOL bUpdate = FALSE;
			
			while(r.First)
            {

                //MDS if range exceeds server, it's left over from bogus range
                //in newsrc or server has reset article numbers
                if (r.First <= SrcGroup->ServerLast && r.Last <= SrcGroup->ServerLast)
                {
					DestGroup->pSeendb->MarkSeen(r.First, r.Last);

					//If group is visible, update it too
					if((DestGroup->total_headers > 0) && (DestGroup->header_handle) && (DestGroup->SubjDoc))
					{
						header_p xheaders;
						

						xheaders = lock_headers (DestGroup->header_handle, DestGroup->thread_handle);
						for (long index = 0; index < DestGroup->total_headers; index++) 
						{
							if ((long) xheaders[index].number >= r.First &&
								(long) xheaders[index].number <= r.Last &&
								!(xheaders[index].SeenSelected & ART_SEEN) )
							{
								xheaders[index].SeenSelected |= ART_SEEN;
								xheaders[index].SeenSelected &= ~ART_SELECTED;

							}
						}

						unlock_headers (DestGroup->header_handle, DestGroup->thread_handle);

					}

				}
				if (r.Last >= SrcGroup->ServerLast)
					break;

				SrcGroup->pSeendb->GetRange(r);
			}

			if (bUpdate && DestGroup->SubjDoc->hDocWnd)
				InvalidateRect(DestGroup->SubjDoc->hDocWnd, NULL, FALSE);

		}

	}

    DestGroup->NumUnread = CalcNumUnread(DestGroup);

    DestGroup->pSeendb->DestroyBitmap(); /* if all seen or all unseen free Bitmap */

	return TRUE;

} 

// If the newsrc has been updated by another instance of WinVN we should merge it's seen-article
// list with our in-memory version.
//
// Open the newsrc, read the ranges of an entry, mark current DB seen ranges, clean up DB, next entry
int MergeNewsrc()
{
	//TypBlock far *BlockPtr;
	TypLine *LinePtrNew, *LinePtr;
	//HANDLE hBlock;
	HFILE hRetCode;
	//unsigned int Offset;
	//TypLineID MyLineID;
	TypMRRFile *MRRFile;
	
	int iRet = 0;
	int nNew = 0;
	int nLines = 0;
	extern TypLine huge * huge * NetHashTable;

	
	SetCursor (hHourGlass);


	InitGroupTable();

	hRetCode = MRROpenFile (szNewsSrc, OF_READ, &MRRFile);
    if((int) hRetCode <= 0)
    {
		iRet = 0;
    }
    else
    {
		LinePtrNew = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, TEMPBUFSIZE);

		/* Initialize the hash table */
		InitHashTable (LinesInRC);

		/* And set up the hash table for stuff from the newsrc */
		HashNetGroups (&NetDoc, NetHashTable);
		nNewGroups = 0;

		MRRReadInternalBuff(MRRFile);
		while(MRRFile->buf[MRRFile->bufidx])
		{ 
			nLines++;

			if (! (nLines%100) )
			{

				WVArgXSetStatbarText(NetDoc.hWndFrame, IDS_STBAR_MERGE ,&NetDoc, TRUE, FALSE, iRet, nNew);
			}

			BOOL bSubsc = CrackGroupLine(MRRFile, LinePtrNew);

			//find existing group
			char * pName = GetGroupName(LinePtrNew);
			TypGroup *Tgroup = GetGroup(LinePtrNew);

			//if (!FindGroup(pName, &NetDoc, &BlockPtr, &LinePtr))
			LinePtr = FindHashEntry(pName);
			if (!LinePtr)
			{

				//This is a new group.
				//TRACE("New group encountered %s\r\n", pName);
				LinePtrNew->active = FALSE;
				if (Tgroup->HighestPrevSeen == Tgroup->ServerLast)
					Tgroup->HighestPrevSeen = Tgroup->ServerFirst; //do want new group to show as read
				if (AddGroupToTable ( (char *) LinePtrNew))
					nNew++;

			}
			else
			{
				//UnlockLine (BlockPtr, LinePtr, &(BlockPtr)->hCurBlock,&Offset,&MyLineID);

				TypGroup *MyGroup = GetGroup(LinePtr);
				BOOL bChange = FALSE;

				//which one is newer?
				if (MyGroup->ServerLast > Tgroup->ServerLast)
				{
					TRACE("MyGroup is newer than newsrc group %s\r\n",pName); 
					bChange = TRUE;

				}
				else if (MyGroup->ServerLast < Tgroup->ServerLast)
				{
					TRACE("MyGroup is older than newsrc group %s\r\n",pName); 
					bChange = TRUE;

				}
				else
				{
					if (CalcNumUnread(MyGroup) > CalcNumUnread(Tgroup))
					{
						TRACE("MyGroup has less read than newsrc group %s\r\n",pName);
						bChange = TRUE;

					}
					else if (CalcNumUnread(MyGroup) < CalcNumUnread(Tgroup))
					{
						TRACE("MyGroup more read than newsrc group %s\r\n",pName);
						bChange = TRUE;

					}
				//else they're the same

				}

				if (bChange)
				{
					iRet++;
					UpdateRanges(MyGroup, Tgroup);
					

				}

				//only delete if the group is not new
				//because the pointer has been copied to the AddGroupToTable list
				if ( Tgroup->pSeendb)
				{
					  delete Tgroup->pSeendb;
					  Tgroup->pSeendb = NULL;

				}



			}

		}
		MRRCloseFile(MRRFile);

		FreeHashTable();

		if (nNew)
		{
			BuildPtrList ();
			MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
			CleanUpGroupTable ();
			ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
			NetDoc.LongestLine = 0;
			SetGroupActiveLines ();
			if (NetDoc.hDocWnd && IsWindow(NetDoc.hDocWnd))
			{
				ScreenToTop (&NetDoc);
				SetNetDocTitle ();
				InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
			}

			LinesInRC += nNew;

		}
		else
		{
			/* Unlock and free the NewGroupTable and NewGroupData.  */
			if (hNewGroupData)
			{
				GlobalUnlock (hNewGroupData);
				GlobalFree (hNewGroupData);
				hNewGroupData = NULL;
			}
			if (hNewGroupTable) 
			{
				GlobalUnlock (hNewGroupTable);
				GlobalFree (hNewGroupTable);
				hNewGroupTable = NULL;
			}

		}
      

		GlobalFreePtr(LinePtrNew); 

    }

	if (iRet > 0 || nNew)
	
		NewsrcDirty =  TRUE;

		
	WVArgXSetStatbarText(NetDoc.hWndFrame, IDS_STBAR_MERGE ,&NetDoc, TRUE, FALSE, iRet, nNew);


    SetCursor (hArrowCursor);

	return iRet;
} 


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

#define NEWSRCLINESIZE 2048

BOOL WriteNewsrc()
{
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    HANDLE hBlock;
    unsigned int Offset;
    TypLineID MyLineID;
    char far *fromptr;
    char *toptr;
    HFILE hRetCode;
    TypMRRFile *MRRFile;
    int percent,opercent,errcode;
    TypGroup far *Group;
    TypRange r;
    char *NewsLine;
    BOOL firstrange;
    char newsrc_temp[MAXFILENAME];
    char newsrc_extension[_MAX_EXT ];
    int extension_length, filename_length;
    long loopctr = 0;
    long loopend = max(1,LinesInRC);
	DWORD dwError; 
	FILETIME ftCurrent;

    /* conditions in which we do not want to write it out */
    if (Initializing == INIT_READING_NEWSRC ||
        Initializing == INIT_SCANNING_NETDOC ||
        Initializing == INIT_GETTING_LIST) 
    {
        WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_NEWSRC_INIT, IDS_WINVN_NEWSRC_SAVE1, MB_OK | MB_ICONSTOP);  
        return FALSE;
    }

    if ((NewsLine = (char *) GlobalAllocPtr (GHND, NEWSRCLINESIZE * sizeof (char))) == NULL) 
    {
        WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_ALLOCFAIL, IDS_WINVN_ERROR, MB_OK);
        return FALSE;
    }
savetop:
	loopctr = 0;

    WVArgSetStatbarText(NetDoc.hWndFrame, IDS_STBAR_SAVE_NEWSRC, &NetDoc, TRUE, TRUE, szNewsSrc);
    SetStatbarPercent(NetDoc.hWndFrame,(percent = 0),&NetDoc,TRUE);
    LockLine (NetDoc.hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr);


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

    WVArgSetStatbarText(NetDoc.hWndFrame, IDS_STBAR_OPEN_NEWSRC, &NetDoc, TRUE, TRUE, szNewsSrc);
   	WVGetFileWriteTime(szNewsSrc, ftCurrent);

	if ( CompareFileTime(&ftNewsSrc, &ftCurrent) < 0 )
	{
		
		UINT_PTR nRet = DialogBox (hInst, MAKEINTRESOURCE(IDD_MERGE), NetDoc.hDocWnd, lpfnWinVnMergeDlg);
		if (nRet == IDCANCEL)
			return FALSE;

		if (nRet == IDOK)
		{
			MergeNewsrc();
			WVGetFileWriteTime(szNewsSrc, ftNewsSrc);
		}

		//IDIGNORE means overwrite...


	
	}


    hRetCode = MRROpenFile (newsrc_temp, OF_WRITE, &MRRFile);
	

   
    WVArgSetStatbarText(NetDoc.hWndFrame,IDS_STBAR_WRITE_NEWSRC, &NetDoc, TRUE, TRUE, szNewsSrc);
    opercent = percent;

    if ((int) hRetCode <= 0) 
    {
       if ( WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_NEWSRC_WRITE, IDS_WINVN_NEWSRC_SAVE2, MB_RETRYCANCEL  | MB_ICONSTOP) == IDRETRY )
		goto savetop;
    }
    else 
    {
		BOOL rmGroups = FALSE;  //remove old groups

        do 
        {
			percent = (int) ((100 * loopctr++) / loopend);
            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! */

            /* don't remove a subscribed group. We lost information about */
            /* which articles are seen in case of a newgroup/rmgroup war */

            if ( !rmGroups && (Group->Determined || Group->Subscribed || !did_list) )
            {
                /* Copy group name                                          */
                fromptr = GetGroupName(LinePtr);
#if 0
				if (did_list && !Group->Determined && Group->Subscribed )
				{
					//Group not found on sever...
					if (WVArgXMessageBox(NetDoc.hDocWnd, IDS_WVUSENET_RMGROUP, IDS_WVUSENET_RMGROUPTITLE, MB_YESNO, fromptr) == IDYES)
					{
						continue;
					}
				}
#endif
#ifdef MULTIGROUP
				//we don't save this group
				if (fromptr && !strcmp(fromptr, "MULTIGROUP"))
				{
					//NextLine (&BlockPtr, &LinePtr)
					continue;
				}
#endif

                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)
                        BIGINTtoa (Group->ServerLast, toptr, 10);
                    else
                        BIGINTtoa (Group->HighestPrevSeen, toptr, 10);
                    while (*toptr)
                        toptr++;
                    *(toptr++) = ' ';
                }
                if(Group->ServerFirst)
                {
                    /* Affix ranges of articles seen. */
                    if(Group->pSeendb)
                    {
#ifdef OFFLINE
                        /* mark all articles we will get by SENDME as unseen */
                        TypSendmeList * SME;
                        TypSendmeList * SMEptr = Group->pSME;
                        while (SMEptr) 
                        {
                            Group->pSeendb->MarkUnseen(SMEptr->article);
                            SME = SMEptr;
                            SMEptr = SMEptr->next;
                            delete SME;
                        }

#endif
                        r.First = r.Last = 0;
                        Group->pSeendb->GetRange(r);
                        if (Group->Determined && r.Last > Group->ServerLast)
                            r.Last = Group->ServerLast; //MDS last can not be more than what server has, maybe user is switching servers & not newsrc
                        r.Last = max(r.Last, Group->ServerFirst - 1); // quicker but can cause 'extra range' error
                        //if(r.Last < Group->ServerFirst - 1)
                        //{
                        //  Group->pSeendb->MarkSeen(1, Group->ServerFirst - 1); // mark expired articles as 'seen'
                        //  r.First = r.Last = 0;
                        //  Group->pSeendb->GetRange(r);
                        //}
                        firstrange = TRUE;

                        while(r.First)
                        {

                            //MDS if range exceeds server, it's left over from bogus range
                            //in newsrc or server has reset article numbers
                            if (r.First <= Group->ServerLast && r.Last <= Group->ServerLast)
                            {

                                if(toptr > NewsLine + NEWSRCLINESIZE - 25)
                                {
                                    dwError = MRRWriteBuff(MRRFile, NewsLine, toptr - NewsLine);
                                    toptr = NewsLine;
                                }
                                /* Write out ',' if not first range of articles. */
                                if (!firstrange)
                                {
                                    *(toptr++) = ',';
                                }
                                
                                /* Write out first article in a range.*/
                                BIGINTtoa ( r.First, toptr, 10);
                                while (*toptr)
                                    toptr++;
                                /* If the range is of form "n-n", just put out "n"  */
                                if (firstrange || r.Last > r.First)
                                {
                                    /* Put out the hyphen in middle of range. */
                                    *(toptr++) = '-';
                                    /* Put out the last article in a range. */
                                    BIGINTtoa ( r.Last, toptr, 10);
                                    while (*toptr)
                                        toptr++;
                                }
								firstrange = FALSE;

                            }
							if (Group->Determined && r.Last >= Group->ServerLast)
								break;
                            Group->pSeendb->GetRange(r);
                        }
                    }
                    else // no range info recorded
                    {
                        *(toptr++) = '1';
                        if (Group->ServerFirst > 1) 
                        {
                            *(toptr++) = '-';
                            BIGINTtoa (Group->ServerFirst-1, toptr, 10);            
                            while (*toptr)
                                toptr++;
                        }
                    }
                }
                dwError = MRRWriteLine (MRRFile, NewsLine, toptr - NewsLine);
				if (dwError != NO_ERROR)
				{
					//write failed
					LPTSTR   message;
					
					MRRCloseFile (MRRFile);
					DeleteFile(newsrc_temp);
					UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
					 

					FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
								   FORMAT_MESSAGE_FROM_SYSTEM, 
								   NULL, 
								   dwError, 
								   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
								   (LPTSTR) &message, 
								   0, 
								   NULL); 

					if (WVArgMessageBox (NetDoc.hDocWnd, IDS_WVUSENET_SAVENEWSRC, IDS_WINVN_NEWSRC_SAVE2,
									  MB_YESNO | MB_ICONQUESTION, message) == IDNO)
					{
						LocalFree (message);
						GlobalFreePtr(NewsLine);
						SetStatbarPercent(NetDoc.hWndFrame,0,&NetDoc,TRUE);
						SetStatbarText(NetDoc.hWndFrame,"Error newsrc not saved!",&NetDoc, TRUE, TRUE);

						return FALSE;
					}
					else
					{
						LocalFree (message);

						goto savetop;

					}

				}
            }
			else
			{
				TRACE("Removing unavailable group \"%s\"\r\n",GetGroupName(LinePtr));
	
			}
        } while (NextLine (&BlockPtr, &LinePtr));

        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID); 
        WVArgSetStatbarText(NetDoc.hWndFrame, IDS_STBAR_CLOSE_NEWSRC,&NetDoc, TRUE, TRUE, szNewsSrc);
        SetStatbarPercent(NetDoc.hWndFrame,100,&NetDoc,TRUE);
        
		MRRCloseFile (MRRFile);
		BOOL bRetry = TRUE;
		while (bRetry)
		{
			bRetry = FALSE;
			unlink (szNewsSrc);
			SetStatbarPercent(NetDoc.hWndFrame,0,&NetDoc,TRUE);
			SetStatbarText(NetDoc.hWndFrame,"",&NetDoc, TRUE, TRUE);

			errcode = rename (newsrc_temp,szNewsSrc);
			//  errcode = EXDEV;  /* force fail for debug */
			if (errcode != 0) 
			{
				char error_string[20];

				switch (errno) 
				{
					default:
					case EACCES:
					case EEXIST:
						bRetry = (WVArgXMessageBox(NetDoc.hDocWnd,IDS_WINVN_NEWSRC_REN1, IDS_WINVN_NEWSRC_SAVE3,
								MB_RETRYCANCEL | MB_ICONSTOP, newsrc_temp, szNewsSrc) ==IDRETRY );   
						break;
					case ENOENT:
						WVArgMessageBox (NetDoc.hDocWnd, IDS_WINVN_NEWSRC_REN2 , IDS_WINVN_NEWSRC_SAVE4, MB_OK | MB_ICONSTOP, newsrc_temp);
						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  */
						WVArgSetStatbarText(NetDoc.hWndFrame, IDS_STBAR_MAKEPERM_NEWSRC, &NetDoc, TRUE, TRUE, szNewsSrc);
						errcode = CopyFile(newsrc_temp,szNewsSrc,FALSE);
						if (errcode == 0) 
						{
							sprintf (error_string, "%d", errno);
							WVArgXMessageBox (NetDoc.hDocWnd, IDS_WINVN_NEWSRC_REN3, IDS_WINVN_NEWSRC_SAVE5,
							MB_OK | MB_ICONSTOP, newsrc_temp, error_string);
						}
						else
							unlink (newsrc_temp);
						SetStatbarText(NetDoc.hWndFrame,"",&NetDoc, TRUE, TRUE);
						break;
					case 0:
						break;
					
				}
			}
        
		} //while bRetry


    }
	WVGetFileWriteTime(szNewsSrc, ftNewsSrc);

    NewsrcDirty = FALSE;
    GlobalFreePtr(NewsLine);
	return TRUE;
}



/*--- function SetNetDocTitle -------------------------------------------
 *
 */
void  SetNetDocTitle ()
{
  char txt1[20];
  char txt2[20];

  sprintf(txt1, "%lu", NetDoc.TotalLines);
  sprintf(txt2, "%lu", NetDoc.CountedLines);
  WVArgXSetWindowText(NetDoc.hWndFrame, IDS_WINVN_TITLE, txt1, txt2);

  //sprintf (mybuf, "WinVN:  %lu groups; %lu 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 ** 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 = (char) 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_CATCHUP:
            if ( ((GetGroup(*LinePtr))->Selected)
                && ((GetGroup(*LinePtr))->Determined)) 
            {
                TypDoc *MyDoc = NULL;
                TypGroup far *GroupPtr;

                GroupPtr = GetGroup(*LinePtr);
                GroupPtr->HighestPrevSeen = GroupPtr->ServerLast;
                GroupPtr->NumUnread = 0;
                
				if (!GroupPtr->pSeendb)
					GroupPtr->pSeendb = new WVSeenDB( 1, GroupPtr->HighestPrevSeen );
				else
				{
					//destroys bitmap
					GroupPtr->pSeendb->MarkAllSeen( GroupPtr->ServerLast);

					//GroupPtr->pSeendb->MarkSeen(1, GroupPtr->ServerLast);
					//GroupPtr->pSeendb->DestroyBitmap();
				}
				GroupPtr->NumUnread = 0;

                if (MyDoc = (GroupPtr->SubjDoc)) 
                {
                    article_operation (MyDoc, 0, mark_read_all);
                    InvalidateRect(MyDoc->hDocWnd, NULL, FALSE);
                }
            }
            break;

		case GROUP_ACTION_UNREAD:
            if ( ((GetGroup(*LinePtr))->Selected)) 
            {
                TypDoc *MyDoc = NULL;
                TypGroup far *GroupPtr;

                GroupPtr = GetGroup(*LinePtr);

				if ((GetGroup(*LinePtr))->Determined)
				{
					GroupPtr->HighestPrevSeen = GroupPtr->ServerFirst -1;
					if (GroupPtr->pSeendb)
					{
						delete GroupPtr->pSeendb;
						GroupPtr->pSeendb = new WVSeenDB ( GroupPtr->ServerFirst, GroupPtr->ServerLast);
					}

					GroupPtr->NumUnread = CalcNumUnread(GroupPtr);

                }
				else
				{
					GroupPtr->HighestPrevSeen = 0;
					if (GroupPtr->pSeendb)
						delete GroupPtr->pSeendb;
					GroupPtr->NumUnread = CalcNumUnread(GroupPtr);
				}
				if (MyDoc = (GroupPtr->SubjDoc)) 
                {
                    GroupPtr->pSeendb = new WVSeenDB( GroupPtr->uLoadFirst, GroupPtr->uLoadLast );
					//article_operation (MyDoc, 0, mark_unread_all);
                    InvalidateRect(MyDoc->hDocWnd, NULL, FALSE);
                }
				else
				{
					GroupPtr->uLoadFirst = 0;
					GroupPtr->uLoadLast = 0;
				}
            }

			break;

        case GROUP_ACTION_CHECK_ACTIVE:
            if ((GetGroup(*LinePtr))->Subscribed || ShowUnsubscribed) 
            {
                (*LinePtr)->active = TRUE;
                (*BlockPtr)->NumActiveLines++;
                NetDoc.ActiveLines++;
                NetDoc.LongestLine = max (NetDoc.LongestLine, 
                //shimomai        (unsigned)(GetGroup(*LinePtr))->NameLen + GROUP_NAME_OFFSET);
                    (unsigned)(GetGroup(*LinePtr))->NameLen + GroupListSeparator / CharWidth);
                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;
}

void DoHelp(HWND hWnd, UINT uCommand, DWORD dwData)
{
	
	TCHAR szHelpPath[MAXINTERNALLINE];

    TCHAR *pcFileName;
    int nFileNameLen;

    nFileNameLen = GetModuleFileName (hInst, szHelpPath, MAXINTERNALLINE);
    pcFileName = szHelpPath + nFileNameLen;

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

	_tcscat(szHelpPath, LFN_HELP);

	if (g_bIsVista || GetFileAttributes(szHelpPath) == (DWORD) -1)
	{
		_tcscpy(pcFileName, LFN_HTMLHELP);

		switch (uCommand)
		{
			case HELP_INDEX:   //Displays the topic specified by the Contents option in the [OPTIONS] section of the .hpj file.
				HtmlHelp( hWnd, szHelpPath, HH_DISPLAY_TOC, 0 );
				HtmlHelp( hWnd, szHelpPath, HH_DISPLAY_INDEX, 0 );
				break;

			case HELP_WM_HELP: //display the topic for the control identified by hWnd parameter in a popup
				//huh? if ( *(DWORD *)dwData != 0)
				if ( dwData != 0)
				{
					HtmlHelp( hWnd, szHelpPath, HH_TP_HELP_WM_HELP, dwData );
					break;
				}
				else
				{
					HtmlHelp( hWnd, szHelpPath, HH_DISPLAY_TOC, 0 );
					break;
				}
			case HELP_CONTEXT: //Displays the topic identified by the specified context identifier defined in the [MAP] section of the .hpj file
				HtmlHelp( hWnd, szHelpPath, HH_HELP_CONTEXT, dwData );
				break;
			case HELP_QUIT:	   //Inform help we no longer need it
				HtmlHelp(NULL, NULL, HH_CLOSE_ALL, NULL);
				break;
			case HELP_CONTEXTMENU: //Displays the Help menu for the selected window, then displays the topic for the selected control in a pop-up window.
				HtmlHelp(hWnd, szHelpPath, HH_TP_HELP_CONTEXTMENU, dwData);
			default:
				break;
		}

	}
	else
	{
		WinHelp (hWnd, szHelpPath, uCommand, dwData);
	} 
}

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

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

	if (ptChar)
	{
		ptChar->x = -1;
		ptChar->y = -1;
	}

    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;
		
		if (ptChar)
			ptChar->y = SelLine * iLineHeight + TopSpace;

        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);
        }
		int nLastX = -1;
        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;
			}
			nLastX = extent.cx;
        }
		if (ptChar)
			ptChar->x = nLastX + DocPtr->ScXOffset * (iCharWidth + 1) + iSideSpace;

        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"

LRESULT CALLBACK WinVnConfFrameWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    DLGPROC lpProcAbout;
    RECT myRect;                  /* selection rectangle  */
//  static HANDLE ToolBarLib;
    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;
    BOOL bReverse = FALSE;
    UINT ToolbarID = IDB_TOOLBAR;
    int ToolbarHeight = TOOLBARHEIGHT;
    int ToolbarSize = 16;
    BOOL bSkipInit = FALSE;





  if (message == uMSH_MOUSEWHEEL) /* set by registerwindowsmessage in winvn.c but only used un NT 351 and Win95 */
      message = WM_MYMOUSEWHEEL;

  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
    return 0;
    int nCurrent = nToolbarMain;
	if ( (message == WM_SIZE || message == WM_RETHINKTOOLBAR ||message == WM_CREATE) && nCurrent == 3)
	{
		//determine dpi to pick size
		int size = xGetWindowDPI(hWnd);
		if (size <= 96)
			nCurrent = 0;
		else if (size <= 120)
			nCurrent = 1;
		else
			nCurrent = 2;
	}   

    switch (nCurrent)
    {
        
        case 1:
            ToolbarID = IDB_TOOLBARMED;
            ToolbarHeight = TOOLBARHEIGHTMED;
            ToolbarSize = 24;
            break;
        case 2:
            ToolbarID = IDB_TOOLBARBIG;
            ToolbarHeight = TOOLBARHEIGHTBIG;
            ToolbarSize = 32;
            break;
        case 0:
        default:
            ToolbarID = IDB_TOOLBAR;
            ToolbarHeight = TOOLBARHEIGHT;
            ToolbarSize = 16;
            break;
    }

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

  case WM_ACTIVATE:
    if ( wParam == WA_INACTIVE && DragMouseAction != DRAG_NONE) //lost capture (Alt+Tab'd?)
        DragMouseAction = DRAG_NONE;
    return (DefWindowProc (hWnd, message, wParam, lParam));
    break;

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

  case WM_SYSCOMMAND:
    switch (wParam) 
    {
        case ID_ABOUT:
            lpProcAbout = &About;//(DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
            DialogBox (hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, lpProcAbout);
            FreeProcInstance ((FARPROC) lpProcAbout);
            break;

        case IDM_RESTORE_ALL:
            RestoreWindows();
            break;

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

  case WM_RETHINKTOOLBAR:
      if (NetDoc.hWndTB)
        DestroyWindow(NetDoc.hWndTB);
      bSkipInit = TRUE;
      PostMessage(hWnd, WM_SIZE, 0 ,0);
      //fall through
  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,    
                                    ToolbarID,        
                                    i + 1,      
                                    hInst,      
                                    ToolbarID,        
                                    tbButton,   
                                    j + 1,      
                                    ToolbarSize,         
                                    ToolbarSize,         
                                    ToolbarSize,         
                                    ToolbarSize,         
                                    sizeof(TBBUTTON));  
#else

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

    assert (NetDoc.hWndTB != NULL);
    if (bSkipInit)
        break;
#ifdef COMMCTRL
	
     NetDoc.hDocWnd = CreateWindowEx (WS_EX_CLIENTEDGE,
                                    "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);
#else
    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);
#endif

    assert (NetDoc.hDocWnd != NULL);

	{
		g_hPalette = NULL;
		HDC hDC = GetDC(NetDoc.hDocWnd);
		if (hDC)
		{
			int nBits = GetDeviceCaps(hDC, BITSPIXEL);
			if (nBits == 8)
			{
				g_hPalette = CreateHalftonePalette(hDC);
				SelectPalette(hDC, g_hPalette, FALSE);
				RealizePalette(hDC);
			}
			ReleaseDC(NetDoc.hDocWnd, hDC); 
		}
	}

    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();
    delete g_pAction;
    g_pAction = new WVArticleAction((const char*)NULL);
    g_pAction->ReadActions();
    /* Get the global additional headers */
    delete g_pAddHdr;
    g_pAddHdr = new WVAddHdr((const char*)NULL);
    g_pAddHdr->ReadAddHdr();

#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);
    EnableMenuItem (hSubMenu, IDM_CATCHUP, NetDoc.SelectedLines && did_list ? ENABLE_MENU: DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_MARK_ALL_UNSEEN, NetDoc.SelectedLines ? ENABLE_MENU: DISABLE_MENU);

    
    EnableMenuItem (hSubMenu, IDM_DECODE_SELECTED, NetDoc.SelectedLines ? ENABLE_MENU: DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_SAVE_SELECTED, NetDoc.SelectedLines ? ENABLE_MENU: DISABLE_MENU);

    {
      register int i;
      BOOL bEnable = FALSE;
      for (i = 0; AutoDecode == 0 && i < MAXGROUPWNDS; i++)
        if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd) 
        {
            if ( GroupDocs[i].SelectedLines > 0 )
            {
                bEnable = TRUE;
                break;
            }

        }
        EnableMenuItem(hSubMenu, IDM_DECODE_SELECTED,  bEnable ?  ENABLE_MENU: DISABLE_MENU);
        EnableMenuItem(hSubMenu, IDM_SAVE_SELECTED, bEnable ?  ENABLE_MENU: DISABLE_MENU);
        EnableMenuItem(hSubMenu, IDM_SAVE_SELECTED_ARTICLE_NZB, bEnable ?  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, 4), IDM_SEND_ALL_POST,
                    NumPostWnds ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (GetSubMenu (hSubMenu, 4), IDM_SEND_ALL_MAIL,
                    NumMailWnds ? ENABLE_MENU : DISABLE_MENU);

#ifdef MULTIGROUP
	//EnableMenuItem(hMenu, IDM_ARTICLE, ENABLE_MENU);
#else 
	EnableMenuItem(hMenu, IDM_ARTICLE, DISABLE_MENU); 
#endif

    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_NCHITTEST:
		{
			
			LRESULT ht = DefWindowProc(hWnd, message, wParam, lParam); 
			if( ht == HTCLIENT ) 
			{ 
				POINT pt;
				RECT rc;
				pt.x = GET_X_LPARAM(lParam);  // horizontal position of cursor 
				pt.y = GET_Y_LPARAM(lParam);  // vertical position of cursor

				GetWindowRect(hWnd, &rc ); 
				rc.left = rc.right - GetSystemMetrics(SM_CXHSCROLL); 
				rc.top = rc.bottom - GetSystemMetrics(SM_CYVSCROLL); 
				if( PtInRect( &rc, pt ) ) 
					ht = HTBOTTOMRIGHT; 
			}
			return ht; 
			
		}
		break;

  case WM_CLOSE:
    if (Initializing == INIT_ESTAB_CONN) 
    {
        break;      /* can't quit if in process of connecting (will GPF) */
    }
	if (ConfirmExitActive)
	{

		for (i = 0 ; i < MAXGROUPWNDS ; i++) 
		{
		  if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd && (!CommBusy || &GroupDocs[i] != CommDoc)) 
		  {
			if (GroupDocs[i].SelectedLines)
			{
				if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_EXITACTIVE, IDS_WINVN_CONFIRM1,
								  MB_YESNO | MB_ICONQUESTION) == IDNO)
				{
					//TODO: set focus/show to active newsgroup
					return 0;
				}
				break;
			}

		  }
		}
		if (AreComposeWndsDirty())
			return 0;
	
	}
    if (!ConfirmSaveOnExit) 
    {
      // SaveNewsrc = SaveConfig = TRUE;  //MDS set when we start now
    }
    else 
    {
      /* WinVnExitDlg sets SaveNewsrc and SaveConfig */
      if (!DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNEXIT), hWnd, lpfnWinVnExitDlg)) 
      {
         if (NetDoc.hDocWnd)
         {
            InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
         }
        break;
      }
    }
	if (AutoDeleteList)
		CloseMonitoredFiles();


    MailClose (hWnd);

    for (i = 0 ; i < MAXGROUPWNDS ; i++) 
    {
      if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd && (!CommBusy || &GroupDocs[i] != CommDoc)) 
	  {
        UpdateSeenArts(&GroupDocs[i]);
      }
    }
  Jmp_ExitApp:;
    SetHandleBkBrush (NetDoc.hDocWnd, (HBRUSH) GetStockObject (WHITE_BRUSH));    

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

    delete g_pAction;
    g_pAction = NULL;
    delete g_pAddHdr;
    g_pAddHdr = NULL;
    Disconnect ();
       
    /* Remove resources before exiting program */
    FreeTextBlock (Signature);
    if (MailList)
      FreeTextBlock (MailList);

    if (ExtMapSourceList)
        FreeTextBlock(ExtMapSourceList);
    if (ExtMapDosList)
        FreeTextBlock(ExtMapDosList);

    DestroyFonts ();
    DestroyBitmaps ();

    if (hListBackgroundBrush)
      DeleteObject (hListBackgroundBrush);
    if (hArticleBackgroundBrush)
      DeleteObject (hArticleBackgroundBrush);
    if (hStatusBackgroundBrush)
      DeleteObject (hStatusBackgroundBrush);
//  if (StatbarPntData.hFontStatbar)
//    DeleteObject (StatbarPntData.hFontStatbar);
    if (g_hPalette)
	{
		DeleteObject(g_hPalette);
		g_hPalette = NULL;
	}

    
    KillTimer (NetDoc.hDocWnd, ID_TIMER);
    DestroyWindow(NetDoc.hWndTB);
    // jn 2004 try mem leak fix
    FreeDoc(&NetDoc);
    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);
	if (g_hPalette)
	{
		SelectPalette(hDC, g_hPalette, FALSE);
		RealizePalette(hDC);
	}
    PaintStatbar (hWnd, hDC, &NetDoc);
    EndPaint (hWnd, &ps);
    if (GetFocus () == hWnd)
      SetFocus (NetDoc.hDocWnd);
    return (DefWindowProc (hWnd, message, wParam, lParam));
    break;


  case WM_MYMOUSEWHEEL:
  case WM_MOUSEWHEEL:
  case WM_KEYDOWN:
    SendMessage (NetDoc.hDocWnd, message, wParam, lParam);
    break;
  case  WM_HELP:
	{
		LPHELPINFO lphi = (LPHELPINFO) lParam; 
		if (lphi->iContextType == HELPINFO_WINDOW && lphi->iCtrlId != 9 )
		{
			DWORD dwHelp[4] = {	lphi->iCtrlId, lphi->iCtrlId + 0x10000, 0,0};
			//DoHelp ((HWND)lphi->hItemHandle/*NetDoc.hDocWnd*/,  HELP_WM_HELP, (DWORD)(LPVOID)dwHelp);
			DoHelp ((HWND)lphi->hItemHandle/*NetDoc.hDocWnd*/,  HELP_INDEX, 0);
		}
	}
	  break;
	case WM_CONTEXTMENU:
	{
		HWND hwnd = (HWND) wParam;

		if (hwnd == hWnd)
			return 1;

		/*
		LPHELPINFO lphi = (LPHELPINFO) lParam; 
		MakeHelpPathName (mybuf, MAXINTERNALLINE);
		DWORD dwHelp[4] = {	lphi->iCtrlId, lphi->iCtrlId + 0x10000, 0,0};

		DoHelp (NetDoc.hDocWnd,
			HELP_CONTEXTMENU,
			(DWORD)(LPVOID)dwHelp);
		*/
		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;
      goto Jmp_ExitApp;
      //return (0);
      //break;
      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 */
      goto Jmp_ExitApp;
      //SetHandleBkBrush (NetDoc.hDocWnd, (HBRUSH) GetStockObject (WHITE_BRUSH));
      //DestroyWindow (hWnd);
      //return (0);
      //break;
      break;

    case IDB_TOGGLE_CONNECT:
      if (Initializing == INIT_NOT_CONNECTED && Reconnecting != 1) 
      {
        SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
        Connect ();
        break;
      }
      /* else fall into disconnect */
      SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, FALSE);
      goto disconnected;

    case IDM_DISCONNECTED: //unexpected disconnection from server
    {
        TypDoc * SaveDoc = CommDoc;
		TypDoc * pGroup = NULL;
		unsigned long artindex = 0;
		BOOL bSaving = FALSE;
		BOOL bWasDecoding = CommDecoding;
		BOOL bArticle = FALSE;
		

		if ( SaveDoc && SaveDoc->ParentDoc)
		{
			if (bWasDecoding)
				pGroup = SaveDoc;
			else
				pGroup = SaveDoc->ParentDoc;
			 

			artindex = pGroup->curSaveArtIndex;
			bSaving= pGroup && pGroup->savingArtIndex >= 0;  
		}

		if (bWasDecoding && Autoconnect)
		{
			  currentCoded->numBytes = 0;
			  currentCoded->numLines = 0;
			  currentCoded->endFlag = currentCoded->beginFlag = FALSE;
			  currentCoded->nMimeState = 0;
			  currentCoded->nAppType = 0;
			  currentCoded->nTotal = -1;
			  currentCoded->dwCRC = 0xFFFFFFFFL;
			  currentCoded->dwExpectedBytes = 0;
			  currentCoded->dwPartSize = 0;
			  DecodeInit(TRUE);

		}
		if (CommState == ST_ARTICLE_RESP)
		{
			//we've just requested article, but then we noticed we're disconnected
			bArticle = TRUE;
			artindex = CommDoc->LastSeenLineID;
		}
		if (CommState == ST_LIST_GROUPLINE)
		{
			//how rude! We were getting the groups list and got disconnected!
			CommState = ST_NONE;
			ProcEndList ();
			CommBusy = FALSE;
		}	
		Reconnecting = 0;
		AbortAllComm (Autoconnect);
		KillTimer (NetDoc.hDocWnd, ID_TIMER);
        Initializing = INIT_NOT_CONNECTED;
		SetStatbarText (NetDoc.hWndFrame, "--Disconnected--", &NetDoc, TRUE, TRUE);

        if (Autoconnect)
		{
			Reconnecting = 1;
			
			if (bSaving || bArticle)
			{
#ifdef _DEBUG
				MessageBeep( (UINT) -1);
#endif				
				if (SaveDoc)
					SetStatbarText (SaveDoc->hWndFrame, "Reconnecting...", &NetDoc, TRUE, FALSE);
				if (InlineReconnect(hWnd))
				{
					CommDoc = SaveDoc;
					if (SaveDoc)
						SetStatbarText (SaveDoc->hWndFrame, "Connected", &NetDoc, TRUE, FALSE);
					if (bWasDecoding)
					{
						
						CodingState = DECODE_SKIPPING;
						CommDecoding = TRUE;
						//if (currentDecodeThread)
						//	UpdateThreadStatus (currentDecodeThread, "Restarting download from server");

					}


					SendMessage (pGroup->hWndFrame, (UINT) WM_COMMAND,
						(WPARAM) ID_ARTICLE_RETRIEVE_RESTART, (LPARAM) bArticle ? -1 : artindex);

				}
				else
				{
					//really end
					AbortAllComm (FALSE);
					SetConnectMenuItems (&NetDoc, ENABLE);
				}
			}


			return 1;
		} 




			


        
		HideShowWindows(SW_HIDE);
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);


		SetFocus(NetDoc.hDocWnd);
	
        if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_RECONNECT, IDS_WINVN_CONFIRM1,
                          MB_YESNO | MB_ICONQUESTION) == IDYES)
        {
            Reconnecting = 1;
            Connect (); 
            break;  
        }


      Disconnect ();
      //Disconnect ();
      //complain (-1,"MRRReadComm","NNTP server has closed the connection");
        
    }
    //fall through
    case IDM_DISCONNECT:
disconnected:
      /* If the NNTP server disconnected, then Initializing == INIT_NOT_CONNECTED */
      if ((Initializing != INIT_NOT_CONNECTED && ConfirmDisconnect &&
           (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_DISCONNECT, IDS_WINVN_CONFIRM1,
                          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:   
      WVMessageBox (hWnd, IDS_WINVN_NOTIMPLEMENTED, IDS_WINVN_SORRY , MB_OK);
      break;

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

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

    case IDM_CATCHUP:
      ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_CATCHUP, 0);
      InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
      NewsrcDirty = TRUE;
      break;
	case IDM_MARK_ALL_UNSEEN:
      ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_UNREAD, 0);
      InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
      NewsrcDirty = TRUE;
      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 ();
      if (SortGroupList) 
	  {
        ShellSort (NewGroupTable,nNewGroups, (size_t) sizeof (void far *),
                         (int (*)(const void huge *,const void huge *)) GroupCompare);
      }
                 
// #ifdef _WIN32
//                 (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 ();
	  {
		TypBlock *BlockPtr;
		TypLine  *LinePtr;

		TypLineID nID = PositionFirstSelected(&BlockPtr, &LinePtr);

		ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SELECTED, TRUE);
		BuildPtrList ();
		ShellSort (NewGroupTable, nNewGroups, (size_t) sizeof (void far *),
			 (int (*)(const void *,const void *)) GroupCompare);
//#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, nID);
		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_DECODE_SELECTED:
      /* Query user for file name */

      if (!DialogBoxParam (hInst, MAKEINTRESOURCE(IDD_WINVNDECODEARTS), hWnd, lpfnWinVnDecodeArtsDlg, 1)) 
      {
        InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
        break;
      }
      else 
      {
        if (TestCommBusy (hWnd, IDS_TEST_DECODE_ARTICLES) ||
            TestCodingBusy (hWnd, IDS_TEST_DECODE_ARTICLES))
          break;
      }
      AutoDecode = 1;
      AbortDownload = 0;

    case IDM_SAVE_SELECTED:
        /* Query user for file name */
        if (!AutoDecode)
        {
            if (!DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNSAVEARTS), hWnd, lpfnWinVnSaveArtsDlg)) 
            {
                InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
                break;
            }
            else 
            {
                if (TestCommBusy (hWnd, IDS_TEST_DECODE_ARTICLES) ||
                    TestCodingBusy (hWnd, IDS_TEST_DECODE_ARTICLES))
                  break;                
                
                

            }
            AutoDecode = 2;
            AbortDownload = 0;
        }
        for (i = 0; i < MAXGROUPWNDS; i++)
            if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd) 
            {
                if ( GroupDocs[i].SelectedLines > 0 )
                {
                    PostMessage(GroupDocs[i].hWndFrame, AutoDecode == 1 ? WM_MULTIDECODE : WM_MULTISAVE, 0, 0);
                    break;
                }

            }
        
        break;

	case IDM_SAVE_SELECTED_ARTICLE_NZB:

		{
			TCHAR szfile[_MAX_PATH];
			HANDLE hWriteFile = INVALID_HANDLE_VALUE;
			OPENFILENAME ofn;
			long nAffected = 0;
			*szfile = '\0';

			if (!*DecodePathName)
				GetPrivateProfileString (MRUENTRIES, "file01", "", DecodePathName, MAXFILENAME, szAppProFile);

			memset (&ofn, 0, sizeof (OPENFILENAME));
			ofn.lStructSize = sizeof(OPENFILENAME);
			ofn.lpstrTitle = "Export to NZB";
			ofn.lStructSize = sizeof (OPENFILENAME);
			ofn.hwndOwner = hWnd;
			ofn.lpstrFilter = "All Files (*.*)\0*.*\0NZB files (*.nzb)\0*.nzb\0\0";
			ofn.nFilterIndex = 2;
			ofn.lpstrFile = szfile;
			ofn.nMaxFile = _MAX_PATH;
			ofn.lpstrInitialDir = DecodePathName;
			ofn.lpstrDefExt = ".nzb";

			ofn.Flags =  OFN_EXPLORER | OFN_NOREADONLYRETURN | OFN_NOCHANGEDIR;

			if (GetSaveFileName (&ofn) )
			{	
				
				BOOL bOK = TRUE;
				int nOpen = CREATE_NEW;

				strncpy(DecodePathName, szfile, MAXFILENAME);
				char *p = strrchr( DecodePathName, '\\');
				if (p)
					*p = '\0';
				if (GetFileAttributes( szfile ) != (DWORD) -1)
				{
					
					
					UINT nRet = DialogBox (hInst, MAKEINTRESOURCE(IDD_APPENDDLG), hWnd, lpfnWinVnAppendDlg);

					if (nRet == IDCANCEL)
						break;
					if (nRet == IDOK)
						nOpen = OPEN_EXISTING;
					else if (nRet == IDIGNORE)
					{
						nOpen = CREATE_ALWAYS;
						DeleteFile(	szfile);
					}
				}
				

				
				hWriteFile = CreateFile(szfile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, 
							nOpen, FILE_ATTRIBUTE_NORMAL, NULL);
				
				if (hWriteFile != INVALID_HANDLE_VALUE )
				{			
				
					
					for (i = 0; i < MAXGROUPWNDS; i++)
						if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd) 
						{
							if ( GroupDocs[i].SelectedLines > 0 )
							{
                    
								nAffected +=  SaveArtNZB( &GroupDocs[i], hWriteFile, TRUE, nOpen == OPEN_EXISTING);
								nOpen = OPEN_EXISTING;
							}

						}
					
					
					
					
					CloseHandle(hWriteFile);
					bOK = nAffected >= 0;

				}
				else
				{
					bOK = FALSE;
				}
				if (!bOK)
				{
					
					LPTSTR   message; 

					FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
								   FORMAT_MESSAGE_FROM_SYSTEM, 
								   NULL, 
								   GetLastError(), 
								   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
								   (LPTSTR) &message, 
								   0, 
								   NULL); 
					
					WVArgMessageBox(hWnd, IDS_WVGROUP_SAVE_ERROR, IDS_WVGROUP_SAVE_FAILED, MB_OK | MB_ICONEXCLAMATION, message);
					LocalFree (message);
				}
				else
				{
					WVArgXMessageBox(hWnd,  IDS_WVGROUP_SAVE_OK, IDS_WVGROUP_SAVE_TITLE, MB_OK | MB_ICONINFORMATION, nAffected, szfile);

				}

			}
		}




		break;



    case IDM_COMMOPTIONS:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNCOMM), hWnd, lpfnWinVnCommDlg);

      break;

    case IDM_CONFIG_PERSONAL:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNPERSONAL), hWnd, lpfnWinVnPersonalInfoDlg);
      break;
    
    case IDM_CONFIG_EXECUTE:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNEXECUTE), hWnd, lpfnWinVnExecuteInfoDlg);
      break;
        
    case IDM_CONFIG_CONFIRMATIONS:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNCONFIRMATIONS), hWnd, lpfnWinVnConfirmationDlg);
      break;

    case IDM_CONFIG_LOG:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNLOGOPTS), hWnd, lpfnWinVnLogOptDlg);
      break;

    case IDM_CONFIG_SMART_FILER:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNSMARTFILER), NetDoc.hDocWnd, lpfnWinVnSmartFilerDlg);
      break;

    case IDM_CONFIG_ARTLIST:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNARTICLELISTPREFS), hWnd, lpfnWinVnArtListDlg);
      break;

    case IDM_CONFIG_ARTICLE:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNARTICLEPREFS), hWnd, lpfnWinVnArticleDlg);
      break;

    case IDM_CONFIG_GROUPLIST:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNGROUPLISTPREFS), hWnd, lpfnWinVnGroupListDlg);
      break;

    case IDM_COMPOSE_PREFS:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNCOMPOSEPREFS), hWnd, lpfnWinVnComposePrefsDlg);
      break;

    case IDM_ATTACH_PREFS:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNATTACHPREFS), hWnd, lpfnWinVnAttachPrefsDlg);
      break;

    case IDM_CONFIG_CODING:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNCODINGPREFS), hWnd, lpfnWinVnCodingPrefsDlg);
      break;

    case IDM_FONTS:
		WinVNFontChange = 0;
		if (!AskForFont(NetDoc.hDocWnd, NULL, NULL, NULL))
		{
			if (WinVNFontChange & FONT_MASK_SYSTEM)
			{
				//Winvn system
				InitWinVnFonts ();
				InitStatBar (&StatbarPntData);
				SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
				InvalidateRect (NetDoc.hWndFrame, NULL, TRUE);
				InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
				RefreshArticleWnds ();
				RefreshGroupWnds (0);
				RefreshComposeWnds ();
			}


			if (WinVNFontChange & FONT_MASK_COMPOSITION)
			{
				//Composition fonts
				InitCompositionFonts ();
				RefreshComposeWnds ();
			}

			if (WinVNFontChange & FONT_MASK_LIST)
			{
				//Grouplist
				InitListFonts ();
				SendMessage (NetDoc.hWndFrame, WM_SIZE, 0, 0L);
				InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
				SendMessage (NetDoc.hDocWnd, WM_SIZE, 0, 0L);
				RefreshGroupWnds (0);
			}

			if (WinVNFontChange & FONT_MASK_ART)
			{
				//Article text
				InitArticleFonts ();
				RefreshArticleWnds ();
				RefreshComposeWnds ();
			}

			if (WinVNFontChange & FONT_MASK_STATUS)
			{
				//Status fonts
				InitStatusFonts ();
				if(STATUSWIDTH > xScreen) 
				{
					WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_STATUSFONT, IDS_WINVN_FONTTOOBIG, MB_OK | MB_ICONSTOP);
					goto askStatusFont;
				}
				RefreshStatusWnds ();
			}

			if (WinVNFontChange & FONT_MASK_PRINT)
			{
				//Printer fonts
				InitPrintFonts ();
			}

		}
		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 (0);
		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 (0);
		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) 
		{
			WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_STATUSFONT, IDS_WINVN_FONTTOOBIG, 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 (0);
      break;

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

	case IDM_COLORS:
	{
		HBRUSH hOldBr;
		BOOL bOldInverse = UseInverseSelections;


		AskForColor (NetDoc.hDocWnd, NULL);
		hOldBr = hListBackgroundBrush;
		hListBackgroundBrush = CreateSolidBrush (ListBackgroundColor);
		SetHandleBkBrush (NetDoc.hDocWnd, hListBackgroundBrush);
		RefreshGroupWnds (0);
		DeleteObject (hOldBr);

		hOldBr = hArticleBackgroundBrush;
		hArticleBackgroundBrush = CreateSolidBrush (ArticleBackgroundColor);
		RefreshArticleWnds ();
		DeleteObject (hOldBr);

		hOldBr = hStatusBackgroundBrush;
		hStatusBackgroundBrush = CreateSolidBrush (StatusBackgroundColor);
		RefreshStatusWnds ();
		DeleteObject (hOldBr);

		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		if (bOldInverse != UseInverseSelections)
			RefreshGroupWnds (0);

   }
		break;

    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 (0);
      break;

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

    case IDM_COLOR_KILLED:
      if (AskForColor (NetDoc.hDocWnd, &ArticleKilledColor))
        break;
      WarnColors (ArticleKilledColor, ListBackgroundColor, "List Background");
      RefreshGroupWnds (0);
      break;

    case IDM_COLOR_ERROR:
      if (AskForColor (NetDoc.hDocWnd, &ArticleErrorColor))
        break;
      WarnColors (ArticleErrorColor, ListBackgroundColor, "List Background");
      RefreshGroupWnds (0);
      break;

	case IDM_COLOR_PREVIEW:
      if (AskForColor (NetDoc.hDocWnd, &ArticlePreviewColor))
        break;
      WarnColors (ArticlePreviewColor, ListBackgroundColor, "List Background");
      RefreshGroupWnds (0);
      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);

      //yes the following code is set to use || so you only get warned ONCE even if
      //multiple duplicates are detected
      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 (0);
      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 (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CLOSE_WINDOW, IDS_WINVN_CONFIRM2,
                          MB_YESNO | MB_ICONQUESTION) == IDNO)
          break;
      CloseWindows ();
      break;

    case IDM_CLOSE_ALL_ARTICLE:
      if (ConfirmBatchOps)
        if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CLOSE_ARTICLE, IDS_WINVN_CONFIRM3,
                          MB_YESNO | MB_ICONQUESTION) == IDNO)
          break;
      CloseArticleWnds ();
      break;

    case IDM_CLOSE_ALL_GROUP:
      if (ConfirmBatchOps)
        if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CLOSE_GROUP, IDS_WINVN_CONFIRM4,
                          MB_YESNO | MB_ICONQUESTION) == IDNO)
          break;
      CloseGroupWnds ();
      break;

    case IDM_CLOSE_ALL_COMPOSE:
      if (ConfirmBatchOps)
        if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CLOSE_COMPOSE, IDS_WINVN_CONFIRM5,
                          MB_YESNO | MB_ICONQUESTION) == IDNO)
          break;
      CloseComposeWnds ();
      break;

    case IDM_CLOSE_ALL_STATUS:
      if (ConfirmBatchOps)
        if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CLOSE_STATUS, IDS_WINVN_CONFIRM6,
                          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 (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CLOSE_POST, IDS_WINVN_CONFIRM7, MB_YESNO | MB_ICONQUESTION) == IDNO)
          break;
    case IDM_SEND_NEXT_POST:  
      BatchSend (DOCTYPE_POSTING);
      break;

    case IDM_SEND_ALL_MAIL:
      if (ConfirmBatchOps)
        if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CLOSE_MAIL, IDS_WINVN_CONFIRM8, MB_YESNO | MB_ICONQUESTION) == IDNO)
          break;
    case IDM_SEND_NEXT_MAIL:  
      BatchSend (DOCTYPE_MAIL);
      break;

    case IDM_DECODE_FILE:
      if (TestCodingBusy (NetDoc.hDocWnd, IDS_TEST_DECODE_FILE))
        break;

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

    case IDM_ENCODE_FILE:
      {
        TypAttachment attach;
		ZeroMemory(&attach, sizeof(TypAttachment));
        if (TestCodingBusy (NetDoc.hDocWnd, IDS_TEST_ENCODE_FILE))
          break;
        if (!DialogBoxParam (hInst, MAKEINTRESOURCE(IDD_WINVNENCODE), NetDoc.hDocWnd, &WinVnEncodeDlg, (LPARAM) & attach))
          InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
        else
          EncodeToFile (NetDoc.hDocWnd, &attach);
      }
      break;

    case IDM_RESET:
		if (!CommBusy || SendingMail || SendingPost) 
		{
			if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_RESET1, IDS_WINVN_CONFIRM9, MB_YESNO | MB_ICONQUESTION) != IDYES) 
				break;
		} 
		else 
		{  
			if (WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_RESET2, IDS_WINVN_CONFIRM10, MB_YESNO | MB_ICONQUESTION) != IDYES) 
				break;

		}
		{
			TypDoc * SaveDoc = CommDoc;
			hMenu = GetMenu (hWnd);
			hSubMenu = GetSubMenu (hMenu, 1);
			EnableMenuItem (hSubMenu, IDM_CONNECT, ENABLE_MENU);
			SendMessage (NetDoc.hWndTB, TB_ENABLEBUTTON, IDB_TOGGLE_CONNECT, TRUE);   // JD 3/8/97

			DropConnection();
			if (InlineReconnect(hWnd))
			{
				CommDoc = SaveDoc;
			}
		}

      break;

    case IDM_RECONNECT:
      if ((Initializing != INIT_NOT_CONNECTED && Initializing != INIT_ESTAB_CONN) &&
          (!ConfirmDisconnect ||
			WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_DISCONNECT, IDS_WINVN_CONFIRM1,
                         MB_YESNO | MB_ICONQUESTION) == IDYES) ) 
        {
            Reconnect();
        }
        break;
        
    case IDM_SAVE_CONFIG:
      WriteWinvnProfile ();
      WVMessageBox (NetDoc.hDocWnd, IDS_WINVN_CONFIGSAVED, IDS_WINVN_CONFIG2, 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, MAKEINTRESOURCE(IDD_WINVNFIND), hWnd, lpfnWinVnFindDlg)) 
      {
        found = DoFind (TRUE, GroupListMultiSelect, FALSE);
        if (!found) 
        {
            WVArgMessageBox(hWnd, IDS_WINVN_ARGNOTFOUND, IDS_WINVN_NOT_FOUND, MB_OK, NetDoc.SearchStr);
        }
        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_PREV_SAME:
      bReverse = TRUE;
    case IDM_FIND_NEXT_SAME:
      FindDoc = &NetDoc;
      continueFind = TRUE;
      if (!FindDoc->SearchStr[0]) 
      {
        if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0])
          strcpy (FindDoc->SearchStr, LastGroupNameFind);
        continueFind = (BOOL) DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNFIND), hWnd, lpfnWinVnFindDlg);
      }

      if (continueFind && FindDoc->SearchStr[0]) 
      {
        found = DoFind (!FindDoc->hFindBlock && !FindDoc->FindLineID, GroupListMultiSelect, bReverse);
        if (!found) 
        {
          WVArgMessageBox (hWnd, IDS_WINVN_OCCNOTFOUND, IDS_WINVN_OCCURENCE, MB_OK, NetDoc.SearchStr);
        }
        else 
        {
          if (LineIDtoLinePtr (FindDoc->FindLineID, FindDoc, &BlockPtr, &LinePtr)) 
          {
            if (bReverse)
              FindDoc->AnchorLineID = LinePtr->LineID;
            SetSelections (FindDoc, LinePtr);
            if (GroupListMultiSelect) 
            {
              /* set the active and anchor line IDs = FindLineID */
              FindDoc->AnchorLineID = FindDoc->FindLineID;
            }
          }
        }
      }
      break;

    case ID_ABOUT:
      lpProcAbout = &About;//(DLGPROC) MakeProcInstance ((FARPROC) About, hInst);
      DialogBox (hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, lpProcAbout);
      FreeProcInstance ((FARPROC) lpProcAbout);
      break;

    case IDM_HELP:
      DoHelp (NetDoc.hDocWnd, HELP_INDEX, 0L);
	  return 1L;
      break;

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

    case IDM_LOGOUT:
      (MailCtrl.fnMlLogout) (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;

	case IDM_ARTICLE: //retrieve article from Message ID
	 
	  FindDoc = &NetDoc;
      

      if (DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNFIND), hWnd, lpfnWinVnFindDlg)) 
	  {
		char mesg[MAXHEADERLINE];

		strcpy (LastMIDFind, FindDoc->SearchStr);
		
             if (!TestArgCommBusy(hWnd, IDS_TEST_AVAILABLE, str) && (CommState==ST_IN_GROUP || CommState==ST_NONE) 
                && CommDoc) 
			 { //MDS NULL if artlist closed!
                IsArticleAvailable = FALSE;
                AsynMsg.hwnd = hWnd;
                AsynMsg.uMsg = WM_COMMAND;
                AsynMsg.wParam = (WPARAM) ID_STAT_CHECK_COMPLETE;
                AsynMsg.lParam = (LPARAM) 0L;
                strncpy(TestArticleId,str,MAXHEADERLINE);
                sprintf(mesg,"STAT %s",LastMIDFind); // Execute STAT msg-id command
                CommBusy = TRUE;
                PutCommLine(mesg);
                CommState = ST_IS_ARTICLE_ON_SERVER;
                ExecAsynMessage = TRUE;
             }


	  }
		break;
    
	case ID_STAT_CHECK_COMPLETE:
		//
		 if ((CommBusy) || (CommState == ST_IS_ARTICLE_ON_SERVER)) 
		 {
			ExecAsynMessage = TRUE;
			break;
		 }
		 if (IsArticleAvailable) 
		 {




#ifdef MULTIGROUP
			TypGroup far *MyGroup =NULL;
			TypBlock far *BlockPtr;
			TypLine *LinePtr;
			unsigned int Offset;
			TypLineID MyLineID;			

			if (!MultiGroupDoc)
			{
				if (!FindGroup("MULTIGROUP", &NetDoc, &BlockPtr, &LinePtr))
					return 0L;

				MyGroup = GetGroup(LinePtr);

				

				//ViewArticle (ThisDoc->ParentDoc, ThisDoc->LastSeenLineID, REUSE, SHOW, TestArticleId);

				
				
				if (!ActiveGroupDoc)
				for (j = MAXGROUPWNDS - 1; j >= 0; j--) 
				{
				  if (GroupDocs[j].InUse) 
				  {
					ActiveGroupDoc = &(GroupDocs[j]);
					break;
				  }
				}

				/*LockLine (ActiveGroupDoc->ParentDoc->hParentBlock,
						ActiveGroupDoc->ParentDoc->ParentOffset,
						ActiveGroupDoc->ParentDoc->ParentLineID,
						&BlockPtr, &LinePtr);
				GroupDoc = GetGroup(LinePtr);
				*/
				if (1)//ActiveGroupDoc)
				{
					//SetArticleMenus (ActiveGroupDoc, DISABLE);
					ViewArticle (MultiGroupDoc, -1, REUSE, SHOW, LastMIDFind);
				}
			 
			 UnlockLine (BlockPtr, LinePtr, &(BlockPtr)->hCurBlock,&Offset,&MyLineID);
			}
#endif

		 }
		 return (0);

		break;
	}

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

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

void HideShowWindows(int nShow)
{
    int i;

    for (i = 0; i < MAXGROUPWNDS; i++)
        if (GroupDocs[i].InUse && GroupDocs[i].hWndFrame) 
        {
            
            ShowWindow(GroupDocs[i].hWndFrame, nShow);
        }

    for (i = 0; i < MAXARTICLEWNDS; i++)
        if (ArticleDocs[i].InUse && ArticleDocs[i].hWndFrame) 
        {
            
            ShowWindow(ArticleDocs[i].hWndFrame, nShow);
        }
    for (i = 0; i < MAXPOSTWNDS; i++)
        if (WndPosts[i].hWnd && IsWindow(WndPosts[i].hWnd) ) 
        {
            
            ShowWindow(WndPosts[i].hWnd, nShow);
        }
    for (i = 0; i < MAXMAILWNDS; i++)
        if (WndMails[i].hWnd && IsWindow(WndMails[i].hWnd) ) 
        {
            
            ShowWindow(WndMails[i].hWnd, nShow);
        }

}


/* Last line of WVUSENET.CPP */
