/* WinVn.c * * This program is a visual Usenet news reader for Microsoft Windows. * It bears a vague similarity to the Unix program "vn"; hence the * name WINVN. * * WinVN talks NNTP (Network News Transport (?) Protocol) to a news * server, which must be running the NNTP program. (The source for * a unix implementation of NNTP is readily and apparently freely * available.) * * WINVN currently only reads articles; it does not allow posting * or mailing. Posting will be implemented when I get around to it; * the delay is due to the need to first implement an authentication * scheme so we know the poster is accurately identified. * * For more information, see WINVN.WRI and WVDOC.C. * * Mark Riordan September - October 1989 riordanmr@clvax1.cl.msu.edu * 1100 Parker Lansing, MI 48912 */ #define WINMAIN #include "windows.h" #include "WVglob.h" #include "WinVn.h" #ifndef MAC #include "winundoc.h" #endif #include "ctype.h" #include char *CommStrtoID (char *, int *, int *, char *); /*--- function WinMain ----------------------------------------------- * * Main program for WinVN. * Initialize, then execute main loop. * * Entry hInstance is a handle to this instance of execution * of this program. * hPrevInstance is a handle to a previous instance * of execution of this program (usually * 0, i.e., none. Few people would have * two copies of WinVN running simultaneously.) * lpCmdLine points to the command line--currently * not used. (Not to useful for Windows programs.) * nCmdShow is a flag indicating that the main window * should be displayed. (Fairly worthless.) */ int PASCAL WinMain (hInstance, hPrevInstance, lpCmdLine, nCmdShow) HANDLE hInstance; HANDLE hPrevInstance; LPSTR lpCmdLine; int nCmdShow; { HWND hWnd; HDC hDC; if (!hPrevInstance) if (!WinVnInit (hInstance)) return (0); #ifndef MAC hInst = hInstance; #endif /* Set up communications--serial or IP. */ MRRInitComm (); /* Initialize the document that will contain the list of */ /* newsgroups. This will be the main window. */ InitDoc (&NetDoc, (HWND) 0, (TypDoc *) NULL, DOCTYPE_NET); /* Create and display the main window. At first, the window */ /* just has an initialization message in it. */ hWnd = CreateWindow ("WinVn", "WinVN -- Usenet News Reader", WS_OVERLAPPEDWINDOW | WS_VSCROLL, 0, /* Initial X position */ 0, /* Initial Y position */ (int) (xScreen * 1 / 2), /* Initial X width */ (int) (yScreen * 9 / 10),/* Initial Y height */ NULL, NULL, hInstance, NULL); if (!hWnd) return (0); hWndConf = hWnd; NetDoc.hDocWnd = hWnd; /* mrr */ MoreInit (); #ifndef MAC ShowWindow (hWnd, nCmdShow); #endif UpdateWindow (hWnd); SendMessage (hWnd, WM_SIZE, 0, 0L); /* Read the NEWSRC file and display its contents in the */ /* main window. */ if (!ReadNewsrc ()) { MessageBox (hWndConf, "Cannot open the NEWSRC file.", "Fatal error", MB_OK | MB_ICONHAND); PostQuitMessage (0); } Initializing = INIT_ESTAB_CONN; InvalidateRect (hWnd, NULL, FALSE); /* And now for the main loop, which appears in all Windows programs. */ #ifndef MAC hAccel = LoadAccelerators (hInstance, "WinVNAccel"); #endif #ifndef MAC while (MainLoopPass ()); return (MainMsg.wParam); #endif } /* --- FUNCTION WinVnInit --------------------------------------------- * * Initialize the program (first stage). * This routine does some initialization needed before the * creation of the main window. * I put off additional initialization until after the main * window is created. * * Entry hInstance is a handle to the current instance of * execution. * * Exit Window classes have been registered, and a small * amount of other window- and comm-related initialization * is done. */ BOOL WinVnInit (hInstance) HANDLE hInstance; { HANDLE hMemory; PWNDCLASS pWndClass; char mesbuf[60], mesbuf2[60]; char buf[60]; char *errptr; char *env_var; BOOL bSuccess; Initializing = INIT_READING_NEWSRC; SaveNewsrc = TRUE; CommLineLWAp1 = CommLineIn + MAXCOMMLINE; LineHeight = 30; /* kludge so Net window doesn't get divide-by-zero */ #ifndef MAC szAppName = "WinVN"; env_var = getenv ("WINVN"); /* get path to winvn.ini */ if (lstrlen (env_var)) { lstrcpy (szAppProFile, env_var); if (*(szAppProFile + lstrlen (szAppProFile) - 1) == '\\') lstrcat (szAppProFile, "winvn.ini"); else lstrcat (szAppProFile, "\\winvn.ini"); } else { MessageBox (hWndConf,"Environment variable WINVN not set.\nWINVN must point to your login directory\nto find your WINVN.INI and NEWSRC\nThis is done automatically if you are logged in\nor you can type \"SET WINVN=N:\\WINVN\"","Fatal Error", MB_OK | MB_ICONEXCLAMATION); return (0); } /* Read profile strings. */ AskComm = GetPrivateProfileInt (szAppName, "AskComm", ASK_COMM_INITIAL, szAppProFile); UsingSocket = GetPrivateProfileInt (szAppName, "UseSocket", 1, szAppProFile); GetPrivateProfileString (szAppName, "NNTPHost", "titan", NNTPHost, MAXNNTPSIZE,szAppProFile); NNTPPort = GetPrivateProfileInt (szAppName, "NNTPPort", 119 ,szAppProFile); GetPrivateProfileString (szAppName, "CommString", "COM1:9600,e,7", szCommString, MAXCOMMCHARS,szAppProFile); errptr = CommStrtoID (szCommString, &CommPortID, &CommParityID, pszCommSpeed); if (errptr) { strcpy (mesbuf, "Error parsing "); strcat (mesbuf, szCommString); MessageBox (hWndConf, errptr, mesbuf, MB_OK | MB_ICONEXCLAMATION); } DoList = GetPrivateProfileInt (szAppName, "DoList", ID_DOLIST_ASK,szAppProFile); GetPrivateProfileString (szAppName, "UserName", "", UserName, MAILLEN,szAppProFile); GetPrivateProfileString (szAppName, "MailAddress", "", MailAddress, MAILLEN,szAppProFile); GetPrivateProfileString (szAppName, "Organization", "", Organization, MAILLEN,szAppProFile); FontSize = GetPrivateProfileInt (szAppName, "FontSize", 0, szAppProFile); ArticleFontSize = GetPrivateProfileInt (szAppName, "ArticleFontSize", 0,szAppProFile); GetPrivateProfileString (szAppName, "FontFace", "Helv", FontFace, 32,szAppProFile); GetPrivateProfileString (szAppName, "ArticleFontFace", "Helv", ArticleFontFace, 32, szAppProFile); FontBold = GetPrivateProfileInt (szAppName, "FontBold", TRUE, szAppProFile); ArticleFixedFont = GetPrivateProfileInt (szAppName, "ArticleFixedFont", FALSE, szAppProFile); GetPrivateProfileString (szAppName, "NetUnSubscribedColor", "255,33,33" /*"200,60,150"*/ , buf, 32, szAppProFile); NetUnSubscribedColor = StrToRGB (buf); GetPrivateProfileString (szAppName, "GroupSeenColor", "80,100,235" /*"100,120,180"*/ , buf, 32, szAppProFile); GroupSeenColor = StrToRGB (buf); ViewNew = GetPrivateProfileInt (szAppName, "NewWndGroup", FALSE, szAppProFile); NewArticleWindow = GetPrivateProfileInt (szAppName, "NewWndArticle", FALSE, szAppProFile); SaveArtAppend = GetPrivateProfileInt (szAppName, "SaveArtAppend", TRUE, szAppProFile); ThumbTrack = GetPrivateProfileInt (szAppName, "ThumbTrack", TRUE, szAppProFile); /* Create pointers to the dialog box functions, needed */ /* for routine processing of dialog boxes. */ lpfnWinVnCommDlg = MakeProcInstance (WinVnCommDlg, hInstance); lpfnWinVnSaveArtDlg = MakeProcInstance (WinVnSaveArtDlg, hInstance); lpfnWinVnFindDlg = MakeProcInstance (WinVnFindDlg, hInstance); lpfnWinVnSubjectDlg = MakeProcInstance (WinVnSubjectDlg, hInstance); lpfnWinVnAuthDlg = MakeProcInstance (WinVnAuthDlg, hInstance); lpfnWinVnDoListDlg = MakeProcInstance (WinVnDoListDlg, hInstance); lpfnWinVnPersonalInfoDlg = MakeProcInstance (WinVnPersonalInfoDlg, hInstance); lpfnWinVnMiscDlg = MakeProcInstance (WinVnMiscDlg, hInstance); lpfnWinVnAppearanceDlg = MakeProcInstance (WinVnAppearanceDlg, hInstance); xScreen = GetSystemMetrics (SM_CXSCREEN); yScreen = GetSystemMetrics (SM_CYSCREEN); #else xScreen = screenBits.bounds.right; yScreen = screenBits.bounds.bottom - 20; TEHCurrent = 0; #endif CommDoc = &NetDoc; Authorized = FALSE; #ifndef MAC hMemory = LocalAlloc (LPTR, sizeof (WNDCLASS)); pWndClass = (PWNDCLASS) LocalLock (hMemory); pWndClass->hCursor = LoadCursor (NULL, IDC_ARROW); pWndClass->hIcon = LoadIcon (hInstance, (LPSTR) "winvn"); pWndClass->lpszMenuName = (LPSTR) "ConfMenu"; pWndClass->lpszClassName = (LPSTR) "WinVn"; pWndClass->hbrBackground = GetStockObject (WHITE_BRUSH); pWndClass->hInstance = hInstance; pWndClass->style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; pWndClass->lpfnWndProc = WinVnConfWndProc; bSuccess = RegisterClass (pWndClass); if (bSuccess) { pWndClass->hCursor = LoadCursor (NULL, IDC_ARROW); pWndClass->hIcon = LoadIcon (hInstance, (LPSTR) "wvgroup"); pWndClass->lpszMenuName = (LPSTR) "ViewMenu"; pWndClass->lpszClassName = (LPSTR) "WinVnView"; pWndClass->hbrBackground = GetStockObject (WHITE_BRUSH); pWndClass->hInstance = hInstance; pWndClass->style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; pWndClass->lpfnWndProc = WinVnViewWndProc; bSuccess = RegisterClass (pWndClass); } if (bSuccess) { pWndClass->hCursor = LoadCursor (NULL, IDC_ARROW); pWndClass->hIcon = LoadIcon (hInstance, (LPSTR) "wvart"); pWndClass->lpszMenuName = (LPSTR) "ArtMenu"; pWndClass->lpszClassName = (LPSTR) "WinVnArt"; pWndClass->hbrBackground = GetStockObject (WHITE_BRUSH); pWndClass->hInstance = hInstance; pWndClass->style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; pWndClass->lpfnWndProc = WinVnArtWndProc; bSuccess = RegisterClass (pWndClass); } if (bSuccess) { pWndClass->hCursor = LoadCursor (NULL, IDC_ARROW); pWndClass->hIcon = LoadIcon (hInstance, (LPSTR) "wvpost"); pWndClass->lpszMenuName = (LPSTR) "PostMenu"; pWndClass->lpszClassName = (LPSTR) "WinVnPost"; pWndClass->hbrBackground = GetStockObject (WHITE_BRUSH); pWndClass->hInstance = hInstance; pWndClass->style = CS_HREDRAW | CS_VREDRAW; pWndClass->lpfnWndProc = WinVnPostWndProc; bSuccess = RegisterClass (pWndClass); } if (bSuccess) { pWndClass->hCursor = LoadCursor (NULL, IDC_ARROW); pWndClass->hIcon = LoadIcon (hInstance, (LPSTR) "wvmail"); pWndClass->lpszMenuName = (LPSTR) "MailMenu"; pWndClass->lpszClassName = (LPSTR) "WinVnMail"; pWndClass->hbrBackground = GetStockObject (WHITE_BRUSH); pWndClass->hInstance = hInstance; pWndClass->style = CS_HREDRAW | CS_VREDRAW; pWndClass->lpfnWndProc = WinVnMailWndProc; bSuccess = RegisterClass (pWndClass); } LocalUnlock (hMemory); LocalFree (hMemory); #else bSuccess = TRUE; #endif return (bSuccess); } /* --- Function MoreInit ----------------------------------------------- * * Finish up initializing the program. * I do as much initialization here as possible. I'd rather * have code run after the main window is created (in WinVNInit), * so I have someplace to display error messages if necessary. * * I read quite a few profile strings (from WIN.INI) * here and act appropriately (usually means setting a global variable). * I try to read as many as possible of the profile strings used by this * program here. * In particular, I do a bunch of font stuff here. */ BOOL MoreInit () { char szComName[10]; int retcode, j; #ifndef MAC TEXTMETRIC tmFontInfo; char mesbuf[50]; HDC hDC; CheckView (hWndConf); /* modify menu as necessary */ hDC = GetDC (hWndConf); /* Unless user specified the system font, create a font as per * the user's specifications. */ /* sprintf (mesbuf, "FontFace = %s, Bold = %d", FontFace,FontBold); MessageBox (hWndConf, mesbuf, "Hey!", MB_OK); */ if (FontSize) { hFont = CreateFont (FontSize, 0, /* width */ 0, /* escapement */ 0, /* orientation */ FontBold ? FW_BOLD : FW_MEDIUM, 0, /* no italics */ 0, /* no underline */ 0, /* no strikeout */ 0, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 0, /* no PROOF_QUALITY */ 0, FontFace); } else { hFont = GetStockObject (OEM_FIXED_FONT); } if (ArticleFontSize && !ArticleFixedFont) { hFontArt = CreateFont (ArticleFontSize, 0, /* width */ 0, /* escapement */ 0, /* orientation */ FW_MEDIUM, /* default weight */ 0, /* no italics */ 0, /* no underline */ 0, /* no strikeout */ 0, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 0, /* no PROOF_QUALITY */ 0, ArticleFontFace); } else if (ArticleFixedFont) { hFontArt = GetStockObject (ANSI_FIXED_FONT); } else { hFontArt = hFont; } ReleaseDC (hWndConf, hDC); /* Get information about the font. LineHeight and CharWidth * are pretty self-explanatory and the methods of determining these * are standard. TopSpace and SideSpace are the top and left margins * in pixels; I compute them by a method I determined empirically. */ hDC = GetDC (hWndConf); SelectObject (hDC, hFont); GetTextMetrics (hDC, (LPTEXTMETRIC) & tmFontInfo); LineHeight = tmFontInfo.tmExternalLeading + tmFontInfo.tmHeight; CharWidth = tmFontInfo.tmAveCharWidth; TopSpace = tmFontInfo.tmExternalLeading; TopSpace = LineHeight / 4; StartPen = TopSpace; SideSpace = tmFontInfo.tmAveCharWidth / 2; ReleaseDC (hWndConf, hDC); hDC = GetDC (hWndConf); SelectObject (hDC, hFontArt); GetTextMetrics (hDC, (LPTEXTMETRIC) & tmFontInfo); ArtLineHeight = tmFontInfo.tmExternalLeading + tmFontInfo.tmHeight; ArtCharWidth = tmFontInfo.tmAveCharWidth; ReleaseDC (hWndConf, hDC); #else BigClipRgn = NewRgn (); SetRectRgn (BigClipRgn, 0, 0, 2000, 2000); SetWindowVars (); #endif strcpy (SaveArtFileName, ""); /* Initialize some document-related stuff. */ ActiveGroupDoc = (TypDoc *) NULL; ActiveArticleDoc = (TypDoc *) NULL; for (j = 0; j < MAXGROUPWNDS; j++) GroupDocs[j].InUse = FALSE; for (j = 0; j < MAXARTICLEWNDS; j++) ArticleDocs[j].InUse = FALSE; return retcode; } #ifndef MAC /*--- Function WinVnDoComm ----------------------------------- * * Set communications parameters. * * Entry szComm is a string equivalent to that on a MODE statement. * E.g., "COM1:2400,n,8" */ int WinVnDoComm (szComm) char *szComm; { #if 0 int retcode; char mesbuf[60]; if (retcode = BuildCommDCB (szComm, (DCB FAR *) & DCBComm)) { sprintf (mesbuf, "BuildComm returned %d", retcode); MessageBox (hWndConf, mesbuf, "Error building COM1 DCB", MB_OK | MB_ICONEXCLAMATION); } else { SetCommState ((DCB FAR *) & DCBComm); } return (retcode); #endif } /*--- Function CommStrtoID -------------------------------------- * * Takes a communications string of the form given to the MODE command * and breaks it down to its constituent parts. * * Entry CommStr is the string; e.g., "COM1:2400,n,8" * * Exit *Port is the port (an IDD_* variable) * *Parity is the parity/data bits infor (an IDD_* symbol) * szSpeed is the speed, in character form * CommStr has been converted to upper case. * Function value NULL if no error, else a pointer * to an error message. */ char * CommStrtoID (CommStr, Port, Parity, szSpeed) char *CommStr; int *Port, *Parity; char *szSpeed; { char *ptr, *Speedptr; int myPort; strupr (CommStr); ptr = CommStr; while (*(ptr) == ' ') ptr++; /* Crack the "COMx" part of the string. */ if (strncmp (ptr, "COM", 3) != 0) return ("Must be COM port"); ptr += 3; if (*ptr == '1') { *Port = IDD_COM1; } else if (*ptr == '2') { *Port = IDD_COM2; } else { return ("COM port must be 1 or 2"); } /* Crack the speed parameter. */ ptr += 2; while (*ptr == ' ') ptr++; for (Speedptr = szSpeed; isdigit (*ptr); *(Speedptr++) = *(ptr++)); *Speedptr = '\0'; if (szSpeed == Speedptr) { return ("Non-numeric COM speed"); } while (isdigit (*ptr) || *ptr == ' ' || *ptr == ',') ptr++; /* Crack the parity parameter. */ if (*ptr == 'N') { *Parity = IDD_8NONE; } else if (*ptr == 'E') { *Parity = IDD_7EVEN; } else { return ("Bad parity"); } return (NULL); } /*--- function MainLoopPass --------------------------------------------- * * Do one pass of the main loop. * * Entry * * Exit returns result of GetMessage() */ BOOL MainLoopPass () { BOOL NoQuit; if (NoQuit = GetMessage (&MainMsg, NULL, NULL, NULL)) { if (!TranslateAccelerator (MainMsg.hwnd, hAccel, &MainMsg)) { TranslateMessage (&MainMsg); DispatchMessage (&MainMsg); } } return (NoQuit); } #endif