A little bit later than I had hoped, I now bring you the third article in my series on Internet Explorer add-on development. As promised, we're going to look at dealing with user input.
This article is really only relevant if you want to handle keyboard input, but you probably at least want to allow a user to use the arrow keys to navigate your toolbar buttons, so the odds are good that you need this if you're writing a toolbar. The Find As You Type add-on also has a textbox on the toolbar, so it needs this even more.
As it says on MSDN, if you want to handle keyboard input you must implement the IInputObject interface on your object, and when you implement IObjectWithSite::SetSite, you should QueryInterface the IUnknown object you get passed for IInputObjectSite and store this pointer somewhere.
This IInputObjectSite pointer is used to tell IE when you have the keyboard focus. It has a single method named OnFocusChangedIO, which you should call whenever your toolbar receives or loses the input focus.
This means you should handle the WM_SETFOCUS and WM_KILLFOCUS messages. Last time, we subclassed IE's rebar control, but this is not the place to handle these messages! Internet Explorer really isn't interested in you telling it when its own rebar control got focus. It wants to know when your controls got focus.
This means we must do yet more subclassing. Every control in your add-on that can get the keyboard focus must be subclassed. In the Find As You Type add-on, these are the toolbar itself and the textbox. You can use the same procedure as used previously, just substitute the window handles, window procedures and the variable used to store the old window procedure. Don't forget to put always call the original window procedure at the end of your own and to put the original back when the control is destroyed, just like last time.
In this case, you own the windows you are subclassing, so you can store the pointer to your toolbar object directly in those windows (for the toolbar, we already did this last time) and we don't need to use FindWindow in the window procedure, just call GetWindowLongPtr on the window handle that was passed to the WndProc. We can now handle the WM_SETFOCUS and WM_KILLFOCUS messages and call IInputObjectSite::OnFocusChangedIO passing true and false respectively.
In concert with this, you should implement IInputObject::HasFocusIO to return true when you have the focus and false when not. You can set a flag when handling the focus messages, or you can use GetFocus to see if one of your windows has the focus.
The more interesting part comes with TranslateAcceleratorIO. Even if you don't want to do anything special, you must call TranslateMessage and DispatchMessage here, otherwise keyboard input can break in strange ways, which tend to be inconsistent across browser version. In my case it seemed to work right in IE6, but broke in IE7. So call those two functions.
For IE7 compatibility, you should also check if the message is referring to any of the window navigation keys, such as tab, shift-tab and F6. When you get any of those keys, don't call TranslateMessage and DispatchMessage. Instead, return S_FALSE, so IE can properly respond to those keys. If you have more than one control, you can choose to handle the tab key messages to navigate between your own controls. IE6 doesn't seem to send tab key messages to toolbars, but IE7 does.
If you have any actual accelerators, here's the place to implement them. You can manually check the message, or add an accelerator table and call TranslateAccelerator. You can use this for shortcut keys and mnemonics used by your toolbar.
You might have noticed that TranslateAcceleratorIO is called by IE only when your toolbar has the input focus. It is indeed impossible to handle keyboard shortcuts if your toolbar doesn't have the focus using this method. Vista appears to have a way to solve this (although I have not tested if it actually works) with the IInputObject2 interface, which defines a TranslateAcceleratorGlobal method. This solution, if it works, would still be limited to when your toolbar is loaded (even if it doesn't need to have the focus) and would be Vista specific.
The other way to respond to keys globally that doesn't have these drawbacks is by using a Browser Helper Object and a keyboard hook. But I will leave that for a future post.