/*
 * $Id: wvart.cpp 1.3 1997/02/14 20:14:15 dumoulin Exp $
 */

/*---  wvart.cpp ------------------------------------------- */
/*  This file contains the window procedure for the Article Viewing window
 *  for WinVn.  (In RCS this files was formally wvart.c v 1.52)
 */

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <ctype.h>
#include "WVClass.h"
#ifdef COMMCTRL
#include <commctrl.h>     /* for tooltips */
#else
extern "C"
{
#include "wvtb\wvtb.h"
}
#endif
#include <string.h>
#include <shellapi.h>
#include <ddeml.h>

/********************* find_article_by_subject *********************
 *
 *   Amended to ignore Re: in either heading line as part of match
 *   criteria
 *
 *  Note the duplication of Knowledge of the rules of subject header
 *  format with wvheader : GetArticleSubject
 *
 *  Ideally some object containing all knowledge of rules of format
 *  would be a more elegant implementation
 *  Also note heading changes with each find next of same heading
 *  request. Ideally want to maintain first heading in series
 *  allowing a more liberal interpreation of 'same' in same heading
 *
 *                       Matthew Bretherton 2nd March 1993
 *
 ******************************************************************/

long 
find_article_by_subject (header_p headers,
                         long artindex,
                         long num_headers)
{
  const char far *nextsub;
  const char far *thissub;

  thissub = (header_elt (headers, artindex++))->pHD->sSubject;
  if (_strnicmp (thissub, "Re:", 3) == 0)
    thissub = thissub + 4;

  if (artindex >= num_headers)
    return (-1);

  do {
    nextsub = (header_elt (headers, artindex))->pHD->sSubject;
    if (_strnicmp (nextsub, "Re:", 3) == 0)
      nextsub = nextsub + 4;
    if (lstrcmpi (nextsub, thissub) == 0)
      return (artindex);        /* return the index */
  } while (artindex++ < num_headers);

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

//  WinVN to WWW Interface
//             
//                  Mitsuo Shimomai
//                  Kagoshima University, Japan
//                 
//    Mods to support User Interface  JD 7/24/95   
//    mods to support DDE Shimomai 97/1/10
BOOL
 DoShell (char far *client, char far *argument)
 {
   char execute[BIGMAXINTERNALLINE + MAXFILENAME];
   unsigned int err;

 #ifdef WIN32
   STARTUPINFO sInfo;
   PROCESS_INFORMATION pInfo;
   char modulename[BIGMAXINTERNALLINE + MAXFILENAME];
   char *p1, *p2;
#endif
   
    if (argument)
       _snprintf (execute, BIGMAXINTERNALLINE + MAXFILENAME, "%s %s", client, argument);
    else
       _snprintf (execute, BIGMAXINTERNALLINE + MAXFILENAME, "%s", client);

 #ifdef WIN32
   p1 = execute;
   p2 = modulename;
   if (execute[0] == '"') {
     p1 += 1;
     while ((*p1 != '\0') && (*p1 != '"')) *p2++=*p1++;
   } else {
     while ((*p1 != '\0') && (*p1 != ' ')) *p2++=*p1++;
   }
   *p2 = '\0';

   memset (&sInfo, 0, sizeof (STARTUPINFO));
   sInfo.cb = sizeof(sInfo);
   sInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
   sInfo.wShowWindow = SW_SHOWNORMAL;
   err = CreateProcess (NULL, execute, NULL, NULL, FALSE,
                       DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL,
                       &sInfo, &pInfo);
   if (err == TRUE) {           // successful

    CloseHandle (pInfo.hThread);    /* let it run detached */
    CloseHandle (pInfo.hProcess);
   }
   else {
     sprintf(str,"Error executing %s, error %u",execute, err); 
    SetStatbarText(CommDoc->hWndFrame,str,&NetDoc, TRUE, TRUE);
    return FALSE;
   }
#else
   if ((err = WinExec (execute, SW_SHOWNORMAL)) < 32) {
    sprintf(str,"Error executing %s, error %u",execute, err); 
    SetStatbarText(CommDoc->hWndFrame,str,&NetDoc, TRUE, TRUE);
    return FALSE;
   }
#endif
 return TRUE;
 }
 
#include <ddeml.h>

HDDEDATA CALLBACK DdeCallback(
	UINT uType,       /* transaction type                 */
	UINT uFmt,        /* clipboard data format            */
	HCONV hconv,      /* handle of conversation           */
	HSZ hsz1,         /* handle of string                 */
	HSZ hsz2,         /* handle of string                 */
	HDDEDATA hdata,   /* handle of global memory object   */
	DWORD dwData1,    /* transaction-specific data        */
	DWORD dwData2    /* transaction-specific data        */
	)
{
	return (HDDEDATA) NULL;
}

 
BOOL
DdeRequest(DWORD idInst, char *szServName, char *szSysTopic, char *szItem)
{
	HCONV hConv;
	HSZ hszServName, hszSysTopic, hszItem;
	BOOL result = FALSE;
	HDDEDATA hData = NULL;

	hszServName = szServName ?
				DdeCreateStringHandle(idInst, szServName, CP_WINANSI) : (HSZ) NULL;
	hszSysTopic = DdeCreateStringHandle(idInst, szSysTopic, CP_WINANSI);

	hConv = DdeConnect(
    	idInst,               /* instance identifier        */
    	hszServName,          /* service-name string handle */
    	hszSysTopic,          /* System-topic string handle */
    	(PCONVCONTEXT) NULL); /* use default context        */

	if (hConv) {
		hszItem = DdeCreateStringHandle(idInst, szItem, CP_WINANSI);
		hData = DdeClientTransaction(
			(unsigned char *)szItem,
			(DWORD) strlen(szItem),
			hConv,
			hszItem,
			CF_TEXT,
			XTYP_REQUEST,
			1000,
			NULL);
		DdeDisconnect(hConv);
		DdeFreeStringHandle(idInst, hszItem);
		if (hData != NULL) result = TRUE;
	}

	DdeFreeStringHandle(idInst, hszServName);
	DdeFreeStringHandle(idInst, hszSysTopic);
	return result;
}

#define DDECLIENT_NO_DIRECTIVE	0
#define DDECLIENT_NETSCAPE	1
#define DDECLIENT_IEXPLORE	2

BOOL
OpenURL(char *url)
{
	DWORD idInst = 0L;
	char *szServName = NULL;
	char szArg[MAXINTERNALLINE];
	BOOL result;

	if (DdeInitialize(&idInst,         /* receives instance identifier */
		(PFNCALLBACK) DdeCallback, /* address of callback function */
		APPCMD_CLIENTONLY,
		0)) {
		/* error handler */
		return FALSE;
	}
	if (DdeServID == DDECLIENT_NETSCAPE) szServName = "NETSCAPE";
	if (DdeServID == DDECLIENT_IEXPLORE) szServName = "IExplore";

	sprintf(szArg,"\"%s\",,-1,0,,,",url);
	result = DdeRequest(idInst, szServName, "WWW_OpenURL", szArg);
	if (!result && DdeServID == DDECLIENT_NO_DIRECTIVE) {
		szServName = "NETSCAPE";
		result = DdeRequest(idInst, szServName, "WWW_OpenURL", szArg);
	}
	if (!result && DdeServID == DDECLIENT_NO_DIRECTIVE) {
		szServName = "IExplore";
		result = DdeRequest(idInst, szServName, "WWW_OpenURL", szArg);
	}
	if (result) {
		DdeRequest(idInst, szServName, "WWW_Activate", "-1,0");
	}
	DdeUninitialize(idInst);
	return result;
}


//  WinVN to WWW Interface
//             
//                  Mitsuo Shimomai
//                  Kagoshima University, Japan
//                 
//    Modified by JD 7/24/95 to support User Interface,
//    use of environment variables and user definable
//    location of command line.
   
int
 URLCheckExecute(char far *str)
 {
   char far *Client, *CmdLine, *CmdLine1, *EnvVar, *p;
   unsigned int i,j,k,len;
   char envvar[MAXENVVAR];
   char cmdstring[BIGMAXINTERNALLINE + MAXFILENAME];
   char pstring[10];
   char hstring[MAXINTERNALLINE];
   char fstring[MAXINTERNALLINE];

   p = str + strlen(str) -1;
   if (*str=='<' && *p=='>' && strchr(str,'@'))
    return (FALSE);     // Maybe, Message-ID

   Client = NULL;
   hstring[0] = '\0';
   pstring[0] = '\0';
   fstring[0] = '\0';

   if( CmdLine = strstr(str,"http://") ) {
    Client = HttpClient;
    strcpy(pstring,"http");
    if (CmdLine1 = strstr(CmdLine+7,"/")) {
      strncpy(hstring,CmdLine+7,(int)((char far *)CmdLine1 - (char far *)(CmdLine+7)));
      strcpy(fstring,CmdLine1);
    }
   }
   else if( (CmdLine = strstr(str,"ftp://")) || 
    (CmdLine = strstr(str,"file://")) ) {
     Client = FtpClient;
     strcpy(pstring,"ftp");
     if (CmdLine1 = strstr(CmdLine+6,"/")) {
      len = (int)((char far *)CmdLine1 - (char far *)(CmdLine+6));
      strncpy(hstring,CmdLine+6,len);
      hstring[len]='\0';
      strcpy(fstring,CmdLine1);
    }
     if( Client == NULL ) Client = HttpClient;
   }
   else if(CmdLine = strstr(str,"gopher://")) {
     Client = GopherClient;
     strcpy(pstring,"gopher");
     if (CmdLine1 = strstr(CmdLine+9,"/")) {
      len = (int)((char far *)CmdLine1 - (char far *)(CmdLine+9));
      strncpy(hstring,CmdLine+9,len);
      hstring[len]='\0';
      strcpy(fstring,CmdLine1);
    }
     if( Client == NULL ) Client = HttpClient;
   }
   else if(CmdLine = strstr(str,"wais://")) {
     Client = WaisClient;
     strcpy(pstring,"wais");
     if (CmdLine1 = strstr(CmdLine+7,"/")) {
      len = (int)((char far *)CmdLine1 - (char far *)(CmdLine+7));
      strncpy(hstring,CmdLine+7,len);
      hstring[len]='\0';
      strcpy(fstring,CmdLine1);
    }
     if( Client == NULL ) Client = HttpClient;
   }
   else if(p = strstr(str,"://")) {
     Client = HttpClient;
     CmdLine1 = p;
     for (--p ; p >= str && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) ; p--);
       CmdLine = ++p;
     len = (int)((char far *)CmdLine1 - (char far *)CmdLine);
     strncpy(pstring, CmdLine, len);
     pstring[len]='\0';
     if (p = strstr(CmdLine1+3,"/")) {
       len = (int)((char far *)p - (char far *)(CmdLine1+3));
       strncpy(hstring,CmdLine1+3,len);
       hstring[len]='\0';
       strcpy(fstring,p);
     }
   }
   else if( strstr(str,":/") ) {
     Client = FtpClient;
     strcpy(pstring,"ftp");
     CmdLine = str;
     strcpy(fstring,CmdLine);
     if(*CmdLine == '<' || *CmdLine == ' ') ++CmdLine;
   }

   if (Client) {
   
      for (p = CmdLine; *p && !strchr(" <>()\"\'[]{}",*p); p++);
      *p = '\0';

      // DDE Control, if enable.
      if (Client == HttpClient && UsingDDE && OpenURL(CmdLine))
	  	return TRUE;

      j = 0;

      for (i=0; Client[i]; i++) {
        cmdstring[j] = Client[i];
        
        // check for special case of %u to find URL string
        if ((Client[i] == '%') &&  
            ((Client[i+2] == '\0') || strchr(" :<>()\"\'[]{}",Client[i+2]))) {
           switch (Client[i+1]){
              case 'u': 
                 i++;
                 for (p = CmdLine; *p ; p++) {
                    cmdstring[j] = *p;
                    j++;
                 }
                 break;
                 
               case 'p':
                  i++;
                  for (p = pstring; *p ; p++) {
                     cmdstring[j] = *p;
                     j++;
                  }
                  break;

               case 'h':
                  i++;
                  for (p = hstring; *p ; p++) {
                     cmdstring[j] = *p;
                     j++;
                  }
                  break;

               case 'f':
                  i++;
                  for (p = fstring; *p && !strchr(" <>()\"\'[]{}",*p) ; p++) {
                     cmdstring[j] = *p;
                     j++;
                  }
                  break;
           }
        }

        // check to see if we have an environment variable
        // if so, look it up and use that instead
        else if (Client[i] == '%') {
                
            // check to see if this is a special case of %%
            // if so, reduce it to a single %
            if (Client[i+1] == '%'){
              i++;
            } 
            else {
              for (k = 0; Client[i+k+1] && !strchr(" %<>()\"\'[]{}", Client[i+k+1]); k++){
                 envvar[k] = Client[i+k+1];
              }
              envvar[k] = '\0';
              EnvVar = getenv(envvar);
              i=i+k;
              if (EnvVar){
                 i++;   // strip off trailing %
                 for (p = EnvVar; *p; p++) {
                    cmdstring[j] = *p;
                    j++;
                 }
               }
              else
                 for (p = envvar; *p; p++) {
                    cmdstring[j] = *p;
                    j++;
                 }
            }
        }
       else j++;
    }
    cmdstring[j]='\0';
            
 // DoShell(Client, CmdLine);
    DoShell(cmdstring, '\0');
 // DoShell(cmdstring);
   }
   return(TRUE);
 }


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

    FUNCTION: WinVnArtWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

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

long WINAPI 
WinVnArtWndProc (HWND hWnd, unsigned message, WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT ps;

  HDC hDC;                  /* handle to display context */
  RECT myRect;              /* selection rectangle      */
  TypDoc *ThisDoc;
  TypGroup far *GroupDoc;
  int ih, j;
  int found;
  long charnum;
  _int16 X, Y;
 // WORD X,Y;
  int CtrlState;
  TypLine far *LinePtr;
  TypBlock far *BlockPtr;
  char mybuf[MAXINTERNALLINE];
  char far *textptr;
  int textlen;
  TypLineID MyLineID;
  HMENU hMenu, hSubMenu;

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

  if (!found) {
    ThisDoc = CommDoc;
  }

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

  switch (message) {

  case WM_SIZE:
    GetClientRect (hWnd, &myRect);
    ThisDoc->ScXWidth = myRect.right;
    ThisDoc->ScYHeight = myRect.bottom;
    ThisDoc->ScYLines = (RectHeight (myRect) - ArtTopSpace) / ArtLineHeight;
    ThisDoc->ScXChars = (RectWidth (myRect) - ArtSideSpace) / ArtCharWidth;
    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.
     */

    if (wParam == VK_F6) {
      NextWindow (ThisDoc->hWndFrame, ThisDoc->DocType);
    }
    else if (wParam == VK_F5) {
      if (ThisDoc->ActiveLines > (ThisDoc->ScYLines + ThisDoc->TopLineOrd)) {
        SendMessage (hWnd, (UINT) WM_KEYDOWN, (WPARAM) VK_NEXT, (LPARAM) 0L);
        break;
      }
      else {
        if (!(CommBusy && ThisDoc == CommDoc))
          SendMessage (ThisDoc->hWndFrame, (UINT) WM_COMMAND, (WPARAM) IDM_FIND_NEXT_SELECTED, (LPARAM) 0L);
        break;
      }
    }
    else if (wParam == VK_SPACE) {
//shimomai	  if (ThisDoc->ActiveLines > (ThisDoc->ScYLines + ThisDoc->TopLineOrd)) {
	  if ((ThisDoc->NextTopLine > 0L && (ThisDoc->NextTopLine < ThisDoc->TotalLines))
	      || ThisDoc->ActiveLines > (ThisDoc->ScYLines + ThisDoc->TopLineOrd)) {
        SendMessage (hWnd, (UINT) WM_KEYDOWN, (WPARAM) VK_NEXT, (LPARAM) 0L);
        break;
      }
      else {
        if (!(CommBusy && ThisDoc == CommDoc))
          SendMessage (ThisDoc->hWndFrame, (UINT) WM_COMMAND, (WPARAM) IDM_FIND_NEXT_UNSEEN, (LPARAM) 0L);
        break;
      }
    }
    else {
      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_RBUTTONDOWN:
    if (ThisDoc->TextSelected) {
      CopyTextToClipBoard (ThisDoc);
      SendMessage (ThisDoc->hWndFrame, (UINT) WM_COMMAND, (WPARAM) IDM_DESELECT_ALL, (LPARAM) 0L);  //Un-mark selection
    }
    break;

  case WM_LBUTTONDOWN:
    {
      long TempX, TempY;

      if (CommBusy && CommDoc == ThisDoc) {
        break;
      }
      else {
        DragMouseAction = DRAG_SELECT;
        ThisDoc->TextSelected = FALSE;

        hMenu = GetMenu (ThisDoc->hWndFrame);
        hSubMenu = GetSubMenu (hMenu, 1);
        EnableMenuItem (hSubMenu, IDM_COPY, DISABLE_MENU);
        SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_COPY, FALSE);
        EnableMenuItem (hSubMenu, IDM_DESELECT_ALL, DISABLE_MENU);

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

//        if (X < ArtSideSpace) {
        if ((int)X < (int)ArtSideSpace) {
          TempX = (long) ArtSideSpace;
        }
        else {
//          TempX = (long) min (X, ThisDoc->ScXWidth);
          TempX = (long) min ((int)X, (int)ThisDoc->ScXWidth);
        }

//        if (Y < ArtTopSpace) {
        if ((int)Y < (int)ArtTopSpace) {
          TempY = ArtTopSpace;
        }
        else {
          TempY = min ((long) Y, (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
          if (TempY > (long) (ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
            TempY = (long) ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight;
          }
        }

        ThisDoc->BeginSelect.LineNum = (int) ((TempY - ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd);
        ThisDoc->BeginSelect.CharNum = (int) cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
        ThisDoc->EndSelect.LineNum = -1;
        SetRect (&myRect, 0, 0, ThisDoc->ScXWidth, ThisDoc->ScYHeight);
        InvalidateRect (ThisDoc->hDocWnd, &myRect, FALSE);
        hSaveCursor = SetCursor (LoadCursor (NULL, IDC_IBEAM));
        SetCapture (hWnd);
      }
    }
    break;

  case WM_MOUSEMOVE:
    {
      long TempX, TempY;
      TextSelect Previous;
      int ScMin, ScMax;
      POINT CursorLocation;
      MSG Message;
      BOOL ScrollText = FALSE;
      void CheckForUpdate ();

      if (DragMouseAction == DRAG_SELECT) {
        ThisDoc->TextSelected = TRUE;

        hMenu = GetMenu (ThisDoc->hWndFrame);
        hSubMenu = GetSubMenu (hMenu, 1);
        EnableMenuItem (hSubMenu, IDM_COPY, ENABLE_MENU);
        SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_COPY, TRUE);
        EnableMenuItem (hSubMenu, IDM_DESELECT_ALL, ENABLE_MENU);

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

        // Check to see if text selection needs to scroll left.
        if ((X < 0) && (ThisDoc->ScXOffset > 0)) {
          ScrollText = TRUE;

          do                    // keep scrolling until the mouse moves.
           {
            TempX = ArtSideSpace;
//            if (Y < ArtTopSpace) {
            if ((int)Y < (int)ArtTopSpace) {
              TempY = ArtTopSpace;
            }
            else {
              TempY = min ((long) Y, (long)(ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
              if (TempY > (long) (ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
                TempY = (long) (ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight);
              }
            }

            Previous.LineNum = ThisDoc->EndSelect.LineNum;
            Previous.CharNum = ThisDoc->EndSelect.CharNum;
            SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINELEFT, 0);
            ThisDoc->EndSelect.LineNum = (int) ((TempY -  ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd);
            ThisDoc->EndSelect.CharNum = (int) cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
            UpdateHighlightedText (ThisDoc, &Previous);
            SendMessage (ThisDoc->hDocWnd, WM_PAINT, 0, 0);
            if (PeekMessage (&Message, ThisDoc->hDocWnd, WM_LBUTTONUP, WM_LBUTTONUP, PM_NOREMOVE))
              break;
            GetCursorPos (&CursorLocation);
            ScreenToClient (ThisDoc->hDocWnd, &CursorLocation);
          }
//          while ((X == (unsigned int) CursorLocation.x) && (Y == (unsigned int) CursorLocation.y) &&
          while ((X == CursorLocation.x) && (Y ==  CursorLocation.y) &&
                 (ThisDoc->ScXOffset > 0));
        }

        GetScrollRange (ThisDoc->hDocWnd, SB_HORZ, &ScMin, &ScMax);
        // Check to see if text selection needs to scroll right.
//        if ((X > ThisDoc->ScXWidth) && (ThisDoc->ScXOffset < (unsigned int) ScMax)) {
        if (((int) X > (int)ThisDoc->ScXWidth) && (ThisDoc->ScXOffset < (unsigned int) ScMax)) {
          ScrollText = TRUE;

          do                    // keep scrolling until the mouse moves.
           {
            TempX = ThisDoc->ScXWidth;
//            if (Y < ArtTopSpace) {
            if ((int)Y < (int)ArtTopSpace) {
              TempY = ArtTopSpace;
            }
            else {
              TempY = min ((long) Y, (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
              if (TempY > (long) (ArtTopSpace + ((ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight)))
              {
                TempY =  (long) (ArtTopSpace + ((ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight));
              }
            }

            Previous.LineNum = ThisDoc->EndSelect.LineNum;
            Previous.CharNum = ThisDoc->EndSelect.CharNum;
            SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINERIGHT, 0);
            ThisDoc->EndSelect.LineNum = (int) ((TempY - ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd);
            ThisDoc->EndSelect.CharNum = (int) cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);

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

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

          do                    // keep scrolling until the mouse moves.
           {
            TempY = ArtTopSpace;
//            if (X < ArtSideSpace) {
            if ((int)X < (int)ArtSideSpace) {
              TempX = ArtSideSpace;
            }
            else {
//              TempX = min (X, ThisDoc->ScXWidth);
              TempX = min ((int)X, (int)ThisDoc->ScXWidth);
            }

            Previous.LineNum = ThisDoc->EndSelect.LineNum;
            Previous.CharNum = ThisDoc->EndSelect.CharNum;
            SendMessage (ThisDoc->hDocWnd, WM_VSCROLL, SB_LINEUP, 0);
            ThisDoc->EndSelect.LineNum = (int) ((TempY - ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd);
            ThisDoc->EndSelect.CharNum = (int) cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
            UpdateHighlightedText (ThisDoc, &Previous);
            SendMessage (ThisDoc->hDocWnd, WM_PAINT, 0, 0);
            if (PeekMessage (&Message, ThisDoc->hDocWnd, WM_LBUTTONUP, WM_LBUTTONUP, PM_NOREMOVE))
              break;
            GetCursorPos (&CursorLocation);
            ScreenToClient (ThisDoc->hDocWnd, &CursorLocation);
          }
//          while ((X ==(unsigned int) CursorLocation.x) && (Y == (unsigned int) CursorLocation.y)
          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 ((Y > ThisDoc->ScYHeight) && (ThisDoc->TopLineOrd < (unsigned int) ScMax)) {
        if (((int)Y > (int)ThisDoc->ScYHeight) && (ThisDoc->TopLineOrd < (unsigned int) ScMax)) {
          ScrollText = TRUE;

          do                    // keep scrolling until the mouse moves.
           {
            TempY = (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight);
            if (TempY >  (long)(ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
              TempY = (long) (ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight);
            }

//            if (X < ArtSideSpace) {
            if ((int)X < (int)ArtSideSpace) {
              TempX = ArtSideSpace;
            }
            else {
//              TempX = min (X, ThisDoc->ScXWidth);
              TempX = min ((int)X, (int)ThisDoc->ScXWidth);
            }

            Previous.LineNum = ThisDoc->EndSelect.LineNum;
            Previous.CharNum = ThisDoc->EndSelect.CharNum;
            SendMessage (ThisDoc->hDocWnd, WM_VSCROLL, SB_LINEDOWN, 0);
            ThisDoc->EndSelect.LineNum = (int) (TempY - ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd;
            ThisDoc->EndSelect.CharNum = (int) cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
            UpdateHighlightedText (ThisDoc, &Previous);
            SendMessage (ThisDoc->hDocWnd, WM_PAINT, 0, 0);
            if (PeekMessage (&Message, ThisDoc->hDocWnd, WM_LBUTTONUP, WM_LBUTTONUP, PM_NOREMOVE))
              break;
            GetCursorPos (&CursorLocation);
            ScreenToClient (ThisDoc->hDocWnd, &CursorLocation);
          }
//          while ((X == (unsigned int) CursorLocation.x) && (Y == (unsigned int) CursorLocation.y)
          while ((X == CursorLocation.x) && (Y == CursorLocation.y)
                 && (ThisDoc->TopLineOrd < (unsigned int) ScMax));
        }

        if (!ScrollText) {
//          if (X < ArtSideSpace) {
          if ((int)X < (int)ArtSideSpace) {
            TempX = ArtSideSpace;
          }
          else {
//            TempX = min (X, ThisDoc->ScXWidth);
            TempX = min ((int)X, (int)ThisDoc->ScXWidth);
          }
//          if (Y < ArtTopSpace) {
          if ((int)Y < (int)ArtTopSpace) {
            TempY = ArtTopSpace;
          }
          else {
            TempY = min ((long) Y, (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
            if (TempY >  (long) (ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
              TempY =  (long) (ArtTopSpace + (ThisDoc->TotalLines - ThisDoc->TopLineOrd - 1) * ArtLineHeight);
            }
          }

          Previous.LineNum = ThisDoc->EndSelect.LineNum;
          Previous.CharNum = ThisDoc->EndSelect.CharNum;
          ThisDoc->EndSelect.LineNum = (int) (TempY - ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd;
          ThisDoc->EndSelect.CharNum = (int) cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
          UpdateHighlightedText (ThisDoc, &Previous);

        }

        if ((ThisDoc->EndSelect.LineNum > ThisDoc->BeginSelect.LineNum) ||
            ((ThisDoc->EndSelect.LineNum == ThisDoc->BeginSelect.LineNum) &&
             (ThisDoc->EndSelect.CharNum >= ThisDoc->BeginSelect.CharNum))) {
          if (((Previous.LineNum < ThisDoc->BeginSelect.LineNum) ||
               ((Previous.LineNum == ThisDoc->BeginSelect.LineNum) &&
                (Previous.CharNum <= ThisDoc->BeginSelect.CharNum)))) {
            InvalidateRect (hWnd, NULL, FALSE);
          }
        }
        else {
          if (((Previous.LineNum > ThisDoc->BeginSelect.LineNum) ||
               ((Previous.LineNum == ThisDoc->BeginSelect.LineNum) &&
                (Previous.CharNum >= ThisDoc->BeginSelect.CharNum)))) {
            InvalidateRect (hWnd, NULL, FALSE);
          }
        }
      }
    }
    break;

  case WM_LBUTTONUP:
    {
      long TempX, TempY;

      if (DragMouseAction == DRAG_SELECT) {
        DragMouseAction = DRAG_NONE;
        if (ThisDoc->TextSelected == TRUE) {
          X = LOWORD (lParam);
          Y = HIWORD (lParam);

//          if (X < ArtSideSpace) {
          if ((int)X < (int)ArtSideSpace) {
            TempX = ArtSideSpace;
          }
          else {
//            TempX = min (X, ThisDoc->ScXWidth);
            TempX = min ((int)X, (int)ThisDoc->ScXWidth);
          }
//          if (Y < ArtTopSpace) {
          if ((int)Y < (int)ArtTopSpace) {
            TempY = ArtTopSpace;
          }
          else {
            TempY = min ((long) Y, (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
          }

          ThisDoc->EndSelect.LineNum = (int) (TempY - ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd;
          ThisDoc->EndSelect.CharNum = (int) cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
          InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
        }
        SetCursor (hSaveCursor);
        ReleaseCapture ();
      }
    }
    break;

  case WM_LBUTTONDBLCLK:
    X = LOWORD (lParam);
    Y = HIWORD (lParam);

    charnum = cursor_to_char_number (X, Y, ThisDoc, &BlockPtr, &LinePtr);

    if (charnum >= 0) {
      textlen = GetTextLen(LinePtr);
      textptr = GetTextPtr(LinePtr);

      if (textlen) {            /* find a message-id or URL */
        char far *start, far * end;

                for (start = textptr + charnum; start > textptr; start--)
                  if (*start == '<' || *start == ' ')
                 break;

        for (end = textptr + charnum; end < (textptr + textlen-1); end++)
                 if (*end == '>' || *end == ' ' || *end >= 0x7f)
            break;

//      if ((start >= textptr) && (end < (textptr + textlen))) {
#ifdef __OS2__
          snprintf (str,(int) (((long) end - (long) start) + 2),"%s",start);
#else
          sprintf (str, "%.*Fs", (int) ((long) end - (long) start + 1), start);
#endif
		  end = str + strlen(str) - 1;
		  if (*end <= ' ' || *end >= 0x7f || *end == '.') *end = '\0';

          if (!URLCheckExecute(str)){
          LockLine (ThisDoc->ParentDoc->hParentBlock,
                    ThisDoc->ParentDoc->ParentOffset,
                    ThisDoc->ParentDoc->ParentLineID,
                    &BlockPtr, &LinePtr);
          GroupDoc = GetGroup(LinePtr);
          SetArticleMenus (ThisDoc, DISABLE);
          ViewArticle (ThisDoc->ParentDoc, ThisDoc->LastSeenLineID, REUSE, SHOW, str);
        }
      }
    }

    break;

  case WM_VSCROLL:
    ScrollIt (ThisDoc, wParam, lParam);
    break;

  case WM_HSCROLL:
    HScrollIt (ThisDoc, wParam, lParam);
    break;

  case WM_PAINT:
    {
      HANDLE hBlock;
      unsigned int Offset, MyLen;
      long VertLines;
      long HorzChars;
      int EndofDoc = FALSE;
      long RangeHigh, CurPos;
      long lineNum;
      char far *textptr;
      TypBlock far *BlockPtr;
      TypLine far *LinePtr;
      int MyColorMask = 1, PrevColorMask = MyColorMask;
      BOOL ROT13Mode = GetArticleRot13Mode (ThisDoc->hWndFrame);
      RECT aRect;
      TextSelect FAR *Start, *End;
      SIZE extent;
      HBRUSH hSolidBrush, hOldBrush;
      HANDLE hOldFont;
	  BOOL PageFeed = FALSE;	//shimomai

      hDC = BeginPaint (hWnd, &ps);
      SetBkColor (hDC, ArticleBackgroundColor);
      SetTextColor (hDC, ArticleTextColor);

      GetClientRect (hWnd, &myRect);
      VertLines = ThisDoc->ScYLines;
      HorzChars = ThisDoc->ScXChars;

      // Prepare to highlight text if it has been selected
      if (ThisDoc->TextSelected) {
        // check to see if selection is in a forward direction 
        // (i.e. top-to-bottom or left-to-right
        if ((ThisDoc->EndSelect.LineNum > ThisDoc->BeginSelect.LineNum) ||
            ((ThisDoc->EndSelect.LineNum == ThisDoc->BeginSelect.LineNum) &&
             (ThisDoc->EndSelect.CharNum >= ThisDoc->BeginSelect.CharNum))) {
          Start = &ThisDoc->BeginSelect;
          End = &ThisDoc->EndSelect;
        }
        else {
          Start = &ThisDoc->EndSelect;
          End = &ThisDoc->BeginSelect;
        }
      }

      /* Update the scroll bar thumb position. */

      RangeHigh = ThisDoc->TotalLines - VertLines;
	  if (RangeHigh < 0 && ThisDoc->NextTopLine == 0) {
        RangeHigh = 0;
        ThisDoc->TopLineOrd = 0;
        ThisDoc->TopScLineID = 0;
        ThisDoc->hCurTopScBlock = ThisDoc->hFirstBlock;
        ThisDoc->TopScOffset = sizeof (TypBlock);
        LockLine (ThisDoc->hFirstBlock, ThisDoc->TopScOffset, ThisDoc->TopScLineID, 
                &BlockPtr, &LinePtr);
      } else {
          LockLine (ThisDoc->hCurTopScBlock, ThisDoc->TopScOffset, ThisDoc->TopScLineID,
                    &BlockPtr, &LinePtr);
      }
      CurPos = lineNum = ThisDoc->TopLineOrd;
      if (CurPos < 0)
        CurPos = 0;

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

      RangeHigh = ThisDoc->LongestLine - ThisDoc->ScXChars;
      if (RangeHigh < 0) {
        RangeHigh = 0;
        ThisDoc->ScXOffset = 0;
      }
      SetScrollRange (hWnd, SB_HORZ, 0, OrdToScroll(RangeHigh,RangeHigh), FALSE);
      SetScrollPos (hWnd, SB_HORZ, OrdToScroll(RangeHigh,ThisDoc->ScXOffset), TRUE);

      /* Now paint this stuff on the screen for debugging. */
      X = ArtSideSpace - ThisDoc->ScXOffset * (ArtCharWidth + 1);
      Y = ArtTopSpace;

      if (LinePtr->length != END_OF_BLOCK)
        do {
          MyLen = GetTextLen(LinePtr);
          textptr = GetTextPtr(LinePtr);
          if (*textptr == SpoilChar) PageFeed = TRUE;	//shimomai
          
          /*  within body of article take copy of string to avoid changing original data */
          if (ROT13Mode && ((unsigned) lineNum > ThisDoc->HeaderLines)) {   
            strcpy (mybuf, textptr);
            textptr = mybuf;
            strROT13 (textptr);
          }
           
          /* prepare to print a quotation Line */
          if (ItalicizeQuotes && isLineQuotation (textptr)) {   
            hOldFont = SelectObject (hDC, hFontArtQuote);
          }
          else {  /* prepare to print a normal line */
            hOldFont = SelectObject (hDC, hFontArtNormal);
          }

          /* Now write out the line. */
          /* Check to see the line is to be highlighted */
          if ((ThisDoc->TextSelected) && (lineNum >= Start->LineNum) &&
              (lineNum <= End->LineNum)) {
            if (MyLen != 0) {
              if (Start->LineNum == End->LineNum) {     /* Only one line contains highlighted text */
                GetTextExtentPoint (hDC, (LPSTR) textptr, Start->CharNum, &extent);
                SetRect (&aRect, X, Y, X + extent.cx, Y + ArtLineHeight);
                ExtTextOut (hDC, X, Y, ETO_OPAQUE, &aRect, textptr,
                            Start->CharNum, (LPINT) NULL);
                SetBkColor (hDC, ArticleTextColor);
                SetTextColor (hDC, ArticleBackgroundColor);
                aRect.left = aRect.right;
                GetTextExtentPoint (hDC, (LPSTR) textptr, End->CharNum + 1, &extent);
                aRect.right = X + extent.cx;
                ExtTextOut (hDC, aRect.left, Y, ETO_OPAQUE, &aRect,
                            textptr + Start->CharNum,
                            End->CharNum - Start->CharNum + 1, (LPINT) NULL);
                aRect.left = aRect.right;
                GetTextExtentPoint (hDC, (LPSTR) textptr, MyLen, &extent);
                aRect.right = X + extent.cx;
                SetBkColor (hDC, ArticleBackgroundColor);
                SetTextColor (hDC, ArticleTextColor);
                ExtTextOut (hDC, aRect.left, Y, ETO_OPAQUE, &aRect,
                       textptr + End->CharNum + 1, MyLen - End->CharNum - 1,
                            (LPINT) NULL);
                Y += ArtLineHeight;
                lineNum++;
              }
              else {
                if (lineNum == Start->LineNum) {    /* First line of more than one line to highlight */
                  GetTextExtentPoint (hDC, (LPSTR) textptr, Start->CharNum, &extent);
                  SetRect (&aRect, X, Y, X + extent.cx, Y + ArtLineHeight);
                  ExtTextOut (hDC, X, Y, ETO_OPAQUE, &aRect, textptr,
                              Start->CharNum, (LPINT) NULL);
                  SetBkColor (hDC, ArticleTextColor);
                  SetTextColor (hDC, ArticleBackgroundColor);
                  aRect.left = aRect.right;
                  GetTextExtentPoint (hDC, (LPSTR) textptr, MyLen, &extent);
                  aRect.right = X + extent.cx;
                  ExtTextOut (hDC, aRect.left, Y, ETO_OPAQUE, &aRect,
                           textptr + Start->CharNum, MyLen - Start->CharNum,
                              (LPINT) NULL);
                  SetBkColor (hDC, ArticleBackgroundColor);
                  SetTextColor (hDC, ArticleTextColor);
                }
                if ((lineNum > Start->LineNum) && (lineNum < End->LineNum)) {   /* Whole lines to be highlighted */
                  GetTextExtentPoint (hDC, (LPSTR) textptr, MyLen, &extent);
                  SetRect (&aRect, X, Y, X + extent.cx, Y + ArtLineHeight);
                  SetBkColor (hDC, ArticleTextColor);
                  SetTextColor (hDC, ArticleBackgroundColor);
                  ExtTextOut (hDC, X, Y, ETO_OPAQUE, &aRect, textptr,
                              MyLen, (LPINT) NULL);
                  SetBkColor (hDC, ArticleBackgroundColor);
                  SetTextColor (hDC, ArticleTextColor);
                }
                if (lineNum == End->LineNum) {  /* Last line to be highlighted */
                  SetBkColor (hDC, ArticleTextColor);
                  SetTextColor (hDC, ArticleBackgroundColor);
                  GetTextExtentPoint (hDC, (LPSTR) textptr, End->CharNum + 1, &extent);
                  SetRect (&aRect, X, Y, X + extent.cx, Y + ArtLineHeight);
                  ExtTextOut (hDC, X, Y, ETO_OPAQUE, &aRect, textptr,
                              End->CharNum + 1, (LPINT) NULL);
                  aRect.left = aRect.right;
                  GetTextExtentPoint (hDC, (LPSTR) textptr, MyLen, &extent);
                  aRect.right = X + extent.cx;
                  SetBkColor (hDC, ArticleBackgroundColor);
                  SetTextColor (hDC, ArticleTextColor);
                  ExtTextOut (hDC, aRect.left, Y, ETO_OPAQUE, &aRect,
                       textptr + End->CharNum + 1, MyLen - End->CharNum - 1,
                              (LPINT) NULL);
                }

                Y += ArtLineHeight;
                lineNum++;

              }
            }
            else {              /* Blank Line -- Rectangle for CR/LF */
              SetRect (&aRect, X, Y, X + 4, Y + ArtLineHeight);
              hSolidBrush = CreateSolidBrush (ArticleTextColor);
              FillRect (hDC, &aRect, hSolidBrush);
              DeleteObject ((HGDIOBJ) hSolidBrush);
              Y += ArtLineHeight;
              lineNum++;
            }
          }
          else {                /* Line is not highlighted */
            GetTextExtentPoint (hDC, (LPSTR) textptr, MyLen, &extent);
            SetRect (&aRect, X, Y, X + extent.cx, Y + ArtLineHeight);

            ExtTextOut (hDC, X, Y, ETO_OPAQUE, &aRect, textptr,
                        MyLen, (LPINT) NULL);
            aRect.left = aRect.right;
            Y += ArtLineHeight;
            lineNum++;
          }

          SelectObject (hDC, hOldFont);

          /* Fill in the space to the right of the end of the line *
           * with the article background color                     */
          aRect.left = aRect.right;
          aRect.right = ThisDoc->ScXWidth;
          hSolidBrush = CreateSolidBrush (ArticleBackgroundColor);
          FillRect (hDC, &aRect, hSolidBrush);
          DeleteObject ((HGDIOBJ) hSolidBrush);

        }
//shimomai while (--VertLines > 0 && NextLine (&BlockPtr, &LinePtr));
        while (--VertLines > 0 && NextLine (&BlockPtr, &LinePtr)
                && (PageFeed == FALSE));

//shimomai
        if (PageFeed == TRUE) ThisDoc->NextTopLine = lineNum;
        else if (ThisDoc->NextTopLine)
          ThisDoc->NextTopLine = ThisDoc->TotalLines + 1;
        else ThisDoc->NextTopLine = 0;

      /* 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 and blank top space.          */
      hOldBrush = (HBRUSH) SelectObject (hDC, hArticleBackgroundBrush);
      PatBlt (hDC, 0, Y, myRect.right - 1, myRect.bottom - Y, PATCOPY);
      PatBlt (hDC, 0, 0, myRect.right - 1, ArtTopSpace, PATCOPY);
      if (ThisDoc->ScXOffset == 0) {
        PatBlt (hDC, 0, 0, ArtSideSpace, ThisDoc->ScYHeight, PATCOPY);
      }

      UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
      SelectObject (hDC, hOldBrush);
      EndPaint (hWnd, &ps);
      break;
    }

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

/*------------ CloseArtWnd ------------------------------
 *
 *  Make sure this Wnd is not the active Comm window, then destroy it
 */
void 
CloseArtWnd (HWND hWnd, TypDoc * ThisDoc)
{

  if (CommBusy && ThisDoc == CommDoc) {
    MessageBox (hWnd,
                "Please wait until article retrieval is complete",
                "Cannot close article window", MB_OK | MB_ICONSTOP);
    return;
  }

  SetHandleBkBrush (ThisDoc->hDocWnd, (HBRUSH) GetStockObject (WHITE_BRUSH));
  DestroyWindow (hWnd);
}

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

  hMenu = GetMenu (DocPtr->hWndFrame);
  hSubMenu = GetSubMenu (hMenu, 4);     // respond menu

  EnableMenuItem (hSubMenu, IDM_MAIL, MailCtrl.enableMail);
  EnableMenuItem (hSubMenu, IDM_FORWARD, MailCtrl.enableForward);

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

// dis/enable menu items which depend on article being completely retrieved
void
SetArticleMenus (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);     // file menu

  EnableMenuItem (hSubMenu, IDM_SAVE, mode);
  EnableMenuItem (hSubMenu, IDM_SAVEAS, mode);
  EnableMenuItem (hSubMenu, IDM_PRINT, mode);
  EnableMenuItem (hSubMenu, IDM_PRINT_SETUP, mode);
  EnableMenuItem (hSubMenu, IDV_EXIT, mode);
  hSubMenu = GetSubMenu (hMenu, 1);     // edit menu
  EnableMenuItem (hSubMenu, IDM_SELECT_ALL, mode);
  hSubMenu = GetSubMenu (hMenu, 2);     // search menu
  EnableMenuItem (hSubMenu, IDM_SEARCH, mode);
  EnableMenuItem (hSubMenu, IDM_SEARCH_NEXT, mode);
  hSubMenu = GetSubMenu (hMenu, 4);     // respond menu
  EnableMenuItem (hSubMenu, IDM_POST, mode);

  if (enable) {
    // activate based on transport availability
    SetArticleMailMenu (DocPtr);
  }
  else {
    // deactivate regardless
    EnableMenuItem (hSubMenu, IDM_MAIL, mode);
    EnableMenuItem (hSubMenu, IDM_FORWARD, mode);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FORWARD, FALSE);
    SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_MAIL, FALSE);
  }

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

  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_POST, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_SAVEAS, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_PRINT, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_SEARCH, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_SEARCH_NEXT, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDV_EXIT, mode);

  // initmenu handles next/prev article buttons, as well as decode, and send
  SendMessage (DocPtr->hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
}

/*------GetArticleRot13Mode-------------------------------
 *
 *  Routine to get the this article window Rot mode.
 *  Note :
 *     interogation of windows menu state to determine
 *     ROT13 drawing mode
 *    
 *-----------------------------------------------------*/

BOOL
GetArticleRot13Mode (HWND hWnd)
{
  return (GetMenuState (GetMenu (hWnd), IDM_ROT13, 0) == MF_CHECKED);
}

/*------SetArticleRot13Mode-------------------------------
 *
 *  Routine to set the this article window into (or out of
 *  Rot mode).
 *  Note :
 *     interogation of windows menu state to determine
 *     ROT13 drawing mode
 *     need to invalidate window to force repaint to display
 *     in new mode
 *-----------------------------------------------------*/

void
SetArticleRot13Mode (HWND hWnd, BOOL RotMode)
{
  UINT action;
  HWND hParentWnd;

  if (RotMode == TRUE)
    action = MF_CHECKED;
  else
    action = MF_UNCHECKED;

  if ((hParentWnd = GetParent (hWnd)) == NULL)
    CheckMenuItem (GetMenu (hWnd), IDM_ROT13, action);
  else
    CheckMenuItem (GetMenu (hParentWnd), IDM_ROT13, action);

}

/*            strROT13
 * change the input string by ROT'ing each character
 *
 */
void
strROT13 (char *cstring)
{
  char *cptr = cstring;

  while (*cptr) {
    (*cptr++) = chROT13 (*cptr);

  }
}

/*        chROT13
 * return a new character that is the ROT(ation of) 13 characters
 * of the input character
 *
 */
char 
chROT13 (char achar)
{
  char newchar;

  if (isalpha (achar)) {
    if ((achar & 31) <= 13) {
      newchar = achar + 13;
    }
    else {
      newchar = achar - 13;
    }
  }
  else
    newchar = achar;

  return (newchar);
}
/*            strnROT13
 * change the input string by ROT'ating each character
 * for len or end of string (whichever is less)
 *
 */
void 
strnROT13 (char *cstring, int cstringlen)
{
  char *cptr = cstring;
  int clen = cstringlen;

  while (*cptr && clen) {
    (*cptr) = chROT13 (*cptr);
    cptr++;
    clen--;
  }

}

/* UpdateHighlightedText

 * This function updates hightlighted text from the previous
 * position to the current position.
 *
 */

void 
UpdateHighlightedText (TypDoc far * DocPtr, TextSelect far * PreviousPos)
{
  SIZE extent;
  char far *textptr;
  int textlen;
  RECT Rect;
  long CurrentLineNum;
  //BOOL ChangedSelectionDirection = FALSE;
  HBRUSH hSolidBrush;
  HDC hDC;
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;
  TextSelect far *Start, *End, *Temp;
  HANDLE hOldFont;
  TEXTMETRIC tmFontInfo;
  int charWidth;

  hDC = GetDC (DocPtr->hDocWnd);

  // check to see if selection is in a forward direction 
  // (i.e. top-to-bottom or left-to-right
  if ((DocPtr->EndSelect.LineNum > DocPtr->BeginSelect.LineNum) ||
      ((DocPtr->EndSelect.LineNum == DocPtr->BeginSelect.LineNum) &&
       (DocPtr->EndSelect.CharNum >= DocPtr->BeginSelect.CharNum))) {
    if ((PreviousPos->LineNum == -1)) {
      Start = &DocPtr->BeginSelect;
      //if (ChangedSelectionDirection) { // unreachable code
      //  InvalidateRect (DocPtr->hDocWnd, NULL, FALSE);
      //}
    }
    else {
      Start = PreviousPos;
    }
    End = &DocPtr->EndSelect;

    // check to see if highlighting or unhighlighting
    if ((End->LineNum > Start->LineNum) ||
        ((End->LineNum == Start->LineNum) &&
         (End->CharNum >= Start->CharNum))) {
      SetBkColor (hDC, ArticleTextColor);
      SetTextColor (hDC, ArticleBackgroundColor);
    }
    else {
      SetBkColor (hDC, ArticleBackgroundColor);
      SetTextColor (hDC, ArticleTextColor);
      Temp = Start;
      Start = End;
      End = Temp;
    }
  }
  else {

    Start = &DocPtr->EndSelect;
    if ((PreviousPos->LineNum == -1)) {
      End = &DocPtr->BeginSelect;
      //if (ChangedSelectionDirection) { // unreachable code
      //  InvalidateRect (DocPtr->hDocWnd, NULL, FALSE);
      //}
    }
    else {
      End = PreviousPos;
    }

    // check to see if highlighting or unhighlighting
    if ((End->LineNum > Start->LineNum) ||
        ((End->LineNum == Start->LineNum) &&
         (End->CharNum >= Start->CharNum))) {
      SetBkColor (hDC, ArticleTextColor);
      SetTextColor (hDC, ArticleBackgroundColor);
    }
    else {
      SetBkColor (hDC, ArticleBackgroundColor);
      SetTextColor (hDC, ArticleTextColor);
      Temp = Start;
      Start = End;
      End = Temp;
    }
  }
  
  if ((End->LineNum >= 0) && (Start->LineNum >= 0) && 
      (End->CharNum >= 0) && (Start->CharNum >= 0))
   {
    FindLineOrd (DocPtr, (unsigned) Start->LineNum, &BlockPtr, &LinePtr);
    textlen = GetTextLen(LinePtr);
    textptr = GetTextPtr(LinePtr);
    
    /* prepare to print a quotation Line */
    if (ItalicizeQuotes && isLineQuotation (textptr)) { 
        hOldFont = SelectObject (hDC, hFontArtQuote);
    }
    else {  /* prepare to print a normal line */
        hOldFont = SelectObject (hDC, hFontArtNormal);
    }
    GetTextMetrics (hDC, (LPTEXTMETRIC) & tmFontInfo);
    charWidth = tmFontInfo.tmAveCharWidth;

    GetTextExtentPoint (hDC, (LPSTR) textptr, Start->CharNum, &extent);
    Rect.top = (int) (ArtTopSpace + ((Start->LineNum - DocPtr->TopLineOrd) * ArtLineHeight));
    Rect.left = extent.cx - ((int) DocPtr->ScXOffset * (charWidth + 1) - ArtSideSpace);

    if (Start->LineNum == End->LineNum) {
        GetTextExtentPoint (hDC, (LPSTR) textptr, End->CharNum + 1, &extent);
        Rect.bottom = Rect.top + ArtLineHeight;
        Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (charWidth + 1) - ArtSideSpace);

        if (textlen != 0) {
          ExtTextOut (hDC, Rect.left, Rect.top, ETO_OPAQUE, &Rect,
                      textptr + Start->CharNum, End->CharNum - Start->CharNum + 1, (LPINT) NULL);
          }
        else {
          Rect.right = Rect.left + 4;
          hSolidBrush = CreateSolidBrush (GetBkColor (hDC));
          FillRect (hDC, &Rect, hSolidBrush);   /* Rectangle used to represent CR/LF */
          DeleteObject ((HGDIOBJ) hSolidBrush);
        }
    }
    else {
        GetTextExtentPoint (hDC, (LPSTR) textptr, textlen, &extent);
        Rect.bottom = Rect.top + ArtLineHeight;
        Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (charWidth + 1) - ArtSideSpace);

        if (textlen != 0) {
        ExtTextOut (hDC, Rect.left, Rect.top, ETO_OPAQUE, &Rect,
              textptr + Start->CharNum, textlen - Start->CharNum, (LPINT) NULL);
        }
        else {
            Rect.right = Rect.left + 4;
            hSolidBrush = CreateSolidBrush (GetBkColor (hDC));
            FillRect (hDC, &Rect, hSolidBrush); /* Rectangle used to represent CR/LF */
            DeleteObject ((HGDIOBJ) hSolidBrush);
        }

        SelectObject (hDC, hOldFont);
        CurrentLineNum = Start->LineNum + 1;
         
        if (CurrentLineNum >= 0)
        {
        while (CurrentLineNum < End->LineNum) {
         NextLine (&BlockPtr, &LinePtr);
         textlen = GetTextLen(LinePtr);
         textptr = GetTextPtr(LinePtr);
        
        /* prepare to print a quotation Line */ 
        if (ItalicizeQuotes && isLineQuotation (textptr)) { 
            hOldFont = SelectObject (hDC, hFontArtQuote);
        }
        else {  /* prepare to print a normal line */
            hOldFont = SelectObject (hDC, hFontArtNormal);
        }
        GetTextMetrics (hDC, (LPTEXTMETRIC) & tmFontInfo);
        charWidth = tmFontInfo.tmAveCharWidth;

        Rect.top = (int) (ArtTopSpace + ((CurrentLineNum - DocPtr->TopLineOrd) * ArtLineHeight));
        Rect.left = ArtSideSpace - (int) DocPtr->ScXOffset * (charWidth + 1);
        GetTextExtentPoint (hDC, (LPSTR) textptr, textlen, &extent);
        Rect.bottom = Rect.top + ArtLineHeight;
        Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (charWidth + 1) - ArtSideSpace);

        if (textlen != 0) {
            ExtTextOut (hDC, Rect.left, Rect.top, ETO_OPAQUE, &Rect,
                        textptr, textlen, (LPINT) NULL);
        }
        else {
            Rect.right = Rect.left + 4;
            hSolidBrush = CreateSolidBrush (GetBkColor (hDC));
            FillRect (hDC, &Rect, hSolidBrush);     /* Rectangle used to represent CR/LF */
            DeleteObject ((HGDIOBJ) hSolidBrush);
          }

        CurrentLineNum++;
        SelectObject (hDC, hOldFont);
        }

        NextLine (&BlockPtr, &LinePtr);
        textlen = GetTextLen(LinePtr);
        textptr = GetTextPtr(LinePtr);
        
        /* prepare to print a quotation Line */
        if (ItalicizeQuotes && isLineQuotation (textptr)) { 
            hOldFont = SelectObject (hDC, hFontArtQuote);
        }
        else {  /* prepare to print a normal line */
        hOldFont = SelectObject (hDC, hFontArtNormal);
        }
        GetTextMetrics (hDC, (LPTEXTMETRIC) & tmFontInfo);
        charWidth = tmFontInfo.tmAveCharWidth;

        Rect.top = (int) (ArtTopSpace + ((CurrentLineNum - DocPtr->TopLineOrd) *  ArtLineHeight));
        Rect.left = ArtSideSpace - (int) DocPtr->ScXOffset * (charWidth + 1);
        GetTextExtentPoint (hDC, (LPSTR) textptr, End->CharNum + 1, &extent);
        Rect.bottom = Rect.top + ArtLineHeight;
        Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (charWidth + 1) - ArtSideSpace);

        if (textlen != 0) {
            ExtTextOut (hDC, Rect.left, Rect.top, ETO_OPAQUE, &Rect,
                      textptr, End->CharNum + 1, (LPINT) NULL);
        }
        else {
        Rect.right = Rect.left + 4;
        hSolidBrush = CreateSolidBrush (GetBkColor (hDC));
        FillRect (hDC, &Rect, hSolidBrush); /* Rectangle used to represent CR/LF */
        DeleteObject ((HGDIOBJ) hSolidBrush);
        }

        SelectObject (hDC, hOldFont);
     }
    }
   }
  ReleaseDC (DocPtr->hDocWnd, hDC);
}

void 
CopyTextToClipBoard (TypDoc FAR * DocPtr)
{
  TextSelect *Start, *End;
  TypBlock FAR *BlockPtr;
  TypLine FAR *LinePtr;
  long CurrentLineNum;
  int j;
  int textlen;
  char FAR *textptr;
  BOOL ROT13Mode = GetArticleRot13Mode (DocPtr->hWndFrame);

  HGLOBAL hGlobalMemory;
  char FAR *GlobalMemoryPtr;
  DWORD SelectedTextLength = 0;

  /* Check to see if selection is in a forward direction.           *
   * Start should always point to first selected position           *
   * when the document is seen from top-to-bottom and               *
   * left-to-right.                                                 */
  if ((DocPtr->EndSelect.LineNum > DocPtr->BeginSelect.LineNum) ||
      ((DocPtr->EndSelect.LineNum == DocPtr->BeginSelect.LineNum) &&
       (DocPtr->EndSelect.CharNum >= DocPtr->BeginSelect.CharNum))) {
    Start = &DocPtr->BeginSelect;
    End = &DocPtr->EndSelect;
  }
  else {
    Start = &DocPtr->EndSelect;
    End = &DocPtr->BeginSelect;
  }

  /*Calculate the number of bytes to be moved to the clipboard.     */
  CurrentLineNum = Start->LineNum;
  FindLineOrd (DocPtr, (unsigned) CurrentLineNum, &BlockPtr, &LinePtr);
  textlen = GetTextLen(LinePtr);

  if (Start->LineNum == End->LineNum) {
    /* Text is selected on only one line.                           */
    SelectedTextLength = End->CharNum - Start->CharNum + 1;
  }
  else {
    /* Length of the first line. The 2 is for CR/LF.*/
    SelectedTextLength = textlen - Start->CharNum + 2;

    NextLine (&BlockPtr, &LinePtr);
    CurrentLineNum++;

    /* Add in the lengths of all whole lines selected */
    while (CurrentLineNum < End->LineNum) {
      textlen = GetTextLen(LinePtr);
      SelectedTextLength += textlen + 2;  /* The 2 is for CR/LF.*/
      NextLine (&BlockPtr, &LinePtr);
      CurrentLineNum++;
    }

    /* Add in the length of the last line selected.*/
    textlen = GetTextLen(LinePtr);
    if (textlen != 0) {
      SelectedTextLength += End->CharNum + 1;
    }
    else {
      SelectedTextLength += 2;  /* The 2 is for CR/LF.*/
    }
  }

  /* Allocate memory and move selected text to clipboard.     *
   * The 1 is for NULL string termination.                    */
  hGlobalMemory = GlobalAlloc (GHND, SelectedTextLength + 1);
  if (hGlobalMemory) {
    GlobalMemoryPtr = (char*) GlobalLock (hGlobalMemory);
    CurrentLineNum = Start->LineNum;
    FindLineOrd (DocPtr, (unsigned) CurrentLineNum, &BlockPtr, &LinePtr);
    textlen = GetTextLen(LinePtr);
    textptr = GetTextPtr(LinePtr);
    /* Move textptr to the first character selected.                 */
    textptr += Start->CharNum;

    if (Start->LineNum == End->LineNum) {
      /* Copy the text from the only line selected.                   */
      for (j = 0; j < End->CharNum - Start->CharNum + 1; j++) {
        *GlobalMemoryPtr++ = (ROT13Mode && ((unsigned) CurrentLineNum > DocPtr->HeaderLines)) 
                            ? chROT13(*textptr++) : *textptr++;
      }
    }
    else {
      /* Copy the first line and add CR/LF at the end of the line.    */
      for (j = 0; j < textlen - Start->CharNum; j++) {
        *GlobalMemoryPtr++ = (ROT13Mode && ((unsigned) CurrentLineNum > DocPtr->HeaderLines))
                            ? chROT13(*textptr++) : *textptr++;
      }
      *GlobalMemoryPtr++ = '\r';
      *GlobalMemoryPtr++ = '\n';
      NextLine (&BlockPtr, &LinePtr);
      CurrentLineNum++;
      
      /* Copy all whole lines selected add add CR/LF at the end of each line. */
      while (CurrentLineNum < End->LineNum) {
        textlen = GetTextLen(LinePtr);
        textptr = GetTextPtr(LinePtr);
        for (j = 0; j < textlen; j++) {
          *GlobalMemoryPtr++ = (ROT13Mode && ((unsigned) CurrentLineNum > DocPtr->HeaderLines)) 
                              ? chROT13(*textptr++) : *textptr++;
        }
        *GlobalMemoryPtr++ = '\r';
        *GlobalMemoryPtr++ = '\n';
        NextLine (&BlockPtr, &LinePtr);
        CurrentLineNum++;
      }
      /* Copy the last line selected */
      textlen = GetTextLen(LinePtr);
      textptr = GetTextPtr(LinePtr);
      if (textlen != 0) {
        for (j = 0; j < End->CharNum + 1; j++) {
            *GlobalMemoryPtr++ = (ROT13Mode && ((unsigned) CurrentLineNum > DocPtr->HeaderLines))  
                                ? chROT13(*textptr++) : *textptr++;
        }
      }
      else {
        *GlobalMemoryPtr++ = '\r';
        *GlobalMemoryPtr++ = '\n';
      }
    }

    GlobalUnlock (hGlobalMemory);
    OpenClipboard (DocPtr->hDocWnd);
    EmptyClipboard ();
    SetClipboardData (CF_TEXT, hGlobalMemory);
    CloseClipboard ();
  }
  else {
    MessageBox (DocPtr->hDocWnd, "Not enough memory to copy selected text.", "Out Of Memory", MB_OK);
  }

}

long WINAPI 
WinVnArtFrameWndProc (HWND hWnd, unsigned message, WPARAM wParam, LPARAM lParam)
{

  RECT myRect;                  /* selection rectangle      */
  TypGroup far *GroupDoc;
  TypDoc *ThisDoc;
  int ih, i, j;
  BOOL found;
  TBBUTTON tbButton[20];
  TypLine far *LinePtr;
  TypBlock far *BlockPtr;
  HANDLE hBlock;
  char mybuf[MAXINTERNALLINE];
  char mybuf1[MAXINTERNALLINE];
  char headerLine[MAXHEADERLINE];
  char *ptr,*ptr1;
  unsigned int Offset;
  int textlen;
  BOOL continueFind;
  BOOL AllowCancel;
  TypLineID MyLineID, artindex;
  HANDLE header_handle;
  HANDLE thread_handle;
  header_p headers;
  header_p header;
  long header_num;
  HMENU hMenu, hSubMenu;
  PAINTSTRUCT ps;               /* paint structure          */
  HDC hDC;                      /* handle to display context */

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

  if (!found)
    ThisDoc = CommDoc;

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

  switch (message) {
  case WM_SETFOCUS:
    StatBarArtPopups (ThisDoc);
    SetCapsLockText(hWnd);
    SetNumLockText(hWnd);
    /* fall through */
    
  case WM_SYSCOMMAND:
    return (DefWindowProc (hWnd, message, wParam, lParam));

  case WM_ACTIVATE:
    if (wParam)
      ActiveArticleDoc = ThisDoc;
    /* fall throughto update toolbars */
    
  case WM_MYINITMENU:
  case WM_INITMENU:
    hMenu = GetMenu (hWnd);
    hSubMenu = GetSubMenu (hMenu, 0);   /* File menu */
    i = ((!CommBusy || ThisDoc != CommDoc) && CodingState == INACTIVE);
    EnableMenuItem (hSubMenu, IDM_DECODE_ARTICLE, i ? ENABLE_MENU : DISABLE_MENU);
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_DECODE_ARTICLE, i);

    hSubMenu = GetSubMenu (hMenu, 3);   /* view menu */
    i = CommBusy || (ThisDoc->ParentDoc == (TypDoc *)NULL);
    EnableMenuItem (hSubMenu, IDM_NEXT_ARTICLE, i ? DISABLE_MENU : ENABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_FIND_NEXT_UNSEEN, i ? DISABLE_MENU : ENABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_NEXT_SAME_SUBJ, i ? DISABLE_MENU : ENABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_FIND_NEXT_SELECTED, i ? DISABLE_MENU : ENABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_PREV_ARTICLE, i ? DISABLE_MENU : ENABLE_MENU);

    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_NEXT_ARTICLE, i ? FALSE : TRUE);
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_FIND_NEXT_UNSEEN, i ? FALSE : TRUE);
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_NEXT_SAME_SUBJ, i ? FALSE : TRUE);
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_FIND_NEXT_SELECTED, i ? FALSE : TRUE);
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_PREV_ARTICLE, i ? FALSE : TRUE);

  // The return causes a GPF in the Watcom runtime system AFTER the program has
  // ended. It modifies values in a  __OpenStreams memory array. These variables
  // are used by Watcom to close open streams.   (Peter Heirich)

#if defined(__WATCOMC__) && !defined(__OS2__)
#else   
    if (message == WM_ACTIVATE)	// kludgeville
      return (DefWindowProc (hWnd, message, wParam, lParam));
#endif
    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_SIZE:
    /* Store the new size of the window.                     */
    GetClientRect (hWnd, &myRect);
    MoveWindow (ThisDoc->hDocWnd, 0, TOOLBARHEIGHT,
                myRect.right - myRect.left,
    (myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
                TRUE);
    MoveWindow (ThisDoc->hWndTB, 0, 0,
                myRect.right - myRect.left,
                TOOLBARHEIGHT,
                TRUE);
    break;

  case WM_CLOSE:
    ThisDoc->TextSelected = FALSE;
    CloseArtWnd (hWnd, ThisDoc);
    return (0);
    break;

  case WM_DESTROY:
    NumArticleWnds--;
    ThisDoc->TextSelected = FALSE;
    ThisDoc->InUse = FALSE;
    ThisDoc->LongestLine = 0;
    ThisDoc->ScXOffset = 0;
    if (ThisDoc == CommDoc) {
      CommBusy = FALSE;
      CommDoc = (TypDoc *) NULL;
    }
    
    /* Clean up pointers and Clear document */
    SeverArticleParent(ThisDoc);
    FreeDoc (ThisDoc);

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

    ActiveArticleDoc = (TypDoc *)NULL;
    for (j = MAXARTICLEWNDS - 1; j >= 0; j--) {
      if (ArticleDocs[j].InUse) {
        ActiveArticleDoc = &(ArticleDocs[j]);
        break;
      }
    }
    return (0);
    break;

  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 = i;    // copy

    tbButton[j].idCommand = IDM_COPY;
    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

    tbButton[j].idCommand = IDM_SEARCH;
    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_SEARCH_NEXT;
    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 = ++i;    // Forward Mail

    tbButton[j].idCommand = IDM_FORWARD;
    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 article

    tbButton[j].idCommand = IDM_SAVEAS;
    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 article

    tbButton[j].idCommand = IDM_DECODE_ARTICLE;
    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;    // Print

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

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

    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_SEP;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

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

    tbButton[j].idCommand = IDM_PREV_ARTICLE;
    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;    // Next Article

    tbButton[j].idCommand = IDM_NEXT_ARTICLE;
    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;    // Next Unseen

    tbButton[j].idCommand = IDM_FIND_NEXT_UNSEEN;
    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;    // Next Same Subject

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

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

    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_SEP;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    // Exit

    tbButton[j].idCommand = 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,
                                      IDB_ARTTOOLBAR,
                                      i + 1,
                                      hInst,
                                      IDB_ARTTOOLBAR,
                                      tbButton,
                                      j + 1,
                                      16,
                                      16,
                                      16,
                                      16,
                                      sizeof(TBBUTTON));
#else

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

    ThisDoc->hWndFrame = hWnd;
    ThisDoc->hDocWnd = CreateWindow ("WinVnArt",
                                     NULL,
                                     WS_CHILD | WS_VSCROLL | WS_HSCROLL,
                                     0,                             /* Initial X position */
                                     TOOLBARHEIGHT,                 /* Initial Y position */
                                     myRect.right - myRect.left,    /* Initial X width */
    (myRect.bottom - myRect.top) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,    /* Initial Y height */
                                     hWnd,
                                     NULL,
                                     hInst,
                                     NULL);
    SetHandleBkBrush (ThisDoc->hDocWnd, hArticleBackgroundBrush);
    ShowWindow (ThisDoc->hDocWnd, SW_SHOWNORMAL);

    NumArticleWnds++;

    SetArticleMenus (ThisDoc, DISABLE);
    // copy button should be disabled to begin with
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_COPY, FALSE);
    break;

  case WM_KEYDOWN:
    SendMessage (ThisDoc->hDocWnd, (UINT) WM_KEYDOWN, wParam, lParam);
    break;

  case WM_COMMAND:
    {
    WORD wFunc = LOWORD(wParam);
    switch (wFunc) {
    case IDV_EXIT:
      ThisDoc->TextSelected = FALSE;
      CloseArtWnd (hWnd, ThisDoc);
      return (0);
      break;

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

    case IDM_SAVE:
      if (strcmp (SaveArtFileName, "")) {
        SaveArtAppend = TRUE;
        MRRWriteDocument (ActiveArticleDoc, sizeof (TypText), SaveArtFileName, SaveArtAppend);
        break;
      }
      else {
        goto saveas;
      }

    case IDM_SAVEAS:
    saveas:;

      if (DialogBox (hInst, "WinVnSaveArt", hWnd, lpfnWinVnSaveArtDlg)) {
        InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
      }
      break;

    case IDM_PRINT:
      PrintArticle (hWnd, ThisDoc);
      break;

    case IDM_PRINT_SETUP:
      PrinterSetup (hWnd, PD_PRINTSETUP);
      break;

    case IDM_CANCELART:                         /* JD 4/10/95 */
      
        AllowCancel = FALSE;
        // Extra checks to make sure we really are allowed to Cancel this Article
        GetHeaderLine (ThisDoc, "From:", headerLine, MAXHEADERLINE);
        if (headerLine) {
          GetFromAddress (mybuf, MAXHEADERLINE, ThisDoc);
          if (string_compare_insensitive(mybuf,headerLine+6))    /* 6 = size of "From: " */
              AllowCancel = TRUE;
          }
            
        if (AllowCancel == TRUE) {
          GetHeaderLine (ThisDoc, "Organization:", headerLine, MAXHEADERLINE);
          if (headerLine) {
            GetOrganization (mybuf, MAXHEADERLINE, ThisDoc);
            if (stricmp(mybuf,headerLine+14) == 0)    /* 14 = size of "Organization " */
              AllowCancel = TRUE;
            else
              AllowCancel = FALSE;
          }
        }
        
        if (AllowCancel == TRUE)
             CreateComposeWnd (hWnd, ThisDoc, DOCTYPE_CANCEL);
        else             
             MessageBox (hWnd, "Sorry..Only the original author is allowed \n"
                        "to cancel an article.","Cancel Not Allowed", 
                         MB_OK | MB_ICONSTOP);

      break;


    case IDM_DECODE_ARTICLE:
      if (TestCodingBusy (hWnd, "Can't decode this article"))
        break;
      if (CommBusy && CommDoc == ThisDoc) {
        MessageBox (hWnd, "Please wait until article retrieval is complete before decoding", "Please Wait", MB_OK);
        break;
      }

      if (!DialogBoxParam (hInst, "WinVnDecodeArts", hWnd, lpfnWinVnDecodeArtsDlg, 0)) {
        InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
      }
      else {
        DecodeInit ();
        if (AlsoDecodeOpenArticles) {
          DecodeOpenArticles (hWnd);
        }
        else {
          DecodeDoc (hWnd, ThisDoc);
        }
        DecodeDone ();
      }
      break;

    case IDM_SELECT_ALL:
      hMenu = GetMenu (hWnd);
      hSubMenu = GetSubMenu (hMenu, 1);     /* Edit menu */

      EnableMenuItem (hSubMenu, IDM_COPY, ENABLE_MENU);
      SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_COPY, TRUE);
      EnableMenuItem (hSubMenu, IDM_DESELECT_ALL, ENABLE_MENU);

      ThisDoc->TextSelected = TRUE;
      ThisDoc->BeginSelect.LineNum = 0;
      ThisDoc->BeginSelect.CharNum = 0;
      ThisDoc->EndSelect.LineNum = ThisDoc->TotalLines - 1;
      FindLineOrd (ThisDoc, (unsigned) ThisDoc->EndSelect.LineNum, &BlockPtr, &LinePtr);
      textlen = GetTextLen(LinePtr);
      ThisDoc->EndSelect.CharNum = textlen - 1;
      GetClientRect (hWnd, &myRect);
      InvalidateRect (ThisDoc->hDocWnd, &myRect, FALSE);
      break;

    case IDM_DESELECT_ALL:
      hMenu = GetMenu (hWnd);
      hSubMenu = GetSubMenu (hMenu, 1);
      EnableMenuItem (hSubMenu, IDM_COPY, DISABLE_MENU);
      SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_COPY, FALSE);
      EnableMenuItem (hSubMenu, IDM_DESELECT_ALL, DISABLE_MENU);

      ThisDoc->TextSelected = FALSE;
      GetClientRect (hWnd, &myRect);
      InvalidateRect (ThisDoc->hDocWnd, &myRect, FALSE);
      break;

    case IDM_COPY:
      if (ThisDoc->TextSelected) {
        CopyTextToClipBoard (ThisDoc);
        SendMessage (ThisDoc->hWndFrame, (UINT) WM_COMMAND, (WPARAM) IDM_DESELECT_ALL, (LPARAM) 0L);    //Un-mark selection
      }
      break;

    case IDM_NEXT_SAME_SUBJ:
    case IDM_NEXT_ARTICLE:
    case IDM_PREV_ARTICLE:
    case IDM_FIND_NEXT_UNSEEN:
    case IDM_FIND_NEXT_SAME:
    case IDM_FIND_NEXT_SELECTED:

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

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

        if (GroupDoc) {
          if (wFunc == IDM_FIND_NEXT_UNSEEN) {
           artindex = ThisDoc->LastSeenLineID;
            header_num = -1;

            while ((++artindex < GroupDoc->total_headers) && header_num < 0) {
              header = header_elt (headers, artindex);
              if (ART_UNSEEN == (ART_SEENORKILLED & header->SeenSelected))
                header_num = artindex;
            }

            if (header_num >= 0) {
              if (ArtListMultiSelect) {
                ThisDoc->ParentDoc->AnchorLineID = header_num;
                SetGroupSelections (ThisDoc->ParentDoc, headers, header_num);
              }
              SetArticleMenus (ThisDoc, DISABLE);
              ViewArticle (ThisDoc->ParentDoc, header_num, REUSE, SHOW, NO_ID);
              AdjustTopScByDoc (ThisDoc->ParentDoc, header_num);

            }
            else
              MessageBox (hWnd, "No more Unseen articles in this Group", "That's all!", MB_OK);
          }
                    
      else if (wFunc == IDM_FIND_NEXT_SELECTED)
      {
        artindex = -1;
        header_num = -1;

        while ((++artindex < GroupDoc->total_headers) && header_num < 0)
        {
          header = header_elt (headers, artindex);
          if(ART_SELECTED & header->SeenSelected)
          {
            if(artindex == ThisDoc->LastSeenLineID)
            {
              header->SeenSelected &= ~ART_SELECTED;
            }
            else
            {
              header_num = artindex;
            }
          }
        }

        if (header_num >= 0)
        {
          SetArticleMenus (ThisDoc, DISABLE);
          ViewArticle (ThisDoc->ParentDoc, header_num, REUSE, SHOW, NO_ID);
          article_operation(ThisDoc->ParentDoc, header_num, selected_false);
          AdjustTopScByDoc (ThisDoc->ParentDoc, header_num);
        }
        else
          MessageBox (hWnd, "No more Selected articles in this Group", "That's all!", MB_OK);
      }
          else if (wFunc == IDM_NEXT_SAME_SUBJ || wFunc == IDM_FIND_NEXT_SAME) {
            header_num =
              find_article_by_subject (headers,
                                       ThisDoc->LastSeenLineID,
                                       GroupDoc->total_headers - 1);

            if (header_num >= 0) {
              if (ArtListMultiSelect) {
                ThisDoc->ParentDoc->AnchorLineID = header_num;
                SetGroupSelections (ThisDoc->ParentDoc, headers, header_num);
              }
              SetArticleMenus (ThisDoc, DISABLE);
              ViewArticle (ThisDoc->ParentDoc, header_num, REUSE, SHOW, NO_ID);
              AdjustTopScByDoc (ThisDoc->ParentDoc, header_num);
            }
            else
              MessageBox (hWnd, "No more articles", "That's all!", MB_OK);
          }

          else if ((wFunc == IDM_NEXT_ARTICLE)
             && (ThisDoc->LastSeenLineID < (GroupDoc->total_headers - 1))) {
            if (ArtListMultiSelect) {
              ThisDoc->ParentDoc->AnchorLineID = ThisDoc->LastSeenLineID;
              SetGroupSelections (ThisDoc->ParentDoc, headers, ThisDoc->LastSeenLineID + 1);
            }
            SetArticleMenus (ThisDoc, DISABLE);
            ViewArticle (ThisDoc->ParentDoc, ThisDoc->LastSeenLineID + 1, REUSE, SHOW, NO_ID);
            AdjustTopScByDoc (ThisDoc->ParentDoc, ThisDoc->LastSeenLineID + 1);
          }
          else if ((wFunc == IDM_PREV_ARTICLE)
                   && (ThisDoc->LastSeenLineID > 0)) {
            if (ArtListMultiSelect) {
              ThisDoc->ParentDoc->AnchorLineID = ThisDoc->LastSeenLineID;
              SetGroupSelections (ThisDoc->ParentDoc, headers, ThisDoc->LastSeenLineID - 1);
            }
            SetArticleMenus (ThisDoc, DISABLE);
            ViewArticle (ThisDoc->ParentDoc, ThisDoc->LastSeenLineID - 1, REUSE, SHOW, NO_ID);
            AdjustTopScByDoc (ThisDoc->ParentDoc, ThisDoc->LastSeenLineID - 1);
          }
          else
            MessageBox (hWnd, "No more articles", "That's all!", MB_OK);
        }

        else
          MessageBox (hWnd, "The original group-list window for this article\n"
                      "has been closed.  WinVN cannot find the next/prev article",
                      "Can't find next/prev article", MB_OK);

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

      else
          MessageBox (hWnd, "The original group-list window for this article\n"
                      "has been closed.  WinVN cannot find the next/prev article",
                      "Can't find next/prev article", MB_OK);

      break;

    case IDM_POST:
    if (FollowupToPoster(ThisDoc) == TRUE)
      CreateComposeWnd (hWnd, ThisDoc, DOCTYPE_MAIL);
    else
      CreateComposeWnd (hWnd, ThisDoc, DOCTYPE_POSTING);
      break;

    case IDM_MAIL:
      (MailCtrl.fnMlWinCreate) (hWnd, ThisDoc, DOCTYPE_MAIL);
      break;

    case IDM_FORWARD:
      (MailCtrl.fnMlWinCreate) (hWnd, ThisDoc, DOCTYPE_FORWARD);
      break;

    case IDM_HELP:
      MakeHelpPathName (mybuf, MAXINTERNALLINE);
      WinHelp (ThisDoc->hDocWnd, mybuf, HELP_INDEX, 0L);
      break;

    case IDM_FIND:
    case IDM_SEARCH:
      FindDoc = ThisDoc;
      if (!(FindDoc->SearchStr[0]) && LastArticleTextFind[0])
        strcpy (FindDoc->SearchStr, LastArticleTextFind);

      if (DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg)) {
        found = DoFind (TRUE, FALSE);
        if (!found) {
          strcpy (mybuf, "\"");
          strcat (mybuf, FindDoc->SearchStr);
          strcat (mybuf, "\" not found.");
          MessageBox (hWnd, mybuf, "Not found", MB_OK);
        }
        else
          strcpy (LastArticleTextFind, FindDoc->SearchStr);
      }
      break;

    case IDM_SEARCH_NEXT:

      FindDoc = ThisDoc;
      continueFind = TRUE;
      if (!FindDoc->SearchStr[0]) {
        if (!(FindDoc->SearchStr[0]) && LastArticleTextFind[0])
          strcpy (FindDoc->SearchStr, LastArticleTextFind);
        continueFind = DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
      }

      if (continueFind && FindDoc->SearchStr[0]) {
        found = DoFind (!FindDoc->hFindBlock && !FindDoc->FindLineID, FALSE);
        if (!found) {
          strcpy (mybuf, "\"");
          strcat (mybuf, FindDoc->SearchStr);
          strcat (mybuf, "\" not found.");
          MessageBox (hWnd, mybuf, "No more occurrences", MB_OK);
        }
      }
      break;


    case IDM_ROT13:
      SetArticleRot13Mode (ThisDoc->hWndFrame, GetArticleRot13Mode (ThisDoc->hWndFrame) != TRUE);
      InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
      break;

    case ID_ARTICLE_RETRIEVE_COMPLETE:
      SetArticleMenus (ThisDoc, ENABLE);
      /* let my group know that I'm done receiving */
      if (ThisDoc->ParentDoc) {
        SendMessage (ThisDoc->ParentDoc->hWndFrame, message, wParam, lParam);
      }
      SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
      InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
      
      /* find out if this article is crossposted to other groups  JD 5/4/95*/
      if (((TrackSubscribedCrossposts == TRUE) || 
           (TrackUnsubscribedCrossposts == TRUE)) &&  
          (GetXref(headerLine,MAXHEADERLINE,ThisDoc))) {
        ptr = headerLine;
        while (*ptr != 0) {
          ptr1 = ptr;
          while((*ptr1 != 0) && (*ptr1 != ':')) (ptr1)++;  /* HB 1996/03/23 */
          strntcpy(mybuf,ptr,ptr1-ptr);                       /* save groupname */
          NextToken(&ptr);                                    /* skip over artindex */
          ptr1++;
          strntcpy(mybuf1,ptr1,ptr-ptr1);                     /* save artindex */
          if (stricmp(mybuf,CurrentGroup) != 0)               /* mark if not our group */ 
             mark_article_seen(&NetDoc, mybuf,atol(mybuf1));
        }
      }
      break;

    }
    }

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

  }
  SetFocus (ThisDoc->hDocWnd);
  return (0);
}

//
// Mark an article as "seen".    (JD 5/4/95)
//
//  Doc       = pointer to the main Newsgroups Doc structure
//  groupname = string containing name of newsgroup
//  artindex  = long containing the article index number on the server
//

void 
mark_article_seen(TypDoc FAR * Doc, char * groupname, long artindex)
{
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;
  HANDLE hBlock;
  unsigned int Offset,TextOffset;
  BOOL found = -1;
  int TargLen = 0;
  int SourceLen;
  char *Target;
  char sourceline[MAXHEADERLINE];
  char targline[MAXFINDSTRING];
  char *sourceptr, far * orglineptr;
  char *targptr;
  TypGroup far *MyGroup;
  TypLineID MyLineID;  
  register char ch;
  int CharPos = -1;
  long index;
  
  //  First locate the Group Doc structure
  if(LockLine (Doc->hFirstBlock, sizeof(TypBlock), 0L, &BlockPtr, &LinePtr))
  {
    TextOffset = Doc->OffsetToText;
    Target = groupname;
    for (targptr = targline; ch = *Target, *(targptr++) = tolower (ch);TargLen++)
      Target++;
    
    if((LinePtr->length != END_OF_BLOCK) && (LinePtr->length > 0))
    {
      do
      {
        if(LinePtr->active)
        {
          orglineptr = (char far *) LinePtr + TextOffset;
          sourceptr = sourceline;
          for(SourceLen = 0;
          ch = *(orglineptr), ((*(sourceptr++) = tolower (ch)) && SourceLen < MAXHEADERLINE);
          SourceLen++)
            orglineptr++;
          CharPos = SearchLine (sourceline, SourceLen, targline, TargLen);
        }
      }
      while(CharPos == -1 && NextLine (&BlockPtr, &LinePtr));
    }
    
    if(CharPos >= 0)
    {
      //  Found Group, now get pointers to Group structure
      MyGroup = GetGroup(LinePtr);
      
      if(MyGroup->Subscribed ? TrackSubscribedCrossposts : TrackUnsubscribedCrossposts)
      {
//        if(!MyGroup->pSeendb)
//          MyGroup->pSeendb = new WVSeenDB(MyGroup->ServerFirst, MyGroup->ServerLast); // this can fail...
//
        WVSeenDB*pSDB;
        pSDB = MyGroup->pSeendb;
        MyGroup->pSeendb = new WVSeenDB(MyGroup->ServerFirst, MyGroup->ServerLast, pSDB);
        if(MyGroup->pSeendb)
           delete pSDB;
         else
           MyGroup->pSeendb = pSDB;

        MyGroup->pSeendb->MarkSeen(artindex);
        
        if((MyGroup->total_headers > 0) && (MyGroup->header_handle) && (MyGroup->SubjDoc))
        {
          index = find_index_from_artnum(artindex, MyGroup->header_handle, 
            MyGroup->thread_handle, MyGroup->total_headers);
          if(index>=0)
          {
            article_operation(MyGroup->SubjDoc, index, seen_true);
            if(MyGroup->SubjDoc->hDocWnd)
              InvalidateRect(MyGroup->SubjDoc->hDocWnd, NULL, FALSE);
          }
        }
      }
    } 
    UnlockLine (BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  }
}
