home *** CD-ROM | disk | FTP | other *** search
/ PC Loisirs 18 / cd.iso / kit / mail / pmail.exe / forms / ph / ph.c next >
Encoding:
C/C++ Source or Header  |  1995-02-06  |  15.3 KB  |  442 lines

  1. //
  2. //  PH.C
  3. //  Pegasus Mail for Windows extension providing a PH (QI/CSO)
  4. //  interface.
  5. //
  6. //  Copyright (c) 1994, David Harris, All Rights Reserved.
  7. //
  8. //  The author grants explicit permission for this source code to be
  9. //  used or modified as required, subject only to the conditions that
  10. //  the copyright notices above are preserved and that by using this
  11. //  code you agree that the code is provided without warranty of any
  12. //  kind, either explicit or implied, and you use it at your own
  13. //  risk.
  14. //
  15.  
  16. #define STRICT
  17. #include <windows.h>
  18. #include <bwcc.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <alloc.h>
  22. #include "..\wpmforms.h"
  23. #include "ph.h"               //  Dialog control item IDs
  24.  
  25. int atoi (char *s);
  26.  
  27. //  Form dialogs and callbacks: a form DLL can export a function
  28. //  which WinPMail's form manager will call with Windows messages
  29. //  destined for the form's dialog or window (such as menu selections
  30. //  and so forth). The function takes the following form:
  31.  
  32. typedef long pascal far (*FORM_CALLBACK) (HWND hWnd, WORD wMsg,
  33.    WPARAM wParam, LPARAM lParam);
  34.  
  35. //  The return from a form callback is passed back through the
  36. //  Windows call chain if non-zero, otherwise the default window
  37. //  proc is called. The reasons we call this function instead of
  38. //  simply sending messages to the dialog itself are:
  39. //
  40. //   1: Dialogs can only return Boolean values, whereas the callback
  41. //      can provide the long value Windows expects to pass back
  42. //      through the calling chain.
  43. //
  44. //   2: Having a separate callback means that it is potentially
  45. //      possible to have a form which does not create a dialog.
  46. //
  47. //
  48. //  Minimum interface: the minimum interface a form DLL must provide
  49. //  is a routine called "forminit" which is exported by name. "forminit"
  50. //  has the following prototype:
  51. //
  52. //  WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
  53. //      char *data, HWND *hDialog, char *callback_name);
  54. //
  55. //  "version" is passed in with the version of the WinPMail forms
  56. //     manager which is running.
  57. //  "variant" indicates what type of form is required - the following
  58. //     values are currently defined:
  59. //       0: Create a form for composing a message
  60. //       1: Create a form for reading a message
  61. //  "hParent" contains the handle of the WinPMail MDI child window
  62. //     which is to contain the form.
  63. //  "data" contains any string defined as being required for the
  64. //     form in the menu interface.
  65. //  "hDialog" should be filled in with the window handle of the
  66. //     modeless dialog created within the MDI child window.
  67. //  "callback_name" (optional) should be filled in with the name of the
  68. //     function in the DLL of the exported function to which messages
  69. //     should be sent or NULL if there is none. If NULL, messages are
  70. //     sent to the dialog returned in "hDialog". You will use an
  71. //     indirect callback of this kind when your extension does not
  72. //     create a dialog within the enclosing parent window.
  73. //
  74. //  When forminit is called, the DLL should register any window
  75. //  classes it needs then create the dialog within the MDI parent
  76. //  window and size it to the correct size. On return WinPMail will
  77. //  resize the parent window to enclose the dialog correctly. The
  78. //  DLL should NOT make the dialog visible - WinPMail will do that
  79. //  as required.
  80.  
  81. #define WM_MARGIN (WM_USER + 1099)
  82. #define WM_SETDIR (WM_USER + 400)
  83. #define WM_ADDFILE (WM_USER + 401)
  84. #define WM_STARTUP (WM_USER + 403)
  85. #define WM_GET_ALIST (WM_USER + 404)
  86.  
  87. #define IDC_QUICKHELP_TO   970
  88. #define IDC_QUICKHELP_CC   971
  89.  
  90.  
  91. int register_form_classes (void);
  92.  
  93.  
  94. HFONT hPhFont;
  95. int ec_xdelta, ec_ydelta;
  96. HINSTANCE  hLibInstance;            // set in LibMain, used throughout the DLL
  97. char szFormDlgName [] = "PH";       // Name of dialog resource template.
  98. char szFormDlgClassName [] =
  99.    "bordlg_ph";                     // Class name for the form dialog.
  100. HWND last_focus;                    // The last control to have focus
  101. char no_tcp [] =
  102.    "This extension can only run on systems where WinPMail "
  103.    "has access to Winsock TCP/IP services.";
  104.  
  105. #pragma warn -par
  106.  
  107.  
  108. void GetLocalWindowRect (HWND hWnd, RECT *r)
  109.    {
  110.    //  A utility function which retrieves the bounding rectangle
  111.    //  for a Child window expressed in the coordinate system of
  112.    //  its parent.
  113.  
  114.    HWND hParent;
  115.    RECT r2;
  116.    int width, height;
  117.  
  118.    GetWindowRect (GetParent (hWnd), &r2);
  119.    GetWindowRect (hWnd, r);
  120.    height = r->bottom - r->top;
  121.    width = r->right - r->left;
  122.    r->left -= r2.left;
  123.    r->top -= r2.top;
  124.    r->bottom = r->top + height;
  125.    r->right = r->left + width;
  126.    }
  127.  
  128.  
  129. WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
  130.    char *data, HWND *hDialog, char *callback_name)
  131.    {
  132.    RECT r, r2;
  133.    char szBuf [80];
  134.    int i;
  135.    HWND hControl;
  136.    BYTE tm [7];
  137.  
  138.    //  First, check to see if the version of the form manager is
  139.    //  one we can work with. This check is pretty arbitrary - you
  140.    //  should probably assume that you may not work with any version
  141.    //  where the major version number is higher than the original
  142.    //  version you targeted.
  143.  
  144.    if ((version & 0xFF00) > 0x100) return 0;
  145.  
  146.    //  Now check the variant number; for the PH Query Client, we only
  147.    //  provide a COMPOSER format.
  148.  
  149.    if (variant != 0) return 0;
  150.  
  151.    (*hDialog) = CreateDialog (hLibInstance, (LPCSTR) szFormDlgName, hParent, NULL);
  152.    if ((*hDialog) == NULL) return 0;
  153.  
  154.    GetClientRect (*hDialog, &r);
  155.    MoveWindow (*hDialog, 0, 0, r.right, r.bottom, FALSE);
  156.  
  157.    GetClientRect (*hDialog, &r);
  158.    GetLocalWindowRect (GetDlgItem (*hDialog, 103), &r2);
  159.    ec_xdelta = r.right - r2.right;
  160.    ec_ydelta = r.bottom - r2.bottom;
  161.  
  162.    if (! hPhFont)
  163.       hPhFont = CreateFont (14, 0, 0, 0, 400, FALSE,
  164.          FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
  165.          CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  166.          DEFAULT_PITCH | FF_DONTCARE, "Courier");
  167.    SendMessage (GetDlgItem (*hDialog, 103), WM_SETFONT,
  168.       (WPARAM) hPhFont, (LPARAM) TRUE);
  169.  
  170.    //  Some default values can be passed in the "data" parameter -
  171.    //  for the PH client, we expect it to contain the default PH
  172.    //  server we should use.
  173.  
  174.    if ((data != NULL) && (*data))
  175.      SetDlgItemText (*hDialog, 101, data);      // Default host
  176.    return 1;
  177.    }
  178.  
  179.  
  180. void trim_newline (char *str)
  181.    {
  182.    /*  Remove the terminating newline from a string
  183.    **  if it's present. */
  184.  
  185.    int i;
  186.  
  187.    i = strlen (str) - 1;
  188.    while ((i >= 0) && ((str [i] == '\n') || (str [i] == '\r'))) 
  189.       str [i -- ] = '\0';
  190.    }
  191.  
  192.  
  193. void ph_query (HWND hParent, HWND hControl, char *host, char *search)
  194.    {
  195.    //  Perform a PH query given the server and match string entered
  196.    //  by the user. PH is a simple protocol developed at UIUC which,
  197.    //  while suffering from some inadequacies in data formatting, is
  198.    //  still a useful way of querying users at a site.
  199.  
  200.    char buffer [256], *s;
  201.    int last, i;
  202.  
  203.    if (SendMessage (hParent, WM_F_TCPOPEN, 105, (LPARAM) host) < 1)
  204.       return;
  205.  
  206.    //  Select the entire contents of our "results" edit control so
  207.    //  it will be cleared by any reply from the server.
  208.  
  209.    SendMessage (hControl, EM_SETSEL, 0, 0x7FFF0000);
  210.    SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "");
  211.  
  212.    //  We don't do anything fancy - we just use the absolute minimum
  213.    //  form of lookup and we make no particular attempt to parse the
  214.    //  data returned.
  215.  
  216.    sprintf (buffer, "query %s\r\n", search);
  217.    last = 1;
  218.    if (SendMessage (hParent, WM_F_TCPPUTS, 0, (LPARAM) buffer) > 0)
  219.       {
  220.       for (;;)
  221.          {
  222.          buffer [0] = '\0';
  223.          if (SendMessage (hParent, WM_F_TCPGETS, sizeof (buffer), (LPARAM) buffer) < 1)
  224.             break;
  225.          if (strnicmp (buffer, "200:OK", 6) == 0) break;
  226.          if (buffer [0] == '\0') break;
  227.          trim_newline (buffer);
  228.          for (s = buffer; *s && (*s != ':'); s ++) ;
  229.          if (*s == ':') ++ s;
  230.          i = atoi (s);
  231.          if (i > last)
  232.             {
  233.             SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "\r\n");
  234.             last = i;
  235.             }
  236.          SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) s);
  237.          SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "\r\n");
  238.          }
  239.       SendMessage (hParent, WM_F_TCPPUTS, 0, (LPARAM) "quit\r\n");
  240.       SendMessage (hParent, WM_F_TCPGETS, sizeof (buffer), (LPARAM) buffer);
  241.       }
  242.    SendMessage (hParent, WM_F_TCPCLOSE, 0, 0);
  243.    }
  244.  
  245.  
  246. LONG FAR PASCAL _export FormProc (HWND hWnd, WORD wMsg, 
  247.    WORD wParam, LONG lParam)
  248.    {
  249.    //  Service routine for the form's enclosed dialog. This is a
  250.    //  standard windows modeless WndProc.
  251.  
  252.    DWORD dwResult = 0, l;
  253.    BOOL fCallDefProc = TRUE;
  254.    char host [80], search [180], *s, *txt;
  255.    RECT r;
  256.    HWND hControl;
  257.    HCURSOR hOldCursor;
  258.    unsigned int len;
  259.  
  260.    switch (wMsg)
  261.       {
  262.       case WM_FM_INIT :
  263.          //  Check to see that TCP/IP services are actually
  264.          //  available on this system. If they aren't, we simply
  265.          //  post a close message to the parent.
  266.  
  267.          if (SendMessage (GetParent (hWnd), WM_F_TCPPRESENT, 0, 0) == 0)
  268.             {
  269.             MessageBox (hWnd, no_tcp, " PH Client", MB_OK | MB_ICONSTOP);
  270.             PostMessage (GetParent (hWnd), WM_CLOSE, 0, 0);
  271.             }
  272.          break;
  273.  
  274.       case WM_DESTROY :
  275.          //  Get rid of our edit control font when we're destroyed.
  276.  
  277.          if (hPhFont)
  278.             {
  279.             SendMessage (GetDlgItem (hWnd, 103), WM_SETFONT,
  280.                (WPARAM) 0, FALSE);
  281.             DeleteObject (hPhFont);
  282.             }
  283.          break;
  284.  
  285.       case WM_FM_INITFOCUS :
  286.          SetFocus (GetDlgItem (hWnd, 101));
  287.          break;
  288.  
  289.       case WM_FM_RESTOREFOCUS :
  290.          //  There's a glitch in the way the Windows MDI manager
  291.          //  seems to manage focus which means that we can have
  292.          //  focus taken away and not returned when the user does
  293.          //  anything outside our window. WinPMail has some quite
  294.          //  complex logic to deal with this case and will send
  295.          //  this message whenever focus should be restored in
  296.          //  our window. We set focus to the last active control
  297.          //  (which we know from trapping EN_SETFOCUS messages).
  298.  
  299.          if (last_focus) SetFocus (last_focus);
  300.          break;
  301.  
  302.       case WM_FM_SIZE :
  303.          fCallDefProc = FALSE;
  304.          MoveWindow (hWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
  305.          if ((hControl = GetDlgItem (hWnd, 103)) == NULL) break;
  306.          GetLocalWindowRect (hControl, &r);
  307.          MoveWindow (hControl, r.left, r.top,
  308.             LOWORD(lParam) - r.left - ec_xdelta,
  309.             HIWORD(lParam) - r.top - ec_ydelta, TRUE);
  310.  
  311.          // Because of an apparent bug in BWCC, we need to invalidate the entire
  312.          // window on any resize, otherwise occasional vertical black lines get
  313.          // left on the client area...
  314.          InvalidateRect (hWnd, NULL, TRUE);
  315.          break;
  316.  
  317.       case WM_FM_COMMAND :       // Menu selections
  318.          if (! last_focus) break;
  319.          SetFocus (last_focus);
  320.          switch (wParam)
  321.             {
  322.             case IDM_UNDO :
  323.                SendMessage (last_focus, WM_UNDO, 0, 0);
  324.                break;
  325.  
  326.             case IDM_CUT :
  327.                SendMessage (last_focus, WM_CUT, 0, 0);
  328.                break;
  329.  
  330.             case IDM_COPY :
  331.                SendMessage (last_focus, WM_COPY, 0, 0);
  332.                break;
  333.  
  334.             case IDM_PASTE :
  335.                SendMessage (last_focus, WM_PASTE, 0, 0);
  336.                break;
  337.             }
  338.          break;
  339.  
  340.       case WM_COMMAND :
  341.          fCallDefProc = FALSE;
  342.          if (HIWORD (lParam) == EN_SETFOCUS)
  343.             {
  344.             //  We have to trap EN_SETFOCUS messages so we know which
  345.             //  control was active last. When a menu selection is made
  346.             //  our current control will lose focus and because of a
  347.             //  curiosity in Windows' MDI management, we won't get it
  348.             //  back. Instead, WinPMail generates a WM_FM_RESTOREFOCUS
  349.             //  message which signals to us that we should set focus to
  350.             //  whatever the last active control was.
  351.  
  352.             last_focus = (HWND) LOWORD (lParam);
  353.             break;
  354.             }
  355.  
  356.          switch (wParam)
  357.             {
  358.             case 139 :
  359.                GetDlgItemText (hWnd, 101, host, sizeof (host));
  360.                GetDlgItemText (hWnd, 102, search, sizeof (search));
  361.                hOldCursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
  362.                ph_query (GetParent (hWnd), GetDlgItem (hWnd, 103), host, search);
  363.                SetCursor (hOldCursor);
  364.                break;
  365.  
  366.             case 140 :
  367.                len = GetWindowTextLength (GetDlgItem (hWnd, 103));
  368.                if (len)
  369.                   {
  370.                   l = SendDlgItemMessage (hWnd, 103, EM_GETSEL, 0, 0);
  371.                   if ((s = (char *) malloc (len + 16)) == NULL) break;
  372.                   GetWindowText (GetDlgItem (hWnd, 103), s, len);
  373.                   txt = s + LOWORD(l);
  374.                   *(s + HIWORD(l)) = '\0';
  375.                   SendMessage (GetParent (hWnd), WM_F_PASTEDATA, 1, (LPARAM) txt);
  376.                   free (s);
  377.                   }
  378.                break;
  379.             }
  380.          break;
  381.       }
  382.  
  383.    if (fCallDefProc)
  384.       dwResult = BWCCDefDlgProc (hWnd, wMsg, wParam, lParam);
  385.  
  386.    return dwResult;
  387.    }
  388.  
  389.  
  390. #pragma warn -sus
  391.  
  392. void unregister_form_classes (void)
  393.    {
  394.    //  Remove any classes associated with the form; we have the
  395.    //  same problem here as we do with registering the classes
  396.    //  for the DLL - we only want to deregister the classes on
  397.    //  the last time we're unloaded.
  398.  
  399.    if (GetModuleUsage (hLibInstance) > 1) return;      //  Not a problem
  400.    UnregisterClass (szFormDlgClassName, hLibInstance);
  401.    }
  402.  
  403.  
  404. BOOL FAR PASCAL LibMain (HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
  405.    {
  406.    WNDCLASS wc;
  407.  
  408.    if (! hLibInstance)
  409.       {
  410.       hLibInstance = hInst;
  411.       BWCCGetVersion ();      //  Forces BWCC to be dynamically loaded.
  412.  
  413.       //  Register any window classes used by the form. Forms will usually
  414.       //  register either one or occasionally two classes which define
  415.       //  the composition and reader dialogs created by the DLL.
  416.       //
  417.       //  There's a gotcha here, of course (aren't there always, in
  418.       //  Windows?)... You can't register a window class more than once,
  419.       //  so if the DLL has already been loaded and the user asks to
  420.       //  create a second instance of the form, we have to be careful
  421.       //  not to re-register the class. We do this by checking the
  422.       //  instance usage counter of the DLL - if it's greater than 1,
  423.       //  then we DON'T register the classes.
  424.  
  425.       wc.style          = WS_CHILD;
  426.       wc.lpfnWndProc    = FormProc;
  427.       wc.cbClsExtra     = 0;
  428.       wc.cbWndExtra     = DLGWINDOWEXTRA;
  429.        wc.hInstance      = hLibInstance;
  430.        wc.hIcon          = NULL;
  431.       wc.hCursor        = LoadCursor (NULL, IDC_ARROW);
  432.       wc.hbrBackground  = (HBRUSH) (COLOR_WINDOW + 1);
  433.       wc.lpszMenuName   = NULL;
  434.       wc.lpszClassName  = szFormDlgClassName;
  435.       if (! RegisterClass (&wc))
  436.          MessageBeep (0);
  437.       }
  438.  
  439.    return (TRUE);             // Initialization went OK
  440.    }
  441.  
  442.