// -*- C++ -*-
/********************************************************************
 *                                                                  *
 *  MODULE    :  WVGROUP.CPP                                        *
 *                                                                  *
 *  PURPOSE   : This file contains the window procedure for the     *
 *              Group viewing window for WinVN.                     *
 *                                                                  *
 *  FUNCTIONS :                                                     *
 *              WinVnViewWndProc()    -  Window Procedure for any   *
 *                                       of WinVN's group windows   *
 *                                       (now a child window window)*
 *                                                                  *
 *              WinVnViewFrameWndProc()- Window Procedure for any   *
 *                                       WinVN group frame window   *
 *                                                                  *
 *              FileLength()          -  Find the size in bytes of  *
 *                                       a file                     *
 *                                                                  *
 *              ViewArticle()         -  Requests and views an      *
 *                                       article from the server    * 
 *                                                                  *
 *              setArticleFocus()     -  Makes an article active    *
 *                                                                  *
 *              UnlinkArtsInGroup()   -  Prepares article window    *
 *                                       for deletion               *
 *                                                                  *
 *              UpdateSeenArts()      -  Save seen info in document *
 *                                                                  *
 *              CursorToTextLine()    -  Locates text in a document *
 *                                       based on cursor position   *
 *                                                                  *
 *              search_headers()      -  Search all headers for a   *
 *                                       given substring            *
 *                                                                  *
 *    string_compare_insensitive()    -  Compare two strings while  *
 *                                       ignoring case sensitivity  *
 *                                                                  *
 *               AffectSelected()     -  Set selected flag based on *
 *                                       search criteria            *
 *                                                                  *
 ********************************************************************/


/*
 * $Id: wvgroup.cpp 1.86 1997/03/18 19:24:33 dumoulin Exp $
 *
 */
#pragma warning( disable : 4786 ) // Turn off map template length warning

#include <windows.h>
#include <windowsx.h>           // for GlobalFreePtr (JSC)
#include <sys/stat.h>
#include "wvglob.h"
#include "winvn.h"
#include "wvnzb.h"
#pragma hdrstop

#include <limits.h>
#include <commctrl.h>
#include "wvclass.h"
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
#include <mmsystem.h>


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

#define WM_DISPLAY_SORTED    WM_USER + 3570
#define WM_AA_ENABLE_DISABLE WM_USER + 3571

/* Ray Dunn:99/10: */
//static void CheckSortMenuItem(HMENU hMenu, int idItem, int CheckState);
//static void UncheckAllSortMenuItems(HMENU hMenu);
static void EnableSortMenuItem(HMENU hMenu, int idItem, int mode);
/* Ray Dunn:99/10:End */

//char_p string_compare_insensitive (char_p a, LPSTR b);
long find_first_selected_art(TypDoc far * Doc);
void DrawDragingCursor(HWND hWnd, long oldPosition, long newPosition, BYTE flag); //ishido

/*--- FUNCTION: WinVnViewWndProc --------------------------------------------
 *
 *    Window procedure for a Group window, which contains the subjects
 *    of the various articles in a newsgroup.
 *    Note that there may be several different Group windows active;
 *    this routine gets called any time anything happens to any of them.
 */

LRESULT CALLBACK WinVnViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

  PAINTSTRUCT ps;               /* paint structure          */

  HDC hDC;                      /* handle to display context */
  RECT clientRect;              /* selection rectangle      */
  TypDoc *ThisDoc = NULL;
  int ih, j;
  unsigned int i;
  long stop;
  TypLineID artindex, il, lastartindex;
  BOOL found;
  BOOL endless;
  int CtrlState;
  TypLineID MyLineID;
  _int16 X, Y;
  TypLine far *LinePtr;
  TypBlock far *BlockPtr;
  TypGroup far *group;
  header_p headers;
  HANDLE header_handle;
  HANDLE thread_handle;
  static long DragCursorPosition = 0;
  static BOOL bSel = FALSE;

  /* We know what *window* is being acted on, but we must find
   * out which *document* is being acted on.  There's a one-to-one
   * relationship between the two, and we find out which document
   * corresponds to this window by scanning the GroupDocs array.
   */

  for (ih = 0, found = FALSE; !found && ih < MAXGROUPWNDS; ih++) 
  {
    if (GroupDocs[ih].hDocWnd == hWnd) 
    {
      found = TRUE;
      ThisDoc = &(GroupDocs[ih]);
    }
  }

  if (!found) 
  {
    ThisDoc = CommDoc;
  }

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

  switch (message) 
  {
  //case WM_DESTROY:
    //break;

  case WM_CREATE:
    WidthCursor = FALSE;
    /* Article List Separator */
    if (ArtListSeparator1 <= 0) 
    {
      ArtListSeparator1 = CharWidth * 8;
      ArtListSeparator2 = CharWidth * 15;
      ArtListSeparator3 = CharWidth * 40;
    }
    
    ThisDoc->TopLineOrd = 0;
    ThisDoc->ScXOffset = 0;

    break;

  case WM_SIZE:
    GetClientRect (hWnd, &clientRect);
    ThisDoc->ScXWidth = clientRect.right;
    ThisDoc->ScYHeight = clientRect.bottom;
    //assume there is always a horiz scrollbar
	if (ThisDoc->LongestLine || g_bIsXP)
		ThisDoc->ScYLines = (RectHeight (clientRect) - TopSpace ) / LineHeight;
	else
		ThisDoc->ScYLines = (RectHeight (clientRect) - TopSpace - GetSystemMetrics(SM_CYHSCROLL)) / LineHeight;
    ThisDoc->ScXChars = (RectWidth (clientRect) - SideSpace) / CharWidth;
    break;

	case WM_HELP:
		DoHelp (hWnd, HELP_CONTEXT, (DWORD)414);
		break;


  case WM_COMMAND:
    return (SendMessage (ThisDoc->hWndFrame, message, wParam, lParam));

  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.
     */
    LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
              ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
    group = GetGroup(LinePtr);

    if (group->header_handle && group->total_headers > 0) 
    {
      header_handle = group->header_handle;
      thread_handle = group->thread_handle;
      headers = lock_headers (header_handle, thread_handle);
    }
    else
      headers = NULL;

    switch (wParam) 
    {
    case VK_F5: // This never gets touched if F5 accelerator is defined
        artindex = find_first_selected_art(ThisDoc);
        if (artindex == -1 )
                break; /* no selected articles to view */
        ThisDoc->ActiveLineID = artindex;
        AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
        goto getarticle1;

    case VK_F6:
      NextWindow (ThisDoc->hDocWnd, ThisDoc->DocType);
      break;

    case VK_DOWN:
      if (headers != NULL) 
      {
        /* move one line down from Active line */
        if (ThisDoc->ActiveLineID >= 0 &&
            ThisDoc->ActiveLineID < group->total_headers - 1) 
        {
          SetGroupSelections (ThisDoc, headers, ThisDoc->ActiveLineID + 1);
          AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
        }
      }
      break;

    case VK_UP:
      if (headers != NULL) 
      {
        /* move one line up from Active line */
        if (ThisDoc->ActiveLineID > 0) 
        {
          SetGroupSelections (ThisDoc, headers, ThisDoc->ActiveLineID - 1);
          AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
        }
      }
      break;

    case VK_HOME:
      if (headers != NULL) 
      {
        SetGroupSelections (ThisDoc, headers, 0);
        AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
      }
      break;

    case VK_END:
      if (headers != NULL) 
      {
        SetGroupSelections (ThisDoc, headers, group->total_headers - 1);
        AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
      }
      break;

    case VK_NEXT:
        case VK_SPACE: 
      if (headers != NULL) 
      {
        if (ThisDoc->ActiveLineID >= 0 &&
            ThisDoc->ActiveLineID < group->total_headers - 1) 
        {
           //MDS PgDn = one page past this top, not one pg past current article
           //otherwise it skips viewing articles since AdjustTopScByDoc centers selection
           //add 1/2 page more to compensate for AdjustTopScByDoc
           stop = min ((long)(ThisDoc->TopLineOrd + ThisDoc->ScYLines + ThisDoc->ScYLines/2),
                       (long) group->total_headers );
          for (il = ThisDoc->ActiveLineID + 1; il < stop; il++)
            SetGroupSelections (ThisDoc, headers, il);
          AdjustTopScByDoc (ThisDoc, stop);
        }
      }
      break;

    case VK_PRIOR:
      if (headers != NULL) 
      {
        if (ThisDoc->ActiveLineID > 0) 
        {
          //stop = max (0, ThisDoc->ActiveLineID - (long) ThisDoc->ScYLines);
          stop = max ((long)0, (long)(ThisDoc->TopLineOrd - ThisDoc->ScYLines + ThisDoc->ScYLines/2));
          for (il = ThisDoc->ActiveLineID - 1; il >= stop; il--)
            SetGroupSelections (ThisDoc, headers, il);
          AdjustTopScByDoc (ThisDoc, stop);
        }
      }
      break;

    case VK_ESCAPE: //ishido
      if (DragMouseAction == DRAG_ON) 
      {
        DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
        ReleaseCapture ();
        DragMouseAction = DRAG_NONE;
        WidthCursor = FALSE;
        SetCursor (hArrowCursor);
        InvalidateRect(hWnd, NULL, FALSE);
      }
      break;
      case VK_BACK:  //toggle back to art wnd  MDS 20004011
      {
        TypDoc *adoc;
        for (j = MAXARTICLEWNDS; j >= 0; j--)
        {
          adoc = &ArticleDocs[j];
          if ((adoc->InUse) && (adoc->ParentDoc) && 
              (adoc->ParentDoc->hWndFrame == ThisDoc->hWndFrame) )
          {
            if (IsMinimized(adoc->hWndFrame))
              ShowWindow(adoc->hWndFrame, SW_RESTORE);
            SetFocus(adoc->hWndFrame);
          }
        }

      }
      break;

#ifdef OFFLINE  // TANAKA Goh
    case VK_DELETE:
      SendMessage(ThisDoc->hWndFrame, WM_COMMAND, IDM_DELETE_SELECTED, 0);
      break;

    case VK_INSERT:
      SendMessage(ThisDoc->hWndFrame, WM_COMMAND, IDM_SENDME_SELECTED, 0);
      break;
#endif

    default:
      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_CHAR:
  {
        //  int X, Y;
        /* If any articles are selected, find the first one and view it.  If none selected,
        * Carriage Return means the same as double-clicking
        * on where the cursor is currently pointing.
        */
        if (wParam == '\r') 
        {
            if (ArtListMultiSelect) 
            {
                artindex = find_first_selected_art(ThisDoc);
                if(artindex == -1)
                {
                    artindex = ThisDoc->ActiveLineID;
                }
                goto getarticle1;
            }
            else 
            {
                artindex = ThisDoc->ActiveLineID;
                goto getarticle1;
                //      GetCursorPos (&ptCursor);
                //      ScreenToClient (hWnd, &ptCursor);
                //      X = ptCursor.x;
                //      Y = ptCursor.y;
                //      goto getarticle;
                //
            }
        }
        else 
        {
        }
    }
    break;

  case WM_RBUTTONDBLCLK:
    /* double right click will mark all up to here as read */

    X = GET_X_LPARAM (lParam);
    Y = GET_Y_LPARAM (lParam);

    artindex = NewCursorToTextLine (X, Y, ThisDoc);
    if (artindex > -1) 
    {
      article_operation (ThisDoc, artindex, mark_read_to_here);
      InvalidateRect (hWnd, NULL, FALSE);
    }

    break;

  case WM_MBUTTONDOWN:
    MiddleButton_Is_Down = TRUE;
    if (GScrollMode == WHEELSCROLL_CLICK) 
    {
        endless = FALSE; /* first loop assuemd as save */
        do {
                GPosScrollByWheel++;
                if ( GPosScrollByWheel >= MAX_WHEEL_ENTRIES ) 
                {
                        GPosScrollByWheel = 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 (GScrollByWheel[GPosScrollByWheel] == 0);
    }
	else
	{
dogroupsel:

		X = GET_X_LPARAM (lParam);
		Y = GET_Y_LPARAM (lParam);

		artindex = NewCursorToTextLine (X, Y, ThisDoc);
		if (artindex > -1 && !IsFlagSet(ThisDoc, PreviewDecode)) 
		{
			ThisDoc->ActiveLineID = artindex;

			HANDLE hBlock;
			unsigned int Offset;
			TypLineID MyLineID;
			TypBlock far *BlockPtr;
			TypLine far *LinePtr;
			TypGroup *GroupDoc;
			HANDLE header_handle;
			HANDLE thread_handle;
			header_p headers;
			header_p header;


			LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
			GroupDoc = GetGroup(LinePtr);
			header_handle = GroupDoc->header_handle;

			if (header_handle)
			{
				thread_handle = GroupDoc->thread_handle;
				headers = lock_headers (header_handle, thread_handle);
				header = header_elt (headers, artindex);

				BOOL bSet = FALSE;
				BOOL bWasSelected = (header->SeenSelected & ART_SELECTED);


				WVFileSortHeaders* pFileList = NULL; 
				if (!GroupDoc->pFile_sort)
				{
					HCURSOR hCursorOld = SetCursor (LoadCursor (NULL, IDC_WAIT));
					SetStatbarText (ThisDoc->hWndFrame, "Building grouped file list...", ThisDoc, TRUE, FALSE);
					GroupDoc->pFile_sort = pFileList = new WVFileSortHeaders();
					pFileList->SortDoc(ThisDoc, 0 );
					SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, FALSE);

					SetCursor(hCursorOld);
				}
				else
				{
					pFileList = (WVFileSortHeaders*) GroupDoc->pFile_sort;
				}


				if (pFileList)
				{

					//create a new Entry
					HEADERENTRY* pEntry = WVFileSortHeaders::ProcessSubject(header->pHD->sSubject, header);
					if (pEntry)
					{


						if (pEntry->nTotal > 0 &&  pEntry->nPart > 0 && pEntry->nTotal != 1 )
						{

							int nTotal = -1;
							int nAffected = pFileList->SelectArticlesBySubject( pEntry, nTotal, ART_PREVIEW, FALSE);
							if (nAffected > 0 || nTotal > 0)
								bSet = TRUE;
							/*
							HEADERLIST::iterator itr;
							itr = pFileList->find(pEntry);
							if (itr == pFileList->end())
								break;
							HEADERENTRY* pListGroup = *itr;
							int nLastEntry = -1;
							int nAffected = 1;
							int nTotal = -1;
							while (	pListGroup)
							{
								if ( (pEntry->HeaderP == pListGroup->HeaderP) || //if this is the item we clicked on
								     (pEntry->nPart != pListGroup->nPart && (nLastEntry < 0 || //if this is the 1st one
									 (pListGroup->nPart >= 0 && nLastEntry != pListGroup->nPart)) ) )
								{
									nLastEntry = pListGroup->nPart;
									
									{
										pListGroup->HeaderP->SeenSelected |= ART_PREVIEW;
										nAffected++;
										bSet = TRUE;
									}

								}
								if (nTotal < pEntry->nTotal)
									nTotal = pEntry->nTotal;


								pListGroup = pListGroup->pDupes;

							}
							
							if (!bSet && nTotal > 1)
								bSet = TRUE;
							*/

							if (bSet)
							{
								InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
								UpdateWindow( ThisDoc->hDocWnd);
								BOOL bNeverMind = FALSE;
								if (nAffected < nTotal && !bWasSelected)
								{
									if (WVArgXMessageBox(hWnd, IDS_WVGROUP_INCOMPLETE,IDS_WVART_INCOMPLETE_TITLE,MB_YESNO | MB_ICONQUESTION, nAffected, nTotal) != IDYES)
									{
										bNeverMind = TRUE;
									}

								} 


								if (bNeverMind)
								{
									pFileList->SelectArticlesBySubject( pEntry, nTotal, -ART_PREVIEW, FALSE);
									/*
									pListGroup = *itr;
									while (	pListGroup)
									{
										pListGroup->HeaderP->SeenSelected &= ~ART_PREVIEW;
										pListGroup = pListGroup->pDupes;
									}
									*/

								}
								else
								{
									LPHEADERENTRY pListGroup = pFileList->FindHeaderSubject(pEntry);
									while (	pListGroup)
									{

										if (pListGroup->HeaderP->SeenSelected & ART_PREVIEW)
										{
											pListGroup->HeaderP->SeenSelected &= ~ART_PREVIEW;
											SelectHeader (ThisDoc, pListGroup->HeaderP, !bWasSelected ? SELECT : DESELECT);
										}
										pListGroup = pListGroup->pDupes;
									}

								}
							}

							//


						}
						else
						{
							SelectHeader (ThisDoc, header, !bWasSelected ? SELECT : DESELECT);
						}
						InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
						free( pEntry);
						pEntry = NULL;

					}
				}


				unlock_headers (header_handle, thread_handle);
				UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);


			} //if (Thisdoc)

		}

	}
    break;
    
  case WM_RBUTTONDOWN:
    /* Single middle click or right click will toggle the seen/unseen status 
       of the article */
    DragMouseAction = DRAG_NONE;
    if (!CommBusy || (CommDoc != ThisDoc || CommDecoding)) 
    {
      BOOL status;
      X = GET_X_LPARAM (lParam);
      Y = GET_Y_LPARAM (lParam);

      artindex = NewCursorToTextLine (X, Y, ThisDoc);

	  /* //testing right-mouse menus MDS
	  X -= SideSpace - ThisDoc->ScXOffset * (CharWidth + 1);

        if (X >= 0 && X < ArtListSeparator1) 
        {
            
			SendMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam); 
			
			HMENU hMenu = CreatePopupMenu();

              if (hMenu)
              {
                POINT pt;
                GetCursorPos(&pt);

                InsertMenu(hMenu, -1, MF_BYPOSITION,  IDM_VIEW_ACTION_REM_USER, "Remove articles from this &User");
                InsertMenu(hMenu, -1, MF_BYPOSITION, IDM_VIEW_ACTION_REM_SUBJ, "Remove articles with this &Subject");
                InsertMenu(hMenu, -1, MF_BYPOSITION, IDM_VIEW_ACTION_REM_THREAD, "Remove this &Thread");
                InsertMenu(hMenu, -1, MF_BYPOSITION, IDM_VIEW_ACTION_RESTORE, "&Restore all hidden articles");
                InsertMenu(hMenu, -1, MF_BYPOSITION, IDM_VIEW_ACTION, "&Filters...");

                //but disable it
                //EnableMenuItem(hMenu, 0, MF_BYPOSITION | MF_GRAYED);


                TrackPopupMenu(hMenu, TPM_LEFTALIGN  , pt.x, pt.y, 0, hWnd, NULL);
                DestroyMenu(hMenu);
                hMenu = NULL;
              }

           
		   
		   break;
        }
	  */


      if (artindex > -1) 
      {
        status = article_operation (ThisDoc, artindex, toggle_read_unread);
        DragMouseAction = status ? SEEN_SELECT : SEEN_DESELECT;
        InvalidateRect (hWnd, NULL, FALSE);
        SetCapture (hWnd);      // release capture on button up
        ThisDoc->AnchorLineID = artindex;
      }
    }
    break;

  case WM_LBUTTONDOWN:
    /*  Clicking the left button on an article name toggles the
     *  selected/not selected status of that group.
     *  Currently selected groups are displayed in reverse video.
     */
	 if (GetKeyState(VK_MENU) < 0)
		goto dogroupsel;

    DragMouseAction = DRAG_NONE;
//shimomai & ishido :-)
    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 (!CommBusy || (CommDoc != ThisDoc || CommDecoding)) 
    {
      BOOL status;
      X = GET_X_LPARAM (lParam);
      Y = GET_Y_LPARAM (lParam);

      // make this behave more like a multi-select listbox (jlg)
      // if click,       make the line the only one selected
      // if ctrl-click,  add the line to the selection
      // if shift-click, select from previously selected to current
      if (ArtListMultiSelect) 
      {
        if (!(wParam & MK_CONTROL) && !(wParam & MK_SHIFT)) 
        {
          if (ThisDoc->SelectedLines == 1) 
          {
            article_operation (ThisDoc, ThisDoc->ActiveLineID, selected_false);
          }
          else 
          {
            AffectSelected (ThisDoc, DESELECT, NO_COMPARE);
          }
        }
      }

      artindex = NewCursorToTextLine (X, Y, ThisDoc);
      if (artindex > -1) 
      {
        ThisDoc->ActiveLineID = artindex;
        if (ArtListMultiSelect) 
        {
          status = article_operation (ThisDoc, artindex, toggle_selected);

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

          if (wParam & MK_SHIFT)
            AffectSelected (ThisDoc, DESELECT, EXTEND);
          else
            ThisDoc->AnchorLineID = artindex;
        }
        else 
        {
          if (MK_SHIFT & wParam) 
          {
                         
            AffectSelected (ThisDoc, bSel ? SELECT : DESELECT, ADDSELECT); //MDS want shift to select range
                        //status = article_operation (ThisDoc, artindex, toggle_read_unread);
            //DragMouseAction = status ? SEEN_SELECT : SEEN_DESELECT;
          }
          else 
          {
            ThisDoc->AnchorLineID = artindex;
            status = article_operation (ThisDoc, artindex, toggle_selected);
            DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
                        bSel = status;
          }
        }
        SetCapture (hWnd);      // release capture on button up
        InvalidateRect (hWnd, NULL, FALSE);
      }
    }
    else 
    {                      /* let user cheat while retrieving to see headers so far */
      InvalidateRect (hWnd, NULL, FALSE);
    }
    break;

  case WM_LBUTTONUP:
    /*  Letting up on the left button on an article name 
     *  gets us out of Dragging mode   */
//shimomai & ishido :-)
    if (DragMouseAction == DRAG_ON) 
    {
      DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
      ReleaseCapture ();
      *Separator += GET_X_LPARAM (lParam) - orgPos;
      if (ArtListSeparator1 < CharWidth ||
        ArtListSeparator1 > ArtListSeparator2 ||
        ArtListSeparator2 > ArtListSeparator3) 
      {
        ArtListSeparator1 = CharWidth * 8;
        ArtListSeparator2 = CharWidth * 15;
        ArtListSeparator3 = CharWidth * 40;
      }
      InvalidateRect (hWnd, NULL, FALSE);
    }

    ReleaseCapture ();
    DragMouseAction = DRAG_NONE;
    

	break;
  case WM_RBUTTONUP:
    /*  Letting up on the middle button on an article name
     *  gets us out of Dragging mode   */
    if (DragMouseAction != DRAG_NONE)
	{
		ReleaseCapture ();
		DragMouseAction = DRAG_NONE;
	}
    break;

  case WM_MBUTTONUP:
    MiddleButton_Is_Down = FALSE;
    break;
    
  case WM_LBUTTONDBLCLK:
    /*  Double-clicking on an article subject creates an "Article"
     *  window, whose purpose is to display the article.
     */
    if (TestCommBusy (hWnd, IDS_TEST_RETRIEVE_ARTICLE))
      break;

    if (CommDoc && CommDoc->hWndFrame && IsWindow(CommDoc->hWndFrame) )
    {
        //if window disable, there is probably a message box up. No new articles  MDS
        if (!IsWindowEnabled(CommDoc->hWndFrame) )
        {
            BringWindowToTop( CommDoc->hWndFrame);
            break;
        }
        
    }

    X = GET_X_LPARAM (lParam);
    Y = GET_Y_LPARAM (lParam);
// getarticle:;

    artindex = NewCursorToTextLine (X, Y, ThisDoc);

getarticle1:;

    if (artindex > -1) 
    {
      if (!ArtListMultiSelect)
      {
        article_operation (ThisDoc, artindex, selected_false);
        article_operation (ThisDoc, artindex, error_false);
      }
	  ThisDoc->curSaveArtIndex = -1;
      ViewArticle (ThisDoc, artindex, NO_REUSE, SHOW, NO_ID);
    }

    break;

  case WM_SETCURSOR:    //shimomai
    if ((!CommBusy || CommDoc != ThisDoc || CommDecoding)) 
    {
      if (WidthCursor)
        SetCursor (hSizeArrow);
      else
        SetCursor (hArrowCursor);
    }
    break;

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

//  if ((DragMouseAction != DRAG_NONE) &&
//      (!CommBusy || CommDoc != ThisDoc || CommDecoding)) {
// edited by shimomai
    if ((!CommBusy || CommDoc != ThisDoc || CommDecoding)) 
    {
      X = GET_X_LPARAM (lParam);
      Y = GET_Y_LPARAM (lParam);

      switch (DragMouseAction) 
      {
      case DRAG_NONE:
        WidthCursor = FALSE;
        X -= SideSpace - ThisDoc->ScXOffset * (CharWidth + 1);
        if (Y < LineHeight) 
        {
            WidthCursor = TRUE;
            if (X >= ArtListSeparator1 && X < ArtListSeparator1 + CharWidth) 
            {
                Separator = &ArtListSeparator1;
            }
            else if (X >= ArtListSeparator2 && X < ArtListSeparator2 + CharWidth) 
            {
                Separator = &ArtListSeparator2;
            }
            else if (X >= ArtListSeparator3 && X < ArtListSeparator3 + CharWidth) 
            {
                Separator = &ArtListSeparator3;
            }
            else 
            {
                WidthCursor = FALSE;
            }
        }
        break;

      case DRAG_ON:
        {
          if (!(wParam & MK_LBUTTON)) 
          {    // changed from && JD 12/31/96
            DragMouseAction = DRAG_NONE;
            DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
            ReleaseCapture ();
          } 
          else 
          {
            DrawDragingCursor(hWnd, DragCursorPosition, X, 2);
            DragCursorPosition = X;
          }
          break;
        }

      default:

      artindex = NewCursorToTextLine (X, Y, ThisDoc);

      if (artindex == -1 && !ArtListMultiSelect)
      {
        if (Y < TopSpace)
          artindex = ThisDoc->TopLineOrd;
        else
          artindex = ThisDoc->ScYLines + ThisDoc->TopLineOrd -1; 
      }
      if (artindex > -1) 
      {
        if (ArtListMultiSelect &&
            DragMouseAction != SEEN_SELECT && DragMouseAction != SEEN_DESELECT) 
        {
          
          ThisDoc->ActiveLineID = artindex;
          AffectSelectedOnScreen (ThisDoc, DESELECT, EXTEND);
        }
        else 
        {
          //select all articles between last & current mouse position
          //prevents skipping when quicky moving mouse MDS 19991206
          BOOL bDirUp = ThisDoc->AnchorLineID > artindex ? TRUE : FALSE;
          long count = labs (ThisDoc->AnchorLineID - artindex);
          
          for (lastartindex = ThisDoc->AnchorLineID + (bDirUp ? -1 : +1);
               count != 0; lastartindex += (bDirUp ? -1 : +1), count--)
          {
          
          switch (DragMouseAction) 
          {
            case DRAG_SELECT:
              article_operation (ThisDoc, lastartindex, selected_true);
              break;

            case DRAG_DESELECT:
              article_operation (ThisDoc, lastartindex, selected_false);
              break;

            case SEEN_SELECT:
              article_operation (ThisDoc, lastartindex, seen_true);
              break;

            case SEEN_DESELECT:
              article_operation (ThisDoc, lastartindex, seen_false);
              break;
            }
          }
          InvalidateRect (hWnd, NULL, FALSE);
          ThisDoc->AnchorLineID = artindex;
        }
      }
      artindex = NewCursorToTextLine (X, Y, ThisDoc);
      if (artindex < 0)// && !ArtListMultiSelect)
      {
        //time to auto-scroll

        int ScMin, ScMax;
        POINT CursorLocation;
        DWORD dwTime = 0;

        // Check to see if text selection needs to scroll up.
        if (((int)Y < 0) && (ThisDoc->TopLineOrd > 0)) 
        {

		  int nTime =  50;          
		  do                    // keep scrolling until the mouse moves.
          {
            if  (GetTickCount () > dwTime)
            {
              NewScrollIt (ThisDoc, (WPARAM) SB_LINEUP, lParam);
              
              lastartindex = ThisDoc->TopLineOrd;
              switch (DragMouseAction) 
              {
                case DRAG_SELECT:
                  article_operation (ThisDoc, lastartindex, selected_true);
                  break;

                case DRAG_DESELECT:
                  article_operation (ThisDoc, lastartindex, selected_false);
                  break;

                case SEEN_SELECT:
                  article_operation (ThisDoc, lastartindex, seen_true);
                  break;

                case SEEN_DESELECT:
                  article_operation (ThisDoc, lastartindex, seen_false);
                  break;
                }

              InvalidateRect (hWnd, NULL, FALSE);
              UpdateWindow(ThisDoc->hDocWnd);
			  //SendMessage (ThisDoc->hDocWnd, WM_PAINT, 0, 0L);
              dwTime = GetTickCount() + (nTime ? nTime-- : 0) ;
            }
			if (GetQueueStatus(QS_MOUSEBUTTON))
				break;

            GetCursorPos (&CursorLocation);
            ScreenToClient (ThisDoc->hDocWnd, &CursorLocation);
          }
          while ((X == CursorLocation.x) && (Y == CursorLocation.y)
                 && (ThisDoc->TopLineOrd > 0));
        }

        GetScrollRange (ThisDoc->hDocWnd, SB_VERT, &ScMin, &ScMax);
        // Check to see if text selection needs to scroll down.
        if (((int)Y > (int)ThisDoc->ScYHeight) && (ThisDoc->TopLineOrd < (unsigned int) ScMax)) 
        {
		  int nTime =  50;
		  do  // keep scrolling until the mouse moves.
          {
            if  (GetTickCount () > dwTime)
            {
              NewScrollIt (ThisDoc, (WPARAM) SB_LINEDOWN, lParam);

              lastartindex = ThisDoc->TopLineOrd + ThisDoc->ScYLines-1;
              switch (DragMouseAction) 
              {
                case DRAG_SELECT:
                  article_operation (ThisDoc, lastartindex, selected_true);
                  break;

                case DRAG_DESELECT:
                  article_operation (ThisDoc, lastartindex, selected_false);
                  break;

                case SEEN_SELECT:
                  article_operation (ThisDoc, lastartindex, seen_true);
                  break;

                case SEEN_DESELECT:
                  article_operation (ThisDoc, lastartindex, seen_false);
                  break;
                }

              //InvalidateRect (hWnd, NULL, FALSE);
              UpdateWindow(ThisDoc->hDocWnd);
			  //SendMessage (ThisDoc->hDocWnd, WM_PAINT, 0, 0L);
              dwTime = GetTickCount() + (nTime ? nTime-- : 0); 
			  

            }
			if (GetQueueStatus(QS_MOUSEBUTTON))
				break;

            //if (PeekMessage (&Message, ThisDoc->hDocWnd, WM_LBUTTONUP, WM_LBUTTONUP, PM_NOREMOVE))
            //  break;
            GetCursorPos (&CursorLocation);
            ScreenToClient (ThisDoc->hDocWnd, &CursorLocation);
          }
          while ((X == CursorLocation.x) && (Y == CursorLocation.y)
                 && (ThisDoc->TopLineOrd < (unsigned int) ScMax));

		}


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

      GTotalDelta += zDelta;
      if ((GScrollMode != WHEELSCROLL_DONT)  && (abs(GTotalDelta) >= WHEEL_DELTA)) 
      {
        GTotalDelta = 0;
        if ( zDelta > 0 )
        {
            if ((GScrollMode == WHEELSCROLL_SCROLL) && (MiddleButton_Is_Down)) 
            {
                if ((GPosScrollByWheel > 0) && (GScrollByWheel[GPosScrollByWheel-1]))
                   GPosScrollByWheel--;
            } 
            else 
            {
                
				if ( GScrollByWheel[GPosScrollByWheel] == (UINT) -1)
				{
					//system want page scroll
					NewScrollIt (ThisDoc, (WPARAM) SB_PAGEUP, lParam);
				}
				else
				{
				
					for (i = 0 ; i < GScrollByWheel[GPosScrollByWheel] ; i++)
						NewScrollIt (ThisDoc, (WPARAM) SB_LINEUP, lParam);
				}
            }
        }
        else
        {
            if ((GScrollMode == WHEELSCROLL_SCROLL) && (MiddleButton_Is_Down)) 
            {
                if ((GPosScrollByWheel < (MAX_WHEEL_ENTRIES-1)) && (GScrollByWheel[GPosScrollByWheel+1]))
                   GPosScrollByWheel++;
            } 
            else 
            { 
                
				if ( GScrollByWheel[GPosScrollByWheel] == (UINT) -1)
				{
					//system want page scroll
					NewScrollIt (ThisDoc, (WPARAM) SB_PAGEDOWN, lParam);
				}
				else
				{
					for (i = 0 ; i < GScrollByWheel[GPosScrollByWheel] ; i++ )
						NewScrollIt (ThisDoc, (WPARAM) SB_LINEDOWN, lParam);
				}
            }
        }
      }
    }
    break;
    
  case WM_VSCROLL:
    NewScrollIt (ThisDoc, wParam, lParam);
    break;

  case WM_HSCROLL:
    HScrollIt (ThisDoc, wParam, lParam);
    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 (1);
		}

	break;

  case WM_PAINT:
    {
      HANDLE hBlock;
      int X, Y;
      unsigned int Offset;
      long VertLines, RangeHigh;
      long HorzChars;
      long CurPos;
      char indicator;
      TypLineID artindex;
      BIGINT artnum;
      TypBlock far *NetBlockPtr;
      TypLine far *NetLinePtr;
      TypGroup far *group;
      header_p headers;
      header_p header;
      HANDLE header_handle;
      HANDLE thread_handle;
      char scratch_line[MAXINTERNALLINE];
      UBIGINT OldHighestSeen;
      COLORREF MyColors[8], MyBack[8];
      RECT aRect;
      int MyColorMask = 0;
      int PrevColorMask = MyColorMask;
      HBRUSH hOldBrush;
      HANDLE hOldFont;
      char AuthorName[MAXINTERNALLINE];
      const char *ptr;     //shimomai
#ifdef _WIN32
      SCROLLINFO scrollInfo;  //MDS
#endif

      /* MyColors and MyBack are arrays of colors used to display text
       * foreground and background.
       * The ColorMask variables are indices into these arrays.
       * We set and clear bits in these indices depending upon
       * whether the article has been selected, seen or killed.
       */

      hDC = BeginPaint (hWnd, &ps);
      GetClientRect (hWnd, &clientRect);
	  if (g_hPalette)
	  {
		SelectPalette(hDC, g_hPalette, FALSE);
		RealizePalette(hDC);
	  }


      hOldFont = SelectObject (hDC, hListFont);

      VertLines = (ThisDoc->ScYLines > (ThisDoc->TotalLines - ThisDoc->TopLineOrd))
        ? (ThisDoc->TotalLines - ThisDoc->TopLineOrd) : ThisDoc->ScYLines;

      HorzChars = ThisDoc->ScXChars;

      MyColors[0] = ArticleUnSeenColor;     // unseen/unselected
      MyBack[0] = ListBackgroundColor;

      MyColors[1] = ArticleSeenColor;       // seen/unselected
      MyBack[1] = ListBackgroundColor;

      MyColors[4] = ArticleKilledColor; // marked seen/unselected
      MyBack[4] = ListBackgroundColor;

      MyColors[5] = ArticleErrorColor;      //error/unselected
      MyBack[5] = ListBackgroundColor;
      MyColors[6] = ArticleErrorColor;
	  MyBack[7] = ArticlePreviewColor;
 


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

        MyBack[2] = ArticleUnSeenColor;
        MyColors[3] = ListBackgroundColor;  // [marked] seen/selected

        MyBack[3] = ArticleSeenColor;

        MyColors[6] = ListBackgroundColor;  //error/selected
        MyBack[6] = ArticleErrorColor;
      }
      else 
      {
        if (NOPALETTERGB(ArticleUnSeenColor) == RGB (0, 0, 0) ||
            NOPALETTERGB(ListBackgroundColor) == RGB (0, 0, 0) && IsBright (ArticleUnSeenColor))
          MyColors[2] = ListBackgroundColor;    // unseen/selected

        else
          MyColors[2] = ArticleUnSeenColor;

        if ( NOPALETTERGB(ArticleSeenColor) == RGB (0, 0, 0) ||
        NOPALETTERGB(ListBackgroundColor) == RGB (0, 0, 0) && IsBright (ArticleSeenColor))
          MyColors[3] = ListBackgroundColor;    // seen/selected

        else
          MyColors[3] = ArticleSeenColor;

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

      SetTextColor (hDC, MyColors[MyColorMask]);
      SetBkColor (hDC, MyBack[MyColorMask]);
      /* Update the scroll bar thumb position.  */
      RangeHigh = ThisDoc->TotalLines - ThisDoc->ScYLines;
      if (RangeHigh < 0) 
      {
        RangeHigh = 0;
        ThisDoc->TopLineOrd = 0;
        VertLines = ThisDoc->TotalLines;
      }
      CurPos = ThisDoc->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 = VertLines ? RangeHigh + VertLines -1 : 0;
      scrollInfo.nPage = ThisDoc->ScYLines;
      if (!IsFlagSet(ThisDoc, ThumbTracking))
      {
        scrollInfo.fMask |= SIF_POS;
        scrollInfo.nPos = CurPos;
      }

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

      SetScrollRange (hWnd, SB_VERT, 0, OrdToScroll(RangeHigh,RangeHigh),
         IsFlagSet(ThisDoc, ThumbTracking) ? FALSE : TRUE);
         
      /* thumb pos is updated at end of thumb track */
      if (!IsFlagSet(ThisDoc, ThumbTracking)) 
      {
        SetScrollPos (hWnd, SB_VERT, OrdToScroll(RangeHigh,CurPos), TRUE);
      }

#endif

      RangeHigh = ThisDoc->LongestLine - ThisDoc->ScXChars;
      if (RangeHigh < 0) 
      {
        RangeHigh = 0;
        ThisDoc->ScXOffset = 0;
      }

#ifdef _WIN32  //MDS proportional scroll
      scrollInfo.cbSize = sizeof(SCROLLINFO);
      scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
      scrollInfo.nMin = 0;
      //when there is no data loaded, show a horiz scrollbar so ScYLines
	  //is calculated correctly (assuming there will be a scrollbar)
	  //so initital active item is not scrolled off bottom of screen/hidden by scrollbar
      scrollInfo.nMax = ThisDoc->LongestLine > 0 ? ThisDoc->LongestLine-1 : ThisDoc->ScXChars+1;
      scrollInfo.nPage = ThisDoc->ScXChars;
      scrollInfo.nPos = ThisDoc->ScXOffset;
      SetScrollInfo( hWnd, SB_HORZ, &scrollInfo, TRUE);
#else

      SetScrollRange (hWnd, SB_HORZ, 0,OrdToScroll(RangeHigh,RangeHigh), FALSE);
      SetScrollPos (hWnd, SB_HORZ, OrdToScroll(RangeHigh,ThisDoc->ScXOffset), TRUE);
#endif

      X = SideSpace - ThisDoc->ScXOffset * (CharWidth + 1); //shimomai
      Y = StartPen;

      /* Now paint this stuff on the screen.  */
      artindex = ThisDoc->TopLineOrd;
      if (ThisDoc->ActiveLines) {
        LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
                  &NetBlockPtr, &NetLinePtr);
        group = GetGroup(NetLinePtr);
        header_handle = group->header_handle;
        thread_handle = group->thread_handle;

        if (header_handle)
          headers = lock_headers (header_handle, thread_handle);
        else
          break;                /* this can happen during Update */

        OldHighestSeen = group->HighestPrevSeen;
        UnlockLine (NetBlockPtr, NetLinePtr, &hBlock, &Offset, &MyLineID);
        do 
        {
          header = header_elt (headers, artindex);
          artnum = header->number;
          indicator = ' ';
          if(!(header->SeenSelected & ART_SEENORKILLED))
          {
            if (OldHighestSeen && (header->number > OldHighestSeen))
                indicator = 'n';
          }
          else if(ART_KILLED & header->SeenSelected)  //MDS apply 'killed' before seen
          {
                indicator = 'k';
          }
          else if(ART_SEEN & header->SeenSelected)
          {
                indicator = 's';
          }

          if (ThisDoc->FindOffset == (unsigned) artindex)
            indicator = '>';

          { //ishido DBCS aware author name trimming
            const char *base_p = header->pHD->sFrom;
            const char *p = base_p;
            while ((*p != '\0') && (p - base_p < sizeof(AuthorName)-1))
              p = AnsiNext(p);
            strncpy(AuthorName, (const char*)header->pHD->sFrom, (p - base_p) + 1);
            AuthorName[p - base_p] = '\0';
          }

          /* Figure out the color of this line. */
          if (ART_SEENORKILLED & header->SeenSelected) 
          {
            if (ART_SELECTED & header->SeenSelected)
              MyColorMask = 3;
            else
              MyColorMask = (ART_KILLED == header->SeenSelected) ? 4 : 1;
          }
          else 
          {
            if (ART_SELECTED & header->SeenSelected)
              MyColorMask = 2;
            else
              MyColorMask = 0;
          }

          /* Error retrieving arts get priority */
          if (ART_ERROR & header->SeenSelected)
          {
            MyColorMask = 5;
            if (ART_SELECTED & header->SeenSelected)
              MyColorMask = 6;
          }

		  if (ART_PREVIEW & header->SeenSelected)
		  {
			SetTextColor (hDC, MyColors[MyColorMask]);
			SetBkColor (hDC, ArticlePreviewColor);
			PrevColorMask = 7;
		  }
          else if (MyColorMask != PrevColorMask) 
          {
            SetTextColor (hDC, MyColors[MyColorMask]);
            SetBkColor (hDC, MyBack[MyColorMask]);
            PrevColorMask = MyColorMask;
          }

          /* Now write out the line. */
                  /* out article number */
          SetRect (&aRect, 0, Y, X+ArtListSeparator1+CharWidth, Y + LineHeight);
          SetTextAlign (hDC, TA_TOP | TA_RIGHT);
                  BIGINTtoa(artnum, scratch_line, 10);
          ExtTextOut (hDC, X+ArtListSeparator1, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
                      scratch_line, strlen(scratch_line), (LPINT) NULL);

                  /* out indicator */
          SetTextAlign (hDC, TA_TOP | TA_LEFT);
                  TextOut (hDC, X, Y, &indicator, 1);

                  /* out date */
                  SetRect (&aRect, X+ArtListSeparator1+CharWidth, Y, X+ArtListSeparator2, Y + LineHeight);
                  StringDate (scratch_line, header->pHD->date),
                        ExtTextOut (hDC, X+ArtListSeparator1+CharWidth, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
                        scratch_line, strlen(scratch_line), (LPINT) NULL);

                  /* out author name */
          SetRect (&aRect, X+ArtListSeparator2, Y, X+ArtListSeparator3, Y + LineHeight);
          ExtTextOut (hDC, X+ArtListSeparator2, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
                      AuthorName, strlen(AuthorName), (LPINT) NULL);

                  /* out lines */
          SetTextAlign (hDC, TA_TOP | TA_RIGHT);
                  scratch_line[0] = ' ';
                  _ltoa(header->lines, &(scratch_line[1]), 10);
          TextOut (hDC, X+ArtListSeparator3, Y, scratch_line, strlen(scratch_line));

                  /* out subject */
          SetRect (&aRect, X+ArtListSeparator3, Y, clientRect.right, Y + LineHeight);
          SetTextAlign (hDC, TA_TOP | TA_LEFT);
          ptr = (ThreadFullSubject || !(header->thread_depth))
            ? (const char*)header->pHD->sSubject : ThreadDepthIndicator;

          //MDS account for threading depth to prevent subject truncation 20000505
          {
            SIZE szExtent;
            unsigned long lSize = 0;
            GetTextExtentPoint( hDC,(const char*)ptr, strlen(ptr), &szExtent);
            lSize = szExtent.cx + ArtListSeparator3+ CharWidth * (1 + header->thread_depth * 2);
            
            if (!CharWidth)
            {
                CharWidth = 1;
            }
            
            if (  lSize >  ThisDoc->LongestLine * CharWidth && CharWidth)
              ThisDoc->LongestLine = max( (ThisDoc->LongestLine), ( (lSize + 4 * CharWidth) / CharWidth) );

          }  
          ExtTextOut (hDC, X+ArtListSeparator3+ CharWidth * (1 + header->thread_depth * 2), Y,
                    ETO_OPAQUE | ETO_CLIPPED, &aRect,
                    (const char*)ptr, strlen(ptr), (LPINT) NULL);
          SetRect (&aRect, 0, Y, clientRect.right, Y + LineHeight);

          if (ThisDoc->ActiveLineID == -1L)
            ThisDoc->ActiveLineID = artindex;
          if (ThisDoc->ActiveLineID == artindex /*&& ArtListMultiSelect*/) 
          { //MDS req. to turn this on 20001001
            DrawFocusRect (hDC, &aRect);
          }

          Y += LineHeight;
          artindex++;

        }
        while (--VertLines > 0);

        if (header_handle)
          unlock_headers (header_handle, thread_handle);
      }
      SelectObject (hDC, hOldFont);

      /* We've reached the end of the data to be displayed     */
      /* on this window.  If there's more screen real estate   */
      /* left, just blank it out.                              */

      hOldBrush = (HBRUSH) SelectObject (hDC, hListBackgroundBrush);
      PatBlt (hDC, 0, Y, clientRect.right - 1, clientRect.bottom - Y, PATCOPY);
      PatBlt (hDC, 0, 0, clientRect.right - 1, StartPen, PATCOPY);
      SelectObject (hDC, hOldBrush);
      EndPaint (hWnd, &ps);
      break;
    }

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

/*------------ CloseGroupWnd ------------------------------
 *
 *  Make sure this Wnd is not the active Comm window, then destroy it
 *  also make sure no child articles are active comm... jsc 11/1/94 
 */
void CloseGroupWnd (HWND hWnd, TypDoc far * ThisDoc)
{
  register int i;
  BOOL stop = FALSE;

  if (CommBusy) {
    stop = (ThisDoc == CommDoc);
    for (i = 0; !stop && i < MAXARTICLEWNDS; i++) 
    {
      if (ArticleDocs[i].InUse && CommDoc == &ArticleDocs[i] &&
          ArticleDocs[i].ParentDoc == ThisDoc) 
      {
        stop = TRUE;
      }
    }
  }

  if (stop)
    WVMessageBox(hWnd, IDS_WVGROUP_WAIT, IDS_WVGROUP_WAIT_T, MB_OK | MB_ICONSTOP);
  else 
  {
    SetHandleBkBrush (ThisDoc->hDocWnd, (HBRUSH) GetStockObject (WHITE_BRUSH));
    DestroyWindow(hWnd);
    if (SaveNewsrcOnClose && NewsrcDirty) 
		WriteNewsrc ();

#ifdef OFFLINE  // TANAKA Goh
    if (SaveSOUPOnClose) 
    {
       char* pGenSockDLL;
          
       if ((pGenSockDLL = strrchr(GenSockDLL, '\\')) == NULL) 
       {
            pGenSockDLL = GenSockDLL;
       } 
       else 
       {
            pGenSockDLL++;
       }
          
       if (!stricmp(pGenSockDLL, "gwssoup.dll") || !stricmp(pGenSockDLL, "gsoup32.dll")) 
       {
            CommDoc = ThisDoc;
            CommState = ST_WRITE;
            CommBusy = TRUE;
            PutCommLine("WRITE");
       }
    }
#endif

    /* Ray Dunn:99/10:  When the group window is closed, the next thing the
       user invariably wants to do is select another group from the main window,
       or to exit winvn using the main window.
       It's a pain if the main window has been minimized or is hidden, that it
       doesn't appear automatically - so this does it
    */
    if (IsMinimized(NetDoc.hWndFrame))
      ShowWindow(NetDoc.hWndFrame, SW_RESTORE);
    SetFocus(NetDoc.hWndFrame);

    /* Ray Dunn:99/10:End */

    /* invalidate main window, so '*' will be updated, if appropriate */
    InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
  }
}

void SetGroupMailMenu (TypDoc far * DocPtr)
{
    HMENU hMenu, hSubMenu;

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

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

/*------------ SetGroupMenus ------------------------------
 * dis/enable menu items which depend on group list being completely 
 * retrieved
 */
void SetGroupMenus (TypDoc far * DocPtr, int enable)
{
    HMENU hMenu, hSubMenu;
    UINT mode;

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

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

    EnableMenuItem (hSubMenu, IDM_SELECT_ALL, mode);
	EnableMenuItem (hSubMenu, IDM_INVERT_SELECTION, mode);
    EnableMenuItem (hSubMenu, IDM_SELECT_MATCH, mode);
    EnableMenuItem (hSubMenu, IDM_DESELECT_MATCH, mode);
    EnableMenuItem (hSubMenu, IDM_CATCH_UP_AND_EXIT, mode);
    EnableMenuItem (hSubMenu, IDM_MARK_ALL, mode);
    EnableMenuItem (hSubMenu, IDV_EXIT, mode);

    EnableMenuItem( hSubMenu, IDM_CANCEL_DOWNLOAD, DISABLE_MENU );
    // mail menu ignores this enable flag - it depends solely
    // on transport availability
    SetGroupMailMenu (DocPtr);

    /* Ray Dunn:99/10 */

    //if (enable != ENABLE)
    {
        EnableMenuItem (hMenu, IDM_VIEW_ACTION, mode);
		EnableMenuItem (hMenu, IDM_VIEW_NEXT_SEL, mode);

        EnableSortMenuItem (hMenu, IDM_SORT_THREADS, mode);
        EnableSortMenuItem (hMenu, IDM_SORT_THREADSUB, mode);
    }
    /* Ray Dunn:99/11: No Sort Enhancement */
    EnableSortMenuItem (hMenu, IDM_DONT_SORT, mode);
    /* Ray Dunn:99/11:End */
    EnableSortMenuItem (hMenu, IDM_SORT_DATE, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_SUBJECT, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_LINES, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_ARTNUM, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_FROM, mode);
    /* Ray Dunn:99/10:End */
    EnableSortMenuItem (hMenu, IDM_SORT_FILENAME, mode);

    /*
    hSubMenu = GetSubMenu (hMenu, 1);     // Sort menu

    EnableMenuItem (hSubMenu, IDM_SORT_DATE, mode);
    EnableMenuItem (hSubMenu, IDM_SORT_SUBJECT, mode);
    EnableMenuItem (hSubMenu, IDM_SORT_LINES, mode);
    //  EnableMenuItem (hSubMenu, IDM_SORT_THREADS, mode);  // handled in INITMENU
    EnableMenuItem (hSubMenu, IDM_SORT_ARTNUM, mode);
    EnableMenuItem (hSubMenu, IDM_SORT_FROM, mode);
    */
    hSubMenu = GetSubMenu (hMenu, 2);     // Search menu

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

    // set toolbar buttons
    mode = (enable == ENABLE) ? TRUE : FALSE;
    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);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_CATCH_UP_AND_EXIT, mode);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDV_EXIT, mode);

    // initmenu handles save/decode selected, as well as deselect all
    SendMessage (DocPtr->hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
}



/* this is for a empty group window  */
/* can be disabled with SetGroupMenu(..,ENABLE); */
void SetMenusForEmptyGroup(TypDoc far * DocPtr)
{
    HMENU hMenu, hSubMenu;

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

    SetGroupMenus(DocPtr, DISABLE);
    EnableMenuItem (hSubMenu, IDV_EXIT, ENABLE_MENU);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_POST, TRUE);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDV_EXIT, TRUE);
}




/*------------ SetMenusForMultiArticleOperation ----------
 *  (jsc) killer function name, huh?!
 * During multi-article op (i.e. Save selected, Decode selected)
 * we don't want the user to do any of these things
 */
void SetMenusForMultiArticleOperation (TypDoc far * DocPtr, int enable)
{
    HMENU hMenu, hSubMenu;
    UINT mode;
#ifdef OFFLINE  // TANAKA Goh
    char* pGenSockDLL;
#endif

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

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

    EnableMenuItem (hSubMenu, IDM_SAVE_SELECTED, mode);
    EnableMenuItem (hSubMenu, IDM_DECODE_SELECTED, mode);

#ifdef OFFLINE  // TANAKA Goh
    if ((pGenSockDLL = strrchr(GenSockDLL, '\\')) == NULL) 
    {
        pGenSockDLL = GenSockDLL;
    } 
    else 
    {
        pGenSockDLL++;
    }

    if (!stricmp(pGenSockDLL, "gwssoup.dll") || !stricmp(pGenSockDLL, "gsoup32.dll")) 
    {
        EnableMenuItem (hSubMenu, IDM_DELETE_SELECTED, mode);
        EnableMenuItem (hSubMenu, IDM_SENDME_SELECTED, mode);
    } 
    else if (!stricmp(pGenSockDLL, "ggnspool.dll") || !stricmp(pGenSockDLL, "gspool32.dll")) 
    {
        EnableMenuItem (hSubMenu, IDM_DELETE_SELECTED, mode);
        EnableMenuItem (hSubMenu, IDM_SENDME_SELECTED, DISABLE_MENU);
    } 
    else 
    {
        //EnableMenuItem (hSubMenu, IDM_DELETE_SELECTED, DISABLE_MENU);
        //EnableMenuItem (hSubMenu, IDM_SENDME_SELECTED, DISABLE_MENU);
        DeleteMenu (hSubMenu, IDM_DELETE_SELECTED, MF_BYCOMMAND);
        DeleteMenu (hSubMenu, IDM_SENDME_SELECTED, MF_BYCOMMAND);
    }
#endif

    //  EnableMenuItem (hSubMenu, IDM_READ_SELECTED, mode);
    EnableMenuItem (hSubMenu, IDV_EXIT, mode);
    EnableMenuItem (hSubMenu, IDM_CATCH_UP_AND_EXIT, mode);
    EnableMenuItem (hSubMenu, IDM_MARK_ALL, mode);
    EnableMenuItem (hSubMenu, IDM_THREADREAD, mode);

	EnableMenuItem (hSubMenu,IDM_SAVE_SELECTED_ARTICLE_LIST, mode);
	EnableMenuItem (hSubMenu,IDM_SAVE_SELECTED_ARTICLE_NZB, mode);

    /* Ray Dunn:99/11: No Sort Enhancement */
    EnableSortMenuItem (hMenu, IDM_DONT_SORT, mode);
    /* Ray Dunn:99/11:End */
    /* Ray Dunn:99/10:Start */
    EnableSortMenuItem (hMenu, IDM_SORT_DATE, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_SUBJECT, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_LINES, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_THREADS, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_THREADSUB, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_ARTNUM, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_FROM, mode);
    EnableSortMenuItem (hMenu, IDM_SORT_FILENAME, mode);
    /* Ray Dunn:99/10:End */




    // set toolbar buttons
    if (enable == ENABLE)
        mode = TRUE;
    else
        mode = FALSE;
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_SAVE_SELECTED, mode);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_DECODE_SELECTED, mode);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDV_EXIT, mode);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_MARK_ALL, mode);
}

long find_first_selected_art(TypDoc far * Doc)
{
    long retval = -1;
    HANDLE hBlock;
    unsigned int Offset;
    TypLineID MyLineID;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    TypGroup *GroupDoc;
    HANDLE header_handle;
    HANDLE thread_handle;
    header_p headers;
    header_p header;
    TypLineID artindex;

    LockLine (Doc->hParentBlock, Doc->ParentOffset,
            Doc->ParentLineID, &BlockPtr, &LinePtr);
    GroupDoc = GetGroup(LinePtr);
    header_handle = GroupDoc->header_handle;

    if (header_handle)
    {
        thread_handle = GroupDoc->thread_handle;
        headers = lock_headers (header_handle, thread_handle);

        artindex = -1;

        while((++artindex < GroupDoc->total_headers) && retval < 0)
        {
            header = header_elt (headers, artindex);
            if(ART_SELECTED & header->SeenSelected)
                retval = artindex;
        }
        unlock_headers (header_handle, thread_handle);
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
    }
    return (retval);
}


/*----------------------------------------------------------------------------
 * article header manipulation routines
 */
BOOL SelectHeader (TypDoc far * Doc, header_p header, int value)
{
    int was = Doc->SelectedLines;
	if (value == INVERT)
	{
        value = (ART_SELECTED & header->SeenSelected) == 0;
	}
	else if (value & SELKILLED)
	{
		//only apply to filter-killed articles
		if ( (ART_KILLED & header->SeenSelected) == 0)
			return FALSE;
		//mask off kill flag to allow select/deselect operation
		value &= ~SELKILLED;
	}

    if ((ART_SELECTED & header->SeenSelected) != (value ? ART_SELECTED: ART_UNSELECTED)) 
    {
        if (value)
        {
            header->SeenSelected |= ART_SELECTED;
            (Doc->SelectedLines)++;
        }
        else
        {
            header->SeenSelected &= ~ART_SELECTED;
            if (Doc->SelectedLines) (Doc->SelectedLines)--;
        }
    }
    if ((was == 0 && value) || (was != 0 && Doc->SelectedLines == 0)) 
    {
        /* this is a kludge to update the toolbar */
        SendMessage (Doc->hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
    }

    return value;
}

BOOL article_operation (TypDoc far * Doc, long artindex,
                                    BOOL (*art_fun) (TypDoc far * Doc, header_p headers,
                                    TypGroup * group, long artindex))
{
    HANDLE hBlock;
    unsigned int Offset;
    TypLineID MyLineID;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    TypGroup *GroupDoc;
    HANDLE header_handle;
    HANDLE thread_handle;
    BOOL result = FALSE;
    header_p headers;

    LockLine (Doc->hParentBlock, Doc->ParentOffset,
                Doc->ParentLineID, &BlockPtr, &LinePtr);
    GroupDoc = GetGroup(LinePtr);
    header_handle = GroupDoc->header_handle;
    NewsrcDirty = TRUE;

    if (header_handle) 
    {
        thread_handle = GroupDoc->thread_handle;
        headers = lock_headers (header_handle, thread_handle);
        result = art_fun (Doc, headers, GroupDoc, artindex);

        unlock_headers (header_handle, thread_handle);
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
    }
    return (result);
}

BOOL toggle_read_unread (TypDoc far * Doc, header_p headers,
                    TypGroup * GroupDoc, long artindex)
{

    /* only try if in range.  All this work for these two lines 8^) */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        header->SeenSelected = (ART_SELECTED & header->SeenSelected) |
                        ((ART_SEENORKILLED & header->SeenSelected) ? ART_UNSEEN : ART_SEEN);
        return (ART_SEEN & header->SeenSelected);
    }
    return (FALSE);
}

BOOL mark_read_to_here (TypDoc far * Doc, header_p headers,
                   TypGroup * GroupDoc, long artindex)
{
    header_p header;

    /* only try if in range.  All this work for these two lines 8^) */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        int i;
        for (i = 0; i <= artindex; i++) 
        {
            header = header_elt (headers, i);
            header->SeenSelected |= ART_SEEN;
			if (RMDeselectOnMark)
				header->SeenSelected &= ~ART_SELECTED;
            // added by jlg to get rid of erroneous '*' in group window
            // this causes to become new marked articles to unseen only in case of threaded reading Heirich 19980315
            //if (header->number > GroupDoc->HighestPrevSeen)
            //  GroupDoc->HighestPrevSeen = header->number;
        }
        return (TRUE);
    }
    return (FALSE);
}

// * this is just a pseudo code hack
// Mark all articles in this thread as read
BOOL mark_read_thread (TypDoc far * Doc, header_p headers,
					   TypGroup * GroupDoc, long artindex)
{
    header_p header;
	HANDLE thread_handle = GroupDoc->thread_handle;

    if (artindex > GroupDoc->max_headers || artindex < 0)
		return FALSE;
		 
	long i = artindex;
	header = header_elt (headers, artindex);
	int nDepth = header->thread_depth;

	while (i > 0)
	{
		
		header = header_elt (headers, i--);
		if (header->thread_depth > 0 )
			//header->SeenSelected |= ART_SEEN;
			SelectHeader (Doc, header, 1);
		else
		{
			SelectHeader (Doc, header, 1); 
			break;
		}

	}
	i = artindex;
	while (i < GroupDoc->max_headers)
	{
		header = header_elt (headers, i++);
		if (header->thread_depth > 0)
			//header->SeenSelected |= ART_SEEN;
			SelectHeader (Doc, header, 1);
		else
			break;

	}

    return (TRUE);
}



BOOL mark_read_all (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->max_headers; i++) 
    {
        header = header_elt (headers, i);
        header->SeenSelected |= ART_SEEN;
    }
    //GroupDoc->pSeendb->MarkSeen(1, GroupDoc->uLoadLast); // HB - catch expired articles
    GroupDoc->HighestPrevSeen = GroupDoc->uLoadLast;
    return (TRUE);
}

BOOL mark_unread_all (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->max_headers; i++) 
    {
        header = header_elt (headers, i);
        header->SeenSelected &= ~ART_SEEN;
    }
	
    GroupDoc->HighestPrevSeen = GroupDoc->uLoadFirst;
    return (TRUE);
}


/* Ray Dunn:99/10 */
BOOL mark_read_all_catchup (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->max_headers; i++) 
    {
        //header = header_elt (headers, i);  //if threading is screwed we dont access all arts, so why go thru index anyway?
        header = &headers[i];
        header->SeenSelected |= ART_SEEN;
    }
    GroupDoc->HighestPrevSeen = GroupDoc->ServerLast;
    GroupDoc->NumUnread = 0;
    GroupDoc->pSeendb->MarkSeen(1, GroupDoc->ServerLast);

    return (TRUE);

}
/* Ray Dunn:99/10:End */

BOOL mark_read_selected (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if (ART_SELECTED & header->SeenSelected) 
        {
            header->SeenSelected |= ART_SEEN;
        }
    }
    return (TRUE);
}

BOOL mark_selected_read_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc,
               long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if (ART_SEENORKILLED & header->SeenSelected) 
        { 
            SelectHeader (Doc, header, 1);
        }
    }
    return (TRUE);
}

BOOL mark_selected_killed_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc,
               long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if (ART_KILLED & header->SeenSelected && !(ART_SEEN & header->SeenSelected)) 
        { 
            SelectHeader (Doc, header, 1);
        }
    }
    return (TRUE);
}

BOOL mark_selected_error_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc,
               long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if ((ART_ERROR & header->SeenSelected) == ART_ERROR) 
        {
            SelectHeader (Doc, header, 1);
        }

    }
    return (TRUE);
}


BOOL mark_selected_unread_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if ((ART_SEENORKILLED & header->SeenSelected) == ART_UNSEEN) 
        {
            SelectHeader (Doc, header, 1);
        }
    }
    return (TRUE);
}

BOOL mark_deselected_unread_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc,
               long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if ((ART_SEENORKILLED & header->SeenSelected) == ART_UNSEEN) 
        {
            SelectHeader (Doc, header, 0);
        }
    }
  return (TRUE);
}


BOOL mark_deselected_killed_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc,
               long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if ((ART_SEENORKILLED & header->SeenSelected) == ART_KILLED) 
        {
            SelectHeader (Doc, header, 0);
        }
    }
  return (TRUE);
}

BOOL mark_deselected_error_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc,
               long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if ((ART_ERROR & header->SeenSelected) == ART_ERROR) 
        {
            SelectHeader (Doc, header, 0);
        }
    }
  return (TRUE);
}


BOOL mark_deselected_read_articles (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if (ART_SEENORKILLED & header->SeenSelected) 
        {
            SelectHeader (Doc, header, 0);
        }
    }
    return (TRUE);
}


BOOL deselect_dupes (TypDoc far * Doc, header_p headers, TypGroup * GroupDoc, long artindex)
{

	WVFileSortHeaders* pSortList = NULL; 
	if (!GroupDoc->pFile_sort)
	{
		HCURSOR hCursorOld = SetCursor (LoadCursor (NULL, IDC_WAIT));
		GroupDoc->pFile_sort = pSortList = new WVFileSortHeaders();
		if (pSortList)
			pSortList->SortDoc(Doc, 0 );
		SetCursor(hCursorOld);
	}
	else
	{
		pSortList = (WVFileSortHeaders*) GroupDoc->pFile_sort;
	}

	if (!pSortList)
		return FALSE;

	HEADERLIST::iterator itr;
	for (itr = pSortList->m_SubjList.begin(); itr != pSortList->m_SubjList.end(); itr++)
	{
		HEADERENTRY* pEntry = *itr;
		HEADERENTRY *pSelected = pEntry;
		int nFound = 0;
		int nMatched = 0;
		int nExpected = 0;
		BOOL bDone = FALSE;

		pEntry = *itr;
		while (pEntry)
		{
			
			HEADERENTRY *pNext = pEntry->pDupes;
			HEADERENTRY *pLast = pEntry;
			HEADERENTRY *pMatched = NULL;
		
			//Need to match multipart articles with parent so we dont select part1 from poster#1 and part2 from poster#2
			if (ART_SELECTED & pEntry->HeaderP->SeenSelected) 
			{
				
				if (bDone)
				{
					pEntry = pEntry->pDupes;
					continue;
				}

				if (pNext && pNext->nTotal == pEntry->nTotal)
				{
					pSelected = pEntry;
					SelectHeader (Doc, pEntry->HeaderP, 0);
					//compare 2 identical filenames, choose more likely target
					while (pNext && pNext->nPart > 0 && pNext->nPart == pEntry->nPart && pNext->nTotal == pEntry->nTotal)
					{
						
						if (ART_SELECTED & pNext->HeaderP->SeenSelected)
						{
							SelectHeader (Doc, pNext->HeaderP, 0);

							int nCurrent = 0;
							int nNew = 0;	
							
							if (!stristr(pSelected->HeaderP->pHD->sSubject, "Re:") )
								nCurrent++;
							
							if (!stristr(pNext->HeaderP->pHD->sSubject, "Re:") )
								nNew++;
							
							if (pSelected->HeaderP->lines > pNext->HeaderP->lines)
								nCurrent++;
							if (pSelected->HeaderP->lines < pNext->HeaderP->lines)
								nNew++;

							if (pMatched)
							{
								if ( !strcmp(pMatched->Subject, pNext->Subject))
									nNew +=2;
								if ( !strcmp(pMatched->Subject, pSelected->Subject))
									nCurrent += 2;
							}
							
							if (nNew > nCurrent)
								pSelected = pNext;
						}
						pLast = pNext;

						pNext = pNext->pDupes;
												

					} //while
					SelectHeader (Doc, pSelected->HeaderP, 1);

				}
				else if (pNext)
				{
					bDone = TRUE;

				}

				SelectHeader (Doc, pSelected->HeaderP, 1);
				if (nMatched < pSelected->nPart)
				{
					nMatched = pSelected->nPart;
					nFound++;
				}

				if (1)
				{
					pMatched = pSelected;
					nExpected = pSelected->nTotal;
					

				}
				pEntry = pLast;

				
			}
			pEntry = pEntry->pDupes;

		}

	}
	


    return (TRUE);
}

BOOL mark_unread_selected (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc,  long artindex)
{
    header_p header;

    int i;
    for (i = 0; i < GroupDoc->total_headers; i++) 
    {
        header = header_elt (headers, i);
        if (ART_SELECTED & header->SeenSelected) 
        {
            header->SeenSelected &= ~ART_SEENORKILLED;
        }
    }
    return (TRUE);
}

BOOL toggle_selected (TypDoc far * Doc, header_p headers,
                 TypGroup * GroupDoc, long artindex)
{

    /* only try if in range.  All this work for these two lines 8^) */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        SelectHeader (Doc, header, (ART_SELECTED & ~header->SeenSelected) ? 1 : 0);
        return (ART_SELECTED & header->SeenSelected);
    }
    return (FALSE);
}

BOOL seen_false (TypDoc far * Doc, header_p headers,
            TypGroup * GroupDoc, long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        header->SeenSelected &= ~ART_SEENORKILLED;
        return (TRUE);
    }
    return (FALSE);
}

BOOL  seen_true (TypDoc far * Doc, header_p headers,
           TypGroup * GroupDoc, long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        header->SeenSelected |= ART_SEEN;
        return (TRUE);
    }
    return (FALSE);
}

BOOL selected_true (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        SelectHeader (Doc, header, 1);
        return (TRUE);
    }
    return (FALSE);
}

BOOL selected_false (TypDoc far * Doc, header_p headers,
                TypGroup * GroupDoc,
                long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        SelectHeader (Doc, header, 0);
        return (TRUE);
    }
    return (FALSE);
}

BOOL error_false (TypDoc far * Doc, header_p headers,
            TypGroup * GroupDoc, long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        header->SeenSelected &= ~ART_ERROR;
        return (TRUE);
    }
    return (FALSE);
}
BOOL error_true (TypDoc far * Doc, header_p headers,
            TypGroup * GroupDoc,
            long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        header->SeenSelected |= ART_ERROR;
        return (TRUE);
    }
    return (FALSE);
}

BOOL preview_true (TypDoc far * Doc, header_p headers,
               TypGroup * GroupDoc, long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        header->SeenSelected |= ART_PREVIEW;
        return (TRUE);
    }
    return (FALSE);
}

BOOL preview_false (TypDoc far * Doc, header_p headers,
                TypGroup * GroupDoc,
                long artindex)
{

    /* only try if in range. */
    if ((artindex > -1) && (artindex < GroupDoc->total_headers)) 
    {
        header_p header = header_elt (headers, artindex);
        header->SeenSelected &= ~ART_PREVIEW;
        return (TRUE);
    }
    return (FALSE);
}



#ifdef OFFLINE  // TANAKA Goh
BOOL delete_selected(TypDoc far *Doc, header_p headers, TypGroup *GroupDoc, long artindex)
{
    TypBlock far    *BlockPtr;
    TypLine far             *LinePtr;
    char far                *lpszGroupName;
    char                    mybuf[MAXINTERNALLINE];
    HANDLE                  hBlock;
    unsigned int    Offset;
    TypLineID               MyLineID;

    LockLine(Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &BlockPtr, &LinePtr);
    lpszGroupName = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypGroup);

    if (lstrcmp(CurrentGroup, lpszGroupName)) 
    {
        CommState = ST_GROUP_REJOIN;
        sprintf(mybuf, "GROUP %s", lpszGroupName);
        PutCommLine(mybuf);
        strntcpy(CurrentGroup, lpszGroupName, MAXGROUPNAME);
        strntcpy(CurrentDescription, GroupDoc->Description, MAXDESCRIPTION);
    }

    UnlockLine(BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);

    TypLineID               ActiveLineID = Doc->ActiveLineID;

    set_index_to_identity(GroupDoc->header_handle, GroupDoc->thread_handle, GroupDoc->total_headers);

    long int        total_headers = GroupDoc->total_headers;

    for (long int i = 0, j = 0; i < total_headers; i++) 
    {
        header_p        header = header_elt(headers, i);

        if (ART_SELECTED & header->SeenSelected) 
        {
            CommState = ST_DELETE;
            sprintf(mybuf, "DELETE %I64d", header->number);
            PutCommLine(mybuf);
            Doc->ActiveLines--;
            Doc->TotalLines--;
            GroupDoc->ServerEstNum--;
            GroupDoc->total_headers--;

            if (ActiveLineID > i) 
            {
            Doc->ActiveLineID--;
            }
        } 
        else 
        {
            header_p header_new = header_elt(headers, j++);
            memcpy(header_new, header, sizeof(TypHeader));
        }
    }

    if (Doc->TotalLines == 0) 
    {
        UpdateSeenArts(Doc);
    }

    if (Doc->ActiveLineID >= Doc->ActiveLines) 
    {
        Doc->ActiveLineID = Doc->ActiveLines - 1;
    }

    SendMessage(Doc->hDocWnd, WM_COMMAND, iSortOption, 0);
    AdjustTopScByDoc(Doc, Doc->ActiveLineID);
    //sprintf(mybuf, "%s (%u) %s", CurrentGroup, Doc->TotalLines, CurrentDescription);
    //SetWindowText(Doc->hWndFrame, mybuf);
	FormatNumber(Doc->TotalLines, mybuf);
    WVArgXSetWindowText(Doc->hWndFrame,IDS_GROUP_DESCRIPTION,CurrentGroup, mybuf, CurrentDescription);

    return TRUE;
}

BOOL sendme_selected(TypDoc far *Doc, header_p headers, TypGroup *GroupDoc, long artindex)
{
    TypBlock far    *BlockPtr;
    TypLine far             *LinePtr;
    char far                *lpszGroupName;
    char                    mybuf[MAXINTERNALLINE];
    HANDLE                  hBlock;
    unsigned int    Offset;
    TypLineID               MyLineID;

    LockLine(Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &BlockPtr, &LinePtr);
    lpszGroupName = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypGroup);

    if (lstrcmp(CurrentGroup, lpszGroupName)) 
    {
        CommState = ST_GROUP_REJOIN;
        sprintf(mybuf, "GROUP %s", lpszGroupName);
        PutCommLine(mybuf);
        strntcpy(CurrentGroup, lpszGroupName, MAXGROUPNAME);
        strntcpy(CurrentDescription, GroupDoc->Description, MAXDESCRIPTION);
    }

    UnlockLine(BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);

    for (int i = 0, j = 0; i < GroupDoc->total_headers; i++) 
    {
        header_p        header = header_elt(headers, i);

        if (ART_SELECTED & header->SeenSelected) 
        {
            //CommState = ST_SENDME;
            TypSendmeList * SMEptr = new TypSendmeList;
            if (SMEptr) 
            {
                SMEptr->next = GroupDoc->pSME;
                SMEptr->article = header->number;
                GroupDoc->pSME = SMEptr;
            }
            sprintf(mybuf, "SENDME %I64d", header->number);
            PutCommLine(mybuf);
            Doc->SelectedLines--;
            header->SeenSelected = header->SeenSelected & ~ART_SELECTED;
        }
    }

    return TRUE;
}
#endif

/*-- function ViewArticle -------------------------------------------------
 *
 *  View a given article.   Either create a new window for it or
 *  recycle an existing window.
 *  This function requests an article from the server, so there
 *  must not already be a transaction in progress.
 *  Can retrieve an article by artindex from the group header list,
 *  or any arbitrary article ID.
 *
 *    Entry    Doc            points to the document for this group.
 *             artindex       index into header array for this group.
 *             reuse          is TRUE if we ought to reuse the
 *                            currently active article window (if any).
 *             showArt        is TRUE to retrieve maximized (shown)
 *                            is FALSE to retrieve minimized
 *             articleId      is NO_ID if wish to retrieve artindex->number
 *                            is some art ID if wish to retrieve article by ID
 */
void ViewArticle (TypDoc far *Doc, long artindex, BOOL reuse, BOOL showArt, char far *articleId)
{
    TypGroup far *GroupDoc;
    BOOL newdoc;
    BOOL found;
    int docnum = 0;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    HWND hWndArt;
    int x, y, width, height, num;
    char mybuf[32];
    //WVString sTitle;
    const char far *lpsz;
    HWND hWndGroup = Doc->hDocWnd;
    HANDLE header_handle;
    HANDLE thread_handle;
    header_p headers;
    header_p header = NULL;
    HMENU hMenu, hSubMenu;
    char  FromAddress[MAXHEADERLINE];
    char  StripAddress[MAXDIALOGSTRING];   // JD 3/8/97
    char  StripName[MAXDIALOGSTRING];      // JD 3/8/97
	BOOL  bNoLock = (artindex < 0 && articleId != NO_ID);

    
	if (bNoLock) //we're fetching an article by ID possibly with no doc index or group
	{
		if (TestCommBusy (hWndGroup, IDS_TEST_RETRIEVE_ARTICLE))
			return;

        char headerLine[MAXHEADERLINE];

		//Do we already have that article?
		found = FALSE;
		for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++) 
		{

			if ( ArticleDocs[docnum].InUse &&
				 GetInReplyTo (headerLine,MAXHEADERLINE, &ArticleDocs[docnum]) &&
				!_stricmp(articleId, headerLine)	)
			{

				ActiveArticleDoc = CommDoc = &(ArticleDocs[docnum]);
				found = TRUE;
				break;

			}

		}
		if (found)
		{
            setArticleFocus (ActiveArticleDoc->hWndFrame);
            ShowWindow (ActiveArticleDoc->hWndFrame, SW_SHOWNORMAL);
			SendMessage (hWndGroup, (UINT) WM_COMMAND, (WPARAM) ID_ARTICLE_RETRIEVE_COMPLETE, 0L);

			return;

		}

        if (1) 
        {
            char poschars[MAXINTERNALLINE];

            /* Compute default screen position. */
            num = (WrapIncomingArticleText) ? WrapIncomingArticleTextLength : 88;
            num = min (max (15, WrapIncomingArticleTextLength), 88);
            if (xScreen > num * (int) ArtCharWidth) 
            {
                width = num * ArtCharWidth;
            }
            else 
            {
                width = xScreen - 1 * ArtCharWidth;
            }
            x = xScreen - width;
            y = (int) (yScreen * 3 / 8);
            height = (int) (yScreen * 5 / 8) - (1 * ArtLineHeight);

            /* If the screen position has been saved, use that instead. */
            GetPrivateProfileString (ARTICLE, "ArticleWindowPos", "!",
                                    poschars, MAXINTERNALLINE, szAppProFile);
            if (poschars[0] != '!') 
            {
                sscanf (poschars, "%d,%d,%d,%d", &x, &y, &width, &height);
            }

            hWndArt = CreateWindow ("WinVnArtFrame",
                                    "",
                                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                                    x + (docnum * ArtCharWidth),
                                    y + (docnum * ArtLineHeight),
                                    width,
                                    height,
                                    NULL,
                                    NULL,
                                    hInst,
                                    NULL);

            if (!hWndArt)
                return;                   /* ??? */
            WVArgXSetWindowText(hWndArt,IDS_WVGROUP_RETRIEVE, articleId);
        }
        else 
        {
            hWndArt = CommDoc->hWndFrame;
            WVArgXSetWindowText(hWndArt,IDS_WVGROUP_RETRIEVE, articleId);
        }

        /*  Now that we have created the window, create the corresponding
        *  document, and make the new window active.
        */

        InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE);

#ifdef JAPANESE
        InitDoc (GetArtRec(CommDoc), hWndArt, Doc, DOCTYPE_ARTICLE);
#endif

        CommDoc->InUse = TRUE;
        CommDoc->LastSeenLineID = artindex;   /* Keep an index with the article */


		CommLinePtr = CommLineIn;
		CommBusy = TRUE;
		CommState = ST_ARTICLE_RESP;
		

		sprintf (headerLine, "ARTICLE %s", articleId);
        PutCommLine (headerLine);
		return;




	}
	
	
	if (LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
                &BlockPtr, &LinePtr)) 
    {

        Doc->ActiveLineID = artindex;
        GroupDoc = GetGroup(LinePtr);

        if (articleId == NO_ID)     /* if retrieving artindex, only try if in range */
            if ((artindex < 0) || (artindex >= GroupDoc->total_headers))
            {
                UnlockLine (BlockPtr, LinePtr, &Doc->hParentBlock,
                &Doc->ParentOffset, &Doc->ParentLineID);
                return;
            }

        header_handle = GroupDoc->header_handle;
        thread_handle = GroupDoc->thread_handle;
        headers = lock_headers (header_handle, thread_handle);
        header = header_elt (headers, artindex); 

        if ((articleId == NO_ID) &&
            (header->ArtDoc != NULL) &&
            (header->ArtDoc->CountedLines == header->lines))
        { 

            /* We already have a document containing the article */
            /* so just activate it.                */

            if (showArt) 
            {
                setArticleFocus (header->ArtDoc->hWndFrame);
                ShowWindow (header->ArtDoc->hWndFrame, SW_SHOWNORMAL);
            }
            else
                ShowWindow (header->ArtDoc->hWndFrame, SW_SHOWMINNOACTIVE);

            InvalidateRect (header->ArtDoc->hWndFrame, NULL, FALSE);
            InvalidateRect (header->ArtDoc->hDocWnd, NULL, FALSE);
            SendMessage (hWndGroup, (UINT) WM_COMMAND, (WPARAM) ID_ARTICLE_RETRIEVE_COMPLETE, 0L);
            goto endit;
        } 

        if (TestCommBusy (hWndGroup, IDS_TEST_RETRIEVE_ARTICLE))
            goto endit;

        newdoc = FALSE;

        if ((NewArticleWindow && !reuse) || !ActiveArticleDoc || 
            !(ActiveArticleDoc->InUse)) 
        {
            found = FALSE;
            for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++) 
            {
                if (!ArticleDocs[docnum].InUse) 
                {
                    found = TRUE;
                    newdoc = TRUE;
                    ActiveArticleDoc = CommDoc = &(ArticleDocs[docnum]);
                    break;
                }
            }
            if (!found) 
            {
                WVMessageBox(hWndGroup, IDS_WVGROUP_TOO_MANY, IDS_WVGROUP_CANT_OPEN_WIN,
                MB_OK | MB_ICONASTERISK);
                goto endit;
            }
        }
        else 
        {
            /* Must reuse old window for this article.         */
            ActiveArticleDoc->LongestLine = 0;
            ActiveArticleDoc->ScXOffset = 0;
            ActiveArticleDoc->TextSelected = FALSE;
            SetArticleMenus (ActiveArticleDoc, DISABLE);
            CommDoc = ActiveArticleDoc;
            if (ActiveArticleDoc->hWndXFace)
            {
                DestroyWindow(ActiveArticleDoc->hWndXFace);
                ActiveArticleDoc->hWndXFace = NULL;
            }
            /* sever the article/artindex connection */
            if (CommDoc->ParentDoc == Doc)
                (header_elt (headers, CommDoc->ParentOffset))->ArtDoc = (TypDoc far *) NULL;
            else 
            {
                SeverArticleParent(CommDoc);
            }

            /* clear out old doc and clear its window*/
            FreeDoc (CommDoc);
            InvalidateRect(CommDoc->hDocWnd, NULL, FALSE);
        }

        if (!bNoLock)
		{
			header->SeenSelected |= ART_SEEN;
			// header->SeenSelected &= ~ART_SELECTED; /* buggy!!  SelectedLines isn't maintained */
			SelectHeader(Doc,header,0);

			NewsrcDirty = TRUE;
			InvalidateRect (hWndGroup, NULL, FALSE);
		}

        if (articleId == NO_ID)
            lpsz = header->pHD->sSubject;
        else
            lpsz = articleId;

        //sTitle = "Retrieving \"";
        //sTitle += header->pHD->sSubject;
        //sTitle += "\"";

        if (newdoc) 
        {
            char poschars[MAXINTERNALLINE];

            /* Compute default screen position. */
            num = (WrapIncomingArticleText) ? WrapIncomingArticleTextLength : 88;
            num = min (max (15, WrapIncomingArticleTextLength), 88);
            if (xScreen > num * (int) ArtCharWidth) 
            {
                width = num * ArtCharWidth;
            }
            else 
            {
                width = xScreen - 1 * ArtCharWidth;
            }
            x = xScreen - width;
            y = (int) (yScreen * 3 / 8);
            height = (int) (yScreen * 5 / 8) - (1 * ArtLineHeight);

            /* If the screen position has been saved, use that instead. */
            GetPrivateProfileString (ARTICLE, "ArticleWindowPos", "!",
                                    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;	
			}            

            hWndArt = CreateWindow ("WinVnArtFrame",
                                    "",
                                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                                    x + (docnum * ArtCharWidth),
                                    y + (docnum * ArtLineHeight),
                                    width,
                                    height,
                                    NULL,
                                    NULL,
                                    hInst,
                                    NULL);

            if (!hWndArt)
                return;                   /* ??? */
            WVArgXSetWindowText(hWndArt,IDS_WVGROUP_RETRIEVE, lpsz);
        }
        else 
        {
            hWndArt = CommDoc->hWndFrame;
            WVArgXSetWindowText(hWndArt,IDS_WVGROUP_RETRIEVE, lpsz);
        }

        /*  Now that we have created the window, create the corresponding
        *  document, and make the new window active.
        */

        InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE);

#ifdef JAPANESE
        InitDoc (GetArtRec(CommDoc), hWndArt, Doc, DOCTYPE_ARTICLE);
#endif

        CommDoc->InUse = TRUE;
        CommDoc->LastSeenLineID = artindex;   /* Keep an index with the article */

        if (showArt) 
        {
            setArticleFocus (hWndArt);
            ShowWindow (hWndArt, newdoc?SW_SHOWNORMAL:SW_SHOW);
        }
        else 
        {
            ShowWindow (hWndArt, SW_SHOWMINNOACTIVE);
        }

        if (articleId == NO_ID) 
        {
            if (header)
			header->ArtDoc = CommDoc;
        } 
        else 
        {
            if (header)
			header->ArtDoc = (TypDoc *)NULL;
        }
        
        CommDoc->ParentOffset = (int) artindex;

        /* stash Lines: header in CountedLines */
		if (header)
        CommDoc->CountedLines = header->lines;
        CommDoc->NextTopLine = 0; //yoshida 
        InvalidateRect (hWndArt, NULL, FALSE);
        UpdateWindow (hWndArt);

        if (articleId == NO_ID) 
        {
            //  if (header->number > GroupDoc->HighestPrevSeen) {
            //    GroupDoc->HighestPrevSeen = header->number;
            //  }
            sprintf (mybuf, "%I64d", header->number);
            InitiateReceiveArticle (Doc, mybuf);
        }
        else
            InitiateReceiveArticle (Doc, articleId);

        /* decide if we are the author and allowed to cancel this post */
        hMenu = GetMenu (hWndArt); 
        hSubMenu = GetSubMenu(hMenu,0);

        // allow cancel article even FullNameFrom is not selected (TANAKA Goh   1996/09/20)
        // Adopted to new TypHeader struct (Masaki Ishido 1997/01/15)

        GetFromAddress(FromAddress, MAXHEADERLINE, NULL);
        ParseAddress(FromAddress, StripAddress, MAXDIALOGSTRING, StripName, MAXDIALOGSTRING);

        if (!bNoLock)
		{
		if (FullNameFrom) 
        {
            //       ParseAddress(FromAddress, AddressString, MAXDIALOGSTRING, NameString, MAXDIALOGSTRING);
            //  if ((stricmp (header->pHD->sFrom, UserName) == 0) ||
            //      (stricmp (header->pHD->sFrom, MailAddress) == 0) ||
            //      (stricmp (header->pHD->sFrom, ReplyTo) == 0)) 
            //     EnableMenuItem(hSubMenu,IDM_CANCELART,ENABLE_MENU);
            //  else
            //     EnableMenuItem(hSubMenu,IDM_CANCELART,DISABLE_MENU);

            if (_stricmp(header->pHD->sFrom, NameString) == 0 ||
               _stricmp(header->pHD->sFrom, StripName) == 0)
			{
                EnableMenuItem(hSubMenu, IDM_CANCELART, ENABLE_MENU);
                EnableMenuItem(hSubMenu, IDM_SUPERSEDE, ENABLE_MENU);
				
			}
            else
			{
                EnableMenuItem(hSubMenu, IDM_CANCELART, DISABLE_MENU);
                EnableMenuItem(hSubMenu, IDM_SUPERSEDE, DISABLE_MENU);
			}
        }
        else 
        {
            if (_stricmp(header->pHD->sFrom, FromAddress) == 0
               || _stricmp(header->pHD->sFrom, StripAddress) == 0)
			{
                EnableMenuItem(hSubMenu, IDM_CANCELART, ENABLE_MENU);
                EnableMenuItem(hSubMenu, IDM_SUPERSEDE, ENABLE_MENU);
			}
            else
			{
                EnableMenuItem(hSubMenu, IDM_CANCELART, DISABLE_MENU);
                EnableMenuItem(hSubMenu, IDM_SUPERSEDE, DISABLE_MENU);
			}
        }
		}

endit:
        if (!bNoLock)
		{
			UnlockLine (BlockPtr, LinePtr, &Doc->hParentBlock, &Doc->ParentOffset, &Doc->ParentLineID);
			unlock_headers (header_handle, thread_handle);
		}
    }
}

void setArticleFocus (HWND hWndArt)
{
  SetArticleRot13Mode (hWndArt, FALSE);
  SetActiveWindow (hWndArt);
  SetFocus (hWndArt);
}


void WaitForBuggyNntpd()
{
    unsigned i,j;

    for (i=0; i < NntpdBugWait; i++)
        for (j=0; j < 65534; j++);
}

void InitiateReceiveArticle (TypDoc far * Doc, char far * articleId)
{
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    TypGroup far *GroupDoc;
    char far *lpszGroupName;
    char mybuf[MAXINTERNALLINE];
    HANDLE hBlock;
    unsigned int Offset;
    TypLineID MyLineID;

    CommLinePtr = CommLineIn;


    // kludge to update the toolbar (deactivate the comm stuff)
    if (!IsMinimized(Doc->hWndFrame)) 
    {
        SendMessage (Doc->hWndFrame, WM_MYINITMENU, 0, 0L);
    }

    if (!IsMinimized(CommDoc->hWndFrame)) 
    {
        SendMessage (CommDoc->hWndFrame, WM_MYINITMENU, 0, 0L);
    }

    if (LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
                &BlockPtr, &LinePtr))
    {

        /* If we're not already in this group on the server,
        * send out a GROUP command for this window so we get back
        * into the right Group.
        */
        lpszGroupName = GetGroupName(LinePtr);
        GroupDoc = GetGroup(LinePtr);
        if (lstrcmp (CurrentGroup, lpszGroupName)) 
        {
            
			//CommState = ST_GROUP_REJOIN;
            //strcpy (mybuf, "GROUP ");
            //lstrcat (mybuf, lpszGroupName);
            
			strntcpy (CurrentGroup, lpszGroupName, MAXGROUPNAME);
            strntcpy(CurrentDescription, GroupDoc->Description, MAXDESCRIPTION);
			InlineGroup(CurrentGroup ); 

            //PutCommLine (mybuf);
            // if running nntpd under inetd there is a bug
            // if the server give a reply ALL outstanding input on the server is discarted
            // so we should wait here if we have such a server
            //WaitForBuggyNntpd();
        }

        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);

        strncpy(CurrentArticleID, articleId, MAXARTICLEIDLEN); //save this in case we get a 412 later
		sprintf (mybuf, "ARTICLE %s", articleId);
		CommBusy = TRUE;
		CommState = ST_ARTICLE_RESP;
		if (CommDoc)
			WVSetStatbarText (CommDoc->hWndFrame, ST_ARTICLE_RESP, CommDoc, TRUE, TRUE);
        if (!PutCommLine (mybuf)) //if the server has disconnected but the we don't know it, this will cause it to disconnect
		{
			int x = 1;
		}
#if 0 // defined(OFFLINE) S.Yoshida
    {
        MSG msg;
        if (!PeekMessage(&msg, NetDoc.hDocWnd, WM_TIMER, WM_TIMER, PM_NOREMOVE)) 
        {
            PostMessage (NetDoc.hDocWnd, WM_TIMER, ID_TIMER, 0 );
        }
    }
#endif
    }
}


/* Clear the pointer in the line for this article in the   */
/* group  document.  This pointer currently points       */
/* to the current document, which we are wiping out      */
/* with the destruction of this window.                  */
void SeverArticleParent(TypDoc *doc)
{
    TypGroup far *GroupDoc;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    HANDLE header_handle;
    HANDLE thread_handle;
    HANDLE hBlock;
    header_p headers;
    TypLineID MyLineID;
    unsigned int Offset;

    if (doc->ParentDoc &&
        LockLine (doc->ParentDoc->hParentBlock,
                  doc->ParentDoc->ParentOffset,
                  doc->ParentDoc->ParentLineID,
        &BlockPtr, &LinePtr)) 
    {

        GroupDoc = GetGroup(LinePtr);
        header_handle = GroupDoc->header_handle;
        thread_handle = GroupDoc->thread_handle;
        headers = lock_headers (header_handle, thread_handle);

        (header_elt (headers, doc->LastSeenLineID))->ArtDoc = (TypDoc *) NULL;
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
        unlock_headers (header_handle, thread_handle);
    }
}

/*-- Function UnlinkArtsInGroup ---------------------------------------
 *
 *  Modify all the article documents and all the article windows currently
 *  associated with a group so that none of them points to that group.
 *  Used when the group window is going away or is being recycled.
 *
 *    Entry    GroupDoc    points to the document to which references
 *                         should be eliminated.
 */
void UnlinkArtsInGroup (TypDoc far *GroupDoc)
{
    int iart;

    for (iart = 0; iart < MAXARTICLEWNDS; iart++) 
    {
        if (ArticleDocs[iart].InUse && ArticleDocs[iart].ParentDoc == GroupDoc) 
        {
            ArticleDocs[iart].ParentDoc = (TypDoc far *) NULL;
            ArticleDocs[iart].hParentBlock = 0;
        }
    }
}

/*--- function UpdateSeenArts -------------------------------------------
 *
 *  Given a Group document, update the TypGroup line for
 *  that document in the Net document with respect to which
 *  articles have been seen.
 *  This routine would typically be called just before a Group document
 *  is going to be destroyed or erased.  That would be the time to
 *  take the information in the TypArticle structures of each line
 *  in the document and transfer it to the line in the NetDoc document
 *  corresponding to this group.
 *
 *  This routine has to take information of the form:
 *    123:Unseen;  124:Seen; 125:Unseen; 126:Unseen; 127:Seen; 128:Seen; 129:Seen
 *  found in the TypArticle structures in consecutive lines in the document
 *  and transform it to the general form used by .newsrc files:
 *    124,127-129
 *  (though we are using our internal representation & not ASCII characters).
 *
 *    Entry    Doc      points to the document for this group.
 *
 *    Exit     The line in the Net document corresponding to this
 *              group has been updated.
 */
void UpdateSeenArts (TypDoc far *Doc)
{
    TypGroup *group;
    TypLine far *ParentLine;
    TypBlock far *ParentBlock;
    HANDLE header_handle, thread_handle;
    header_p headers;
    header_p header;
    BOOL GotLock;
    long artindex;

    /*  Get the line in the Net document that corresponds to this
    *  group.  Make a local copy of it and set RangePtr to point to
    *  the first range in that line.  We will ignore the old line's
    *  "seen" data and create the information afresh from what we
    *  have in this document.
    */
    GotLock = LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
                &ParentBlock, &ParentLine);

    if(!GotLock)
        return;

    group = GetGroup(ParentLine);

    if (group->max_headers > 0)
    {
        header_handle = group->header_handle;
        thread_handle = group->thread_handle;

        if (!header_handle) // this can happen if user selects "Update from server" then cancel.
            return;

        set_index_to_identity (header_handle, thread_handle, group->max_headers);
        headers = lock_headers (header_handle, thread_handle);

        artindex = 0;
        header = (header_elt (headers, artindex));

        do
        {
            header = (header_elt (headers, artindex));
            
			if(ART_SEENORKILLED & header->SeenSelected)
            {
                group->pSeendb->MarkSeen(header->number);
            }
            else
            {
                group->pSeendb->MarkUnseen(header->number);
            }
        } while(++artindex < group->max_headers);   //was Doc->TotalLines -Want killed arts marked too
        unlock_headers (header_handle, thread_handle);
    }
    group->NumUnread = CalcNumUnread(group);

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

    GlobalUnlock (ParentBlock->hCurBlock);
    NewsrcDirty = TRUE;
}



/*-- 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.
 */
long NewCursorToTextLine (int X, int Y, TypDoc far *DocPtr)
{
    int SelLine;
    long result;

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

long search_headers (TypDoc far * HeaderDoc, header_p headers,
                    long artindex, long num_headers, BOOL bReverse)
{
    char_p temp;

    do 
    {
        temp = (char*)(const char*)(header_elt (headers, artindex))->pHD->sSubject;
        if (string_compare_insensitive (temp, HeaderDoc->SearchStr))
            return (artindex);        /* return the index */
    } while ( (bReverse ? (--artindex >= 0) : (artindex++ < (num_headers - 1)) ));

    return (-1);                  /* not found */
}

char_p string_compare_insensitive (char_p a, char far * b)
{
    int lena = lstrlen (a);
    int lenb = lstrlen (b);
    int count;

    for (count = lena - lenb + 1; count > 0; count--, a++)
        if (_strnicmp (a, b, lenb) == 0)
            return a;

    return (NULL);
}


BOOL AffectOneSelected (TypDoc far * Doc, header_p ThisHeader, long index, int value, int compare)
{
    char_p temp;
    static BOOL extending = FALSE;

    switch (compare) 
    {
        case COMPARE:
            //    temp = (char*)(const char*)ThisHeader->pHD->sSubject;
            temp = (char huge*)(const char huge*)ThisHeader->pHD->sSubject;
            if (string_compare_insensitive (temp, Doc->SearchStr)) 
            {
                SelectHeader (Doc, ThisHeader, value);
                return TRUE;
            }
            break;

        case NO_COMPARE:
            SelectHeader (Doc, ThisHeader, value);
            return TRUE;
            break;

        case ADDSELECT:
            if (index == Doc->ActiveLineID || index == Doc->AnchorLineID) 
            {
                if (extending == TRUE) 
                {
                    SelectHeader (Doc, ThisHeader, value);
                    extending = FALSE;
                }
                else 
                {
                    SelectHeader (Doc, ThisHeader, value);
                    if (Doc->ActiveLineID == Doc->AnchorLineID)
                        extending = FALSE;
                    else
                        extending = TRUE;
                }
            }
            else if (extending)
                SelectHeader (Doc, ThisHeader, value);

            return TRUE;
            break;
        case EXTEND:
            if (index == Doc->ActiveLineID || index == Doc->AnchorLineID) 
            {
                if (extending == TRUE) 
                {
                    SelectHeader (Doc, ThisHeader, 1);
                    extending = FALSE;
                }
                else 
                {
                    SelectHeader (Doc, ThisHeader, 1);
                    if (Doc->ActiveLineID == Doc->AnchorLineID)
                        extending = FALSE;
                    else
                        extending = TRUE;
                }
            }
            else
                SelectHeader (Doc, ThisHeader, extending);

            return TRUE;
            break;
    }
    return FALSE;
}

long AffectSelected (TypDoc far * Doc, int value, int compare)
{
    TypGroup far *group;
    HANDLE header_handle, thread_handle;
    header_p headers, ThisHeader;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    long index;
    long num_affected = 0;

    LockLine (Doc->hParentBlock, Doc->ParentOffset,
        Doc->ParentLineID, &BlockPtr, &LinePtr);
    group = GetGroup(LinePtr);
    header_handle = group->header_handle;
    thread_handle = group->thread_handle;
    if (header_handle) 
    {
        headers = lock_headers (header_handle, thread_handle);

        for (index = 0 ; index < group->total_headers && headers; index++) 
        {
            ThisHeader = header_elt (headers, index);

            if (ThisHeader && AffectOneSelected (Doc, ThisHeader, index, value, compare)) 
            {
                num_affected++;
            }
        }

        InvalidateRect (Doc->hDocWnd, NULL, FALSE);
    }
    return (num_affected);
}

long AffectSelectedOnScreen (TypDoc far * Doc, int value, int compare)
{
    TypGroup far *group;
    HANDLE header_handle, thread_handle;
    header_p headers, ThisHeader;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    long index;
    long num_affected = 0 ;
    long VertLines;

    LockLine (Doc->hParentBlock, Doc->ParentOffset,
                Doc->ParentLineID, &BlockPtr, &LinePtr);
    group = GetGroup(LinePtr);
    header_handle = group->header_handle;
    thread_handle = group->thread_handle;
    if (header_handle) 
    {
        headers = lock_headers (header_handle, thread_handle);

        VertLines = (Doc->ScYLines > (Doc->TotalLines - Doc->TopLineOrd))
                    ? (Doc->TotalLines - Doc->TopLineOrd) : Doc->ScYLines;

        for (index = Doc->TopLineOrd; VertLines > 0; index++, VertLines--) 
        {
            ThisHeader = header_elt (headers, index);

            if (ThisHeader && AffectOneSelected (Doc, ThisHeader, index, value, compare)) 
            {
                num_affected++;
            }
        }
        InvalidateRect (Doc->hDocWnd, NULL, FALSE);
    }
    return (num_affected);
}

void SetGroupSelections (TypDoc * MyDoc, header_p headers, long newActive)
{
    long prevActive = MyDoc->ActiveLineID;
    header_p ThisHeader = header_elt (headers, newActive);

    MyDoc->ActiveLineID = newActive;

    if (GetKeyState (VK_SHIFT) >= 0) 
    {    /* shift is NOT pressed  */
        /* clear select status of all lines */
        
		if (ArtListMultiSelect)
		{

			if (MyDoc->SelectedLines == 1) 
			{
				article_operation (MyDoc, prevActive, selected_false);
			}
			else 
			{
				AffectSelected (MyDoc, DESELECT, NO_COMPARE);
			}
			/* set only current line selected */
			SelectHeader (MyDoc, ThisHeader, 1);
		}
        /* set the current line to be the anchor */
        MyDoc->AnchorLineID = MyDoc->ActiveLineID;
    }
    else 
    {                        /* shift is pressed  */
        /* select all lines between ActiveLineID and AnchorLineID inclusive */
        if (ArtListMultiSelect)
			AffectSelected (MyDoc, DESELECT, EXTEND);
    }
    InvalidateRect (MyDoc->hDocWnd, NULL, FALSE);
}

/* Ray Dunn:99/10: Resort */
void ResortGroup(HWND hWnd)
{
    SendMessage(hWnd, WM_DISPLAY_SORTED, 0, 0);
}
/* Ray Dunn:99/10:End */

/////////////////////////////////////////////////////////////////////////////
// ReadString
// Read a line of a file into the supplied buffer. Terminate of end of file,
// newline or carrige return/newline.
// Returns:
//  a pointer to the string, or NULL if no characters were read
//
bool ReadStr( HANDLE hFile, char * lpsz, unsigned long nMax, unsigned long *pdwActual )
{

    unsigned long dwRead =0;
    char * p;
    unsigned long dwPos;
	unsigned long i;
    
    if (!lpsz || hFile == INVALID_HANDLE_VALUE)
        return NULL;

    dwPos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
    
    if ( !ReadFile(hFile, lpsz, nMax, &dwRead, NULL) || dwRead == 0 )
    {
        return false;
    }
    
    
    p = lpsz;
    
    for ( i = 0; i < dwRead; i++, p++)
    {
        
        if (*p == '\x0a' || *p == '\x0d')
        {
            *p = '\0';
			dwPos = dwPos + i+1;

            if (i < nMax-1)
            {
                if (*(p+1) == '\x0a')
				{
					dwPos++;
					*(lpsz+i+1) = '\0';
				}
                
            }
            //rewind file past end-of-line
            SetFilePointer(hFile, dwPos, NULL, FILE_BEGIN);

            break;
        }
       
    } 
    *(lpsz+i) = '\0';
    if (pdwActual)
        *pdwActual = i;
  
    return true;   

}
#define MAX_BUF 8192

BOOL CountLines( HANDLE hFile, int& nLines, int &nMaxLen )
{

	BYTE pBuf[MAX_BUF+1];
	int lastNL = 0;
	DWORD dwSize, dwRead = 0, dwToRead, dwBytes, dwLast=0;
	BOOL bOK = TRUE;

	nLines = 0;
	nMaxLen = 0;

	if ( hFile == INVALID_HANDLE_VALUE)
		return NULL;

	dwSize = GetFileSize(hFile, NULL);
	if ( dwSize == 0xFFFFFFFF)
		return FALSE;

	if (!dwSize)
		return TRUE;


	SetFilePointer(hFile, 0, NULL, FILE_BEGIN);

	while (dwRead < dwSize)
	{
		if (dwRead + MAX_BUF < dwSize)
			dwToRead = MAX_BUF;
		else
			dwToRead = dwSize - dwRead;
		
		if (!ReadFile( hFile, pBuf, dwToRead, &dwBytes, NULL) || dwBytes != dwToRead)
		{
			//error reading from file
			bOK = FALSE;
		}
		else
		{
			
			for (UINT i = 0; i < dwBytes; i++)
			{
				if (pBuf[i] == '\r')
					continue;

				if (pBuf[i] == '\n')
				{
					nLines++;
					dwLast = dwRead + i;

					if (lastNL > nMaxLen)
						nMaxLen = lastNL; 
					lastNL = 0;

				}
				else
					lastNL++;

			}
			dwRead += dwBytes;

			if (dwRead >= dwSize && dwLast != dwSize)
			{	
				lastNL = dwSize-dwLast;
				if (lastNL > nMaxLen)
					nMaxLen = lastNL;
				nLines++;
			}
		}


	}

	return bOK;

}



#define MAXLINESIZE 256

long SelectLoadArtList( TypDoc far * Doc, HANDLE hFile, long * pnTotal /*=NULL*/)
{

    TypGroup far *group;
    HANDLE header_handle, thread_handle;
    header_p headers, ThisHeader;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    long index;
    long num_affected = 0;
	long nCount = 0;
	char scratch_line[MAXINTERNALLINE];
	DWORD dwRead = 0;

	UINT *pList = NULL;

	int nLines = 0, nMaxLen = 0;

	//see what we're dealing with
	if (! CountLines( hFile, nLines, nMaxLen ) || nLines == 0)
		return FALSE;

	SetFilePointer(hFile, 0, NULL, FILE_BEGIN);

	//make space for list
	pList = new UINT[nLines];
	
	if (!pList)
		return FALSE;

	//read selection
	while (ReadStr(hFile, scratch_line, MAXINTERNALLINE, &dwRead) && nCount < nLines)
	{
		char *p = scratch_line;
		while ( *p && isspace((unsigned char)*p))
			p++;

		if (isdigit(*p))
		{
			pList[nCount++] = atol(p);	
		}

	}


    LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &BlockPtr, &LinePtr);
    group = GetGroup(LinePtr);
    header_handle = group->header_handle;
    thread_handle = group->thread_handle;
    
	if (header_handle) 
    {
        headers = lock_headers (header_handle, thread_handle);

        for (index = 0 ;  index < group->total_headers && headers; index++) 
        {
            ThisHeader = header_elt (headers, index);

			//see if this art should be selected
			for (long i = 0; i < nCount; i++)
			{
				if (ThisHeader->number == pList[i] )
				{
					SelectHeader (Doc, ThisHeader, 1);
					num_affected++;
					break;
				}
			}

		}

        unlock_headers (header_handle, thread_handle);
    }

	delete [] pList;
	if (pnTotal)
		*pnTotal = nCount;

	return num_affected;



}

long SaveArtList( TypDoc far * Doc, HANDLE hFile, BOOL bSelected)
{


    TypGroup far *group;
    HANDLE header_handle, thread_handle;
    header_p headers, ThisHeader;
    TypBlock far *BlockPtr;
    TypLine far *LinePtr;
    long index;
    long num_affected = 0;
	char scratch_line[MAXINTERNALLINE];
	DWORD dwWrote;
	BOOL bOK = TRUE;
	BOOL bHeader = FALSE;


    LockLine (Doc->hParentBlock, Doc->ParentOffset,
        Doc->ParentLineID, &BlockPtr, &LinePtr);
    group = GetGroup(LinePtr);
    header_handle = group->header_handle;
    thread_handle = group->thread_handle;
    if (header_handle) 
    {
        headers = lock_headers (header_handle, thread_handle);

		if (SetFilePointer(hFile, 0, NULL, FILE_END) == 0)
			bHeader = TRUE;

		wsprintf(scratch_line, "Group: %s\r\n", GetGroupName(LinePtr)); 
		bOK = WriteFile(hFile, scratch_line, strlen(scratch_line), &dwWrote, NULL);
		
		
		if (bHeader)
		{
			DWORD dwWrote;
			LPCSTR szHeader = "Article#\tMessage-ID\tDate & Time\tAuthor\tLines\tSubject\r\n";
			WriteFile(hFile, szHeader, strlen(szHeader), &dwWrote, NULL);
		}
		 

        for (index = 0 ; bOK && index < group->total_headers && headers; index++) 
        {
            ThisHeader = header_elt (headers, index);

			if( !bSelected || (ART_SELECTED & ThisHeader->SeenSelected))
			{

			   //art num
			   BIGINTtoa(ThisHeader->number, scratch_line, 10);
			   bOK = WriteFile(hFile, scratch_line, strlen(scratch_line), &dwWrote, NULL);
			   
			   num_affected++; 
			   if (bOK)
			   {
					//MessageID 
					*scratch_line = '\t';
					*(scratch_line+1) = '<';
					strncpy (scratch_line+2, ThisHeader->pHD->sMessage_id, sizeof(scratch_line)-3);
					strcat(scratch_line, ">");
					bOK = WriteFile(hFile, scratch_line, strlen(scratch_line), &dwWrote, NULL); 
				}


			   if (bOK)
			   {
					//date 
					*scratch_line = '\t';
					StringDate (scratch_line+1, ThisHeader->pHD->date);
					bOK = WriteFile(hFile, scratch_line, strlen(scratch_line), &dwWrote, NULL); 
				}


				//Author
				if (bOK)
				{ //ishido DBCS aware author name trimming
					const char *base_p = ThisHeader->pHD->sFrom;
					const char *p = base_p;
					while ((*p != '\0') && (p - base_p < sizeof(scratch_line)-1))
						p = AnsiNext(p);
					strncpy(scratch_line+1, (const char*)ThisHeader->pHD->sFrom, (p - base_p) + 1);
					scratch_line[p+1 - base_p] = '\0';
					*scratch_line = '\t';
					bOK = WriteFile(hFile, scratch_line, strlen(scratch_line), &dwWrote, NULL); 

				}


				//# lines
				if (bOK)
				{
					int len;
					*scratch_line = '\t';
					_ltoa(ThisHeader->lines, &(scratch_line[1]), 10);
					len = strlen(scratch_line);
					scratch_line[len] = '\t';
					scratch_line[len+1] = '\0';
					bOK = WriteFile(hFile, scratch_line, strlen(scratch_line), &dwWrote, NULL); 
				}


				//Subject
				if (bOK)
				{
					bOK = WriteFile(hFile, (LPCSTR) ThisHeader->pHD->sSubject, strlen((LPCSTR) ThisHeader->pHD->sSubject), &dwWrote, NULL); 
				}

				if (bOK)
				{
					bOK = WriteFile(hFile, "\r\n",2, &dwWrote, NULL); 
				}

			}

        }

        unlock_headers (header_handle, thread_handle);
    }
	return bOK ? num_affected : -num_affected;


}


LRESULT CALLBACK  WinVnViewFrameWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    TypDoc *ThisDoc = NULL;
    int ih, i, j;
    UINT IdString;
    BOOL found;
    int skipSave = 0;
    TBBUTTON tbButton[20];
    RECT MyRect;
    TypGroup *GroupDoc;
    TypBlock far *BlockPtr;
    HANDLE hBlock;
    TypLine far *LinePtr;
    unsigned int Offset;
    TypLineID MyLineID;
    long found_artindex;
    BOOL continueFind, threadOk;
    char mybuf[MAXINTERNALLINE];
    HMENU hMenu, hSubMenu;
    PAINTSTRUCT ps;               /* paint structure          */
    HDC hDC;                      /* handle to display context */
    BOOL bReverse = FALSE;
    UINT ToolbarID = IDB_VIEWTOOLBAR;
    int ToolbarHeight = TOOLBARHEIGHT;
    int ToolbarSize = 16;
    BOOL bSkipInit = FALSE;
	int justSavedIndex = -1;
	BOOL bEnable = FALSE;



#ifdef OFFLINE  // TANAKA Goh
    char* pGenSockDLL;
#endif


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

    /* We know what *window* is being acted on, but we must find
     * out which *document* is being acted on.  There's a one-to-one
     * relationship between the two, and we find out which document
     * corresponds to this window by scanning the GroupDocs array.
    */

    for (ih = 0, found = FALSE; !found && ih < MAXGROUPWNDS; ih++) 
    {
        if (GroupDocs[ih].hWndFrame == hWnd) 
        {
            found = TRUE;
            ThisDoc = &(GroupDocs[ih]);
        }
    }

    if (!found)
        ThisDoc = CommDoc;


    if (StatusBarProc (hWnd, message, wParam, lParam, ThisDoc))
        return 0;
	int nCurrent = nToolbarView;
	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_VIEWTOOLBARMED;
            ToolbarHeight = TOOLBARHEIGHTMED;
            ToolbarSize = 24;
            break;
        case 2:
            ToolbarID = IDB_VIEWTOOLBARBIG;
            ToolbarHeight = TOOLBARHEIGHTBIG;
            ToolbarSize = 32;
            break;
        case 0:
        default:
            ToolbarID = IDB_VIEWTOOLBAR;
            ToolbarHeight = TOOLBARHEIGHT;
            ToolbarSize = 16;
            break;
    }


  switch (message) 
  {
  case WM_SYSCOMMAND:
    return (DefWindowProc (hWnd, message, wParam, lParam));

  case WM_CLOSE:
	if (wParam != 12345 && ThisDoc->SelectedLines)	 //special flag to no prompt on close
	{
			if (WVMessageBox (hWnd, IDS_WINVN_CLOSEACTIVE, IDS_WINVN_CONFIRM1,
							  MB_YESNO | MB_ICONQUESTION) == IDNO)
			{
				SetFocus(hWnd);	
				return 0;
			}

	}
    CloseGroupWnd (hWnd, ThisDoc);
    return (0);
    break;

  case WM_DESTROY:
    /* Unlink all the article windows that belong to this group */
    NumGroupWnds--;
	if (!ThisDoc)
		return 0;
    if(LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
              ThisDoc->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.                  */
      TypGroup far *group = GetGroup(LinePtr);
      long int CN_Total_Lines;

      group->SubjDoc = (TypDoc far *) NULL;

      ThisDoc->InUse = FALSE;
      if (ThisDoc == CommDoc) 
      {
        CN_Total_Lines = CommDoc->TotalLines;
        CommBusy = FALSE;
        CommDoc = (TypDoc far *) NULL;
      }
      /* SMR 940420 cause of mysterious F3 bug */
      else
        CN_Total_Lines = group->max_headers;  

      /* make the 'n' in the group-list go away */
      group->HighestPrevSeen = group->uLoadLast;
      InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
      
      /*CN  if (group->total_headers != 0)  */
      if (CN_Total_Lines != 0 || group->max_headers > 0) //MDS include killed
      {    /* CN */
        UpdateSeenArts (ThisDoc);
      }  
        UnlinkArtsInGroup (ThisDoc);
        if (group->header_handle) 
        {
            long i;
            header_p headers;

            headers = lock_headers (group->header_handle, group->thread_handle);
            for(i = 0; i < group->alloc_headers; i++)
            {
                delete headers[i].pHD;
                headers[i].pHD = NULL;
            }
            unlock_headers (group->header_handle, group->thread_handle);
            free_headers (group->header_handle, group->thread_handle);
        }
        group->header_handle = (HANDLE) NULL;
        group->thread_handle = (HANDLE) NULL;
        group->total_headers = 0;
        group->alloc_headers = 0;
        delete group->pAA;
        group->pAA = NULL;
		if (group->pFile_sort)
		{
			WVFileSortHeaders* pList = (WVFileSortHeaders*)group->pFile_sort;
			group->pFile_sort = NULL;
			delete pList;
		}
        delete group->pAddHdr; //Heirich 19970426
        group->pAddHdr = NULL;
        GlobalFreePtr(group->Description); //Heirich 19991025
        group->Description = NULL;

    }

    UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);

    //MDS 2/2005 Child windows are destroyed when parent destroyed
    // jn 2004 tb destroy
    DestroyWindow(ThisDoc->hWndTB);
    ThisDoc->hWndTB = NULL;
    
    /* Clear document                                        */
    FreeDoc (ThisDoc);

    /* If there's another group window, make it the active   */
    /* group window so we don't create a new one if the      */
    /* New Group flag is FALSE.                              */

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

  case WM_RETHINKTOOLBAR:
      if (ThisDoc->hWndTB)
      {
        DestroyWindow(ThisDoc->hWndTB);
        ThisDoc->hWndTB = NULL;
      }
      bSkipInit = TRUE;
    //fall through

  case WM_CREATE:
    /* create the child window and the toolbar  */
    GetClientRect (hWnd, &MyRect);

// add common controls for toolbar (jlg)
    i = j = 0;
    tbButton[j].iBitmap = 0;    // 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 selected

    tbButton[j].idCommand = IDM_SAVE_SELECTED;
    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;    // Decode selected

    tbButton[j].idCommand = IDM_DECODE_SELECTED;
    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;    // Catch Up and Exit

    tbButton[j].idCommand = IDM_CATCH_UP_AND_EXIT; //IDM_MARK_ALL; MDS Make button work like menu
    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;    // Exit

    tbButton[j].idCommand = IDV_EXIT;
    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;    /* Help */
    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;
    

    ThisDoc->hWndTB = CreateToolbarEx(hWnd,     
                                      CCS_TOP | WS_VISIBLE | TBSTYLE_TOOLTIPS,
                                      ToolbarID,
                                      i + 1,
                                      hInst,
                                      ToolbarID,
                                      tbButton,
                                      j + 1,
                                      ToolbarSize,
                                      ToolbarSize,
                                      ToolbarSize,
                                      ToolbarSize,
                                      sizeof(TBBUTTON));
#else

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

    if (bSkipInit)
    {
		PostMessage(hWnd, WM_SIZE, 0 ,0);
        break;
    }

    ThisDoc->hWndFrame = hWnd;
#ifdef COMMCTRL
     ThisDoc->hDocWnd = CreateWindowEx (WS_EX_CLIENTEDGE,
                                      "WinVnView",
                                      NULL,
                                      WS_CHILD | WS_VSCROLL | WS_HSCROLL,
                                      0,                 /* Initial X position */
                                      ToolbarHeight,     /* Initial Y position */
                                      RectWidth (MyRect),
              RectHeight (MyRect) - ToolbarHeight - StatbarPntData.dyStatbar,
                                      hWnd,
                                      NULL,
                                      hInst,
                                      NULL);
#else
    ThisDoc->hDocWnd = CreateWindow ("WinVnView",
                                     NULL,
                                     WS_CHILD | WS_VSCROLL | WS_HSCROLL,
                                     0,                 /* Initial X position */
                                     ToolbarHeight,     /* Initial Y position */
                                     RectWidth (MyRect),
             RectHeight (MyRect) - ToolbarHeight - StatbarPntData.dyStatbar,
                                     hWnd,
                                     NULL,
                                     hInst,
                                     NULL);
#endif
#ifndef OFFLINE
      //remove unused menu items
      hMenu = GetMenu (hWnd);
      if (hMenu)
      {
        hSubMenu = GetSubMenu (hMenu, 0);     //articles menu
        if (hSubMenu)
        {
          DeleteMenu(hSubMenu, IDM_SENDME_SELECTED, MF_BYCOMMAND);
          DeleteMenu(hSubMenu, IDM_DELETE_SELECTED, MF_BYCOMMAND);
        }
      }      
#endif

    SetHandleBkBrush (ThisDoc->hDocWnd, hListBackgroundBrush);
    ShowWindow (ThisDoc->hDocWnd, SW_SHOWNORMAL);

    SetGroupMenus (ThisDoc, DISABLE);
    NumGroupWnds++;
    break;

  case WM_ACTIVATEAPP:
	return 1;
	break;
  case WM_ACTIVATE:
    if((wParam == WA_ACTIVE) || (wParam == WA_CLICKACTIVE))
	{
      ActiveGroupDoc = ThisDoc;
	InvalidateRect(hWnd, NULL, FALSE);

	}
    
    if ( wParam == WA_INACTIVE && DragMouseAction != DRAG_NONE) //lost capture (Alt+Tab'd?)
        DragMouseAction = DRAG_NONE;

    /* fall through to update toolbars */
  case WM_MYINITMENU:
  case WM_INITMENU:
    hMenu = GetMenu (hWnd);
    hSubMenu = GetSubMenu (hMenu, 0);
    
    i = (CommBusy && (ThisDoc->SelectedLines || IsFlagSet(ThisDoc, PreviewDecode)) );
    EnableMenuItem (hSubMenu, IDM_CANCEL_DOWNLOAD, (i)  ? ENABLE_MENU : DISABLE_MENU);

    i = (!CommBusy && ThisDoc->SelectedLines);

    EnableMenuItem (hSubMenu, IDM_SAVE_SELECTED, i ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_DECODE_SELECTED, (i && CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
    

#ifdef OFFLINE  // TANAKA Goh
    if ((pGenSockDLL = strrchr(GenSockDLL, '\\')) == NULL) 
    {
          pGenSockDLL = GenSockDLL;
    } 
    else 
    {
          pGenSockDLL++;
    }
        
    if (!stricmp(pGenSockDLL, "gwssoup.dll") || !stricmp(pGenSockDLL, "gsoup32.dll")) 
    {
          EnableMenuItem (hSubMenu, IDM_DELETE_SELECTED, (i && CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
          EnableMenuItem (hSubMenu, IDM_SENDME_SELECTED, (i && CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
    } 
    else if (!stricmp(pGenSockDLL, "ggnspool.dll") || !stricmp(pGenSockDLL, "gspool32.dll")) 
    {
          EnableMenuItem (hSubMenu, IDM_DELETE_SELECTED, (i && CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
          EnableMenuItem (hSubMenu, IDM_SENDME_SELECTED, DISABLE_MENU);
    } 
    else 
    {
          //EnableMenuItem (hSubMenu, IDM_DELETE_SELECTED, DISABLE_MENU);
          //EnableMenuItem (hSubMenu, IDM_SENDME_SELECTED, DISABLE_MENU);
          DeleteMenu (hSubMenu, IDM_DELETE_SELECTED, MF_BYCOMMAND);
          DeleteMenu (hSubMenu, IDM_SENDME_SELECTED, MF_BYCOMMAND);
    }
#endif

	bEnable = ThisDoc->hParentBlock && (!CommBusy || CommDoc != ThisDoc);

    EnableMenuItem (hSubMenu, IDM_READ_SELECTED, (i && CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_DESELECT_ALL, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_MARK_SELECTED, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_MARK_READ_ARTICLES, ThisDoc->TotalLines ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_MARK_UNREAD_ARTICLES, ThisDoc->TotalLines ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_UNMARK_SELECTED, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_UNMARK_READ_ARTICLES, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_UNMARK_UNREAD_ARTICLES, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_DESELECT_MATCH, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU); 
    EnableMenuItem (hSubMenu, IDM_UPDATE, CommBusy ? DISABLE_MENU : ENABLE_MENU);
	EnableMenuItem( hSubMenu, IDM_DESELECT_DUPES, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);

	EnableMenuItem (hSubMenu,IDM_SAVE_SELECTED_ARTICLE_LIST, bEnable && ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu,IDM_SAVE_SELECTED_ARTICLE_NZB, bEnable && ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	
	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_SAVE_SELECTED, i);
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_DECODE_SELECTED, (i && CodingState == INACTIVE));

    /* only allow sort by threads if the group has threads.  it's not enough
     * to check threadp - since the user may have changed the threadp
     * option after retrieving this group article-list 
     */
    threadOk = FALSE;
    /*
     * we check hParentBlock because this code can be called before the Document
     * has been initialized via InitDoc
     */
    if (ThisDoc->hParentBlock && (!CommBusy || CommDoc != ThisDoc)) 
    {
      LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
                ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
      if (LinePtr) 
      {
        GroupDoc = GetGroup(LinePtr);
        threadOk = GroupDoc->Threaded;
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
      }
    }

    SendMessage(hWnd, WM_AA_ENABLE_DISABLE, 0, 0); // conditionally enable Article Actions
    hSubMenu = GetSubMenu (hMenu, 1);   /* sorting menu */
    EnableMenuItem (hSubMenu, IDM_SORT_THREADS, threadOk ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_SORT_THREADSUB, threadOk ? ENABLE_MENU : DISABLE_MENU);

	//scroll to next art
	EnableMenuItem (hSubMenu, IDM_VIEW_NEXT_SEL, bEnable && ThisDoc->SelectedLines > 0 ? ENABLE_MENU : DISABLE_MENU);


    if(message == WM_INITMENU)
    {
/* Ray Dunn:99/10 */
      UncheckAllSortMenuItems(hMenu);
      CheckSortMenuItem(hMenu, iSortOption, MF_CHECKED);
/* Ray Dunn:99/10:End */

/*      TRACE2("WVGroup/WM_INITMENU: Newsgroup <%s> iSortOption: %d\n", CurrentGroup, iSortOption); */
    }

    if (message == WM_ACTIVATE) // kludgeville
      return (DefWindowProc (hWnd, message, wParam, lParam));
    break;

  case WM_SETFOCUS:
    StatBarGroupPopups (ThisDoc);
    SetCapsLockText(hWnd);
    SetNumLockText(hWnd);
    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:
    /* paint status bar  */
    hDC = BeginPaint (hWnd, &ps);
    PaintStatbar (hWnd, hDC, ThisDoc);
    EndPaint (hWnd, &ps);
    return (DefWindowProc (hWnd, message, wParam, lParam));
    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_SIZE:
    /* Store the new size of the window.                     */
    GetClientRect (hWnd, &MyRect);
    MoveWindow (ThisDoc->hDocWnd, 0, ToolbarHeight,
                RectWidth (MyRect),
             RectHeight (MyRect) - ToolbarHeight - StatbarPntData.dyStatbar,
                TRUE);
    MoveWindow (ThisDoc->hWndTB, 0, 0,
                RectWidth (MyRect),
                ToolbarHeight,
                TRUE);

    break;


  case WM_MYMOUSEWHEEL:
  case WM_MOUSEWHEEL:
  case WM_KEYDOWN:
    SendMessage (ThisDoc->hDocWnd, message, wParam, lParam);
    break;

  case WM_DISPLAY_SORTED:
    {
        TypGroup far *group;
        HANDLE header_handle, thread_handle;
        header_p headers, hp;
        thread_array thread_index;
        BOOL threadOk;
        long i;
        uint32 i1;
        TypLineID oldIndex = -1;
        
        	
        LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
                  ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
        group = GetGroup(LinePtr);
        header_handle = group->header_handle;
        thread_handle = group->thread_handle;
        threadOk = group->Threaded;

        if (header_handle == NULL)
           break;

        /* this dependency on [thread_array_p][head0][head1][head2]... */
        /* should be moved into headarry.c */
        headers = lock_headers (header_handle, thread_handle);
        thread_index = *((thread_array_p) ((char_p) headers - sizeof (char_p)));

        /* clear thread_depth info */
        for (i = 0; i < group->total_headers; i++)
          headers[i].thread_depth = 0;

        ThisDoc->SelectedLines = 0; // (this recount may be redundant...)
        if(ThisDoc->TotalLines > 0)
        {
          for(i1 = 0; i1 < ThisDoc->TotalLines; i1++)
          {
            hp = header_elt(headers, i1);
            if(ART_SELECTED & hp->SeenSelected)
              ThisDoc->SelectedLines++;
			//MDS if we are currently viewing an article, remove header->ArtDoc pointer
			if (hp->ArtDoc)
			{
				oldIndex = i1;
				TRACE("TODO: Fix up ArtDoc pointer!\r\n");
			}              
          }
        }
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);

        sort_by_option(headers, thread_index, threadOk, ThisDoc->TotalLines,
           header_handle, thread_handle);
        unlock_headers (header_handle, thread_handle);

		if (oldIndex >= 0)
		{
          for(i1 = 0; i1 < ThisDoc->TotalLines; i1++)
          {
            hp = header_elt(headers, i1);
			//MDS if we are currently viewing an article, remove header->ArtDoc pointer
			if (hp->ArtDoc)
			{
				hp->ArtDoc = NULL;
				break;
			}
		  }
		}
/* Ray Dunn:99/10
   If an article is selected, find it again after the sort and bring it into view.
   Previously, any selected articles were not recognized, and selecting another article
   didn't clear the highlight from the existing one
*/
        long artindex;
        
        artindex = find_first_selected_art(ThisDoc);
        if (artindex != -1)
        {
          ThisDoc->ActiveLineID = artindex;
          AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
        }
        else
          ThisDoc->ActiveLineID = 0;
/* Ray Dunn:99/10:End */

        InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
    }
    break;
  
  case WM_AA_ENABLE_DISABLE:
    hMenu = GetMenu (hWnd);
    hSubMenu = GetSubMenu (hMenu, 1);   /* sorting menu */
    EnableMenuItem (hSubMenu, IDM_VIEW_ACTION, bEnableArticleAction ? ENABLE_MENU : DISABLE_MENU);
    break;

    case WM_MULTISAVE:
        ThisDoc->numArtsSaved = 0;
        ThisDoc->numArtsError = 0;
        ThisDoc->savingArtIndex = 0;
		ThisDoc->curSaveArtIndex = 0;
        SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
        goto doretrieve;
      break;

        //fall through
     case WM_MULTIDECODE:
        if (!hCodedBlockWnd)
            DecodeInit (FALSE);
        CommDoc = ThisDoc;
        CommDecoding = TRUE;
        AbortDownload = 0;
        ThisDoc->numArtsSaved = 0;
        ThisDoc->numArtsError = 0;
        ThisDoc->savingArtIndex = 0;
		ThisDoc->curSaveArtIndex = 0;
        SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
        if (AlsoDecodeOpenArticles) 
        {
          DecodeOpenArticles (hWnd);
        }
        /* now start the retrieving the selected arts */
        goto doretrieve;

        break;

  
  case WM_COMMAND:
    switch (GET_WM_COMMAND_ID(wParam, lParam)) 
    {
    case IDV_EXIT:
      CloseGroupWnd (hWnd, ThisDoc);
      return (0);
      break;

    case IDM_CONFIG_ARTLIST:
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNARTICLELISTPREFS), hWnd, lpfnWinVnArtListDlg);
      SendMessage(hWnd, WM_AA_ENABLE_DISABLE, 0, 0); // conditionally enable Article Actions
      break;

    case IDV_NEXT:
      break;

    case IDM_MARK_ALL: // actually "Catch up and exit this group"
      article_operation (ThisDoc, 0, mark_read_all);
      NewsrcDirty = TRUE;
      CloseGroupWnd (hWnd, ThisDoc);
      return (0);
      break;
      
      case IDM_THREADREAD:
        article_operation (ThisDoc, ThisDoc->ActiveLineID, mark_read_thread); 
        NewsrcDirty = TRUE;
        InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
        return (0);   
		break;

/* Ray Dunn:99/10:
   This is "Catch-up and exit group" on the menu, which previously did IDM_MARK_ALL
   above (as from button), which only marks the articles fetched as read, not those
   that are unfetched.
   This is not what "Catch-up" should do!
   It should have the same functionality as:
   "Mark all articles as read in Selected Groups" in CONFMENU
   The command for "Catch up and Exit" in "Articles" in VIEWMENU in the resources has
   now been changed to IDM_CATCH_UP_AND_EXIT
*/
    case IDM_CATCH_UP_AND_EXIT: // actually "Catch up and exit this group"
      article_operation (ThisDoc, 0, mark_read_all_catchup);
      NewsrcDirty = TRUE;
      CloseGroupWnd (hWnd, ThisDoc);
      return (0);
      break;
/* Ray Dunn:99/10:End */

    case IDM_MARK_SELECTED:
      article_operation (ThisDoc, 0, mark_read_selected);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break;

    case IDM_UNMARK_SELECTED:
      article_operation (ThisDoc, 0, mark_unread_selected);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break;
    
    case IDM_MARK_READ_ARTICLES:
      article_operation (ThisDoc, 0, mark_selected_read_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break;
      
    case IDM_UNMARK_READ_ARTICLES:
      article_operation (ThisDoc, 0, mark_deselected_read_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break;
    
    case IDM_MARK_UNREAD_ARTICLES:
      article_operation (ThisDoc, 0, mark_selected_unread_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break;
      
    case IDM_UNMARK_UNREAD_ARTICLES:
      article_operation (ThisDoc, 0, mark_deselected_unread_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break; 

	case IDM_SELECT_MARKED_SEEN:
      article_operation (ThisDoc, 0, mark_selected_killed_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break; 
	case IDM_UNMARK_MARKED_SEEN:
      article_operation (ThisDoc, 0, mark_deselected_killed_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
      break;
	
	case IDM_SELECT_ERRORS:
      article_operation (ThisDoc, 0, mark_selected_error_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;
	
	case IDM_DESELECT_ERRORS:
      article_operation (ThisDoc, 0, mark_deselected_error_articles);
      InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;
	case IDM_DESELECT_DUPES:
	{
		article_operation (ThisDoc, 0, deselect_dupes);
		InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);


	}
	break;
	

	  
	case IDM_SAVE_SELECTED_ARTICLE_LIST:
		{
			TCHAR szfile[_MAX_PATH];
			HANDLE hFile = INVALID_HANDLE_VALUE;
			OPENFILENAME ofn;
			*szfile = '\0';
			BOOL bLoadIt = (GetAsyncKeyState(VK_SHIFT) >>15); //secret menu

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

			memset (&ofn, 0, sizeof (OPENFILENAME));
			ofn.lStructSize = sizeof(OPENFILENAME);
			ofn.lpstrTitle = "Save List of Articles";
			ofn.lStructSize = sizeof (OPENFILENAME);
			ofn.hwndOwner = hWnd;
			ofn.lpstrFilter = "All Files (*.*)\0*.*\0Text files (*.txt)\0*.txt\0\0";
			ofn.nFilterIndex = 2;
			ofn.lpstrFile = szfile;
			ofn.nMaxFile = _MAX_PATH;
			ofn.lpstrInitialDir = DecodePathName;
			ofn.lpstrDefExt = ".txt";

			ofn.Flags =  OFN_EXPLORER | OFN_NOREADONLYRETURN | OFN_NOCHANGEDIR;

			if (bLoadIt)
			{
				ofn.Flags =  OFN_EXPLORER;
				ofn.lpstrTitle = "Load Selected Articles";
				if (GetOpenFileName( &ofn))
				{
					
			
					strncpy(DecodePathName, szfile, MAXFILENAME);
					char *p = strrchr( DecodePathName, '\\');
					if (p)
						*p = '\0';


					hFile = CreateFile(szfile, GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
					
					if (hFile != INVALID_HANDLE_VALUE )
					{
						long nTotal = 0;
						long nSel = SelectLoadArtList(ThisDoc, hFile, &nTotal);
						CloseHandle(hFile);
						InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);	
						WVArgXMessageBox(hWnd,  IDS_WVGROUP_LOAD_OK, IDS_WVGROUP_LOAD_TITLE, MB_OK | MB_ICONINFORMATION, nSel, nTotal);

					
					}
				}
			}
			else
			{
				if (GetSaveFileName (&ofn) )
				{	
					
					BOOL bOK = TRUE;
					int nOpen = CREATE_NEW;
					long nAffected = 0;
					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)
						{
							if (WVArgMessageBox( hWnd, IDS_WVCNCM_OVERWRITE, IDS_WVCNCM_OVERWRITE_TITLE, MB_YESNO | MB_ICONWARNING, szfile) == IDNO)
								break;
							
							nOpen = CREATE_ALWAYS;
							DeleteFile(	szfile);
						}
					}
					
					hFile = CreateFile(szfile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, 
								nOpen, FILE_ATTRIBUTE_NORMAL, NULL);
					
					if (hFile != INVALID_HANDLE_VALUE )
					{	

						nAffected = SaveArtList( ThisDoc, hFile, TRUE);
						CloseHandle(hFile);
						if (nAffected < 0)
							bOK = FALSE;

					}
					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_ICONERROR, message);
						LocalFree (message);
					}
					else
					{
						WVArgXMessageBox(hWnd,  IDS_WVGROUP_SAVE_OK, IDS_WVGROUP_SAVE_TITLE, MB_OK /*| MB_ICONINFORMATION*/, nAffected, szfile);
					}

				}
			}
		}


		break;
		
	case IDM_SAVE_SELECTED_ARTICLE_NZB:
		{
			TCHAR szfile[_MAX_PATH];
			HANDLE hFile = INVALID_HANDLE_VALUE;
			OPENFILENAME ofn;
			long nAffected = 0;
			*szfile = '\0';
			BOOL bLoadIt = (GetAsyncKeyState(VK_SHIFT) >>15); //secret menu
			BOOL bOK = FALSE;

			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 (bLoadIt)
			{
				ofn.Flags =  OFN_EXPLORER;
				ofn.lpstrTitle = "Load NZB Selected Articles";
				if (GetOpenFileName( &ofn))
				{
					
					bOK = FALSE;
					
					strncpy(DecodePathName, szfile, MAXFILENAME);
					char *p = strrchr( DecodePathName, '\\');
					if (p)
						*p = '\0';


					hFile = CreateFile(szfile, GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
					
					if (hFile != INVALID_HANDLE_VALUE )
					{
						long nTotal = 0;
						long nSel = SelectLoadNZBArtList(ThisDoc, hFile, &nTotal);
						CloseHandle(hFile);
						InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);	
						WVArgXMessageBox(hWnd,  IDS_WVGROUP_LOAD_OK, IDS_WVGROUP_LOAD_TITLE, MB_OK /*| MB_ICONINFORMATION*/, nSel, nTotal);

					
					}
				}
			}
			else
			{
				bOK = FALSE;
				if (GetSaveFileName (&ofn) )
				{	
					TCHAR szOldFile[_MAX_PATH] = {0};
					bOK = TRUE;
					int nOpen = CREATE_NEW;

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

						if (nRet == IDCANCEL)
							break;
						if (nRet == IDOK)
							nOpen = OPEN_EXISTING;
						else if (nRet == IDIGNORE)
						{
							if (WVArgMessageBox( hWnd, IDS_WVCNCM_OVERWRITE, IDS_WVCNCM_OVERWRITE_TITLE, MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING, szfile) == IDNO)
							{
								WVMessageBox( hWnd, IDS_WVGROUP_CANCELED, IDS_WVGROUP_SAVE_TITLE, MB_OK);
								break;
							}
															
							_tcscpy(szOldFile, szfile);
							_tcscat(szOldFile, ".old");
							MoveFile(szfile, szOldFile); //make a backup copy
							nOpen = CREATE_ALWAYS;

						}
					}
					
					hFile = CreateFile(szfile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, 
								nOpen, FILE_ATTRIBUTE_NORMAL, NULL);
					
					if (hFile != INVALID_HANDLE_VALUE )
					{			
					
						nAffected =  SaveArtNZB( ThisDoc, hFile, GET_WM_COMMAND_ID(wParam, lParam) == IDM_SAVE_SELECTED_ARTICLE_NZB, nOpen == OPEN_EXISTING);
						CloseHandle(hFile);
						bOK = nAffected >= 0;

					}
					else
					{
						bOK = FALSE;
					}
					if (*szOldFile)
						DeleteFile(	szOldFile);	 //delete old one, it's recoverable with undelete if necessary

					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_ICONERROR, message);
						LocalFree (message);
					}
					else
					{
						
						article_operation (ThisDoc, 0, mark_read_selected);
						InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);						
						WVArgXMessageBox(hWnd,  IDS_WVGROUP_SAVE_OK, IDS_WVGROUP_SAVE_TITLE, MB_OK /*| MB_ICONINFORMATION*/, nAffected, szfile);

					}

				}
			}
		}


		break; 

	//scroll view to next selected article without retrieving it
	case IDM_VIEW_NEXT_SEL:
	{

		TypGroup far *group;
		HANDLE header_handle = NULL, thread_handle = NULL;
		header_p headers = NULL, header;


		LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
			ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
		group = GetGroup(LinePtr);

		if (group->header_handle && group->total_headers > 0) 
		{
			header_handle = group->header_handle;
			thread_handle = group->thread_handle;
			headers = lock_headers (header_handle, thread_handle);
		}

		
		if (headers != NULL) 
		{

			TypLineID artindex  = -1;

			if (ThisDoc->ActiveLineID >= 0 &&
				ThisDoc->ActiveLineID < group->total_headers - 1) 
			{
				artindex = ThisDoc->ActiveLineID;
			}
			while((++artindex < group->total_headers) )
			{
				header = header_elt (headers, artindex);
				if(ART_SELECTED & header->SeenSelected)
					break;
			}
			if (artindex >= group->total_headers && ThisDoc->ActiveLineID > 0)
			{
				//start over from top
				artindex  = -1;
				while((++artindex < group->total_headers) )
				{
					header = header_elt (headers, artindex);
					if(ART_SELECTED & header->SeenSelected)
						break;
				}

			}
			
			if ( artindex < group->total_headers)
			{
				ThisDoc->ActiveLineID = artindex;
				AdjustTopScByDoc (ThisDoc, artindex);
				
			}

			unlock_headers (header_handle, thread_handle);
		}
		break;
	}

    case IDM_VIEW_ACTION:
      {
        TypGroup far *group;
        HANDLE header_handle, thread_handle;
        header_p headers;
		bool bReSort = false;

        if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
                  ThisDoc->ParentLineID, &BlockPtr, &LinePtr))
		{
			
			group = GetGroup(LinePtr);
			header_handle = group->header_handle;
			thread_handle = group->thread_handle;
			threadOk = group->Threaded;

			if (header_handle) 
			{
			  headers = lock_headers (header_handle, thread_handle);
			  if (group->pAA)
				group->pAA->RemoveStoredNGroupValue();

			  
			  if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ACTION), hWnd, lpfnWinVnArticleAction, (LPARAM)group->pAA))
			  //if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ACTIONDLG), hWnd, lpfnWinVnArticleActionDlg, (LPARAM)group->pAA))
			  {
				HCURSOR hCursorOld = SetCursor (LoadCursor (NULL, IDC_WAIT));
			    char str2[32];
				char str1[32];
				//long total_headers = group->total_headers;
				group->total_headers = group->max_headers;
				unkill_killed_headers( header_handle, thread_handle, group->max_headers);
				if ( !group->pAA->IgnoreGlobalFilter() )
					g_pAction->ActOnArticles(group, headers, TRUE);
				group->pAA->ActOnArticles(group, headers, FALSE);
				group->total_headers = move_killed_headers ( header_handle, thread_handle, group->max_headers);
				ThisDoc->TotalLines = group->total_headers;
				ThisDoc->ActiveLines = ThisDoc->TotalLines;
				ThisDoc->KilledLines = group->max_headers - group->total_headers;
			    FormatNumber(ThisDoc->TotalLines, str2);
			    FormatNumber(ThisDoc->KilledLines, str1);
			    WVArgXSetWindowText (ThisDoc->hWndFrame, IDS_GROUP_DESCRIPTIONKILLED, CurrentGroup, 
								 str2, str1, CurrentDescription);
				
				SetCursor(hCursorOld);
				//if we've moved headers, thread index is invalid
				if (group->pFile_sort)
				{
					delete group->pFile_sort;
					group->pFile_sort = NULL;
				}
				
				if (ThisDoc->KilledLines)
				  bReSort = true;
				else
				{
					ThisDoc->SelectedLines = 0; // (this recount may be redundant...)
					if(ThisDoc->TotalLines > 0)
					{
					  unsigned long i1;
					  for(i1 = 0; i1 < ThisDoc->TotalLines; i1++)
					  {
						header_p hp = header_elt(headers, i1);
						if(ART_SELECTED & hp->SeenSelected)
						  ThisDoc->SelectedLines++;            
					  }
					}				
				}

			  }
			  unlock_headers (header_handle, thread_handle);

			} 
			else 
			{
			  if (group->pAA)
				group->pAA->RemoveStoredNGroupValue();
			  DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ACTION), hWnd, lpfnWinVnArticleAction,(LPARAM)group->pAA);
			  //DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ACTIONDLG), hWnd, lpfnWinVnArticleActionDlg,(LPARAM)group->pAA);
			}

			UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
		  
			if (bReSort)
			{
				SendMessage(hWnd, WM_DISPLAY_SORTED, 0, 0); //also fixes up selected lines
				ThisDoc->TopLineOrd = 0;
				PostMessage(ThisDoc->hDocWnd, WM_SIZE,0,0);
			}
			InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
		  
		  }
      }
      break;

/* Ray Dunn:99/10: */
    case IDM_DONT_SORT:
    case IDM_SORT_DATE_TIME:
    case IDM_SORT_DATE_SUBJECT:
    case IDM_SORT_DATE_LINES:
    case IDM_SORT_DATE_ARTNUM:
    case IDM_SORT_DATE_FROM:
	case IDM_SORT_DATE_FILENAME:
    case IDM_SORT_SUBJECT_DATE:
    case IDM_SORT_SUBJECT_LINES:
    case IDM_SORT_SUBJECT_ARTNUM:
    case IDM_SORT_SUBJECT_FROM:
    case IDM_SORT_THREADS:
    case IDM_SORT_THREADSUB:
    case IDM_SORT_LINES:
    case IDM_SORT_ARTNUM:
    case IDM_SORT_FROM_SUBJECT:
    case IDM_SORT_FROM_DATE:
    case IDM_SORT_FROM_LINES:
    case IDM_SORT_FROM_ARTNUM:
	case IDM_SORT_FROM_FILENAME:
    case IDM_SORT_FILENAME:
      iSortOption = LOWORD(wParam);
      WriteSortOption(iSortOption, CurrentGroup);

      hMenu = GetMenu(hWnd);
      UncheckAllSortMenuItems(hMenu);
      CheckSortMenuItem(hMenu, iSortOption, MF_CHECKED);

/* Ray Dunn:99/10:End */

      SendMessage(hWnd, WM_DISPLAY_SORTED, 0, 0);
      break;


    case IDM_HELP:
      DoHelp (ThisDoc->hDocWnd, HELP_INDEX, 0L);
      break;


    case IDM_FIND_PREV_SAME:
      bReverse = TRUE;  
    case IDM_FIND:
    case IDM_FIND_NEXT_SAME:
      FindDoc = ThisDoc;

      continueFind = TRUE;
      if (!FindDoc->SearchStr[0] || LOWORD (wParam) == IDM_FIND) 
      {
        if (!(FindDoc->SearchStr[0]) && LastArticleHeaderFind[0])
          strcpy (FindDoc->SearchStr, LastArticleHeaderFind);
        continueFind = (BOOL) DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNFIND), hWnd, lpfnWinVnFindDlg);
      }
      if (continueFind && FindDoc->SearchStr[0]) 
      {
        TypGroup far *group;
        header_p headers;
        HANDLE header_handle;
        HANDLE thread_handle;
        TypLineID starting_at;

        if (!LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
                  &BlockPtr, &LinePtr))
			break;
        group = GetGroup(LinePtr);
        header_handle = group->header_handle;
        thread_handle = group->thread_handle;
        if (header_handle == NULL)
           break;
        headers = lock_headers (header_handle, thread_handle);
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);

                /* 'Find Next' will start one line after */
        if (ThisDoc->savingArtIndex == -1)// if searching while downloading, don't use active line
        {
          //not downloading
		  if (bReverse)
            starting_at = ThisDoc->ActiveLineID - ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);
          else
            starting_at = ThisDoc->ActiveLineID + ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);
        } 
        else 
        {
          //downloading
		  if (bReverse)
            starting_at = min(ThisDoc->FindOffset,ThisDoc->TopLineOrd+ThisDoc->ScYLines) - ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);
          else
            starting_at = max(ThisDoc->FindOffset, ThisDoc->TopLineOrd) + ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);
  
        }
        /* back up one if we're at the end */
        if (starting_at >= group->total_headers)
          starting_at = group->total_headers-1;
        if (starting_at < 0)
          starting_at = 0;
        
        found_artindex = search_headers (ThisDoc, headers, starting_at, group->total_headers, bReverse);
        if (found_artindex == -1) 
        {
          WVArgMessageBox(hWnd, IDS_WVGROUP_ARGNOTFOUND, IDS_WVGROUP_NOT_FOUND, MB_OK,
                          ThisDoc->SearchStr);
        }
        else 
        {
          //set active line ID so search/enter reads matched article instead of something else MDS 20000822
          ThisDoc->FindOffset = (int) found_artindex;
          strcpy (LastArticleHeaderFind, FindDoc->SearchStr);

          if (ArtListMultiSelect) 
          {
            ThisDoc->AnchorLineID = (int) found_artindex;
            SetGroupSelections (ThisDoc, headers, (int) found_artindex);
          }
		  else
		  {
		    ThisDoc->ActiveLineID = (int) found_artindex;

		  }

		  AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
		  InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
        }
        unlock_headers (header_handle, thread_handle);
      }
      break;

    case IDM_POST:
      /* We are creating the skeleton text of a new posting.
         * Most of the work is done by CreatePostingWnd and
         * CreatePostingText.  Here we have to identify
         * the newsgroup for those routines.
         * Get the newsgroup from the line in NetDoc that
         * points to this document.
       */
      if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
                    ThisDoc->ParentLineID, &BlockPtr, &LinePtr)) 
      {
        ExtractTextLine (ThisDoc->ParentDoc, LinePtr,
                       mybuf, MAXINTERNALLINE);
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
        NewsgroupsPtr = mybuf;
        CreateComposeWnd (hWnd, (TypDoc far *) NULL, DOCTYPE_POSTING);
      }
      break;

    case IDM_SAVE_SELECTED:
      /* Query user for file name */
      if (!DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNSAVEARTS), hWnd, lpfnWinVnSaveArtsDlg)) 
      {
        InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
      }
      else 
      {
        ThisDoc->numArtsSaved = 0;
        ThisDoc->numArtsError = 0;
        ThisDoc->savingArtIndex = 0;
		ThisDoc->curSaveArtIndex = 0;
        AbortDownload = 0;
        SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
        goto doretrieve;
      }
      break;
    
    case IDM_FIND_NEXT_SELECTED:
    case IDM_READ_SELECTED:
      SendMessage(ThisDoc->hDocWnd, (UINT) WM_KEYDOWN, (WPARAM) VK_F5, 0);
      break;

    case IDM_DECODE_SELECTED:
      /* Query user for file name */
      if (!DialogBoxParam (hInst, MAKEINTRESOURCE(IDD_WINVNDECODEARTS), hWnd, lpfnWinVnDecodeArtsDlg, 1)) 
      {
        InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
      }
      else 
      {
		if (Initializing == INIT_NOT_CONNECTED && Autoconnect)
			Reconnecting = 1;
        if (TestCommBusy (hWnd, IDS_TEST_DECODE_ARTICLES) ||
            TestCodingBusy (hWnd, IDS_TEST_DECODE_ARTICLES))
          break;
        DecodeInit (FALSE);
        CommDoc = ThisDoc;
        CommDecoding = TRUE;
        AbortDownload = 0;
        ThisDoc->numArtsSaved = 0;
        ThisDoc->numArtsError = 0;
        ThisDoc->savingArtIndex = 0;
		ThisDoc->curSaveArtIndex = 0;
        SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
        if (AlsoDecodeOpenArticles) 
        {
          DecodeOpenArticles (hWnd);
        }
        /* now start the retrieving the selected arts */
        goto doretrieve;
      }
      break;
	  case IDM_PREVIEW:
	    CommDoc = ThisDoc;
        CommDecoding = TRUE;
        AbortDownload = 0;
        ThisDoc->numArtsSaved = 0;
        ThisDoc->numArtsError = 0;
        ThisDoc->savingArtIndex = 0;
		ThisDoc->curSaveArtIndex = 0;
		SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
		goto doretrieve;
		break;

#ifdef OFFLINE  // TANAKA Goh
    case IDM_DELETE_SELECTED:
      if ((pGenSockDLL = strrchr(GenSockDLL, '\\')) == NULL) 
      {
           pGenSockDLL = GenSockDLL;
      } 
      else 
      {
           pGenSockDLL++;
      }
          
      if (!stricmp(pGenSockDLL, "gwssoup.dll") || !stricmp(pGenSockDLL, "gsoup32.dll") || !stricmp(pGenSockDLL, "ggnspool.dll") || !stricmp(pGenSockDLL, "gspool32.dll")) {
          if (ThisDoc->SelectedLines) 
          {
              if (ConfirmDeleteArticle) 
              {
                if (WVMessageBox(hWnd, IDS_WVGROUP_VERIFY_DEL, IDS_WVART_PLEASE_CONFIRM,  MB_YESNO | MB_ICONQUESTION) == IDYES) 
                {
                    article_operation(ThisDoc, 0, delete_selected);
                    InvalidateRect(ThisDoc->hDocWnd, NULL, FALSE);
                 }
                } 
                else 
                {
                 article_operation(ThisDoc, 0, delete_selected);
                 InvalidateRect(ThisDoc->hDocWnd, NULL, FALSE);
              }
          }
      }
          
      break;

    case IDM_SENDME_SELECTED:
      if ((pGenSockDLL = strrchr(GenSockDLL, '\\')) == NULL) 
      {
           pGenSockDLL = GenSockDLL;
      } 
      else 
      {
           pGenSockDLL++;
      }
          
      if (!stricmp(pGenSockDLL, "gwssoup.dll") || !stricmp(pGenSockDLL, "gsoup32.dll")) 
      {
         if (ThisDoc->SelectedLines) 
         {
             article_operation(ThisDoc, 0, sendme_selected);
             InvalidateRect(ThisDoc->hDocWnd, NULL, FALSE);
         }
      }
          
      break;
#endif

    case IDM_SELECT_ALL:
      /* select all articles */
      AffectSelected (ThisDoc, SELECT, NO_COMPARE);
      break;

	case IDM_INVERT_SELECTION:
	  AffectSelected (ThisDoc, INVERT, NO_COMPARE);
	  break;

    case IDM_DESELECT_ALL:
      /* deselect all articles */
      AffectSelected (ThisDoc, DESELECT, NO_COMPARE);
      break;

    case IDM_SELECT_MATCH:
      /* select all articles containing a string */
      FindDoc = ThisDoc;
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNFIND), hWnd, lpfnWinVnFindDlg);
      if (strcmp (FindDoc->SearchStr, "")) 
      {
        if (AffectSelected (FindDoc, SELECT, COMPARE) == 0) 
        {
          WVArgMessageBox(hWnd, IDS_WVGROUP_ARGNOTFOUND, IDS_WVGROUP_NOT_FOUND, MB_OK,
                          FindDoc->SearchStr);    
        }
      }
      break;
      
    case IDM_DESELECT_MATCH:
      /* deselect all articles containing a string */
      FindDoc = ThisDoc;
      DialogBox (hInst, MAKEINTRESOURCE(IDD_WINVNFIND), hWnd, lpfnWinVnFindDlg);
      if (strcmp (FindDoc->SearchStr, "")) 
      {
        if (AffectSelected (FindDoc, DESELECT, COMPARE) == 0) 
        {
            WVArgMessageBox(hWnd, IDS_WVGROUP_ARGNOTFOUND, IDS_WVGROUP_NOT_FOUND, MB_OK,
                          FindDoc->SearchStr);  
        }
      }
      break;

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

	case IDM_INSTANTUPDATE:	    
		if (TestCommBusy (hWnd, IDS_TEST_LIST))
			break;
		SetFlags(ThisDoc, InstantUpdate);
		//fall through
    case IDM_UPDATE:
	    if (TestCommBusy (hWnd, IDS_TEST_LIST))
			break;

      if (!CommBusy) 
      {
        TypGroup far *group;
        char far *group_name;

        /* update the newsrc data */
        UpdateSeenArts (ThisDoc);
        UnlinkArtsInGroup (ThisDoc);
        if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
                      ThisDoc->ParentLineID, &BlockPtr, &LinePtr)) 
        {
            group = GetGroup(LinePtr);
            group_name = GetGroupName(LinePtr);

			if (group->pFile_sort)
			{
				delete group->pFile_sort;
				group->pFile_sort = NULL;
			}


            // Free whatever headers are there.
            if (group->header_handle) 
            {
              long i;
              header_p headers;
              
              headers = lock_headers (group->header_handle, group->thread_handle);
              for(i = 0; i < group->alloc_headers; i++)
              {
                delete headers[i].pHD;
                headers[i].pHD=NULL;
              }
              unlock_headers (group->header_handle, group->thread_handle);
              free_headers (group->header_handle, group->thread_handle);
            }
            group->header_handle = (HANDLE) NULL;
            group->thread_handle = (HANDLE) NULL;
            group->total_headers = 0;
                        group->alloc_headers = 0;
            group->Threaded = FALSE;
            delete group->pAA;
            group->pAA = NULL;
            delete group->pAddHdr; //Heirich 19970426
            group->pAddHdr = NULL;
            //NooN
            GlobalFreePtr(group->Description);
            //GlobalUnlock(GlobalHandle(group->Description));
            //GlobalFree(GlobalHandle(group->Description));

            group->Description = NULL; //Heirich 19991025

            ThisDoc->hLastSeenBlock = 0;
            ThisDoc->TopLineOrd = 0;
            ThisDoc->FindLineID = 0L;
            ThisDoc->TopScLineID = 0L;
            ThisDoc->hFindBlock = 0;
            ThisDoc->ActiveLineID = -1L;
            ThisDoc->AddLineID = 0L;
            ThisDoc->TopScLineID = 0L;
            ThisDoc->LastSeenLineID = 0L;
            ThisDoc->LongestLine = 0;
            ThisDoc->ScXOffset = 0;

			ThisDoc->curSaveArtIndex = -1L;
			ThisDoc->Flags = 0;
			ThisDoc->NextTopLine = 0;
			ThisDoc->numArtsSaved = 0;
			ThisDoc->numArtsError = 0;
			ThisDoc->KilledLines = 0;
			ThisDoc->StatBarPercent =0;
			ThisDoc->szStatBarText[0] = '\0';

			//should never need these
			if (ThisDoc->hbmFace)
			{
				DeleteObject(ThisDoc->hbmFace);
				ThisDoc->hbmFace = NULL;
			}

			if ( ThisDoc->hWndXFace)
			{
				DestroyWindow(ThisDoc->hWndXFace);
				ThisDoc->hWndXFace = NULL;
			}



			if (GET_WM_COMMAND_ID(wParam, lParam) == IDM_INSTANTUPDATE) 
				SetFlags(ThisDoc, InstantUpdate);

			

            SetGroupMenus (ThisDoc, DISABLE);

            RcvLineCount = 0;
            CommDoc = ThisDoc;
            CommState = ST_GROUP_RESP;
            CommBusy = TRUE;
            strcpy (mybuf, "GROUP ");
            lstrcat (mybuf, group_name);
            PutCommLine (mybuf);
            NewsrcDirty = TRUE;
			
        }
      }
      break;

      /* the following msgs are sent by wvutil engine on completion 
       * retrieval
       */
    case ID_RETRIEVE_COMPLETE:
      {
        TypGroup far *group;
          
        if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
                      ThisDoc->ParentLineID, &BlockPtr, &LinePtr)) 
        {
            group = GetGroup(LinePtr);
            if (group->header_handle)
               SetGroupMenus (ThisDoc, ENABLE);
            else
               SetMenusForEmptyGroup(ThisDoc);
        }    
        SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
        SetCursor (hArrowCursor); //shimomai
		PostMessage(ThisDoc->hDocWnd, WM_SIZE,0,0);

        InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
      }
      break;

	case ID_ARTICLE_RETRIEVE_RESTART:
		
		{
        	long artindex = GET_LPARAM(wParam,lParam);
			if (artindex == -1)
			{
				//article request restart
				if (CurrentArticleID[0])
				{
					sprintf (mybuf, "ARTICLE %s", CurrentArticleID);
					PutCommLine (mybuf);
					CurrentArticleID[0] = '\0';
					CommState = ST_ARTICLE_RESP;
					WVSetStatbarText (CommDoc->hWndFrame, IDS_WVGROUP_REREQUEST, CommDoc, TRUE, FALSE);
				}
				break;
			}

			ThisDoc->savingArtIndex	= artindex;
#ifdef _DEBUG
			MessageBeep((UINT)-1);
#endif

			article_operation (ThisDoc, artindex, selected_true);
			article_operation (ThisDoc, artindex, seen_false);

			InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
			skipSave = !Decoding;
			
			if (ThisDoc->numArtsSaved > 1)
				ThisDoc->numArtsSaved--;

			if (ActiveArticleDoc)
				DestroyWindow(ActiveArticleDoc->hWndFrame);
			if (artindex >= 0)
				goto doretrieve;
		}


		break;    
	case ID_ARTICLE_ERROR: //MDS allow continued download
    {
        long artindex = ThisDoc->savingArtIndex >= 0 ? ThisDoc->savingArtIndex : (ThisDoc->curSaveArtIndex > 0 ? ThisDoc->curSaveArtIndex : ThisDoc->ActiveLineID);

        MessageBeep((UINT)-1);
        skipSave = 1;
        if (artindex >= 0)
		{
			article_operation (ThisDoc, artindex, error_true);
			article_operation (ThisDoc, artindex, seen_false);
			InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
		}
        //fall through
    }

    case ID_ARTICLE_RETRIEVE_COMPLETE:
    {
        TypGroup far *group;

        header_p headers;
        HANDLE header_handle;
        HANDLE thread_handle;
        int startIndex;
		
        if (!IsFlagSet(ThisDoc, PreviewDecode) && ThisDoc->savingArtIndex == -1)  /* this retrieve wasn't part of a save operation */
            break;

        if (CodingState == INACTIVE) 
        {
            /* If this is the 1st article saved, then value of Append depends
            * on what the user selected in dialog.  If > 1st, always append 
            */
            if (skipSave==0 && MRRWriteDocument (ActiveArticleDoc, sizeof (TypText),
                SaveArtFileName, (ThisDoc->numArtsSaved == 0) ? SaveArtAppend : TRUE) != NO_ERROR) 
            {
                *SaveArtFileName = '\0';
                ThisDoc->savingArtIndex = -1;
				AutoDecode = 0;
				//set flags before showing error to prevent recursion
				BringWindowToTop(hWnd);

				WVMessageBox(hWnd, IDS_WVGROUP_WRITE_ERROR, IDS_WVGROUP_SAVE_ERROR,  MB_OK | MB_ICONEXCLAMATION);
				SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
				SetStatbarPercent(ThisDoc->hWndFrame,0,ThisDoc,TRUE);
                break;
            }
            DestroyWindow(ActiveArticleDoc->hWndFrame);
        }
        else 
        {
			int nDecodeResult = CompleteThisDecode (IsFlagSet(ThisDoc, PreviewDecode)); 

			if ( nDecodeResult == FAIL) 
            {
                DecodeDone (IsFlagSet(ThisDoc, PreviewDecode));

                ThisDoc->savingArtIndex = -1;
				ThisDoc->curSaveArtIndex = -1;
				AutoDecode = 0;
				BringWindowToTop(hWnd);
				//set flags before showing error to prevent recursion
                WVMessageBox(hWnd, IDS_WVGROUP_ABORTED_DECODE, IDS_WVGROUP_PROB_DECODING, MB_OK | MB_ICONEXCLAMATION); 
                SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
				SetStatbarPercent(ThisDoc->hWndFrame,0,ThisDoc,TRUE);
                break;
            }
			else if (nDecodeResult == SUCCESS && AbortDownload )
			{
					
			}

        }
        

        if (skipSave==0)
            ThisDoc->numArtsSaved++;
        else if (skipSave == 1)
            ThisDoc->numArtsError++;
		justSavedIndex = ThisDoc->savingArtIndex;

doretrieve:;
		
        if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
                &BlockPtr, &LinePtr)) 
        {
            group = GetGroup(LinePtr);
            header_handle = group->header_handle;
            thread_handle = group->thread_handle;
            headers = lock_headers (header_handle, thread_handle);
            UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
            if (IsFlagSet(ThisDoc, PreviewDecode) )
			{
				if ( justSavedIndex >= 0)  // deselect the article just finished
					article_operation (ThisDoc, justSavedIndex, preview_false);

			}
			else
			{
				if ( (ThisDoc->numArtsSaved > 0 || skipSave==1) && justSavedIndex >= 0)  // deselect the article just finished
					SelectHeader (ThisDoc, header_elt (headers, justSavedIndex), 0);
			}

            /* skip to next selected article */
            /* i'm paranoid of infinite loop in case Doc->SelectedLines is wrong */

            if (ThisDoc->savingArtIndex >= group->total_headers) 
            {
                ThisDoc->savingArtIndex = 0;
            }
            startIndex = ThisDoc->savingArtIndex;
			char cMatch = IsFlagSet(ThisDoc, PreviewDecode) ? ART_PREVIEW : ART_SELECTED;
            while ((ThisDoc->SelectedLines || IsFlagSet(ThisDoc, PreviewDecode) )&&
                !(cMatch & (header_elt (headers, ThisDoc->savingArtIndex))->SeenSelected)) 
            {
                ThisDoc->savingArtIndex = ThisDoc->curSaveArtIndex = (ThisDoc->savingArtIndex + 1) % (int) (group->total_headers);
                if (ThisDoc->savingArtIndex == startIndex)
                {
                    AbortDownload = 1;
					break;
                }
            }

            if (!IsFlagSet(ThisDoc, PreviewDecode) && ThisDoc->savingArtIndex == startIndex &&        /* this shouldn't happen */
                !(ART_SELECTED & (header_elt (headers, ThisDoc->savingArtIndex))->SeenSelected)) 
            {
                ThisDoc->SelectedLines = 0;
            }


            if (AbortDownload || (ThisDoc->SelectedLines == 0 && !IsFlagSet(ThisDoc, PreviewDecode) )) /* done */
            {  

                if (IsFlagSet(ThisDoc, PreviewDecode) && AbortDownload)
				{
					//clear aborted preview flags
					for (long nArt = 0; nArt < group->total_headers; nArt++)
					{
						if (headers[nArt].SeenSelected & ART_PREVIEW)
							headers[nArt].SeenSelected &= ~ART_PREVIEW;	
					}
				}
				if (!AbortDownload && AutoDecode && !IsFlagSet(ThisDoc, PreviewDecode))
                {
                    for (ih = 0, found = FALSE; !found && ih < MAXGROUPWNDS; ih++) 
                    {
                        if (GroupDocs[ih].InUse  && GroupDocs[ih].SelectedLines > 0) 
                        {
                            found = TRUE;
                            break;
                        }
                    }
                    
                    if (found) //found another group to dl
                    {
                        SetStatbarPercent(ThisDoc->hWndFrame,0,ThisDoc,TRUE);

						PostMessage(GroupDocs[ih].hWndFrame, AutoDecode == 1 ? WM_MULTIDECODE : WM_MULTISAVE, 0, 0);
                        SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
                        InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
                        unlock_headers (header_handle, thread_handle);
                        ThisDoc->savingArtIndex = -1;   /* reset to non-saving state */
                        return 0;
                    }


                }

                SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
                if (ThisDoc->numArtsSaved + ThisDoc->numArtsError > 0) 
                {

                    if (EnableDownloadSnd && *szDownloadSnd && !IsFlagSet(ThisDoc, PreviewDecode))
                    {
#ifdef _WIN32
                        char *p = strchr(szDownloadSnd, '%');
						if (p && strchr(p+1, '%') )
						{
							char buf[_MAX_PATH];

							DWORD dwSize = ExpandEnvironmentStrings(szDownloadSnd, buf,_MAX_PATH );
							if (dwSize)
							{
								buf[_MAX_PATH-1] = '\0';
								PlaySound( buf, NULL, SND_FILENAME);
							}
						}
						else
						{
							PlaySound( szDownloadSnd, NULL, SND_FILENAME);
						}
#else 
                        sndPlaySound( szDownloadSnd, SND_SYNC);
#endif
                    }


                    if (CodingState == INACTIVE) 
                    {
                        SetStatbarPercent(ThisDoc->hWndFrame,0,ThisDoc,TRUE);
                        sprintf(mybuf, "%d", ThisDoc->numArtsSaved);
                        AutoDecode = 0;
                        if (ThisDoc->numArtsError > 0)
                        {
                            IdString = IDS_STBAR_DONE1_ERR;

                            if (ThisDoc->numArtsSaved > 1)
                                IdString = IDS_STBAR_DONEX_ERR;

                                WVArgXSetStatbarText(ThisDoc->hWndFrame, IdString, ThisDoc, TRUE, TRUE, 
                                       ThisDoc->numArtsSaved, SaveArtFileName, ThisDoc->numArtsError);

                        }
                        else
                        {
                            IdString = IDS_STBAR_DONE1_SAVE;

                            if (ThisDoc->numArtsSaved > 1) 
                                IdString = IDS_STBAR_DONEX_SAVE;

                            WVArgXSetStatbarText( ThisDoc->hWndFrame, IdString, ThisDoc, TRUE, TRUE,
                                    ThisDoc->numArtsSaved, SaveArtFileName);
                        }
                    }
                }
                else
                {
                    if (!IsFlagSet(ThisDoc, PreviewDecode))
						WVSetStatbarText (ThisDoc->hWndFrame, IDS_STBAR_NO_ARTICLE_SEL, ThisDoc, TRUE, TRUE);
                }
                if (CodingState != INACTIVE) 
                    DecodeDone (IsFlagSet(ThisDoc, PreviewDecode));

                ClearFlags(ThisDoc, PreviewDecode);
				SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
                InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
                unlock_headers (header_handle, thread_handle);
                ThisDoc->savingArtIndex = -1;   /* reset to non-saving state */
            }
            else 
            {
                if (ThisDoc->numArtsError > 0)
                {
                    IdString = IDS_STBAR_SAVED1_ERR;

                    if (ThisDoc->numArtsSaved > 1)
                        IdString = IDS_STBAR_SAVEDX_ERR;

                    WVArgXSetStatbarText(ThisDoc->hWndFrame, IdString, ThisDoc, TRUE, TRUE, 
                        ThisDoc->numArtsSaved, SaveArtFileName,ThisDoc->numArtsError);

                }
                else
                {
                    IdString = IDS_STBAR_SAVED1_SAVE;

                    if (ThisDoc->numArtsSaved > 1) 
                        IdString = IDS_STBAR_SAVEDX_SAVE;

                    WVArgXSetStatbarText( ThisDoc->hWndFrame, IdString, ThisDoc, TRUE, TRUE,
                            ThisDoc->numArtsSaved, SaveArtFileName);
                }

                if (!IsFlagSet(ThisDoc, PreviewDecode))
					SetStatbarPercent(ThisDoc->hWndFrame,
						((int)(ThisDoc->numArtsSaved * 100) / (ThisDoc->numArtsSaved + ThisDoc->SelectedLines)),ThisDoc,TRUE);

                if (KeepArticleHeaderVisible)
                    AdjustTopScByDoc (ThisDoc, ThisDoc->savingArtIndex);

                if (hCodedBlockWnd) 
                {
                    if (!IsFlagSet(ThisDoc, PreviewDecode))
					{
						sprintf (str, "%d", ThisDoc->SelectedLines);
						WVArgXSetWindowText (hCodedBlockWnd, IDS_WVGROUP_STATUS, str);
					}
					else
					{
						strcpy(str, "Previewing...");
						//sprintf (str, "%d", ThisDoc->SelectedLines);
					}

                    
                }
                sprintf (mybuf, "%I64d", (header_elt (headers, ThisDoc->savingArtIndex))->number);
                (header_elt (headers, ThisDoc->savingArtIndex))->SeenSelected |= ART_SEEN;
                (header_elt (headers, ThisDoc->savingArtIndex))->SeenSelected &= ~ART_ERROR;
                unlock_headers (header_handle, thread_handle);

                if (CodingState != INACTIVE) 
                {
                    if (GET_WM_COMMAND_ID(wParam, lParam) != ID_ARTICLE_RETRIEVE_RESTART && (currentCoded = InitCoded (hWnd)) == NULL) 
                    {
                        WVMessageBox(hWnd, IDS_WVGROUP_LOW_MEM, IDS_WVGROUP_INIT_ERROR, MB_OK);
                        DecodeDone ();
                        SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
                        ThisDoc->savingArtIndex = -1;
						ClearFlags(ThisDoc, PreviewDecode);
                        break;
                    }
                    InitiateReceiveArticle (ThisDoc, mybuf);
                    UpdateBlockStatus (IsFlagSet(ThisDoc, PreviewDecode), ThisDoc->hWndFrame);   // display some initial status
                }
                else 
                {
                    ViewArticle (ThisDoc, ThisDoc->savingArtIndex, REUSE, NO_SHOW, NO_ID);
                }
            }
            InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
            return (0);
            }
        }
        break;

    case IDM_CANCEL_DOWNLOAD:
        //only warn if use keeps trying to cancel download
        WVSetStatbarText (ThisDoc->hWndFrame, IDS_STBAR_CANCEL, ThisDoc, TRUE, TRUE);

        if (AbortDownload)
            WVMessageBox(hWnd, IDS_WVGROUP_ABORTDL, IDS_WVGROUP_ABORTDL_T, MB_OK | MB_ICONSTOP);

        if (!AbortDownload)
            AbortDownload = 1;
            
        break;
    }
    default:
        return (DefWindowProc (hWnd, message, wParam, lParam));
    }

    if (GetFocus () == hWnd)
        SetFocus (ThisDoc->hDocWnd);
    return (0);
}

/* Ray Dunn:99/10: */

/* Positions of the BY_POSITION items in the menus (Damn windows!) */

#define SORT_MENU_IN_WINDOW_MENU 1

/* Ray Dunn:99/10: Modified for DONT_SORT */
#define DATE_IN_VIEW_MENU    4
#define SUBJECT_IN_VIEW_MENU 5
#define AUTHOR_IN_VIEW_MENU  10

  
#define TIME_IN_DATE_MENU    0
#define SUBJECT_IN_DATE_MENU 1
#define LINES_IN_DATE_MENU   2
#define ARTNUM_IN_DATE_MENU  3
#define AUTHOR_IN_DATE_MENU  4
#define FILENAME_IN_DATE_MENU 5

#define DATE_IN_SUBJECT_MENU   0
#define LINES_IN_SUBJECT_MENU  1
#define ARTNUM_IN_SUBJECT_MENU 2
#define AUTHOR_IN_SUBJECT_MENU 3

#define DATE_IN_AUTHOR_MENU    0
#define SUBJECT_IN_AUTHOR_MENU 1
#define LINES_IN_AUTHOR_MENU   2
#define ARTNUM_IN_AUTHOR_MENU  3
#define FILENAME_IN_AUTHOR_MENU 4


void UncheckAllSortMenuItems(HMENU hMenu)
{
    CheckSortMenuItem(hMenu, IDM_DONT_SORT,           MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_DATE_TIME,      MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_DATE_SUBJECT,   MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_DATE_LINES,     MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_DATE_ARTNUM,    MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_DATE_FROM,      MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_DATE_FILENAME,  MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_SUBJECT_DATE,   MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_SUBJECT_LINES,  MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_SUBJECT_ARTNUM, MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_SUBJECT_FROM,   MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_LINES,          MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_THREADS,        MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_THREADSUB,      MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_ARTNUM,         MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_FROM_SUBJECT,   MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_FROM_DATE,      MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_FROM_LINES,     MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_FROM_ARTNUM,    MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_FROM_FILENAME,  MF_UNCHECKED);
    CheckSortMenuItem(hMenu, IDM_SORT_FILENAME,       MF_UNCHECKED);
}


void  CheckSortMenuItem(HMENU hMenu, int idItem, int CheckState)
{ 
    HMENU hViewMenu, hSubMenu;
    int i, j;

    hViewMenu = GetSubMenu(hMenu, SORT_MENU_IN_WINDOW_MENU);
    if (!hViewMenu)
        return;

    switch (idItem)
    {
        case IDM_SORT_DATE:
        case IDM_SORT_DATE_TIME:
        case IDM_SORT_DATE_SUBJECT:
        case IDM_SORT_DATE_LINES:
        case IDM_SORT_DATE_ARTNUM:
        case IDM_SORT_DATE_FROM:
		case IDM_SORT_DATE_FILENAME:
            i = DATE_IN_VIEW_MENU;
            break;

        case IDM_SORT_SUBJECT:
        case IDM_SORT_SUBJECT_DATE:
        case IDM_SORT_SUBJECT_LINES:
        case IDM_SORT_SUBJECT_ARTNUM:
        case IDM_SORT_SUBJECT_FROM:
            i = SUBJECT_IN_VIEW_MENU;
            break;

        case IDM_DONT_SORT:
        case IDM_SORT_LINES:
        case IDM_SORT_THREADS:
        case IDM_SORT_THREADSUB:
        case IDM_SORT_ARTNUM:
        case IDM_SORT_FILENAME:
            CheckMenuItem(hMenu, idItem, MF_BYCOMMAND | CheckState);
            return;

        case IDM_SORT_FROM:
        case IDM_SORT_FROM_SUBJECT:
        case IDM_SORT_FROM_DATE:
        case IDM_SORT_FROM_LINES:
        case IDM_SORT_FROM_ARTNUM:
		case IDM_SORT_FROM_FILENAME:
            i = AUTHOR_IN_VIEW_MENU;
            break;

        default:
            return;
    }

    CheckMenuItem(hViewMenu, i, MF_BYPOSITION | CheckState);

    switch (idItem)
    {
        case IDM_SORT_DATE_TIME:
            j = TIME_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_SUBJECT:
            j = SUBJECT_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_LINES:
            j = LINES_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_ARTNUM:
            j = ARTNUM_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_FROM:
            j = AUTHOR_IN_DATE_MENU;
            break;
		case IDM_SORT_DATE_FILENAME:
			j = FILENAME_IN_DATE_MENU;
			break;
        case IDM_SORT_SUBJECT_DATE:
            j = DATE_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_SUBJECT_LINES:
            j = LINES_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_SUBJECT_ARTNUM:
            j = ARTNUM_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_SUBJECT_FROM:
            j = AUTHOR_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_FROM_DATE:
            j = DATE_IN_AUTHOR_MENU;
            break;
        case IDM_SORT_FROM_SUBJECT:
            j = SUBJECT_IN_AUTHOR_MENU;
            break;
        case IDM_SORT_FROM_LINES:
            j = LINES_IN_AUTHOR_MENU;
            break;
        case IDM_SORT_FROM_ARTNUM:
            j = ARTNUM_IN_AUTHOR_MENU;
            break;
		case IDM_SORT_FROM_FILENAME:
			j =	FILENAME_IN_AUTHOR_MENU;
			break; 

        default:
            return;
    }

    hSubMenu = GetSubMenu(hViewMenu, i);
    if (!hSubMenu)
        return;
    
    CheckMenuItem(hSubMenu, j, MF_BYPOSITION | CheckState);
}


static void
EnableSortMenuItem(HMENU hMenu, int idItem, int mode)
{ 
    HMENU hSubMenu, hViewMenu;
    int i, j;

    hViewMenu = GetSubMenu(hMenu, SORT_MENU_IN_WINDOW_MENU);
    if (!hViewMenu)
        return;

    switch (idItem)
    {
        case IDM_SORT_DATE:
        case IDM_SORT_DATE_TIME:
        case IDM_SORT_DATE_SUBJECT:
        case IDM_SORT_DATE_LINES:
        case IDM_SORT_DATE_ARTNUM:
        case IDM_SORT_DATE_FROM:
		case IDM_SORT_DATE_FILENAME:
            i = DATE_IN_VIEW_MENU;
            break;

        case IDM_SORT_SUBJECT:
        case IDM_SORT_SUBJECT_DATE:
        case IDM_SORT_SUBJECT_LINES:
        case IDM_SORT_SUBJECT_ARTNUM:
        case IDM_SORT_SUBJECT_FROM:
            i = SUBJECT_IN_VIEW_MENU;
            break;

        case IDM_DONT_SORT:
        case IDM_SORT_LINES:
        case IDM_SORT_THREADS:
        case IDM_SORT_THREADSUB:
        case IDM_SORT_ARTNUM:
        case IDM_SORT_FILENAME:
            EnableMenuItem(hViewMenu, idItem, MF_BYCOMMAND | mode);
            return;

        case IDM_SORT_FROM:
        case IDM_SORT_FROM_SUBJECT:
        case IDM_SORT_FROM_DATE:
        case IDM_SORT_FROM_LINES:
        case IDM_SORT_FROM_ARTNUM:
		case IDM_SORT_FROM_FILENAME:
            i = AUTHOR_IN_VIEW_MENU;
            break;

        default:
            return;
    }

    EnableMenuItem(hViewMenu, i, MF_BYPOSITION | mode);

    switch (idItem)
    {
        case IDM_SORT_DATE_TIME:
            j = TIME_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_SUBJECT:
            j = SUBJECT_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_LINES:
            j = LINES_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_ARTNUM:
            j = ARTNUM_IN_DATE_MENU;
            break;
        case IDM_SORT_DATE_FROM:
            j = AUTHOR_IN_DATE_MENU;
            break;
		case IDM_SORT_DATE_FILENAME:
			j = FILENAME_IN_DATE_MENU;
			break;
        case IDM_SORT_SUBJECT_DATE:
            j = DATE_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_SUBJECT_LINES:
            j = LINES_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_SUBJECT_ARTNUM:
            j = ARTNUM_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_SUBJECT_FROM:
            j = AUTHOR_IN_SUBJECT_MENU;
            break;
        case IDM_SORT_FROM_DATE:
            j = DATE_IN_AUTHOR_MENU;
            break;
        case IDM_SORT_FROM_SUBJECT:
            j = SUBJECT_IN_AUTHOR_MENU;
            break;
        case IDM_SORT_FROM_LINES:
            j = LINES_IN_AUTHOR_MENU;
            break;
        case IDM_SORT_FROM_ARTNUM:
            j = ARTNUM_IN_AUTHOR_MENU;
            break;
		case IDM_SORT_FROM_FILENAME:
			j = FILENAME_IN_AUTHOR_MENU;
			break;
        default:
            return;
    }

    hSubMenu = GetSubMenu(hViewMenu, i);
    if (!hSubMenu)
    return;

    EnableMenuItem(hSubMenu, j, MF_BYPOSITION | mode);
}
/* Ray Dunn:99/10:End */

// WVSeendb member functions

WMemPool WVSeenDB::m_ClassPool(sizeof(WVSeenDB), POOLBLOCKSIZE / sizeof(WVSeenDB));

WVSeenDB::WVSeenDB(UBIGINT uLo, UBIGINT uHi, WVSeenDB*pSDB)
: m_uLowVal(uLo), m_uHiVal(uHi), m_uLowBase(0), m_uHiBase(0), m_pBitmap(NULL)
{
#if 1 // S.Yoshida for NEWSRC_MAKE_COMPAT
    if( uHi < uLo )
        uHi = uLo;
#endif

    if (pSDB) 
    {
        CreateBitmap(uLo,uHi);
        TypRange r;
        r.First = r.Last = 0;
        pSDB->GetRange(r);
        while (r.First) 
        {
	        MarkSeen(r.First,r.Last);
            pSDB->GetRange(r);
        }
    }
}  


void WVSeenDB::CreateBitmap(UBIGINT uLo, UBIGINT uHi, BOOL bCompact/* = FALSE*/)
{
    UINT_PTR * newpBitmap;
    UINT_PTR * pBm;


    UBIGINT minVal,maxVal;
    UBIGINT minBase,maxBase;
    UBIGINT i,j,uNumInts;

    if (!this) /* invalid usage */
        return;
        
    if (bCompact && !m_pBitmap)
		return;

    if (bCompact)
    {
		minVal = max(uLo,m_uLowVal);
		maxVal = min(uHi,m_uHiVal);    
    }
    else
    {
		minVal = min(uLo,m_uLowVal);
		maxVal = max(uHi,m_uHiVal);

		/* now check if we can reuse the existing bitmap if any */
		if (m_pBitmap) 
		{
			if ((minVal >= m_uLowBase) && (maxVal < m_uHiBase + BMSIZE)) 
			{
				m_uLowVal = minVal;
				m_uHiVal = maxVal;
				return;
			}
		}
	}

    /* in the all seen state is m_uLowVal = m_uHiVal + 1, if the new bitmap fit also */
    /* simply return, because we are unable to create a bitmap with negative length */
    if (minVal > maxVal)
        return;

    minBase = ((minVal/BMSIZE) * BMSIZE);
    maxBase = ((maxVal/BMSIZE) * BMSIZE);

    if ( maxBase - minBase > MaxDBSize )
    {
#ifdef _DEBUG
		MessageBeep( (UINT) -1);
#endif
        //return;

    }

    uNumInts = 1 + (maxBase - minBase)/BMSIZE;



    if (uNumInts < (UINT_MAX3264 / sizeof(UINT_PTR) )) 
    {
        newpBitmap = new UINT_PTR[ (UINT_PTR) uNumInts];
#ifdef _WIN64
        TRACE("allocated %I64d bytes @ 0x%08I64x\r\n", (UINT_PTR)(sizeof(UINT_PTR) * uNumInts), (INT_PTR)newpBitmap);
#else
         TRACE("allocated %d bytes @ 0x%08x\r\n", (int)(sizeof(UINT_PTR) * uNumInts), (INT_PTR)newpBitmap);
#endif
    } 
    else 
    {
#ifdef __WATCOMC__
        newpBitmap = new ((unsigned long)uNumInts) unsigned long[1];
#else
        newpBitmap = new UINT_PTR[(UINT_PTR)uNumInts]; //was new size_t[4,uNumInts]; 
        TRACE("allocated %I64d bytes @ 0x%08I64x\r\n", (INT_PTR) sizeof(UINT_PTR)*uNumInts, (INT_PTR) newpBitmap );    
#endif
    if (!newpBitmap)
    {
        WVMessageBox(NULL, IDS_WVGROUP_LOW_MEM, IDS_WVGROUP_INIT_ERROR, MB_OK);
        return;
    }


    }
    if (m_pBitmap) 
    {
        /* copy old bitmap */
        if (bCompact)
        {
			UINT_PTR * pBMCur;
			pBm = newpBitmap;
			j = 0;
			i = (minBase - m_uLowBase)/BMSIZE;
			pBMCur = m_pBitmap + i;
			for (j = 0; j < uNumInts; j++ )
				*pBm++ = *(pBMCur++);
				
			TRACE("Reduced SeenDB from %I64d to %I64d\r\n",  (m_uHiBase-m_uLowBase)/8,uNumInts * sizeof(UINT_PTR));
			delete m_pBitmap; /* the old Bitmap is not longer needed */
			
			m_pBitmap = newpBitmap;
			m_uLowBase = minBase;
			m_uHiBase = maxBase;
			m_uLowVal = minVal;
			m_uHiVal = maxVal;
			return;				
			
        }
		uNumInts = (m_uLowBase - minBase ) /BMSIZE;
		i = 0;
		pBm = newpBitmap;

		while ( i++ < uNumInts )
			*pBm++ = ALLBITS; /* old articles are seen by default */
			//newpBitmap[i++] = ALLBITS; /* old articles are seen by default */


        j = 0;
        uNumInts = 1 + (m_uHiBase - m_uLowBase)/BMSIZE;  

        while ( j < uNumInts )
            *pBm++ = m_pBitmap[j++];
            //newpBitmap[i++] = m_pBitmap[j++];

        uNumInts = (maxBase - m_uHiBase)/BMSIZE;

        for (j = 0; j < uNumInts; j++)
            *pBm++ = 0L;
            //newpBitmap[i++] = 0L;
         
        TRACE("freed memory @ 0x%08lx\r\n", m_pBitmap);
		delete m_pBitmap; /* the old Bitmap is not longer needed */

        m_pBitmap = NULL;

    } 
    else 
    {
        /* init all as unseen */ 
        pBm = newpBitmap;

        for (i = 0; i < uNumInts; i++)
            *pBm++ = 0L; 

        /* mark old articles as seen as default */
        /* go until m_uLowVal in case we have extended the lower boundary of the bitmap */
        for (i = minBase; i < m_uLowVal; i++) 
        {
            j = (i - minBase) / BMSIZE;
            //UINT_PTR mod = (i % BMSIZE);
            //UINT_PTR shift = (1ui64 << mod);
            //newpBitmap[j] |= shift;
            newpBitmap[j] |= ((UINT_PTR)1 << (i % BMSIZE));
        }
    }
    m_pBitmap = newpBitmap;
    m_uLowBase = minBase;
    m_uHiBase = maxBase;
    m_uLowVal = minVal;
    m_uHiVal = maxVal;
}

void WVSeenDB::MarkAllSeen(UBIGINT uArtIdxHi)
{
	if (this)
	{
		m_uHiVal = uArtIdxHi;	
		m_uLowVal = m_uHiVal + 1;
	
		if ( m_pBitmap)
		{
     
			TRACE("freed memory @ 0x%08lx\r\n", m_pBitmap);
			delete [] m_pBitmap;

			m_pBitmap = NULL; 
		}
	}
}
// the following is mainly for the 16-Bit version, which is limited by windows
// Windows 3.x is only able to hande 64K Memoryhandles
// for each group containing a significant number of unread articles
// normaly one memory-handle is allocated.
//
// case 1: all articles unseen simply delete the bitmap
// case 2: all articles seen, set m_uLowVal to m_uHiVal + 1 and delete the bitmap
// case 3: there are a significant amount of seen arts, make a smaller DB from 1st unseen to last seen
void WVSeenDB::DestroyBitmap(void)
{
    if (this && m_pBitmap) 
    {
		UBIGINT i;
		UBIGINT uNumInts = (m_uHiBase - m_uLowBase)/BMSIZE;
		BOOL SomeSeen;
		BOOL SomeUnseen;

		/* test all between the second and the pre-last doubleword */
		SomeSeen = SomeUnseen = FALSE;
		for(i = 1; i < uNumInts; i++) 
		{
			if (m_pBitmap[i] != 0)
				SomeSeen = TRUE;
			if (m_pBitmap[i] != ALLBITS)
				SomeUnseen = TRUE;
			if (SomeSeen && SomeUnseen)
				break;
		}
		if (SomeSeen && SomeUnseen && (m_uHiVal-m_uLowVal >0x100000*8)) //more than 1mb of data
		{

			UBIGINT lo, hi;
			UBIGINT lotrigger = m_uLowVal + (m_uHiVal-m_uLowVal)/4; //25%
			UBIGINT hitrigger = m_uHiVal - (m_uHiVal-m_uLowVal)/4; //75%
			TypRange r;
			r.First = r.Last = 0;
			GetRange(r);
			if (r.Last)
			{
				hi = lo = r.Last; //1st unseen
				while (r.Last)
				{
					hi = r.Last; //last seen
					GetRange(r);
				}
				if (lo > lotrigger || hi <  hitrigger)
					CreateBitmap(lo, hi, TRUE); //make a smaller DB hopefully

			}
			return;
		}
		for(i = m_uLowVal; i <= min(m_uHiVal,(m_uLowBase + BMSIZE -1)) && !(SomeSeen && SomeUnseen); i++) 
		{
			if (WasSeen(i))
				SomeSeen = TRUE;
			else
				SomeUnseen = TRUE;
		}
		for (i = m_uHiBase; i<= m_uHiVal && !(SomeSeen && SomeUnseen); i++) 
		{
			if (WasSeen(i))
				SomeSeen = TRUE;
			else
				SomeUnseen = TRUE;
		}
		if (SomeSeen && SomeUnseen)
			return;
		if (!SomeUnseen) 
		{ /* all seen */
			m_uLowVal = m_uHiVal + 1;
			m_uLowBase = (m_uLowVal/BMSIZE)*BMSIZE;
		}
     
		TRACE("freed memory @ 0x%08lx\r\n", m_pBitmap);
		delete [] m_pBitmap;

		m_pBitmap = NULL;        
    }
	else if (this)
	{
		m_uLowVal = m_uHiVal + 1;

	}
}
  
// Mark an article as 'seen'
void WVSeenDB::MarkSeen(UBIGINT uArtIdx)
{
    UINT_PTR uArrIdx;

    if (this && (uArtIdx >= m_uLowVal)) 
    {
        if (uArtIdx > m_uHiVal)
		{
        // reallocate the Bitmap, because we should mark crosspostings as seen
        // even we have not opened the group yet
		    CreateBitmap(m_uLowVal,uArtIdx);
			if ( uArtIdx > m_uHiVal) //CreateBitmap ignored the request
			{
				TRACE("WVSeenDB::MarkSeen -Failed to create bitmap");
				return;
			}
		}
		else
            CreateBitmap(m_uLowVal,m_uHiVal);

        if(m_pBitmap) 
        {
            uArrIdx = (size_t) (uArtIdx - m_uLowBase) / BMSIZE;
            size_t val = m_pBitmap[uArrIdx];
            val |= ((UINT_PTR)1 << (uArtIdx % BMSIZE)); 
            m_pBitmap[uArrIdx] = val;
        }
    }
}


// Mark a range of articles as 'seen'
void WVSeenDB::MarkSeen(UBIGINT uArtIdx1, UBIGINT uArtIdx2)
{
    UBIGINT i,start,stop;


    if (this) 
    {
        if (uArtIdx1 < m_uLowVal && uArtIdx2 < m_uLowVal)
			return;
		/* articles below m_uLowVal are seen by default, don't mark them */
        uArtIdx1 = max(uArtIdx1,m_uLowVal);
        if(uArtIdx1 <= uArtIdx2) 
        {
            MarkSeen(uArtIdx2); /* create Bitmap and reallocate if necessary */
            /* handle the last unsigned long doubleword by bits */
            start = (uArtIdx2 / BMSIZE) * BMSIZE;
			
			if (start < uArtIdx1 ) 
            {
                /* but start at least with the first article */
                for (i = uArtIdx1; i < uArtIdx2; i++)
                    MarkSeen(i);
				return; /* done */
            }
            for (i = start; i < uArtIdx2; i++) /* uArtIdx2 is already marked seen */
                MarkSeen(i);
                
            /* handle first long word by bits */
            stop = (UBIGINT)((UBIGINT)(uArtIdx1/BMSIZE) * BMSIZE) + BMSIZE;
            if (stop >= start ) 
            {
                for (i = uArtIdx1 ; i < start; i++ )
                    MarkSeen(i);
                return; /* all necessary articles marked seen */
            }
            for (i = uArtIdx1; i < stop; i++ )
                MarkSeen(i);

            /* mark all from second doubleword until pre-last doubleword as seen */
            start = (start-m_uLowBase)/BMSIZE;
            stop = (stop-m_uLowBase)/BMSIZE;
            for (i = stop; i < start; i++)
                m_pBitmap[i] = ALLBITS;
        }
    }
}



// Mark an article as 'unseen'
void WVSeenDB::MarkUnseen(UBIGINT uArtIdx)
{

    if (this && (uArtIdx <= m_uHiVal)) 
    {
        if (uArtIdx < m_uLowVal)
		{
            CreateBitmap(uArtIdx,m_uHiVal); /* reallocate Bitmap */
			if ( uArtIdx < m_uLowVal) //did it happen?
				return;
		}
        else
            CreateBitmap(m_uLowVal,m_uHiVal);
        if(m_pBitmap) 
        {
            UINT_PTR uArrIdx = (uArtIdx - m_uLowBase) / BMSIZE;
            UINT_PTR val = m_pBitmap[uArrIdx];

            val &= ~((UINT_PTR)1 << (uArtIdx % BMSIZE));
            m_pBitmap[uArrIdx] = val;
        }
    }
}

// Mark a range of articles as 'unseen'
void WVSeenDB::MarkUnseen(UBIGINT uArtIdx1, UBIGINT uArtIdx2)
{
    UBIGINT i,start,stop;

    if (this) 
    {
        /* articles below m_uLowVal are seen by default, don't mark them */
        uArtIdx1 = max(uArtIdx1,m_uLowVal);
        if(uArtIdx1 <= uArtIdx2) 
        {
            MarkUnseen(uArtIdx2); /* create Bitmap and reallocate if necessary */
            /* handle the last unsigned long doubleword by bits */
            start = (uArtIdx2/BMSIZE) * BMSIZE;
            if (start < uArtIdx1 ) 
            {
                /* but start at least with the first article */
                for (i = uArtIdx1; i < uArtIdx2; i++)
                    MarkUnseen(i);
                return; /* all necessary articles marked unseen */
            }
            for (i = start; i < uArtIdx2; i++) /* uArtIdx2 is already marked unseen */
                MarkUnseen(i);
                
            /* handle first long word by bits */
            stop = ((uArtIdx1/BMSIZE) * BMSIZE) + BMSIZE;
            if (stop >= start ) 
            {
                for (i = uArtIdx1 ; i < start; i++ )
                    MarkUnseen(i);
                return; /* all necessary articles marked seen */
            }
            for (i = uArtIdx1; i < stop; i++ )
                MarkUnseen(i);

            /* mark all from second doubleword until pre-last doubleword as unseen */
            UINT_PTR startidx = (start-m_uLowBase)/BMSIZE;
            UINT_PTR stopidx = (stop-m_uLowBase)/BMSIZE;
            for (UINT_PTR j = stopidx; j < startidx; j++)
                m_pBitmap[j] = ~ALLBITS;
        }
    }
}



// Has a particular article been 'seen'?
BOOL WVSeenDB::WasSeen(UBIGINT uArtIdx)
{
    UINT_PTR uArrIdx;

    if (this && uArtIdx) 
    {
        if (uArtIdx < m_uLowVal)
            return TRUE;
        if(m_pBitmap && (uArtIdx <= m_uHiVal)) 
        {
            uArrIdx = (UINT_PTR)((uArtIdx - m_uLowBase)/ BMSIZE);

            UINT_PTR val = m_pBitmap[uArrIdx];
            if( val & ((UINT_PTR)1 << (uArtIdx % BMSIZE)))
                return TRUE;
        }
    }
    return FALSE;
}


// Count number of unseen articles
UBIGINT WVSeenDB::NumUnseen()
{
    UBIGINT uBitIdx1, uBitIdx2;
    size_t uArrInd;
    UBIGINT uMaxInd;
    UBIGINT uRetval = 0;
    register UINT_PTR test;

    if(this && (m_uLowVal < m_uHiVal)) //MDS changed 5/2011 from <= since low=high = no unseen
    {
        uRetval = (m_uHiVal - m_uLowVal) + 1;
        if (m_pBitmap) 
        {
            uRetval = 0;
            uMaxInd = (m_uHiBase - m_uLowBase)/BMSIZE;

            uBitIdx2 = uMaxInd ? BMSIZE - 1 : m_uHiVal % BMSIZE;
            for(uBitIdx1 = m_uLowVal % BMSIZE; uBitIdx1 <= uBitIdx2; uBitIdx1++)
            {
                if(!(m_pBitmap[0] & ((UINT_PTR)1 << uBitIdx1)))
                uRetval++;
            }
            if(uMaxInd) // more than one array member
            {
                uBitIdx2 = m_uHiVal % BMSIZE;
                for(uBitIdx1 = 0; uBitIdx1 <= uBitIdx2; uBitIdx1++)
                {
                    if(!(m_pBitmap[uMaxInd] & ((UINT_PTR)1 << uBitIdx1)))
                        uRetval++;
                }
                for(uArrInd = 1; uArrInd < uMaxInd; uArrInd++)
                {
                    test = m_pBitmap[uArrInd];
                    if(test != ALLBITS) // all seen
                    {  
                        if(test == 0L) // all unseen
                            uRetval += BMSIZE;
                        else  // count 'em
                        {
                            for(uBitIdx1 = 0; uBitIdx1 < BMSIZE; uBitIdx1++)
                            {
                                /* Ray Dunn:99/10: Gotcha.  This bug caused the unseen count to be wrong
                                when there were unseen spread through the articles - it counted the *seen*
                                articles in the bitmap array word when that word was not 0 or ALLBITS!!
                                if ( test & 1)
                                */

                                if (!(test & 1))
                                    uRetval++;

                                /* Ray Dunn:99/10:End */
                            
                                test >>= 1;

                            }
                        }
                    }
                }
            }
        }
    }
    return uRetval;
}

// Find the highest seen article
// Return:
//   Highest seen article (if found)
//   Lowest article number if all seen
UBIGINT WVSeenDB::HighestSeen()
{
    UBIGINT uRetval;

    if (this) 
    {
        for(uRetval = m_uHiVal; uRetval >= m_uLowVal; uRetval--)
        {
            if(WasSeen(uRetval))
                return uRetval;
        }
        return m_uLowVal ? m_uLowVal - 1 : 0;
    }
    return 0;
}




// Find the next unseen article, starting with 'uNext'
// Return:
//   Next unseen article number (if found)
//   Highest article number + 1 if all seen
//   0 if invalid, no bitmap available, etc.
UBIGINT WVSeenDB::NextUnseen(UBIGINT uNext)
{
    UBIGINT uBitIdx1;
    UBIGINT uBitIdx2;
    UINT_PTR uArrIdx;
    UBIGINT uMaxInd;
    UBIGINT uRetval = 0;

    if (this) 
    {
        uRetval = max(uNext, m_uLowVal);
        if(m_pBitmap)
        {
            //if(uNext > m_uHiVal)
            //  return m_uHiVal + 1;
            uMaxInd = (m_uHiBase - m_uLowBase)/BMSIZE;

            for(uArrIdx = (UINT_PTR)(uRetval - m_uLowBase)/ BMSIZE; uArrIdx <= uMaxInd; uArrIdx++)
            {
                uBitIdx1 = uRetval % BMSIZE;
                if((!uBitIdx1) && m_pBitmap[uArrIdx] == ALLBITS ) // all seen
                    uRetval += BMSIZE;
                else
                {
                    uBitIdx2 = (uArrIdx == uMaxInd) ? m_uHiVal % BMSIZE : BMSIZE - 1;
                    while(uBitIdx1 <= uBitIdx2)
                    {
                        if(!(m_pBitmap[uArrIdx] & ((UINT_PTR)1 << uBitIdx1))) 
                        {
                            return uRetval;
                        }
                        uBitIdx1++;
                        uRetval++;
                    }
                }
            }
        }
        if(uRetval > m_uHiVal)
        {
            uRetval = m_uHiVal + 1;
        }
    }
    return uRetval;
}


// Find the next seen article, starting with 'uNext'
// Return:
//   Next seen article number (if found)
//   0 if all seen, invalid, no bitmap available, etc.
UBIGINT WVSeenDB::NextSeen(UBIGINT uNext)
{
    UBIGINT uBitIdx1;
    UBIGINT uBitIdx2;
    UBIGINT uArrIdx;
    UBIGINT uMaxInd;
    UBIGINT uRetval = 0;

    if (this) 
    {
        if (uNext < m_uLowVal)
            return uNext;
        if(m_pBitmap && uNext <= m_uHiVal)
        {
            uMaxInd = (m_uHiBase - m_uLowBase)/BMSIZE;
            uRetval = uNext;

            for(uArrIdx = (uRetval - m_uLowBase)/ BMSIZE; uArrIdx <= uMaxInd; uArrIdx++)
            {
                uBitIdx1 = uRetval % BMSIZE;
                if((!uBitIdx1) && m_pBitmap[uArrIdx] == 0L) // all unseen
                    uRetval += BMSIZE;
                else
                {
                    uBitIdx2 = (uArrIdx == uMaxInd) ? m_uHiVal % BMSIZE : BMSIZE - 1;
                    while(uBitIdx1 <= uBitIdx2)
                    {
                        if(m_pBitmap[uArrIdx] & ((UINT_PTR)1 << uBitIdx1)) 
                        {
                            return uRetval;
                        }
                        uBitIdx1++;
                        uRetval++;
                    }
                }
            }
            if(uRetval > m_uHiVal)
                uRetval = 0;
        }
    }
    return uRetval;
}

// Get the next range.  Return {0,0} at end of range or invalid
void WVSeenDB::GetRange(TypRange& r)
{
    if(this)
    {
        r.First = NextSeen(r.Last + 1);
        if(r.First) 
        {
            r.Last = NextUnseen(r.First + 1) - 1;
            if ((r.First<=r.Last) && (r.Last <= m_uHiVal))
                return;
        }
    }

    r.First = r.Last = 0;
    return;
}

