home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / winsock / wvnsc926 / wvusenet.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  61.2 KB  |  2,141 lines

  1. /********************************************************************
  2.  *                                                                  *
  3.  *  MODULE    :  WVUSENET.C                                         *
  4.  *                                                                  *
  5.  *  PURPOSE   : This file contains the window procedure and support *
  6.  *              routines for WinVn's main window                    *
  7.  *                                                                  *
  8.  ********************************************************************/
  9.  
  10. /*
  11.  * $Id: wvusenet.c 1.53 1994/09/18 22:46:50 jcooper Exp $
  12.  *
  13.  */
  14. #include <windows.h>
  15. #include <windowsx.h>
  16. #include "wvglob.h"
  17. #include "winvn.h"
  18. #pragma hdrstop
  19. #include <stdlib.h>
  20.  
  21. // This is for the special case where the user invoked the
  22. // program with dolist=0, then turned it on.  Without checking
  23. // this condition, the newsrc will be truncated to only those
  24. // groups read.  SMR 930224
  25.  
  26. int started_with_no_dolist;
  27.  
  28. void show_version_strings(HWND);
  29. void SetGroupActiveLines(void);
  30. void AbortAllComm();
  31.  
  32. /*--- FUNCTION: WinVnConfWndProc ---------------------------------------
  33.  *
  34.  *    Window procedure for the "Net" window.
  35.  *    This window displays the names of the newsgroups, one per line.
  36.  *
  37.  *    Entry    The usual parameters for any window function.
  38.  *
  39.  *    Note:    We don't do anything until "Initialized" is TRUE.
  40.  *             This way, the user won't try to perform functions
  41.  *             while the communications are still being set up.
  42.  */
  43.  
  44. long WINAPI WinVnConfWndProc (hWnd, message, wParam, lParam)
  45.      HWND hWnd;
  46.      unsigned message;
  47.      WPARAM wParam;
  48.      LPARAM lParam;
  49. {
  50.  
  51.   DLGPROC lpProcAbout;
  52.   HMENU hMenu, hSubMenu;
  53.  
  54.   PAINTSTRUCT ps;               /* paint structure          */
  55.   POINT ptCursor;
  56.  
  57.   HDC hDC;                      /* handle to display context */
  58.   HWND hWndView;
  59.   RECT myRect;                                    /* selection rectangle                */
  60.  
  61.   int j, idoc;
  62.   int found;
  63.   char far *lpsz;
  64.   TypLine far *LinePtr, far * GroupLinePtr;
  65.   TypBlock far *BlockPtr, far * GroupBlockPtr;
  66.   HANDLE hBlock;
  67.   TypDoc *MyDoc;
  68.   unsigned int Offset;
  69.   BOOL looping = TRUE;
  70.   BOOL continueFind;
  71.   TypGroup far *MyGroup;
  72.   
  73.   int X, Y;
  74.   int docnum;
  75.   int newdoc;
  76.   char mybuf[MAXINTERNALLINE];
  77.   int OldSel = FALSE;
  78.   int CtrlState;
  79.   TypLineID MyLineID;
  80.  
  81.   switch (message)
  82.     {
  83.  
  84.     case WM_SYSCOMMAND:
  85.       if (wParam == ID_ABOUT)
  86.    {
  87.      lpProcAbout = (DLGPROC)MakeProcInstance ((FARPROC)About, hInst);
  88.      DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
  89.      FreeProcInstance ((FARPROC)lpProcAbout);
  90.      break;
  91.    }
  92.       else
  93.    return (DefWindowProc (hWnd, message, wParam, lParam));
  94.  
  95.     case WM_CREATE:
  96.       MailInit (hWnd) ;
  97.  
  98.       /* Get handle to the hourglass cursor */
  99.  
  100.       hHourGlass = LoadCursor (hInst, IDC_WAIT);
  101.  
  102.       /* Add the "About" option to the system menu.            */
  103.  
  104.       hMenu = GetSystemMenu (hWnd, FALSE);
  105.       ChangeMenu (hMenu, NULL, NULL, NULL, MF_APPEND | MF_SEPARATOR);
  106.       ChangeMenu (hMenu, NULL, "A&bout WinVn...", ID_ABOUT,
  107.         MF_APPEND | MF_STRING);
  108.  
  109.       /* Fill in the Net document's window handle that we
  110.        * were unable to fill in in the call to InitDoc.
  111.        */
  112.       NetDoc.hDocWnd = hWnd;
  113.       InitBitmaps();
  114.       break;
  115.  
  116.     case WM_INITMENU:
  117.       hMenu = GetMenu (hWnd);
  118.       hSubMenu = GetSubMenu (hMenu, 2);    /* Utilities menu */
  119.       EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_POST, NumPostWnds ? MF_ENABLED : MF_DISABLED|MF_GRAYED);
  120.       EnableMenuItem (GetSubMenu (hSubMenu, 3), IDM_SEND_ALL_MAIL, NumMailWnds ? MF_ENABLED : MF_DISABLED|MF_GRAYED);
  121.         
  122.       hSubMenu = GetSubMenu (hMenu, 4);    /* Window menu */
  123.       EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_ARTICLE, NumArticleWnds ? MF_ENABLED : MF_DISABLED|MF_GRAYED);
  124.       EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_GROUP, NumGroupWnds ? MF_ENABLED : MF_DISABLED|MF_GRAYED);
  125.       EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL_STATUS, NumStatusTexts ? MF_ENABLED : MF_DISABLED|MF_GRAYED);
  126.       EnableMenuItem (GetSubMenu (hSubMenu, 1), IDM_CLOSE_ALL, (NumArticleWnds||NumGroupWnds||NumStatusTexts) ? MF_ENABLED : MF_DISABLED|MF_GRAYED);
  127.       break;
  128.     
  129.     case WM_CHAR:
  130.       /* Hitting CR on a group name is the same as double-clicking
  131.        * on the name.
  132.        */
  133.       if (!Initializing)
  134.    {
  135.      if (wParam == '\r')
  136.        {
  137.  
  138.          GetCursorPos (&ptCursor);
  139.          ScreenToClient (hWnd, &ptCursor);
  140.          X = ptCursor.x;
  141.          Y = ptCursor.y;
  142.          goto getgroup;
  143.        }
  144.    }
  145.       break;
  146.  
  147.     case WM_KEYDOWN:
  148.       /* See if this key should be mapped to a scrolling event
  149.        * for which we have programmed the mouse.  If so,
  150.        * construct the appropriate mouse call and call the mouse code.
  151.        * Also, F6 means switch to next window.
  152.        * (This latter should also be table-driven.)
  153.        */
  154.       if (!Initializing)
  155.    {
  156.      if (wParam == VK_F6)
  157.        {
  158.          NextWindow (&NetDoc);
  159.        }
  160.      else
  161.        {
  162.          /* Based on the state of the Control key and the value
  163.           * of the key just pressed, look up in the array
  164.           * key2scroll to see if we should process this keystroke
  165.           * as if it were a mouse event.  If so, simulate the
  166.           * mouse event by sending the appropriate message.
  167.           */
  168.  
  169.          CtrlState = GetKeyState (VK_CONTROL) < 0;
  170.          for (j = 0; j < NUMKEYS; j++)
  171.       {
  172.         if (wParam == key2scroll[j].wVirtKey &&
  173.             CtrlState == key2scroll[j].CtlState)
  174.           {
  175.             SendMessage (hWnd, key2scroll[j].iMessage,
  176.                key2scroll[j].wRequest, 0L);
  177.             break;
  178.           }
  179.       }
  180.  
  181.        }
  182.    }
  183.       break;
  184.  
  185.     case WM_LBUTTONDOWN:
  186.       /*  Clicking the left button on a group name toggles the
  187.        *  selected/not selected status of that group.
  188.        *  Currently selected groups are displayed in reverse video.
  189.        */
  190.    
  191.       DragMouseAction = DRAG_NONE;
  192.       if (!Initializing)
  193.    {
  194.           BOOL status;
  195.      X = LOWORD (lParam);
  196.      Y = HIWORD (lParam);
  197.  
  198.      // make this behave more like a multi-select listbox (jlg)
  199.      // if click,       make the line the only one selected
  200.      // if ctrl-click,  add the line to the selection
  201.      // if shift-click, select from previously selected to current
  202.      if (GroupMultiSelect) {                                 
  203.         if ( !(wParam & MK_CONTROL) && !(wParam & MK_SHIFT) ) {
  204.            ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
  205.            }
  206.         }
  207.      if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr))
  208.        {
  209.          status = ((TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)))
  210.                      ->Selected ^= TRUE;
  211.          if (GroupMultiSelect) {                                 
  212.             if ( wParam & MK_SHIFT ) {
  213.                do {
  214.                   looping = PrevLine (&BlockPtr, &LinePtr);
  215.                   MyGroup = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
  216.                   if (MyGroup->Selected)
  217.                      looping = FALSE;
  218.                   else
  219.                      MyGroup->Selected = TRUE;   
  220.                   } 
  221.                   while (looping);
  222.                }
  223.             }
  224.          DragMouseAction = status ? DRAG_SELECT : DRAG_DESELECT;
  225.          GlobalUnlock (BlockPtr->hCurBlock);
  226.          SetCapture(hWnd); // release capture on button up
  227.        }
  228.  
  229.      InvalidateRect (hWnd, NULL, FALSE);
  230.    }
  231.       break;
  232.  
  233.     case WM_LBUTTONUP:
  234.       /*  Letting up on the left button on a group name 
  235.        *  gets us out of Dragging mode   */
  236.  
  237.       ReleaseCapture();
  238.       DragMouseAction = DRAG_NONE;
  239.       break;
  240.  
  241.     case WM_MOUSEMOVE:
  242.       /*  Code to drag the mouse and change the select/not selected
  243.        *  status of that group.
  244.        */
  245.  
  246.       if ((!Initializing) && (DragMouseAction != DRAG_NONE))
  247.       {
  248.         X = LOWORD (lParam);
  249.         Y = HIWORD (lParam);
  250.  
  251.         GetClientRect (hWnd, &myRect);
  252.         if (CursorToTextLine (X, Y, &NetDoc, &BlockPtr, &LinePtr))
  253.         {
  254.               switch (DragMouseAction)
  255.               {
  256.                case DRAG_SELECT:
  257.                  ((TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)))
  258.                     ->Selected = TRUE;
  259.                  break;
  260.                case DRAG_DESELECT:
  261.                  ((TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)))
  262.                     ->Selected = FALSE;
  263.                break;
  264.               }
  265.              GlobalUnlock (BlockPtr->hCurBlock);
  266.           }
  267.           InvalidateRect (hWnd, NULL, FALSE);
  268.       }
  269.       break;
  270.  
  271.     case WM_LBUTTONDBLCLK:
  272.       /*  Double-clicking on a group name creates a "Group"
  273.        *  window, whose purpose is to display the subjects
  274.        *  of the articles in the group.
  275.        */
  276.       if (!Initializing)
  277.    {
  278.      X = LOWORD (lParam);
  279.      Y = HIWORD (lParam);
  280.    getgroup:;
  281.  
  282.  
  283.      if (CursorToTextLine (X, Y, &NetDoc, &GroupBlockPtr, &GroupLinePtr))
  284.        {
  285.          if (MyDoc = (((TypGroup far *)
  286.       (((char far *) GroupLinePtr) + sizeof (TypLine)))->SubjDoc))
  287.       {
  288.  
  289.         /* We already have a document containing the subjects */
  290.         /* of this group, so just activate it.                */
  291.  
  292.         SetActiveWindow (MyDoc->hDocWnd);
  293.         SetFocus (MyDoc->hDocWnd);
  294.         GlobalUnlock (GroupBlockPtr->hCurBlock);
  295.         break;
  296.       }
  297.       if (TestCommBusy(hWnd, "Can't request info on group"))
  298.    {
  299.      GlobalUnlock (GroupBlockPtr->hCurBlock);
  300.      break;
  301.    }
  302.  
  303.          /* At this point, we've determined that the subjects for
  304.           * this newsgroup are not in memory, and that it's OK
  305.           * to fetch them from the server.  (Comm line not busy.)
  306.           * Figure out whether a new window/document should be
  307.           * created for this group & set "newdoc" accordingly.
  308.           */
  309.          newdoc = FALSE;
  310.          if (ViewNew || !ActiveGroupDoc || !(ActiveGroupDoc->InUse))
  311.       {
  312.         found = FALSE;
  313.         for (docnum = 0; docnum < MAXGROUPWNDS; docnum++)
  314.           {
  315.             if (!GroupDocs[docnum].InUse)
  316.          {
  317.            found = TRUE;
  318.            newdoc = TRUE;
  319.            CommDoc = &(GroupDocs[docnum]); 
  320.            CommDoc->LongestLine = 0;
  321.            CommDoc->ScXOffset = 0;
  322.            break;
  323.          }
  324.           }
  325.         if (!found)
  326.           {
  327.             MessageBox (hWnd, "You have too many group windows \
  328. active;\nClose one or select 'Reuse Old Window'.", "Can't open new window", MB_OK | MB_ICONASTERISK);
  329.             UnlockLine (GroupBlockPtr, GroupLinePtr, &hBlock, &Offset, &MyLineID);
  330.             break;
  331.           }
  332.       }
  333.          else
  334.       {
  335.         /* Must reuse old window for this group.           */  
  336.         ActiveGroupDoc->LongestLine = 0;
  337.         ActiveGroupDoc->ScXOffset = 0;
  338.         CommDoc = ActiveGroupDoc;
  339.     UpdateSeenArts (CommDoc);
  340.         UnlinkArtsInGroup (CommDoc);
  341.         LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset, CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  342.         ((TypGroup far *)
  343.          (((char far *) LinePtr) + sizeof (TypLine)))->SubjDoc = (TypDoc *) NULL;
  344.         UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  345.         /* clear out contents of this document for reuse */
  346.         FreeDoc (CommDoc);
  347.       }
  348.  
  349.          /* Create window title indicating we are retrieving text. */
  350.  
  351.          CommDoc->InUse = TRUE;
  352.          lpsz = (char far *) (((char far *) GroupLinePtr) +
  353.                sizeof (TypLine) + sizeof (TypGroup));
  354.          strcpy (mybuf, "Retrieving ");
  355.          lstrcat (mybuf, lpsz);
  356.  
  357.          /* If necessary, create a new window.              */
  358.  
  359.          if (newdoc)
  360.       {
  361.         int x,y,width,height;
  362.         char poschars[MAXINTERNALLINE];
  363.  
  364.         /* Compute default screen position. */
  365.         x = 1;
  366.         y = 0;
  367.         width = (int) xScreen;
  368.         height = (int) (yScreen * 1 / 2);
  369.  
  370.         /* If the screen position has been saved, use that instead. */
  371.         GetPrivateProfileString (szAppName, "GroupWindowPos", "!",
  372.           poschars,MAXINTERNALLINE,szAppProFile);
  373.         if(poschars[0] != '!') {
  374.           sscanf(poschars,"%d,%d,%d,%d",&x,&y,&width,&height);
  375.         }
  376.         hWndView = CreateWindow ("WinVnView",
  377.                   mybuf,
  378.                   WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
  379.                   x + (int) docnum * CharWidth,/* Initial X pos */
  380.                   y + (int) docnum * LineHeight,
  381.                   width,           /* Initial X Width */
  382.                   height,
  383.                   NULL,
  384.                   NULL,
  385.                   hInst,
  386.                   NULL);
  387.  
  388.         if (!hWndView)
  389.           return (0); /* ??? */
  390.  
  391.     SetHandleBkBrush (hWndView, hListBackgroundBrush);
  392.    ShowWindow (hWndView, SW_SHOWNORMAL);
  393.       }
  394.          else
  395.       {
  396.         hWndView = CommDoc->hDocWnd;
  397.         SetWindowText (hWndView, mybuf);
  398.       }
  399.          RcvLineCount = 0;
  400.          TimesWndUpdated = 0;
  401.  
  402.          /* Do some housekeeping:  Set group as selected,
  403.           * initialize empty document to hold subject lines,
  404.           * set focus to this document, set pointers linking
  405.           * this document and the subject line.
  406.           */
  407.          ((TypGroup far *) (((char far *) GroupLinePtr) +
  408.              sizeof (TypLine)))->Selected = TRUE;
  409.          InitDoc (CommDoc, hWndView, &NetDoc, DOCTYPE_GROUP);
  410.          PtrToOffset (GroupBlockPtr, GroupLinePtr, &(CommDoc->hParentBlock),
  411.          &(CommDoc->ParentOffset), &(CommDoc->ParentLineID));
  412.          SetActiveWindow (hWndView);
  413.          SetFocus (hWndView);
  414.  
  415.          ((TypGroup far *) (((char far *) GroupLinePtr) + sizeof (TypLine)))
  416.       ->SubjDoc = CommDoc;
  417.          GlobalUnlock (GroupBlockPtr->hCurBlock);
  418.          InvalidateRect (hWndView, NULL, FALSE);
  419.  
  420.          UpdateWindow (hWndView);
  421.  
  422.          InvalidateRect (NetDoc.hDocWnd, NULL, FALSE);
  423.  
  424.          /* Deal with Comm-related stuff:  set FSA variables,
  425.           * send the GROUP command to NNTP.
  426.           */
  427.  
  428.          CommLinePtr = CommLineIn;
  429.          CommBusy = TRUE;
  430.          CommState = ST_GROUP_RESP;
  431.          hSaveCursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
  432.          ShowCursor (TRUE);
  433.  
  434. /* capture the mouse to the usenet window, so that we keep the hourglass */
  435.  
  436. //       SetCapture (hWnd);
  437.          strcpy (mybuf, "GROUP ");
  438.          lpsz = (char far *) GroupLinePtr + sizeof (TypLine) + sizeof (TypGroup);
  439.          lstrcat (mybuf, lpsz);
  440.          mylstrncpy (CurrentGroup, lpsz, MAXFINDSTRING);
  441.          PutCommLine (mybuf);
  442.        }
  443.    }
  444.       break;
  445.  
  446. // If we've lost the connection for some reason, kill the timer
  447. // that's banging on the socket.
  448.     case WM_TIMER:
  449.      if ((CommState == ST_CLOSED_COMM) && (!Initializing))
  450.        {
  451.      KillTimer(hWnd,ID_TIMER) ;
  452.        }
  453.      else 
  454.        {
  455.     if (CommState != ST_CLOSED_COMM)
  456.       DoCommInput ();
  457.        };
  458.       break;
  459.  
  460.     case WM_SIZE:
  461.       /* Store the new size of the window.                     */
  462.       GetClientRect (hWnd, &myRect);
  463.       NetDoc.ScXWidth = myRect.right;
  464.       NetDoc.ScYHeight = myRect.bottom;
  465.       NetDoc.ScYLines = (myRect.bottom - myRect.top - TopSpace) / LineHeight;
  466.       NetDoc.ScXChars = (myRect.right - myRect.left - SideSpace) / CharWidth;
  467.       break;
  468.  
  469.     case WM_VSCROLL:
  470.       if (!Initializing)
  471.    {
  472.      ScrollIt (&NetDoc, wParam, lParam);
  473.    }
  474.       break;
  475.  
  476.     case WM_HSCROLL:
  477.       if (!Initializing)
  478.       {
  479.         HScrollIt (&NetDoc, wParam, lParam);
  480.       }
  481.       break;
  482.  
  483.     case WM_COMMAND:
  484.       switch (LOWORD(wParam))
  485.    {
  486.                                                            
  487.    case IDM_QUIT:
  488.      SaveNewsrc = SaveConfig = FALSE;
  489.      DestroyWindow (hWnd);
  490.      break;
  491.  
  492.    case IDM_EXIT: 
  493.       SendMessage(hWnd, WM_CLOSE, 0, 0L);
  494.       break;
  495.  
  496.    case IDM_VIEW_SEL_GROUP:
  497.      break;
  498.  
  499.    case IDM_SHOW_SUBSCR:
  500.    case IDM_SHOW_ALL_GROUP:
  501.    case IDM_SEL_SUBSCR:
  502.    case IDM_SELECTALL:
  503.      MessageBox (hWnd, "Command not implemented",
  504.             "Sorry", MB_OK);
  505.      break;
  506.  
  507.    case IDM_UNSEL_ALL:
  508.      ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
  509.      InvalidateRect (hWnd, NULL, FALSE);
  510.      break;
  511.  
  512.    case IDM_SUBSCRIBE:
  513.      InitGroupTable ();
  514.      ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
  515.      BuildPtrList ();
  516.      MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
  517.      CleanUpGroupTable ();
  518.      ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
  519.      NetDoc.LongestLine = 0;
  520.      SetGroupActiveLines();
  521.      ScreenToTop (&NetDoc);
  522.      SetNetDocTitle ();
  523.      InvalidateRect (hWnd, NULL, FALSE);
  524.      break;
  525.  
  526.    case IDM_UNSUBSCRIBE:
  527.      InitGroupTable ();
  528.      ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_UNSUBSCRIBE, FALSE);
  529.      BuildPtrList ();
  530.      ShellSort (NewGroupTable,
  531.         (size_t) nNewGroups,
  532.         (size_t) sizeof (void far *),
  533.         GroupCompare);
  534.      MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
  535.      CleanUpGroupTable ();
  536.      ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
  537.      NetDoc.LongestLine = 0;
  538.      SetGroupActiveLines();
  539.      ScreenToTop (&NetDoc);
  540.      SetNetDocTitle ();
  541.      InvalidateRect (hWnd, NULL, FALSE);
  542.      break;
  543.  
  544.    case IDM_GROUP_TOP:
  545.      InitGroupTable ();
  546.      ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_SUBSCRIBE, TRUE);
  547.      BuildPtrList ();
  548.      MergeGroups (ADD_SUBSCRIBED_TOP_OF_DOC);
  549.      CleanUpGroupTable ();
  550.      ForAllLines (&NetDoc, SetLineFlag, 0, FALSE);
  551.      ScreenToTop (&NetDoc);
  552.      InvalidateRect (hWnd, NULL, FALSE);
  553.      break;
  554.  
  555. #if 0 // This code not used anywhere (JSC 1/12/94)
  556.    case IDM_NEW_WIN_GROUP:
  557.      ViewNew = !ViewNew;
  558.      CheckView (hWnd);
  559.      break;
  560.  
  561.    case IDM_NEW_WIN_ARTICLE:
  562.      NewArticleWindow = !NewArticleWindow;
  563.      CheckView (hWnd);
  564.      break;
  565. #endif
  566.  
  567.    case IDM_COMMOPTIONS:
  568.  
  569.      if (DialogBox (hInst, "WinVnComm", hWnd, lpfnWinVnCommDlg))
  570.        {
  571.          InvalidateRect (hWnd, NULL, TRUE);
  572.        }
  573.  
  574.      break;
  575.  
  576.    case IDM_CONFIG_PERSONAL:
  577.  
  578.      DialogBox (hInst, "WinVnPersonal", hWnd, lpfnWinVnPersonalInfoDlg);
  579.      break;
  580.  
  581.    case IDM_CONFIG_MISC:
  582.  
  583.      DialogBox (hInst, "WinVnMisc", hWnd, lpfnWinVnMiscDlg);
  584.  
  585.      break;
  586.  
  587.    case IDM_CONFIG_LOG:
  588.  
  589.      DialogBox (hInst, "WinVnLogOpts", hWnd, lpfnWinVnLogOptDlg);
  590.  
  591.      break;
  592.  
  593.    case IDM_CONFIG_SIGFILE:
  594.      if (DialogBox (hInst, "WinVnSigFile", hWndConf, lpfnWinVnSigFileDlg))
  595.        {
  596.          InvalidateRect (hWnd, NULL, TRUE);
  597.        }
  598.  
  599.      break;
  600.  
  601.    case IDM_CONFIG_SMART_FILER:
  602.      DialogBox(hInst, "WinvnSmartFiler", hWndConf, lpfnWinVnSmartFilerDlg);
  603.      break;
  604.  
  605.    case IDM_FONT_GROUPLIST:
  606.      if (AskForFont (hWndConf, ListFontFace, &ListFontSize, ListFontStyle)) 
  607.        break;
  608.      InitListFonts();
  609.      SendMessage (hWndConf, WM_SIZE, 0, 0L);
  610.      InvalidateRect (hWndConf, NULL, TRUE);
  611.      RefreshGroupWnds();
  612.      break;
  613.  
  614.    case IDM_FONT_ARTICLE_TEXT:
  615.      if (AskForFont (hWndConf, ArticleFontFace, &ArticleFontSize, ArticleFontStyle)) 
  616.        break;
  617.      InitArticleFonts();
  618.      RefreshArticleWnds();
  619.      break;
  620.  
  621.    case IDM_FONT_STATUS_TEXT:
  622.     askStatusFont:;
  623.      if (AskForFont (hWndConf, StatusFontFace, &StatusFontSize, StatusFontStyle)) 
  624.         break;
  625.      InitStatusFonts();
  626.      if (STATUSWIDTH > xScreen)
  627.      {
  628.        MessageBox (hWndConf,"The status window will not fit on your screen with this font.\nPlease select a smaller font",
  629.                        "Font too big", MB_OK|MB_ICONSTOP);
  630.        goto askStatusFont;
  631.      }
  632.      RefreshStatusWnds();
  633.      break;
  634.  
  635.    case IDM_FONT_PRINT_TEXT:
  636.      if (AskForFont (hWndConf, PrintFontFace, &PrintFontSize, "Printer")) 
  637.        break;
  638.      InitPrintFonts();
  639.      InvalidateRect (hWndConf, NULL, TRUE);
  640.      break;
  641.  
  642.    case IDM_COLOR_SUBSCRIBED:
  643.      if (AskForColor (hWndConf, &NetSubscribedColor))
  644.        break;
  645.      InvalidateRect (hWndConf, NULL, TRUE);
  646.      break;
  647.  
  648.    case IDM_COLOR_UNSUBSCRIBED:
  649.      if (AskForColor (hWndConf, &NetUnSubscribedColor))
  650.        break;
  651.      InvalidateRect (hWndConf, NULL, TRUE);
  652.      break;
  653.  
  654.    case IDM_COLOR_SEEN:
  655.      if (AskForColor (hWndConf, &ArticleSeenColor))
  656.        break;
  657.      RefreshGroupWnds();
  658.      break;
  659.  
  660.    case IDM_COLOR_UNSEEN:
  661.      if (AskForColor (hWndConf, &ArticleUnSeenColor))
  662.        break;
  663.      RefreshGroupWnds();
  664.      break;
  665.  
  666.    case IDM_COLOR_ARTICLE_TEXT:
  667.      if (AskForColor (hWndConf, &ArticleTextColor))
  668.        break;
  669.      RefreshArticleWnds();
  670.      break;
  671.  
  672.    case IDM_COLOR_STATUS_TEXT:
  673.      if (AskForColor (hWndConf, &StatusTextColor))
  674.        break;
  675.      RefreshStatusWnds();
  676.      break;
  677.  
  678.    case IDM_COLOR_LIST_BACKGROUND:
  679.      if (AskForColor (hWndConf, &ListBackgroundColor))
  680.        break;
  681.      DeleteObject (hListBackgroundBrush);
  682.      hListBackgroundBrush = CreateSolidBrush (ListBackgroundColor);
  683.      SetHandleBkBrush (hWnd, hListBackgroundBrush);
  684.  
  685.      RefreshGroupWnds();
  686.      InvalidateRect (hWndConf, NULL, TRUE);
  687.      break;
  688.  
  689.    case IDM_COLOR_ARTICLE_BACKGROUND:
  690.      if (AskForColor (hWndConf, &ArticleBackgroundColor))
  691.         break;
  692.       DeleteObject (hArticleBackgroundBrush);
  693.       hArticleBackgroundBrush = CreateSolidBrush (ArticleBackgroundColor);
  694.       RefreshArticleWnds();
  695.       break;
  696.  
  697.    case IDM_COLOR_STATUS_BACKGROUND:
  698.      if (AskForColor (hWndConf, &StatusBackgroundColor))
  699.        break;
  700.      DeleteObject (hStatusBackgroundBrush);
  701.      hStatusBackgroundBrush = CreateSolidBrush (StatusBackgroundColor);
  702.      RefreshStatusWnds();
  703.      break;
  704.  
  705.  
  706.    case IDM_WINDOW_CASCADE:
  707.      CascadeWindows();
  708.      break;
  709.    
  710.    case IDM_CLOSE_ALL:
  711.      if (ConfirmBatchOps)
  712.        if (MessageBox (hWndConf, "Are you sure you wish to close all open windows?", 
  713.                        "Please confirm", MB_YESNO|MB_ICONQUESTION) == IDNO)
  714.          break;
  715.      CloseArticleWnds();
  716.      CloseGroupWnds();
  717.      CloseStatusWnds();
  718.      break;
  719.  
  720.    case IDM_CLOSE_ALL_ARTICLE:
  721.      if (ConfirmBatchOps)
  722.        if (MessageBox (hWndConf, "Are you sure you wish to close all open article windows?", 
  723.                        "Please confirm", MB_YESNO|MB_ICONQUESTION) == IDNO)
  724.          break;
  725.      CloseArticleWnds();
  726.      break;
  727.  
  728.    case IDM_CLOSE_ALL_GROUP:
  729.      if (ConfirmBatchOps)
  730.        if (MessageBox (hWndConf,"Are you sure you wish to close all open group windows?", 
  731.                        "Please confirm", MB_YESNO|MB_ICONQUESTION) == IDNO)
  732.          break;
  733.      CloseGroupWnds();
  734.      break;
  735.  
  736.    case IDM_CLOSE_ALL_STATUS:
  737.      if (ConfirmBatchOps)
  738.        if (MessageBox (hWndConf, "Are you sure you wish to close all open status windows?", 
  739.                        "Please confirm", MB_YESNO|MB_ICONQUESTION) == IDNO)
  740.          break;
  741.      CloseStatusWnds();
  742.      break;
  743.  
  744.    case IDM_SEND_ALL_POST:
  745.      if (ConfirmBatchOps)
  746.        if (MessageBox (hWndConf, "Are you sure you wish to send all open post windows?", 
  747.                        "Please confirm", MB_YESNO|MB_ICONQUESTION) == IDNO)
  748.          break;
  749.      BatchSend(DOCTYPE_POSTING);
  750.      break;
  751.  
  752.    case IDM_SEND_ALL_MAIL:
  753.      if (ConfirmBatchOps)
  754.        if (MessageBox (hWndConf, "Are you sure you wish to send all open mail windows?", 
  755.                        "Please confirm", MB_YESNO|MB_ICONQUESTION) == IDNO)
  756.          break;
  757.      BatchSend(DOCTYPE_MAIL);
  758.      break;
  759.  
  760.    case IDM_DECODE_FILE:
  761.      if (TestDecodeBusy(hWndConf, "Can't decode file"))
  762.        break;
  763.  
  764.      if (!DialogBoxParam (hInst, "WinVnDecodeArts", hWndConf, lpfnWinVnDecodeArtsDlg, 0))
  765.         InvalidateRect (hWndConf, NULL, TRUE);
  766.      else
  767.         DecodeFile(hWnd);
  768.      break;
  769.  
  770.    case IDM_ENCODE_FILE:
  771.       if (TestDecodeBusy(hWndConf, "Can't encode file"))
  772.         break;
  773.       if (!DialogBox (hInst, "WinVnEncode", hWndConf, lpfnWinVnEncodeDlg))
  774.          InvalidateRect (hWndConf, NULL, TRUE);
  775.       else
  776.          EncodeToFile (hWndConf, AttachFileName);
  777.       break;
  778.  
  779.    case IDM_RESET:
  780.      AbortAllComm();
  781.      break;
  782.  
  783.    case IDM_CONNECT:
  784.      Connect (); // menus are enabled in WM_PAINT once connection is established
  785.      break;
  786.  
  787.    case IDM_DISCONNECT:
  788.      /* If the NNTP server disconnected, then Initializing == INIT_NOT_CONNECTED */
  789.      if ((Initializing != INIT_NOT_CONNECTED)
  790.         && (MessageBox (hWndConf, 
  791.                    "Are you sure you wish to disconnect from the server?\n"
  792.                    "All active windows will be closed.",
  793.                         "Please confirm", MB_YESNO|MB_ICONQUESTION) == IDNO))
  794.         break;
  795.  
  796.    //  PutCommLine ("QUIT\r\n");      // breaks Pathworks stack 
  797.        PutCommData ("QUIT\r\n",6);
  798.      KillTimer(hWnd, ID_TIMER);
  799.  
  800.      Initializing = INIT_NOT_CONNECTED;
  801.      SetUserMenus (hWnd, DISABLE);
  802.      InvalidateRect (hWnd, NULL, TRUE);
  803.      UpdateWindow (hWnd);
  804.  
  805.      MRRCloseComm();     // blammo
  806.  
  807.      if (Decoding)
  808.      {
  809.          CompleteThisDecode();
  810.          DecodeDone();
  811.      }
  812.      else if (Attaching)
  813.          FinishAttachment (ABORT);
  814.  
  815.      CommDoc = NULL;
  816.      CloseArticleWnds();
  817.      CloseGroupWnds();
  818.      CloseStatusWnds();
  819.     
  820.      break;
  821.  
  822.    case IDM_SAVE_CONFIG:
  823.      WriteWinvnProfile();
  824.      MessageBox (hWndConf, "WinVn Configuration Saved", "WinVn Configuration", MB_OK);
  825.      break;
  826.  
  827.    case IDM_SAVE_WINDOW:
  828.      {
  829.      RECT rect;
  830.  
  831.      /* Save position and size of Usenet window. */
  832.      GetWindowRect(hWndConf,&rect);
  833.      sprintf(mybuf,"%d,%d,%d,%d",rect.left,rect.top,
  834.        rect.right-rect.left,rect.bottom-rect.top);
  835.      WritePrivateProfileString
  836.       (szAppName, "UsenetWindowPos", mybuf, szAppProFile);
  837.  
  838.  
  839.      /* Save position and size of first Group window */
  840.  
  841.      for(found=FALSE,idoc=0; !found && idoc<MAXGROUPWNDS; idoc++) {
  842.        if(GroupDocs[idoc].InUse) {
  843.          GetWindowRect(GroupDocs[idoc].hDocWnd,&rect);
  844.          found = TRUE;
  845.          sprintf(mybuf,"%d,%d,%d,%d",rect.left,rect.top,
  846.             rect.right-rect.left,rect.bottom-rect.top);
  847.          WritePrivateProfileString
  848.            (szAppName, "GroupWindowPos", mybuf, szAppProFile);
  849.        }
  850.      }
  851.  
  852.      /* Save position and size of first Article window */
  853.  
  854.      for(found=FALSE,idoc=0; !found && idoc<MAXARTICLEWNDS; idoc++) {
  855.        if(ArticleDocs[idoc].InUse) {
  856.          GetWindowRect(ArticleDocs[idoc].hDocWnd,&rect);
  857.          found = TRUE;
  858.          sprintf(mybuf,"%d,%d,%d,%d",rect.left,rect.top,
  859.             rect.right-rect.left,rect.bottom-rect.top);
  860.          WritePrivateProfileString
  861.            (szAppName, "ArticleWindowPos", mybuf, szAppProFile);
  862.        }
  863.      }
  864.  
  865.      break;
  866.      }
  867.  
  868.    case IDM_FIND:
  869.      FindDoc = &NetDoc;
  870.      if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0]) 
  871.         strcpy(FindDoc->SearchStr, LastGroupNameFind);
  872.  
  873.      if (DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg))
  874.        {
  875.          found = DoFind (TRUE);
  876.          if (!found)
  877.       {
  878.         strcpy (mybuf, "\"");
  879.         strcat (mybuf, NetDoc.SearchStr);
  880.         strcat (mybuf, "\" not found.");
  881.         MessageBox (hWnd, mybuf, "Not found", MB_OK);
  882.       }
  883.       else
  884.           strcpy(LastGroupNameFind, FindDoc->SearchStr);
  885.  
  886.        }
  887.      break;
  888.  
  889.    case IDM_FIND_NEXT_SAME:
  890.      FindDoc = &NetDoc;
  891.    
  892.      continueFind = TRUE;
  893.      if (!FindDoc->SearchStr[0]) {
  894.        if (!(FindDoc->SearchStr[0]) && LastGroupNameFind[0]) 
  895.           strcpy(FindDoc->SearchStr, LastGroupNameFind);
  896.        continueFind = DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
  897.      }
  898.  
  899.      if (continueFind && FindDoc->SearchStr[0])
  900.        {
  901.          found = DoFind (!FindDoc->hFindBlock && !FindDoc->FindLineID);
  902.          if (!found)
  903.       {
  904.         strcpy (mybuf, "\"");
  905.         strcat (mybuf, NetDoc.SearchStr);
  906.         strcat (mybuf, "\" not found.");
  907.         MessageBox (hWnd, mybuf, "No more occurrences", MB_OK);
  908.       }
  909.        }
  910.      break;
  911.  
  912.    case ID_ABOUT:
  913.      lpProcAbout = (DLGPROC)MakeProcInstance ((FARPROC)About, hInst);
  914.      DialogBox (hInst, "AboutBox", hWnd, lpProcAbout);
  915.      FreeProcInstance ((FARPROC)lpProcAbout);
  916.      break;
  917.  
  918.    case IDM_HELP:
  919.      MakeHelpPathName (mybuf, MAXINTERNALLINE);
  920.      WinHelp (hWndConf, mybuf, HELP_INDEX, 0L);
  921.      break;
  922.  
  923.    case IDM_MAIL:
  924.      (MailCtrl.fnMlWinCreate)(hWnd, (TypDoc *) NULL, DOCTYPE_MAIL);
  925.      break;
  926.  
  927.    case IDM_LOGOUT:
  928.      (MailCtrl.fnMlLogout)(hWnd);
  929.      break;
  930.  
  931.    case IDM_SHOW_VERSION:
  932.      show_version_strings(hWnd);
  933.      break;
  934.  
  935.    case IDM_POST:
  936.      DialogBoxParam (hInst, "WinVnGeneric", hWnd,
  937.            lpfnWinVnGenericDlg, (LPARAM) (char far *) "Newsgroup(s):");
  938.      NewsgroupsPtr = DialogString;
  939.      CreatePostingWnd (hWnd, (TypDoc *) NULL, DOCTYPE_POSTING);
  940.      break;
  941.  
  942.    }
  943.       break;
  944.  
  945.     case WM_PAINT:
  946.       {
  947.    HANDLE hBlock;
  948.    SIZE sz;
  949.    unsigned int Offset, MyLen, indicatorwidth;
  950.    int VertLines, HorzChars;
  951.    int EndofDoc = FALSE;
  952.    int RangeHigh, CurPos;
  953.    int Xtext;
  954.    char far *textptr;
  955.    char *cptr, *cptr2;
  956.    char indicator;
  957.    char indcptr[128];
  958.    char group_name[MAXINTERNALLINE];
  959.    char init_msg[60];
  960.    TypGroup far *MyGroup;
  961.    TypRange far *RangePtr;
  962.    unsigned long int   HighestRead;
  963.    COLORREF MyColors[4], MyBack[4];
  964. // DWORD Rop;
  965.    RECT myRect, aRect;
  966.    int MyColorMask = 1, PrevColorMask = MyColorMask;
  967. #define SUBSCR_MASK 1
  968. #define SELECT_MASK 2
  969.  
  970.    hDC = BeginPaint (hWnd, &ps); 
  971.    SetBkColor (hDC, ListBackgroundColor);
  972.    if (ListBackgroundColor == RGB(0,0,0))
  973.       SetTextColor(hDC, RGB(200,200,200));     // if background is black, make text white
  974.    else
  975.       SetTextColor(hDC, RGB(0,0,0));        // otherwise text is black
  976.  
  977.    GetClientRect (hWnd, &myRect);
  978.    SelectObject (hDC, hListFont);
  979.  
  980.    /* If still initializing comm link, put out a message    */
  981.    /* to that effect.                                       */
  982.  
  983.    switch (Initializing)
  984.      {
  985.      case INIT_NOT_CONNECTED:
  986.        cptr = "Not connected";
  987.        cptr2 = "to news server";
  988.  
  989.        goto display_msg;
  990.  
  991.      case INIT_ESTAB_CONN:
  992.        cptr = "Establishing link";
  993.        cptr2 = "to news server...";
  994.  
  995.        goto display_msg;
  996.  
  997.      case INIT_READING_NEWSRC:
  998.        cptr = "Reading your log";
  999.        cptr2 = "(\"newsrc\")...";
  1000.        goto display_msg;
  1001.  
  1002.      case INIT_SCANNING_NETDOC:
  1003.        cptr = "Creating hash table";
  1004.        cptr2 = "from current groups...";
  1005.        goto display_msg;
  1006.  
  1007.      case INIT_GETTING_LIST:
  1008.        sprintf (mybuf, "Receiving %uth newsgroup", RcvLineCount);
  1009.        cptr = mybuf;
  1010.        cptr2 = "name from server...";
  1011.  
  1012.      display_msg:;
  1013.        X = SideSpace + CharWidth;
  1014.        Y = StartPen + 2 * LineHeight;
  1015.        strcpy (init_msg, cptr);
  1016.        strcat (init_msg, "    ");
  1017.        j = strlen (init_msg);
  1018.        TextOut (hDC, X, Y, init_msg, j);
  1019.        strcpy (init_msg, cptr2);
  1020.        strcat (init_msg, "    ");
  1021.        j = strlen (init_msg);
  1022.        TextOut (hDC, X, Y + LineHeight, init_msg, j);
  1023.        break;
  1024.  
  1025.      case INIT_READY:
  1026.         /* this state happens when all connection prep is finished 
  1027.          * and we are ready to begin normal WinVn user operation
  1028.          */
  1029.          SetUserMenus (hWnd, ENABLE);
  1030.         Initializing = INIT_DONE;
  1031.         // fall into INIT_DONE
  1032.  
  1033.      case INIT_DONE:
  1034.  
  1035.        VertLines = NetDoc.ScYLines;
  1036.        HorzChars = NetDoc.ScXChars;
  1037.        MyColors[0] = NetUnSubscribedColor;      // unseen/unselected    
  1038.        MyColors[1] = NetSubscribedColor;     // seen/unselected
  1039.        MyColors[2] = MyColors[0];         // unseen/selected
  1040.        MyColors[3] = ListBackgroundColor;    // seen/selected
  1041.  
  1042.        MyBack[0] = MyColors[3];
  1043.        MyBack[1] = MyBack[0];
  1044.        if (ListBackgroundColor == RGB(0,0,0))
  1045.          MyBack[2] = RGB(200,200,200);    // selected = white background
  1046.        else
  1047.          MyBack[2] = RGB(0,0,0);       // selected = black background
  1048. //       MyBack[2] = MyColors[1];
  1049.        MyBack[3] = MyBack[2];
  1050.  
  1051.        SetTextColor (hDC, MyColors[MyColorMask]);
  1052.        SetBkColor (hDC, MyBack[MyColorMask]);
  1053.  
  1054.        GetTextExtentPoint (hDC, "*", 2, &sz);             
  1055.        indicatorwidth = sz.cx*7*7;                        
  1056.  
  1057.        LockLine (NetDoc.hCurTopScBlock, NetDoc.TopScOffset, NetDoc.TopScLineID,
  1058.             &BlockPtr, &LinePtr);
  1059.  
  1060.        /* Update the scroll bar thumb position.                 */
  1061.  
  1062.        CurPos = NetDoc.TopLineOrd;
  1063.        if (CurPos < 0)
  1064.          CurPos = 0;   
  1065.        RangeHigh = NetDoc.ActiveLines - VertLines;;
  1066.        if (RangeHigh < 0)
  1067.          RangeHigh = 0;
  1068.        SetScrollRange (hWnd, SB_VERT, 0, RangeHigh, FALSE);
  1069.        SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
  1070.  
  1071.        RangeHigh = NetDoc.LongestLine - NetDoc.ScXChars; 
  1072.        if (RangeHigh < 0) 
  1073.        {
  1074.          RangeHigh = 0;
  1075.          NetDoc.ScXOffset = 0;
  1076.        }
  1077.        SetScrollRange (hWnd, SB_HORZ, 0, RangeHigh, FALSE);
  1078.        SetScrollPos (hWnd, SB_HORZ, NetDoc.ScXOffset, TRUE); 
  1079.  
  1080.        /* Loop through the lines, painting them on the screen. */
  1081.  
  1082.        X = SideSpace - NetDoc.ScXOffset * (CharWidth+1);
  1083.        Xtext = X + indicatorwidth;
  1084.        Y = StartPen;
  1085.        if (LinePtr->length != END_OF_BLOCK)
  1086.          do
  1087.        {                               
  1088.         MyGroup = ((TypGroup far *)
  1089.               ((char far *) LinePtr + sizeof (TypLine)));
  1090.         MyLen = MyGroup->NameLen;
  1091.         textptr = (char far *) LinePtr + sizeof (TypLine) +
  1092.           sizeof (TypGroup); 
  1093.           
  1094.         /* Display this line only if it is active. */  
  1095.         if(LinePtr->active) {
  1096.  
  1097.         /* Figure out the color of this line.                 */
  1098.  
  1099.         if (MyGroup->Subscribed)
  1100.           {
  1101.             MyColorMask |= SUBSCR_MASK;
  1102.           }
  1103.         else
  1104.           {
  1105.             MyColorMask &= (0xff - SUBSCR_MASK);
  1106.           }
  1107.         if (MyGroup->Selected)
  1108.           {
  1109.             MyColorMask |= SELECT_MASK;
  1110.           }
  1111.         else
  1112.           {
  1113.             MyColorMask &= 0xff - SELECT_MASK;
  1114.           }
  1115.         if (MyColorMask != PrevColorMask)
  1116.           {
  1117.             SetTextColor (hDC, MyColors[MyColorMask]);
  1118.             SetBkColor (hDC, MyBack[MyColorMask]);
  1119.             PrevColorMask = MyColorMask;
  1120.           }
  1121.  
  1122.         /* Figure out what indicator character to use. */
  1123.  
  1124.         indicator = ' ';
  1125.         if (NetDoc.FindLineID == LinePtr->LineID)
  1126.           {
  1127.             indicator = '>';
  1128.           }
  1129.         else if (MyGroup->HighestPrevSeen)
  1130.           {
  1131.             if (MyGroup->ServerLast > MyGroup->HighestPrevSeen)
  1132.             {
  1133.               indicator = '*';
  1134.             }
  1135.             else {
  1136.               /* check if there are unread articles - jlg 4/9/94 */
  1137.               RangePtr = (TypRange far *) ((char far *) MyGroup + RangeOffset (MyGroup->NameLen));
  1138.               if (MyGroup->nRanges) {
  1139.                  HighestRead = RangePtr[MyGroup->nRanges-1].Last;
  1140.                  if (MyGroup->ServerLast > HighestRead && MyGroup->ServerEstNum > 0)
  1141.                    indicator = '*';
  1142.                  }
  1143.             }
  1144.           }
  1145.  
  1146.         mylstrncpy (group_name, textptr, MyGroup->NameLen + 1);
  1147.  
  1148.         if (MyGroup->Determined == FALSE)
  1149.            strcpy(str, "-");
  1150.         else
  1151.            sprintf(str, "%5lu", MyGroup->ServerEstNum); 
  1152.         
  1153.         sprintf (indcptr, "%c %5s %s ", indicator, str, group_name);
  1154.  
  1155.         /* Now write out the line.                            */
  1156.  
  1157.         MyLen = strlen (indcptr);
  1158.  
  1159.         SetRect (&aRect, 0, Y, myRect.right, Y+LineHeight);
  1160.         
  1161.         ExtTextOut (hDC, X, Y, ETO_OPAQUE|ETO_CLIPPED, &aRect,
  1162.                     indcptr, MyLen, (LPINT)NULL);
  1163.        
  1164.         Y += LineHeight;
  1165.         --VertLines;
  1166.         }  /* end if LinePtr->active */  
  1167.        }
  1168.        while (VertLines > 0 && NextLine (&BlockPtr, &LinePtr));
  1169.  
  1170. //       SetTextColor (hDC, MyColors[1]);
  1171. //       SetBkColor (hDC, MyBack[1]);
  1172. //       SelectObject (hDC, UnSelectedBrush);
  1173.  
  1174.        /* Blank out bottom and top of screen */
  1175.        SelectObject (hDC, hListBackgroundBrush);
  1176.        PatBlt (hDC, 0, Y, myRect.right-1, myRect.bottom - Y, PATCOPY);
  1177.        PatBlt (hDC, 0, 0, myRect.right-1, TopSpace, PATCOPY);
  1178.  
  1179.        UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  1180.      }
  1181.      EndPaint (hWnd, &ps);
  1182.      break;
  1183.     }
  1184.  
  1185.     case WM_ENDSESSION:
  1186.  
  1187.       WinHelp (hWndConf, LFN_HELP, HELP_QUIT, 0L);
  1188.       //CloseComm (CommDevice);
  1189.  
  1190.       break;
  1191.  
  1192.     case WM_CLOSE:
  1193.       if (!ConfirmExit) {
  1194.         SaveNewsrc = SaveConfig = TRUE;
  1195.       } else {
  1196.         // WinVnExitDlg sets SaveNewsrc and SaveConfig
  1197.         if (!DialogBox (hInst, "WinVnExit", hWnd, lpfnWinVnExitDlg))
  1198.         {
  1199.           InvalidateRect (hWnd, NULL, TRUE);
  1200.           break;
  1201.         }
  1202.       } 
  1203.       DestroyWindow (hWnd);
  1204.       break;
  1205.  
  1206.     case WM_DESTROY:
  1207. #if 0    /* happens as each group window is destroyed by CloseGroupWnds (jsc) */
  1208.       for (idoc = 0; idoc < MAXGROUPWNDS; idoc++)
  1209.    {
  1210.      if (GroupDocs[idoc].InUse)
  1211.        {
  1212.          UpdateSeenArts (&(GroupDocs[idoc]));
  1213.        }
  1214.    }
  1215. #endif
  1216.       if (Initializing != INIT_NOT_CONNECTED) {
  1217.   //       PutCommLine ("QUIT\r\n");                // breaks pathworks 
  1218.            PutCommData ("QUIT\r\n",6);
  1219.          MRRCloseComm ();
  1220.       }
  1221.  
  1222.       MailClose(hWnd);
  1223.  
  1224.       AbortAllComm();
  1225.  
  1226.       /* force client wnds to close before doing WriteNewsrc */
  1227.       CloseArticleWnds();    
  1228.       CloseGroupWnds();    
  1229.       CloseStatusWnds();
  1230.  
  1231.       if (SaveNewsrc)    /* do we need  && !Initializing? */
  1232.         WriteNewsrc();
  1233.       
  1234.       if (SaveConfig)
  1235.           WriteWinvnProfile();
  1236.  
  1237.       /* Remove resources before exiting program */
  1238.       FreeTextBlock (Signature);
  1239.       if (MailList)
  1240.         FreeTextBlock (MailList);
  1241.       
  1242.       DestroyFonts();
  1243.       DestroyBitmaps();
  1244.       // deselect the brush from the DC, or we can't free it
  1245.      SetHandleBkBrush (hWndConf, GetStockObject (WHITE_BRUSH));
  1246.       if (hListBackgroundBrush) DeleteObject(hListBackgroundBrush);
  1247.       if (hArticleBackgroundBrush) DeleteObject(hArticleBackgroundBrush);
  1248.       if (hStatusBackgroundBrush) DeleteObject(hStatusBackgroundBrush);
  1249.  
  1250.       PostQuitMessage (0);
  1251.       break;
  1252.  
  1253.     default:
  1254.       return (DefWindowProc (hWnd, message, wParam, lParam));
  1255.     }
  1256.   return (0);
  1257. }
  1258.  
  1259. void
  1260. SetConnectMenuItem (HWND hWnd, int enable)
  1261. {
  1262.     HMENU hMenu, hSubMenu;
  1263.     UINT mode;
  1264.     
  1265.     if (enable == ENABLE)    
  1266.         mode = MF_BYCOMMAND|MF_ENABLED;
  1267.     else
  1268.         mode = MF_BYCOMMAND|MF_DISABLED|MF_GRAYED;
  1269.     
  1270.     hMenu = GetMenu (hWnd);
  1271.     hSubMenu = GetSubMenu (hMenu, 0);    // config menu
  1272.     EnableMenuItem (hSubMenu, IDM_CONNECT, mode);
  1273. }
  1274.  
  1275. void
  1276. SetMainMailMenu (HWND hWnd)
  1277. {
  1278.     HMENU hMenu, hSubMenu;
  1279.     
  1280.     hMenu = GetMenu (hWnd);
  1281.     hSubMenu = GetSubMenu (hMenu, 0);    // network menu
  1282.       EnableMenuItem (hSubMenu, IDM_LOGOUT, MailCtrl.enableLogout) ;    
  1283.  
  1284.     hSubMenu = GetSubMenu (hMenu, 2);    // utilities menu
  1285.     EnableMenuItem (hSubMenu, IDM_MAIL, MailCtrl.enableMail) ;    
  1286. }
  1287.  
  1288. void
  1289. SetUserMenus (HWND hWnd, int enable)
  1290. {
  1291.     HMENU hMenu, hSubMenu;
  1292.     UINT mode;
  1293.     
  1294.     if (enable == ENABLE)    
  1295.         mode = MF_BYCOMMAND|MF_ENABLED;
  1296.     else
  1297.         mode = MF_BYCOMMAND|MF_DISABLED|MF_GRAYED;
  1298.     
  1299.     hMenu = GetMenu (hWnd);
  1300.     hSubMenu = GetSubMenu (hMenu, 1);    // group menu
  1301.     EnableMenuItem (hSubMenu, IDM_FIND, mode);
  1302.     EnableMenuItem (hSubMenu, IDM_FIND_NEXT_SAME, mode);
  1303.     EnableMenuItem (hSubMenu, IDM_SUBSCRIBE, mode);
  1304.     EnableMenuItem (hSubMenu, IDM_UNSUBSCRIBE, mode);
  1305.     EnableMenuItem (hSubMenu, IDM_GROUP_TOP, mode);
  1306.     EnableMenuItem (hSubMenu, IDM_UNSEL_ALL, mode);
  1307.  
  1308.     hSubMenu = GetSubMenu (hMenu, 2);    // utilities menu
  1309.     EnableMenuItem (hSubMenu, IDM_POST, mode);
  1310.     EnableMenuItem (hSubMenu, 3, mode|MF_BYPOSITION ) ;      // batch submenu
  1311.     
  1312.     hSubMenu = GetSubMenu (hMenu, 0);    // network menu
  1313.     EnableMenuItem (hSubMenu, IDM_RESET, mode);
  1314.     EnableMenuItem (hSubMenu, IDM_DISCONNECT, mode);
  1315.  
  1316.     SetConnectMenuItem (hWnd, !enable);        // connect item has reverse attributes
  1317.     SetMainMailMenu (hWnd);
  1318. }
  1319.  
  1320.  
  1321. /*---  FUNCTION: AbortAllComm -------------------------------------------
  1322.  *
  1323.  *  Kills all active comm stuff
  1324.  *  jsc 9/12/94
  1325.  */
  1326.  
  1327. void
  1328. AbortAllComm()
  1329. {
  1330.    if (Decoding) {
  1331.      CompleteThisDecode();
  1332.      DecodeDone();
  1333.    }
  1334.    else if (Attaching)
  1335.      FinishAttachment(ABORT);
  1336.     
  1337.    if (PostEdit)    /* !NULL if posting */
  1338.      CompletePost(PostEdit, ABORT);
  1339.  
  1340.    CommDoc = (TypDoc *)NULL;
  1341.    CommBusy = FALSE;
  1342.    CommState = ST_NONE;
  1343. }
  1344.  
  1345.      
  1346. /*---  FUNCTION: About ---------------------------------------------------
  1347.  *
  1348.  *  Process messages for "About" dialog box
  1349.  */
  1350.  
  1351. BOOL WINAPI About (hDlg, message, wParam, lParam)
  1352.      HWND hDlg;
  1353.      unsigned message;
  1354.      WORD wParam;
  1355.      LONG lParam;
  1356. {
  1357.   switch (message)
  1358.     {
  1359.     case WM_INITDIALOG:
  1360.       return (TRUE);
  1361.  
  1362.     case WM_COMMAND:
  1363.       if (wParam == IDOK)
  1364.    {
  1365.      EndDialog (hDlg, TRUE);
  1366.      return (TRUE);
  1367.    }
  1368.       break;
  1369.     }
  1370.   return (FALSE);
  1371. }
  1372.  
  1373. #if 0 // This code not used anywhere (JSC 1/12/94)
  1374. /* Function CheckView -----------------------------
  1375.  *
  1376.  *  This function handles checking and unchecking menu items.
  1377.  *
  1378.  *    Entry    hWnd     is the window whose menu is to be altered.
  1379.  *                      Currently works only for the Net window.
  1380.  *
  1381.  *    Exit     The appropriate items on the menu have been checked or
  1382.  *             unchecked.
  1383.  */
  1384.  
  1385. VOID 
  1386. CheckView (hWnd)
  1387.      HWND hWnd;
  1388. {
  1389.   HMENU hMenu;
  1390.   int CheckIt;
  1391.  
  1392.   if (ViewNew)
  1393.     {
  1394.       CheckIt = MF_CHECKED | MF_BYCOMMAND;
  1395.     }
  1396.   else
  1397.     {
  1398.       CheckIt = MF_UNCHECKED | MF_BYCOMMAND;
  1399.     }
  1400.   hMenu = GetMenu (hWnd);
  1401.  
  1402.   CheckMenuItem (hMenu, IDM_NEW_WIN_GROUP, CheckIt);
  1403.  
  1404.   if (NewArticleWindow)
  1405.     {
  1406.       CheckIt = MF_CHECKED | MF_BYCOMMAND;
  1407.     }
  1408.   else
  1409.     {
  1410.       CheckIt = MF_UNCHECKED | MF_BYCOMMAND;
  1411.     }
  1412.   CheckMenuItem (hMenu, IDM_NEW_WIN_ARTICLE, CheckIt);
  1413. }
  1414. #endif
  1415. /* --- function CrackGroupLine --------------------------------------
  1416.  *
  1417.  *  Given a line from a .newsrc file, create a text line containing
  1418.  *  a structure of type TypGroup containing the same information.
  1419.  *  The line is zero-terminated upon input.
  1420.  *
  1421.  *    Entry    buf      points to a zero-terminated line from .newsrc.
  1422.  *             lineptr  points to a place to put the converted textline.
  1423.  *
  1424.  *    Exit     The line pointed to by "lineptr" now is a TypLine textline
  1425.  *             containing a structure of type TypGroup, containing the
  1426.  *             information in the input line from .newsrc.
  1427.  *
  1428.  *  Returns TRUE iff group was subscribed to.
  1429.  */
  1430. BOOL
  1431. CrackGroupLine (buf, lineptr)
  1432.      char *buf;
  1433.      TypLine *lineptr;
  1434. {
  1435.   char *grname = (char *) lineptr + sizeof (TypLine) + sizeof (TypGroup);
  1436.   TypGroup *group = (TypGroup *) ((char *) lineptr + sizeof (TypLine));
  1437.   TypRange *RangePtr;
  1438.   BOOL MoreNums;
  1439.   unsigned int MyLength;
  1440.  
  1441.   group->Subscribed = FALSE;
  1442.   group->Selected = FALSE;
  1443.   group->Determined = FALSE;
  1444.   group->NameLen = 0;
  1445.   group->SubjDoc = (TypDoc *) NULL;
  1446.   group->ServerEstNum = 0;
  1447.   group->ServerFirst = 0;
  1448.   group->ServerLast = 0;
  1449.   group->HighestPrevSeen = 0;
  1450.   group->nRanges = 0;
  1451.   group->header_handle = (HANDLE) NULL;
  1452.  
  1453.   /* Copy group name to output line.                               */
  1454.  
  1455.   while (*buf && *buf != ':' && *buf != '!')
  1456.     {
  1457.       *(grname++) = *(buf++);
  1458.       (group->NameLen)++;
  1459.     }
  1460.   *(grname++) = '\0';
  1461.  
  1462.   if (!(*buf))
  1463.     {
  1464.       /* Ran off end of line without seeing ':' or '!'.  Error.      */
  1465.     }
  1466.   else
  1467.     {
  1468.       if (*buf == ':')
  1469.    {
  1470.      group->Subscribed = TRUE;
  1471.    }
  1472.       buf++;
  1473.     }
  1474.  
  1475.   /* Look for the highest article number previously seen, in an
  1476.    * entry of form "s" followed by a number.
  1477.    */
  1478.  
  1479.   while (*buf && *buf == ' ' && *buf != '\n')
  1480.     buf++;
  1481.   if (*buf == 's')
  1482.     {
  1483.       buf++;
  1484.       GetNum (&buf, &(group->HighestPrevSeen));
  1485.     }
  1486.  
  1487.   /* Convert the article number ranges to the internal numeric
  1488.    * form we use in WinVN.
  1489.    */
  1490.  
  1491.   RangePtr = (TypRange *) ((char *) lineptr + sizeof (TypLine) +
  1492.             RangeOffset (group->NameLen));
  1493.  
  1494.   RangePtr->Last = RangePtr->First = 0;
  1495.  
  1496.   // allow lines like this, with no range data:
  1497.   // news.group: 
  1498.   //
  1499.   MoreNums = ( (*buf)=='\n' ? FALSE : TRUE);
  1500.   while (MoreNums)
  1501.     {
  1502.       while (*buf && (*buf == ' ' || *buf == ','))
  1503.    buf++;
  1504.       if (GetNum (&buf, &(RangePtr->First)))
  1505.    {
  1506.      /* We have the first number in a range.                     */
  1507.      (group->nRanges)++;
  1508.      RangePtr->Last = RangePtr->First;
  1509.      if (*buf == '-')
  1510.        {
  1511.          buf++;
  1512.          if (GetNum (&buf, &(RangePtr->Last)))
  1513.       {
  1514.         RangePtr++;
  1515.         /* at this point, we are positioned just after a range */
  1516.       }
  1517.          else
  1518.       {
  1519.         RangePtr->Last = RangePtr->First;
  1520.         MoreNums = FALSE;
  1521.       }
  1522.        }
  1523.      else if (*buf == ',')
  1524.        {
  1525.          /* We have a single number "n"; interpret as the range "n-n".
  1526.           */
  1527.          RangePtr++;
  1528.        }
  1529.      else
  1530.        {
  1531.          /* That must have been the last number.                  */
  1532.          MoreNums = FALSE;
  1533.        }
  1534.    }
  1535.       else
  1536.    {
  1537.      MoreNums = FALSE;
  1538.    }
  1539.     }
  1540.   if (group->nRanges == 0)
  1541.     (group->nRanges)++;
  1542.  
  1543.   MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) +
  1544.     sizeof (TypRange) * (group->nRanges) + sizeof (int);
  1545.  
  1546.   lineptr->length = MyLength;
  1547.   lineptr->LineID = NextLineID++;
  1548.   *(int *) ((char *) lineptr + MyLength - sizeof (int)) = MyLength;
  1549.  
  1550.   return (group->Subscribed);
  1551. }
  1552.  
  1553. /*-- function CursorToTextLine ----------------------------------------
  1554.  *
  1555.  *   Routine to locate a text line in a document, based on the
  1556.  *   cursor position.  Used to figure out which line is being selected
  1557.  *   when a user clicks a mouse button.
  1558.  *
  1559.  *   Entry    X, Y    are the position of the cursor.
  1560.  *            DocPtr  points to the current document.
  1561.  *
  1562.  *   Exit     *LinePtr points to the current line, if one was found.
  1563.  *            *BlockPtr points to the current block, if found.
  1564.  *            Function returns TRUE iff a line was found that corresponds
  1565.  *              to the cursor position.
  1566.  */
  1567. BOOL
  1568. CursorToTextLine (X, Y, DocPtr, BlockPtr, LinePtr)
  1569.      int X;
  1570.      int Y;
  1571.      TypDoc *DocPtr;
  1572.      TypBlock far **BlockPtr;
  1573.      TypLine far **LinePtr;
  1574. {
  1575.   int found;
  1576.   int SelLine;
  1577.  
  1578.   if (Y < TopSpace || (unsigned) Y > TopSpace + DocPtr->ScYLines * LineHeight ||
  1579.       X < SideSpace)
  1580.     {
  1581.       /* Cursor is in no-man's-land at edge of window.               */
  1582.       found = FALSE;
  1583.     }
  1584.   else
  1585.     {
  1586.       found = TRUE;
  1587.       SelLine = (Y - TopSpace) / LineHeight;
  1588.  
  1589.       LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
  1590.       BlockPtr, LinePtr);
  1591.       AdvanceToActive(BlockPtr,LinePtr);
  1592.  
  1593.       for (found = TRUE, il = 0; il < SelLine; )
  1594.    {
  1595.      if (!NextLine (BlockPtr, LinePtr))
  1596.        {
  1597.          found = FALSE;    /* ran off end of document */
  1598.          break;
  1599.        } else if((*LinePtr)->active){
  1600.          il++;
  1601.        }
  1602.    }
  1603.     }
  1604.   return (found);
  1605. }
  1606.  
  1607. /*-- function ReadNewsrc ----------------------------------------------
  1608.  *
  1609.  *    Reads NEWSRC into the Net document.
  1610.  *    This routine opens NEWSRC, reads & parses the lines into the NetDoc
  1611.  *    document, and closes the file.  One call does it all.
  1612.  *
  1613.  *    Entry    The NetDoc document is assumed to be initialized.
  1614.  *
  1615.  *    Exit     Returns TRUE if all went well, else zero.
  1616.  */
  1617. int
  1618. ReadNewsrc ()
  1619. {
  1620.   TypBlock far *BlockPtr;
  1621.   TypLine *LocalLinePtr, far * GroupPtr;
  1622.   HANDLE hLine;
  1623.   HANDLE hBlock;
  1624.   HFILE hRetCode;
  1625.   unsigned int Offset;
  1626.   TypLineID MyLineID;
  1627.   char mybuf[TEMPBUFSIZE];
  1628.   TypMRRFile *MRRFile;
  1629.   int returned;
  1630.  
  1631.   LockLine (NetDoc.hCurAddBlock, NetDoc.AddOffset, NetDoc.AddLineID, &BlockPtr, &GroupPtr);
  1632.   NetDoc.hDocWnd = hWndConf;
  1633.  
  1634.   hLine = LocalAlloc (LMEM_MOVEABLE, TEMPBUFSIZE);
  1635.   LocalLinePtr = (TypLine *) LocalLock (hLine);
  1636.  
  1637. #if 0
  1638.   env_var = getenv ("WINVN");           /* get path to winvn.ini */
  1639.   if (lstrlen (env_var))
  1640.    {
  1641.      lstrcpy (newsrc_filename, env_var);
  1642.      if (*(newsrc_filename + lstrlen (newsrc_filename) - 1) == '\\')
  1643.        lstrcat (newsrc_filename, "newsrc");
  1644.      else
  1645.        lstrcat (newsrc_filename, "\\newsrc");
  1646.    }
  1647.   else
  1648.    {
  1649.      MessageBox (hWndConf,"Environment variable WINVN not set.","Fatal Error", MB_OK | MB_ICONEXCLAMATION);
  1650.      return (0);
  1651.    }
  1652.  
  1653. #endif
  1654.  
  1655.  
  1656.   hRetCode = MRROpenFile (szNewsSrc, OF_READ, &MRRFile);
  1657.   if ((int) hRetCode <= 0)
  1658.     {
  1659.       return FALSE;
  1660.     }
  1661.   else
  1662.     {
  1663.       /* Loop to read lines, convert them to internal format, and
  1664.        * insert them into the NetDoc document.
  1665.        */
  1666.       
  1667.       LinesInRC = 0;
  1668.       while ((returned = MRRReadLine (MRRFile, mybuf, TEMPBUFSIZE)) > (-1))
  1669.    {
  1670.      mybuf[returned] = '\0';
  1671.      if (CrackGroupLine (mybuf, LocalLinePtr))
  1672.        {
  1673.          NetDoc.CountedLines++;
  1674.        }
  1675.      AddLine (LocalLinePtr, &BlockPtr, &GroupPtr);
  1676.      LinesInRC++;
  1677.    }
  1678.       MRRCloseFile (MRRFile);
  1679.     }
  1680.  
  1681.   /* Change the title of the Net document.  I suppose that,
  1682.    * strictly speaking, this oughtn't be done in this routine.
  1683.    */
  1684.   SetNetDocTitle ();
  1685.   UnlockLine (BlockPtr, GroupPtr, &hBlock, &Offset, &MyLineID);
  1686.  
  1687.   NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
  1688.   NetDoc.TopScOffset = sizeof (TypBlock);
  1689.   NetDoc.TopScLineID = 0L;
  1690.   NetDoc.LongestLine = 0;
  1691.   /* Mark lines active or inactive according to ShowUnsubscribed. */                                        
  1692.   SetGroupActiveLines();                                          
  1693.   // we'll check this during WriteNewsrc(). SMR 930224
  1694.   started_with_no_dolist = !DoList;
  1695.   LocalUnlock (hLine);
  1696.   LocalFree (hLine);
  1697.  
  1698.   return (TRUE);
  1699. }
  1700.  
  1701. char *ltoa ();
  1702.  
  1703. /*--- function WriteNewsrc ---------------------------------------------
  1704.  *
  1705.  *  Write out a NEWSRC file, based on the information in the
  1706.  *  NetDoc document.  Use the standard Unix "rn" format for .newsrc.
  1707.  *
  1708.  *    Entry    no parameters
  1709.  *             NetDoc   has the group information.
  1710.  *
  1711.  *    Exit     The NEWSRC file has been written.
  1712.  */
  1713. void
  1714. WriteNewsrc ()
  1715. {
  1716.   TypBlock far *BlockPtr;
  1717.   TypLine far *LinePtr;
  1718.   HANDLE hLine;
  1719.   HANDLE hBlock;
  1720.   unsigned int Offset;
  1721.   TypLineID MyLineID;
  1722.   char mes[60], mybuf[60];
  1723.   char far *fromptr;
  1724.   char *toptr;
  1725.   char *NewsLine;
  1726.   HFILE hRetCode;
  1727.   TypMRRFile *MRRFile;
  1728.   int nranges;
  1729.   TypGroup far *Group;
  1730.   TypRange far *RangePtr;
  1731.   BOOL firstrange;
  1732.  
  1733.   LockLine (NetDoc.hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr);
  1734.  
  1735.   hLine = LocalAlloc (LMEM_MOVEABLE, BLOCK_SIZE);
  1736.   NewsLine = (char *) LocalLock (hLine);
  1737.  
  1738.   hRetCode = MRROpenFile (szNewsSrc, OF_CREATE, &MRRFile);
  1739.   if ((int) hRetCode < 0)
  1740.     {
  1741.       sprintf (mes, "MRROpenFile returned %d", hRetCode);
  1742.       MessageBox (hWndConf, mes, mybuf, MB_OK);
  1743.     }
  1744.   else
  1745.     {
  1746.       do
  1747.    {
  1748.      toptr = NewsLine;
  1749.      Group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1750.  
  1751.      /* Remove groups only if we performed a LIST command and */
  1752.      /* the server did not have them.  Otherwise if !did_list, the */
  1753.      /* newsrc will be practically empty! */
  1754.  
  1755.      /* Jeeeeez!!  I'm fixing this AGAIN! */
  1756.      /* DoList is not a boolean. SMR 930610 */
  1757.  
  1758.      if ((Group->ServerFirst || Group->ServerEstNum) || !did_list ) {
  1759.      /* Copy group name                                          */
  1760.      fromptr = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
  1761.      while (*(toptr++) = *(fromptr++));
  1762.      toptr--;
  1763.  
  1764.      /* Affix : or ! depending upon whether subscribed.          */
  1765.      *(toptr++) = (char) (Group->Subscribed ? ':' : '!');
  1766.      *(toptr++) = ' ';
  1767.  
  1768.      /* If we know the highest article number on the server,
  1769.       * output it preceded by an "s".
  1770.       */
  1771.      if (Group->ServerLast || Group->HighestPrevSeen)
  1772.        {
  1773.          *(toptr++) = 's';
  1774.          if (Group->ServerLast)
  1775.              ltoa ((unsigned long) Group->ServerLast, toptr, 10);
  1776.          else
  1777.              ltoa ((unsigned long) Group->HighestPrevSeen, toptr, 10);
  1778.  
  1779.          while (*toptr)
  1780.       toptr++;
  1781.          *(toptr++) = ' ';
  1782.        }
  1783.  
  1784.      /* Affix ranges of articles read.                          */
  1785.      firstrange = TRUE;
  1786.      nranges = Group->nRanges;
  1787.      RangePtr = (TypRange far *) ((char far *) Group + RangeOffset (Group->NameLen));
  1788.  
  1789.      while ((nranges--) > 0)
  1790.        {
  1791.          /* Write out ',' if not first range of articles.         */
  1792.  
  1793.          if (!firstrange)
  1794.       {
  1795.         *(toptr++) = ',';
  1796.       }
  1797.          else
  1798.       {
  1799.         firstrange = FALSE;
  1800.       }
  1801.          /* Write out first article in a range.                   */
  1802.  
  1803.          ltoa ((unsigned long) RangePtr->First, toptr, 10);
  1804.          while (*toptr)
  1805.       toptr++;
  1806.  
  1807.          /* If the range is of form "n-n", just put out "n"       */
  1808.  
  1809.          if (RangePtr->First != RangePtr->Last)
  1810.       {
  1811.         /* Put out the hyphen in middle of range.                */
  1812.         *(toptr++) = '-';
  1813.         /* Put out the last article in a range.                  */
  1814.         ltoa ((unsigned long) RangePtr->Last, toptr, 10);
  1815.         while (*toptr)
  1816.           toptr++;
  1817.       }
  1818.          RangePtr++;
  1819.        }
  1820.      MRRWriteLine (MRRFile, NewsLine, toptr - NewsLine);
  1821.      }
  1822.    }
  1823.       while (NextLine (&BlockPtr, &LinePtr));
  1824.       UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  1825.       MRRCloseFile (MRRFile);
  1826.     }
  1827.     LocalUnlock (hLine);
  1828.     LocalFree (hLine);
  1829.     
  1830. }
  1831.  
  1832. /*--- function SetNetDocTitle -------------------------------------------
  1833.  *
  1834.  */
  1835. void
  1836. SetNetDocTitle ()
  1837. {
  1838.   char mybuf[120];
  1839.  
  1840.   sprintf (mybuf, "WinVN:  %u groups; %u subscribed", NetDoc.TotalLines,
  1841.       NetDoc.CountedLines);
  1842.   SetWindowText (hWndConf, mybuf);
  1843.  
  1844. }
  1845.  
  1846. /*--- function SetLineFlag --------------------------------------------
  1847.  *
  1848.  *  Set some flag in a line in a document.
  1849.  *
  1850.  *  Entry   Doc      points to the document.
  1851.  *          LinePtr  points to th line.
  1852.  *
  1853.  *  Exit    lFlag    says what to do.
  1854.  */
  1855. void
  1856. SetLineFlag (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
  1857. {
  1858.   switch (wFlag)
  1859.     {
  1860.       case LINE_FLAG_SET:
  1861.       ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
  1862.       ->Selected = wValue;
  1863.       break;
  1864.     }
  1865. }
  1866.  
  1867.  
  1868. /*--- function GroupAction --------------------------------------------
  1869.  *
  1870.  *  Perform some action on a group that is specified by a pointer
  1871.  *  to a line in the Net document.
  1872.  *  Typically called for each line in the Net document by
  1873.  *  ForAllLines.
  1874.  *
  1875.  *  Entry   Doc      points to NetDoc
  1876.  *          LinePtr  points to a line in that document.
  1877.  *          lFlag    indicates what to do with that line.
  1878.  */
  1879. void
  1880. GroupAction (TypDoc * Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue)
  1881. {
  1882.  
  1883.   switch (wFlag)
  1884.     {
  1885.       case GROUP_ACTION_SUBSCRIBE:
  1886.       case GROUP_ACTION_UNSUBSCRIBE:
  1887.       if (((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
  1888.      ->Selected)
  1889.    {
  1890.      ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
  1891.      ->Subscribed = wValue;
  1892.      AddGroupToTable ((char far *) *LinePtr);
  1893.      DeleteLine (BlockPtr, LinePtr);
  1894.    }
  1895.       break;
  1896.       
  1897.       case GROUP_ACTION_CHECK_ACTIVE:
  1898.          if(((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
  1899.             ->Subscribed || ShowUnsubscribed ) {
  1900.             (*LinePtr)->active = TRUE;
  1901.             (*BlockPtr)->NumActiveLines++;
  1902.             NetDoc.ActiveLines++;
  1903.             NetDoc.LongestLine = max(NetDoc.LongestLine,(unsigned)
  1904.                                     ((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
  1905.                                     ->NameLen+GROUP_NAME_OFFSET);
  1906.             if(((TypGroup far *) (((char far *) *LinePtr) + sizeof (TypLine)))
  1907.             ->Subscribed) NetDoc.CountedLines++;
  1908.          } else {
  1909.             (*LinePtr)->active = FALSE;
  1910.          }
  1911.          break;
  1912.  
  1913.     }
  1914. }
  1915.  
  1916. /****************************************************************************
  1917.  
  1918.    FUNCTION:   MakeHelpPathName
  1919.  
  1920.    PURPOSE:    HelpEx assumes that the .HLP help file is in the same
  1921.           directory as the HelpEx executable.This function derives
  1922.           the full path name of the help file from the path of the
  1923.           executable.
  1924.  
  1925.    Taken from HELPEX.C, from the MS Windows SDK.
  1926.  
  1927. ****************************************************************************/
  1928.  
  1929. void 
  1930. MakeHelpPathName (szFileName, maxchars)
  1931.      char *szFileName;
  1932.      int maxchars;
  1933. {
  1934.   char *pcFileName;
  1935.   int nFileNameLen;
  1936.  
  1937.   nFileNameLen = GetModuleFileName (hInst, szFileName, maxchars);
  1938.   pcFileName = szFileName + nFileNameLen;
  1939.  
  1940.   while (pcFileName > szFileName)
  1941.     {
  1942.       if (*pcFileName == '\\' || *pcFileName == ':')
  1943.    {
  1944.      *(++pcFileName) = '\0';
  1945.      break;
  1946.    }
  1947.       nFileNameLen--;
  1948.       pcFileName--;
  1949.     }
  1950.  
  1951.   if ((nFileNameLen + 13) < maxchars)
  1952.     {
  1953.       lstrcat (szFileName, LFN_HELP);
  1954.     }
  1955.  
  1956.   else
  1957.     {
  1958.       lstrcat (szFileName, "?");
  1959.     }
  1960.  
  1961.   return;
  1962. }
  1963.  
  1964.  
  1965. int
  1966. cursor_to_char_number (X, Y, DocPtr, BlockPtr, LinePtr)
  1967.      int X;
  1968.      int Y;
  1969.      TypDoc *DocPtr;
  1970.      TypBlock far **BlockPtr;
  1971.      TypLine far **LinePtr;
  1972. {
  1973.   int SelLine;
  1974.   int charnum = -1;
  1975.   SIZE extent;
  1976.                                                                                       
  1977.   char far * textptr;
  1978.   int textlen;
  1979.   HDC display_context;
  1980.   int iTopSpace, iSideSpace, iLineHeight, iCharWidth;
  1981.   
  1982.   if (DocPtr->DocType == DOCTYPE_ARTICLE)
  1983.   {
  1984.     iTopSpace = ArtTopSpace;
  1985.     iSideSpace = ArtSideSpace;
  1986.     iLineHeight = ArtLineHeight;                    
  1987.     iCharWidth = ArtCharWidth;
  1988.   }
  1989.   else
  1990.   {
  1991.     iTopSpace = TopSpace;
  1992.     iSideSpace = SideSpace;
  1993.     iLineHeight = LineHeight;
  1994.     iCharWidth = CharWidth;  
  1995.   }
  1996.     
  1997.   if (Y < iTopSpace || (unsigned) Y > iTopSpace + DocPtr->ScYLines * iLineHeight ||
  1998.       X < iSideSpace)
  1999.     {
  2000.    return (-1);
  2001.     }
  2002.   else
  2003.     {
  2004.       SelLine = (Y - iTopSpace) / iLineHeight;
  2005.  
  2006.       LockLine (DocPtr->hCurTopScBlock, DocPtr->TopScOffset, DocPtr->TopScLineID,
  2007.       BlockPtr, LinePtr);
  2008.  
  2009.       for (il = 0; il < SelLine; il++)
  2010.    {
  2011.      if (!NextLine (BlockPtr, LinePtr))
  2012.        {
  2013.          return (-1);  
  2014.          break;
  2015.        }
  2016.    }
  2017.     }
  2018.  
  2019.   /* find the right character on the text line */
  2020.   textlen = ((TypText far *) ((char far *) (*LinePtr) +
  2021.                sizeof (TypLine)))->NameLen;
  2022.  
  2023.  
  2024.   if (textlen) {
  2025.     textptr = (char far *) ((char far *) (*LinePtr) + sizeof (TypLine) + sizeof (TypText) );
  2026.     display_context = GetDC (DocPtr->hDocWnd);
  2027.  
  2028.     if (isLineQuotation(textptr))
  2029.       {  /* prepare to print a quotation Line */
  2030.         SelectObject (display_context, hFontArtQuote);
  2031.       }
  2032.     else
  2033.       {  /* prepare to print a normal line */
  2034.         SelectObject (display_context, hFontArtNormal);
  2035.       }
  2036.  
  2037.     for (charnum = 1; charnum < textlen; charnum++)
  2038.       {
  2039.     GetTextExtentPoint(display_context, (LPSTR) textptr, charnum, &extent); 
  2040.     if (extent.cx > (X + (int)DocPtr->ScXOffset * (iCharWidth + 1)- iSideSpace)) break;                                 
  2041.       }
  2042.     ReleaseDC (DocPtr->hDocWnd, display_context);
  2043.     return (charnum - 1);
  2044.   }
  2045.  
  2046. }
  2047.  
  2048. /*--- function SetGroupActiveLines --------------------------------------
  2049.  *
  2050.  *  Go through all the lines in the Net document, marking each
  2051.  *  as active or inactive according to whether the corresponding
  2052.  *  group is subscribed and whether we are displaying unsubscribed
  2053.  *  groups.
  2054.  *
  2055.  *  Entry:  NetDoc and ShowUnsubscribed are set properly.
  2056.  *
  2057.  *  Exit:   Each of the lines in NetDoc has had its "active" field
  2058.  *          set properly.
  2059.  */
  2060. void
  2061. SetGroupActiveLines() {
  2062.    NetDoc.ActiveLines = 0; 
  2063.    NetDoc.CountedLines = 0;
  2064.    ForAllBlocks (&NetDoc, SetForBlock, BLOCK_ACTION_SET_ACTIVE, 0);
  2065.    ForAllLines (&NetDoc, GroupAction, GROUP_ACTION_CHECK_ACTIVE, 0);
  2066. }                 
  2067.  
  2068.  
  2069. /* stuff for showing version numbers of the files */
  2070.  
  2071. #include "version.h"
  2072. #include "version.c"
  2073.  
  2074. /*-- function VerListDlgProc ---------------------------------------
  2075.  *
  2076.  *  Dialog function to handle selection of Version List
  2077.  *                                  JD 8/3/94
  2078.  */
  2079.  
  2080. LRESULT CALLBACK VerListDlgProc (HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)                  
  2081.                           
  2082. {
  2083.   int j;
  2084.   char far *cptr;
  2085.   char    szVersion[64];
  2086.  
  2087.   switch (iMessage)
  2088.     {
  2089.     case WM_INITDIALOG: 
  2090.          sprintf(szVersion,"Version %s",(LPSTR) WINVN_VERSION);    
  2091.          SetDlgItemText ((HWND) hDlg, IDD_VERSION_NUMBER, (LPSTR)szVersion);         
  2092.            hVerDlgList = GetDlgItem (hDlg, IDD_VERSION_LISTBOX);          
  2093.            SendMessage (hVerDlgList, WM_SETREDRAW, FALSE, 0L);
  2094.                    
  2095.          cptr = 0;
  2096.       for (j = 0; j < (sizeof(version_string) / sizeof version_string[0]); j++)
  2097.           {
  2098.             cptr = version_string[j];
  2099.             SendMessage (hVerDlgList, LB_ADDSTRING, 0, (LONG) cptr);
  2100.        }
  2101.           SendMessage (hVerDlgList, WM_SETREDRAW, TRUE, 0L);
  2102.           return TRUE;
  2103.           break;
  2104.  
  2105.     case WM_COMMAND:
  2106.       switch (wParam)
  2107.        {
  2108.        case IDOK:
  2109.               EndDialog (hDlg, TRUE);
  2110.              break;
  2111.  
  2112.         case IDCANCEL:
  2113.              EndDialog (hDlg, FALSE);
  2114.              break;
  2115.  
  2116.         default:
  2117.              return FALSE;
  2118.        }
  2119.       break;
  2120.  
  2121.     case WM_DESTROY:
  2122.       break;
  2123.  
  2124.     default:
  2125.       return FALSE;
  2126.       break;
  2127.     }
  2128.   return TRUE;
  2129. }
  2130.           
  2131. /*  Display Dialog Box to show version info   JD 8/4/94 */
  2132. void show_version_strings(HWND hWnd)
  2133. {
  2134.   lpfnWinVnVersionListDlg = (DLGPROC) MakeProcInstance((FARPROC) VerListDlgProc, (HINSTANCE) hInst);
  2135.   DialogBox (hInst, "WINVNVERSIONLIST", hWnd, lpfnWinVnVersionListDlg);  
  2136.   FreeProcInstance((FARPROC) lpfnWinVnVersionListDlg);
  2137.  
  2138. }
  2139.  
  2140. /* Last line of WVUSENET.C */
  2141.