head	1.86;
access;
symbols
	V0-99-8:1.78
	V0-99-7:1.77
	V0-99-6:1.73
	V0-99-5:1.73
	V0-99-4:1.71
	V0-99-2:1.66
	V0-99-1:1.65
	V0-93-14:1.65
	V0-93-13:1.65
	V0-93-12:1.65
	V0-93-11:1.60
	V0-93-10:1.59
	V0-93-9:1.58
	V0-93-8:1.57
	V0-93-7:1.55
	V0-93-5:1.54
	V80:1.15
	V76d:1.1;
locks; strict;
comment	@ * @;


1.86
date	97.03.18.19.24.33;	author dumoulin;	state Exp;
branches;
next	1.85;

1.85
date	97.03.07.02.16.32;	author dumoulin;	state Exp;
branches;
next	1.84;

1.84
date	97.02.28.19.24.12;	author dumoulin;	state Exp;
branches;
next	1.83;

1.83
date	97.02.27.23.38.01;	author dumoulin;	state Exp;
branches;
next	1.82;

1.82
date	97.02.14.20.12.31;	author dumoulin;	state Exp;
branches;
next	1.81;

1.81
date	97.02.08.11.15.56;	author dumoulin;	state Exp;
branches;
next	1.80;

1.80
date	97.02.08.10.57.39;	author dumoulin;	state Exp;
branches;
next	1.79;

1.79
date	97.02.06.21.22.10;	author dumoulin;	state Exp;
branches;
next	1.78;

1.78
date	96.08.13.04.45.48;	author tanaka;	state Exp;
branches;
next	1.77;

1.77
date	95.11.10.06.45.49;	author dumoulin;	state Exp;
branches;
next	1.76;

1.76
date	95.11.07.23.02.35;	author dumoulin;	state Exp;
branches;
next	1.75;

1.75
date	95.08.25.01.41.23;	author dumoulin;	state Exp;
branches;
next	1.74;

1.74
date	95.08.23.23.35.18;	author dumoulin;	state Exp;
branches;
next	1.73;

1.73
date	95.06.06.05.59.55;	author dumoulin;	state Exp;
branches;
next	1.72;

1.72
date	95.06.06.04.07.56;	author dumoulin;	state Exp;
branches;
next	1.71;

1.71
date	95.05.19.22.56.32;	author dumoulin;	state Exp;
branches;
next	1.70;

1.70
date	95.04.21.21.47.11;	author dumoulin;	state Exp;
branches;
next	1.69;

1.69
date	95.04.18.19.07.36;	author dumoulin;	state Exp;
branches;
next	1.68;

1.68
date	95.04.14.16.44.43;	author dumoulin;	state Exp;
branches;
next	1.67;

1.67
date	95.04.12.20.53.36;	author dumoulin;	state Exp;
branches;
next	1.66;

1.66
date	95.03.29.00.35.03;	author dumoulin;	state Exp;
branches;
next	1.65;

1.65
date	95.01.19.02.47.25;	author jglasser;	state Exp;
branches;
next	1.64;

1.64
date	95.01.19.02.35.45;	author rushing;	state Exp;
branches;
next	1.63;

1.63
date	95.01.17.19.08.04;	author rushing;	state Exp;
branches;
next	1.62;

1.62
date	95.01.03.20.37.22;	author brydon;	state Exp;
branches;
next	1.61;

1.61
date	95.01.03.18.57.42;	author jcooper;	state Exp;
branches;
next	1.60;

1.60
date	94.12.21.18.38.00;	author jcooper;	state Exp;
branches;
next	1.59;

1.59
date	94.12.12.19.39.13;	author jcooper;	state Exp;
branches;
next	1.58;

1.58
date	94.11.30.22.22.08;	author jcooper;	state Exp;
branches;
next	1.57;

1.57
date	94.11.28.22.01.06;	author jcooper;	state Exp;
branches;
next	1.56;

1.56
date	94.11.24.00.20.03;	author rushing;	state Exp;
branches;
next	1.55;

1.55
date	94.11.21.21.20.46;	author jcooper;	state Exp;
branches;
next	1.54;

1.54
date	94.11.11.01.40.08;	author jglasser;	state Exp;
branches;
next	1.53;

1.53
date	94.11.10.23.00.19;	author jcooper;	state Exp;
branches;
next	1.52;

1.52
date	94.11.10.01.27.31;	author rushing;	state Exp;
branches;
next	;


desc
@winvn version 0.76 placed into RCS
@


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


/*
 * $Id: wvgroup.cpp 1.85 1997/03/07 02:16:32 dumoulin Exp $
 *
 */

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

#include <limits.h>
#ifdef COMMCTRL
#include <commctrl.h>
#else
extern "C"
{
#include "wvtb\wvtb.h"          /* for toolbar */
}
#endif
#include "wvclass.h"
#include <stdlib.h>
#include <assert.h>

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

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

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

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

long FAR PASCAL
WinVnViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

  PAINTSTRUCT ps;               /* paint structure          */

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

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

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

  if (!found) {
    ThisDoc = CommDoc;
  }

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

  switch (message) {
  case WM_DESTROY:
    break;

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

  case WM_SIZE:
    GetClientRect (hWnd, &clientRect);
    ThisDoc->ScXWidth = clientRect.right;
    ThisDoc->ScYHeight = clientRect.bottom;
    ThisDoc->ScYLines = (RectHeight (clientRect) - TopSpace) / LineHeight;
    ThisDoc->ScXChars = (RectWidth (clientRect) - SideSpace) / CharWidth;
    break;

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

  case WM_KEYDOWN:
    /* See if this key should be mapped to a scrolling event
     * for which we have programmed the mouse.  If so,
     * construct the appropriate mouse call and call the mouse code.
     */
    LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
              ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
    group = GetGroup(LinePtr);

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

    switch (wParam) {
    case VK_F5: // This never gets touched if F5 accelerator is defined
        artindex = find_first_selected_art(ThisDoc);
        ThisDoc->ActiveLineID = artindex;
        AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
        goto getarticle1;

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

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

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

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

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

    case VK_NEXT: 
      if (headers != NULL) {
        if (ThisDoc->ActiveLineID >= 0 &&
            ThisDoc->ActiveLineID < group->total_headers - 1) {
          stop = min (ThisDoc->ActiveLineID + (long) ThisDoc->ScYLines,
                      group->total_headers - 1);
          for (il = ThisDoc->ActiveLineID + 1; il < stop; il++);
          SetGroupSelections (ThisDoc, headers, il);
          AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
        }
      }
      break;

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

    case VK_ESCAPE: //ishido
      if (DragMouseAction == DRAG_ON) {
        DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
        ReleaseCapture ();
        DragMouseAction = DRAG_NONE;
        WidthCursor = FALSE;
        SetCursor (hArrowCursor);
        InvalidateRect(hWnd, NULL, FALSE);
      }
      break;

    default:
      CtrlState = GetKeyState (VK_CONTROL) < 0;
      for (j = 0; j < NUMKEYS; j++) {
        if (wParam == key2scroll[j].wVirtKey &&
            CtrlState == key2scroll[j].CtlState) {
          SendMessage (hWnd, key2scroll[j].iMessage,
                       key2scroll[j].wRequest, 0L);
          break;
        }
      }

    }

    break;


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

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

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

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

    break;

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

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

      }
    }
    break;

  case WM_LBUTTONDOWN:
    /*  Clicking the left button on an article name toggles the
     *  selected/not selected status of that group.
     *  Currently selected groups are displayed in reverse video.
     */

    DragMouseAction = DRAG_NONE;
//shimomai & ishido :-)
    if (WidthCursor) {
      orgPos = LOWORD (lParam);
      DragCursorPosition = orgPos;
      DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
        SetCapture (hWnd);      // release capture on button up
      DragMouseAction = DRAG_ON;
    }
    else if (!CommBusy || (CommDoc != ThisDoc || CommDecoding)) {
      BOOL status;
      X = LOWORD (lParam);
      Y = HIWORD (lParam);

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

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

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

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

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

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

  case WM_LBUTTONDBLCLK:
    /*  Double-clicking on an article subject creates an "Article"
     *  window, whose purpose is to display the article.
     */
    if (TestCommBusy (hWnd, "Can't Retrieve Article"))
      break;

    X = LOWORD (lParam);
    Y = HIWORD (lParam);
// getarticle:;

    artindex = NewCursorToTextLine (X, Y, ThisDoc);

  getarticle1:;

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

    break;

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

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

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

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

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

      default:

      artindex = NewCursorToTextLine (X, Y, ThisDoc);
      if (artindex > -1) {
        if (ArtListMultiSelect &&
            DragMouseAction != SEEN_SELECT && DragMouseAction != SEEN_DESELECT) {
          ThisDoc->ActiveLineID = artindex;
          AffectSelectedOnScreen (ThisDoc, DESELECT, EXTEND);
        }
        else {
          switch (DragMouseAction) {
          case DRAG_SELECT:
            article_operation (ThisDoc, artindex, selected_true);
            InvalidateRect (hWnd, NULL, FALSE);
            break;

          case DRAG_DESELECT:
            article_operation (ThisDoc, artindex, selected_false);
            InvalidateRect (hWnd, NULL, FALSE);
            break;

          case SEEN_SELECT:
            article_operation (ThisDoc, artindex, seen_true);
            InvalidateRect (hWnd, NULL, FALSE);
            break;

          case SEEN_DESELECT:
            article_operation (ThisDoc, artindex, seen_false);
            InvalidateRect (hWnd, NULL, FALSE);
            break;
          }

        }
      }
    }
    }
    break;

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

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

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

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

      hDC = BeginPaint (hWnd, &ps);
      GetClientRect (hWnd, &clientRect);
      hOldFont = SelectObject (hDC, hListFont);

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

      HorzChars = ThisDoc->ScXChars;

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

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

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

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

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

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

        else
          MyColors[2] = ArticleUnSeenColor;

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

        else
          MyColors[3] = ArticleSeenColor;

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

      SetTextColor (hDC, MyColors[MyColorMask]);
      SetBkColor (hDC, MyBack[MyColorMask]);

      /* Update the scroll bar thumb position.  */
      RangeHigh = ThisDoc->TotalLines - ThisDoc->ScYLines;
      if (RangeHigh < 0) {
        RangeHigh = 0;
        ThisDoc->TopLineOrd = 0;
        VertLines = ThisDoc->TotalLines;
      }
      CurPos = ThisDoc->TopLineOrd;
      if (CurPos < 0)
        CurPos = 0;

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

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

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

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

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

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

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

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

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


          if (MyColorMask != PrevColorMask) {
            SetTextColor (hDC, MyColors[MyColorMask]);
            SetBkColor (hDC, MyBack[MyColorMask]);
            PrevColorMask = MyColorMask;
          }

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

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

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

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

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

		  /* out subject */
          SetRect (&aRect, X+ArtListSeparator3, Y, clientRect.right, Y + LineHeight);
          SetTextAlign (hDC, TA_TOP | TA_LEFT);
          ptr = (ThreadFullSubject || !(header->thread_depth))
            ? (const char*)header->pHD->sSubject : ThreadDepthIndicator;
          ExtTextOut (hDC, X+ArtListSeparator3+ CharWidth * (1 + header->thread_depth * 2), Y,
                    ETO_OPAQUE | ETO_CLIPPED, &aRect,
                    (const char*)ptr, strlen(ptr), (LPINT) NULL);
          SetRect (&aRect, 0, Y, clientRect.right, Y + LineHeight);

          if (ThisDoc->ActiveLineID == -1L)
            ThisDoc->ActiveLineID = artindex;
          if (ThisDoc->ActiveLineID == artindex && ArtListMultiSelect) {
            DrawFocusRect (hDC, &aRect);
          }

          Y += LineHeight;
          artindex++;

        }
        while (--VertLines > 0);

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

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

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

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

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

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

  if (stop)
    MessageBox (hWnd,
                "Please wait until group activity is complete",
                "Cannot close group window", MB_OK | MB_ICONSTOP);
  else {
    SetHandleBkBrush (ThisDoc->hDocWnd, (HBRUSH) GetStockObject (WHITE_BRUSH));
    if (SaveNewsrcOnClose && NewsrcDirty) WriteNewsrc ();
    DestroyWindow (hWnd);
    /* invalidate main window, so '*' will be updated, if appropriate */
    InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
  }
}

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

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

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

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

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

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

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

  // mail menu ignores this enable flag - it depends solely
  // on transport availability
  SetGroupMailMenu (DocPtr);

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

  EnableMenuItem (hSubMenu, IDM_SORT_DATE, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_SUBJECT, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_LINES, mode);
//  EnableMenuItem (hSubMenu, IDM_SORT_THREADS, mode);  // handled in INITMENU
  EnableMenuItem (hSubMenu, IDM_SORT_ARTNUM, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_FROM, mode);

  hSubMenu = GetSubMenu (hMenu, 2);     // Search menu

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

  // set toolbar buttons
  mode = (enable == ENABLE) ? TRUE : FALSE;
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_FIND_NEXT_SAME, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_POST, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_MARK_ALL, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDV_EXIT, mode);

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

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

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

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

  EnableMenuItem (hSubMenu, IDM_SAVE_SELECTED, mode);
  EnableMenuItem (hSubMenu, IDM_DECODE_SELECTED, mode);
//  EnableMenuItem (hSubMenu, IDM_READ_SELECTED, mode);
  EnableMenuItem (hSubMenu, IDV_EXIT, mode);
  EnableMenuItem (hSubMenu, IDM_MARK_ALL, mode);

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

  EnableMenuItem (hSubMenu, IDM_SORT_DATE, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_SUBJECT, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_LINES, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_THREADS, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_THREADSUB, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_ARTNUM, mode);
  EnableMenuItem (hSubMenu, IDM_SORT_FROM, mode);
  // set toolbar buttons
  if (enable == ENABLE)
    mode = TRUE;
  else
    mode = FALSE;
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_SAVE_SELECTED, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_DECODE_SELECTED, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDV_EXIT, mode);
  SendMessage (DocPtr->hWndTB, TB_ENABLEBUTTON, IDM_MARK_ALL, mode);
}

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

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

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

    artindex = -1;

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

    header_handle = GroupDoc->header_handle;
    thread_handle = GroupDoc->thread_handle;
    headers = lock_headers (header_handle, thread_handle);
    header = header_elt (headers, artindex); 
             
    if ((articleId == NO_ID) &&
        (header->ArtDoc != NULL) &&
        (header->ArtDoc->CountedLines == header->lines)){ 
      
        /* We already have a document containing the article */
        /* so just activate it.                */

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

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

    if (TestCommBusy (hWndGroup, "Can't Retrieve Article"))
        goto endit;

    newdoc = FALSE;

    if ((NewArticleWindow && !reuse) || !ActiveArticleDoc || 
                !(ActiveArticleDoc->InUse)) {
        found = FALSE;
        for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++) {
        if (!ArticleDocs[docnum].InUse) {
            found = TRUE;
            newdoc = TRUE;
            CommDoc = &(ArticleDocs[docnum]);
            break;
        }
        }
        if (!found) {
        MessageBox (hWndGroup,
                      "You have too many article windows active;\n"
                      "Close one or uncheck the option"
                     "'New Window for each Article'.",
                     "Can't open new window", MB_OK | MB_ICONASTERISK);
        goto endit;
        }
    }
    else {
        /* Must reuse old window for this article.         */
        ActiveArticleDoc->LongestLine = 0;
        ActiveArticleDoc->ScXOffset = 0;
        ActiveArticleDoc->TextSelected = FALSE;
        SetArticleMenus (ActiveArticleDoc, DISABLE);
        CommDoc = ActiveArticleDoc;
        /* sever the article/artindex connection */
        if (CommDoc->ParentDoc == Doc)
            (header_elt (headers, CommDoc->ParentOffset))->ArtDoc = (TypDoc far *) NULL;
        else {
            SeverArticleParent(CommDoc);
        }

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

    header->SeenSelected |= ART_SEEN;
    header->SeenSelected &= ~ART_SELECTED;
    NewsrcDirty = TRUE;
    InvalidateRect (hWndGroup, NULL, FALSE);

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

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

  if (newdoc) {
    char poschars[MAXINTERNALLINE];

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

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

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

    if (!hWndArt)
      return;                   /* ??? */
  }
  else {
    hWndArt = CommDoc->hWndFrame;
    SetWindowText (CommDoc->hWndFrame, (const char*) sTitle);
  }

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

  InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  /* If we're not already in this group on the server,
   * send out a GROUP command for this window so we get back
   * into the right Group.
   */
    lpszGroupName = GetGroupName(LinePtr);
    if (lstrcmp (CurrentGroup, lpszGroupName)) {
      CommState = ST_GROUP_REJOIN;
      strcpy (mybuf, "GROUP ");
      lstrcat (mybuf, lpszGroupName);
      mylstrncpy (CurrentGroup, lpszGroupName, MAXGROUPNAME);
      PutCommLine (mybuf);
    }

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

    sprintf (mybuf, "ARTICLE %s", articleId);
    PutCommLine (mybuf);
  }
}


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

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

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

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

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

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

/*--- function UpdateSeenArts -------------------------------------------
 *
 *  Given a Group document, update the TypGroup line for
 *  that document in the Net document with respect to which
 *  articles have been seen.
 *  This routine would typically be called just before a Group document
 *  is going to be destroyed or erased.  That would be the time to
 *  take the information in the TypArticle structures of each line
 *  in the document and transfer it to the line in the NetDoc document
 *  corresponding to this group.
 *
 *  This routine has to take information of the form:
 *    123:Unseen;  124:Seen; 125:Unseen; 126:Unseen; 127:Seen; 128:Seen; 129:Seen
 *  found in the TypArticle structures in consecutive lines in the document
 *  and transform it to the general form used by .newsrc files:
 *    124,127-129
 *  (though we are using our internal representation & not ASCII characters).
 *
 *    Entry    Doc      points to the document for this group.
 *
 *    Exit     The line in the Net document corresponding to this
 *              group has been updated.
 */
void
UpdateSeenArts (TypDoc far *Doc)
{
  TypGroup *group;
  TypLine far *ParentLine;
  TypBlock far *ParentBlock;
  HANDLE header_handle, thread_handle;
  header_p headers;
  header_p header;
  BOOL GotLock;
  unsigned long artindex;
  
  /*  Get the line in the Net document that corresponds to this
   *  group.  Make a local copy of it and set RangePtr to point to
   *  the first range in that line.  We will ignore the old line's
   *  "seen" data and create the information afresh from what we
   *  have in this document.
   */
  GotLock = LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
    &ParentBlock, &ParentLine);
  
  if(!GotLock)
    return;
  
  group = GetGroup(ParentLine);
  
  if (group->total_headers > 0)
  {
    header_handle = group->header_handle;
    thread_handle = group->thread_handle;
    
    if (!header_handle) // this can happen if user selects "Update from server" then cancel.
      return;
    
    set_index_to_identity (header_handle, thread_handle, group->total_headers);
    headers = lock_headers (header_handle, thread_handle);
    
    artindex = 0;
    header = (header_elt (headers, artindex));
    
    do
    {
      header_p prev_header = header;
      
      header = (header_elt (headers, artindex));
      if(ART_SEENORKILLED & header->SeenSelected)
      {
        group->pSeendb->MarkSeen(header->number);
      }
      else
      {
        group->pSeendb->MarkUnseen(header->number);
      }
    } while(++artindex < Doc->TotalLines);
    unlock_headers (header_handle, thread_handle);
  }
  group->NumUnread = CalcNumUnread(group);
  
  GlobalUnlock (ParentBlock->hCurBlock);
  NewsrcDirty = TRUE;
}



/*-- function CursorToTextLine ----------------------------------------
 *
 *   Routine to locate a text line in a document, based on the
 *   cursor position.  Used to figure out which line is being selected
 *   when a user clicks a mouse button.
 *
 *   Entry    X, Y    are the position of the cursor.
 *            DocPtr  points to the current document.
 *
 *   Exit     *LinePtr points to the current line, if one was found.
 *            *BlockPtr points to the current block, if found.
 *            Function returns TRUE iff a line was found that corresponds
 *              to the cursor position.
 */
long
NewCursorToTextLine (int X, int Y, TypDoc far *DocPtr)
{
  int SelLine;
  long result;

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

long
search_headers (TypDoc far * HeaderDoc, header_p headers,
                long artindex, long num_headers)
{
  char_p temp;
  
  do {
    temp = (char*)(const char*)(header_elt (headers, artindex))->pHD->sSubject;
    if (string_compare_insensitive (temp, HeaderDoc->SearchStr))
      return (artindex);        /* return the index */
  } while (artindex++ < (num_headers - 1));

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

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

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

  return (NULL);
}


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

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

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

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

    return TRUE;
    break;
  }
  return FALSE;
}

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

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

  for (index = 0, num_affected = 0; index < group->total_headers; index++) {
    ThisHeader = header_elt (headers, index);
    if (AffectOneSelected (Doc, ThisHeader, index, value, compare)) {
      num_affected++;
    }
  }

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

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

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

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

  for (index = Doc->TopLineOrd, num_affected = 0; VertLines > 0; index++, VertLines--) {
    ThisHeader = header_elt (headers, index);
    if (AffectOneSelected (Doc, ThisHeader, index, value, compare)) {
      num_affected++;
    }
  }
  InvalidateRect (Doc->hDocWnd, NULL, FALSE);
  return (num_affected);
}

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

  MyDoc->ActiveLineID = newActive;

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

long FAR PASCAL
WinVnViewFrameWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  TypDoc far *ThisDoc;
  int ih, i, j;
  BOOL found;
  TBBUTTON tbButton[20];
  RECT MyRect;
  TypGroup *GroupDoc;
  TypBlock far *BlockPtr;
  HANDLE hBlock;
  TypLine far *LinePtr;
  unsigned int Offset;
  TypLineID MyLineID;
  long found_artindex;
  BOOL continueFind, threadOk;
  char mybuf[MAXINTERNALLINE];
  HMENU hMenu, hSubMenu;
  PAINTSTRUCT ps;               /* paint structure          */
  HDC hDC;                      /* handle to display context */

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

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

  if (!found)
    ThisDoc = CommDoc;

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

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

  case WM_CLOSE:
    CloseGroupWnd (hWnd, ThisDoc);
    return (0);
    break;

  case WM_DESTROY:
    /* Unlink all the article windows that belong to this group */
    NumGroupWnds--;

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

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

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

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

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

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

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

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

// add common controls for toolbar (jlg)
    i = j = 0;
    tbButton[j].iBitmap = 0;    // find
    tbButton[j].idCommand = IDM_FIND;
    tbButton[j].fsState = TBSTATE_ENABLED;
    tbButton[j].fsStyle = TBSTYLE_BUTTON;
#ifdef COMMCTRL
    tbButton[j].dwData = 0;
    tbButton[j].iString = 0;
#endif

    tbButton[++j].iBitmap = ++i;    // find next

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

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

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

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

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

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

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

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

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

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

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

    tbButton[++j].iBitmap = ++i;    // Decode selected

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

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

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

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

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

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

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

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

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

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

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

    ThisDoc->hWndFrame = hWnd;
#ifdef COMMCTRL
     ThisDoc->hDocWnd = CreateWindowEx (WS_EX_CLIENTEDGE,
                                      "WinVnView",
                                      NULL,
                                      WS_CHILD | WS_VSCROLL | WS_HSCROLL,
                                      0,                 /* Initial X position */
                                      TOOLBARHEIGHT,     /* Initial Y position */
                                      RectWidth (MyRect),
              RectHeight (MyRect) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
                                      hWnd,
                                      NULL,
                                      hInst,
                                      NULL);
#else
    ThisDoc->hDocWnd = CreateWindow ("WinVnView",
                                     NULL,
                                     WS_CHILD | WS_VSCROLL | WS_HSCROLL,
                                     0,                 /* Initial X position */
                                     TOOLBARHEIGHT,     /* Initial Y position */
                                     RectWidth (MyRect),
             RectHeight (MyRect) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
                                     hWnd,
                                     NULL,
                                     hInst,
                                     NULL);
#endif
    SetHandleBkBrush (ThisDoc->hDocWnd, hListBackgroundBrush);
    ShowWindow (ThisDoc->hDocWnd, SW_SHOWNORMAL);

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

  case WM_ACTIVATE:
    if((wParam == WA_ACTIVE) || (wParam == WA_CLICKACTIVE))
      ActiveGroupDoc = ThisDoc;

    /* fall through to update toolbars */
  case WM_MYINITMENU:
  case WM_INITMENU:
    hMenu = GetMenu (hWnd);
    hSubMenu = GetSubMenu (hMenu, 0);
    i = (!CommBusy && ThisDoc->SelectedLines);

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

    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_SAVE_SELECTED, i);
    SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_DECODE_SELECTED, (i && CodingState == INACTIVE));

    /* only allow sort by threads if the group has threads.  it's not enough
     * to check threadp - since the user may have changed the threadp
     * option after retrieving this group article-list 
     */
    threadOk = FALSE;
    /*
     * we check hParentBlock because this code can be called before the Document
     * has been initialized via InitDoc
     */
    if (ThisDoc->hParentBlock && (!CommBusy || CommDoc != ThisDoc)) {
      LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
                ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
      if (LinePtr) {
        GroupDoc = GetGroup(LinePtr);
        threadOk = GroupDoc->Threaded;
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
      }
    }
    SendMessage(hWnd, WM_AA_ENABLE_DISABLE, 0, 0); // conditionally enable Article Actions
    hSubMenu = GetSubMenu (hMenu, 1);   /* sorting menu */
    EnableMenuItem (hSubMenu, IDM_SORT_THREADS, threadOk ? ENABLE_MENU : DISABLE_MENU);
    EnableMenuItem (hSubMenu, IDM_SORT_THREADSUB, threadOk ? ENABLE_MENU : DISABLE_MENU);

    if(message == WM_INITMENU)
    {
      CheckMenuItem(hMenu, IDM_SORT_DATE,      MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_SUBJECT,   MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_LINES,     MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_THREADS,   MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_THREADSUB, MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_FROM,      MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_ARTNUM,    MF_BYCOMMAND|MF_UNCHECKED);

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

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

  case WM_SETFOCUS:
    StatBarGroupPopups (ThisDoc);
    SetCapsLockText(hWnd);
    SetNumLockText(hWnd);
    break;

  case WM_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,
                RectWidth (MyRect),
             RectHeight (MyRect) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
                TRUE);
    MoveWindow (ThisDoc->hWndTB, 0, 0,
                RectWidth (MyRect),
                TOOLBARHEIGHT,
                TRUE);

    break;

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

  case WM_DISPLAY_SORTED:
    {
        TypGroup far *group;
        HANDLE header_handle, thread_handle;
        header_p headers, hp;
        thread_array thread_index;
        BOOL threadOk;
        long i;
        uint32 i1;

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

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

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

        ThisDoc->SelectedLines = 0; // (this recount may be redundant...)
        if(ThisDoc->TotalLines > 0)
        {
          for(i1 = 0; i1 < ThisDoc->TotalLines; i1++)
          {
            hp = header_elt(headers, i1);
            if(ART_SELECTED & hp->SeenSelected)
              ThisDoc->SelectedLines++;
          }
        }
        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);

        sort_by_option(headers, thread_index, threadOk, ThisDoc->TotalLines,
           header_handle, thread_handle);
        unlock_headers (header_handle, thread_handle);
        InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
    }
    break;
  
  case WM_AA_ENABLE_DISABLE:
    hMenu = GetMenu (hWnd);
    hSubMenu = GetSubMenu (hMenu, 1);   /* sorting menu */
    EnableMenuItem (hSubMenu, IDM_VIEW_ACTION, bEnableArticleAction ? ENABLE_MENU : DISABLE_MENU);
    break;
  
  case WM_COMMAND:
    switch (LOWORD (wParam)) {
    case IDV_EXIT:
      CloseGroupWnd (hWnd, ThisDoc);
      return (0);
      break;

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

    case IDV_NEXT:
      break;

    case IDM_MARK_ALL: // actually "Catch up and exit this group"
      article_operation (ThisDoc, 0, mark_read_all);
      NewsrcDirty = TRUE;
      CloseGroupWnd (hWnd, ThisDoc);
      return (0);
      break;

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

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

    case IDM_VIEW_ACTION:
      {
        TypGroup far *group;
        HANDLE header_handle, thread_handle;
        header_p headers;

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

        headers = lock_headers (header_handle, thread_handle);

        if(DialogBoxParam(hInst, "ActionDlg", hWnd, lpfnWinVnArticleActionDlg,
          (LPARAM)group->pAA))
        {
            g_pAction->ActOnArticles(group, headers);
            group->pAA->ActOnArticles(group, headers);
        }
        unlock_headers (header_handle, thread_handle);
        SendMessage(hWnd, WM_DISPLAY_SORTED, 0, 0);
      }
      break;

    case IDM_SORT_DATE:
    case IDM_SORT_SUBJECT:
    case IDM_SORT_LINES:
    case IDM_SORT_THREADSUB:
    case IDM_SORT_THREADS:
    case IDM_SORT_ARTNUM:
    case IDM_SORT_FROM:
      iSortOption = LOWORD(wParam);
      WriteSortOption(iSortOption, CurrentGroup);

      hMenu = GetMenu(hWnd);
      CheckMenuItem(hMenu, IDM_SORT_DATE,      MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_SUBJECT,   MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_LINES,     MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_THREADS,   MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_THREADSUB, MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_FROM,      MF_BYCOMMAND|MF_UNCHECKED);
      CheckMenuItem(hMenu, IDM_SORT_ARTNUM,    MF_BYCOMMAND|MF_UNCHECKED);

      CheckMenuItem(hMenu, iSortOption, MF_BYCOMMAND|MF_CHECKED);   
      SendMessage(hWnd, WM_DISPLAY_SORTED, 0, 0);
      break;


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


    case IDM_FIND:
    case IDM_FIND_NEXT_SAME:
      FindDoc = ThisDoc;

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

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

        /* 'Find Next' will start one line after */
        if (ArtListMultiSelect)
          starting_at = ThisDoc->ActiveLineID + ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);
        else
          starting_at = ThisDoc->TopLineOrd + ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);

        /* back up one if we're at the end */
        if (starting_at >= group->total_headers)
          starting_at--;

        found_artindex = search_headers (ThisDoc, headers, starting_at, group->total_headers);
        if (found_artindex == -1) {
          strcpy (mybuf, "\"");
          strcat (mybuf, ThisDoc->SearchStr);
          strcat (mybuf, "\" not found.");
          MessageBox (hWnd, mybuf, "Not found", MB_OK);
        }
        else {
          if (ArtListMultiSelect) {
            ThisDoc->FindOffset = (int) found_artindex;
            ThisDoc->AnchorLineID = (int) found_artindex;
            strcpy (LastArticleHeaderFind, FindDoc->SearchStr);
            SetGroupSelections (ThisDoc, headers, (int) found_artindex);
            AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
          }
          else {
            ThisDoc->TopLineOrd = (int) found_artindex;
            ThisDoc->FindOffset = (int) found_artindex;
            strcpy (LastArticleHeaderFind, FindDoc->SearchStr);
            InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
          }
        }
        unlock_headers (header_handle, thread_handle);
      }
      break;

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

    case IDM_SAVE_SELECTED:
      /* Query user for file name */
      if (!DialogBox (hInst, "WinVnSaveArts", hWnd, lpfnWinVnSaveArtsDlg)) {
        InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
      }
      else {
        ThisDoc->numArtsSaved = 0;
        ThisDoc->savingArtIndex = 0;
        SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
        goto doretrieve;
      }
      break;

    case IDM_FIND_NEXT_SELECTED:
    case IDM_READ_SELECTED:
      SendMessage(ThisDoc->hDocWnd, (UINT) WM_KEYDOWN, (WPARAM) VK_F5, 0);
      break;

    case IDM_DECODE_SELECTED:
      /* Query user for file name */
      if (!DialogBoxParam (hInst, "WinVnDecodeArts", hWnd, lpfnWinVnDecodeArtsDlg, 1)) {
        InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
      }
      else {
        if (TestCommBusy (hWnd, "Can't Decode Articles") ||
            TestCodingBusy (hWnd, "Can't Decode Articles"))
          break;

        DecodeInit ();
        CommDoc = ThisDoc;
        CommDecoding = TRUE;
        ThisDoc->numArtsSaved = 0;
        ThisDoc->savingArtIndex = 0;
        SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
        if (AlsoDecodeOpenArticles) {
          DecodeOpenArticles (hWnd);
        }
        /* now start the retrieving the selected arts */
        goto doretrieve;
      }
      break;

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

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

    case IDM_SELECT_MATCH:
      /* select all articles containing a string */
      FindDoc = ThisDoc;
      DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
      if (strcmp (FindDoc->SearchStr, "")) {
        if (AffectSelected (FindDoc, SELECT, COMPARE) == 0) {
          strcpy (mybuf, "\"");
          strcat (mybuf, FindDoc->SearchStr);
          strcat (mybuf, "\" not found.");
          MessageBox (hWnd, mybuf, "Not found", MB_OK);
        }
      }
      break;
      
    case IDM_DESELECT_MATCH:
      /* deselect all articles containing a string */
      FindDoc = ThisDoc;
      DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
      if (strcmp (FindDoc->SearchStr, "")) {
        if (AffectSelected (FindDoc, DESELECT, COMPARE) == 0) {
          strcpy (mybuf, "\"");
          strcat (mybuf, FindDoc->SearchStr);
          strcat (mybuf, "\" not found.");
          MessageBox (hWnd, mybuf, "Not found", MB_OK);
        }
      }
      break;

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

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

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

            // Free whatever headers are there.
            if (group->header_handle) {
              long i;
              header_p headers;
              
              headers = lock_headers (group->header_handle, group->thread_handle);
              for(i = 0; i < group->total_headers; i++)
              {
                delete headers[i].pHD;
              }
              unlock_headers (group->header_handle, group->thread_handle);
            free_headers (group->header_handle, group->thread_handle);
            }
            group->header_handle = (HANDLE) NULL;
            group->thread_handle = (HANDLE) NULL;
            group->total_headers = 0;
            delete group->pAA;
            group->pAA = NULL;

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

            SetGroupMenus (ThisDoc, DISABLE);

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

      /* the following msgs are sent by wvutil engine on completion 
       * retrieval
       */
    case ID_RETRIEVE_COMPLETE:
      SetGroupMenus (ThisDoc, ENABLE);
      SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
      SetCursor (hArrowCursor); //shimomai
      InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
      break;

    case ID_ARTICLE_RETRIEVE_COMPLETE:
      {
        TypGroup far *group;

        header_p headers;
        HANDLE header_handle;
        HANDLE thread_handle;
        int justSavedIndex, startIndex;

        if (ThisDoc->savingArtIndex == -1)  /* this retrieve wasn't part of a save operation */
          break;

        if (CodingState == INACTIVE) {
          /* If this is the 1st article saved, then value of Append depends
             * on what the user selected in dialog.  If > 1st, always append 
           */
          if (!MRRWriteDocument (ActiveArticleDoc, sizeof (TypText),
                                 SaveArtFileName,
                              (ThisDoc->numArtsSaved == 0) ? SaveArtAppend : TRUE)) {
            *SaveArtFileName = '\0';
            MessageBox (hWnd, "Could not write to file.  Operation canceled", "Problems saving file", MB_OK | MB_ICONEXCLAMATION);
            SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
            ThisDoc->savingArtIndex = -1;
            break;
          }
        }
        else {
          if (CompleteThisDecode () == FAIL) {
            DecodeDone ();
            MessageBox (hWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
            SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
            ThisDoc->savingArtIndex = -1;
            break;
          }
        }
        justSavedIndex = ThisDoc->savingArtIndex;
        ThisDoc->numArtsSaved++;

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

            SelectHeader (ThisDoc, header_elt (headers, justSavedIndex), FALSE);

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

            if (ThisDoc->savingArtIndex >= group->total_headers) {
            ThisDoc->savingArtIndex = 0;
            }
            startIndex = ThisDoc->savingArtIndex;
            while (ThisDoc->SelectedLines &&
                !(ART_SELECTED & (header_elt (headers, ThisDoc->savingArtIndex))->SeenSelected)) {
            ThisDoc->savingArtIndex = (ThisDoc->savingArtIndex + 1) % (int) (group->total_headers);
            if (ThisDoc->savingArtIndex == startIndex)
                break;
            }

            if (ThisDoc->savingArtIndex == startIndex &&        /* this shouldn't happen */
                !(ART_SELECTED & (header_elt (headers, ThisDoc->savingArtIndex))->SeenSelected)) {
            ThisDoc->SelectedLines = 0;
            }
            
            if (ThisDoc->SelectedLines == 0) {  /* done */
               SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
               if (ThisDoc->numArtsSaved > 0) {
                  if (CodingState == INACTIVE) {
                     SetStatbarPercent(ThisDoc->hWndFrame,0,ThisDoc,TRUE);
                     sprintf (mybuf, "Done! Saved %d article%c to file %s",ThisDoc->numArtsSaved, 
                              (ThisDoc->numArtsSaved == 1) ? ' ' : 's', SaveArtFileName);
                     SetStatbarText (ThisDoc->hWndFrame, mybuf, ThisDoc, TRUE, TRUE);
                   }
                }
                else
                    SetStatbarText (ThisDoc->hWndFrame, "No articles selected", ThisDoc, TRUE, TRUE);

                if (CodingState != INACTIVE) DecodeDone ();

                SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
                InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
                unlock_headers (header_handle, thread_handle);
                ThisDoc->savingArtIndex = -1;   /* reset to non-saving state */
            }
            else {
                sprintf (mybuf, "Saved %d article%c to file %s",ThisDoc->numArtsSaved, 
                        (ThisDoc->numArtsSaved == 1) ? ' ' : 's',SaveArtFileName);
                SetStatbarText (ThisDoc->hWndFrame, mybuf, ThisDoc, TRUE, TRUE);
                SetStatbarPercent(ThisDoc->hWndFrame,
                                 ((int)(ThisDoc->numArtsSaved / (ThisDoc->numArtsSaved + ThisDoc->SelectedLines)) * 100),ThisDoc,TRUE);

                if (KeepArticleHeaderVisible)
                    AdjustTopScByDoc (ThisDoc, ThisDoc->savingArtIndex);
                if (hCodedBlockWnd) {
                    sprintf (str, "Decoding Status: %d to go", ThisDoc->SelectedLines);
                    SetWindowText (hCodedBlockWnd, (LPCSTR) str);
                }
                sprintf (mybuf, "%ld", (header_elt (headers, ThisDoc->savingArtIndex))->number);
                (header_elt (headers, ThisDoc->savingArtIndex))->SeenSelected |= ART_SEEN;
                unlock_headers (header_handle, thread_handle);

                if (CodingState != INACTIVE) {
                    if ((currentCoded = InitCoded (hWnd)) == NULL) {
                        MessageBox (hWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
                        DecodeDone ();
                        SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
                        ThisDoc->savingArtIndex = -1;
                        break;
                    }
                    InitiateReceiveArticle (ThisDoc, mybuf);
                    UpdateBlockStatus ();   // display some initial status
                }
                else
                    ViewArticle (ThisDoc, ThisDoc->savingArtIndex, REUSE, NO_SHOW, NO_ID);
                }
                InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
                return (0);
         }
      }
      break;
    }
  default:
    return (DefWindowProc (hWnd, message, wParam, lParam));
  }
  if (GetFocus () == hWnd)
    SetFocus (ThisDoc->hDocWnd);
  return (0);
}


// WVSeendb member functions

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


// Create bitmap of articles in the range uLo to uHi.
// Init all, either taking possibly overlapping historical information
// from pre-existing info, otherwise unseen
WVSeenDB::WVSeenDB(uint32 uLo, uint32 uHi, const WVSeenDB*pSDB)
: m_uLowVal(min(uLo, uHi)), m_uHiVal(max(uLo, uHi)),
  m_uLowBase((m_uLowVal/BMSIZE) * BMSIZE), m_uHiBase((m_uHiVal/BMSIZE) * BMSIZE)
{
  uint32 i;
  uint32 idx1, idx2;
  uint32 uBase1, uBase2;
  uint32 uNumInts = 1 + (m_uHiBase - m_uLowBase)/BMSIZE;
  
//#ifdef _DEBUG
//  TRACE4("WVSeenDB(): SeenDB created from %ld (%ld)-%ld (%ld)\n",
//    m_uLowBase, m_uLowVal, m_uHiBase + BMSIZE-1, m_uHiVal);
//  if(pSDB)
//  {
//    TRACE4(" Old range was %ld (%ld)-%ld (%ld)\n",
//      pSDB->m_uLowBase, pSDB->m_uLowVal, pSDB->m_uHiBase + BMSIZE-1, pSDB->m_uHiVal);
//  }
//#endif
    
// m_pBitmap = new unsigned int[uNumInts]; 

// The new operator will return a data structure
// that is uNumInts * 4 in length. If this is
// greater than 64K then we need to deal with
// huge pointers.

  if (uNumInts < (INT_MAX / 4)) { 
    m_pBitmap = new unsigned long[uNumInts];
  }
  else {
#ifdef _WIN32
	m_pBitmap = new __far unsigned long[4,uNumInts];  
#else 
    m_pBitmap = new __huge unsigned long[4,(unsigned long) uNumInts];
  	
#endif
  }
  
  if(m_pBitmap)
  {
    if(pSDB)
    {
      uBase1 = this->m_uLowBase;
      uBase2 = pSDB->m_uLowBase;
      idx2 = 0;
      while(uBase2 < this->m_uLowBase)
      {
        idx2++;
        uBase2 += BMSIZE;
      }
      for(idx1 = 0; idx1 < uNumInts; idx1++)
      {
        if(uBase1 < uBase2)
          m_pBitmap[idx1] = (unsigned int) (int) -1;
        else if((uBase1 == uBase2) && uBase2 <= pSDB->m_uHiBase)
        {
          this->m_pBitmap[idx1] = pSDB->m_pBitmap[idx2++];
          uBase2 += BMSIZE;
        }
        else
          m_pBitmap[idx1] = 0;
        uBase1 += BMSIZE;
      }
    }
    else // pSDB is NULL, init all bits to zero
    {
      for(i = 0; i < uNumInts; i++)
        m_pBitmap[i] = 0;
    }
  }
#ifdef _DEBUG
  else
    DEBUG_BREAK; // Unable to allocate bitmap
#endif
}

  
// Mark an article as 'seen'
void WVSeenDB::MarkSeen(uint32 uArtIdx)
{
  uint32 uArrIdx;
  
  if(this && m_pBitmap)
  {
    if((uArtIdx >= m_uLowVal) && (uArtIdx <= m_uHiVal))
    {
      uArrIdx = (uArtIdx - m_uLowBase) / BMSIZE;
      m_pBitmap[uArrIdx] |= 1 << (uArtIdx % BMSIZE);
    }
  }
}


// Mark a range of articles as 'seen'
void WVSeenDB::MarkSeen(uint32 uArtIdx1, uint32 uArtIdx2)
{
  uint32 uBitIdx1, uBitIdx2;
  uint32 uArrIdx1, uArrIdx2;
  
  if(this && m_pBitmap)
  {
    uArtIdx1 = max(uArtIdx1, m_uLowBase);
    uArtIdx1 = min(uArtIdx1, m_uHiVal);
    uArtIdx2 = max(uArtIdx1, uArtIdx2);
    uArtIdx2 = min(uArtIdx2, m_uHiVal);
    uArrIdx1 = (uArtIdx1 - m_uLowBase) / BMSIZE;
    uArrIdx2 = (uArtIdx2 - m_uLowBase) / BMSIZE;
    uBitIdx2 = (uArrIdx1 == uArrIdx2) ? uArtIdx2 % BMSIZE : BMSIZE - 1;
    
    for(uBitIdx1 = uArtIdx1 % BMSIZE; uBitIdx1 <= uBitIdx2; uBitIdx1++)
    {
      m_pBitmap[uArrIdx1] |= 1 << uBitIdx1;
    }
    
    if(uArrIdx1 < uArrIdx2)
    {
      for(uArrIdx1++; uArrIdx1 < uArrIdx2; uArrIdx1++)
        m_pBitmap[uArrIdx1] = (unsigned int) (int) -1;
      
      uBitIdx2 = uArtIdx2 % BMSIZE;
      for(uBitIdx1 = 0; uBitIdx1 <= uBitIdx2; uBitIdx1++)
        m_pBitmap[uArrIdx2] |= 1 << uBitIdx1;
    }
  }
}


// Mark an article as 'unseen'
void WVSeenDB::MarkUnseen(uint32 uArtIdx)
{
  uint32 uArrIdx;
  
  if(this && m_pBitmap)
  {
    if((uArtIdx >= m_uLowVal) && (uArtIdx <= m_uHiVal))
    {
      uArrIdx = (uArtIdx - m_uLowBase) / BMSIZE;
      m_pBitmap[uArrIdx] &= ~(1 << (uArtIdx % BMSIZE));
    }
  }
}


// Has a particular article been 'seen'?
BOOL WVSeenDB::WasSeen(uint32 uArtIdx)
{
  uint32 uArrIdx;
  
  if(this && m_pBitmap && (uArtIdx <= m_uHiVal))
  {
    if(uArtIdx < m_uLowVal && uArtIdx)
      return TRUE;
    uArrIdx = (uArtIdx - m_uLowBase)/ BMSIZE;
    if(m_pBitmap[uArrIdx] & (1 << (uArtIdx % BMSIZE)))
      return TRUE;
  }
  return FALSE;
}


// Count number of unseen articles
uint32 WVSeenDB::NumUnseen()
{
  uint32 uBitIdx1, uBitIdx2;
  uint32 uArrInd;
  uint32 uMaxInd;
  uint32 uRetval = 0;
  
  if(this && m_pBitmap)
  {
    uMaxInd = (m_uHiBase - m_uLowBase)/BMSIZE;
    
    uBitIdx2 = uMaxInd ? BMSIZE - 1 : m_uHiVal % BMSIZE;
    for(uBitIdx1 = m_uLowVal % BMSIZE; uBitIdx1 <= uBitIdx2; uBitIdx1++)
    {
      if(!(m_pBitmap[0] & (1 << uBitIdx1)))
        uRetval++;
    }
    if(uMaxInd) // more than one array member
    {
      uBitIdx2 = m_uHiVal % BMSIZE;
      for(uBitIdx1 = 0; uBitIdx1 <= uBitIdx2; uBitIdx1++)
      {
        if(!(m_pBitmap[uMaxInd] & (1 << uBitIdx1)))
          uRetval++;
      }
      for(uArrInd = 1; uArrInd < uMaxInd; uArrInd++)
      {
        if(m_pBitmap[uArrInd] != (unsigned int) (int) -1) // all seen
        {
          if(m_pBitmap[uArrInd] == 0) // all unseen
            uRetval += BMSIZE;
          else  // count 'em
          {
            for(uBitIdx1 = 0; uBitIdx1 < BMSIZE; uBitIdx1++)
            {
              if(!(m_pBitmap[uArrInd] & (1 << uBitIdx1)))
                uRetval++;
            }
          }
        }
      }
    }
  }
  return uRetval;
}

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

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




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

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

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


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

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

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

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

@


1.85
log
@moved location of <limits.h> call so precompiled header file (PCH) will work
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.84 1997/02/28 19:24:12 dumoulin Exp $
d1338 2
d1526 2
d1529 1
a1529 2
 	 ParseAddress(FromAddress, AddressString, MAXDIALOGSTRING, NameString, MAXDIALOGSTRING);

d1537 2
a1538 1
	  if (stricmp(header->pHD->sFrom, NameString) == 0)
d1544 2
a1545 1
	  if (stricmp(header->pHD->sFrom, FromAddress) == 0)
@


1.84
log
@fixed problems with Toolbar
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.83 1997/02/27 23:38:01 dumoulin Exp dumoulin $
a51 1
#include <limits.h>
d54 1
d56 1
a56 1
#pragma hdrstop
@


1.83
log
@fixed missing line on toolbar
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.82 1997/02/14 20:12:31 dumoulin Exp dumoulin $
a3059 1
//    uBitIdx2 = (uMaxInd > 1) ? BMSIZE : m_uHiVal % BMSIZE;
a3111 43
// Find the lowest unseen article
// Return:
//   Lowest unseen article number (if found)
//   Highest article number + 1 if all seen
//   0 if invalid, no bitmap available, etc.
//uint32 WVSeenDB::LowestUnseen()
//{
//  uint32 uBitIdx1, uBitIdx2;
//  uint32 uArrIdx;
//  uint32 uMaxInd = (m_uHiBase - m_uLowBase)/BMSIZE;
//  uint32 uRetval = 0;
//
//  if(this && m_pBitmap)
//  {
//    uRetval = m_uLowVal;
//    uBitIdx2 = (uMaxInd > 1) ? BMSIZE - 1: m_uHiVal % BMSIZE;
//    for(uBitIdx1 = m_uLowVal % BMSIZE; uBitIdx1 <= uBitIdx2; uBitIdx1++, uRetval++)
//    {
//      if(!(m_pBitmap[0] & (1 << uBitIdx1)))
//        return uRetval;
//    }
//    if(uMaxInd)
//    {
//      for(uArrIdx = 1; uArrIdx <= uMaxInd; uArrIdx++)
//    {
//        if(m_pBitmap[uArrIdx] == (unsigned int) (int) -1) // all seen
//        uRetval += BMSIZE;
//        else // probably got one, go find it...
//        {
//        uBitIdx2 = (uArrIdx == uMaxInd) ? m_uHiVal % BMSIZE : BMSIZE;
//          for(uBitIdx1 = 0; uBitIdx1 <= uBitIdx2; uBitIdx1++, uRetval++)
//          {
//          if(!(m_pBitmap[uArrIdx] & (1 << uBitIdx1)))
//              return uRetval;
//        }
//        }
//      }
//    }
//    if(uRetval > m_uHiVal)
//      uRetval = m_uHiVal + 1;
//  }
//  return uRetval;
//}
@


1.82
log
@Add patches for WinVN .99-9 Beta 2
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.81 1997/02/08 11:15:56 dumoulin Exp dumoulin $
d65 1
a65 1
#include "WVClass.h"
d2224 14
d2241 1
a2241 1
                                     0,     /* Initial X position */
d2249 1
@


1.81
log
@minor formatting changes
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.80 1997/02/08 10:57:39 dumoulin Exp $
a56 1
#include <commctrl.h>
@


1.80
log
@Add support for ToolTips under 32bit version (glasser)
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.79 1997/02/06 21:22:10 dumoulin Exp dumoulin $
d56 1
a56 1
#pragma hdrstop
@


1.79
log
@support for bitmap of seen articles, mods from ints to longs
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.78 1996/08/13 04:45:48 tanaka Exp $
d56 5
a60 1
#pragma hdrstop
d65 1
d513 2
a514 2
            DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
			ReleaseCapture ();
d757 1
a757 1
                  _ltoa(artnum, scratch_line, 10);
d763 1
a763 1
                  TextOut (hDC, X, Y, &indicator, 1);
d766 4
a769 4
          SetRect (&aRect, X+ArtListSeparator1+CharWidth, Y, X+ArtListSeparator2, Y + LineHeight);
                  StringDate (scratch_line, header->pHD->date),
          ExtTextOut (hDC, X+ArtListSeparator1+CharWidth, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
                                          scratch_line, strlen(scratch_line), (LPINT) NULL);
a772 1
//        SetTextAlign (hDC, TA_TOP | TA_LEFT);
d778 2
a779 2
                 scratch_line[0] = ' ';
                  _ltoa(header->lines, &(scratch_line[1]), 10);
a791 2
//          ExtTextOut (hDC, X, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
//                      scratch_line, MyLen, (LPINT) NULL);
d1338 1
a1431 5
    // Wise guy who likes Free Agent has posted articles with subjects long
    // enough to exceed sizeof(mybuf) and crash WinVN...
    //strcpy (mybuf, "Retrieving \"");
    //lstrcat (mybuf, lpsz);
    //lstrcat (mybuf, "\"");
d1520 14
a1533 6
  if ((stricmp (header->pHD->sFrom, UserName) == 0) ||
      (stricmp (header->pHD->sFrom, MailAddress) == 0) ||
      (stricmp (header->pHD->sFrom, ReplyTo) == 0)) 
     EnableMenuItem(hSubMenu,IDM_CANCELART,ENABLE_MENU);
  else
     EnableMenuItem(hSubMenu,IDM_CANCELART,DISABLE_MENU);
d1535 11
a2077 1

d2081 5
d2091 4
d2100 4
d2110 5
d2120 4
d2129 4
d2139 4
d2149 4
d2158 4
d2168 5
d2178 4
d2186 4
d2195 18
d2222 1
d3046 2
a3047 1
    uBitIdx2 = (uMaxInd > 1) ? BMSIZE : m_uHiVal % BMSIZE;
d3104 46
a3149 1
uint32 WVSeenDB::LowestUnseen()
d3151 2
a3152 1
  uint32 uBitIdx1, uBitIdx2;
d3154 1
a3154 1
  uint32 uMaxInd = (m_uHiBase - m_uLowBase)/BMSIZE;
d3159 6
a3164 3
    uRetval = m_uLowVal;
    uBitIdx2 = (uMaxInd > 1) ? BMSIZE - 1: m_uHiVal % BMSIZE;
    for(uBitIdx1 = m_uLowVal % BMSIZE; uBitIdx1 <= uBitIdx2; uBitIdx1++, uRetval++)
d3166 4
a3169 6
      if(!(m_pBitmap[0] & (1 << uBitIdx1)))
        return uRetval;
    }
    if(uMaxInd)
    {
      for(uArrIdx = 1; uArrIdx <= uMaxInd; uArrIdx++)
d3171 2
a3172 3
        if(m_pBitmap[uArrIdx] == (unsigned int) (int) -1) // all seen
          uRetval += BMSIZE;
        else // probably got one, go find it...
d3174 4
a3177 6
          uBitIdx2 = (uArrIdx == uMaxInd) ? m_uHiVal % BMSIZE : BMSIZE;
          for(uBitIdx1 = 0; uBitIdx1 <= uBitIdx2; uBitIdx1++, uRetval++)
          {
            if(!(m_pBitmap[uArrIdx] & (1 << uBitIdx1)))
              return uRetval;
          }
d3182 3
d3186 1
d3192 5
a3196 3
#if 0
// Get the next range.  Return {0,0} at end of range or invalid
void WVSeenDB::GetRange(TypRange& r)
d3198 2
a3199 1
  uint32 uBitIdx;
d3201 4
a3204 3
  BOOL bFound;
  
  if(this && m_pBitmap)
d3206 6
a3211 3
    // Find the first seen article
    r.First = r.Last + 1;
    if(r.First >= m_uLowVal)
d3213 4
a3216 2
      bFound = FALSE;
      for(uArrIdx = (r.First - m_uLowBase)/BMSIZE; r.First <= m_uHiVal && !bFound; uArrIdx++)
d3218 2
a3219 2
        uBitIdx = r.First % BMSIZE;
        if(m_pBitmap[uArrIdx] || uBitIdx)
d3221 4
a3224 12
          for(; uBitIdx < BMSIZE; uBitIdx++, r.First++)
          {
            if(m_pBitmap[uArrIdx] & (1 << uBitIdx))
            {
              bFound = TRUE;
              break;
            }
          }
        }
        else // zero - skip it
        {
          r.First += BMSIZE;
d3228 2
a3229 32
    // Phew! - Now, find the first unseen article
    
    if(r.First <= m_uHiVal)
    {
      r.Last = max(r.First, m_uLowVal);
      uArrIdx = (r.Last - m_uLowBase)/BMSIZE;
      for(; r.Last <= m_uHiVal; uArrIdx++)
      {
        uBitIdx = r.Last % BMSIZE;
        if((m_pBitmap[uArrIdx] != (unsigned int) -1) || uBitIdx)
        {
          for(; uBitIdx < BMSIZE; uBitIdx++, r.Last++)
          {
            if(!(m_pBitmap[uArrIdx] & (1 << uBitIdx)))
            {
              if(--r.Last <= m_uHiVal)
                return;
              r.Last++;
            }
          }
        }
        else // 0xFFFF/0xFFFFFFFF - skip it
        {
          r.Last += BMSIZE;
        }
      }
      if(--r.Last == m_uHiVal)
      {
        DEBUG_BREAK;
        return;
      }
    }
d3231 1
a3231 3
  
  r.First = r.Last = 0;
  return;
d3233 2
a3234 2
#else
// Get the next range.  Return {0,0} if end of range or invalid
d3239 2
a3240 4
    r.First = r.Last + 1;
    while((r.First <= m_uHiVal) && !WasSeen(r.First)) // TODO: this could be
      r.First++;                                      // more efficient
    if(r.First <= m_uHiVal)
d3242 2
a3243 4
      r.Last = m_uLowVal ? max(r.First, m_uLowVal - 1) : r.First;
      while((r.Last <= m_uHiVal) && WasSeen(r.Last)) // TODO: this could be
        r.Last++;                                    // more efficient
      if(--r.Last <= m_uHiVal)
d3248 1
a3248 2
  r.First = 0;
  r.Last = 0;
a3250 1
#endif
a3251 5
//
// Local Variables:
// tab-width: 2
// end:
//
@


1.78
log
@fixes to errors with newsrc files
@
text
@d21 1
a21 1
 *                                       article from the server    *
d46 1
a46 1
 * $Id: wvgroup.cpp 1.77 1995/11/10 06:45:49 dumoulin Exp $
d52 1
a52 2
//extern "C"
//{
d55 1
a55 1
//}
d73 1
a73 1
#define DRAG_ON		5
d78 1
a78 1
char_p string_compare_insensitive (char_p a, LPSTR b);
d140 7
a146 7
	WidthCursor = FALSE;
	/* Article List Separator */
	if (ArtListSeparator1 <= 0) {
	  ArtListSeparator1 = CharWidth * 8;
	  ArtListSeparator2 = CharWidth * 15;
	  ArtListSeparator3 = CharWidth * 40;
	}
d223 1
a223 1
    case VK_NEXT:
d247 10
a256 10
	case VK_ESCAPE: //ishido
	  if (DragMouseAction == DRAG_ON) {
		DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
		ReleaseCapture ();
		DragMouseAction = DRAG_NONE;
		WidthCursor = FALSE;
		SetCursor (hArrowCursor);
		InvalidateRect(hWnd, NULL, FALSE);
	  }
	  break;
d319 1
a319 1
    /* Single middle click or right click will toggle the seen/unseen status
d346 8
a353 8
	if (WidthCursor) {
	  orgPos = LOWORD (lParam);
	  DragCursorPosition = orgPos;
	  DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
		SetCapture (hWnd);		// release capture on button up
	  DragMouseAction = DRAG_ON;
	}
	else if (!CommBusy || (CommDoc != ThisDoc || CommDecoding)) {
d409 1
a409 1
    /*  Letting up on the left button on an article name
d412 11
a422 11
	if (DragMouseAction == DRAG_ON) {
	  DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
	  ReleaseCapture ();
	  *Separator += LOWORD (lParam) - orgPos;
	  if (ArtListSeparator1 < CharWidth ||
		ArtListSeparator1 > ArtListSeparator2 ||
		ArtListSeparator2 > ArtListSeparator3) {
		ArtListSeparator1 = CharWidth * 8;
		ArtListSeparator2 = CharWidth * 15;
		ArtListSeparator3 = CharWidth * 40;
	  }
d424 1
a424 1
	}
d462 8
a469 8
  case WM_SETCURSOR:	//shimomai
	if ((!CommBusy || CommDoc != ThisDoc || CommDecoding)) {
	  if (WidthCursor)
		SetCursor (hSizeArrow);
	  else
		SetCursor (hArrowCursor);
	}
	break;
d476 2
a477 2
//	if ((DragMouseAction != DRAG_NONE) &&
//		(!CommBusy || CommDoc != ThisDoc || CommDecoding)) {
d479 1
a479 1
	if ((!CommBusy || CommDoc != ThisDoc || CommDecoding)) {
d483 33
a515 33
	  switch (DragMouseAction) {
	  case DRAG_NONE:
		WidthCursor = FALSE;
	    X -= SideSpace - ThisDoc->ScXOffset * (CharWidth + 1);
	    if (Y < LineHeight) {
			WidthCursor = TRUE;
		    if (X >= ArtListSeparator1 && X < ArtListSeparator1 + CharWidth) {
				Separator = &ArtListSeparator1;
			}
		    else if (X >= ArtListSeparator2 && X < ArtListSeparator2 + CharWidth) {
				Separator = &ArtListSeparator2;
			}
		    else if (X >= ArtListSeparator3 && X < ArtListSeparator3 + CharWidth) {
				Separator = &ArtListSeparator3;
			}
			else {
				WidthCursor = FALSE;
			}
		}
		break;

	  case DRAG_ON:
		{
		  if (!(wParam && MK_LBUTTON)) {
		    DragMouseAction = DRAG_NONE;
			DrawDragingCursor(hWnd, DragCursorPosition, 0, 1);
		ReleaseCapture ();
		  } else {
			DrawDragingCursor(hWnd, DragCursorPosition, X, 2);
			DragCursorPosition = X;
		  }
		  break;
		}
d517 1
a517 1
	  default:
d568 2
a569 1
      int VertLines, HorzChars;
d571 1
a571 1
      int RangeHigh, CurPos;
d574 1
a574 1
      long int artnum;
d583 1
a583 1
      long OldHighestSeen;
d590 2
a591 2
			char AuthorName[32]; //shimomai
			char *ptr;	//shimomai
d651 1
a651 3
      /* Update the scroll bar thumb position.                 */

      //RangeHigh = ThisDoc->TotalLines - VertLines;
d662 1
a662 1
      SetScrollRange (hWnd, SB_VERT, 0, RangeHigh,
d664 1
d667 1
a667 1
        SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
d675 2
a676 6
      SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
      SetScrollPos (hWnd, SB_HORZ, ThisDoc->ScXOffset, TRUE);

	  X = SideSpace - ThisDoc->ScXOffset * (CharWidth + 1); //shimomai
//      GetTextExtentPoint (hDC, "s 99999 ", 8, &sz);
//      indicatorwidth = sz.cx;
d678 1
a678 2
//      X = SideSpace - ThisDoc->ScXOffset * (CharWidth + 2);
//      Xtext = X + indicatorwidth;
d701 2
a702 2
      if(ART_UNSEEN == header->Seen)
      {
d704 7
a710 7
          indicator = 'n';
      }
      else if(ART_SEEN & header->Seen)
      {
        indicator = 's';
      }
          else if(ART_KILLED & header->Seen)
d712 1
a712 1
        indicator = 'k';
d718 8
a725 10
		  { //ishido DBCS aware author name trimming
			char *base_p = header->from;
			char *p = base_p;
			while ((*p != '\0') && (p - base_p < sizeof(AuthorName)-1))
			  p = AnsiNext(p);
			strncpy(AuthorName, header->from, (p - base_p) + 1);
			AuthorName[p - base_p] = '\0';
		  }

          ptr = scratch_line;
d728 2
a729 2
          if (ART_SEENORKILLED & header->Seen) {
            if (header->Selected)
d732 1
a732 1
              MyColorMask = (ART_KILLED == header->Seen) ? 4 : 1;
d735 1
a735 1
            if (header->Selected)
d749 6
a754 6
/* out article number */
		  SetRect (&aRect, 0, Y, X+ArtListSeparator1+CharWidth, Y + LineHeight);
		  SetTextAlign (hDC, TA_TOP | TA_RIGHT);
                  _ltoa(artnum, ptr, 10);
      ExtTextOut (hDC, X+ArtListSeparator1, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
                      ptr, strlen(ptr), (LPINT) NULL);
d756 2
a757 2
/* out indicator */
		  SetTextAlign (hDC, TA_TOP | TA_LEFT);
d760 9
a768 9
/* out date */
		  SetRect (&aRect, X+ArtListSeparator1+CharWidth, Y, X+ArtListSeparator2, Y + LineHeight);
                  StringDate (ptr, header->date),
		  ExtTextOut (hDC, X+ArtListSeparator1+CharWidth, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
                                          ptr, strlen(ptr), (LPINT) NULL);

/* out author name */
		  SetRect (&aRect, X+ArtListSeparator2, Y, X+ArtListSeparator3, Y + LineHeight);
//		  SetTextAlign (hDC, TA_TOP | TA_LEFT);
d772 11
a782 11
/* out lines */
		  SetTextAlign (hDC, TA_TOP | TA_RIGHT);
                 ptr[0] = ' ';
                  _ltoa(header->lines, &(ptr[1]), 10);
          TextOut (hDC, X+ArtListSeparator3, Y, ptr, strlen(ptr));

/* out subject */
		  SetRect (&aRect, X+ArtListSeparator3, Y, clientRect.right, Y + LineHeight);
		  SetTextAlign (hDC, TA_TOP | TA_LEFT);
		  ptr = (ThreadFullSubject || !(header->thread_depth))
						? header->subject : ThreadDepthIndicator;
d784 2
a785 2
          			ETO_OPAQUE | ETO_CLIPPED, &aRect,
                    ptr, strlen(ptr), (LPINT) NULL);
d828 1
a828 1
 *  also make sure no child articles are active comm... jsc 11/1/94
d830 1
a830 1
void
d859 1
a859 1
void
d873 1
a873 1
 * dis/enable menu items which depend on group list being completely
a966 1
//  int32 retval = -1;
a989 1
//    artindex = 0;
d995 1
a995 1
        if(header->Selected)
d1012 1
a1012 2
  if (header->Selected != value) {
    header->Selected = value;
d1014 2
d1017 1
d1019 2
d1022 1
d1028 1
a1028 1

d1074 3
a1076 2
    header->Seen = (header->Seen ? ART_UNSEEN : ART_SEEN);
    return (header->Seen);
d1093 1
a1093 1
      header->Seen = ART_SEEN;
d1113 1
a1113 1
    header->Seen = ART_SEEN;
d1115 2
a1116 1
  GroupDoc->HighestPrevSeen = GroupDoc->ServerLast;
d1130 2
a1131 2
    if (header->Selected) {
        header->Seen = ART_SEEN;
d1147 1
a1147 1
    if (header->Seen) {
d1164 1
a1164 1
    if (header->Seen == ART_UNSEEN) {
d1181 1
a1181 1
    if (header->Seen == ART_UNSEEN) {
d1198 1
a1198 1
    if (header->Seen) {
d1215 2
a1216 2
    if (header->Selected) {
        header->Seen = ART_UNSEEN;
d1231 2
a1232 2
    SelectHeader (Doc, header, header->Selected ? FALSE : TRUE);
    return (header->Selected);
d1246 1
a1246 1
    header->Seen = ART_UNSEEN;
d1261 1
a1261 1
    header->Seen = ART_SEEN;
d1327 3
a1329 2
  char mybuf[MAXINTERNALLINE];
  char far *lpsz;
d1353 2
a1354 2
    header = header_elt (headers, artindex);

d1357 2
a1358 2
        (header->ArtDoc->CountedLines == header->lines)){

d1373 1
a1373 1
    }
d1380 1
a1380 1
    if ((NewArticleWindow && !reuse) || !ActiveArticleDoc ||
d1419 2
a1420 2
    header->Seen |= ART_SEEN;
    header->Selected &= ~ART_SELECTED;
d1425 1
a1425 1
        lpsz = (char far *) header->subject;
d1429 8
a1436 3
    strcpy (mybuf, "Retrieving \"");
    lstrcat (mybuf, lpsz);
    lstrcat (mybuf, "\"");
d1444 1
a1444 1
    if (xScreen > num * ArtCharWidth) {
d1462 1
a1462 1
                            mybuf,
d1478 1
a1478 1
    SetWindowText (CommDoc->hWndFrame, mybuf);
d1505 1
a1505 1
  CommDoc->CountedLines = header->lines;
d1520 1
a1520 1
  hMenu = GetMenu (hWndArt);
d1522 3
a1524 3
  if ((stricmp (header->from, UserName) == 0) ||
      (stricmp (header->from, MailAddress) == 0) ||
      (stricmp (header->from, ReplyTo) == 0))
d1563 1
a1563 1

a1672 1
  TypRange MyRange, *RangePtr;
a1676 1
  TypLine *LinePtr;
d1679 1
a1679 4
  BOOL InSeen = TRUE;
  BOOL GotLock = FALSE;
  unsigned int MyLength;
  unsigned int maxRanges;
d1681 1
a1681 1

d1689 3
a1691 4
                 &ParentBlock, &ParentLine);

  if ((GotLock == FALSE) ||
      (LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) == NULL) {
d1693 5
a1697 6
  }
  group = GetGroup(LinePtr);
  MoveBytes (ParentLine, LinePtr, ParentLine->length);

  if (group->total_headers > 0) {

d1700 2
a1701 2
    /* this can happen if user selects "Update from server" then cancel. */
    if (!header_handle)
d1703 1
a1703 1

d1706 1
a1706 70

#if 0	// TANAKA Goh (newsrc patch)
	group->nRanges = 0;
	group->nSpareRanges	= SpareRanges;

	maxRanges = ((Doc->BlockSize - Doc->SplitSize) - ParentLine->length +
				 group->nRanges * sizeof (TypRange) +
				 group->nSpareRanges * sizeof (TypRange)) / sizeof (TypRange) - 1;

	RangePtr = GetRangePtr(group);
	MyRange.First = MyRange.Last = 1;
#else	// TANAKA Goh (newsrc patch)
	if (group->ServerEstNum == group->total_headers) {
    group->nRanges = 0;
    group->nSpareRanges = SpareRanges;

    maxRanges = ((Doc->BlockSize - Doc->SplitSize) - ParentLine->length +
                 group->nRanges * sizeof (TypRange) +
                 group->nSpareRanges * sizeof (TypRange)) / sizeof (TypRange) - 1;

    RangePtr = GetRangePtr(group);
    MyRange.First = MyRange.Last = 1;
	} else {
	  TypRange far *PrevRangePtr = (TypRange far *)((char far *)group + RangeOffset(group->NameLen));
	  unsigned int	nPrevRanges = group->nRanges;

	  group->nRanges = 0;
	  group->nSpareRanges = SpareRanges;

	  maxRanges = ((Doc->BlockSize - Doc->SplitSize) - ParentLine->length +
				   group->nRanges * sizeof (TypRange) +
				   group->nSpareRanges * sizeof (TypRange)) / sizeof (TypRange) - 1;

	  RangePtr = GetRangePtr(group);

	  for (unsigned int i = 0 ; i < nPrevRanges ; i++) {
		if (PrevRangePtr[i].First <= PrevRangePtr[i].Last) {
		  if (PrevRangePtr[i].First < group->ServerFirst) {
			if (PrevRangePtr[i].Last < group->ServerFirst - 1) {
			  InSeen = FALSE;
			  *(RangePtr++) = PrevRangePtr[i];
			  (group->nRanges)++;
			} else {
			  InSeen = TRUE;
			  MyRange.First = PrevRangePtr[i].First;
			  MyRange.Last = group->ServerFirst - 1;
			  break;
			}
		  } else {
			InSeen = FALSE;
			break;
		  }
		}
	  }

	  if (InSeen == FALSE) {
		InSeen = TRUE;
		MyRange.First = group->ServerFirst;
	  }
	}
#endif	// TANAKA Goh (newsrc patch)

    /* Get the first line in this document.
     * If it cannot be found, just set Last=First and skip the
     * proceeding processing.  Otherwise, assume we've seen everything
     * up to but not including the first article in the document.
     */

    /*  LockLine (Doc->hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr); */

d1708 6
a1713 13
    if (!Doc->TotalLines) {
#if 0	// TANAKA Goh (newsrc patch)
      MyRange.Last = 1;
#else	// TANAKA Goh (newsrc patch)
	  MyRange.Last = group->ServerLast;

	  if (MyRange.First <= MyRange.Last) {
		*(RangePtr++) = MyRange;
		(group->nRanges)++;
	  }
#endif	// TANAKA Goh (newsrc patch)
    }
    else {
d1715 7
a1721 48
      MyRange.Last = header->number - 1;

      /* Loop to scan through the document, fabricating article ranges.*/
      do {
#if 1	// TANAKA Goh (newsrc patch)
		header_p	prev_header = header;
#endif	// TANAKA Goh (newsrc patch)

        header = (header_elt (headers, artindex));
#if 1	// TANAKA Goh (newsrc patch)
		if (header->number - prev_header->number > 1) {
		  if (InSeen) {
			/* Continuing a sequence of seen articles.            */
			MyRange.Last = header->number - 1;
		  }
		  else {
			/* Starting a new sequence of seen articles.          */
			MyRange.First = prev_header->number + 1;
			MyRange.Last = header->number - 1;
			InSeen = TRUE;
		  }
		}
#endif	// TANAKA Goh (newsrc patch)
        if (header->Seen) {
          if (InSeen) {
            /* Continuing a sequence of seen articles.*/
            MyRange.Last = header->number;
          }
          else {
            /* Starting a new sequence of seen articles.*/
            MyRange.First = header->number;
            MyRange.Last = header->number;
            InSeen = TRUE;
          }
        }
        else {
          if (InSeen) {
            /* Ending a sequence of seen articles.*/
            InSeen = FALSE;
            if (MyRange.First <= MyRange.Last) {
              *(RangePtr++) = MyRange;
              (group->nRanges)++;
            }
          }
          else {
            /* Continuing a sequence of unseen articles.*/
          }
        }
d1723 2
a1724 28
      while ((group->nRanges < maxRanges) &&
             ((++artindex < Doc->TotalLines)));

#if 0	// TANAKA Goh (newsrc patch)
      if (InSeen) {
        *(RangePtr++) = MyRange;
        (group->nRanges)++;
      }
#else	// TANAKA Goh (newsrc patch)
	  if (InSeen) {
		MyRange.Last = group->ServerLast;

		if (MyRange.First <= MyRange.Last) {
		  *(RangePtr++) = MyRange;
		  (group->nRanges)++;
		}
	  } else {
		MyRange.First = header->number + 1;
		MyRange.Last = group->ServerLast;

		if (MyRange.First <= MyRange.Last) {
		  *(RangePtr++) = MyRange;
		  (group->nRanges)++;
		}
	  }
#endif	// TANAKA Goh (newsrc patch)
    }

d1727 1
a1727 6
  unlock_headers (header_handle, thread_handle);

  MyLength = CalcGroupLen(group);
  LinePtr->length = MyLength;
  GroupLenPtr(LinePtr) = MyLength;
  ReplaceLine (LinePtr, &ParentBlock, &ParentLine);
a1728 1
  GlobalFreePtr (LinePtr);
d1732 2
d1768 1
a1768 1
//         return ((long)DocPtr->TotalLines-1);
a1773 2
/* this has been changed to use header_elt (SMR) */

a1777 1

d1779 1
a1779 1

d1781 1
a1781 1
    temp = (header_elt (headers, artindex))->subject;
d1812 1
a1812 1
    temp = ThisHeader->subject;
d1884 1
a1884 2
  static BOOL extending = FALSE;
  int VertLines;
d1906 1
a1906 1
void
a1911 1

d1987 2
a1988 3
    LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
              ThisDoc->ParentLineID, &BlockPtr, &LinePtr);

d2010 1
a2010 1
      group->HighestPrevSeen = group->ServerLast;
d2012 1
a2012 1

d2018 8
a2025 2
#if 0
          /* Is this necessary? SMR 941221 */
a2026 1
#endif
d2032 2
d2125 1
a2125 1
    tbButton[j].idCommand = IDM_HELP;
d2176 1
a2176 1
    EnableMenuItem (hSubMenu, IDM_DESELECT_MATCH, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
d2184 1
a2184 1
     * option after retrieving this group article-list
d2287 1
a2287 1
            if(ART_SELECTED & hp->Selected)
d2299 1
a2299 1

d2305 1
a2305 1

d2321 1
a2321 1
    case IDM_MARK_ALL:
d2323 1
d2337 1
a2337 1

d2342 1
a2342 1

d2347 1
a2347 1

d2352 1
a2352 1

d2356 1
a2356 1
      break;
a2362 1
        WVArticleAction aa;
d2373 2
a2374 1
        if(DialogBox(hInst, "ActionDlg", hWnd, lpfnWinVnArticleActionDlg))
d2376 2
a2377 3
            g_action.ActOnArticles(group, headers);
            aa.ReadActions(CurrentGroup);
            aa.ActOnArticles(group, headers);
d2403 1
a2403 1
      CheckMenuItem(hMenu, iSortOption, MF_BYCOMMAND|MF_CHECKED);
d2558 1
a2558 1

d2592 9
a2600 4
#if 0
            /* is this necessary ? SMR 941221 */
            unlock_headers (group->header_handle, group->thread_handle);
#endif
d2606 2
d2635 1
a2635 1
      /* the following msgs are sent by wvutil engine on completion
d2641 1
a2641 1
	  SetCursor (hArrowCursor);	//shimomai
d2659 1
a2659 1
             * on what the user selected in dialog.  If > 1st, always append
d2703 1
a2703 1
                (header_elt (headers, ThisDoc->savingArtIndex))->Selected == FALSE) {
d2710 1
a2710 1
                (header_elt (headers, ThisDoc->savingArtIndex))->Selected == FALSE) {
d2713 1
a2713 1

d2719 1
a2719 1
                     sprintf (mybuf, "Done! Saved %d article%c to file %s",ThisDoc->numArtsSaved,
d2735 1
a2735 1
                sprintf (mybuf, "Saved %d article%c to file %s",ThisDoc->numArtsSaved,
d2748 1
a2748 1
                (header_elt (headers, ThisDoc->savingArtIndex))->Seen = ART_SEEN;
d2779 375
@


1.77
log
@fix multiple save to file from pausing at a message box with
locked memory after a multiple save operation
@
text
@d4 1
a4 1
 *  MODULE    :  WVGROUP.C                                          *
d21 1
a21 1
 *                                       article from the server    * 
d46 1
a46 1
 * $Id: wvgroup.cpp 1.76 1995/11/07 23:02:35 dumoulin Exp $
d51 3
a53 3
#include <windowsx.h>			// for GlobalFreePtr (JSC)
extern "C"
{
d56 1
a56 1
}
d60 1
a60 1
#include "wvtb\wvtb.h"			/* for toolbar */
d62 2
d66 13
d80 2
d95 1
a95 1
  PAINTSTRUCT ps;				/* paint structure          */
d97 2
a98 2
  HDC hDC;						/* handle to display context */
  RECT clientRect;				/* selection rectangle      */
d114 1
d123 4
a126 4
	if (GroupDocs[ih].hDocWnd == hWnd) {
	  found = TRUE;
	  ThisDoc = &(GroupDocs[ih]);
	}
d130 1
a130 1
	ThisDoc = CommDoc;
d138 12
a149 1
	break;
d151 6
a156 6
	GetClientRect (hWnd, &clientRect);
	ThisDoc->ScXWidth = clientRect.right;
	ThisDoc->ScYHeight = clientRect.bottom;
	ThisDoc->ScYLines = (RectHeight (clientRect) - TopSpace) / LineHeight;
	ThisDoc->ScXChars = (RectWidth (clientRect) - SideSpace) / CharWidth;
	break;
d159 1
a159 1
	return (SendMessage (ThisDoc->hWndFrame, message, wParam, lParam));
d162 22
a183 15
	/* See if this key should be mapped to a scrolling event
	 * for which we have programmed the mouse.  If so,
	 * construct the appropriate mouse call and call the mouse code.
	 */
	LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
			  ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
	group = GetGroup(LinePtr);

	if (group->header_handle && group->total_headers > 0) {
	  header_handle = group->header_handle;
	  thread_handle = group->thread_handle;
	  headers = lock_headers (header_handle, thread_handle);
	}
	else
	  headers = NULL;
d185 3
a187 4
	switch (wParam) {
	case VK_F6:
	  NextWindow (ThisDoc->hDocWnd, ThisDoc->DocType);
	  break;
d189 10
a198 10
	case VK_DOWN:
	  if (headers != NULL) {
		/* move one line down from Active line */
		if (ThisDoc->ActiveLineID >= 0 &&
			ThisDoc->ActiveLineID < group->total_headers - 1) {
		  SetGroupSelections (ThisDoc, headers, ThisDoc->ActiveLineID + 1);
		  AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
		}
	  }
	  break;
d200 9
a208 9
	case VK_UP:
	  if (headers != NULL) {
		/* move one line up from Active line */
		if (ThisDoc->ActiveLineID > 0) {
		  SetGroupSelections (ThisDoc, headers, ThisDoc->ActiveLineID - 1);
		  AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
		}
	  }
	  break;
d210 1
a210 1
	case VK_HOME:
d212 4
a215 4
		SetGroupSelections (ThisDoc, headers, 0);
		AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
	  }
	  break;
d217 1
a217 1
	case VK_END:
d219 4
a222 4
		SetGroupSelections (ThisDoc, headers, group->total_headers - 1);
		AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
	  }
	  break;
d224 1
a224 1
	case VK_NEXT: 
d226 10
a235 10
		if (ThisDoc->ActiveLineID >= 0 &&
			ThisDoc->ActiveLineID < group->total_headers - 1) {
		  stop = min (ThisDoc->ActiveLineID + (long) ThisDoc->ScYLines,
					  group->total_headers - 1);
		  for (il = ThisDoc->ActiveLineID + 1; il < stop; il++);
		  SetGroupSelections (ThisDoc, headers, il);
		  AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
		}
	  }
	  break;
d237 1
a237 1
	case VK_PRIOR:
d239 17
a255 6
		if (ThisDoc->ActiveLineID > 0) {
		  stop = max (0, ThisDoc->ActiveLineID - (long) ThisDoc->ScYLines);
		  for (il = ThisDoc->ActiveLineID - 1; il > stop; il--);
		  SetGroupSelections (ThisDoc, headers, il);
		  AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
		}
d259 10
d270 1
a270 10
	default:
	  CtrlState = GetKeyState (VK_CONTROL) < 0;
	  for (j = 0; j < NUMKEYS; j++) {
		if (wParam == key2scroll[j].wVirtKey &&
			CtrlState == key2scroll[j].CtlState) {
		  SendMessage (hWnd, key2scroll[j].iMessage,
					   key2scroll[j].wRequest, 0L);
		  break;
		}
	  }
d272 1
a272 3
	}

	break;
d277 9
a285 6
//	int X, Y;
	/* Carriage Return means the same as double-clicking
	 * on where the cursor is currently pointing.
	 */
	if (wParam == '\r') {
//	  if (ArtListMultiSelect) {
d287 9
a295 9
		artindex = ThisDoc->ActiveLineID;
		goto getarticle1;
	  }
//	  else {
//		GetCursorPos (&ptCursor);
//		ScreenToClient (hWnd, &ptCursor);
//		X = ptCursor.x;
//		Y = ptCursor.y;
//		goto getarticle;
d297 4
a300 4
//	  }
	}
	else {
	}
d302 1
a302 1
	break;
d305 1
a305 1
	/* double right click will mark all up to here as read */
d307 2
a308 2
	X = LOWORD (lParam);
	Y = HIWORD (lParam);
d310 5
a314 5
	artindex = NewCursorToTextLine (X, Y, ThisDoc);
	if (artindex > -1) {
	  article_operation (ThisDoc, artindex, mark_read_to_here);
	  InvalidateRect (hWnd, NULL, FALSE);
	}
d316 1
a316 1
	break;
d320 14
a333 14
	/* Single middle click or right click will toggle the read/unread status 
	   of the article */
	DragMouseAction = DRAG_NONE;
	if (!CommBusy || (CommDoc != ThisDoc || CommDecoding)) {
	  BOOL status;
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  artindex = NewCursorToTextLine (X, Y, ThisDoc);
	  if (artindex > -1) {
		status = article_operation (ThisDoc, artindex, toggle_read_unread);
		DragMouseAction = status ? SEEN_SELECT : SEEN_DESELECT;
		InvalidateRect (hWnd, NULL, FALSE);
		SetCapture (hWnd);		// release capture on button up
d335 3
a337 3
	  }
	}
	break;
d340 11
a350 52
	/*  Clicking the left button on an article name toggles the
	 *  selected/not selected status of that group.
	 *  Currently selected groups are displayed in reverse video.
	 */

	DragMouseAction = DRAG_NONE;
	if (!CommBusy || (CommDoc != ThisDoc || CommDecoding)) {
	  BOOL status;
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

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

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

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

		  if (wParam & MK_SHIFT)
			AffectSelected (ThisDoc, DESELECT, EXTEND);
		  else
			ThisDoc->AnchorLineID = artindex;
		}
		else {
		  if (MK_SHIFT & wParam) {
			status = article_operation (ThisDoc, artindex, toggle_read_unread);
			DragMouseAction = status ? SEEN_SELECT : SEEN_DESELECT;
		  }
		  else {
			status = article_operation (ThisDoc, artindex, toggle_selected);
			DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
		  }
		}
d352 1
a352 2
		InvalidateRect (hWnd, NULL, FALSE);
	  }
d354 54
a407 4
	else {						/* let user cheat while retrieving to see headers so far */
	  InvalidateRect (hWnd, NULL, FALSE);
	}
	break;
d410 21
a430 5
	/*  Letting up on the left button on an article name 
	 *  gets us out of Dragging mode   */
	ReleaseCapture ();
	DragMouseAction = DRAG_NONE;
	break;
d434 5
a438 5
	/*  Letting up on the middle button on an article name
	 *  gets us out of Dragging mode   */
	ReleaseCapture ();
	DragMouseAction = DRAG_NONE;
	break;
d441 5
a445 5
	/*  Double-clicking on an article subject creates an "Article"
	 *  window, whose purpose is to display the article.
	 */
	if (TestCommBusy (hWnd, "Can't Retrieve Article"))
	  break;
d447 2
a448 2
	X = LOWORD (lParam);
	Y = HIWORD (lParam);
d451 1
a451 1
	artindex = NewCursorToTextLine (X, Y, ThisDoc);
d455 14
a468 4
	if (artindex > -1) {
	  if (!ArtListMultiSelect)
		article_operation (ThisDoc, artindex, selected_false);
	  ViewArticle (ThisDoc, artindex, NO_REUSE, SHOW, NO_ID);
a469 1

d473 29
a501 15
	/*  Code to drag the mouse and change the select/not selected
	 *  status of that group.
	 */

	if ((DragMouseAction != DRAG_NONE) &&
		(!CommBusy || CommDoc != ThisDoc || CommDecoding)) {
	  X = LOWORD (lParam);
	  Y = HIWORD (lParam);

	  artindex = NewCursorToTextLine (X, Y, ThisDoc);
	  if (artindex > -1) {
		if (ArtListMultiSelect && 
			DragMouseAction != SEEN_SELECT && DragMouseAction != SEEN_DESELECT) {
		  ThisDoc->ActiveLineID = artindex;
		  AffectSelectedOnScreen (ThisDoc, DESELECT, EXTEND);
d503 1
a503 6
		else {
		  switch (DragMouseAction) {
		  case DRAG_SELECT:
			article_operation (ThisDoc, artindex, selected_true);
			InvalidateRect (hWnd, NULL, FALSE);
			break;
d505 12
a516 4
		  case DRAG_DESELECT:
			article_operation (ThisDoc, artindex, selected_false);
			InvalidateRect (hWnd, NULL, FALSE);
			break;
d518 1
a518 4
		  case SEEN_SELECT:
			article_operation (ThisDoc, artindex, seen_true);
			InvalidateRect (hWnd, NULL, FALSE);
			break;
d520 29
a548 5
		  case SEEN_DESELECT:
			article_operation (ThisDoc, artindex, seen_false);
			InvalidateRect (hWnd, NULL, FALSE);
			break;
		  }
d550 5
a554 5
		}
	  }
	}

	break;
d557 2
a558 2
	NewScrollIt (ThisDoc, wParam, lParam);
	break;
d561 2
a562 2
	HScrollIt (ThisDoc, wParam, lParam);
	break;
d565 166
a730 149
	{
	  HANDLE hBlock;
	  SIZE sz;
	  int X, Y, MyLen;
	  unsigned int Offset;
	  int VertLines, HorzChars;
	  int EndofDoc = FALSE;
	  int RangeHigh, CurPos;
	  int indicatorwidth, Xtext;
	  char indicator;
	  TypLineID artindex;
	  long int artnum;
	  TypBlock far *NetBlockPtr;
	  TypLine far *NetLinePtr;
	  TypGroup far *group;
	  header_p headers;
	  header_p header;
	  HANDLE header_handle;
	  HANDLE thread_handle;
	  char scratch_line[MAXINTERNALLINE];
	  char date_string[80];
	  unsigned long OldHighestSeen;
	  COLORREF MyColors[4], MyBack[4];
	  RECT aRect;
	  int MyColorMask = 0;
	  int PrevColorMask = MyColorMask;
	  HBRUSH hOldBrush;
	  HANDLE hOldFont;

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

#define SEEN_MASK 1
#define SELECT_MASK 2
	  hDC = BeginPaint (hWnd, &ps);
	  GetClientRect (hWnd, &clientRect);
	  hOldFont = SelectObject (hDC, hListFont);

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

	  HorzChars = ThisDoc->ScXChars;

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

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

	  MyBack[1] = ListBackgroundColor;

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

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

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

		else
		  MyColors[2] = ArticleUnSeenColor;

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

		else
		  MyColors[3] = ArticleSeenColor;

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

	  SetTextColor (hDC, MyColors[MyColorMask]);
	  SetBkColor (hDC, MyBack[MyColorMask]);

	  /* Update the scroll bar thumb position.                 */

	  //RangeHigh = ThisDoc->TotalLines - VertLines;
	  RangeHigh = ThisDoc->TotalLines - ThisDoc->ScYLines;
	  if (RangeHigh < 0) {
		RangeHigh = 0;
		ThisDoc->TopLineOrd = 0;
		VertLines = ThisDoc->TotalLines;
	  }
	  CurPos = 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);

	  GetTextExtentPoint (hDC, "s 99999 ", 8, &sz);
	  indicatorwidth = sz.cx;

	  X = SideSpace - ThisDoc->ScXOffset * (CharWidth + 2);
	  Xtext = X + indicatorwidth;
	  Y = StartPen;

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

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

		OldHighestSeen = group->HighestPrevSeen;
		UnlockLine (NetBlockPtr, NetLinePtr, &hBlock, &Offset, &MyLineID);
		do {
		  header = header_elt (headers, artindex);
 		  artnum = header->number;
		  indicator = ' ';
		  if (header->Seen) {
			indicator = 's';
		  }
		  else if (OldHighestSeen) {
			if (header->number > OldHighestSeen) {
			  indicator = 'n';
			}
d733 1
a733 2
		  if (ThisDoc->FindOffset == (unsigned) artindex)
			indicator = '>';
d735 91
a825 69
		  _snprintf (scratch_line, MAXINTERNALLINE, "%c%5Flu %-5.5Fs %-18.18Fs %4Fd %-*s%-Fs ",
				   indicator,
				   header->number,
				   StringDate (date_string, header->date),
				   header->from,
				   header->lines,
				   header->thread_depth * 2,
				   "",
				   (ThreadFullSubject || !(header->thread_depth))
				   ? header->subject
				   : ThreadDepthIndicator
			);

		  MyLen = lstrlen (scratch_line);

		  /* Figure out the color of this line. */
		  if (header->Seen) {
			if (header->Selected)
			  MyColorMask = 3;
			else
			  MyColorMask = 1;
		  }
		  else {
			if (header->Selected)
			  MyColorMask = 2;
			else
			  MyColorMask = 0;
		  }


		  if (MyColorMask != PrevColorMask) {
			SetTextColor (hDC, MyColors[MyColorMask]);
			SetBkColor (hDC, MyBack[MyColorMask]);
			PrevColorMask = MyColorMask;
		  }

		  /* Now write out the line. */
		  SetRect (&aRect, 0, Y, clientRect.right, Y + LineHeight);

		  ExtTextOut (hDC, X, Y, ETO_OPAQUE | ETO_CLIPPED, &aRect,
					  scratch_line, MyLen, (LPINT) NULL);
		  if (ThisDoc->ActiveLineID == -1L)
			ThisDoc->ActiveLineID = artindex;
		  if (ThisDoc->ActiveLineID == artindex && ArtListMultiSelect) {
			DrawFocusRect (hDC, &aRect);
		  }

		  Y += LineHeight;
		  artindex++;

		}
		while (--VertLines > 0);

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

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

	  hOldBrush = (HBRUSH) SelectObject (hDC, hListBackgroundBrush);
	  PatBlt (hDC, 0, Y, clientRect.right - 1, clientRect.bottom - Y, PATCOPY);
	  PatBlt (hDC, 0, 0, clientRect.right - 1, StartPen, PATCOPY);
	  SelectObject (hDC, hOldBrush);
	  EndPaint (hWnd, &ps);
	  break;
	}
d828 1
a828 1
	return (DefWindowProc (hWnd, message, wParam, lParam));
d836 1
a836 1
 *  also make sure no child articles are active comm... jsc 11/1/94 
d838 1
a838 1
void 
d845 7
a851 7
	stop = (ThisDoc == CommDoc);
	for (i = 0; !stop && i < MAXARTICLEWNDS; i++) {
	  if (ArticleDocs[i].InUse && CommDoc == &ArticleDocs[i] &&
		  ArticleDocs[i].ParentDoc == ThisDoc) {
		stop = TRUE;
	  }
	}
d855 3
a857 3
	MessageBox (hWnd,
				"Please wait until group activity is complete",
				"Cannot close group window", MB_OK | MB_ICONSTOP);
d859 5
a863 5
	SetHandleBkBrush (ThisDoc->hDocWnd, (HBRUSH) GetStockObject (WHITE_BRUSH));
	if (SaveNewsrcOnClose && NewsrcDirty) WriteNewsrc ();
	DestroyWindow (hWnd);
	/* invalidate main window, so '*' will be updated, if appropriate */
	InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
d867 1
a867 1
void 
d873 1
a873 1
  hSubMenu = GetSubMenu (hMenu, 0);		// Articles menu
d877 1
a877 1
			   (MailCtrl.enableMail == MF_ENABLED));
d881 1
a881 1
 * dis/enable menu items which depend on group list being completely 
d893 1
a893 1
  hSubMenu = GetSubMenu (hMenu, 0);		// Articles menu
d905 1
a905 1
  hSubMenu = GetSubMenu (hMenu, 1);		// Sort menu
d910 1
a910 1
//  EnableMenuItem (hSubMenu, IDM_SORT_THREADS, mode);	// handled in INITMENU
d914 1
a914 1
  hSubMenu = GetSubMenu (hMenu, 2);		// Search menu
d945 1
a945 1
  hSubMenu = GetSubMenu (hMenu, 0);		// Articles menu
d949 1
d953 1
a953 1
  hSubMenu = GetSubMenu (hMenu, 1);		// Sort menu
d964 1
a964 1
	mode = TRUE;
d966 1
a966 1
	mode = FALSE;
d973 42
d1023 5
a1027 5
	header->Selected = value;
	if (value)
	  (Doc->SelectedLines)++;
	else
	  (Doc->SelectedLines)--;
d1030 2
a1031 2
	/* this is a kludge to update the toolbar */
	SendMessage (Doc->hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
d1039 2
a1040 2
				   BOOL (*art_fun) (TypDoc far * Doc, header_p headers,
									TypGroup * group, long artindex))
d1054 1
a1054 1
			Doc->ParentLineID, &BlockPtr, &LinePtr);
d1060 3
a1062 3
	thread_handle = GroupDoc->thread_handle;
	headers = lock_headers (header_handle, thread_handle);
	result = art_fun (Doc, headers, GroupDoc, artindex);
d1064 2
a1065 2
	unlock_headers (header_handle, thread_handle);
	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
d1072 2
a1073 2
					TypGroup * GroupDoc,
					long artindex)
d1078 3
a1080 3
	header_p header = header_elt (headers, artindex);
	header->Seen = (header->Seen ? FALSE : TRUE);
	return (header->Seen);
d1087 2
a1088 2
				   TypGroup * GroupDoc,
				   long artindex)
d1094 9
a1102 9
	int i;
	for (i = 0; i <= artindex; i++) {
	  header = header_elt (headers, i);
	  header->Seen = TRUE;
	  // added by jlg to get rid of erroneous '*' in group window
	  if (header->number > GroupDoc->HighestPrevSeen)
		GroupDoc->HighestPrevSeen = header->number;
	}
	return (TRUE);
d1109 2
a1110 2
			   TypGroup * GroupDoc,
			   long artindex)
d1116 2
a1117 2
	header = header_elt (headers, i);
	header->Seen = TRUE;
d1125 2
a1126 2
			   TypGroup * GroupDoc,
			   long artindex)
d1132 4
a1135 4
	header = header_elt (headers, i);
	if (header->Selected) {
		header->Seen = TRUE;
	}
d1142 2
a1143 2
			   TypGroup * GroupDoc,
			   long artindex)
d1149 4
a1152 4
	header = header_elt (headers, i);
	if (header->Seen) { 
	    SelectHeader (Doc, header, TRUE);
	}
d1159 2
a1160 2
			   TypGroup * GroupDoc,
			   long artindex)
d1166 4
a1169 4
	header = header_elt (headers, i);
	if (header->Seen == FALSE) {
		SelectHeader (Doc, header, TRUE);
	}
d1176 2
a1177 2
			   TypGroup * GroupDoc,
			   long artindex)
d1183 3
a1185 3
	header = header_elt (headers, i);
	if (header->Seen == FALSE) {
		    SelectHeader (Doc, header, FALSE);
d1193 2
a1194 2
			   TypGroup * GroupDoc,
			   long artindex)
d1200 4
a1203 4
	header = header_elt (headers, i);
	if (header->Seen) {
		SelectHeader (Doc, header, FALSE);
	}
d1210 2
a1211 2
			   TypGroup * GroupDoc,
			   long artindex)
d1217 4
a1220 4
	header = header_elt (headers, i);
	if (header->Selected) {
		header->Seen = FALSE;
	}
d1227 2
a1228 2
				 TypGroup * GroupDoc,
				 long artindex)
d1233 3
a1235 3
	header_p header = header_elt (headers, artindex);
	SelectHeader (Doc, header, header->Selected ? FALSE : TRUE);
	return (header->Selected);
d1242 2
a1243 2
			TypGroup * GroupDoc,
			long artindex)
d1248 3
a1250 3
	header_p header = header_elt (headers, artindex);
	header->Seen = FALSE;
	return (TRUE);
d1257 2
a1258 2
		   TypGroup * GroupDoc,
		   long artindex)
d1263 3
a1265 3
	header_p header = header_elt (headers, artindex);
	header->Seen = TRUE;
	return (TRUE);
d1272 2
a1273 2
			   TypGroup * GroupDoc,
			   long artindex)
d1278 3
a1280 3
	header_p header = header_elt (headers, artindex);
	SelectHeader (Doc, header, TRUE);
	return (TRUE);
d1287 2
a1288 2
				TypGroup * GroupDoc,
				long artindex)
d1293 3
a1295 3
	header_p header = header_elt (headers, artindex);
	SelectHeader (Doc, header, FALSE);
	return (TRUE);
d1340 4
a1343 1
			  &BlockPtr, &LinePtr)) {
d1345 34
a1378 1
  	GroupDoc = GetGroup(LinePtr);
d1380 1
a1380 25
  	if (articleId == NO_ID)		/* if retrieving artindex, only try if in range */
		if ((artindex < 0) || (artindex >= GroupDoc->total_headers)){
	   	UnlockLine (BlockPtr, LinePtr, &Doc->hParentBlock,
	      	           &Doc->ParentOffset, &Doc->ParentLineID);
	   	return;
	   	}

  	header_handle = GroupDoc->header_handle;
  	thread_handle = GroupDoc->thread_handle;
  	headers = lock_headers (header_handle, thread_handle);
  	header = header_elt (headers, artindex); 
             
  	if ((articleId == NO_ID) &&
      	(header->ArtDoc != NULL) &&
      	(header->ArtDoc->CountedLines == header->lines)){ 
      
		/* We already have a document containing the article */
		/* so just activate it.                */

		if (showArt) {
	 	setArticleFocus (header->ArtDoc->hWndFrame);
	  	ShowWindow (header->ArtDoc->hWndFrame, SW_SHOWNORMAL);
		}
		else
	  	ShowWindow (header->ArtDoc->hWndFrame, SW_SHOWMINNOACTIVE);
d1382 38
a1419 44
		InvalidateRect (header->ArtDoc->hWndFrame, NULL, FALSE);
		InvalidateRect (header->ArtDoc->hDocWnd, NULL, FALSE);
		SendMessage (hWndGroup, (UINT) WM_COMMAND, (WPARAM) ID_ARTICLE_RETRIEVE_COMPLETE, 0L);
		goto endit;
  	} 

  	if (TestCommBusy (hWndGroup, "Can't Retrieve Article"))
		goto endit;

  	newdoc = FALSE;

  	if ((NewArticleWindow && !reuse) || !ActiveArticleDoc || 
  				!(ActiveArticleDoc->InUse)) {
		found = FALSE;
		for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++) {
	  	if (!ArticleDocs[docnum].InUse) {
			found = TRUE;
			newdoc = TRUE;
			CommDoc = &(ArticleDocs[docnum]);
			break;
	  	}
		}
		if (!found) {
	  	MessageBox (hWndGroup,
					  "You have too many article windows active;\n"
					  "Close one or uncheck the option"
				 	 "'New Window for each Article'.",
				 	 "Can't open new window", MB_OK | MB_ICONASTERISK);
	  	goto endit;
		}
  	}
  	else {
		/* Must reuse old window for this article.         */
		ActiveArticleDoc->LongestLine = 0;
		ActiveArticleDoc->ScXOffset = 0;
		ActiveArticleDoc->TextSelected = FALSE;
		SetArticleMenus (ActiveArticleDoc, DISABLE);
		CommDoc = ActiveArticleDoc;
		/* sever the article/artindex connection */
		if (CommDoc->ParentDoc == Doc)
			(header_elt (headers, CommDoc->ParentOffset))->ArtDoc = (TypDoc far *) NULL;
		else {
			SeverArticleParent(CommDoc);
		}
d1421 13
a1433 17
	/* clear out old doc and clear its window*/
		FreeDoc (CommDoc);
		InvalidateRect(CommDoc->hDocWnd, NULL, FALSE);
  	}

  	header->Seen = TRUE;
  	NewsrcDirty = TRUE;
  	InvalidateRect (hWndGroup, NULL, FALSE);

  	if (articleId == NO_ID)
		lpsz = (char far *) header->subject;
  	else
		lpsz = articleId;

  	strcpy (mybuf, "Retrieving \"");
  	lstrcat (mybuf, lpsz);
  	lstrcat (mybuf, "\"");
d1436 1
a1436 1
	char poschars[MAXINTERNALLINE];
d1438 19
a1456 19
	/* Compute default screen position. */
	num = (WrapIncomingArticleText) ? WrapIncomingArticleTextLength : 88;
	num = min (max (15, WrapIncomingArticleTextLength), 88);
	if (xScreen > num * ArtCharWidth) {
	  width = num * ArtCharWidth;
	}
	else {
	  width = xScreen - 1 * ArtCharWidth;
	}
	x = xScreen - width;
	y = (int) (yScreen * 3 / 8);
	height = (int) (yScreen * 5 / 8) - (1 * ArtLineHeight);

	/* If the screen position has been saved, use that instead. */
	GetPrivateProfileString (ARTICLE, "ArticleWindowPos", "!",
							 poschars, MAXINTERNALLINE, szAppProFile);
	if (poschars[0] != '!') {
	  sscanf (poschars, "%d,%d,%d,%d", &x, &y, &width, &height);
	}
d1458 11
a1468 11
	hWndArt = CreateWindow ("WinVnArtFrame",
							mybuf,
							WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
							x + (docnum * ArtCharWidth),
							y + (docnum * ArtLineHeight),
							width,
							height,
							NULL,
							NULL,
							hInst,
							NULL);
d1470 2
a1471 2
	if (!hWndArt)
	  return;					/* ??? */
d1474 2
a1475 2
	hWndArt = CommDoc->hWndFrame;
	SetWindowText (CommDoc->hWndFrame, mybuf);
d1485 1
a1485 1
  CommDoc->LastSeenLineID = artindex;	/* Keep an index with the article */
d1488 2
a1489 2
	setArticleFocus (hWndArt);
	ShowWindow (hWndArt, newdoc?SW_SHOWNORMAL:SW_SHOW);
d1492 1
a1492 1
	ShowWindow (hWndArt, SW_SHOWMINNOACTIVE);
d1495 1
a1495 1
	header->ArtDoc = CommDoc;
d1497 1
a1497 1
	header->ArtDoc = (TypDoc *)NULL;
d1502 1
a1502 1
  CommDoc->CountedLines = header->lines; 
d1507 5
a1511 5
//	if (header->number > GroupDoc->HighestPrevSeen) {
//	  GroupDoc->HighestPrevSeen = header->number;
//	}
	sprintf (mybuf, "%ld", header->number);
	InitiateReceiveArticle (Doc, mybuf);
d1514 1
a1514 1
	InitiateReceiveArticle (Doc, articleId);
d1517 1
a1517 1
  hMenu = GetMenu (hWndArt); 
d1521 1
a1521 1
      (stricmp (header->from, ReplyTo) == 0)) 
d1560 1
a1560 1
  
d1566 1
a1566 1
			  &BlockPtr, &LinePtr)){
d1574 5
a1578 5
  	  CommState = ST_GROUP_REJOIN;
	  strcpy (mybuf, "GROUP ");
	  lstrcat (mybuf, lpszGroupName);
	  mylstrncpy (CurrentGroup, lpszGroupName, MAXGROUPNAME);
	  PutCommLine (mybuf);
d1605 15
a1619 15
	if (doc->ParentDoc &&
	    LockLine (doc->ParentDoc->hParentBlock,
				  doc->ParentDoc->ParentOffset,
				  doc->ParentDoc->ParentLineID,
				  &BlockPtr, &LinePtr)) {

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

	  (header_elt (headers, doc->LastSeenLineID))->ArtDoc = (TypDoc *) NULL;
	  UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	  unlock_headers (header_handle, thread_handle);
	}
d1637 4
a1640 4
	if (ArticleDocs[iart].InUse && ArticleDocs[iart].ParentDoc == GroupDoc) {
	  ArticleDocs[iart].ParentDoc = (TypDoc far *) NULL;
	  ArticleDocs[iart].hParentBlock = 0;
	}
d1691 1
a1691 1
			     &ParentBlock, &ParentLine);
d1695 1
a1695 1
	return;
d1702 5
a1706 5
	header_handle = group->header_handle;
	thread_handle = group->thread_handle;
	/* this can happen if user selects "Update from server" then cancel. */
	if (!header_handle)
	  return;
d1708 2
a1709 2
	set_index_to_identity (header_handle, thread_handle, group->total_headers);
	headers = lock_headers (header_handle, thread_handle);
d1711 1
d1716 1
a1716 1
				 group->nRanges * sizeof (TypRange) + 
d1721 43
d1765 4
a1768 11
	/* Get the first line in this document.
	 * If it cannot be found, just set Last=First and skip the
	 * proceeding processing.  Otherwise, assume we've seen everything
	 * up to but not including the first article in the document.
	 */

	/*  LockLine (Doc->hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr); */

	artindex = 0;
	if (!Doc->TotalLines) {
	  MyRange.Last = 1;
d1770 36
a1805 8
	else {
	  header = (header_elt (headers, artindex));
	  MyRange.Last = header->number - 1;

	  /* Loop to scan through the document, fabricating article ranges.*/
	  do {
		header = (header_elt (headers, artindex));
		if (header->Seen) {
d1807 2
a1808 2
			/* Continuing a sequence of seen articles.*/
			MyRange.Last = header->number;
d1811 3
a1813 3
			/* Starting a new sequence of seen articles.*/
			MyRange.First = header->number;
			MyRange.Last = header->number;
d1817 50
a1866 10
		else {
		  if (InSeen) {
			/* Ending a sequence of seen articles.*/
			InSeen = FALSE;
			*(RangePtr++) = MyRange;
			(group->nRanges)++;
		  }
		  else {
			/* Continuing a sequence of unseen articles.*/
		  }
d1869 2
a1870 2
	  while ((group->nRanges < maxRanges) &&
			 ((++artindex < Doc->TotalLines)));
a1871 6
	  if (InSeen) {
		*(RangePtr++) = MyRange;
		(group->nRanges)++;
	  }
	}
	
d1906 3
a1908 3
	  X < SideSpace) {
	/* Cursor is in no-man's-land at edge of window.               */
	return (-1);
d1911 11
a1921 11
	SelLine = (Y - TopSpace) / LineHeight;
	if ((unsigned int) SelLine >= DocPtr->ScYLines)		/* double-check */
	  return (-1);
	else {
	  result = (long) DocPtr->TopLineOrd + (long) SelLine;
	  if (result < (long) DocPtr->TotalLines)
		return (result);
	  else
//         return ((long)DocPtr->TotalLines-1); 
		return (-1);
	}
d1929 1
a1929 1
				long artindex, long num_headers)
d1935 3
a1937 3
	temp = (header_elt (headers, artindex))->subject;
	if (string_compare_insensitive (temp, HeaderDoc->SearchStr))
	  return (artindex);		/* return the index */
d1940 1
a1940 1
  return (-1);					/* not found */
d1951 2
a1952 2
	if (strnicmp (a, b, lenb) == 0)
	  return a;
d1966 6
a1971 6
	temp = ThisHeader->subject;
	if (string_compare_insensitive (temp, Doc->SearchStr)) {
	  SelectHeader (Doc, ThisHeader, value);
	  return TRUE;
	}
	break;
d1974 3
a1976 3
	SelectHeader (Doc, ThisHeader, value);
	return TRUE;
	break;
d1979 15
a1993 15
	if (index == Doc->ActiveLineID || index == Doc->AnchorLineID) {
	  if (extending == TRUE) {
		SelectHeader (Doc, ThisHeader, TRUE);
		extending = FALSE;
	  }
	  else {
		SelectHeader (Doc, ThisHeader, TRUE);
		if (Doc->ActiveLineID == Doc->AnchorLineID)
		  extending = FALSE;
		else
		  extending = TRUE;
	  }
	}
	else
	  SelectHeader (Doc, ThisHeader, extending);
d1995 2
a1996 2
	return TRUE;
	break;
d2012 1
a2012 1
			Doc->ParentLineID, &BlockPtr, &LinePtr);
d2019 4
a2022 4
	ThisHeader = header_elt (headers, index);
	if (AffectOneSelected (Doc, ThisHeader, index, value, compare)) {
	  num_affected++;
	}
d2042 1
a2042 1
			Doc->ParentLineID, &BlockPtr, &LinePtr);
d2049 1
a2049 1
	? (Doc->TotalLines - Doc->TopLineOrd) : Doc->ScYLines;
d2052 4
a2055 4
	ThisHeader = header_elt (headers, index);
	if (AffectOneSelected (Doc, ThisHeader, index, value, compare)) {
	  num_affected++;
	}
d2061 1
a2061 1
void 
d2070 16
a2085 16
  if (GetKeyState (VK_SHIFT) >= 0) {	/* shift is NOT pressed  */
	/* clear select status of all lines */
	if (MyDoc->SelectedLines == 1) {
	  article_operation (MyDoc, prevActive, selected_false);
	}
	else {
	  AffectSelected (MyDoc, DESELECT, NO_COMPARE);
	}
	/* set only current line selected */
	SelectHeader (MyDoc, ThisHeader, TRUE);
	/* set the current line to be the anchor */
	MyDoc->AnchorLineID = MyDoc->ActiveLineID;
  }
  else {						/* shift is pressed  */
	/* select all lines between ActiveLineID and AnchorLineID inclusive */
	AffectSelected (MyDoc, DESELECT, EXTEND);
d2108 2
a2109 3
  PAINTSTRUCT ps;				/* paint structure          */
  HDC hDC;						/* handle to display context */
  char szTmp[MAXGROUPNAME+4];
d2118 4
a2121 4
	if (GroupDocs[ih].hWndFrame == hWnd) {
	  found = TRUE;
	  ThisDoc = &(GroupDocs[ih]);
	}
d2125 1
a2125 1
	ThisDoc = CommDoc;
d2132 1
a2132 1
	return (DefWindowProc (hWnd, message, wParam, lParam));
d2135 3
a2137 3
	CloseGroupWnd (hWnd, ThisDoc);
	return (0);
	break;
d2140 2
a2141 2
	/* Unlink all the article windows that belong to this group */
	NumGroupWnds--;
d2143 2
a2144 2
	LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
			  ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
d2146 29
a2174 29
	{
	  /* Clear the pointer in the line for this group in the   */
	  /* NetDoc document.  This pointer currently points       */
	  /* to the current document, which we are wiping out      */
	  /* with the destruction of this window.                  */
	  TypGroup far *group = GetGroup(LinePtr);
	  long int CN_Total_Lines;

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

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

	  /* make the 'n' in the group-list go away */
	  group->HighestPrevSeen = group->ServerLast;
	  InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	  
	  /*CN  if (group->total_headers != 0)  */
	  if (CN_Total_Lines != 0) {	/* CN */
		UpdateSeenArts (ThisDoc);
		UnlinkArtsInGroup (ThisDoc);
		if (group->header_handle) {
d2176 2
a2177 2
		  /* Is this necessary? SMR 941221 */
		  unlock_headers (group->header_handle, group->thread_handle);
d2179 11
a2189 7
		  free_headers (group->header_handle, group->thread_handle);
		}
		group->header_handle = (HANDLE) NULL;
		group->thread_handle = (HANDLE) NULL;
		group->total_headers = 0;
	  }
	}
d2191 12
a2202 16
	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	/* Clear document                                        */
	FreeDoc (ThisDoc);

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

	for (j = MAXGROUPWNDS - 1; j >= 0; j--) {
	  if (GroupDocs[j].InUse) {
		ActiveGroupDoc = &(GroupDocs[j]);
		break;
	  }
	}
	return (0);
	break;
d2205 2
a2206 2
	/* create the child window and the toolbar  */
	GetClientRect (hWnd, &MyRect);
d2209 2
a2210 2
	i = j = 0;
	tbButton[j].iBitmap = 0;	// find
d2212 4
a2215 4
	tbButton[j].idCommand = IDM_FIND;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// find next
d2217 3
a2219 3
	tbButton[j].idCommand = IDM_FIND_NEXT_SAME;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
d2221 1
a2221 1
	tbButton[++j].iBitmap = 8;	// separator
d2223 2
a2224 2
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;
d2226 1
a2226 1
	tbButton[++j].iBitmap = ++i;	// New Article
d2228 4
a2231 4
	tbButton[j].idCommand = IDM_POST;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// New Mail
d2233 3
a2235 3
	tbButton[j].idCommand = IDM_MAIL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
d2237 1
a2237 1
	tbButton[++j].iBitmap = 8;	// separator
d2239 2
a2240 2
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;
d2242 1
a2242 1
	tbButton[++j].iBitmap = ++i;	// Save selected
d2244 3
a2246 3
	tbButton[j].idCommand = IDM_SAVE_SELECTED;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
d2248 1
a2248 1
	tbButton[++j].iBitmap = ++i;	// Decode selected
d2250 3
a2252 3
	tbButton[j].idCommand = IDM_DECODE_SELECTED;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
d2254 1
a2254 1
	tbButton[++j].iBitmap = 8;	// separator
d2256 2
a2257 2
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;
d2259 1
a2259 1
	tbButton[++j].iBitmap = ++i;	// Catch Up and Exit
d2261 4
a2264 4
	tbButton[j].idCommand = IDM_MARK_ALL;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
	tbButton[++j].iBitmap = ++i;	// Exit
d2266 3
a2268 3
	tbButton[j].idCommand = IDV_EXIT;
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_BUTTON;
d2270 3
a2272 3
	tbButton[++j].iBitmap = 8;	// separator
	tbButton[j].fsState = TBSTATE_ENABLED;
	tbButton[j].fsStyle = TBSTYLE_SEP;
d2274 2
a2275 2
	tbButton[++j].iBitmap = ++i;    /* Help */
    tbButton[j].idCommand = IDM_HELP; 	 
d2279 27
a2305 27
	ThisDoc->hWndTB = CreateToolbar (hWnd,
									 WS_BORDER | WS_VISIBLE,
									 (WORD) GetMenu (hWnd),
									 i + 1,
									 hInst,
									 IDB_VIEWTOOLBAR,
									 tbButton,
									 j + 1);

	ThisDoc->hWndFrame = hWnd;
	ThisDoc->hDocWnd = CreateWindow ("WinVnView",
									 NULL,
									 WS_CHILD | WS_VSCROLL | WS_HSCROLL,
									 0,		/* Initial X position */
									 TOOLBARHEIGHT,		/* Initial Y position */
									 RectWidth (MyRect),
			 RectHeight (MyRect) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
									 hWnd,
									 NULL,
									 hInst,
									 NULL);
	SetHandleBkBrush (ThisDoc->hDocWnd, hListBackgroundBrush);
	ShowWindow (ThisDoc->hDocWnd, SW_SHOWNORMAL);

	SetGroupMenus (ThisDoc, DISABLE);
	NumGroupWnds++;
	break;
d2308 2
a2309 2
	if((wParam == WA_ACTIVE) || (wParam == WA_CLICKACTIVE))
	  ActiveGroupDoc = ThisDoc;
d2311 1
a2311 1
	/* fall through to update toolbars */
d2314 40
a2353 38
	hMenu = GetMenu (hWnd);
	hSubMenu = GetSubMenu (hMenu, 0);
	i = (!CommBusy && ThisDoc->SelectedLines);

	EnableMenuItem (hSubMenu, IDM_SAVE_SELECTED, i ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_DECODE_SELECTED, (i && CodingState == INACTIVE) ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_DESELECT_ALL, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_MARK_SELECTED, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNMARK_SELECTED, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNMARK_READ_ARTICLES, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_UNMARK_UNREAD_ARTICLES, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_DESELECT_MATCH, ThisDoc->SelectedLines ? ENABLE_MENU : DISABLE_MENU);	
	EnableMenuItem (hSubMenu, IDM_UPDATE, CommBusy ? DISABLE_MENU : ENABLE_MENU);

	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_SAVE_SELECTED, i);
	SendMessage (ThisDoc->hWndTB, TB_ENABLEBUTTON, IDM_DECODE_SELECTED, (i && CodingState == INACTIVE));

	/* only allow sort by threads if the group has threads.  it's not enough
	 * to check threadp - since the user may have changed the threadp
	 * option after retrieving this group article-list 
	 */
	threadOk = FALSE;
	/*
	 * we check hParentBlock because this code can be called before the Document
	 * has been initialized via InitDoc
	 */
	if (ThisDoc->hParentBlock && (!CommBusy || CommDoc != ThisDoc)) {
	  LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
				ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
	  if (LinePtr) {
		GroupDoc = GetGroup(LinePtr);
		threadOk = GroupDoc->Threaded;
		UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	  }
	}
	hSubMenu = GetSubMenu (hMenu, 1);	/* sorting menu */
	EnableMenuItem (hSubMenu, IDM_SORT_THREADS, threadOk ? ENABLE_MENU : DISABLE_MENU);
	EnableMenuItem (hSubMenu, IDM_SORT_THREADSUB, threadOk ? ENABLE_MENU : DISABLE_MENU);
d2369 3
a2371 3
	if (message == WM_ACTIVATE)	// kludgeville
	  return (DefWindowProc (hWnd, message, wParam, lParam));
	break;
d2374 1
a2374 1
	StatBarGroupPopups (ThisDoc);
d2377 1
a2377 1
	break;
d2380 6
a2385 6
	/* paint status bar  */
	hDC = BeginPaint (hWnd, &ps);
	PaintStatbar (hWnd, hDC, ThisDoc);
	EndPaint (hWnd, &ps);
	return (DefWindowProc (hWnd, message, wParam, lParam));
	break;
d2388 10
a2397 10
	/* Store the new size of the window.                     */
	GetClientRect (hWnd, &MyRect);
	MoveWindow (ThisDoc->hDocWnd, 0, TOOLBARHEIGHT,
				RectWidth (MyRect),
			 RectHeight (MyRect) - TOOLBARHEIGHT - StatbarPntData.dyStatbar,
				TRUE);
	MoveWindow (ThisDoc->hWndTB, 0, 0,
				RectWidth (MyRect),
				TOOLBARHEIGHT,
				TRUE);
d2399 1
a2399 1
	break;
d2402 53
a2454 2
	SendMessage (ThisDoc->hDocWnd, (UINT) WM_KEYDOWN, wParam, lParam);
	break;
d2457 10
a2466 5
	switch (LOWORD (wParam)) {
	case IDV_EXIT:
	  CloseGroupWnd (hWnd, ThisDoc);
	  return (0);
	  break;
d2468 2
a2469 3
	case IDM_CONFIG_ARTLIST:
	  DialogBox (hInst, "WinVnArticleListPrefs", hWnd, lpfnWinVnArtListDlg);
	  break;
d2471 5
a2475 2
	case IDV_NEXT:
	  break;
d2477 4
a2480 5
	case IDM_MARK_ALL:
	  article_operation (ThisDoc, 0, mark_read_all);
	  CloseGroupWnd (hWnd, ThisDoc);
	  return (0);
	  break;
d2482 4
a2485 4
	case IDM_MARK_SELECTED:
	  article_operation (ThisDoc, 0, mark_read_selected);
  	  InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;
a2486 5
	case IDM_UNMARK_SELECTED:
	  article_operation (ThisDoc, 0, mark_unread_selected);
  	  InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;
    
d2488 4
a2491 4
	  article_operation (ThisDoc, 0, mark_selected_read_articles);
  	  InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;
	  
d2493 3
a2495 59
	  article_operation (ThisDoc, 0, mark_deselected_read_articles);
  	  InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;
	
	case IDM_MARK_UNREAD_ARTICLES:
	  article_operation (ThisDoc, 0, mark_selected_unread_articles);
  	  InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;
	  
    case IDM_UNMARK_UNREAD_ARTICLES:
	  article_operation (ThisDoc, 0, mark_deselected_unread_articles);
  	  InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	  break;  
	case IDM_SORT_DATE:
	case IDM_SORT_SUBJECT:
	case IDM_SORT_LINES:
	case IDM_SORT_THREADSUB:
	case IDM_SORT_THREADS:
	case IDM_SORT_ARTNUM:
	case IDM_SORT_FROM:
	  {
		TypGroup far *group;
		HANDLE header_handle, thread_handle;
  		header_p headers;
		thread_array thread_index;
		BOOL threadOk;
		long i;

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

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

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

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

        iSortOption = LOWORD(wParam);
        lstrcpy(szTmp,"G: ");
        lstrcat(szTmp, CurrentGroup);
        WritePrivateProfileInt(szTmp, "SortOption", iSortOption, szAppProFile);

        hMenu = GetMenu(hWnd);
        CheckMenuItem(hMenu, IDM_SORT_DATE,      MF_BYCOMMAND|MF_UNCHECKED);
        CheckMenuItem(hMenu, IDM_SORT_SUBJECT,   MF_BYCOMMAND|MF_UNCHECKED);
        CheckMenuItem(hMenu, IDM_SORT_LINES,     MF_BYCOMMAND|MF_UNCHECKED);
        CheckMenuItem(hMenu, IDM_SORT_THREADS,   MF_BYCOMMAND|MF_UNCHECKED);
        CheckMenuItem(hMenu, IDM_SORT_THREADSUB, MF_BYCOMMAND|MF_UNCHECKED);
        CheckMenuItem(hMenu, IDM_SORT_FROM,      MF_BYCOMMAND|MF_UNCHECKED);
        CheckMenuItem(hMenu, IDM_SORT_ARTNUM,    MF_BYCOMMAND|MF_UNCHECKED);
d2497 4
a2500 1
        CheckMenuItem(hMenu, iSortOption, MF_BYCOMMAND|MF_CHECKED);
d2502 3
a2504 7
/*        TRACE2("WVGroup Menu select: Newsgroup <%s> iSortOption %d\n", CurrentGroup, iSortOption); */
        sort_by_option(headers, thread_index, threadOk, ThisDoc->TotalLines,
             header_handle, thread_handle);
        unlock_headers (header_handle, thread_handle);
		InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
	
	  }
d2507 25
a2531 3
	case IDM_HELP:
      MakeHelpPathName (mybuf, MAXINTERNALLINE);
      WinHelp (ThisDoc->hDocWnd, mybuf, HELP_INDEX, 0L);
d2534 9
d2544 8
a2551 60
	case IDM_FIND:
	case IDM_FIND_NEXT_SAME:
	  FindDoc = ThisDoc;

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

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

		/* 'Find Next' will start one line after */
		if (ArtListMultiSelect)
		  starting_at = ThisDoc->ActiveLineID + ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);
		else
		  starting_at = ThisDoc->TopLineOrd + ((LOWORD (wParam) == IDM_FIND) ? 0 : 1);

		/* back up one if we're at the end */
		if (starting_at >= group->total_headers)
		  starting_at--;

		found_artindex = search_headers (ThisDoc, headers, starting_at, group->total_headers);
		if (found_artindex == -1) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, ThisDoc->SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "Not found", MB_OK);
		}
		else {
		  if (ArtListMultiSelect) {
			ThisDoc->FindOffset = (int) found_artindex;
			ThisDoc->AnchorLineID = (int) found_artindex;
			strcpy (LastArticleHeaderFind, FindDoc->SearchStr);
			SetGroupSelections (ThisDoc, headers, (int) found_artindex);
			AdjustTopScByDoc (ThisDoc, ThisDoc->ActiveLineID);
		  }
		  else {
			ThisDoc->TopLineOrd = (int) found_artindex;
			ThisDoc->FindOffset = (int) found_artindex;
			strcpy (LastArticleHeaderFind, FindDoc->SearchStr);
			InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
		  }
		}
		unlock_headers (header_handle, thread_handle);
	  }
	  break;
d2553 3
a2555 17
	case IDM_POST:
	  /* We are creating the skeleton text of a new posting.
	     * Most of the work is done by CreatePostingWnd and
	     * CreatePostingText.  Here we have to identify
	     * the newsgroup for those routines.
	     * Get the newsgroup from the line in NetDoc that
	     * points to this document.
	   */
	  if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
			 		ThisDoc->ParentLineID, &BlockPtr, &LinePtr)) {
	  	ExtractTextLine (ThisDoc->ParentDoc, LinePtr,
					   mybuf, MAXINTERNALLINE);
	  	UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	  	NewsgroupsPtr = mybuf;
	  	CreateComposeWnd (hWnd, (TypDoc far *) NULL, DOCTYPE_POSTING);
	  }
	  break;
a2556 12
	case IDM_SAVE_SELECTED:
	  /* Query user for file name */
	  if (!DialogBox (hInst, "WinVnSaveArts", hWnd, lpfnWinVnSaveArtsDlg)) {
		InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
	  }
	  else {
		ThisDoc->numArtsSaved = 0;
		ThisDoc->savingArtIndex = 0;
		SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
		goto doretrieve;
	  }
	  break;
d2558 4
a2561 9
	case IDM_DECODE_SELECTED:
	  /* Query user for file name */
	  if (!DialogBoxParam (hInst, "WinVnDecodeArts", hWnd, lpfnWinVnDecodeArtsDlg, 1)) {
		InvalidateRect (ThisDoc->hDocWnd, NULL, TRUE);
	  }
	  else {
		if (TestCommBusy (hWnd, "Can't Decode Articles") ||
			TestCodingBusy (hWnd, "Can't Decode Articles"))
		  break;
a2562 13
		DecodeInit ();
		CommDoc = ThisDoc;
		CommDecoding = TRUE;
		ThisDoc->numArtsSaved = 0;
		ThisDoc->savingArtIndex = 0;
		SetMenusForMultiArticleOperation (ThisDoc, DISABLE);
		if (AlsoDecodeOpenArticles) {
		  DecodeOpenArticles (hWnd);
		}
		/* now start the retrieving the selected arts */
		goto doretrieve;
	  }
	  break;
d2564 60
a2623 4
	case IDM_SELECT_ALL:
	  /* select all articles */
	  AffectSelected (ThisDoc, SELECT, NO_COMPARE);
	  break;
d2625 17
a2641 4
	case IDM_DESELECT_ALL:
	  /* deselect all articles */
	  AffectSelected (ThisDoc, DESELECT, NO_COMPARE);
	  break;
d2643 12
a2654 27
	case IDM_SELECT_MATCH:
	  /* select all articles containing a string */
	  FindDoc = ThisDoc;
	  DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
	  if (strcmp (FindDoc->SearchStr, "")) {
		if (AffectSelected (FindDoc, SELECT, COMPARE) == 0) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, FindDoc->SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "Not found", MB_OK);
		}
	  }
	  break;
	  
	case IDM_DESELECT_MATCH:
	  /* deselect all articles containing a string */
	  FindDoc = ThisDoc;
	  DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
	  if (strcmp (FindDoc->SearchStr, "")) {
		if (AffectSelected (FindDoc, DESELECT, COMPARE) == 0) {
		  strcpy (mybuf, "\"");
		  strcat (mybuf, FindDoc->SearchStr);
		  strcat (mybuf, "\" not found.");
		  MessageBox (hWnd, mybuf, "Not found", MB_OK);
		}
	  }
	  break;
d2656 4
a2659 3
	case IDM_MAIL:
	  (MailCtrl.fnMlWinCreate) (hWnd, (TypDoc far *) NULL, DOCTYPE_MAIL);
	  break;
d2661 23
a2683 12
	case IDM_UPDATE:
	  if (!CommBusy) {
		TypGroup far *group;
		char far *group_name;

		/* update the newsrc data */
		UpdateSeenArts (ThisDoc);
		UnlinkArtsInGroup (ThisDoc);
		if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
				      ThisDoc->ParentLineID, &BlockPtr, &LinePtr)) {
			group = GetGroup(LinePtr);
			group_name = GetGroupName(LinePtr);
d2685 4
a2688 37
			// Free whatever headers are there.
			if (group->header_handle) {
#if 0
		  	/* is this necessary ? SMR 941221 */
		  	unlock_headers (group->header_handle, group->thread_handle);
#endif
		  	free_headers (group->header_handle, group->thread_handle);
			}
			group->header_handle = (HANDLE) NULL;
			group->thread_handle = (HANDLE) NULL;
			group->total_headers = 0;

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

			SetGroupMenus (ThisDoc, DISABLE);

			RcvLineCount = 0;
			CommDoc = ThisDoc;
			CommState = ST_GROUP_RESP;
			CommBusy = TRUE;
			strcpy (mybuf, "GROUP ");
			lstrcat (mybuf, group_name);
			PutCommLine (mybuf);
			NewsrcDirty = TRUE;
	  	}
	  }
	  break;
d2690 4
a2693 8
	  /* the following msgs are sent by wvutil engine on completion 
	   * retrieval
	   */
	case ID_RETRIEVE_COMPLETE:
	  SetGroupMenus (ThisDoc, ENABLE);
	  SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
	  InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
	  break;
d2695 13
a2707 8
	case ID_ARTICLE_RETRIEVE_COMPLETE:
	  {
		TypGroup far *group;

		header_p headers;
		HANDLE header_handle;
		HANDLE thread_handle;
		int justSavedIndex, startIndex;
d2709 13
a2721 2
		if (ThisDoc->savingArtIndex == -1)	/* this retrieve wasn't part of a save operation */
		  break;
d2723 3
a2725 25
		if (CodingState == INACTIVE) {
		  /* If this is the 1st article saved, then value of Append depends
		     * on what the user selected in dialog.  If > 1st, always append 
		   */
		  if (!MRRWriteDocument (ActiveArticleDoc, sizeof (TypText),
								 SaveArtFileName,
							  (ThisDoc->numArtsSaved == 0) ? SaveArtAppend : TRUE)) {
			*SaveArtFileName = '\0';
			MessageBox (hWnd, "Could not write to file.  Operation canceled", "Problems saving file", MB_OK | MB_ICONEXCLAMATION);
			SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
			ThisDoc->savingArtIndex = -1;
			break;
		  }
		}
		else {
		  if (CompleteThisDecode () == FAIL) {
			DecodeDone ();
			MessageBox (hWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
			SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
			ThisDoc->savingArtIndex = -1;
			break;
		  }
		}
		justSavedIndex = ThisDoc->savingArtIndex;
		ThisDoc->numArtsSaved++;
d2727 12
a2738 9
	  doretrieve:;
	    if (LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
				      &BlockPtr, &LinePtr)) {
			group = GetGroup(LinePtr);
			header_handle = group->header_handle;
			thread_handle = group->thread_handle;
			headers = lock_headers (header_handle, thread_handle);
			UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
			if (ThisDoc->numArtsSaved > 0)	// deselect the article just finished
d2740 37
a2776 1
		  	SelectHeader (ThisDoc, header_elt (headers, justSavedIndex), FALSE);
d2778 9
a2786 2
		/* skip to next selected article */
		/* i'm paranoid of infinite loop in case Doc->SelectedLines is wrong */
d2788 3
a2790 10
			if (ThisDoc->savingArtIndex >= group->total_headers) {
		  	ThisDoc->savingArtIndex = 0;
			}
			startIndex = ThisDoc->savingArtIndex;
			while (ThisDoc->SelectedLines &&
			   	(header_elt (headers, ThisDoc->savingArtIndex))->Selected == FALSE) {
		  	ThisDoc->savingArtIndex = (ThisDoc->savingArtIndex + 1) % (int) (group->total_headers);
		  	if (ThisDoc->savingArtIndex == startIndex)
				break;
			}
d2792 122
a2913 62
			if (ThisDoc->savingArtIndex == startIndex &&		/* this shouldn't happen */
				(header_elt (headers, ThisDoc->savingArtIndex))->Selected == FALSE) {
		  	ThisDoc->SelectedLines = 0;
			}
			
			if (ThisDoc->SelectedLines == 0) {	/* done */
			   SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
			   if (ThisDoc->numArtsSaved > 0) {
				  if (CodingState == INACTIVE) {
			         SetStatbarPercent(ThisDoc->hWndFrame,0,ThisDoc,TRUE);
			         sprintf (mybuf, "Done! Saved %d article%c to file %s",ThisDoc->numArtsSaved, 
			                  (ThisDoc->numArtsSaved == 1) ? ' ' : 's', SaveArtFileName);
			         SetStatbarText (ThisDoc->hWndFrame, mybuf, ThisDoc, TRUE, TRUE);
				   }
				}
				else
					SetStatbarText (ThisDoc->hWndFrame, "No articles selected", ThisDoc, TRUE, TRUE);

		  		if (CodingState != INACTIVE) DecodeDone ();

		  	  	SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
		  	  	InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
			 	unlock_headers (header_handle, thread_handle);
			    ThisDoc->savingArtIndex = -1;	/* reset to non-saving state */
			}
			else {
				sprintf (mybuf, "Saved %d article%c to file %s",ThisDoc->numArtsSaved, 
			            (ThisDoc->numArtsSaved == 1) ? ' ' : 's',SaveArtFileName);
				SetStatbarText (ThisDoc->hWndFrame, mybuf, ThisDoc, TRUE, TRUE);
				SetStatbarPercent(ThisDoc->hWndFrame,
			                     ((int)(ThisDoc->numArtsSaved / (ThisDoc->numArtsSaved + ThisDoc->SelectedLines)) * 100),ThisDoc,TRUE);

		 	   	if (KeepArticleHeaderVisible)
			   		AdjustTopScByDoc (ThisDoc, ThisDoc->savingArtIndex);
		 	   	if (hCodedBlockWnd) {
					sprintf (str, "Decoding Status: %d to go", ThisDoc->SelectedLines);
					SetWindowText (hCodedBlockWnd, (LPCSTR) str);
		 	   	}
		 	 	sprintf (mybuf, "%ld", (header_elt (headers, ThisDoc->savingArtIndex))->number);
			  	(header_elt (headers, ThisDoc->savingArtIndex))->Seen = TRUE;
			  	unlock_headers (header_handle, thread_handle);

			  	if (CodingState != INACTIVE) {
					if ((currentCoded = InitCoded (hWnd)) == NULL) {
				  		MessageBox (hWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
				  		DecodeDone ();
			 	  		SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
				  		ThisDoc->savingArtIndex = -1;
				  		break;
					}
					InitiateReceiveArticle (ThisDoc, mybuf);
					UpdateBlockStatus ();	// display some initial status
		 	 	}
		 	 	else
					ViewArticle (ThisDoc, ThisDoc->savingArtIndex, REUSE, NO_SHOW, NO_ID);
				}
				InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
				return (0);
	  	 }
	  }
	  break;
	}
d2915 1
a2915 1
	return (DefWindowProc (hWnd, message, wParam, lParam));
d2918 1
a2918 1
	SetFocus (ThisDoc->hDocWnd);
d2924 1
a2924 1
// tab-width: 4
@


1.76
log
@added extra error checking for code that calls LockLine and didn't
check for failure to get lock.
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.75 1995/08/25 01:41:23 dumoulin Exp $
d2502 1
d2504 8
a2511 5
		  	if (ThisDoc->numArtsSaved > 0) {
				if (CodingState == INACTIVE) {
			  	sprintf (mybuf, "Saved %d article%c to file %s",
						   ThisDoc->numArtsSaved, (ThisDoc->numArtsSaved == 1) ? ' ' : 's', SaveArtFileName);
			  	MessageBox (hWnd, mybuf, "Done", MB_OK);
d2513 9
a2521 12
		  	}
		  	else
				MessageBox (hWnd, "No articles selected", "Done", MB_OK);

		  	if (CodingState != INACTIVE)
				DecodeDone ();

		  	  SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
    	  	  SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
		  	  InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);
			  unlock_headers (header_handle, thread_handle);
			  ThisDoc->savingArtIndex = -1;	/* reset to non-saving state */
d2524 29
a2552 17
		 	 if (KeepArticleHeaderVisible)
				AdjustTopScByDoc (ThisDoc, ThisDoc->savingArtIndex);
		 	 if (hCodedBlockWnd) {
				sprintf (str, "Decoding Status: %d to go", ThisDoc->SelectedLines);
				SetWindowText (hCodedBlockWnd, (LPCSTR) str);
		 	 }
		 	 sprintf (mybuf, "%ld", (header_elt (headers, ThisDoc->savingArtIndex))->number);
			  (header_elt (headers, ThisDoc->savingArtIndex))->Seen = TRUE;
			  unlock_headers (header_handle, thread_handle);

			  if (CodingState != INACTIVE) {
				if ((currentCoded = InitCoded (hWnd)) == NULL) {
				  MessageBox (hWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
				  DecodeDone ();
			 	  SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
				  ThisDoc->savingArtIndex = -1;
				  break;
d2554 2
a2555 9
				InitiateReceiveArticle (ThisDoc, mybuf);
				UpdateBlockStatus ();	// display some initial status

		 	 }
		 	 else
				ViewArticle (ThisDoc, ThisDoc->savingArtIndex, REUSE, NO_SHOW, NO_ID);
			}
			InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
			return (0);
@


1.75
log
@fixed problems with menu items not greying out correctly
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.74 1995/08/23 23:35:18 dumoulin Exp dumoulin $
d857 2
a858 1
  
a861 1
	NewsrcDirty = TRUE;
d1139 2
a1140 2
  LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
			&BlockPtr, &LinePtr);
d1142 1
a1142 8
  GroupDoc = GetGroup(LinePtr);

  if (articleId == NO_ID)		/* if retrieving artindex, only try if in range */
	if ((artindex < 0) || (artindex >= GroupDoc->total_headers)){
	   UnlockLine (BlockPtr, LinePtr, &Doc->hParentBlock,
	                 &Doc->ParentOffset, &Doc->ParentLineID);
	   return;
	   }
d1144 11
a1154 4
  header_handle = GroupDoc->header_handle;
  thread_handle = GroupDoc->thread_handle;
  headers = lock_headers (header_handle, thread_handle);
  header = header_elt (headers, artindex); 
d1156 3
a1158 3
  if ((articleId == NO_ID) &&
      (header->ArtDoc != NULL) &&
      (header->ArtDoc->CountedLines == header->lines)){ 
d1160 2
a1161 2
	/* We already have a document containing the article */
	/* so just activate it.                */
d1163 6
a1168 6
	if (showArt) {
	  setArticleFocus (header->ArtDoc->hWndFrame);
	  ShowWindow (header->ArtDoc->hWndFrame, SW_SHOWNORMAL);
	}
	else
	  ShowWindow (header->ArtDoc->hWndFrame, SW_SHOWMINNOACTIVE);
d1170 44
a1213 44
	InvalidateRect (header->ArtDoc->hWndFrame, NULL, FALSE);
	InvalidateRect (header->ArtDoc->hDocWnd, NULL, FALSE);
	SendMessage (hWndGroup, (UINT) WM_COMMAND, (WPARAM) ID_ARTICLE_RETRIEVE_COMPLETE, 0L);
	goto endit;
  } 

  if (TestCommBusy (hWndGroup, "Can't Retrieve Article"))
	goto endit;

  newdoc = FALSE;

  if ((NewArticleWindow && !reuse) || !ActiveArticleDoc || 
  			!(ActiveArticleDoc->InUse)) {
	found = FALSE;
	for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++) {
	  if (!ArticleDocs[docnum].InUse) {
		found = TRUE;
		newdoc = TRUE;
		CommDoc = &(ArticleDocs[docnum]);
		break;
	  }
	}
	if (!found) {
	  MessageBox (hWndGroup,
				  "You have too many article windows active;\n"
				  "Close one or uncheck the option"
				  "'New Window for each Article'.",
				  "Can't open new window", MB_OK | MB_ICONASTERISK);
	  goto endit;
	}
  }
  else {
	/* Must reuse old window for this article.         */
	ActiveArticleDoc->LongestLine = 0;
	ActiveArticleDoc->ScXOffset = 0;
	ActiveArticleDoc->TextSelected = FALSE;
	SetArticleMenus (ActiveArticleDoc, DISABLE);
	CommDoc = ActiveArticleDoc;
	/* sever the article/artindex connection */
	if (CommDoc->ParentDoc == Doc)
		(header_elt (headers, CommDoc->ParentOffset))->ArtDoc = (TypDoc far *) NULL;
	else {
		SeverArticleParent(CommDoc);
	}
d1216 16
a1231 12
	FreeDoc (CommDoc);
	InvalidateRect(CommDoc->hDocWnd, NULL, FALSE);
  }

  header->Seen = TRUE;
  NewsrcDirty = TRUE;
  InvalidateRect (hWndGroup, NULL, FALSE);

  if (articleId == NO_ID)
	lpsz = (char far *) header->subject;
  else
	lpsz = articleId;
a1232 4
  strcpy (mybuf, "Retrieving \"");
  lstrcat (mybuf, lpsz);
  lstrcat (mybuf, "\"");

d1328 1
d1363 2
a1364 2
  LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
			&BlockPtr, &LinePtr);
d1370 8
a1377 8
  lpszGroupName = GetGroupName(LinePtr);
  if (lstrcmp (CurrentGroup, lpszGroupName)) {
	CommState = ST_GROUP_REJOIN;
	strcpy (mybuf, "GROUP ");
	lstrcat (mybuf, lpszGroupName);
	mylstrncpy (CurrentGroup, lpszGroupName, MAXGROUPNAME);
	PutCommLine (mybuf);
  }
d1379 1
a1379 1
  UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
d1381 3
a1383 2
  sprintf (mybuf, "ARTICLE %s", articleId);
  PutCommLine (mybuf);
d1403 5
a1407 5
	if (doc->ParentDoc) {
	  LockLine (doc->ParentDoc->hParentBlock,
				doc->ParentDoc->ParentOffset,
				doc->ParentDoc->ParentLineID,
				&BlockPtr, &LinePtr);
d1581 1
d2285 3
a2287 3
	  LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
				ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
	  ExtractTextLine (ThisDoc->ParentDoc, LinePtr,
d2289 4
a2292 3
	  UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
	  NewsgroupsPtr = mybuf;
	  CreateComposeWnd (hWnd, (TypDoc far *) NULL, DOCTYPE_POSTING);
d2301 2
a2302 2
		numArtsSaved = 0;
		savingArtIndex = 0;
d2321 2
a2322 2
		numArtsSaved = 0;
		savingArtIndex = 0;
d2382 4
a2385 4
		LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
				  ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
		group = GetGroup(LinePtr);
		group_name = GetGroupName(LinePtr);
d2387 2
a2388 2
		// Free whatever headers are there.
		if (group->header_handle) {
d2390 2
a2391 2
		  /* is this necessary ? SMR 941221 */
		  unlock_headers (group->header_handle, group->thread_handle);
d2393 29
a2421 28
		  free_headers (group->header_handle, group->thread_handle);
		}
		group->header_handle = (HANDLE) NULL;
		group->thread_handle = (HANDLE) NULL;
		group->total_headers = 0;

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


		SetGroupMenus (ThisDoc, DISABLE);

		RcvLineCount = 0;
		CommDoc = ThisDoc;
		CommState = ST_GROUP_RESP;
		CommBusy = TRUE;
		strcpy (mybuf, "GROUP ");
		lstrcat (mybuf, group_name);
		PutCommLine (mybuf);
d2443 1
a2443 1
		if (savingArtIndex == -1)	/* this retrieve wasn't part of a save operation */
d2452 1
a2452 1
							  (numArtsSaved == 0) ? SaveArtAppend : TRUE)) {
d2456 1
a2456 1
			savingArtIndex = -1;
d2465 1
a2465 1
			savingArtIndex = -1;
d2469 2
a2470 2
		justSavedIndex = savingArtIndex;
		numArtsSaved++;
d2473 8
a2480 8
		LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
				  &BlockPtr, &LinePtr);
		group = GetGroup(LinePtr);
		header_handle = group->header_handle;
		thread_handle = group->thread_handle;
		headers = lock_headers (header_handle, thread_handle);
		UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
		if (numArtsSaved > 0)	// deselect the article just finished
d2482 1
a2482 1
		  SelectHeader (ThisDoc, header_elt (headers, justSavedIndex), FALSE);
a2485 10
		if (savingArtIndex >= group->total_headers) {
		  savingArtIndex = 0;
		}
		startIndex = savingArtIndex;
		while (ThisDoc->SelectedLines &&
			   (header_elt (headers, savingArtIndex))->Selected == FALSE) {
		  savingArtIndex = (savingArtIndex + 1) % (int) (group->total_headers);
		  if (savingArtIndex == startIndex)
			break;
		}
d2487 9
a2495 10
		if (savingArtIndex == startIndex &&		/* this shouldn't happen */
			(header_elt (headers, savingArtIndex))->Selected == FALSE) {
		  ThisDoc->SelectedLines = 0;
		}
		if (ThisDoc->SelectedLines == 0) {	/* done */
		  if (numArtsSaved > 0) {
			if (CodingState == INACTIVE) {
			  sprintf (mybuf, "Saved %d article%c to file %s",
					   numArtsSaved, (numArtsSaved == 1) ? ' ' : 's', SaveArtFileName);
			  MessageBox (hWnd, mybuf, "Done", MB_OK);
a2496 3
		  }
		  else
			MessageBox (hWnd, "No articles selected", "Done", MB_OK);
d2498 49
a2546 31
		  if (CodingState != INACTIVE)
			DecodeDone ();

		  SetMenusForMultiArticleOperation (ThisDoc, ENABLE);

//                now deselect article as soon as it's successfully finished  (jsc)
		  //                for (savingArtIndex=0; savingArtIndex < group->total_headers; savingArtIndex++ )
		  //                   SelectHeader(ThisDoc, header_elt(headers, savingArtIndex), FALSE);
		  SetStatbarText (ThisDoc->hWndFrame, "", ThisDoc, TRUE, TRUE);
		  InvalidateRect (ThisDoc->hWndFrame, NULL, TRUE);

		  unlock_headers (header_handle, thread_handle);
		  savingArtIndex = -1;	/* reset to non-saving state */
		}
		else {
		  if (KeepArticleHeaderVisible)
			AdjustTopScByDoc (ThisDoc, savingArtIndex);
		  if (hCodedBlockWnd) {
			sprintf (str, "Decoding Status: %d to go", ThisDoc->SelectedLines);
			SetWindowText (hCodedBlockWnd, (LPCSTR) str);
		  }
		  sprintf (mybuf, "%ld", (header_elt (headers, savingArtIndex))->number);
		  (header_elt (headers, savingArtIndex))->Seen = TRUE;
		  unlock_headers (header_handle, thread_handle);
		  if (CodingState != INACTIVE) {
			if ((currentCoded = InitCoded (hWnd)) == NULL) {
			  MessageBox (hWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
			  DecodeDone ();
			  SetMenusForMultiArticleOperation (ThisDoc, ENABLE);
			  savingArtIndex = -1;
			  break;
d2548 3
a2550 9
			InitiateReceiveArticle (ThisDoc, mybuf);
			UpdateBlockStatus ();	// display some initial status

		  }
		  else
			ViewArticle (ThisDoc, savingArtIndex, REUSE, NO_SHOW, NO_ID);
		}
		InvalidateRect (ThisDoc->hDocWnd, NULL, FALSE);
		return (0);
@


1.74
log
@support for deselecting matched/unmatched articles and
other selection type functions
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.73 1995/06/06 05:59:55 dumoulin Exp $
d950 2
a951 2
	if (header->Seen) {
		header->Selected = TRUE;
d968 1
a968 1
		header->Selected = TRUE;
d985 2
a986 2
		header->Selected = FALSE;
	}
d1002 1
a1002 1
		header->Selected = FALSE;
d2020 3
a2022 1
	
@


1.73
log
@fix a few places where global locks were not being checked
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.72 1995/06/06 04:07:56 dumoulin Exp $
d740 1
d941 68
d2127 20
a2146 1

d2349 14
@


1.72
log
@fixed problem with failing to mark newsrc as needing to be saved
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.71 1995/05/19 22:56:32 dumoulin Exp $
d1406 1
d1417 2
a1418 2
  LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
			&ParentBlock, &ParentLine);
d1420 2
a1421 1
  if ((LinePtr = (TypLine *) GlobalAllocPtr (GMEM_MOVEABLE, BLOCK_SIZE)) == NULL) {
@


1.71
log
@fixed raw pointer math to use accessor functions, tightened
up code on who is allowed to cancel articles, added support
for HELP toolbar on group window, added support to grow
article range array dynamically.
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.70 1995/04/21 21:47:11 dumoulin Exp $
d1152 1
@


1.70
log
@Add cancel capability and warning message when posting window is larger
than 78 characters in width.
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.69 1995/04/18 19:07:36 dumoulin Exp dumoulin $
a63 6
void setArticleFocus (HWND hWndArt);
long NewCursorToTextLine (int X, int Y, TypDoc far * DocPtr);
long search_headers (TypDoc far * HeaderDoc, header_p headers, long artindex, long num_headers);
void SaveSelectedArts (HWND hwnd, TypDoc far * Doc, header_p headers, long num_headers);
long AffectSelected (TypDoc far * Doc, BOOL value, BOOL compare);
long AffectSelectedOnScreen (TypDoc far * Doc, BOOL value, BOOL compare);
a64 1
void InitiateReceiveArticle (TypDoc far * Doc, char far * articleId);
a65 17
extern "C" BOOL article_operation (TypDoc far * Doc, long artindex,
						BOOL (*art_fun) (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex));
BOOL toggle_read_unread (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL toggle_selected (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL selected_true (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
extern "C" BOOL selected_false (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL seen_true (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL seen_false (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL mark_read_to_here (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL mark_read_all (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL mark_read_selected (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
BOOL mark_unread_selected (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);

void CloseGroupWnd (HWND hWnd, TypDoc far * ThisDoc);
void SetMenusForMultiArticleOperation (TypDoc far * DocPtr, int enable);
BOOL SelectHeader (TypDoc far * Doc, header_p header, BOOL value);

a88 1
//  POINT ptCursor;
d139 1
a139 1
	group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
a143 1

d273 1
a273 2
	   of the article 
	 */
a344 1

d573 1
a573 2
		group = (TypGroup far *) ((char far *) NetLinePtr + sizeof (TypLine));

a585 1

a586 1

a607 1
		  /* ack! */
d615 1
a615 2
		  /* Figure out the color of this line.                 */

d636 1
a636 2
		  /* Now write out the line.                            */

d703 1
a703 1
	if (SaveNewsrcOnClose) WriteNewsrc ();
d854 1
a854 3

  GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

d856 1
d860 1
a860 1

a934 3
	// added by jlg to get rid of erroneous '*' in group window
//	if (header->number > GroupDoc->HighestPrevSeen)
//	  GroupDoc->HighestPrevSeen = header->number;
a951 3
	// added by jlg to get rid of erroneous '*' in group window
//	if (header->number > GroupDoc->HighestPrevSeen)
//	  GroupDoc->HighestPrevSeen = header->number;
d1073 1
a1073 1
  GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
a1074 1

d1247 4
a1250 3
  if (stricmp (header->from, UserName) == 0){
      EnableMenuItem(hSubMenu,IDM_CANCELART,ENABLE_MENU);
      }
d1252 1
a1252 1
      EnableMenuItem(hSubMenu,IDM_CANCELART,DISABLE_MENU);
d1299 1
a1299 1
  lpszGroupName = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
d1337 1
a1337 2
	  GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

a1365 1
//    SendMessage (ArticleDocs[iart].hWndFrame, WM_MYINITMENU, (WPARAM) 0, (LPARAM) 0);
d1421 1
a1421 2
  group = (TypGroup *) ((char *) LinePtr + sizeof (TypLine));

a1432 1

d1436 1
d1439 2
a1440 1
				 group->nRanges * sizeof (TypRange)) / sizeof (TypRange) - 1;
d1442 2
a1443 3
	RangePtr = (TypRange *) ((char *) LinePtr + sizeof (TypLine) +
							 RangeOffset (group->NameLen));
	MyRange.First = 1;
d1461 1
a1461 2
	  /* Loop to scan through the document, fabricating article ranges.
	   */
d1466 1
a1466 1
			/* Continuing a sequence of seen articles.            */
d1470 1
a1470 1
			/* Starting a new sequence of seen articles.          */
d1478 1
a1478 1
			/* Ending a sequence of seen articles.                   */
d1484 1
a1484 1
			/* Continuing a sequence of unseen articles.             */
d1501 1
a1501 3
  MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) +
	sizeof (TypRange) * (group->nRanges) + sizeof (int);

d1503 1
a1503 2
  *(int *) ((char *) LinePtr + MyLength - sizeof (int)) = MyLength;

a1505 1

d1637 1
a1637 1
  group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
d1667 1
a1667 1
  group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
d1776 1
a1776 2
	  TypGroup far *group =
	  (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
d1895 9
d1967 1
a1967 2
		GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

d1996 2
a1997 2
   SetCapsLockText(hWnd);
   SetNumLockText(hWnd);
d2073 1
a2073 2
		group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

a2075 1

a2079 1

d2114 6
d2139 1
a2139 2
		group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));

a2141 1

a2142 1

d2273 2
a2274 3
		group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
		group_name = (char far *) LinePtr
		  + sizeof (TypLine) + sizeof (TypGroup);
d2300 1
a2300 3
		// capture the mouse to the usenet window, so that we keep
		// the hourglass.
//		SetCapture (NetDoc.hDocWnd);
d2363 1
a2363 1
		group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
@


1.69
log
@Fix GPFs if COMDOC becomes null for any reason
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.68 1995/04/14 16:44:43 dumoulin Exp $
a1093 1
 // TypDoc far *MyDoc;
d1109 1
a1125 1

d1127 1
a1127 1
  header = header_elt (headers, artindex);
d1272 1
a1272 2
  CommDoc->CountedLines = header->lines;

d1286 10
a1298 1

d1983 1
a2019 1
/*      hMenu = GetMenu(hWnd); */  /* (redundant) */
@


1.68
log
@Fix problem with not being able to view some articles after a sort
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.67 1995/04/12 20:53:36 dumoulin Exp $
d1094 1
a1094 1
  TypDoc far *MyDoc;
d1130 3
a1132 4
  if ((articleId == NO_ID) && 
      (CommDoc->ParentDoc == Doc) &&
      (MyDoc = header->ArtDoc) &&
      (MyDoc->CountedLines == header->lines)){ 
d1138 2
a1139 2
	  setArticleFocus (MyDoc->hWndFrame);
	  ShowWindow (MyDoc->hWndFrame, SW_SHOWNORMAL);
d1142 1
a1142 1
	  ShowWindow (MyDoc->hWndFrame, SW_SHOWMINNOACTIVE);
d1144 2
a1145 2
	InvalidateRect (MyDoc->hWndFrame, NULL, FALSE);
	InvalidateRect (MyDoc->hDocWnd, NULL, FALSE);
@


1.67
log
@Make saving newsrc up closing group an option
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.66 1995/03/29 00:35:03 dumoulin Exp $
a180 1
//	  if (ArtListMultiSelect && headers != NULL) {
a192 1
//	  if (ArtListMultiSelect && headers != NULL) {
a201 1
//	  if (ArtListMultiSelect && headers != NULL) {
a208 1
//	  if (ArtListMultiSelect && headers != NULL) {
d215 1
a215 2
	case VK_NEXT:
//	  if (ArtListMultiSelect && headers != NULL) { 
a228 1
//	  if (ArtListMultiSelect && headers != NULL) { 
d1094 1
a1094 1
//  TypDoc far *MyDoc;
d1118 5
a1122 2
	if ((artindex < 0) || (artindex >= GroupDoc->total_headers))
	  return;
d1130 5
a1134 1
 // if (articleId == NO_ID && (MyDoc = header->ArtDoc)) {
d1138 6
a1143 6
//	if (showArt) {
//	  setArticleFocus (MyDoc->hWndFrame);
//	  ShowWindow (MyDoc->hWndFrame, SW_SHOWNORMAL);
//	}
//	else
//	  ShowWindow (MyDoc->hWndFrame, SW_SHOWMINNOACTIVE);
d1145 5
a1149 5
//	InvalidateRect (MyDoc->hWndFrame, NULL, FALSE);
//	InvalidateRect (MyDoc->hDocWnd, NULL, FALSE);
//	SendMessage (hWndGroup, (UINT) WM_COMMAND, (WPARAM) ID_ARTICLE_RETRIEVE_COMPLETE, 0L);
//	goto endit;
//  } 
d1290 1
d2147 1
a2147 1
		unlock_headers (header_handle, thread_handle);
d2149 1
@


1.66
log
@allow PageUp, PageDown, Home and End keys to work regardless of
setting of Multi-Select option in Group Article List window
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.65 1995/01/19 02:47:25 jglasser Exp $
d743 1
a743 1
	WriteNewsrc ();
d1100 1
a1100 1
  TypDoc far *MyDoc;
d1132 2
a1133 2

  if (articleId == NO_ID && (MyDoc = header->ArtDoc)) {
d1137 12
a1148 6
	if (showArt) {
	  setArticleFocus (MyDoc->hWndFrame);
	  ShowWindow (MyDoc->hWndFrame, SW_SHOWNORMAL);
	}
	else
	  ShowWindow (MyDoc->hWndFrame, SW_SHOWMINNOACTIVE);
a1149 5
	InvalidateRect (MyDoc->hWndFrame, NULL, FALSE);
	InvalidateRect (MyDoc->hDocWnd, NULL, FALSE);
	SendMessage (hWndGroup, (UINT) WM_COMMAND, (WPARAM) ID_ARTICLE_RETRIEVE_COMPLETE, 0L);
	goto endit;
  }
d2144 1
a2144 37
        header_handle, thread_handle);

/*		switch (LOWORD (wParam)) {
		case IDM_SORT_DATE:
		  shell_sort_index_array (headers, thread_index,
								  ThisDoc->TotalLines, compare_date);
		  break;

		case IDM_SORT_SUBJECT:
		  shell_sort_index_array (headers, thread_index,
								  ThisDoc->TotalLines, compare_subject);
		  break;

		case IDM_SORT_LINES:
		  shell_sort_index_array (headers, thread_index,
								  ThisDoc->TotalLines, compare_lines);
		  break;

		case IDM_SORT_THREADSUB:
		case IDM_SORT_THREADS:
		  if (threadOk)
			sort_by_threads (header_handle, thread_handle, ThisDoc->TotalLines);
		  else
			MessageBox (hWnd, "Threading disabled", "WinVN", MB_OK);
		  break;

		case IDM_SORT_ARTNUM:
		  shell_sort_index_array (headers, thread_index,
								  ThisDoc->TotalLines, compare_artnum);
		  break;

		case IDM_SORT_FROM:
		  shell_sort_index_array (headers, thread_index,
								  ThisDoc->TotalLines, compare_from);
		  break;
		} */

@


1.65
log
@Jody Glasser's changes of 950113
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.64 1995/01/19 02:35:45 rushing Exp $
d113 1
a113 1
  POINT ptCursor;
d181 2
a182 1
	  if (ArtListMultiSelect && headers != NULL) {
d193 2
a194 1
	  if (ArtListMultiSelect && headers != NULL) {
d204 2
a205 1
	  if (ArtListMultiSelect && headers != NULL) {
d212 2
a213 1
	  if (ArtListMultiSelect && headers != NULL) {
d220 2
a221 1
	  if (ArtListMultiSelect && headers != NULL) {
d234 2
a235 1
	  if (ArtListMultiSelect && headers != NULL) {
d264 1
a264 1
	int X, Y;
d269 2
a270 1
	  if (ArtListMultiSelect) {
d274 8
a281 8
	  else {
		GetCursorPos (&ptCursor);
		ScreenToClient (hWnd, &ptCursor);
		X = ptCursor.x;
		Y = ptCursor.y;
		goto getarticle;

	  }
d411 1
a411 1
  getarticle:;
d743 1
@


1.64
log
@HB's threadOk check needs an extra check for hParentBlock==NULL
because the code is sometimes called before an InitDoc().
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.63 1995/01/17 19:08:04 rushing Exp rushing $
d1263 3
d2021 2
@


1.63
log
@cast result of SelectObject to HBRUSH to remove warnings
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.62 1995/01/03 20:37:22 brydon Exp rushing $
d1979 5
a1983 1
	if (!CommBusy || CommDoc != ThisDoc) {
@


1.62
log
@"Sort" menu: checkmark on sorting option (ie. CheckMenuItem) selected by
user.
"Sort" menu: add Thread/Subject sort option
Record and remember user's sort option for each newsgroup
made ANSI C++
@
text
@d46 1
a46 1
 * $Id: wvgroup.cpp 1.61 1995/01/03 18:57:42 jcooper Exp $
d695 1
a695 1
	  hOldBrush = SelectObject (hDC, hListBackgroundBrush);
d735 1
a735 1
	SetHandleBkBrush (ThisDoc->hDocWnd, GetStockObject (WHITE_BRUSH));
@


1.61
log
@uncomment CalcNumUnread call.
@
text
@d1 1
d46 1
a46 1
 * $Id: wvgroup.c 1.60 1994/12/21 18:38:00 jcooper Exp jcooper $
d52 2
d56 1
d58 2
d61 1
d73 1
a73 1
BOOL article_operation (TypDoc far * Doc, long artindex,
d78 1
a78 1
BOOL selected_false (TypDoc far * Doc, header_p headers, TypGroup * group, long artindex);
d99 1
a99 5
WinVnViewWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 UINT message;
	 WPARAM wParam;
	 LPARAM lParam;
d832 1
d868 1
a868 1
BOOL
d1055 1
a1055 1
BOOL
d1090 1
a1090 6
ViewArticle (Doc, artindex, reuse, showArt, articleId)
	 TypDoc far *Doc;
	 long artindex;
	 BOOL reuse;
	 BOOL showArt;
	 char far *articleId;
d1380 1
a1380 2
UnlinkArtsInGroup (GroupDoc)
	 TypDoc far *GroupDoc;
d1417 1
a1417 2
UpdateSeenArts (Doc)
	 TypDoc far *Doc;
d1553 1
a1553 4
NewCursorToTextLine (X, Y, DocPtr)
	 int X;
	 int Y;
	 TypDoc far *DocPtr;
d1763 1
d1954 1
a1954 1
	if (wParam)
d1956 1
d1991 17
d2075 1
d2082 1
a2082 1
		header_p headers;
d2108 21
a2128 1
		switch (LOWORD (wParam)) {
d2144 1
d2161 2
a2162 1
		}
d2166 1
a2166 1
	  break;
d2505 6
@


1.60
log
@a call to CalcNumUnread() commented out
possible need for unlock_headers() call before two instances of free_headers()
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.59 1994/12/12 19:39:13 jcooper Exp $
d1526 1
a1526 1
  /*  group->NumUnread = CalcNumUnread(group); */
@


1.59
log
@0.9.99 update
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.58 1994/11/30 22:22:08 jcooper Exp $
d1526 1
a1526 1
  group->NumUnread = CalcNumUnread(group);
d1834 5
a1838 1
		if (group->header_handle)
d1840 1
a1840 1

d2293 5
a2297 1
		if (group->header_handle)
d2299 1
a2299 1

@


1.58
log
@93.9 changes
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.57 1994/11/28 22:01:06 jcooper Exp $
d627 1
a627 1
		  sprintf (scratch_line, "%c%5Flu %-5.5Fs %-18.18Fs %4Fd %-*s%-Fs ",
a945 3
	// added by jlg to get rid of erroneous '*' in group window
	if (header->number > GroupDoc->HighestPrevSeen)
	  GroupDoc->HighestPrevSeen = header->number;
d947 1
d965 2
a966 2
	if (header->number > GroupDoc->HighestPrevSeen)
	  GroupDoc->HighestPrevSeen = header->number;
d985 2
a986 2
	if (header->number > GroupDoc->HighestPrevSeen)
	  GroupDoc->HighestPrevSeen = header->number;
d1078 1
a1078 1
 *             Reuse          is TRUE if we ought to reuse the
d1086 1
a1086 1
ViewArticle (Doc, artindex, Reuse, showArt, articleId)
d1089 1
a1089 1
	 BOOL Reuse;
d1144 1
d1146 3
a1148 1
  if ((NewArticleWindow && !Reuse) || !ActiveArticleDoc || !(ActiveArticleDoc->InUse)) {
d1175 5
a1179 3
	if (CommDoc->ParentDoc == Doc)	// (JSC) is this check really necessary?

	  (header_elt (headers, CommDoc->ParentOffset))->ArtDoc = (TypDoc far *) NULL;
d1268 3
a1270 3
	if (header->number > GroupDoc->HighestPrevSeen) {
	  GroupDoc->HighestPrevSeen = header->number;
	}
d1337 34
d1826 4
d2305 2
@


1.57
log
@93.8 update from jcooper
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.56 1994/11/24 00:20:03 rushing Exp $
d1252 1
a1252 1
	ShowWindow (hWndArt, SW_SHOWMINIMIZED);
@


1.56
log
@integer size mismatch.
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.55 1994/11/21 21:20:46 jcooper Exp $
d1254 7
a1260 2
  header->ArtDoc = CommDoc;
  CommDoc->ParentOffset = (int) artindex;	/* should this be iff articleId==NO_ID ? */
d1353 1
d2240 1
d2267 1
a2267 1
		SetCapture (NetDoc.hDocWnd);
d2270 1
@


1.55
log
@93.7 update
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.54 1994/11/11 01:40:08 jglasser Exp $
d2339 1
a2339 1
		  savingArtIndex = (savingArtIndex + 1) % group->total_headers;
@


1.54
log
@toolbar fixes
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.53 1994/11/10 23:00:19 jcooper Exp $
d76 2
d111 1
a111 1
  int X, Y;
d137 1
a137 1
  if (StatusBarProc (hWnd, message, wParam, lParam, &NetDoc))
d163 1
a163 1
	if (group->header_handle) {
d254 2
d275 1
d293 4
a296 3
	/* Single middle click will toggle the read/unread status of the */
	/* article */

d298 1
a298 1
	if (!CommBusy || CommDoc != ThisDoc) {
a379 1

d385 1
a387 1

d427 2
a428 1
		if (ArtListMultiSelect) {
d473 1
a473 1
	  int MyLen;
d559 7
d569 7
a575 6
	  //RangeHigh = ThisDoc->TotalLines - VertLines;
	  RangeHigh = ThisDoc->TotalLines - ThisDoc->ScYLines;
	  if (RangeHigh < 0)
		RangeHigh = 0;
	  SetScrollRange (hWnd, SB_VERT, 0, RangeHigh, FALSE);
	  SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
d594 1
a594 2
	  if (ThisDoc->TotalLines) {

d612 1
a612 1
		  artnum = header->number;
a766 1
  EnableMenuItem (hSubMenu, IDM_UPDATE, mode);
d781 1
a781 1
  EnableMenuItem (hSubMenu, IDM_SORT_THREADS, mode);
d799 1
a799 1
  SendMessage (DocPtr->hWndFrame, WM_INITMENU, (WPARAM) 0, (LPARAM) 0);
a817 1
  EnableMenuItem (hSubMenu, IDM_UPDATE, mode);
d858 1
a858 1
	SendMessage (Doc->hWndFrame, WM_INITMENU, (WPARAM) 0, (LPARAM) 0);
d954 40
d1178 1
a1178 1
	/* clear out old doc */
d1180 1
d1221 3
a1223 6
	/* Initial X pos */
							x - (docnum * ArtCharWidth),
	/* Initial Y pos */
							y + docnum * ArtLineHeight,
							width,	/* Initial X Width */
	/* Initial Y height */
d1249 1
a1249 1
	ShowWindow (hWndArt, SW_SHOWNORMAL);
d1299 1
a1299 1
    SendMessage (Doc->hWndFrame, WM_INITMENU, 0, 0L);
d1303 1
a1303 1
    SendMessage (CommDoc->hWndFrame, WM_INITMENU, 0, 0L);
d1481 1
a1481 1

d1483 1
a1483 1

d1683 1
d1721 1
a1721 1
  BOOL continueFind;
d1743 2
a1744 1
  StatusBarProc (hWnd, message, wParam, lParam, ThisDoc);
d1911 1
d1919 4
d1931 1
d1938 1
a1938 2
		hSubMenu = GetSubMenu (hMenu, 1);	/* sorting menu */
		EnableMenuItem (hSubMenu, IDM_SORT_THREADS, GroupDoc->Threaded ? ENABLE_MENU : DISABLE_MENU);
d1942 4
d1998 10
d2246 1
d2248 9
a2256 2
		// zero out the received line count
		RcvLineCount = 0;
@


1.53
log
@93.5 update
@
text
@d45 1
a45 1
 * $Id: wvgroup.c 1.52 1994/11/10 01:27:31 rushing Exp $
d135 2
a136 1
  StatusBarProc (hWnd, message, wParam, lParam, ThisDoc);
@


1.52
log
@restart
@
text
@d1085 1
a1085 1
	  ShowWindow (MyDoc->hWndFrame, SW_SHOWMINIMIZED);
d1247 8
a1254 2
  // update the toolbar (deactivate the comm stuff 
  SendMessage (Doc->hWndFrame, WM_INITMENU, 0, 0L);
d2221 1
@
