home *** CD-ROM | disk | FTP | other *** search
- //
- // PH.C
- // Pegasus Mail for Windows extension providing a PH (QI/CSO)
- // interface.
- //
- // Copyright (c) 1994, David Harris, All Rights Reserved.
- //
- // The author grants explicit permission for this source code to be
- // used or modified as required, subject only to the conditions that
- // the copyright notices above are preserved and that by using this
- // code you agree that the code is provided without warranty of any
- // kind, either explicit or implied, and you use it at your own
- // risk.
- //
-
- #define STRICT
- #include <windows.h>
- #include <bwcc.h>
- #include <stdio.h>
- #include <string.h>
- #include <alloc.h>
- #include "..\wpmforms.h"
- #include "ph.h" // Dialog control item IDs
-
- int atoi (char *s);
-
- // Form dialogs and callbacks: a form DLL can export a function
- // which WinPMail's form manager will call with Windows messages
- // destined for the form's dialog or window (such as menu selections
- // and so forth). The function takes the following form:
-
- typedef long pascal far (*FORM_CALLBACK) (HWND hWnd, WORD wMsg,
- WPARAM wParam, LPARAM lParam);
-
- // The return from a form callback is passed back through the
- // Windows call chain if non-zero, otherwise the default window
- // proc is called. The reasons we call this function instead of
- // simply sending messages to the dialog itself are:
- //
- // 1: Dialogs can only return Boolean values, whereas the callback
- // can provide the long value Windows expects to pass back
- // through the calling chain.
- //
- // 2: Having a separate callback means that it is potentially
- // possible to have a form which does not create a dialog.
- //
- //
- // Minimum interface: the minimum interface a form DLL must provide
- // is a routine called "forminit" which is exported by name. "forminit"
- // has the following prototype:
- //
- // WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
- // char *data, HWND *hDialog, char *callback_name);
- //
- // "version" is passed in with the version of the WinPMail forms
- // manager which is running.
- // "variant" indicates what type of form is required - the following
- // values are currently defined:
- // 0: Create a form for composing a message
- // 1: Create a form for reading a message
- // "hParent" contains the handle of the WinPMail MDI child window
- // which is to contain the form.
- // "data" contains any string defined as being required for the
- // form in the menu interface.
- // "hDialog" should be filled in with the window handle of the
- // modeless dialog created within the MDI child window.
- // "callback_name" (optional) should be filled in with the name of the
- // function in the DLL of the exported function to which messages
- // should be sent or NULL if there is none. If NULL, messages are
- // sent to the dialog returned in "hDialog". You will use an
- // indirect callback of this kind when your extension does not
- // create a dialog within the enclosing parent window.
- //
- // When forminit is called, the DLL should register any window
- // classes it needs then create the dialog within the MDI parent
- // window and size it to the correct size. On return WinPMail will
- // resize the parent window to enclose the dialog correctly. The
- // DLL should NOT make the dialog visible - WinPMail will do that
- // as required.
-
- #define WM_MARGIN (WM_USER + 1099)
- #define WM_SETDIR (WM_USER + 400)
- #define WM_ADDFILE (WM_USER + 401)
- #define WM_STARTUP (WM_USER + 403)
- #define WM_GET_ALIST (WM_USER + 404)
-
- #define IDC_QUICKHELP_TO 970
- #define IDC_QUICKHELP_CC 971
-
-
- int register_form_classes (void);
-
-
- HFONT hPhFont;
- int ec_xdelta, ec_ydelta;
- HINSTANCE hLibInstance; // set in LibMain, used throughout the DLL
- char szFormDlgName [] = "PH"; // Name of dialog resource template.
- char szFormDlgClassName [] =
- "bordlg_ph"; // Class name for the form dialog.
- HWND last_focus; // The last control to have focus
- char no_tcp [] =
- "This extension can only run on systems where WinPMail "
- "has access to Winsock TCP/IP services.";
-
- #pragma warn -par
-
-
- void GetLocalWindowRect (HWND hWnd, RECT *r)
- {
- // A utility function which retrieves the bounding rectangle
- // for a Child window expressed in the coordinate system of
- // its parent.
-
- HWND hParent;
- RECT r2;
- int width, height;
-
- GetWindowRect (GetParent (hWnd), &r2);
- GetWindowRect (hWnd, r);
- height = r->bottom - r->top;
- width = r->right - r->left;
- r->left -= r2.left;
- r->top -= r2.top;
- r->bottom = r->top + height;
- r->right = r->left + width;
- }
-
-
- WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
- char *data, HWND *hDialog, char *callback_name)
- {
- RECT r, r2;
- char szBuf [80];
- int i;
- HWND hControl;
- BYTE tm [7];
-
- // First, check to see if the version of the form manager is
- // one we can work with. This check is pretty arbitrary - you
- // should probably assume that you may not work with any version
- // where the major version number is higher than the original
- // version you targeted.
-
- if ((version & 0xFF00) > 0x100) return 0;
-
- // Now check the variant number; for the PH Query Client, we only
- // provide a COMPOSER format.
-
- if (variant != 0) return 0;
-
- (*hDialog) = CreateDialog (hLibInstance, (LPCSTR) szFormDlgName, hParent, NULL);
- if ((*hDialog) == NULL) return 0;
-
- GetClientRect (*hDialog, &r);
- MoveWindow (*hDialog, 0, 0, r.right, r.bottom, FALSE);
-
- GetClientRect (*hDialog, &r);
- GetLocalWindowRect (GetDlgItem (*hDialog, 103), &r2);
- ec_xdelta = r.right - r2.right;
- ec_ydelta = r.bottom - r2.bottom;
-
- if (! hPhFont)
- hPhFont = CreateFont (14, 0, 0, 0, 400, FALSE,
- FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
- CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
- DEFAULT_PITCH | FF_DONTCARE, "Courier");
- SendMessage (GetDlgItem (*hDialog, 103), WM_SETFONT,
- (WPARAM) hPhFont, (LPARAM) TRUE);
-
- // Some default values can be passed in the "data" parameter -
- // for the PH client, we expect it to contain the default PH
- // server we should use.
-
- if ((data != NULL) && (*data))
- SetDlgItemText (*hDialog, 101, data); // Default host
- return 1;
- }
-
-
- void trim_newline (char *str)
- {
- /* Remove the terminating newline from a string
- ** if it's present. */
-
- int i;
-
- i = strlen (str) - 1;
- while ((i >= 0) && ((str [i] == '\n') || (str [i] == '\r')))
- str [i -- ] = '\0';
- }
-
-
- void ph_query (HWND hParent, HWND hControl, char *host, char *search)
- {
- // Perform a PH query given the server and match string entered
- // by the user. PH is a simple protocol developed at UIUC which,
- // while suffering from some inadequacies in data formatting, is
- // still a useful way of querying users at a site.
-
- char buffer [256], *s;
- int last, i;
-
- if (SendMessage (hParent, WM_F_TCPOPEN, 105, (LPARAM) host) < 1)
- return;
-
- // Select the entire contents of our "results" edit control so
- // it will be cleared by any reply from the server.
-
- SendMessage (hControl, EM_SETSEL, 0, 0x7FFF0000);
- SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "");
-
- // We don't do anything fancy - we just use the absolute minimum
- // form of lookup and we make no particular attempt to parse the
- // data returned.
-
- sprintf (buffer, "query %s\r\n", search);
- last = 1;
- if (SendMessage (hParent, WM_F_TCPPUTS, 0, (LPARAM) buffer) > 0)
- {
- for (;;)
- {
- buffer [0] = '\0';
- if (SendMessage (hParent, WM_F_TCPGETS, sizeof (buffer), (LPARAM) buffer) < 1)
- break;
- if (strnicmp (buffer, "200:OK", 6) == 0) break;
- if (buffer [0] == '\0') break;
- trim_newline (buffer);
- for (s = buffer; *s && (*s != ':'); s ++) ;
- if (*s == ':') ++ s;
- i = atoi (s);
- if (i > last)
- {
- SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "\r\n");
- last = i;
- }
- SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) s);
- SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "\r\n");
- }
- SendMessage (hParent, WM_F_TCPPUTS, 0, (LPARAM) "quit\r\n");
- SendMessage (hParent, WM_F_TCPGETS, sizeof (buffer), (LPARAM) buffer);
- }
- SendMessage (hParent, WM_F_TCPCLOSE, 0, 0);
- }
-
-
- LONG FAR PASCAL _export FormProc (HWND hWnd, WORD wMsg,
- WORD wParam, LONG lParam)
- {
- // Service routine for the form's enclosed dialog. This is a
- // standard windows modeless WndProc.
-
- DWORD dwResult = 0, l;
- BOOL fCallDefProc = TRUE;
- char host [80], search [180], *s, *txt;
- RECT r;
- HWND hControl;
- HCURSOR hOldCursor;
- unsigned int len;
-
- switch (wMsg)
- {
- case WM_FM_INIT :
- // Check to see that TCP/IP services are actually
- // available on this system. If they aren't, we simply
- // post a close message to the parent.
-
- if (SendMessage (GetParent (hWnd), WM_F_TCPPRESENT, 0, 0) == 0)
- {
- MessageBox (hWnd, no_tcp, " PH Client", MB_OK | MB_ICONSTOP);
- PostMessage (GetParent (hWnd), WM_CLOSE, 0, 0);
- }
- break;
-
- case WM_DESTROY :
- // Get rid of our edit control font when we're destroyed.
-
- if (hPhFont)
- {
- SendMessage (GetDlgItem (hWnd, 103), WM_SETFONT,
- (WPARAM) 0, FALSE);
- DeleteObject (hPhFont);
- }
- break;
-
- case WM_FM_INITFOCUS :
- SetFocus (GetDlgItem (hWnd, 101));
- break;
-
- case WM_FM_RESTOREFOCUS :
- // There's a glitch in the way the Windows MDI manager
- // seems to manage focus which means that we can have
- // focus taken away and not returned when the user does
- // anything outside our window. WinPMail has some quite
- // complex logic to deal with this case and will send
- // this message whenever focus should be restored in
- // our window. We set focus to the last active control
- // (which we know from trapping EN_SETFOCUS messages).
-
- if (last_focus) SetFocus (last_focus);
- break;
-
- case WM_FM_SIZE :
- fCallDefProc = FALSE;
- MoveWindow (hWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
- if ((hControl = GetDlgItem (hWnd, 103)) == NULL) break;
- GetLocalWindowRect (hControl, &r);
- MoveWindow (hControl, r.left, r.top,
- LOWORD(lParam) - r.left - ec_xdelta,
- HIWORD(lParam) - r.top - ec_ydelta, TRUE);
-
- // Because of an apparent bug in BWCC, we need to invalidate the entire
- // window on any resize, otherwise occasional vertical black lines get
- // left on the client area...
- InvalidateRect (hWnd, NULL, TRUE);
- break;
-
- case WM_FM_COMMAND : // Menu selections
- if (! last_focus) break;
- SetFocus (last_focus);
- switch (wParam)
- {
- case IDM_UNDO :
- SendMessage (last_focus, WM_UNDO, 0, 0);
- break;
-
- case IDM_CUT :
- SendMessage (last_focus, WM_CUT, 0, 0);
- break;
-
- case IDM_COPY :
- SendMessage (last_focus, WM_COPY, 0, 0);
- break;
-
- case IDM_PASTE :
- SendMessage (last_focus, WM_PASTE, 0, 0);
- break;
- }
- break;
-
- case WM_COMMAND :
- fCallDefProc = FALSE;
- if (HIWORD (lParam) == EN_SETFOCUS)
- {
- // We have to trap EN_SETFOCUS messages so we know which
- // control was active last. When a menu selection is made
- // our current control will lose focus and because of a
- // curiosity in Windows' MDI management, we won't get it
- // back. Instead, WinPMail generates a WM_FM_RESTOREFOCUS
- // message which signals to us that we should set focus to
- // whatever the last active control was.
-
- last_focus = (HWND) LOWORD (lParam);
- break;
- }
-
- switch (wParam)
- {
- case 139 :
- GetDlgItemText (hWnd, 101, host, sizeof (host));
- GetDlgItemText (hWnd, 102, search, sizeof (search));
- hOldCursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
- ph_query (GetParent (hWnd), GetDlgItem (hWnd, 103), host, search);
- SetCursor (hOldCursor);
- break;
-
- case 140 :
- len = GetWindowTextLength (GetDlgItem (hWnd, 103));
- if (len)
- {
- l = SendDlgItemMessage (hWnd, 103, EM_GETSEL, 0, 0);
- if ((s = (char *) malloc (len + 16)) == NULL) break;
- GetWindowText (GetDlgItem (hWnd, 103), s, len);
- txt = s + LOWORD(l);
- *(s + HIWORD(l)) = '\0';
- SendMessage (GetParent (hWnd), WM_F_PASTEDATA, 1, (LPARAM) txt);
- free (s);
- }
- break;
- }
- break;
- }
-
- if (fCallDefProc)
- dwResult = BWCCDefDlgProc (hWnd, wMsg, wParam, lParam);
-
- return dwResult;
- }
-
-
- #pragma warn -sus
-
- void unregister_form_classes (void)
- {
- // Remove any classes associated with the form; we have the
- // same problem here as we do with registering the classes
- // for the DLL - we only want to deregister the classes on
- // the last time we're unloaded.
-
- if (GetModuleUsage (hLibInstance) > 1) return; // Not a problem
- UnregisterClass (szFormDlgClassName, hLibInstance);
- }
-
-
- BOOL FAR PASCAL LibMain (HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
- {
- WNDCLASS wc;
-
- if (! hLibInstance)
- {
- hLibInstance = hInst;
- BWCCGetVersion (); // Forces BWCC to be dynamically loaded.
-
- // Register any window classes used by the form. Forms will usually
- // register either one or occasionally two classes which define
- // the composition and reader dialogs created by the DLL.
- //
- // There's a gotcha here, of course (aren't there always, in
- // Windows?)... You can't register a window class more than once,
- // so if the DLL has already been loaded and the user asks to
- // create a second instance of the form, we have to be careful
- // not to re-register the class. We do this by checking the
- // instance usage counter of the DLL - if it's greater than 1,
- // then we DON'T register the classes.
-
- wc.style = WS_CHILD;
- wc.lpfnWndProc = FormProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = DLGWINDOWEXTRA;
- wc.hInstance = hLibInstance;
- wc.hIcon = NULL;
- wc.hCursor = LoadCursor (NULL, IDC_ARROW);
- wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
- wc.lpszMenuName = NULL;
- wc.lpszClassName = szFormDlgClassName;
- if (! RegisterClass (&wc))
- MessageBeep (0);
- }
-
- return (TRUE); // Initialization went OK
- }
-
-