Click here to Skip to main content
15,878,970 members
Articles / Desktop Programming / MFC
Article

Mouse Gesture Add-in for MS DevStudio 6

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
8 Nov 200414 min read 119.9K   1.7K   51   34
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:

Image 1

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:

Image 2

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
GlyphKey/Button/Move
1Mouse left button
2Mouse middle button
UMove mouse to upward
LMove mouse to left
RMove mouse to right
DMove mouse to downward
fRotate mouse wheel forward (upward)
bRotate mouse wheel backward (downward)
Default Gestures
patternsnotecommand stringcommand IDdescription
1 -WBDefaultAction35085WizardBar Default
1 1 -DebugRunToCursor34612Run to Cursor
1 1 1 -BuildExecute33807Execute Program
2 -FileClose57602Close
2 2 -MenuFileMRU33051-
2 2 2 -MenuWorkspaceMRU33052-
D -WindowScrollToBottom34904Scroll to Bottom
D L -ProjectSettings34048Settings
D L U -BrowseGoToDefinition34593Go To Definition
D R Draw a L shapeWindowList33488Window List
D R U -ClassWizard35075ClassWizard
D R U D R U Draw a W shapeClassWizard35075ClassWizard
D U -DebugStepOver34614Step Over
D U D -DebugStepInto34613Step 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 downBuildClean34072Clean
L -WindowPreviousMDI32784Previous Window
L D R D L Draw a S shapeFileSave57603Save
L D R D L U -FileSaveAll34560Save All
L R -DebugToggleBreakpoint34636Insert/Remove Breakpoint
L R D -DebugRemoveAllBreakpoints34625Remove 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 rightBuild33803Build
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 lotBuildRebuildAll33806Rebuild All
L R U -DebugBreakpoints34617Breakpoints
R -WindowNextMDI32783Next Window
R L -WBDefaultAction35085WizardBar Default
U -WindowScrollToTop34903Scroll to Top
U D -DebugStepOut34615Step Out
U D U -DebugShowNextStatement34643Show Next Statement
U L -WBOpenInclude43015Open Include File...
U R -Find34561Find
U R D -FindInFiles34562Find in Files
U R D L Draw a rectangle or a circle (CW)BuildBatch33808Batch Build
b -WBGoToNext35087Go To Next Function
f -WBGoToPrevious35088Go 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:

Image 3

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.)

Image 4

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


Written By
Other
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralDon't see commands from other addins Pin
seanbzd17-Nov-04 6:57
seanbzd17-Nov-04 6:57 
GeneralRe: Don't see commands from other addins Pin
JaeWook Choi19-Nov-04 7:42
JaeWook Choi19-Nov-04 7:42 
GeneralMouse Gestures for all applications Pin
Tony Leigh16-Nov-04 6:45
Tony Leigh16-Nov-04 6:45 
GeneralRe: Mouse Gestures for all applications Pin
JaeWook Choi17-Nov-04 5:57
JaeWook Choi17-Nov-04 5:57 
GeneralRe: Mouse Gestures for all applications Pin
Rage27-Apr-06 6:09
professionalRage27-Apr-06 6:09 
GeneralThird party add ins Pin
Anonymous9-Nov-04 5:23
Anonymous9-Nov-04 5:23 
GeneralRe: Third party add ins Pin
JaeWook Choi9-Nov-04 7:32
JaeWook Choi9-Nov-04 7:32 
GeneralMouse Gestures in IE Pin
mbanks8505-Nov-04 10:37
mbanks8505-Nov-04 10:37 
GeneralRe: Mouse Gestures in IE Pin
JaeWook Choi6-Nov-04 16:56
JaeWook Choi6-Nov-04 16:56 
Hi Mike,

try the code below.

spimgCT->Exec(&CGID_MSHTML, wID, OLECMDEXECOPT_DODEFAULT, NULL, NULL);

Regards,
JaeWook Choi

GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
JaeWook Choi8-Nov-04 4:06
JaeWook Choi8-Nov-04 4:06 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
mbanks8508-Nov-04 4:56
mbanks8508-Nov-04 4:56 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
JaeWook Choi8-Nov-04 6:04
JaeWook Choi8-Nov-04 6:04 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
mbanks8508-Nov-04 6:24
mbanks8508-Nov-04 6:24 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
Anonymous9-Nov-04 21:33
Anonymous9-Nov-04 21:33 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
mbanks8508-Nov-04 5:29
mbanks8508-Nov-04 5:29 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
JaeWook Choi8-Nov-04 6:28
JaeWook Choi8-Nov-04 6:28 
GeneralRe: Executing Right click menu (context menu) command directly in IE add-in Pin
mbanks8508-Nov-04 6:46
mbanks8508-Nov-04 6:46 
GeneralEnable Double-Click Scrolling Pin
Vision Guy3-Nov-04 18:33
Vision Guy3-Nov-04 18:33 
GeneralRe: Enable Double-Click Scrolling Pin
JaeWook Choi4-Nov-04 3:34
JaeWook Choi4-Nov-04 3:34 
QuestionWhat about VS 2002/03 Pin
GoodTurn2-Nov-04 6:30
GoodTurn2-Nov-04 6:30 
AnswerRe: What about VS 2002/03 Pin
JaeWook Choi2-Nov-04 15:32
JaeWook Choi2-Nov-04 15:32 
GeneralI am beginning to think you are genius, ... Pin
WREY29-Oct-04 11:39
WREY29-Oct-04 11:39 
GeneralRe: I am beginning to think you are genius, ... Pin
JaeWook Choi29-Oct-04 12:17
JaeWook Choi29-Oct-04 12:17 
Generalunidentified bytes Pin
Paolo Messina29-Oct-04 10:57
professionalPaolo Messina29-Oct-04 10:57 
GeneralRe: unidentified bytes Pin
JaeWook Choi29-Oct-04 12:06
JaeWook Choi29-Oct-04 12:06 

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

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