2

I've discovered that when running a Java program in Mac OS X, text field objects (e.g. JField) support cut, copy, and paste operations using the system clipboard if you use the key commands without you having to do an extra coding. For instance, I can create a JField, type in some text, select it, and then use "Command-C" to copy it, then paste it in another application. The same works for pasting text copied from other applications.

I really like that Java does this automatically, but I notice that the Edit menu doesn't appear automatically with the cut, copy, and paste menu items. Is there any way to automatically add these menu items for accessing the system clipboard when text is selected? If not, what's the best way to implement them so that it behaves like any other application. At this point, I'm only interested in copy and pasting text.

Also, I know that the system clipboard is platform specific. Does this automatic system clipboard functionality for text field objects occur on other platforms?

EDIT: I was actually wanting to know about adding this to the menu bar, but there wound up being such a great answer to the next question I would have had that I decided that I'd select it as the right answer and rename the question.

Thunderforge
  • 19,637
  • 18
  • 83
  • 130

3 Answers3

2

Unfortunately, Swing does not support pop-up context menus. You have to roll your own. Fortunately, this isn't too hard. Perhaps the cleanest approach is to install your own event queue, as described here. That article suggests the following implementation:

// @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);
    } 
}

You then use this with:

public static void main(String[] args){ 
    Toolkit.getDefaultToolkit().getSystemEventQueue().push(new MyEventQueue()); 

    .....
}

With that one line of code, you get a pop-up menu on every text component in your application.

The actions classes aren't too complicated. For instance, here's the implementation of the paste action, which shows interaction with the system clipboard:

// @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; 
    } 
}

See the article for code for the other action implementations.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • +1, Your approach is cleaner because it automatically handles the menu for *all* text components, whereas mine needs the handler added individually. – FThompson Nov 27 '12 at 20:06
  • I like this approach, but I see in the comments of the article you linked to that there was at least one person who is very vocal against this method because changing the event queue could potentially crash the whole application if things got changed in future versions of Java. But if the article was written in 2005 and the code still works...does this mean that it's safe to use? – Thunderforge Nov 27 '12 at 20:10
  • 1
    @Thunderforge - There's apparently a bug in Java 1.7 concerning installing a new event queue. See [this response](http://stackoverflow.com/a/8965448/535871) for details and what to do about it. Other than that, I have every reason to believe that this approach is safe. The concern in the comments to the article seemed to suggest that drag-and-drop might cause problems. You can easily enough write a little app to test this issue. – Ted Hopp Nov 27 '12 at 20:19
  • Great, thanks! I was actually wondering about how to add cut, copy, and paste to the menu bar, but this solves the next question I would have had. I imagine it's just a matter of modifying the code to get it to behave the same way with the menu bar. – Thunderforge Nov 27 '12 at 20:22
  • I'm confused by "Swing does not support pop-up context menus". Isn't that what [JComponent#setComponentPopupMenu(JPopupMenu)](https://docs.oracle.com/javase/1.5.0/docs/api/javax/swing/JComponent.html#setComponentPopupMenu(javax.swing.JPopupMenu)) is for? – Ky - Aug 07 '15 at 13:49
  • @BenC.R.Leggiero - That works for one component at a time. The goal here (if I recall correctly--this post was almost three years ago) was to have a context menu enabled on all text components throughout an application. – Ted Hopp Aug 07 '15 at 15:54
  • @TedHopp I solved this by only using my extension of `JTextField`, which calls its own `setComponentPopupMenu` on init, using a global, static `JPopupMenu`. – Ky - Aug 07 '15 at 16:03
  • 1
    @BenC.R.Leggiero - That makes total sense, provided you have total control over all `JTextField`-derived components in your app and are free to have them all derive from your `JTextField` extension. That's not always the case, particularly if you are using third-party libraries or code-generation tools. As I said, though, I'm kind of vague on what inspired this answer at the time. – Ted Hopp Aug 07 '15 at 16:19
1

You will need to create a custom popup menu for this, which can be done utilizing MouseListener (or MouseAdapter) and a JPopupMenu. My approach would be to create a custom mouse listener which you can add to any text component.

public class ClipboardMenuHandler extends MouseAdapter {

    private final JTextComponent textComponent;

    public ClipboardMenuHandler(JTextComponent textComponent) {
        this.textComponent = textComponent;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.isPopupTrigger()) 
            ClipboardMenu menu = new ClipboardMenu();
            menu.show(textComponent, e.getX(), e.getY());
        }
    }

    private class ClipboardMenu extends JPopupMenu {

        public ClipboardMenu() {
            // add copy/cut/paste actions; refer to Ted's answer for PasteAction
        }
    }
}

Then, you just add a ClipboardMenuHandler as a listener to your text component.

JTextField field = new JTextField();
ClipboardMenuHandler menuHandler = new ClipboardMenuHandler(field);
field.addMouseListener(menuHandler);
FThompson
  • 28,352
  • 13
  • 60
  • 93
0

This has worked for me:

public class MyTextField extends JTextField {
    public static final MyPopupMenu POPUP_MENU = new MyPopupMenu();

    public MyTextField() {
        this.setComponentPopupMenu(POPUP_MENU);
    }
}

Now, I just use MyTextField everywhere I would otherwise use a JTextField.

Ky -
  • 30,724
  • 51
  • 192
  • 308