Forum Controls
Spotlight Features

The Rich Engineering Heritage Behind Dependency Injection

Andrew McVeigh takes us on a tour of the rich heritage behind dependency injection, what it represents, and tells us why its here to stay.

NetBeans 6: Matisse Updates

NetBeans 6 delivers great updates to the Matisse GUI builder. Spend a few minutes with Roman Strobl and get an expert briefing on what's new and what has changed.

Introduction to Groovy Part 3

In this, the third and final installation of Andres' Introduction to Groovy series, you learn about how Groovy handles variable numbers of arguments, named parameters, currying, and more about Groovy operators. Including, some new operators.

Easier Custom Components with Swing Fuse

Swing Fuse (actually just Fuse), is a framework designed to make it easier to create your own custom desktop components. In this article, Daniel Spiewak shows you how to get started and provides sample source code you can download.

Benchmark Analysis: Guice vs Spring

Willam Louth shows how he uses JXInsight Probes to investigate probable performance issues with code bases that he is not familiar with. He also highlights possible pitfalls in creating a benchmark, as well as in the analysis of results.
Replies: 24 - Pages: 2   [ 1 2 | Next ]
Threads: [ Previous | Next ]
  Click to reply to this thread Reply

Swing: Context Menu for TextComponents

At 2:41 PM on Jul 28, 2005, Santhosh Kumar T DeveloperZone Top 100 wrote:

In Windows, it is very common to show context menu (with cut, copy, paste etc) on textfields, textareas. I don't know about other operating systems. So any windows user using a swing application expects a popup menu on right click of text components.

When I am in doubt whether this application is swing or not, what I do is find any textfield and right click. if no popup appears then I conclude it as swing application.

How do I provide context menu for text components ?
Solution1: Create a subclass for each text component (JTextField, JPasswordField etc) and implement the logic inside these subclasses.
Solution2: Create a mouse listener which does the work of showing popup and add this to all text components of your applications.

Each of the above solutions have their own drawbacks:
  • Changing the complete swing application to use our subclass is a tedious job
  • If we use mouse listener, then it is also tedious job to ensure that this mouse listener is registered to all text components of you application
  • There is no way to make this work for text components that are not created by the user code such as JTextField in JOptionPane.showInputDialog(...) or JFileChooser dialog.
  • If the applications tries to show its own popup menu for a text component we will run into problems


I found a very clean solution for this problem: We install our implmentation of EventQueue.
// @author Santhosh Kumar T - santhosh@in.fiorano.com 
public class MyEventQueue extends EventQueue{ 
    protected void dispatchEvent(AWTEvent event){ 
        super.dispatchEvent(event); 
 
        // interested only in mouseevents 
        if(!(event instanceof MouseEvent)) 
            return; 
 
        MouseEvent me = (MouseEvent)event; 
 
        // interested only in popuptriggers 
        if(!me.isPopupTrigger()) 
            return; 
 
        // me.getComponent(...) retunrs the heavy weight component on which event occured 
        Component comp = SwingUtilities.getDeepestComponentAt(me.getComponent(), me.getX(), me.getY()); 
 
        // interested only in textcomponents 
        if(!(comp instanceof JTextComponent)) 
            return; 
 
        // no popup shown by user code 
        if(MenuSelectionManager.defaultManager().getSelectedPath().length>0) 
            return; 
 
        // create popup menu and show 
        JTextComponent tc = (JTextComponent)comp; 
        JPopupMenu menu = new JPopupMenu(); 
        menu.add(new CutAction(tc)); 
        menu.add(new CopyAction(tc)); 
        menu.add(new PasteAction(tc)); 
        menu.add(new DeleteAction(tc)); 
        menu.addSeparator(); 
        menu.add(new SelectAllAction(tc)); 
 
        Point pt = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), tc);
        menu.show(tc, pt.x, pt.y);
    } 
} 

The above class is self-explanatory with comments.

The implementation of actions is here:
// @author Santhosh Kumar T - santhosh@in.fiorano.com 
class CutAction extends AbstractAction{ 
    JTextComponent comp; 
 
    public CutAction(JTextComponent comp){ 
        super("Cut"); 
        this.comp = comp; 
    } 
 
    public void actionPerformed(ActionEvent e){ 
        comp.cut(); 
    } 
 
    public boolean isEnabled(){ 
        return comp.isEditable() 
                && comp.isEnabled() 
                && comp.getSelectedText()!=null; 
    } 
} 
 
// @author Santhosh Kumar T - santhosh@in.fiorano.com 
class PasteAction extends AbstractAction{ 
    JTextComponent comp; 
 
    public PasteAction(JTextComponent comp){ 
        super("Paste"); 
        this.comp = comp; 
    } 
 
    public void actionPerformed(ActionEvent e){ 
        comp.paste(); 
    } 
 
    public boolean isEnabled(){ 
        if (comp.isEditable() && comp.isEnabled()){ 
            Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(this); 
            return contents.isDataFlavorSupported(DataFlavor.stringFlavor); 
        }else 
            return false; 
    } 
} 
 
// @author Santhosh Kumar T - santhosh@in.fiorano.com 
class DeleteAction extends AbstractAction{ 
    JTextComponent comp; 
 
    public DeleteAction(JTextComponent comp){ 
        super("Delete"); 
        this.comp = comp; 
    } 
 
    public void actionPerformed(ActionEvent e){ 
        comp.replaceSelection(null); 
    } 
 
    public boolean isEnabled(){ 
        return comp.isEditable() 
                && comp.isEnabled() 
                && comp.getSelectedText()!=null; 
    } 
} 
 
// @author Santhosh Kumar T - santhosh@in.fiorano.com 
class CopyAction extends AbstractAction{ 
    JTextComponent comp; 
 
    public CopyAction(JTextComponent comp){ 
        super("Copy"); 
        this.comp = comp; 
    } 
 
    public void actionPerformed(ActionEvent e){ 
        comp.copy(); 
    } 
 
    public boolean isEnabled(){ 
        return comp.isEnabled() 
                && comp.getSelectedText()!=null; 
    } 
} 
 
// @author Santhosh Kumar T - santhosh@in.fiorano.com 
class SelectAllAction extends AbstractAction{ 
    JTextComponent comp; 
 
    public SelectAllAction(JTextComponent comp){ 
        super("Select All"); 
        this.comp = comp; 
    } 
 
    public void actionPerformed(ActionEvent e){ 
        comp.selectAll(); 
    } 
 
    public boolean isEnabled(){ 
        return comp.isEnabled() 
                && comp.getText().length()>0; 
    } 
} 


How to use it:
    public static void main(String[] args){ 
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(new MyEventQueue()); 
        
        .....
    } 

Just a single line in the beginning of your application's main method. So simple.

I created a webstart demo to show it in action:



There are two textfields in this demo. Second textfield shows a popupmenu in one of its mouse listener.



When click the button [JOptionPane], it shows a input message dialog which again contains a textfield, which is not created the demo.



Thus, this approach is very simple to use, and works perfectly, even if the application has its own popup menu on mouse click.



Your comments are appreciated.

Found a problem when using with JGoodies LNF. and the code now includes the fix;
1 . At 3:07 PM on Jul 28, 2005, Tim Boudreau DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

Yechhh!!! This may work, it is clever, but it is ABSOLUTELY NOT the way you want to do this, and it is very dangerous. Leave creating a new event queue to the toolkit, debugging code, etc. What you are doing when you replace the system event queue is you are modifying the central mechanism by which all of AWT and Swing function - if there's an unnoticed bug in your replacement EventQueue that gets into production, it is going to cause horrendous and nearly impossible to diagnose problems. Replacing the event dispatching mechanism of the toolkit in order to display a popup menu for text boxes is truly going after a mosquito with a nuclear warhead. Do not do this!

If you want to have a popup on text fields, either,

  1. Do it the normal, sane way, by listening for mouse clicks, or

  2. Write a simple UI delegate and install it in UIManager. The cross-platform aspects are trivial - set the border and you're pretty much done.



Two other problems with the EventQueue approach: 1. Any other code in the app can also push a new event queue, in which case the popup menus mysteriously stop working. Good luck debugging who is doing it and when. 2. What if some part of the app actually does do its own popup menu handling? Who wins?

I repeat, do not do this.
Tim Boudreau
NetBeans.org
Evangelist/Senior Staff Engineer, Sun Microsystems
2 . At 3:18 PM on Jul 28, 2005, Santhosh Kumar T DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

As you said we must be aware that we have replaced the default eventqueue. Ofcourse, you should use it with care and should not allow user run into into the situations as you said.

we can use a system property which can enable this instead of hardcoding:

if(Boolean.getBoolean("swing.text.contextmenu"))
  Toolkit.getDefaultToolkit().getSystemEventQueue().push(new MyEventQueue());


Thus it will be possible for user to turn off this, if he/she found any problems.

regarding second question:
the application's popup menu wins:

see the following line in MyEventQueue

// no popup shown by user code 
if(MenuSelectionManager.defaultManager().getSelectedPath().length>0) 
            return; 


the webstart demo has a textfield which has its popup menu. This is to show that it won't disturb the popup menus created by your application;
Santhosh Kumar T - santhosh@fiorano.com
MySwing Project - https://myswing.dev.java.net/
Cool Swing Stuff - https://myswing.dev.java.net/MyBlog/MySwingTree.html
Sr. Software Eng - http://www.fiorano.com
3 . At 3:48 PM on Jul 28, 2005, Andrea Aime DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

> Yechhh!!! This may work, it is clever, but it is
> ABSOLUTELY NOT the way you want to do this, and it is
> very dangerous.

What about the Marinacci's solution at
http://weblogs.java.net/blog/joshy/archive/2003/10/swing_hack_4_th.html

Seems safer, but has to be done for every window. Well, in my case, I only have two main frames around, the rest are dialog with a common base class, so it's quite easy to add, I just have to modify the base :-)
4 . At 3:57 PM on Jul 28, 2005, Santhosh Kumar T DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

I think the solution you mentioned will create problems, if any of the components in you window has drag-n-drop. But I am not sure.

As you are already using this in your application, try enabling dnd for some textcomponents and see whether dnd works fine or not.

Also any popup menus shown by your application will no longer be shown with the solution you mentioned.
Santhosh Kumar T - santhosh@fiorano.com
MySwing Project - https://myswing.dev.java.net/
Cool Swing Stuff - https://myswing.dev.java.net/MyBlog/MySwingTree.html
Sr. Software Eng - http://www.fiorano.com
5 . At 4:05 PM on Jul 28, 2005, Santhosh Kumar T DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

> Seems safer, but has to be done for every window

I don't think, it is safer than my approach. any bug in that class will no longer redispatch the events to the components and gui becoms non reactive.

> Well, in my case, I only have two main frames around

what about the dialogs shown by JOptionPane, JFileChooser and JColorChooser by your application if any
Santhosh Kumar T - santhosh@fiorano.com
MySwing Project - https://myswing.dev.java.net/
Cool Swing Stuff - https://myswing.dev.java.net/MyBlog/MySwingTree.html
Sr. Software Eng - http://www.fiorano.com
6 . At 4:35 PM on Jul 28, 2005, Andrea Aime DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

> > Seems safer, but has to be done for every window
>
> I don't think, it is safer than my approach. any bug
> in that class will no longer redispatch the events to
> the components and gui becoms non reactive.

Yes, you're right.
It's not the first glass pane I'm using either. I put a glass pane on a panel when doing long operations in a separate thread in order to make it non responsive to events.
And no, I don't have any popup at the moment, mine was only a question.

> > Well, in my case, I only have two main frames
> around
>
> what about the dialogs shown by JOptionPane,
> JFileChooser and JColorChooser by your application if
> any

I guess I can live without popups in them.... I only use the JFileChooser. Anyway, I'll try both solutions.
Thank you for sharing your experience and insight (both of you :-) )
7 . At 4:40 PM on Jul 28, 2005, Andrea Aime DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

Actually this class could be extended to handle a chain of event handler classes. Every event is presented to each member of the chain, until someone says it has consumed it.
This way you can handle special popups for tables, lists, combos and so on. I use a similar approach for exceptions: I perform every dangerous action in runnables (because they usually are long running as well) and catch every exception, a chain of exception handlers handles them performing the most appropriate action (log first, then usually build a message depending on the nature of the exception).

This way I only have to handle exceptions that are manageable in a way other than simply logging and informing the user of what happened.
8 . At 5:50 PM on Jul 28, 2005, Tim Boudreau DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

Toolkit.getDefaultToolkit().addAWTEventListener() will do this without swapping event queues. It's a marginally less dangerous hack, but it's still a pretty horrible hack of that sort that you just don't do unless there is absolutely no other way to do it. It's horribly easy to make assumptions about event ordering and other things in this kind of code, and you'll have no idea you're doing it until one day two years later you find your application is completely broken on one or another platform. Seriously, don't do either of these things unless you have absolutely no other choice.

Also, expect that neither the event queue approach nor the AWTEventListener approach are going to work for an app that doesn't run with full security permissions.

I'm sorry if I sound harsh about this. There are ways to do what you're describing that are quite safe, and are using Swing as designed. The most straightforward of them, if you want to do something globally like this, is to simply write a UI delegate, and put a key and value for it in UIManager, and it will automatically be used for every component of that type throughout the UI. Code that works with the toolkit, rather than against the grain of the toolkit, is much more likely to keep working over time. Why do some evil workaround when there's a straightforward way to do the same thing that's built into the toolkit?

I'm not saying this because I want to spoil anyone's fun - stuff like this is neat to mess around with. But you don't put it in production code. I'm saying this because I've had to maintain code and fix bugs in a codebase where, early on, people tried to do overly clever things exactly like this, working against the toolkit rather than with it. They thought this sort of thing was fun and clever too. The price of that cleverness: The app being completely broken on some platforms, having to release emergency patches to make it work on a new JDK because some subtle non-API thing like the order methods were called in was being depended on and nobody realized it, angry users and an unpopular product. I'm not kidding here.
Tim Boudreau
NetBeans.org
Evangelist/Senior Staff Engineer, Sun Microsystems
9 . At 2:33 AM on Jul 29, 2005, Santhosh Kumar T DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

First I tried to use AWTEventListener approach. but it didn't work.

because AWTEventListeners are informed before the mouseevent gets dispatched to the component. So I have no way to find whether any textcomponent has its own context menu.

I better suggest to get this fixed by swing team rather than doing something like this at our end. They could easily add this to the default UI classes.
Santhosh Kumar T - santhosh@fiorano.com
MySwing Project - https://myswing.dev.java.net/
Cool Swing Stuff - https://myswing.dev.java.net/MyBlog/MySwingTree.html
Sr. Software Eng - http://www.fiorano.com
10 . At 5:12 AM on Jul 29, 2005, Patrick Gotthardt DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

Thank you.

I agree with Tim, anyway. However, there's one line in you're code that helped me very much:
if(MenuSelectionManager.defaultManager().getSelectedPath().length>0) 


Very nice, this will help me to implement it in my LookAndFeel (which is - as I guess - what is supposed to do the job, right?).
Good looking Swing: http://pgslookandfeel.dev.java.net
11 . At 6:06 AM on Jul 29, 2005, Sergey Astakhov wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

Did you look at WinLAF project (https://winlaf.dev.java.net/)? They try to fix many inconsistences in Windows LAF, including showing popup menus for text components and scroll bars.
12 . At 6:13 AM on Jul 29, 2005, Santhosh Kumar T DeveloperZone Top 100 wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

I am aware of winlaf project. but it does this only for windows look and feel.

I suggest this to be done BasicLookAndFell classes.
Santhosh Kumar T - santhosh@fiorano.com
MySwing Project - https://myswing.dev.java.net/
Cool Swing Stuff - https://myswing.dev.java.net/MyBlog/MySwingTree.html
Sr. Software Eng - http://www.fiorano.com
13 . At 5:43 AM on Jul 30, 2005, mohsen wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

hi
i think we can use aspect oriented(AspectJ) to do such a thing more easier. i dont know how could we do that but i've done somthing like that.
14 . At 8:33 AM on Aug 3, 2005, Martin Skopp wrote:
  Click to reply to this thread Reply

Re: Swing: Context Menu for TextComponents

While listing the disadvantages of the solutions 1-2, you mentioned:

> There is no way to make this work for text components that
> are not created by the user code such as JTextField in
> JOptionPane.showInputDialog(...) or JFileChooser dialog.


Well, there is a hack(tm) to achive this anyway, with the backdraw that it works only after the component got the focus the first time (so direct "right" click on a uninitialized component does not work):

Install a global "permanentFocusOwner" Listener into the current KeyboardFocusManager which ensures the Mouse listener is installed for every component. Here's a raw draft how to do it:


// installInFocusManager() {
KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener("permanentFocusOwner", new ActionEnabler());



/* class ActionEnabler enables Cut/Copy/Paste plus
* (with a CaretListener) which is not listed here
* but also ensures the a mouse listener for popup menu
*/
class ActionEnabler implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() instanceof TextComponent) {
JTextComponent comp = (JTextComponent) evt.getNewValue();
MouseListener[] ml = comp.getMouseListeners();
boolean hasRightClickListener = false;
for (int i = 0; i if (ml [ i ] == getMenuPopupListener()) {
hasRightClickListener = true;
break;
}
}
if (!hasRightClickListener) {
comp.addMouseListener(getMenuPopupListener());
}
}
}
}

thread.rss_message