Wednesday, October 10, 2007

A Truth About setAccelerator method in JMenuItem - Technical Article in Java

[Disclaimer: This is going to be my first (and may be the last) technical blog. If you do not understand the subject (especially the words JMenuItem or Java), please click here to go to my previous blog.]

There is a special method called setAccelerator() in JMenuItem which is used to invoke the action listener method of the menu item without navigating through the menus. Simply pressing the key combination would give an effect of clicking the menu item. The most famous accelerators are (ctrl + c, ctrl + v, ctrl + z, ctrl + s, ctrl + a, etc.) which will be available for almost all the applications.

Suppose, if we develop our own application which has a GUI (Graphical User Interface), we will definitely have a menu bar and various menu items placed in different hierarchy of menus. Setting mnemonics is one way to let the user access the menu items without using mouse. But using mnemonics will require the user to navigate through various menus and there could be menus inside menus inside menus, etc. So, for power users, it is best we have this accelerators set.

To do it, we have to use the method setAccelerator() for the particular JMenuItem object. This method takes a KeyStroke object as an argument. This KeyStroke object defines which key stroke combination should be used to activate the menu item. The beauty about this KeyStroke class is that it has various ways to achieve a particular key stroke combination. It has many overloaded methods named getKeyStroke which takes various combination of arguments.

Suppose, if you want to set an accelerator of ctrl+x, if we set the accelerator using menuItem.setAccelerator(KeyStroke.getKeyStroke('x', InputEvent.CTRL_MASK)); it wont work!!! The character argument is considered as an int and it wont work. So, if we use menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK), then it will work fine. But what will we have to do, if we have to set the accelerator based on some runtime input or if we really want to use a character instead of KeyEvent object.

We have one more alternative, and we will be tempted to use KeyStorke.getKeyStroke(new Character('x'), InputEvent.CTRL_MASK). If we use this, we will see that, the accelerator we set, is mentioned in the menu item. But if we use the key combination, it wont work. Because, the key stoke object thus we got is decoded in to "ctrl typed x" but for accelerator it should be "ctrl pressed x". Fortunately, the KeyStroke class one more method which takes a String as an argument. So we will be tempted to use KeyStroke.getKeyStroke("ctrl pressed x"). And the truth is that, even this wont work!!!

The reason is that, we have to give a capital letter for the character and not small letter. So, if you give KeyStroke.getKeyStroke ("ctrl pressed X") will only the accelerator work and there is no documentation which says this, that we have to use capital letter in the key stroke object if we wish to set it as accelerator for a menu item.

So, if you wish to develop an application and then set the accelerator for menu items, and if that does not work, make sure the KeyStroke object that is set has the character "upper cased" and it has "pressed" instead of "typed". If you have had similar kind of technical difficulties and found a way to solve it, you can very well share it here.

P.S.: I am really sorry if this blog has been very confusing and not understandable. [I am editing this blog after some months because, I find that this is the blog which is most viewed, most of the viewers from Google searching about this method. If you find this blog is useful (or useless), please comment about it and let me know how can I improve this or my future blogs]