Jump to content


Emacs key bindings for the Outlook 2007 email editor


  • Please log in to reply
3 replies to this topic

#1 catweazle

catweazle
  • Guests

Posted 26 September 2007 - 09:56 AM

This script provides a basic set of Emacs key bindings for use with the Outlook 2007 email editor. See the commentary for more details.

One caveat: the first time the find dialog is shown (via Ctrl+S) you need to manually click the "More" button so that the script can access the Search dropdown list and change the search direction when Ctrl+R/Ctrl+S are pressed. This requirement could probably be removed.

John.

; Emacs key bindings for the Outlook 2007 email editor -- by John Cooper.
; Last updated: September 24th 2007
; 
; Requires AutoHotkey: http://www.autohotkey.com
;
; This ahk script provides a basic set of Emacs key bindings for use in the
; Outlook 2007 email editor.
;
; In Outlook 2003 and earlier (and Word 2007), an enhanced version of this
; functionality is already provided by VBacs (http://rath.ca/Misc/VBacs/), a
; set of VBA (Visual Basic for Applications) macros to remap the keyboard and
; implement functionality for a basic set of Emacs key bindings.
;
; In Outlook 2007 it is no longer possible to bind arbitrary key sequences to
; macros; user-defined macros can be added to the "Quick Access Toolbar" on the
; left side of the titlebar, but these are consecutively bound to Alt+1, Alt+2,
; Alt+3, etc.
;
; This script implements a limited subset of Emacs bindings, primarily by
; simple key remapping. When interaction with the Word object model is
; required, Outlook macros have been written and added to the Quick Launch
; Toolbar; these are then invoked by mapping the relevant Emacs key sequence to
; the corresponding Alt+<n> binding.
;
; The commands that use macros in this way are:
;   Ctrl+T - Transpose previous 2 characters
;   Ctrl+X Ctrl+X - Exchange point and mark
;
; To configure these 2 macro-based commands, you need to do the following:
;
; 1) In Outlook, open "Tools>Macro>Visual Basic Editor" and enter the following
;    code in the "ThisOutlookSession" project:
;
;      Sub ExchangePointAndMark()
;          Set oDoc = ActiveInspector.WordEditor
;          Set oWord = oDoc.Application
;          Set s = oWord.Selection
;      
;          s.StartIsActive = Not s.StartIsActive
;      End Sub
;      
;      Sub TransposeChars()
;          Set oDoc = ActiveInspector.WordEditor
;          Set oWord = oDoc.Application
;          Set s = oWord.Selection
;      
;          s.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
;          On Error GoTo ErrExit
;          s.Cut
;          s.MoveLeft Unit:=wdCharacter, Count:=1
;          s.Paste
;          s.MoveRight Unit:=wdCharacter, Count:=1
;      ErrExit:
;      End Sub
;   
;   Click the Save icon on the toolbar.
;
; 2) In Outlook, select "File>New>Mail Message" to open a blank email
;    composition window. In this window, locate the "Quick Access Toolbar"
;    towards the left of the title bar and click the down arrow. Select "More
;    Commands" and then in the "Choose commands from" dropdown select
;    "Macros". Select each of the new macros in the left pane and click the
;    "Add" button to add the macros to the quick launch toolbar.
;
; 3) Return to the New Message window and tap and release the Alt key to
;    determine what key sequence (Alt+<number>) is bound to each of the macros
;    that were added to the Quick Launch Toolbar.
;
; 4) Update this script if necessary to send the appropriate macro binding in
;    response to the corresponding Emacs key sequence.


; Match strings anywhere in the window title
SetTitleMatchMode, 2

numericPrefix = 1
ctrlXPrefix = 0

; Emacs key bindings for the Microsoft Outlook 2007 email editor
#IfWinActive - Message ahk_class rctrl_renwnd32

; Ctrl+A - Start of line
^a::
  Send, {Home}
  ctrlXPrefix = 0
  numericPrefix = 1
  return

; Ctrl+B - Character backward
^b::
  Send, {Left %numericPrefix% }
  ctrlXPrefix = 0
  numericPrefix = 1
  return

; Ctrl+D - Delete character forward
^d::
  Send, {Delete}
  ctrlXPrefix = 0
  numericPrefix = 1
  return

; Ctrl+E - End of line
^e::
  Send, {End}
  ctrlXPrefix = 0
  numericPrefix = 1
  return

; Ctrl+F - Character forward
^f::
  Send, {Right %numericPrefix% }
  ctrlXPrefix = 0
  numericPrefix = 1
  return

; Ctrl+G - Cancel
^g::
  Send, {Escape}
  ctrlXPrefix = 0
  numericPrefix = 1
  Return

; Ctrl+K - Delete to end of line
^k::
  ControlGetFocus, OutputVar, A
  If ErrorLevel
    MsgBox, The target window doesn't exist or none of its controls has input focus.
  Else
    If (OutputVar = "RichEdit20WPT2" or OutputVar = "RichEdit20WPT3")
    {
        Send, ^k
        ;MsgBox, Found it!
    }
    Else 
    {
      Send, +{End}
      Send, +{Left}
      Send, ^{c}
      Send, {Delete}
      ctrlXPrefix = 0
      numericPrefix = 1
    }
    ;MsgBox, Control with focus = %OutputVar%
  return

; Ctrl+N - Line down
^n::
  Send, {Down %numericPrefix% }
  ctrlXPrefix = 0
  numericPrefix = 1
  return
;^n:: SendAndClearNumericArg("{Down " . numericPrefix . "}", numericPrefix)

; Ctrl+O - Open line above
^o::
  Send, {Return}
  Send, {Left}
  ctrlXPrefix = 0
  numericPrefix = 1
  return

; Ctrl+P - Line up
^p::
  Send, {Up %numericPrefix% }
  ctrlXPrefix = 0
  numericPrefix = 1
  return

; Ctrl+R - Search backwards
^r::
  Send ^f
  Send +!;
  Send u
  Send {Return}
  Send !{n}
  Send {Return}
  ctrlXPrefix = 0
  numericPrefix = 1
  return
;$^s::
;  Send ^f
;  numericPrefix = 1
;  return

; Ctrl+S - Search forwards
; Ctrl+X Ctrl+S - Save
^s::
  If ctrlXPrefix = 1
    Send, ^s
  Else
  {
    Send ^f
    Send +!;
    Send d
    Send {Return}
    Send !{n}
    Send {Return}
    numericPrefix = 1
  }
  return

; Ctrl+T - Transpose previous characters
^t::
  ControlGetFocus, OutputVar, A
  If ErrorLevel
    MsgBox, The target window doesn't exist or none of its controls has input focus.
  Else
    If (OutputVar = "_WwG1")
    {
      Send, !7
    }
  return

; Ctrl+U - Set a numeric argument, in multiples of 4, which can be applied to
;          the following commands: Character forward, Character backward,
;          Line up, Line down, Delete character, Word forward, Word backward, Delete word.
^u::
  numericPrefix *= 4
  If numericPrefix > 256
    numericPrefix = 1
  ctrlXPrefix = 0
  return

; Ctrl+V - Page down
^v::
  Send, {PgDn}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Ctrl+W - Cut
^w::
  Send, ^{x}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Ctrl+X - Prefix
^x::
  If ctrlXPrefix = 1
  {
    Send, !6
    ctrlXPrefix = 0
  }
  Else
    ctrlXPrefix = 1
  return

; Ctrl+Y - Paste
$^y::
  Send, ^{v}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Ctrl+<space> - Set mark
^Space::
  Send, {F8}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Ctrl+/ - Undo
^/::
  Send, ^{z}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Ctrl+Shift+Z - Redo
^+z::
  Send, ^{y}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Ctrl+Alt+B - Bold
^!b::
  Send, !{h}
  Send, 1
  return

; Ctrl+Alt+I - Italic
^!i::
  Send, !{h}
  Send, 2
  return

; Ctrl+Alt+U - Underline
^!u::
  Send, !{h}
  Send, 3
  return

; Alt+B - Word backward
!b::
  Send, ^{Left %numericPrefix% }
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Alt+D - Delete word forward
!d::
  Send, ^{Delete %numericPrefix% }
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Alt+F - Word forward
!f::
  Send, ^{Right %numericPrefix% }
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Alt+V - Page up
!v::
  Send, {PgUp}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Shift+Alt+, - Start of document
+!,::
  Send, ^{Home}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Shift+Alt+. - End of document
+!.::
  Send, ^{End}
  numericPrefix = 1
  ctrlXPrefix = 0
  return

; Currently unused
SendAndClearNumericArg(key, ByRef numericPrefix)
  {
    Send, %key%
    numericPrefix = 1
    return 0
  }

#IfWinActive


; Microsoft Word's "find and replace" dialog
#IfWinActive Find and Replace
^g:: Send, {Escape}
^r::
  Send +!;
  Send u
  Send {Return}
  Send !{n}
  Send, !f
  return
^s::
  Send +!;
  Send d
  Send {Return}
  Send !{n}
  Send, !f
  return
#IfWinActive


; Microsoft Word's "save changes" confirmation dialog
#IfWinActive ahk_class #32770
^g:: Send, {Escape}
#IfWinActive


#2 maxjin

maxjin
  • Guests

Posted 21 January 2008 - 07:10 PM

This is very useful. Thanks for posting.

#3 Guests

  • Guests

Posted 13 April 2009 - 05:17 AM

I could follow upto step 3. Then completely Lost you on Step 4:

What did you mean by updating the script? :cry:

When I press the ALT key I see 6 & 7 next to PointAndMark() & TransposeChars().

Now what do I do next? I tried changing your NumericPrefix to 6/7 and ctrlXPrefix to 6/7 but did not work.

Please explain your step 4. :)

Thanks :D

#4 catweazle

catweazle
  • Guests

Posted 13 April 2009 - 09:21 AM

From what you say, the ExchangePointAndMark and TransposeChars VB macros are available (on your system) through the keyboard shortcuts Alt+6 and Alt+7. You can test these work as expected by pressing these key sequences in the email composition window.

Once you have that working Step 4 is all about making sure the ahk script maps the standard Emacs bindings (Ctrl+t for transpose-chars, and Ctrl+xCtrl+x for exchange-point-and-mark) to these Alt+ sequences that invoke the VB macros. Now, on your system the macros are already configured as Alt+6 and Alt+7 (i.e., you haven't previously configured any other macros), and the script is already set up to send Alt+6 and Alt+7 when you press the appropriate Emacs keys (Ctrl+xCtrl+x or Ctrl+t).

Therefore Ctrl+t (transpose-chars) and Ctrl+xCtrl+x should both just work without needing to make any script modifications.

Hope that helps,

John.