home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / visvile / oleauto.cpp < prev    next >
C/C++ Source or Header  |  1998-09-07  |  17KB  |  629 lines

  1. /*
  2.  * oleauto.cpp - this module:
  3.  *
  4.  * 1) supplies several miscellaneous helper functions,
  5.  * 2) manipulates the winvile OLE automation server, and
  6.  * 3) implements a visvile configuration dialog box.
  7.  */
  8.  
  9. #include "stdafx.h"
  10. #include "resource.h"
  11. #include <initguid.h>
  12. #include "w32reg.h"    // located in the mainline vile development project
  13.                        // directory, one level "up".
  14. #include "oleauto.h"
  15.  
  16. #include <string.h>
  17.  
  18. #define RESTORE_WDW     TRUE
  19. #define NO_RESTORE_WDW  FALSE
  20.  
  21. // Registry configuration keys and subkeys
  22. #define CD_KEYVAL          "ChdirDocPath"
  23. #define CLOSE_DOC_KEYVAL   "CloseDoc"
  24. #define ENABLE_KEYVAL      "Enable"
  25. #define ROOT_CFG_KEY       "Software\\Winvile\\VisVile"
  26. #define SYNC_ERRBUF_KEYVAL "SyncErrbuf"
  27. #define WRITE_BUFFERS      "WriteModifiedBuffers"
  28.  
  29. static size_t   ansibuf_len,   /* scaled in bytes   */
  30.                 olebuf_len;    /* scaled in wchar_t */
  31. static char     *ansibuf;
  32. static OLECHAR  *olebuf;
  33.  
  34. static int      regupdate_required;
  35.  
  36. CVile           *pVile = NULL;
  37. VISVILE_OPTS    visvile_opts;
  38.  
  39. /* ----------------------------------------------------------------- */
  40.  
  41. void
  42. visvile_regdata_dirty(void)
  43. {
  44.     regupdate_required = TRUE;
  45. }
  46.  
  47.  
  48.  
  49. /*
  50.  * FUNCTION
  51.  *   fmt_win32_error(HRESULT errcode, char **buf, ULONG buflen)
  52.  *
  53.  *   errcode - win32 error code for which message applies.
  54.  *
  55.  *   buf     - indirect pointer to buf to receive formatted message.  If *buf
  56.  *             is NULL, the buffer is allocated on behalf of the client and
  57.  *             must be free'd using LocalFree().
  58.  *
  59.  *   buflen  - length of buffer (specify 0 if *buf is NULL).
  60.  *
  61.  * DESCRIPTION
  62.  *   Format system error reported by Win32 API.
  63.  *
  64.  * RETURNS
  65.  *   *buf
  66.  */
  67.  
  68. static char *
  69. fmt_win32_error(HRESULT errcode, char **buf, ULONG buflen)
  70. {
  71.     int flags = FORMAT_MESSAGE_FROM_SYSTEM;
  72.  
  73.     if (! *buf)
  74.         flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
  75.     FormatMessage(flags,
  76.                   NULL,
  77.                   errcode,
  78.                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* dflt language */
  79.                   (*buf) ? *buf : (LPTSTR) buf,
  80.                   buflen,
  81.                   NULL);
  82.     return (*buf);
  83. }
  84.  
  85.  
  86.  
  87. HRESULT
  88. ReportLastError(HRESULT err)
  89. {
  90.     char *buf = NULL, tmp[512];
  91.  
  92.     if (err != REGDB_E_CLASSNOTREG)
  93.     {
  94.         fmt_win32_error(err, &buf, 0);
  95.         sprintf(tmp,
  96.            "Unexpected error encountered.\r\rError code:  %lx\rDescription: %s",
  97.                 err,
  98.                 buf);
  99.     }
  100.     else
  101.     {
  102.         strcpy(tmp,
  103.                "Winvile OLE Automation registration lookup failed.\r\r"
  104.                "The command 'winvile -Or' registers an OLE-aware "
  105.                "version of the editor.");
  106.     }
  107.  
  108.     ::MessageBox(NULL, tmp, PROGNAM, MB_OK|MB_ICONSTOP);
  109.     if (buf)
  110.         LocalFree(buf);
  111.     return (err);
  112. }
  113.  
  114.  
  115.  
  116. /*
  117.  * Quick & Dirty ANSI/Unicode conversion routines.  These routines use a
  118.  * dynamic buffer to hold the converted string so it may be any arbitrary
  119.  * size.  However, the same dynamic buffer is reused when the routine is
  120.  * called a second time.  So make sure that the converted string is
  121.  * used/copied before the conversion routine is called again.
  122.  *
  123.  */
  124. #if (! defined(_UNICODE) || defined(UNICODE))
  125. char *
  126. ConvertToAnsi(OLECHAR *szW)
  127. {
  128.     size_t len;
  129.  
  130.     len = wcstombs(NULL, szW, 1) + 1;
  131.     if (len > ansibuf_len)
  132.     {
  133.         if (ansibuf)
  134.             free(ansibuf);
  135.         ansibuf_len = ansibuf_len * 2 + len;
  136.         if ((ansibuf = (char *) malloc(ansibuf_len)) == NULL)
  137.             return (ansibuf);  /* We're gonna' die */
  138.     }
  139.     wcstombs(ansibuf, szW, len);
  140.     return (ansibuf);
  141. }
  142.  
  143.  
  144.  
  145. OLECHAR *
  146. ConvertToUnicode(char *szA)
  147. {
  148.     size_t len;
  149.  
  150.     len = strlen(szA) + 1;
  151.     if (len > olebuf_len)
  152.     {
  153.         if (olebuf)
  154.             free(olebuf);
  155.         olebuf_len = olebuf_len * 2 + len;
  156.         if ((olebuf = (OLECHAR *) malloc(olebuf_len * sizeof(OLECHAR))) == NULL)
  157.             return (olebuf);  /* We're gonna' die */
  158.     }
  159.     mbstowcs(olebuf, szA, len);
  160.     return (olebuf);
  161. }
  162. #endif
  163.  
  164.  
  165.  
  166. static DWORD
  167. get_reg_cfg_val(HKEY key, char *value_name, DWORD def_value)
  168. {
  169.     DWORD val_type, result, result_size;
  170.  
  171.     val_type    = REG_DWORD;
  172.     result_size = sizeof(result);
  173.     if (RegQueryValueEx(key,
  174.                         value_name,
  175.                         NULL,
  176.                         &val_type,
  177.                         (BYTE *) &result,
  178.                         &result_size) != ERROR_SUCCESS)
  179.     {
  180.         // Assume value missing -- use default.
  181.  
  182.         result = def_value;
  183.         visvile_regdata_dirty();
  184.     }
  185.     return (result);
  186. }
  187.  
  188.  
  189.  
  190. // Read configuration values from HKEY_CURRENT_USER\Software\Winvile\VisVile
  191. static void
  192. get_addin_config(void)
  193. {
  194.     HKEY hk;
  195.  
  196.     // Supply fallback, default VisVile configuration values.
  197.     visvile_opts.enabled =
  198.         visvile_opts.close_ds_doc =
  199.             visvile_opts.write_buffers = TRUE;
  200.  
  201.     // All other options default to FALSE....
  202.  
  203.     // Now, read "old" config values from registry (if they exist).
  204.     if (RegOpenKeyEx(HKEY_CURRENT_USER,
  205.                      ROOT_CFG_KEY,
  206.                      0,
  207.                      KEY_READ,
  208.                      &hk) == ERROR_SUCCESS)
  209.     {
  210.         visvile_opts.cd_doc_dir    = get_reg_cfg_val(hk,
  211.                                                      CD_KEYVAL,
  212.                                                      visvile_opts.cd_doc_dir);
  213.         visvile_opts.close_ds_doc  = get_reg_cfg_val(hk,
  214.                                                      CLOSE_DOC_KEYVAL,
  215.                                                      visvile_opts.close_ds_doc);
  216.         visvile_opts.enabled       = get_reg_cfg_val(hk,
  217.                                                      ENABLE_KEYVAL,
  218.                                                      visvile_opts.enabled);
  219.         visvile_opts.sync_errbuf   = get_reg_cfg_val(hk,
  220.                                                      SYNC_ERRBUF_KEYVAL,
  221.                                                      visvile_opts.sync_errbuf);
  222.         visvile_opts.write_buffers = get_reg_cfg_val(hk,
  223.                                                      WRITE_BUFFERS,
  224.                                                     visvile_opts.write_buffers);
  225.         RegCloseKey(hk);
  226.     }
  227.     else
  228.     {
  229.         /*
  230.          * No configuration info stored in registry.  Force registry
  231.          * update when visvile exits.
  232.          */
  233.  
  234.         visvile_regdata_dirty();
  235.     }
  236. }
  237.  
  238.  
  239.  
  240. static void
  241. set_reg_cfg_val(HKEY key, char *value_name, DWORD value)
  242. {
  243.     long rc;
  244.  
  245.     if ((rc = RegSetValueEx(key,
  246.                             value_name,
  247.                             NULL,
  248.                             REG_DWORD,
  249.                             (BYTE *) &value,
  250.                             sizeof(value))) != ERROR_SUCCESS)
  251.     {
  252.         ReportLastError(rc);
  253.     }
  254. }
  255.  
  256.  
  257.  
  258. // Write configuration values to HKEY_CURRENT_USER\Software\Winvile\VisVile
  259. static void
  260. set_addin_config(void)
  261. {
  262.     HKEY hk;
  263.     LONG rc;
  264.  
  265.     if ((rc = RegCreateKeyEx(HKEY_CURRENT_USER,
  266.                              ROOT_CFG_KEY,
  267.                              0,
  268.                              NULL,
  269.                              0,
  270.                              KEY_ALL_ACCESS,
  271.                              NULL,
  272.                              &hk,
  273.                              NULL)) != ERROR_SUCCESS)
  274.     {
  275.         ReportLastError(rc);
  276.         return;
  277.     }
  278.     set_reg_cfg_val(hk, CD_KEYVAL,          visvile_opts.cd_doc_dir);
  279.     set_reg_cfg_val(hk, CLOSE_DOC_KEYVAL,   visvile_opts.close_ds_doc);
  280.     set_reg_cfg_val(hk, ENABLE_KEYVAL,      visvile_opts.enabled);
  281.     set_reg_cfg_val(hk, SYNC_ERRBUF_KEYVAL, visvile_opts.sync_errbuf);
  282.     set_reg_cfg_val(hk, WRITE_BUFFERS,      visvile_opts.write_buffers);
  283.     RegCloseKey(hk);
  284. }
  285.  
  286.  
  287.  
  288. HRESULT
  289. oleauto_init(void)
  290. {
  291.     get_addin_config();
  292.     olebuf_len = ansibuf_len = 512;
  293.     ansibuf    = (char *) malloc(ansibuf_len);
  294.     olebuf     = (OLECHAR *) malloc(olebuf_len * sizeof(OLECHAR));
  295.     pVile      = new CVile();
  296.     if (! (olebuf && ansibuf && pVile))
  297.         return (ReportLastError(E_OUTOFMEMORY));
  298.     else
  299.         return (S_OK);
  300. }
  301.  
  302. void
  303. oleauto_exit(void)
  304. {
  305.     if (regupdate_required)
  306.         set_addin_config();
  307.     if (pVile)
  308.         delete pVile;
  309.     if (olebuf)
  310.         free(olebuf);
  311.     if (ansibuf)
  312.         free(ansibuf);
  313. }
  314.  
  315. /* ------------------------------ CVile ------------------------- */
  316.  
  317. void
  318. CVile::Disconnect()
  319. {
  320.     if (pVileAuto)
  321.     {
  322.         (void) pVileAuto->Release();
  323.         pVileAuto = NULL;
  324.     }
  325. }
  326.  
  327.  
  328.  
  329. bool
  330. CVile::Connected()
  331. {
  332.     bool         connected = FALSE;
  333.     VARIANT_BOOL visible;
  334.  
  335.     if (pVileAuto)
  336.     {
  337.         /*
  338.          * We have a connection to a winvile instance (maybe).  Obtain
  339.          * winvile application visibility state to verify connection.
  340.          */
  341.  
  342.         connected = SUCCEEDED(pVileAuto->get_Visible(&visible));
  343.     }
  344.     return (connected);
  345. }
  346.  
  347.  
  348.  
  349. HRESULT
  350. CVile::Connect(int restore_wdw, VARIANT_BOOL *in_insert_mode)
  351. {
  352.     HRESULT hr;
  353.  
  354.     *in_insert_mode = VARIANT_TRUE;   // Set a known, safe value.
  355.     if (pVileAuto)
  356.     {
  357.         VARIANT_BOOL visible;
  358.  
  359.         /*
  360.          * We have a connection to a winvile instance (maybe).  First obtain
  361.          * winvile application visibility state, which provides a true test
  362.          * of whether or not the connection exists.
  363.          */
  364.         hr = pVileAuto->get_Visible(&visible);
  365.         if (FAILED(hr))
  366.         {
  367.             // Assume the user has closed our previous editor instance.
  368.  
  369.             Disconnect();  // kills editor connection and nulls pVileAuto.
  370.         }
  371.         else if (restore_wdw && ! visible)
  372.         {
  373.             hr = pVileAuto->put_Visible(TRUE);
  374.             if (FAILED(hr))
  375.                 Disconnect(); // Certainly didn't expect that.
  376.         }
  377.     }
  378.     if (! pVileAuto)
  379.     {
  380.         LPUNKNOWN punk;
  381.  
  382.         hr = CoCreateInstance(CLSID_VileAuto,
  383.                               NULL,
  384.                               CLSCTX_LOCAL_SERVER,
  385.                               IID_IUnknown,
  386.                               (void **) &punk);
  387.         if (FAILED(hr))
  388.             return (hr);
  389.         hr = punk->QueryInterface(IID_IVileAuto, (void **) &pVileAuto);
  390.         punk->Release();
  391.         if (FAILED(hr))
  392.             return (hr);
  393.         if (restore_wdw)
  394.         {
  395.             hr = pVileAuto->put_Visible(TRUE);   // Make the editor visible.
  396.             if (FAILED(hr))
  397.                 Disconnect(); // Certainly didn't expect that.
  398.         }
  399.     }
  400.     if (SUCCEEDED(hr))
  401.     {
  402.         if (restore_wdw)
  403.         {
  404.             VARIANT_BOOL minimized;
  405.  
  406.             /* Restore editor if minimized. */
  407.  
  408.             hr = pVileAuto->get_IsMinimized(&minimized);
  409.             if (SUCCEEDED(hr) && minimized)
  410.                 hr = pVileAuto->Restore();
  411.         }
  412.         if (SUCCEEDED(hr))
  413.         {
  414.             // return editor's "mode" state to client
  415.  
  416.             hr = pVileAuto->get_InsertMode(in_insert_mode);
  417.         }
  418.     }
  419.     return (hr);
  420. }
  421.  
  422.  
  423.  
  424. HRESULT
  425. CVile::FileOpen(BSTR filename, long lineno)
  426. {
  427.     BSTR         bcmd;
  428.     char         cmd[1024], line[64], cd[512];
  429.     HRESULT      hr;
  430.     VARIANT_BOOL in_insert_mode;        // editor in insert mode?
  431.     OLECHAR      *ole;
  432.  
  433.     hr = Connect(RESTORE_WDW, &in_insert_mode);
  434.     if (SUCCEEDED(hr))
  435.     {
  436.         sprintf(cmd,
  437.                 "%s:e %S\n",
  438.                 (in_insert_mode) ? "\033" : "",   // prepend ESC if necessary
  439.                 filename);
  440.         if (visvile_opts.cd_doc_dir)
  441.         {
  442.             char *tmp, *cp;
  443.  
  444.             if ((tmp = FROM_OLE_STRING(filename)) == NULL)
  445.                 return (E_OUTOFMEMORY);
  446.             if ((cp = strrchr(tmp, '\\')) != NULL)
  447.             {
  448.                 *cp = '\0';
  449.                 sprintf(cd, ":cd %s\n", tmp);
  450.                 *cp = '\\';
  451.                 strcat(cmd, cd);
  452.             }
  453.         }
  454.         if (lineno > 1)
  455.         {
  456.             sprintf(line, ":%ld\n", lineno);
  457.             strcat(cmd, line);
  458.         }
  459.         ole  = TO_OLE_STRING(cmd);
  460.         bcmd = SysAllocString(ole);
  461.         if (! (bcmd && ole))
  462.             return (E_OUTOFMEMORY);
  463.         hr = pVileAuto->VileKeys(bcmd);
  464.         SysFreeString(bcmd);  // don't need to free "ole", it points at a
  465.                               // global, dynamic tmp buffer.
  466.         if (SUCCEEDED(hr))
  467.             hr = pVileAuto->ForegroundWindow();  // Pop editor to front.
  468.     }
  469.     return (hr);
  470. }
  471.  
  472.  
  473.  
  474. HRESULT
  475. CVile::VileCmd(
  476.     const char *cmd,
  477.     bool       restore_wdw,     // T -> make editor visible and pop its window
  478.                                 //      on top.
  479.     bool       force_connection // T -> force connection to editor if
  480.                                 //      necessary.
  481.                                 // F -> don't execute command if editor
  482.                                 //      disconnected.
  483.               )
  484. {
  485.     BSTR         bcmd;
  486.     HRESULT      hr;
  487.     VARIANT_BOOL in_insert_mode;        // editor in insert mode?
  488.     OLECHAR      *ole;
  489.     char         *scratch = NULL;
  490.  
  491.     if (! force_connection && ! Connected())
  492.     {
  493.         // The command should not be executed.
  494.  
  495.         return (S_OK);
  496.     }
  497.  
  498.     hr = Connect(restore_wdw ? RESTORE_WDW : NO_RESTORE_WDW, &in_insert_mode);
  499.     if (SUCCEEDED(hr))
  500.     {
  501.         if (in_insert_mode)
  502.         {
  503.             // Prepend ESC to command.
  504.  
  505.             if ((scratch = (char *) malloc(strlen(cmd) + 2)) == NULL)
  506.                 return (E_OUTOFMEMORY);
  507.             scratch[0] = '\033';   // ESC
  508.             strcpy(scratch + 1, cmd);
  509.         }
  510.         ole  = TO_OLE_STRING((scratch) ? (char *) scratch : (char *) cmd);
  511.         bcmd = SysAllocString(ole);
  512.         if (! (bcmd && ole))
  513.             return (E_OUTOFMEMORY);
  514.         if (restore_wdw)
  515.         {
  516.             /*
  517.              * Pop editor to the front.  Note that there's a subtle
  518.              * sequencing problem here.  Some command strings will require
  519.              * restoration of the editor's window _before_ the string is
  520.              * executed.  Why?  The string may be attempting to open a file
  521.              * that's created as a side effect of a DevStudio operation
  522.              * that _follows_ execution of "cmd".  In that situation,
  523.              * visvile can't be waiting for the editor to finish execting
  524.              * "cmd" before directing the editor to pop to the front.  Why?
  525.              * 'Cause until visvile returns control to DevStudio, DevStudio
  526.              * won't create the target file.  Nasty, eh?  Sure wasted an
  527.              * evening of my life.
  528.              */
  529.  
  530.             hr = pVileAuto->ForegroundWindow();  // Pop editor to front.
  531.         }
  532.         if (SUCCEEDED(hr))
  533.             hr = pVileAuto->VileKeys(bcmd);
  534.         SysFreeString(bcmd);  // don't need to free "ole", it points at a
  535.                               // global, dynamic tmp buffer.
  536.         if (scratch)
  537.             free(scratch);
  538.     }
  539.     return (hr);
  540. }
  541.  
  542.  
  543. /////////////////////////////////////////////////////////////////////////////
  544. // CConfigDlg dialog
  545.  
  546.  
  547. CConfigDlg::CConfigDlg(CWnd* pParent /*=NULL*/)
  548.     : CDialog(CConfigDlg::IDD, pParent)
  549. {
  550.     //{{AFX_DATA_INIT(CConfigDlg)
  551.     m_enabled = FALSE;
  552.     m_sync_errbuf = FALSE;
  553.     m_close_ds_doc = FALSE;
  554.     m_cd_doc_dir = FALSE;
  555.     m_write_buffers = FALSE;
  556.     //}}AFX_DATA_INIT
  557. }
  558.  
  559.  
  560. void CConfigDlg::DoDataExchange(CDataExchange* pDX)
  561. {
  562.     CDialog::DoDataExchange(pDX);
  563.     //{{AFX_DATA_MAP(CConfigDlg)
  564.     DDX_Check(pDX, IDC_ENABLED, m_enabled);
  565.     DDX_Check(pDX, IDC_ERRBUF, m_sync_errbuf);
  566.     DDX_Check(pDX, IDC_CLOSE_DOC, m_close_ds_doc);
  567.     DDX_Check(pDX, IDC_CHDIR, m_cd_doc_dir);
  568.     DDX_Check(pDX, IDC_WRITE_BUFFERS, m_write_buffers);
  569.     //}}AFX_DATA_MAP
  570. }
  571.  
  572.  
  573. BEGIN_MESSAGE_MAP(CConfigDlg, CDialog)
  574.     //{{AFX_MSG_MAP(CConfigDlg)
  575.     ON_BN_CLICKED(IDC_ENABLED, OnEnabled)
  576.     //}}AFX_MSG_MAP
  577. END_MESSAGE_MAP()
  578.  
  579. /////////////////////////////////////////////////////////////////////////////
  580. // CConfigDlg message handlers
  581.  
  582. BOOL CConfigDlg::OnInitDialog()
  583. {
  584.     m_cd_doc_dir    = visvile_opts.cd_doc_dir;
  585.     m_close_ds_doc  = visvile_opts.close_ds_doc;
  586.     m_enabled       = visvile_opts.enabled;
  587.     m_sync_errbuf   = visvile_opts.sync_errbuf;
  588.     m_write_buffers = visvile_opts.write_buffers;
  589.  
  590.     /*
  591.      * Now, depending on value of m_enabled, manually enable/disable all
  592.      * other controls.  I'm doing this because I'm learning MFC/Windows
  593.      * programming, not because I particularly care if the dialog box
  594.      * responds to user input or not.
  595.      */
  596.     update_dlg_control_states(FALSE);
  597.     return (CDialog::OnInitDialog()); }
  598.  
  599. void
  600. CConfigDlg::OnEnabled()
  601. {
  602.     // Called when user clicks the Enabled check box.
  603.     update_dlg_control_states(TRUE);
  604. }
  605.  
  606. void
  607. CConfigDlg::update_dlg_control_states(bool dialog_up)
  608. {
  609.     if (dialog_up)
  610.         UpdateData(TRUE);     // copy current dlg data into member vars.
  611.     update_dlg_control(IDC_CLOSE_DOC, dialog_up);
  612.     update_dlg_control(IDC_CHDIR, dialog_up);
  613.     update_dlg_control(IDC_WRITE_BUFFERS, dialog_up);
  614.     update_dlg_control(IDC_ERRBUF, dialog_up);
  615. }
  616.  
  617. void
  618. CConfigDlg::update_dlg_control(int ctrl_id, bool dialog_up)
  619. {
  620.     CWnd *hctrl;
  621.  
  622.     if ((hctrl = GetDlgItem(ctrl_id)) != NULL)
  623.     {
  624.         hctrl->EnableWindow(m_enabled);
  625.         if (dialog_up)
  626.             hctrl->UpdateWindow();
  627.     }
  628. }
  629.