Version 1 of tcom Allows Emacs as Editor for MS Outlook

Updated 2003-06-25 16:35:39

MNO Playing around with the tcom extension after the inspiration of some Python code at http://disgruntled-programmer.com/notes/emacs-with-outlook.html , I came up with the following:-

It basically allows a running NTEmacs editor to grab the text of an email reply/forward etc. from Outlook and edit it in Emacs. It is then possible to transfer the edited text back to the outlook window and send it from there.

Currently only tested with Outlook 2000, ActiveTcl 8.4.3 and NTEmacs 21.3.1 under Windows 2000.

PLEASE READ THE LAST POINT UNDER "KNOWN ISSUES" BELOW BEFORE USING THIS CODE!

Installation

  • put the outlookedit.el lisp file somewhere in your load-path and add a (require 'outlookedit) into your .emacs file
  • place the two Tcl scripts onto your system and edit the locations in the outlookedit.el file

Usage

  • select a message in Outlook and click "Reply" (or "Reply to all" etc.)
  • switch to your NTEmacs window and press C-c o e (that is Control-C followed by "o" then "e" - mnemonic: Outlook Edit)
  • the message text should now appear in an Emacs buffer
  • edit in the emacs buffer
  • press C-c o s (mnemonic Outlook Save)
  • the edited text should now replace the text in the outlook window
  • press Send as usual

Known Issues

  • Outlook 2000 (contrary to its documentation) renders the edited message as Rich Text regardless of the original format or the users specified preferred format. Outlook 2002 is reported to fix this by introducing a BodyFormat parameter.
  • If you change the configuration of active Outlook windows between grabbing the text and saving it back to Outlook you will probably overwrite the wrong text. You have been warned! Don't blame me!

The Code

The Emacs-Lisp file outlookedit.el

 ;;; outlookedit.el
 ;; use a couple of Tcl scripts to invoke my Tcl scripts to do COM
 ;; related actions on Outlook to get and replace text in the
 ;; reply/compose boxes allowing it to be edited in Emacs
 ;;
 (defvar mno-get-outlook-body 
   "tclsh C:\\Userdata\\Tcl\\grabOutlookMessage.tcl")
 (defvar mno-put-outlook-body 
   "tclsh C:\\Userdata\\Tcl\\putMessageInOutlook.tcl")
 (defvar mno-outlook-default-justification 'full)  

 (global-set-key "\C-coe" 'mno-edit-outlook-message)
 (global-set-key "\C-cos" 'mno-save-outlook-message)

 (defun mno-edit-outlook-message ()
   "* Slurp an outlook message into a new buffer ready for editing 

 The message must be in the active Outlook window.  Typically the
 user would press the Reply or Reply-all button in Outlook then
 switch to Emacs and invoke \\[mno-edit-outlook-message]

 Once all edits are done, the function mno-save-outlook-message
 (invoked via \\[mno-save-outlook-message]) can be used to send the
 newly edited text back into the Outlook window.  It is important that
 the same Outlook window is current in outlook as was current when the
 edit was started when this command is invoked."
   (interactive)
   (save-excursion
     (let ((buf (get-buffer-create "*Outlook Message*"))
           (body (shell-command-to-string mno-get-outlook-body)))
       (switch-to-buffer buf)
       (message-mode) ; enables automagic reflowing of text in quoted
                      ; sections
       (setq default-justification mno-outlook-default-justification)
       (setq body (replace-regexp-in-string "\r" "" body))
       (delete-region (point-min) (point-max))
       (insert body)
       (goto-char (point-min))))) 


 (defun mno-save-outlook-message ()
   "* Send the outlook message buffer contents back to Outlook current window 

 Unfortunately, Outlook 2000 then renders this text as Rich Text format
 rather than plain text, overriding any user preference for plain text.
 The user then needs to select Format->Plain text in the outlook
 compose window to reverse this.

 Outlook 2002 apparently has a BodyFormat parameter to control this."
   (interactive)
   (save-excursion
     (let ((buf (get-buffer "*Outlook Message*")))
       (set-buffer buf)
       (shell-command-on-region (point-min) (point-max) mno-put-outlook-body)
       (set-buffer-modified-p 'nil) ; now kill-buffer won't complain!
       (kill-buffer "*Outlook Message*")))) 

 (provide 'outlookedit)
 ;;;end of file outlookedit.el

The grab and put functions used above.

 #!/bin/sh
 # Emacs please open this in -*-Tcl-*- mode
 # do not remove this backslash! -> \
     exec tclsh $1 ${1+"$@"}
 #
 # loosely based on Python code from 
 # http://disgruntled-programmer.com/notes/emacs-with-outlook.html
 #
 package require tcom

 set body [read stdin]
 #
 # find Outlook:-
 if { [catch {::tcom::ref getactiveobject Outlook.Application} o] } {
     puts "Couldn't find Outlook via COM: $o"
     exit 1
 }
 # get the current item:-
 if { [catch {$o -get ActiveInspector} a] } {
     puts "Couldn't get the ActiveInspector for Outlook via COM: $a"
     exit 2
 }
 if { [catch {$a -get CurrentItem} i] } {
     puts "Couldn't get the CurrentItem for Outlook via COM: $i"
     exit 3
 }   
 # and finally replace its body
 if { [catch {$i -set Body $body} err] } {
     puts "Couldn't replace the Body via COM: $err"
     exit 4
 }
 # The following (untested) might convert back from RTF in Outlook 2002
 catch {$i -set BodyFormat 1}
 # that's it

and,

 #!/bin/sh
 # Emacs please open this in -*-Tcl-*- mode
 # do not remove this backslash! -> \
     exec tclsh $1 ${1+"$@"}
 #
 # loosely based on Python code from 
 # http://disgruntled-programmer.com/notes/emacs-with-outlook.html
 #
 package require tcom
 #
 # find Outlook:-
 if { [catch {::tcom::ref getactiveobject Outlook.Application} o] } {
     puts "Couldn't find Outlook via COM: $o"
     exit 1
 }
 # get the current item:-
 if { [catch {$o -get ActiveInspector} a] } {
     puts "Couldn't get the ActiveInspector for Outlook via COM: $a"
     exit 2
 }
 if { [catch {$a -get CurrentItem} i] } {
     puts "Couldn't get the CurrentItem for Outlook via COM: $i"
     exit 3
 }   
 # and finally slurp its body
 if { [catch {$i -get Body} body] } {
     puts "Couldn't retrieve body from CurrentItem via COM: $body"
     exit 4
 }
 #
 # spew it out to stdout
 #
 puts $body
 # that's it