head	1.52;
access;
symbols
	V0-99-8:1.52
	V0-99-7:1.51
	V0-99-6:1.50
	V0-99-5:1.49
	V0-99-4:1.47
	V0-99-2:1.45
	V0-99-1:1.44
	V0-93-14:1.43
	V0-93-13:1.43
	V0-93-12:1.43
	V0-93-11:1.42
	V0-93-10:1.42
	V0-93-9:1.41
	V0-93-8:1.40
	V0-93-7:1.39
	V0-93-5:1.38
	V80:1.8
	V76d:1.1;
locks; strict;
comment	@ * @;


1.52
date	96.08.31.06.56.48;	author heirich;	state Exp;
branches;
next	1.51;

1.51
date	95.11.07.22.24.14;	author dumoulin;	state Exp;
branches;
next	1.50;

1.50
date	95.08.16.22.35.17;	author shimomai;	state Exp;
branches;
next	1.49;

1.49
date	95.06.06.04.03.49;	author dumoulin;	state Exp;
branches;
next	1.48;

1.48
date	95.05.20.04.09.40;	author dumoulin;	state Exp;
branches;
next	1.47;

1.47
date	95.05.19.23.00.30;	author dumoulin;	state Exp;
branches;
next	1.46;

1.46
date	95.04.21.21.47.11;	author dumoulin;	state Exp;
branches;
next	1.45;

1.45
date	95.03.29.00.34.20;	author dumoulin;	state Exp;
branches;
next	1.44;

1.44
date	95.03.16.17.13.04;	author dumoulin;	state Exp;
branches;
next	1.43;

1.43
date	95.01.19.02.47.25;	author jglasser;	state Exp;
branches;
next	1.42;

1.42
date	94.12.12.19.39.13;	author jcooper;	state Exp;
branches;
next	1.41;

1.41
date	94.11.30.22.22.08;	author jcooper;	state Exp;
branches;
next	1.40;

1.40
date	94.11.28.22.01.06;	author jcooper;	state Exp;
branches;
next	1.39;

1.39
date	94.11.21.21.20.46;	author jcooper;	state Exp;
branches;
next	1.38;

1.38
date	94.11.11.01.39.21;	author jglasser;	state Exp;
branches;
next	1.37;

1.37
date	94.11.10.01.56.22;	author rushing;	state Exp;
branches;
next	;


desc
@winvn version 0.76 placed into RCS
@


1.52
log
@added conditional support for WATCOM compiler
@
text
@
/*
 * $Id: wvart.c 1.51 1995/11/07 22:24:14 dumoulin Exp $
 */

/*---  wvart.c ------------------------------------------- */
/*  This file contains the window procedure for the Article Viewing window
 *  for WinVn.
 */

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <ctype.h>
#include "wvtb\wvtb.h"
#include <string.h>
#include <shellapi.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)
{
  char far *nextsub;
  char far *thissub;

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

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

  do {
	nextsub = (header_elt (headers, artindex))->subject;
	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   

void
 DoShell (char far *client, char far *argument)
 {
   char execute[MAXINTERNALLINE + MAXFILENAME];
   unsigned int err;

 #ifdef WIN32
   STARTUPINFO sInfo;
   PROCESS_INFORMATION pInfo;
 #endif
   
	if (argument)
       _snprintf (execute, MAXINTERNALLINE + MAXFILENAME, "%s %s", client, argument);
	else
	   _snprintf (execute, MAXINTERNALLINE + MAXFILENAME, "%s", client);

 #ifdef WIN32
   memset (&sInfo, 0, sizeof (STARTUPINFO));
   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 {
 #else
   if ((err = WinExec (execute, SW_SHOWNORMAL)) < 32) {
 #endif
    sprintf(str,"Error executing %s, error %u",execute, err); 
    SetStatbarText(CommDoc->hWndFrame,str,&NetDoc, TRUE, TRUE);
 	return;
   }
 }
 
//  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[MAXINTERNALLINE];
   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( 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';
	  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');
   }
   return(TRUE);
 }


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

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

    PURPOSE:  Processes messages

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

long WINAPI 
WinVnArtWndProc (hWnd, message, wParam, lParam)
	 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;
  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_SPACE) {
	  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_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) {
		  TempX = (long) ArtSideSpace;
		}
		else {
		  TempX = (long) min (X, (int) ThisDoc->ScXWidth);
		}

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

			Previous.LineNum = ThisDoc->EndSelect.LineNum;
			Previous.CharNum = ThisDoc->EndSelect.CharNum;
			SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINELEFT, 0);
			ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 == 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 > (int) ThisDoc->ScXWidth) && ((int) ThisDoc->ScXOffset < ScMax)) {
		  ScrollText = TRUE;

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

			Previous.LineNum = ThisDoc->EndSelect.LineNum;
			Previous.CharNum = ThisDoc->EndSelect.CharNum;
			SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINERIGHT, 0);
			ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 == 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)) {
		  ScrollText = TRUE;

		  do					// keep scrolling until the mouse moves.
		   {
			TempY = ArtTopSpace;
			if (X < ArtSideSpace) {
			  TempX = ArtSideSpace;
			}
			else {
			  TempX = min (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 - (int) ArtTopSpace) / (int) 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 == 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 > (int) ThisDoc->ScYHeight) &&
			((int) ThisDoc->TopLineOrd < ScMax)) {
		  ScrollText = TRUE;

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

			if (X < ArtSideSpace) {
			  TempX = ArtSideSpace;
			}
			else {
			  TempX = min (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 - (int) ArtTopSpace) / (int) 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 == CursorLocation.x) && (Y == CursorLocation.y)
				 && ((int) ThisDoc->TopLineOrd < ScMax));
		}

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

		  Previous.LineNum = ThisDoc->EndSelect.LineNum;
		  Previous.CharNum = ThisDoc->EndSelect.CharNum;
		  ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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) {
			TempX = ArtSideSpace;
		  }
		  else {
			TempX = min (X, (int) ThisDoc->ScXWidth);
		  }
		  if (Y < ArtTopSpace) {
			TempY = ArtTopSpace;
		  }
		  else {
			TempY = min ((long) Y, (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
		  }

		  ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 */
		char far *start, far * end;

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

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

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

//		if ((start >= textptr) && (end < (textptr + textlen))) {
		  sprintf (str, "%.*Fs", (int) ((long) end - (long) start + 1), start);
		  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;
	  int VertLines, HorzChars;
	  int EndofDoc = FALSE;
	  int RangeHigh, CurPos;
	  int 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;

	  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) {
		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,
	  	 ThisDoc->ThumbTracking ? FALSE : TRUE);
	  /* thumb pos is updated at end of thumb track */
	  if (!ThisDoc->ThumbTracking) {
	  	SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
	  }

	  RangeHigh = ThisDoc->LongestLine - ThisDoc->ScXChars;
	  if (RangeHigh < 0) {
		RangeHigh = 0;
		ThisDoc->ScXOffset = 0;
	  }
	  SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
	  SetScrollPos (hWnd, SB_HORZ, 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);
		  
          /*  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);

		}
		while (--VertLines > 0 && NextLine (&BlockPtr, &LinePtr));


	  /* 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 = 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, 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 avbailability
	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 (HANDLE 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 (HANDLE 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;
  int 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) {
		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) {
		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 - (int) DocPtr->TopLineOrd) * (int) 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 - (int) DocPtr->TopLineOrd) * (int) 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 - (int) DocPtr->TopLineOrd) * (int) 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;
  int 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 = 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, message, wParam, lParam)
	 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_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_PREV_ARTICLE, i ? FALSE : TRUE);

	#ifdef __WATCOMC__
    #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;
	tbButton[++j].iBitmap = ++i;	// find

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

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

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

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

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

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

	tbButton[j].idCommand = IDM_MAIL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Forward Mail

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

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

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

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

	tbButton[j].idCommand = IDM_SAVEAS;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Decode article

	tbButton[j].idCommand = IDM_DECODE_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	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;

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

	tbButton[j].idCommand = IDM_PREV_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Article

	tbButton[j].idCommand = IDM_NEXT_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Unseen

	tbButton[j].idCommand = IDM_FIND_NEXT_UNSEEN;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Same Subject

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

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

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

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

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

	tbButton[++j].iBitmap = 8;	// separator
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

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

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

	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:
	switch (LOWORD (wParam)) {
	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:

	  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 (LOWORD (wParam) == IDM_FIND_NEXT_UNSEEN) {
			artindex = ThisDoc->LastSeenLineID;
			header_num = -1;

			while ((++artindex < GroupDoc->total_headers) && header_num < 0) {
			  header = header_elt (headers, artindex);
			  if (!header->Seen)
				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 (LOWORD (wParam) == IDM_NEXT_SAME_SUBJ || LOWORD (wParam) == 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 ((LOWORD (wParam) == 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 ((LOWORD (wParam) == 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 ((*ptr != 0) && (*ptr1 != ':')) (ptr1)++;
		  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 and 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,*ParentBlock;
  TypLine far *LinePtr, *ParentLine;
  HANDLE hBlock;
  unsigned int Offset,TextOffset;
  unsigned int rindex,MyLength;
  BOOL found = -1;
  int TargLen = 0;
  int SourceLen;
  char *Target;
  char sourceline[MAXHEADERLINE];
  char targline[MAXFINDSTRING];
  char *sourceptr, far * orglineptr;
  char *targptr;
  TypRange *RangePtr;
  TypGroup far *MyGroup;
  TypLineID MyLineID;  
  register char ch;
  int CharPos = -1;
  long first,last,nextfirst,nextlast,index;

  //  First locate the Group Doc structure
  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 and range array
     MyGroup = GetGroup(LinePtr);
     RangePtr = GetRangePtr(MyGroup);

     if ((TrackSubscribedCrossposts && (MyGroup->Subscribed == TRUE)) ||
         (TrackUnsubscribedCrossposts && (MyGroup->Subscribed == FALSE))) {
     
   //  If we already have downloaded headers for this group, we can just flag
   //  the article as being seen.  If not, we need to reconstruct the ranges
   //  kept in the NewsRC file.
   
      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);
           }
         }
      else { 
	   
	   // if we are out of elements in the SpareRange array, we need to
	   // copy the entire Group structure into a larger area. 
	   if (MyGroup->nSpareRanges <= 0) {

	    MyLineID = LinePtr->LineID;
	    LockLine (Doc->hFirstBlock, sizeof(TypBlock), MyLineID, &ParentBlock, &ParentLine);
        if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) != NULL) {   
            MoveBytes (ParentLine, LinePtr, ParentLine->length);
            MyGroup = GetGroup(LinePtr);
            MyGroup->nSpareRanges	= SpareRanges;
            MyLength = CalcGroupLen(MyGroup);
            LinePtr->length = MyLength;
            GroupLenPtr(LinePtr) = MyLength;
            ReplaceLine (LinePtr, &ParentBlock, &ParentLine);            
            GlobalFreePtr (LinePtr);
            LinePtr=ParentLine;
            BlockPtr = ParentBlock;
          }
          
         MyGroup = GetGroup(LinePtr);
         RangePtr = GetRangePtr(MyGroup);     
         MyLineID = LinePtr->LineID;  
       }
	   
       if ((MyGroup->nRanges == 0) && MyGroup->nSpareRanges > 0){
         MyGroup->nRanges = 1;
		 (MyGroup->nSpareRanges)--;
		 (MyGroup->NumUnread)--;
         LinePtr->length = CalcGroupLen(MyGroup);
         GroupLenPtr(LinePtr) = LinePtr->length;
         RangePtr[0].First = artindex;
         RangePtr[0].Last = artindex;
         }
       else
         {
         for (rindex=0; rindex+1 <= MyGroup->nRanges; rindex++) {
	       first = RangePtr[rindex].First;
	       last = RangePtr[rindex].Last;
	       if ((artindex == first) || 
	           (artindex == last) ||
	           ((artindex >= first) && (artindex <= last)))  break;  /* exit if already seen */
	           
	       if (artindex == first-1) {
	          RangePtr[rindex].First = artindex;                     /* extend start of range */
	          (MyGroup->NumUnread)--;
	          break;
		   }

	       if (artindex == last+1){
	          RangePtr[rindex].Last = artindex;                      /* extend end of range */
	          (MyGroup->NumUnread)--;
	          break;
	       }

		   /* add new range if on last range and if there is room for more */
	       if ((artindex > last) && 
	           (rindex+1 == MyGroup->nRanges) &&
			   (MyGroup->nSpareRanges > 0)) {
              rindex++;
	          RangePtr[rindex].First = artindex;
	          RangePtr[rindex].Last = artindex;
	          (MyGroup->nRanges)++;
			  (MyGroup->nSpareRanges)--;
	          LinePtr->length = CalcGroupLen(MyGroup);
              GroupLenPtr(LinePtr) = LinePtr->length;
              (MyGroup->NumUnread)--;	     
	          break;
	       }
	       if ((artindex < first) && (MyGroup->nSpareRanges > 0)){
	          RangePtr[rindex].First = artindex;
	          RangePtr[rindex].Last = artindex;
	          for (rindex = rindex+1; rindex < MyGroup->nRanges; rindex++) {
	             nextfirst = RangePtr[rindex].First;
	             nextlast = RangePtr[rindex].Last;
	             RangePtr[rindex].First = first;
	             RangePtr[rindex].Last = last;
	             first = nextfirst;
	             last = nextlast;
	          }
	          (MyGroup->nRanges)++;
			  (MyGroup->nSpareRanges)--;
	          LinePtr->length = CalcGroupLen(MyGroup);
              GroupLenPtr(LinePtr) = LinePtr->length;
	          RangePtr[MyGroup->nRanges-1].First = first;
	          RangePtr[MyGroup->nRanges-1].Last = last;
	          (MyGroup->NumUnread)--;
	          break;
	         }
	      }
        }
	  }
     }
	 UnlockLine (BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);  
   } 
  }
@


1.51
log
@Fixed error when cutting the headers of Rot13 encoded articles
to the clipboard.
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.50 1995/08/16 22:35:17 shimomai Exp $
d1573 2
d1577 1
@


1.50
log
@added support for clicking on URLs
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.49 1995/06/06 04:03:49 dumoulin Exp shimomai $
d1435 2
a1436 1
        *GlobalMemoryPtr++ = ROT13Mode ? chROT13(*textptr++) : *textptr++;
d1442 2
a1443 1
	  	*GlobalMemoryPtr++ = ROT13Mode ? chROT13(*textptr++) : *textptr++;
d1455 2
a1456 1
	      *GlobalMemoryPtr++ = ROT13Mode ? chROT13(*textptr++) : *textptr++;
d1468 2
a1469 1
		    *GlobalMemoryPtr++ = ROT13Mode ? chROT13(*textptr++) : *textptr++;
d2215 1
a2215 1
         MyGroup->nRanges == 1;
@


1.49
log
@fixed problem with validation of author when canceling an article
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.48 1995/05/20 04:09:40 dumoulin Exp $
d19 2
d66 6
d73 206
d676 2
a677 3
		for (start = textptr + charnum; start >= textptr; start--)
		  if (*start == '<')
			break;
d679 8
a686 2
		for (end = textptr + charnum; end < (textptr + textlen); end++)
		  if (*end == '>')
d689 1
a689 1
		if ((start >= textptr) && (end < (textptr + textlen))) {
d691 1
@


1.48
log
@fix case sensitivity problem with canceling articles
@
text
@d1 2060
a2060 2060

/*
 * $Id: wvart.c 1.47 1995/05/19 23:00:30 dumoulin Exp $
 */

/*---  wvart.c ------------------------------------------- */
/*  This file contains the window procedure for the Article Viewing window
 *  for WinVn.
 */

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <ctype.h>
#include "wvtb\wvtb.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)
{
  char far *nextsub;
  char far *thissub;

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

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

  do {
	nextsub = (header_elt (headers, artindex))->subject;
	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 */
}


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

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

    PURPOSE:  Processes messages

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

long WINAPI 
WinVnArtWndProc (hWnd, message, wParam, lParam)
	 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;
  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_SPACE) {
	  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_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) {
		  TempX = (long) ArtSideSpace;
		}
		else {
		  TempX = (long) min (X, (int) ThisDoc->ScXWidth);
		}

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

			Previous.LineNum = ThisDoc->EndSelect.LineNum;
			Previous.CharNum = ThisDoc->EndSelect.CharNum;
			SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINELEFT, 0);
			ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 == 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 > (int) ThisDoc->ScXWidth) && ((int) ThisDoc->ScXOffset < ScMax)) {
		  ScrollText = TRUE;

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

			Previous.LineNum = ThisDoc->EndSelect.LineNum;
			Previous.CharNum = ThisDoc->EndSelect.CharNum;
			SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINERIGHT, 0);
			ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 == 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)) {
		  ScrollText = TRUE;

		  do					// keep scrolling until the mouse moves.
		   {
			TempY = ArtTopSpace;
			if (X < ArtSideSpace) {
			  TempX = ArtSideSpace;
			}
			else {
			  TempX = min (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 - (int) ArtTopSpace) / (int) 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 == 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 > (int) ThisDoc->ScYHeight) &&
			((int) ThisDoc->TopLineOrd < ScMax)) {
		  ScrollText = TRUE;

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

			if (X < ArtSideSpace) {
			  TempX = ArtSideSpace;
			}
			else {
			  TempX = min (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 - (int) ArtTopSpace) / (int) 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 == CursorLocation.x) && (Y == CursorLocation.y)
				 && ((int) ThisDoc->TopLineOrd < ScMax));
		}

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

		  Previous.LineNum = ThisDoc->EndSelect.LineNum;
		  Previous.CharNum = ThisDoc->EndSelect.CharNum;
		  ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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) {
			TempX = ArtSideSpace;
		  }
		  else {
			TempX = min (X, (int) ThisDoc->ScXWidth);
		  }
		  if (Y < ArtTopSpace) {
			TempY = ArtTopSpace;
		  }
		  else {
			TempY = min ((long) Y, (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
		  }

		  ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 */
		char far *start, far * end;

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

		for (end = textptr + charnum; end < (textptr + textlen); end++)
		  if (*end == '>')
			break;

		if ((start >= textptr) && (end < (textptr + textlen))) {
		  sprintf (str, "%.*Fs", (int) ((long) end - (long) start + 1), start);
		  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;
	  int VertLines, HorzChars;
	  int EndofDoc = FALSE;
	  int RangeHigh, CurPos;
	  int 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;

	  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) {
		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,
	  	 ThisDoc->ThumbTracking ? FALSE : TRUE);
	  /* thumb pos is updated at end of thumb track */
	  if (!ThisDoc->ThumbTracking) {
	  	SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
	  }

	  RangeHigh = ThisDoc->LongestLine - ThisDoc->ScXChars;
	  if (RangeHigh < 0) {
		RangeHigh = 0;
		ThisDoc->ScXOffset = 0;
	  }
	  SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
	  SetScrollPos (hWnd, SB_HORZ, 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);
		  
          /*  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);

		}
		while (--VertLines > 0 && NextLine (&BlockPtr, &LinePtr));


	  /* 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 = 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, 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 avbailability
	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 (HANDLE 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 (HANDLE 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;
  int 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) {
		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) {
		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 - (int) DocPtr->TopLineOrd) * (int) 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 - (int) DocPtr->TopLineOrd) * (int) 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 - (int) DocPtr->TopLineOrd) * (int) 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;
  int 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 = 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 ? 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 ? 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 ? 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 ? 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, message, wParam, lParam)
	 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_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_PREV_ARTICLE, i ? FALSE : TRUE);

	if (message == WM_ACTIVATE)	// kludgeville
	  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_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;
	tbButton[++j].iBitmap = ++i;	// find

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

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

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

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

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

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

	tbButton[j].idCommand = IDM_MAIL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Forward Mail

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

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

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

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

	tbButton[j].idCommand = IDM_SAVEAS;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Decode article

	tbButton[j].idCommand = IDM_DECODE_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	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;

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

	tbButton[j].idCommand = IDM_PREV_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Article

	tbButton[j].idCommand = IDM_NEXT_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Unseen

	tbButton[j].idCommand = IDM_FIND_NEXT_UNSEEN;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Same Subject

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

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

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

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

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

	tbButton[++j].iBitmap = 8;	// separator
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

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

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

	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:
	switch (LOWORD (wParam)) {
	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:

	  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 (LOWORD (wParam) == IDM_FIND_NEXT_UNSEEN) {
			artindex = ThisDoc->LastSeenLineID;
			header_num = -1;

			while ((++artindex < GroupDoc->total_headers) && header_num < 0) {
			  header = header_elt (headers, artindex);
			  if (!header->Seen)
				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 (LOWORD (wParam) == IDM_NEXT_SAME_SUBJ || LOWORD (wParam) == 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 ((LOWORD (wParam) == 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 ((LOWORD (wParam) == 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 ((*ptr != 0) && (*ptr1 != ':')) (ptr1)++;
		  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 and 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,*ParentBlock;
  TypLine far *LinePtr, *ParentLine;
  HANDLE hBlock;
  unsigned int Offset,TextOffset;
  unsigned int rindex,MyLength;
  BOOL found = -1;
  int TargLen = 0;
  int SourceLen;
  char *Target;
  char sourceline[MAXHEADERLINE];
  char targline[MAXFINDSTRING];
  char *sourceptr, far * orglineptr;
  char *targptr;
  TypRange *RangePtr;
  TypGroup far *MyGroup;
  TypLineID MyLineID;  
  register char ch;
  int CharPos = -1;
  long first,last,nextfirst,nextlast,index;

  //  First locate the Group Doc structure
  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 and range array
     MyGroup = GetGroup(LinePtr);
     RangePtr = GetRangePtr(MyGroup);

     if ((TrackSubscribedCrossposts && (MyGroup->Subscribed == TRUE)) ||
         (TrackUnsubscribedCrossposts && (MyGroup->Subscribed == FALSE))) {
     
   //  If we already have downloaded headers for this group, we can just flag
   //  the article as being seen.  If not, we need to reconstruct the ranges
   //  kept in the NewsRC file.
   
      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);
           }
         }
      else { 
	   
	   // if we are out of elements in the SpareRange array, we need to
	   // copy the entire Group structure into a larger area. 
	   if (MyGroup->nSpareRanges <= 0) {

	    MyLineID = LinePtr->LineID;
	    LockLine (Doc->hFirstBlock, sizeof(TypBlock), MyLineID, &ParentBlock, &ParentLine);
        if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) != NULL) {   
            MoveBytes (ParentLine, LinePtr, ParentLine->length);
            MyGroup = GetGroup(LinePtr);
            MyGroup->nSpareRanges	= SpareRanges;
            MyLength = CalcGroupLen(MyGroup);
            LinePtr->length = MyLength;
            GroupLenPtr(LinePtr) = MyLength;
            ReplaceLine (LinePtr, &ParentBlock, &ParentLine);            
            GlobalFreePtr (LinePtr);
            LinePtr=ParentLine;
            BlockPtr = ParentBlock;
          }
          
         MyGroup = GetGroup(LinePtr);
         RangePtr = GetRangePtr(MyGroup);     
         MyLineID = LinePtr->LineID;  
       }
	   
       if ((MyGroup->nRanges == 0) && MyGroup->nSpareRanges > 0){
         MyGroup->nRanges == 1;
		 (MyGroup->nSpareRanges)--;
		 (MyGroup->NumUnread)--;
         LinePtr->length = CalcGroupLen(MyGroup);
         GroupLenPtr(LinePtr) = LinePtr->length;
         RangePtr[0].First = artindex;
         RangePtr[0].Last = artindex;
         }
       else
         {
         for (rindex=0; rindex+1 <= MyGroup->nRanges; rindex++) {
	       first = RangePtr[rindex].First;
	       last = RangePtr[rindex].Last;
	       if ((artindex == first) || 
	           (artindex == last) ||
	           ((artindex >= first) && (artindex <= last)))  break;  /* exit if already seen */
	           
	       if (artindex == first-1) {
	          RangePtr[rindex].First = artindex;                     /* extend start of range */
	          (MyGroup->NumUnread)--;
	          break;
		   }

	       if (artindex == last+1){
	          RangePtr[rindex].Last = artindex;                      /* extend end of range */
	          (MyGroup->NumUnread)--;
	          break;
	       }

		   /* add new range if on last range and if there is room for more */
	       if ((artindex > last) && 
	           (rindex+1 == MyGroup->nRanges) &&
			   (MyGroup->nSpareRanges > 0)) {
              rindex++;
	          RangePtr[rindex].First = artindex;
	          RangePtr[rindex].Last = artindex;
	          (MyGroup->nRanges)++;
			  (MyGroup->nSpareRanges)--;
	          LinePtr->length = CalcGroupLen(MyGroup);
              GroupLenPtr(LinePtr) = LinePtr->length;
              (MyGroup->NumUnread)--;	     
	          break;
	       }
	       if ((artindex < first) && (MyGroup->nSpareRanges > 0)){
	          RangePtr[rindex].First = artindex;
	          RangePtr[rindex].Last = artindex;
	          for (rindex = rindex+1; rindex < MyGroup->nRanges; rindex++) {
	             nextfirst = RangePtr[rindex].First;
	             nextlast = RangePtr[rindex].Last;
	             RangePtr[rindex].First = first;
	             RangePtr[rindex].Last = last;
	             first = nextfirst;
	             last = nextlast;
	          }
	          (MyGroup->nRanges)++;
			  (MyGroup->nSpareRanges)--;
	          LinePtr->length = CalcGroupLen(MyGroup);
              GroupLenPtr(LinePtr) = LinePtr->length;
	          RangePtr[MyGroup->nRanges-1].First = first;
	          RangePtr[MyGroup->nRanges-1].Last = last;
	          (MyGroup->NumUnread)--;
	          break;
	         }
	      }
        }
	  }
     }
	 UnlockLine (BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);  
   } 
  }
@


1.47
log
@Added support for marking crossposted articles.  Changed
all raw pointer access functions to use accessor macros.
Added Help option to tool bar.  Added seperators to make
toolbar operations farther appart.
@
text
@d1 2060
a2060 2060

/*
 * $Id: wvart.c 1.46 1995/04/21 21:47:11 dumoulin Exp $
 */

/*---  wvart.c ------------------------------------------- */
/*  This file contains the window procedure for the Article Viewing window
 *  for WinVn.
 */

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <ctype.h>
#include "wvtb\wvtb.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)
{
  char far *nextsub;
  char far *thissub;

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

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

  do {
	nextsub = (header_elt (headers, artindex))->subject;
	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 */
}


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

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

    PURPOSE:  Processes messages

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

long WINAPI 
WinVnArtWndProc (hWnd, message, wParam, lParam)
	 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;
  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_SPACE) {
	  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_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) {
		  TempX = (long) ArtSideSpace;
		}
		else {
		  TempX = (long) min (X, (int) ThisDoc->ScXWidth);
		}

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

			Previous.LineNum = ThisDoc->EndSelect.LineNum;
			Previous.CharNum = ThisDoc->EndSelect.CharNum;
			SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINELEFT, 0);
			ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 == 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 > (int) ThisDoc->ScXWidth) && ((int) ThisDoc->ScXOffset < ScMax)) {
		  ScrollText = TRUE;

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

			Previous.LineNum = ThisDoc->EndSelect.LineNum;
			Previous.CharNum = ThisDoc->EndSelect.CharNum;
			SendMessage (ThisDoc->hDocWnd, WM_HSCROLL, SB_LINERIGHT, 0);
			ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 == 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)) {
		  ScrollText = TRUE;

		  do					// keep scrolling until the mouse moves.
		   {
			TempY = ArtTopSpace;
			if (X < ArtSideSpace) {
			  TempX = ArtSideSpace;
			}
			else {
			  TempX = min (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 - (int) ArtTopSpace) / (int) 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 == 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 > (int) ThisDoc->ScYHeight) &&
			((int) ThisDoc->TopLineOrd < ScMax)) {
		  ScrollText = TRUE;

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

			if (X < ArtSideSpace) {
			  TempX = ArtSideSpace;
			}
			else {
			  TempX = min (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 - (int) ArtTopSpace) / (int) 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 == CursorLocation.x) && (Y == CursorLocation.y)
				 && ((int) ThisDoc->TopLineOrd < ScMax));
		}

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

		  Previous.LineNum = ThisDoc->EndSelect.LineNum;
		  Previous.CharNum = ThisDoc->EndSelect.CharNum;
		  ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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) {
			TempX = ArtSideSpace;
		  }
		  else {
			TempX = min (X, (int) ThisDoc->ScXWidth);
		  }
		  if (Y < ArtTopSpace) {
			TempY = ArtTopSpace;
		  }
		  else {
			TempY = min ((long) Y, (long) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
		  }

		  ThisDoc->EndSelect.LineNum = (int) (TempY - (int) ArtTopSpace) / (int) 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 */
		char far *start, far * end;

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

		for (end = textptr + charnum; end < (textptr + textlen); end++)
		  if (*end == '>')
			break;

		if ((start >= textptr) && (end < (textptr + textlen))) {
		  sprintf (str, "%.*Fs", (int) ((long) end - (long) start + 1), start);
		  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;
	  int VertLines, HorzChars;
	  int EndofDoc = FALSE;
	  int RangeHigh, CurPos;
	  int 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;

	  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) {
		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,
	  	 ThisDoc->ThumbTracking ? FALSE : TRUE);
	  /* thumb pos is updated at end of thumb track */
	  if (!ThisDoc->ThumbTracking) {
	  	SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
	  }

	  RangeHigh = ThisDoc->LongestLine - ThisDoc->ScXChars;
	  if (RangeHigh < 0) {
		RangeHigh = 0;
		ThisDoc->ScXOffset = 0;
	  }
	  SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
	  SetScrollPos (hWnd, SB_HORZ, 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);
		  
          /*  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);

		}
		while (--VertLines > 0 && NextLine (&BlockPtr, &LinePtr));


	  /* 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 = 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, 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 avbailability
	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 (HANDLE 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 (HANDLE 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;
  int 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) {
		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) {
		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 - (int) DocPtr->TopLineOrd) * (int) 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 - (int) DocPtr->TopLineOrd) * (int) 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 - (int) DocPtr->TopLineOrd) * (int) 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;
  int 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 = 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 ? 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 ? 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 ? 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 ? 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, message, wParam, lParam)
	 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_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_PREV_ARTICLE, i ? FALSE : TRUE);

	if (message == WM_ACTIVATE)	// kludgeville
	  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_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;
	tbButton[++j].iBitmap = ++i;	// find

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

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

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

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

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

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

	tbButton[j].idCommand = IDM_MAIL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Forward Mail

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

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

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

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

	tbButton[j].idCommand = IDM_SAVEAS;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Decode article

	tbButton[j].idCommand = IDM_DECODE_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	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;

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

	tbButton[j].idCommand = IDM_PREV_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Article

	tbButton[j].idCommand = IDM_NEXT_ARTICLE;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Unseen

	tbButton[j].idCommand = IDM_FIND_NEXT_UNSEEN;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Next Same Subject

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

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

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

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

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

	tbButton[++j].iBitmap = 8;	// separator
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;

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

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

	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:
	switch (LOWORD (wParam)) {
	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 (stricmp(mybuf,headerLine+6) == 0)    /* 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:

	  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 (LOWORD (wParam) == IDM_FIND_NEXT_UNSEEN) {
			artindex = ThisDoc->LastSeenLineID;
			header_num = -1;

			while ((++artindex < GroupDoc->total_headers) && header_num < 0) {
			  header = header_elt (headers, artindex);
			  if (!header->Seen)
				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 (LOWORD (wParam) == IDM_NEXT_SAME_SUBJ || LOWORD (wParam) == 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 ((LOWORD (wParam) == 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 ((LOWORD (wParam) == 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 ((*ptr != 0) && (*ptr1 != ':')) (ptr1)++;
		  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 and 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,*ParentBlock;
  TypLine far *LinePtr, *ParentLine;
  HANDLE hBlock;
  unsigned int Offset,TextOffset;
  unsigned int rindex,MyLength;
  BOOL found = -1;
  int TargLen = 0;
  int SourceLen;
  char *Target;
  char sourceline[MAXHEADERLINE];
  char targline[MAXFINDSTRING];
  char *sourceptr, far * orglineptr;
  char *targptr;
  TypRange *RangePtr;
  TypGroup far *MyGroup;
  TypLineID MyLineID;  
  register char ch;
  int CharPos = -1;
  long first,last,nextfirst,nextlast,index;

  //  First locate the Group Doc structure
  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 and range array
     MyGroup = GetGroup(LinePtr);
     RangePtr = GetRangePtr(MyGroup);

     if ((TrackSubscribedCrossposts && (MyGroup->Subscribed == TRUE)) ||
         (TrackUnsubscribedCrossposts && (MyGroup->Subscribed == FALSE))) {
     
   //  If we already have downloaded headers for this group, we can just flag
   //  the article as being seen.  If not, we need to reconstruct the ranges
   //  kept in the NewsRC file.
   
      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);
           }
         }
      else { 
	   
	   // if we are out of elements in the SpareRange array, we need to
	   // copy the entire Group structure into a larger area. 
	   if (MyGroup->nSpareRanges <= 0) {

	    MyLineID = LinePtr->LineID;
	    LockLine (Doc->hFirstBlock, sizeof(TypBlock), MyLineID, &ParentBlock, &ParentLine);
        if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) != NULL) {   
            MoveBytes (ParentLine, LinePtr, ParentLine->length);
            MyGroup = GetGroup(LinePtr);
            MyGroup->nSpareRanges	= SpareRanges;
            MyLength = CalcGroupLen(MyGroup);
            LinePtr->length = MyLength;
            GroupLenPtr(LinePtr) = MyLength;
            ReplaceLine (LinePtr, &ParentBlock, &ParentLine);            
            GlobalFreePtr (LinePtr);
            LinePtr=ParentLine;
            BlockPtr = ParentBlock;
          }
          
         MyGroup = GetGroup(LinePtr);
         RangePtr = GetRangePtr(MyGroup);     
         MyLineID = LinePtr->LineID;  
       }
	   
       if ((MyGroup->nRanges == 0) && MyGroup->nSpareRanges > 0){
         MyGroup->nRanges == 1;
		 (MyGroup->nSpareRanges)--;
		 (MyGroup->NumUnread)--;
         LinePtr->length = CalcGroupLen(MyGroup);
         GroupLenPtr(LinePtr) = LinePtr->length;
         RangePtr[0].First = artindex;
         RangePtr[0].Last = artindex;
         }
       else
         {
         for (rindex=0; rindex+1 <= MyGroup->nRanges; rindex++) {
	       first = RangePtr[rindex].First;
	       last = RangePtr[rindex].Last;
	       if ((artindex == first) || 
	           (artindex == last) ||
	           ((artindex >= first) && (artindex <= last)))  break;  /* exit if already seen */
	           
	       if (artindex == first-1) {
	          RangePtr[rindex].First = artindex;                     /* extend start of range */
	          (MyGroup->NumUnread)--;
	          break;
		   }

	       if (artindex == last+1){
	          RangePtr[rindex].Last = artindex;                      /* extend end of range */
	          (MyGroup->NumUnread)--;
	          break;
	       }

		   /* add new range if on last range and if there is room for more */
	       if ((artindex > last) && 
	           (rindex+1 == MyGroup->nRanges) &&
			   (MyGroup->nSpareRanges > 0)) {
              rindex++;
	          RangePtr[rindex].First = artindex;
	          RangePtr[rindex].Last = artindex;
	          (MyGroup->nRanges)++;
			  (MyGroup->nSpareRanges)--;
	          LinePtr->length = CalcGroupLen(MyGroup);
              GroupLenPtr(LinePtr) = LinePtr->length;
              (MyGroup->NumUnread)--;	     
	          break;
	       }
	       if ((artindex < first) && (MyGroup->nSpareRanges > 0)){
	          RangePtr[rindex].First = artindex;
	          RangePtr[rindex].Last = artindex;
	          for (rindex = rindex+1; rindex < MyGroup->nRanges; rindex++) {
	             nextfirst = RangePtr[rindex].First;
	             nextlast = RangePtr[rindex].Last;
	             RangePtr[rindex].First = first;
	             RangePtr[rindex].Last = last;
	             first = nextfirst;
	             last = nextlast;
	          }
	          (MyGroup->nRanges)++;
			  (MyGroup->nSpareRanges)--;
	          LinePtr->length = CalcGroupLen(MyGroup);
              GroupLenPtr(LinePtr) = LinePtr->length;
	          RangePtr[MyGroup->nRanges-1].First = first;
	          RangePtr[MyGroup->nRanges-1].Last = last;
	          (MyGroup->NumUnread)--;
	          break;
	         }
	      }
        }
	  }
     }
	 UnlockLine (BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);  
   } 
  }
@


1.46
log
@Add cancel capability and warning message when posting window is larger
than 78 characters in width.
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.45 1995/03/29 00:34:20 dumoulin Exp $
d13 1
d18 1
a18 1
#include "wvtb\wvtb.h"			/* for toolbar  */
d20 1
a20 7
int cursor_to_char_number (int X, int Y, TypDoc * DocPtr, TypBlock far ** BlockPtr, TypLine far ** LinePtr);
void CloseArtWnd (HWND hWnd, TypDoc * ThisDoc);
void UpdateHighlightedText (TypDoc far * DocPtr, TextSelect far * PreviousPos);
void HighlightText (TypDoc far * DocPtrs);
void CopyTextToClipBoard (TypDoc FAR * DocPtr);

/************** find_article_geader_subject *************
d36 1
a36 1
 ********************************************************/
d80 1
a80 1
  PAINTSTRUCT ps;				/* paint structure          */
d82 2
a83 2
  HDC hDC;						/* handle to display context */
  RECT myRect;					/* selection rectangle      */
d205 1
a205 1
		ThisDoc->BeginSelect.CharNum = (int) lcursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d258 1
a258 1
			ThisDoc->EndSelect.CharNum = (int) lcursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d292 1
a292 1
			ThisDoc->EndSelect.CharNum = (int) lcursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d323 1
a323 1
			ThisDoc->EndSelect.CharNum = (int) lcursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d359 1
a359 1
			ThisDoc->EndSelect.CharNum = (int) lcursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d391 1
a391 1
		  ThisDoc->EndSelect.CharNum = (int) lcursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d440 1
a440 1
		  ThisDoc->EndSelect.CharNum = (int) lcursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d453 1
a453 1
	charnum = lcursor_to_char_number (X, Y, ThisDoc, &BlockPtr, &LinePtr);
d456 2
a457 5
	  textlen = ((TypText far *) ((char far *) LinePtr +
								  sizeof (TypLine)))->NameLen;

	  textptr = (char far *) ((char far *) LinePtr +
							  sizeof (TypLine) + sizeof (TypText));
d476 1
a476 2
		  GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

d536 1
a536 2

	  /* Update the scroll bar thumb position.                 */
a569 1

a570 1

d576 5
a580 7
		  //    MyLen = LinePtr->length - sizeof (TypLine) - sizeof (int) - 1;
		  MyLen = ((TypText far *) ((char far *) LinePtr +
									sizeof (TypLine)))->NameLen;
		  textptr = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypText);

		  if (ROT13Mode && ((unsigned) lineNum > ThisDoc->HeaderLines)) {	/*  within body of article take copy of string to
																			   avoid changing original data */
d585 3
a587 2

		  if (ItalicizeQuotes && isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
d590 1
a590 1
		  else {				/* prepare to print a normal line */
a779 1

a780 1

a781 1

a782 1

a783 1

a785 1

a786 1

a859 1

a861 1
 *
d874 1
a874 2
/*            chROT13

a877 1
 *
a897 1

a900 1
 *
a915 3



a952 1

d1007 5
a1011 5
  	textlen = ((TypText far *) ((char far *) (LinePtr) +
								  sizeof (TypLine)))->NameLen;
  	textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));

  	if (ItalicizeQuotes && isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
d1014 1
a1014 1
  	else {						/* prepare to print a normal line */
d1030 1
a1030 1
	  	ExtTextOut (hDC, Rect.left, Rect.top, ETO_OPAQUE, &Rect,
d1032 1
a1032 1
		}
d1034 4
a1037 4
	  	Rect.right = Rect.left + 4;
	  	hSolidBrush = CreateSolidBrush (GetBkColor (hDC));
	  	FillRect (hDC, &Rect, hSolidBrush);	/* Rectangle used to represent CR/LF */
	  	DeleteObject ((HGDIOBJ) hSolidBrush);
a1038 2

//     ReleaseDC(DocPtr->hDocWnd, hDC);
a1055 1

a1056 1

d1063 5
a1067 4
	  	textlen = ((TypText far *) ((char far *) (LinePtr) +
									  sizeof (TypLine)))->NameLen;
		  textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));
	  	if (ItalicizeQuotes && isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
d1070 1
a1070 1
	  	else {					/* prepare to print a normal line */
d1093 1
a1093 1
		  CurrentLineNum++;
a1094 1

d1098 5
a1102 4
		textlen = ((TypText far *) ((char far *) (LinePtr) +
									sizeof (TypLine)))->NameLen;
		textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));
		if (ItalicizeQuotes && isLineQuotation (textptr)) {		/* prepare to print a quotation Line */
d1105 1
a1105 1
		else {						/* prepare to print a normal line */
a1132 1

a1134 1

d1169 1
a1169 2
  textlen = ((TypText far *) ((char far *) (LinePtr) +
							  sizeof (TypLine)))->NameLen;
d1176 1
a1176 1
	/* Length of the first line. The 2 is for CR/LF.                */
d1182 1
a1182 1
	/* Add in the lengths of all whole lines selected               */
d1184 2
a1185 6
	  textlen = ((TypText far *) ((char far *) (LinePtr) +
								  sizeof (TypLine)))->NameLen;

	  SelectedTextLength += textlen + 2;
	  /* The 2 is for CR/LF.                                       */

d1190 2
a1191 3
	/* Add in the length of the last line selected.                 */
	textlen = ((TypText far *) ((char far *) (LinePtr) +
								sizeof (TypLine)))->NameLen;
d1196 1
a1196 2
	  SelectedTextLength += 2;
	  /* The 2 is for CR/LF.                                       */
d1200 2
a1201 2
  /* Allocate memory and move selected text to clipboard.           *
   * The 1 is for NULL string termination.                          */
d1207 2
a1208 3
	textlen = ((TypText far *) ((char far *) (LinePtr) +
								sizeof (TypLine)))->NameLen;
	textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));
d1227 2
a1228 2
	  /* Copy all whole lines selected add add CR/LF at the end       *
	     * of each line.                                                */
d1230 2
a1231 4
		textlen = ((TypText far *) ((char far *) (LinePtr) +
									sizeof (TypLine)))->NameLen;
		textptr = (char far *) ((char far *) (LinePtr) +
								sizeof (TypLine) + sizeof (TypText));
a1236 1

d1240 3
a1242 4
	  /* Copy the last line selected                                  */
	  textlen = ((TypText far *) ((char far *) (LinePtr) +
								  sizeof (TypLine)))->NameLen;
	  textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));
a1256 1

d1284 1
d1286 1
d1290 1
d1317 2
a1318 2
   SetCapsLockText(hWnd);
   SetNumLockText(hWnd);
d1320 1
d1328 1
a1371 1

d1390 2
a1392 2

	/* Clear document                                        */
d1510 8
d1532 2
a1533 2
									 0,		/* Initial X position */
									 TOOLBARHEIGHT,		/* Initial Y position */
a1569 1
		/* Should the 0 be sizeof(TypText) ? */
a1585 1

a1589 1

d1592 1
a1592 1
	case IDM_CANCELART:
d1594 3
a1596 2
	    // Extra checks to make sure we really are allowed to Cancel this Article	
		GetHeaderLine (ThisDoc, "Organization:", headerLine, MAXHEADERLINE);
d1598 14
a1611 8
          GetOrganization (mybuf, MAXHEADERLINE, ThisDoc);
          if (stricmp(mybuf,headerLine+14) != 0)    /* 14 = size of "Organization " */		     
			 MessageBox (hWnd, "Sorry..Only the original author is allowed \n"
			            "to cancel an article.","Cancel Not Allowed", MB_OK | MB_ICONSTOP);
		  else
		    {
			CreateComposeWnd (hWnd, ThisDoc, DOCTYPE_CANCEL);
			}
d1613 7
d1660 1
a1660 2
	  textlen = ((TypText far *) ((char far *) (LinePtr) +
								  sizeof (TypLine)))->NameLen;
d1697 1
a1697 2
		GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

d1787 3
d1801 5
a1822 1

d1860 17
d1888 173
@


1.45
log
@fix GPF when marking text in extreemly large articles.  Fixed
copying of ROT13 encoded text to clipboard
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.44 1995/03/16 17:13:04 dumoulin Exp $
d1332 1
d1631 18
@


1.44
log
@fix part of the cut/past GPF on huge articles
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.43 1995/01/19 02:47:25 jglasser Exp $
a177 1
//        MessageBox (hWnd, "Please wait until article retrieval is complete before selecting text.", "Please Wait", MB_OK);
d1032 8
a1039 57

  FindLineOrd (DocPtr, (unsigned) Start->LineNum, &BlockPtr, &LinePtr);
  textlen = ((TypText far *) ((char far *) (LinePtr) +
							  sizeof (TypLine)))->NameLen;
  textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));

  if (ItalicizeQuotes && isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
	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 - (int) DocPtr->TopLineOrd) * (int) 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);
	}

//     ReleaseDC(DocPtr->hDocWnd, hDC);
  }
  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;
d1041 1
a1041 6
	while (CurrentLineNum < End->LineNum) {
	  NextLine (&BlockPtr, &LinePtr);
	  textlen = ((TypText far *) ((char far *) (LinePtr) +
								  sizeof (TypLine)))->NameLen;
	  textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));
	  if (ItalicizeQuotes && isLineQuotation (textptr)) {	/* prepare to print a quotation Line */
d1043 2
a1044 2
	  }
	  else {					/* prepare to print a normal line */
d1046 23
a1068 3
	  }
	  GetTextMetrics (hDC, (LPTEXTMETRIC) & tmFontInfo);
	  charWidth = tmFontInfo.tmAveCharWidth;
d1070 17
a1086 5
	  Rect.top = (int) ArtTopSpace + ((CurrentLineNum - (int) DocPtr->TopLineOrd) * (int) 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);
a1087 10
	  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);
	  }
d1089 36
a1124 2
	  CurrentLineNum++;
	  SelectObject (hDC, hOldFont);
d1126 2
a1127 1
	}
d1129 1
a1129 12
	NextLine (&BlockPtr, &LinePtr);
	textlen = ((TypText far *) ((char far *) (LinePtr) +
								sizeof (TypLine)))->NameLen;
	textptr = (char far *) ((char far *) (LinePtr) + sizeof (TypLine) + sizeof (TypText));
	if (ItalicizeQuotes && isLineQuotation (textptr)) {		/* prepare to print a quotation Line */
	  hOldFont = SelectObject (hDC, hFontArtQuote);
	}
	else {						/* prepare to print a normal line */
	  hOldFont = SelectObject (hDC, hFontArtNormal);
	}
	GetTextMetrics (hDC, (LPTEXTMETRIC) & tmFontInfo);
	charWidth = tmFontInfo.tmAveCharWidth;
d1131 29
a1159 20
	Rect.top = (int) ArtTopSpace + ((CurrentLineNum - (int) DocPtr->TopLineOrd) * (int) 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);

  }
d1161 4
d1180 1
d1258 1
a1258 1
		*GlobalMemoryPtr++ = *textptr++;
d1264 1
a1264 1
		*GlobalMemoryPtr++ = *textptr++;
d1278 1
a1278 1
		  *GlobalMemoryPtr++ = *textptr++;
d1292 1
a1292 1
		  *GlobalMemoryPtr++ = *textptr++;
@


1.43
log
@Jody Glasser's changes of 950113
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.42 1994/12/12 19:39:13 jcooper Exp $
d92 2
a93 1
  int found, charnum;
d174 1
a174 1
	  int TempX, TempY;
d194 1
a194 1
		  TempX = ArtSideSpace;
d197 1
a197 1
		  TempX = min (X, (int) ThisDoc->ScXWidth);
d204 3
a206 3
		  TempY = min (Y, (int) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
		  if (TempY > (int) (ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
			TempY = (int) ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * (int) ArtLineHeight;
d210 2
a211 2
		ThisDoc->BeginSelect.LineNum = (TempY - ArtTopSpace) / ArtLineHeight + ThisDoc->TopLineOrd;
		ThisDoc->BeginSelect.CharNum = cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d223 1
a223 1
	  int TempX, TempY;
d254 3
a256 3
			  TempY = min (Y, (int) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
			  if (TempY > (int) (ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
				TempY = (int) ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * (int) ArtLineHeight;
d263 2
a264 2
			ThisDoc->EndSelect.LineNum = (TempY - (int) ArtTopSpace) / (int) ArtLineHeight + ThisDoc->TopLineOrd;
			ThisDoc->EndSelect.CharNum = cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d288 3
a290 3
			  TempY = min (Y, (int) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
			  if (TempY > (int) (ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
				TempY = (int) ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * (int) ArtLineHeight;
d297 2
a298 2
			ThisDoc->EndSelect.LineNum = (TempY - (int) ArtTopSpace) / (int) ArtLineHeight + ThisDoc->TopLineOrd;
			ThisDoc->EndSelect.CharNum = cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d328 2
a329 2
			ThisDoc->EndSelect.LineNum = (TempY - (int) ArtTopSpace) / (int) ArtLineHeight + ThisDoc->TopLineOrd;
			ThisDoc->EndSelect.CharNum = cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d349 3
a351 3
			TempY = ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight;
			if (TempY > (int) (ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
			  TempY = (int) ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * (int) ArtLineHeight;
d364 2
a365 2
			ThisDoc->EndSelect.LineNum = (TempY - (int) ArtTopSpace) / (int) ArtLineHeight + ThisDoc->TopLineOrd;
			ThisDoc->EndSelect.CharNum = cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d388 3
a390 3
			TempY = min (Y, (int) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
			if (TempY > (int) (ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * ArtLineHeight)) {
			  TempY = (int) ArtTopSpace + ((int) ThisDoc->TotalLines - (int) ThisDoc->TopLineOrd - 1) * (int) ArtLineHeight;
d396 2
a397 2
		  ThisDoc->EndSelect.LineNum = (TempY - (int) ArtTopSpace) / (int) ArtLineHeight + ThisDoc->TopLineOrd;
		  ThisDoc->EndSelect.CharNum = cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d424 1
a424 1
	  int TempX, TempY;
d442 1
a442 1
			TempY = min (Y, (int) (ArtTopSpace + (ThisDoc->ScYLines - 1) * ArtLineHeight));
d445 2
a446 2
		  ThisDoc->EndSelect.LineNum = (TempY - (int) ArtTopSpace) / (int) ArtLineHeight + ThisDoc->TopLineOrd;
		  ThisDoc->EndSelect.CharNum = cursor_to_char_number (TempX, TempY, ThisDoc, &BlockPtr, &LinePtr);
d459 1
a459 1
	charnum = cursor_to_char_number (X, Y, ThisDoc, &BlockPtr, &LinePtr);
d594 1
a594 1
		  if (ROT13Mode && ((unsigned) lineNum > ThisDoc->HeaderLines)) {	/*  within body of artcile take copy of string to
@


1.42
log
@0.9.99 update
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.41 1994/11/30 22:22:08 jcooper Exp $
d1355 2
@


1.41
log
@93.9 changes
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.40 1994/11/28 22:01:06 jcooper Exp $
d749 1
a749 1
  if (CommBusy && ThisDoc == CommDoc)
d753 1
a753 3
  else {
	SetHandleBkBrush (ThisDoc->hDocWnd, GetStockObject (WHITE_BRUSH));
	DestroyWindow (hWnd);
d755 3
d1425 1
a1425 12
	/* 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.                  */

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

	  GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
a1426 8
	  header_handle = GroupDoc->header_handle;
	  thread_handle = GroupDoc->thread_handle;
	  headers = lock_headers (header_handle, thread_handle);

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

d1749 2
a1750 2
			  ThisDoc->ParentDoc->AnchorLineID = ThisDoc->ParentDoc->ActiveLineID;
			  SetGroupSelections (ThisDoc->ParentDoc, headers, ThisDoc->ParentDoc->ActiveLineID + 1);
d1759 2
a1760 2
			  ThisDoc->ParentDoc->AnchorLineID = ThisDoc->ParentDoc->ActiveLineID;
			  SetGroupSelections (ThisDoc->ParentDoc, headers, ThisDoc->ParentDoc->ActiveLineID - 1);
d1771 3
a1773 3
		  MessageBox (hWnd, "Sorry--you must have the group window around\n"
					  "\for me to be able to find the next article",
					  "Can't find next article", MB_OK);
d1780 3
a1782 3
		MessageBox (hWnd,
					"That Group's window is gone.  Reopen it.",
					"Error", MB_OK);
@


1.40
log
@93.8 update from jcooper
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.39 1994/11/21 21:20:46 jcooper Exp $
d824 1
a824 1
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_SAVE, mode);
d1512 1
a1512 1
	tbButton[j].idCommand = IDM_SAVE;
@


1.39
log
@93.7 update
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.38 1994/11/11 01:39:21 jglasser Exp $
a167 1

d485 1
d1368 1
d1371 10
a1380 10
	EnableMenuItem (hSubMenu, IDM_NEXT_ARTICLE, CommBusy ? DISABLE_MENU : ENABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_FIND_NEXT_UNSEEN, CommBusy ? DISABLE_MENU : ENABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_NEXT_SAME_SUBJ, CommBusy ? DISABLE_MENU : ENABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_PREV_ARTICLE, CommBusy ? DISABLE_MENU : ENABLE_MENU);

	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_DECODE_ARTICLE, i);
	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_NEXT_ARTICLE, CommBusy ? FALSE : TRUE);
	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_FIND_NEXT_UNSEEN, CommBusy ? FALSE : TRUE);
	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_NEXT_SAME_SUBJ, CommBusy ? FALSE : TRUE);
	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_PREV_ARTICLE, CommBusy ? FALSE : TRUE);
d1452 1
d1698 1
d1816 1
@


1.38
log
@toolbar fixes
@
text
@d3 1
a3 1
 * $Id: wvart.c 1.37 1994/11/10 01:56:22 rushing Exp $
d92 2
a93 1
  int found, X, Y, charnum;
d114 1
a114 1
  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
a222 1
	  _int16 X, Y;
a544 2
	  LockLine (ThisDoc->hCurTopScBlock, ThisDoc->TopScOffset, ThisDoc->TopScLineID,
				&BlockPtr, &LinePtr);
d548 13
d564 7
a570 5
	  RangeHigh = ThisDoc->TotalLines - VertLines;
	  if (RangeHigh < 0)
		RangeHigh = 0;
	  SetScrollRange (hWnd, SB_VERT, 0, RangeHigh, FALSE);
	  SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
d831 1
a831 1
  SendMessage (DocPtr->hWndFrame, WM_INITMENU, (WPARAM) 0, (LPARAM) 0);
d966 2
d1043 2
d1048 1
a1048 1
  Rect.left = extent.cx - ((int) DocPtr->ScXOffset * (ArtCharWidth + 1) - ArtSideSpace);
d1053 1
a1053 1
	Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (ArtCharWidth + 1) - ArtSideSpace);
d1071 1
a1071 1
	Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (ArtCharWidth + 1) - ArtSideSpace);
d1100 2
d1104 1
a1104 1
	  Rect.left = ArtSideSpace - (int) DocPtr->ScXOffset * (ArtCharWidth + 1);
d1107 1
a1107 1
	  Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (ArtCharWidth + 1) - ArtSideSpace);
d1135 2
d1139 1
a1139 1
	Rect.left = ArtSideSpace - (int) DocPtr->ScXOffset * (ArtCharWidth + 1);
d1142 1
a1142 1
	Rect.right = extent.cx - ((int) DocPtr->ScXOffset * (ArtCharWidth + 1) - ArtSideSpace);
d1348 2
a1349 1
  StatusBarProc (hWnd, message, wParam, lParam, ThisDoc);
d1362 1
a1381 1

@


1.37
log
@restart
@
text
@d113 2
a114 1
  StatusBarProc (hWnd, message, wParam, lParam, ThisDoc);
@
