Mouse actions, cut and paste & popup menus in Swing

chris (2004-08-15 14:04:04)
3665 views
0 replies
Today I'll show a simple example of how you might go about detecting a right mouse click on a swing component and then pop up a menu. Swing provides oodles of functionality for handling mouse events and in this case we're going to use the Fields which MouseEvent inherits from java.awt.event.InputEvent. The full MouseEvent api docs are here, but we're only going to use a small chunk of the api on offer.

To make this task a little more difficult we will produce a Contaner with two JTextFIeld components - each of which can respond to a right click event resulting in the clipboard menu pop up. The challenge here to know which field has received the mouse event, and hence which will be the target of any cut copy and paste actions. As I will demonstrate, the trick here is to use the setInvoker() method of the JPopupMenu class.

If you want to know what the end result is going to look like, scroll down to where I have entered the full source, paste it into your IDE/editor screen, save, compile and run. You should see a simple JFrame with two JTextField components within.

So let's take this from the top. First of all we need to pull in the packages required to build the GUI.
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import javax.swing.*;

The java.awt.datatransfer package contains classes which are required if we are going to read from or write to the clipboard. It also includes classes to support transfer of data to and from the system clipboard. The package is actually more clever than just that and also includes a DataFlavor class which allows us to determine the type of data on the clipboard via a MIME type based system.
    JTextField textField1 = new JTextField(20);
    JTextField textField2 = new JTextField(20);
    final JPopupMenu cutpasteMenu = new JPopupMenu();
    JMenuItem cutMenuItem = new JMenuItem("Cut");
    JMenuItem copyMenuItem = new JMenuItem("Copy");
    JMenuItem pasteMenuItem = new JMenuItem("Paste");

The code above shows the next part of the setting up process. Two text fields are defined and JPopupMenu is instantiated (which in this case is called cutpasteMenu). Next 3 menu options are defined, 'cut', 'copy' and 'paste' using the JMenuItem class. Once the constructor is called, the first thing we do is bind event listeners to each of these menu items. In this application we have defined the top-level class to be an actionListener, so we can keep binding it to our components using the 'this' keyword like so:
        cutMenuItem.addActionListener(this);
        copyMenuItem.addActionListener(this);
        pasteMenuItem.addActionListener(this);
        
        cutpasteMenu.add(cutMenuItem);
        cutpasteMenu.add(copyMenuItem);
        cutpasteMenu.add(pasteMenuItem);

Note that all we have done here is add an actionlistener to each of the meny items. the following 3 lines add the menu itens to the JPopupMenu (cutpasteMenu) which was created earlier. However, there is nothing yet setup to detect the mouseclicks which are ultimately going to trigger the popup menu in the first place. For this to happen, another type of listener known as a Mouse Listener is required. A mouse listener is created for each of the text fields and added inline as follows:
        textField1.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                switch(e.getModifiers()) {
                    case InputEvent.BUTTON3_MASK: {
                        cutpasteMenu.show(e.getComponent(), e.getX(), e.getY());
                        System.out.println(e.getSource().getClass().getName());
                        break;
                    }
                }
            }
        });
        
        textField2.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                switch(e.getModifiers()) {
                    case InputEvent.BUTTON3_MASK: {
                        cutpasteMenu.show(e.getComponent(), e.getX(), e.getY());
                        cutpasteMenu.setInvoker(e.getComponent());
                        break;
                    }
                }
            }
        });

So each of these two blocks of code does an identical job - but one adds a mouse listener to textField1 and the other to textField2. I guess you could conceivably create a single mouse listener object and add that to each of the text fields instead. Note that the event detected here is a MouseEvent, (fully documented here: http://java.sun.com/j2se/1.4.2/docs/api/java/awt/event/MouseEvent.html). This is a feature-rich class for handling mouse activity. It is a subclass of InputEvent, and so inherits the getModifiers() method, which returns the modifier bitmask for whichever event has occurred. The code block above runs a switch test on the bitmask and currently just accomodates a single right click (BUTTON3_MASK). Extra case statements could be added if we had a need for extra mouse functionality. In this case, just a right click is handled and it simply calls the show() method on our menu object, whilst also calling the setInvoker() method. This last stage is very useful, because it means that we now have a record of exactly which component has triggered the menu popup. From this, we then know which component to target once we have handled the menu selection - ie we know which of the two text fieds will receive the copy/cut/paste action.

This is where the final bit of intelligence comes in:
    public void actionPerformed(ActionEvent evt) {
        Object source = evt.getSource();
        if (source == cutMenuItem) {
            JTextField jte = (JTextField)cutpasteMenu.getInvoker();
            jte.cut();
        }
        if (source == copyMenuItem) {
            JTextField jte = (JTextField)cutpasteMenu.getInvoker();
            jte.copy();
        }
        if (source == pasteMenuItem) {
            JTextField jte = (JTextField)cutpasteMenu.getInvoker();
            jte.paste();
        }
    }

In these few lines, the application statisfies the contract it has with the java compiler to implement the methods of the extended class. In this case we have extended ActionListener (as shown in the class declaration: public class ClipboardDemo extends JFrame implements ActionListener {). This means that our class now 'has' all the features of an action listener, and as such it must have an actionPerformed method. In the actionPerformed method shown above, the cutpasteMenu object is called every time and it's getInvoker() method is accessed. This is then passed back as a reference to the JTextField which was originally clicked on. From there it's easy to just call the cut(), copy() and paste() methods and the job is completed.

This is all that's required to understand a simple clipboard menu in Swing. If you would like to try the application first, just cut and paste all the sources below the dotted line and give it a go. Don't forget to subscribe to this channel too. You will then receive an email when (and only when) a new post arrives in this java section. But until that time, that's all from me.

Christo

full sources from this tutorial:
----------------------------------------------

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import javax.swing.*;

public class ClipboardDemo extends JFrame implements ActionListener {    
    JPanel mainPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
    JLabel descrLabel = new JLabel("JPopupMenu demo");
  
    JTextField textField1 = new JTextField(20);
    JTextField textField2 = new JTextField(20);
    final JPopupMenu cutpasteMenu = new JPopupMenu();
    JMenuItem cutMenuItem = new JMenuItem("Cut");
    JMenuItem copyMenuItem = new JMenuItem("Copy");
    JMenuItem pasteMenuItem = new JMenuItem("Paste");
    
    public ClipboardDemo() {  
        cutMenuItem.addActionListener(this);
        copyMenuItem.addActionListener(this);
        pasteMenuItem.addActionListener(this);
        
        cutpasteMenu.add(cutMenuItem);
        cutpasteMenu.add(copyMenuItem);
        cutpasteMenu.add(pasteMenuItem);
        
        textField1.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                switch(e.getModifiers()) {
                    case InputEvent.BUTTON3_MASK: {
                        cutpasteMenu.show(e.getComponent(), e.getX(), e.getY());
                        System.out.println(e.getSource().getClass().getName());
                        break;
                    }
                }
            }
        });
        
        textField2.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                switch(e.getModifiers()) {
                    case InputEvent.BUTTON3_MASK: {
                        cutpasteMenu.show(e.getComponent(), e.getX(), e.getY());
                        cutpasteMenu.setInvoker(e.getComponent());
                        break;
                    }
                }
            }
        });
        
        mainPane.add(descrLabel);
        mainPane.add(textField1);
        mainPane.add(textField2);
        mainPane.setPreferredSize(new Dimension(250,75));
        
        WindowListener wl = new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        };
        setContentPane(mainPane);
        addWindowListener(wl);
        pack();
        setResizable(false);
        setVisible(true);
    }
    
    public void actionPerformed(ActionEvent evt) {
        Object source = evt.getSource();
        if (source == cutMenuItem) {
            JTextField jte = (JTextField)cutpasteMenu.getInvoker();
            jte.cut();
        }
        if (source == copyMenuItem) {
            JTextField jte = (JTextField)cutpasteMenu.getInvoker();
            jte.copy();
        }
        if (source == pasteMenuItem) {
            JTextField jte = (JTextField)cutpasteMenu.getInvoker();
            jte.paste();
        }
    }
    
    public static void main(String[] args) {
        ClipboardDemo cd = new ClipboardDemo();
    }
}
comment