Click here to Skip to main content
Click here to Skip to main content

Mouse Gesture Add-in for MS DevStudio 6

, 8 Nov 2004
Rate this:
Please Sign up or sign in to vote.
An article on Mouse Gesture add-in for MS DevStudio 6.

Introduction

Mouse Gestures are getting quite popular now, and becomes a nice feature to have especially in web browsers such as Opera, FireFox and many other MDI IE mods (MyIE, Avante, Donut and Sleipnir). It is so addicted that once you get used to using it, you won't be able to surf the net without it. At least, I did. So, why shouldn't I have a similar tool for MS-DevStudio which I have used the second most after web browser. Maybe you are thinking that "Hey I am using the keyboard mostly and already good at using shortcut keys also while I'm developing". OK, right! But when you are debugging codes of your own or when you are browsing some others codes, you might find that mouse gesture is pretty neat. Anyway, it isn't that bad to have one more free tool, and let's save traveling between keyboard and mouse!

Background

As is always, I referred to many good articles available in CodeProject. I specially thank Nick Hodapp and his great article Undocumented Visual C++. Actually, I developed this add-in a few months ago when I started learning how to write code using template libraries (STL, ATL and WTL) and tried to avoid using MFC (I am still in learning phase of those template libraries as a novice programmer). I think I only used MFC for mouse gesture configuration dialog, and you can find many experimental codes I wrote (to learn myself). But don't get scared since the add-in is working pretty well.

If you've seen my other articles in CodeProject, you will notice that I've used my own handy subclassing class called CMessageHook again. Originally, I wrote that class when I was working on this add-in for the first time. It is a very small and pure-Win32 class for instance subclassing using assembly thunking technique that is identical to that of ATL.

The most important consideration I took during implementation was to make it as smaller and as lighter as possible. I've tried using a couple of generic mouse gesture software, and most of them are using global mouse hook which take up lots of resources and slow down system quite noticeably, which blemish its usefulness. I think that is an inevitable trade-off for being generic. Some people might be able to endure such a trade-off but I couldn't. I put every effort that I could think of to make it lighter.

Accidentally, I found the existence of a resource called "COMMAND_TABLE" from modules loaded by MSDEV.exe, I dug into it and happened to find that most of the commands that can be executed in Dev-Studio IDE are stored there as a form of a list of an undocumented structure. I spent quite a lot of time. (I still remember it was really really fun to play around with it, and perhaps I won't do something similar in the next time, never!)

typedef struct tagVSCommand
{
    WORD wID;              // command id
    BYTE bUnidentified[4]; // couldn't identify what these four bytes represent for
    WORD wImageIndex; // index in image list
    WORD cbExtra;     // byte count of extra information (string ID, menu text
                      // long description and short description)
    LPBYTE pbExtra;   // starting address of extra information
    LPTSTR pszID;     // address of string ID in extra information
    LPTSTR pszMenu;   // address of menu text in extra information
    LPTSTR pszLongDescription;  // address of long description in extra information
    LPTSTR pszShortDescription; // address of short description in extra information
} VSCOMMAND, *LPVSCOMMAND;

I couldn't figure out what the four bytes after command ID represented. I suspected that the category of the command must be related with those bytes but couldn't solve it. And that is why, the category combobox in mouse gesture configuration dialog doesn't work unfortunately. I included the MS Excel file of those commands just in case you might be interested in solving the puzzle.

For gesture recognition, I used a simple algorithm. I saw an article on a very intelligent algorithm for gesture recognition in CodeProject. Since I didn't have a good brain to digest such a complex issue like neural network and so on, I simply wrote code to detect just four directions, UP, DOWN, LEFT and RIGHT, by calculating the distance, angle and time of mouse movement. Honestly, I would rather say that I copied and pasted the basic code from Donut RAPT implementation. It is simple and it doesn't require much computing power (thus very light) but you will be very surprised how well it recognizes gesture by applying some good adjustment factors.

Executing the command is simple as well, and there are two ways of doing it. The first is using IApplication::ExecuteCommand(LPCWSTR stringID), and the other is posting WM_COMMAND to MS Dev-Studio with appropriate menu command ID as if a menu item has been selected. The former is COM way and the latter is Win32 way, and as far as I experimented, it does almost the same effect. I chose to use COM way to support the 3rd party add-ins and macros. I will explain this later.

I included my own configuration file of my favorite mouse gesture patterns that I have been using for quite a while. But you don't need to stick to my configuration, and you can create your own. It is fully customizable!

Usage

It is easy to use an add-in. First, install the add-in. See the following link for installation instructions if you don't already know how to install add-ins. After installing it, the add-in is now ready for use and you will be able to see toolbar icons as shown below:

The first icon is to enable or to disable the mouse gesture add-in, and the second icon will bring up the mouse gesture configuration as shown below:

Category:

  • Command category filter. Unfortunately, it doesn't work at the moment.

Commands:

  • All the commands available.

Gesture Control:

  • To add (append) a gesture pattern. (Up, Left, Right, Down, WheelUp, WheelDown, LeftButton, RightButton and BackSpace.)

Registered Mouse Gesture:

  • Your choice of gesture patterns and corresponding commands registered, it is fully customizable.

Command Description:

  • What the currently selected command does. This is a long description from "COMMAND_TABLE" resource.

Enable MG (Mouse Gesture):

  • To enable or to disable mouse gesture add-in.

MGA (Mouse Gesture Activation) Button:

  • Which button to use for activation (initiation) of Mouse Gesture input mode. You can choose either right mouse button (default) or middle mouse button.

Enable MG Suspending Key:

  • To enable or to disable Mouse Gesture suspending mode. While the selected button is being held down, the Mouse Gesture will be in temporary suspending (disabled) mode.

MG Suspending Key:

  • Which key to use for suspending Mouse Gesture. Default: Shift key.

Enable Double-Click Scrolling:

  • To enable or to disable scrolling window when user double-clicks on certain portion of the screen.

Page Up/Down

  • Page Up window when user double-clicks on any point in the upper half of the screen, and Page Down window when user double-clicks on any point in the lower half of the screen.

Page Top/Bottom & Page Top/Bottom Recognition Area (%)

  • To move up to the top of the page when user double-clicks on any point within the specified percentage of the upper portion of the screen, and to move down to the bottom of the page when user double-clicks on any point within the specified percentage of the lower portion of the screen.

Gesture Recognition Sensitivity:

  • The minimum number of pixels that the mouse cursor should be moved in the same direction in order to recognize it as one gesture pattern input.

Same Direction Detecting Rate:

  • The minimum time in milliseconds in which the mouse cursor should be paused at a point to input the next same direction pattern. Take for an example, you can move the mouse cursor to Right direction, then pause at least more than 300 ms (or whatever time you specified), then keep continuing to move to Right direction again in order to input two consecutive "R R" gesture pattern.

Preemptive MGA Button Always:

  • To eat up the WM_XXXBUTTONDOWN message and not let the default handler even see it. Default: false.

Show Gesture Recognition on Status Bar

  • To show or not to show the recognized pattern and its corresponding menu command on status bar.

Identify Target Window Class

  • To identify the target window on which mouse cursor is and the mouse gesture activation button is being clicked. (Extra info for debugging purpose.)

Autoloading on Startup

  • To determine if the add-in will be automatically loaded every time MS Dev-Studio starts.

Default Gestures (Refer to included "Gesture.ini" file)

*) Assuming that the MGA button is the mouse right button.

Glyphs
Glyph Key/Button/Move
1 Mouse left button
2 Mouse middle button
U Move mouse to upward
L Move mouse to left
R Move mouse to right
D Move mouse to downward
f Rotate mouse wheel forward (upward)
b Rotate mouse wheel backward (downward)
Default Gestures
patterns note command string command ID description
1 - WBDefaultAction 35085 WizardBar Default
1 1 - DebugRunToCursor 34612 Run to Cursor
1 1 1 - BuildExecute 33807 Execute Program
2 - FileClose 57602 Close
2 2 - MenuFileMRU 33051 -
2 2 2 - MenuWorkspaceMRU 33052 -
D - WindowScrollToBottom 34904 Scroll to Bottom
D L - ProjectSettings 34048 Settings
D L U - BrowseGoToDefinition 34593 Go To Definition
D R Draw a L shape WindowList 33488 Window List
D R U - ClassWizard 35075 ClassWizard
D R U D R U Draw a W shape ClassWizard 35075 ClassWizard
D U - DebugStepOver 34614 Step Over
D U D - DebugStepInto 34613 Step Into
D U D U
D U D U D
D U D U D U
U D U D
U D U D U
U D U D U D
Shake the mouse up and down BuildClean 34072 Clean
L - WindowPreviousMDI 32784 Previous Window
L D R D L Draw a S shape FileSave 57603 Save
L D R D L U - FileSaveAll 34560 Save All
L R - DebugToggleBreakpoint 34636 Insert/Remove Breakpoint
L R D - DebugRemoveAllBreakpoints 34625 Remove All Breakpoints
L R L R
L R L R L
L R L R L R
R L R L
R L R L R
R L R L R L
Shake the mouse left and right Build 33803 Build
L R L R L R L
L R L R L R L R
L R L R L R L R L
R L R L R L R
R L R L R L R L
R L R L R L R L R
Shake the mouse left and right a lot BuildRebuildAll 33806 Rebuild All
L R U - DebugBreakpoints 34617 Breakpoints
R - WindowNextMDI 32783 Next Window
R L - WBDefaultAction 35085 WizardBar Default
U - WindowScrollToTop 34903 Scroll to Top
U D - DebugStepOut 34615 Step Out
U D U - DebugShowNextStatement 34643 Show Next Statement
U L - WBOpenInclude 43015 Open Include File...
U R - Find 34561 Find
U R D - FindInFiles 34562 Find in Files
U R D L Draw a rectangle or a circle (CW) BuildBatch 33808 Batch Build
b - WBGoToNext 35087 Go To Next Function
f - WBGoToPrevious 35088 Go To Previous Function

How to use 3rd party Add-ins and Macros

Some MS Dev-Studio add-ins include "COMMAND_TABLE" resource itself (i.e., MS Visual SourceSafe and Compuware DevPartner Studio), but the most don't. If an add-in includes "COMMAND_TABLE" resource, you will see those commands provided by the add-in in the "Command" ListBox of Mouse Gesture Configuration Dialog. For those who don't have "COMMAND_TABLE" resource, you can still assign those commands to any gesture pattern you like to draw. It is a little bit more complicated but not difficult. First, you should save your configurations as file, then open it using text editor. You will see the line of gesture configuration similar to below. The number "49" in the second line is the number of gestures that you registered.

[Mouse Gestures]
49
000=1 \WBDefaultAction\35085\WizardBar Default\0
001=1 1 \DebugRunToCursor\34612\Run to Cursor\0
...
047=b \WBGoToNext\35087\Go To Next Function\0
048=f \WBGoToPrevious\35088\Go To Previous Function\0

Even though they don't have "COMMAND_TABLE" resource, commands that are provided by those add-ins or macros will be listed in Commands ListBox under Tools/Customize/Keyboard menu of MS Dev-Studio. You can use Category filter to see only commands of add-ins or macros. For example, if you install CodeWiz add-in, you can see some commands that begin with DepaXXX.

To add DepaFuncUp command (this command works way better than the built-in WBGoToPrevious command) and to assign "U U " pattern to this command, you can change or add a line to the file as shown below:

[Mouse Gestures]
50
000=1 \WBDefaultAction\35085\WizardBar Default\0
001=1 1 \DebugRunToCursor\34612\Run to Cursor\0
...
047=b \WBGoToNext\35087\Go To Next Function\0
048=f \WBGoToPrevious\35088\Go To Previous Function\0
049=U U \DepaFuncUp\65534\Steps Up\0

You can see that I used some big random number as command ID, but actually it can be any number since the add-in doesn't use the command ID but command string to execute the command as I commented earlier. And you don't need to worry about sorting issue either, since it will be re-sorted when the add-in saves the file internally. Once you save the file in text editor, you can load new configuration into add-in in order to use the newly assigned DevFuncUp gesture command.

Implementation Notes

If you can't use context menu because you set the right mouse button as Mouse Gesture Activation button, you will be upset. I think it is a very bad idea altering the default behavior of the application. So it became of great importance for me how to handle WM_RBUTTONDOWN message to activate mouse gesture mode. After many different implementations and experiments, I came up with a little tricky but nice solution that I will show below:

When it receives WM_RBUTTONDOWN, it posts another WM_RBUTTONDOWN message to self, then returns so that the default message handler of the application can handle WM_RBUTTONDOWN. Since it posts a WM_RBUTTONDOWN message to self, we will have chance to receive and to handle WM_RBUTTONDBLCLK message (WM_RBUTTONDOWN message that is posted to self will turn to WM_RBUTTONDBLCLK message) right after the default message handler of the application handled the first WM_RBUTTONDOWN message. If the default handler of the application called ::SetCapture() API when it was handling the first WM_RBUTTONDOWN message, we won't get WM_RBUTTONDBLCLK and as will be no mouse gesture activation. In other words, the mouse gesture activation will have very low priority of operation and will not alter the default behavior of the application as I intended.

If the preemptive mode option is enabled, the mouse gesture activation will have higher priority and eats up WM_RBUTTONDOWN message, therefore the default message handler won't even see it.

Since WM_RBUTTONDBLCLK or WM_MBUTTONDBLCLK are very rarely used messages in most common applications, I believe that this is a well reasonable approach to activate mouse gesture without altering the default behavior of the application.

The second issue was how I can install the MouseTracer (subclassing object derived from CMessageHook) to the target window to monitor the mouse activities. There were many options I could choose, and I narrowed down them to three candidates as shown below:

  • Install Mouse Tracers to all the existing windows. Then start to monitor window creation and destruction to add and to remove the MouseTracers by installing window hook (WH_CALLWNDPROCRET).
  • Create a static Mouse Tracer, then start to monitor mouse activities by installing window mouse hook (WH_MOUSE), and attach the Mouse Tracer to the target window accordingly.
  • Start to monitor mouse activities by installing window mouse hook (WH_MOUSE), and dynamically create (or destroy) the Mouse Trace, and attach (or detach) it to (or from) the target window accordingly.

I used the last option to implement this add-in but I left the other implementations in the source code for your reference. (Please be aware that the other two implementations were not maintained since I made decision to use the last option. And you will be able to see two more implementations which I am currently using for developing Mouse Gesture IE plug-in. The fifth (last) implementation will be suitable for Multiple Threads SDI applications such as IE, and I did some very neat trick there. Hopefully, I will have chance to share it later.)

And the last issue, in most of the ATL implementations, we can see something similar to the code shown below:

template<class T>
class ATL_NO_VTABLE CMouseTracerManager
{
public:
    virtual BOOL SetupMouseTracer(HINSTANCE hMod = NULL,
                                   DWORD dwThreadID = 0) = 0;
    virtual void RemoveMouseTracer(DWORD dwThreadID = 0) = 0;
};

class CMouseTracerManagerImpl3 :
      public CMouseTracerManager<CMouseTracerManagerImpl3>
{
    typedef CMouseTracerManagerImpl3 thisClass;
    typedef CMouseTracerManager<CMouseTracerManagerImpl3> baseClass;

public:
    BOOL SetupMouseTracer(HINSTANCE hMod = NULL, DWORD dwThreadID = 0);
    void RemoveMouseTracer(DWORD dwThreadID = 0);


// implementations
public:
    CMouseTracerManagerImpl3();
    virtual ~CMouseTracerManagerImpl3();

    ...
private:
    static LRESULT CALLBACK MouseProc(int nCode,
                            WPARAM wParam, LPARAM lParam);
    ...
};

However, in my implementation, I used an alternative way as shown below. I believe that this is called as Strategy Pattern. The two methods are similar in that they both provide a set of common interfaces. But because the latter doesn't rely on the vtable to achieve the common interfaces, I guess it should be smaller and faster than the former. Note that I didn't run any kind of comparison to prove this :P.

template<class TImpl>
class CMouseTracerManager
{
public:
    inline BOOL SetupMouseTracer(HINSTANCE hMod = NULL,
           DWORD dwThreadID = 0)
           { return TImpl::SetupMouseTracer(hMod, dwThreadID); }
    inline void RemoveMouseTracer(DWORD dwThreadID = 0)
           { TImpl::RemoveMouseTracer(dwThreadID); }
};

class CMouseTracerManagerImpl3
{
    friend class CMouseTracerManager<CMouseTracerManagerImpl3>

public:
    BOOL SetupMouseTracer(HINSTANCE hMod = NULL, DWORD dwThreadID = 0);
    void RemoveMouseTracer(DWORD dwThreadID = 0);


// implementations
public:
    CMouseTracerManagerImpl3();
    virtual ~CMouseTracerManagerImpl3();

    ...
private:
    static LRESULT CALLBACK MouseProc(int nCode,
                   WPARAM wParam, LPARAM lParam);
    ...
};

History

Version 0.92b - 11/08/2004

  • Added tips support. Tip will be popped up to show the available mouse gesture commands in the current pattern state whenever the MGA button is being held down at the current position for more than two seconds. (Refer to CTrackingTip class and "MouseTracer.hpp" for details).
  • Added an option to choose mouse middle button for double-click scrolling for those who are using the left double click for marking words. (Thanks to Vision Guy for the idea.)

Version 0.91b - 11/01/2004

  • Command category filter is now working. (Special thanks to Paolo Messina.)
  • Changed complier option in Release build. The "Inline function expansion:" in "Optimization / C/C++ / Project Settings" is changed from "Only__inline" to "Disable*". This eliminated the nasty memory error occurring sometimes (but rarely, about one out of ten times) when the DevStudio is being closed. The solution seems working fine for now but I will investigate this issue further.

Version 0.9b - 10/28/2004

  • Initial release on CodeProject.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

JaeWook Choi
Other
Canada Canada
No Biography provided

Comments and Discussions

 
GeneralDon't see commands from other addins Pinsussatkarasi17-Nov-04 7:57 
GeneralRe: Don't see commands from other addins PinmemberJaeWook Choi19-Nov-04 8:42 
GeneralMouse Gestures for all applications PinmemberTony Leigh16-Nov-04 7:45 

Very good article, you get my 5.
 
You may be interested in StrokeIt which is a nice app that lets you add mouse gestures to any application.
 
http://www.tcbmi.com/strokeit/
GeneralRe: Mouse Gestures for all applications PinmemberJaeWook Choi17-Nov-04 6:57 
GeneralRe: Mouse Gestures for all applications PinmemberRage27-Apr-06 7:09 
GeneralThird party add ins PinsussAnonymous9-Nov-04 6:23 
GeneralRe: Third party add ins PinmemberJaeWook Choi9-Nov-04 8:32 
GeneralMouse Gestures in IE Pinmembermbanks8505-Nov-04 11:37 
GeneralRe: Mouse Gestures in IE PinmemberJaeWook Choi6-Nov-04 17:56 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in PinmemberJaeWook Choi8-Nov-04 5:06 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pinmembermbanks8508-Nov-04 5:56 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in PinmemberJaeWook Choi8-Nov-04 7:04 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pinmembermbanks8508-Nov-04 7:24 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in PinsussAnonymous9-Nov-04 22:33 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pinmembermbanks8508-Nov-04 6:29 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in PinmemberJaeWook Choi8-Nov-04 7:28 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pinmembermbanks8508-Nov-04 7:46 
GeneralEnable Double-Click Scrolling PinmemberVision Guy3-Nov-04 19:33 
GeneralRe: Enable Double-Click Scrolling PinmemberJaeWook Choi4-Nov-04 4:34 
QuestionWhat about VS 2002/03 PinmemberGoodTurn2-Nov-04 7:30 
AnswerRe: What about VS 2002/03 PinmemberJaeWook Choi2-Nov-04 16:32 
GeneralI am beginning to think you are genius, ... PinmemberWREY29-Oct-04 12:39 
GeneralRe: I am beginning to think you are genius, ... PinmemberJaeWook Choi29-Oct-04 13:17 
Generalunidentified bytes PinmemberPaolo Messina29-Oct-04 11:57 
GeneralRe: unidentified bytes PinmemberJaeWook Choi29-Oct-04 13:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 9 Nov 2004
Article Copyright 2004 by JaeWook Choi
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid