Wednesday, July 23, 2014

Easy Right-Click Menus for Any Java Swing Application

Add right mouse click (Select All, Copy, Paste) to an entire Java Swing application in one line of code. Sound too good to be true? Before you load your first screen:

new RightMouseMenuMaker().start();

// load your first frame, window, or swingworker here.
 
Add the above line of code and the following file to your project and you're done.
 
RightMouseMenuMaker.java :

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
import javax.swing.text.TextAction;

/**
 * Library code to add RIGHT-CLICK menus to a Java Swing desktop application.
 * usage:  
new RightMouseMenuMaker().start();
* * @author javajon.blogspot.com */ public class RightMouseMenuMaker implements AWTEventListener, ContainerListener { private boolean enabled = true; /** * register this event listener as a global event listener. */ public void start() { Toolkit.getDefaultToolkit().addAWTEventListener(this, -1L); } /** * * @return the enabled state */ public boolean isEnabled() { return enabled; } /** * enable or disable this event listener. * * @param enabled */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * @see java.awt.event.AWTEventListener * @param event */ @Override public void eventDispatched(AWTEvent event) { if (!this.enabled) { return; } if (event instanceof ContainerEvent) { ContainerEvent ce = (ContainerEvent) event; if (ce.getID() == ContainerEvent.COMPONENT_ADDED) { componentAdded(ce); } } } /** * @see java.awt.event.ContainerListener * @param ce */ @Override public void componentAdded(ContainerEvent ce) { final Component child = ce.getChild(); addRightMouseContextMenus(child); } /** * @see java.awt.event.ContainerListener * @param ce */ @Override public void componentRemoved(ContainerEvent e) { } /** * queue to allow the right mouse menus to be added on a background thread * after the screen has been displayed. on larger screens with numerous * controls, adding the right menu click to each component may take some * time. */ private static final BlockingQueue componentsWaitingForRightMouseMenus = new LinkedBlockingDeque<>(); /** * background thread to add the right mouse menus after the screen has been * displayed. on larger screens with numerous controls, adding the right * menu click to each component may take some time. */ private static final Thread rightMouseMenuMakerThread = new Thread(new ComponentQueue(), "Right Mouse Menu Maker"); /** * start the background thread as soon as the first instance of this class * is created. */ static { rightMouseMenuMakerThread.start(); } /** * adds a right mouse menu to a component and any child components. * @param component */ public static void addRightMouseContextMenus(Component component) { try { componentsWaitingForRightMouseMenus.put(component); } catch (InterruptedException ex) { Logger.getLogger(RightMouseMenuMaker.class.getName()) .log(Level.SEVERE, null, ex); } } /** * adds a right mouse menu to a component and any child components. * @param component */ private static void addRightMouseContextMenusPrivate(Component component) { if (component instanceof JTextComponent) { JTextComponent textComponent = (JTextComponent) component; final JPopupMenu existingContextMenu = textComponent.getComponentPopupMenu(); if (existingContextMenu == null) { final JPopupMenu newContextMenu = getContextMenu(textComponent); textComponent.setComponentPopupMenu(newContextMenu); } } if (component instanceof Container) { Container container = (Container) component; for (Component c : container.getComponents()) { addRightMouseContextMenusPrivate(c); } } } /** * creates a right mouse menu for any JTextComponent. * @return right mouse menu * @see javax.swing.text.JTextComponent * @param textComponent */ public static JPopupMenu getContextMenu( final JTextComponent textComponent) { JMenuItem menuItem; // menu container final JPopupMenu rightClickMenu; rightClickMenu = new JPopupMenu("Edit"); // ----------- final TextAction selectAllAction = new TextAction("Select All") { @Override public void actionPerformed(ActionEvent e) { textComponent.selectAll(); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(selectAllAction); menuItem.setMnemonic(KeyEvent.VK_A); rightClickMenu.add(menuItem); // ----------- rightClickMenu.addSeparator(); // ----------- final TextAction cutAction = new TextAction("Cut") { @Override public void actionPerformed(ActionEvent e) { final String selectedText = textComponent.getSelectedText(); if (selectedText == null || "".equals(selectedText)) { textComponent.selectAll(); } textComponent.cut(); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(cutAction); menuItem.setMnemonic(KeyEvent.VK_T); rightClickMenu.add(menuItem); // ----------- rightClickMenu.addSeparator(); // ----------- final TextAction copyAction = new TextAction("Copy") { @Override public void actionPerformed(ActionEvent e) { final String selectedText = textComponent.getSelectedText(); if (selectedText == null || "".equals(selectedText)) { textComponent.selectAll(); } textComponent.copy(); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(copyAction); menuItem.setMnemonic(KeyEvent.VK_C); rightClickMenu.add(menuItem); // ----------- final TextAction copyAllAction = new TextAction(" Copy All") { @Override public void actionPerformed(ActionEvent e) { textComponent.selectAll(); textComponent.copy(); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(copyAllAction); menuItem.setFont(menuItem.getFont() .deriveFont(menuItem.getFont().getSize2D() * 0.75f)); menuItem.setMnemonic(KeyEvent.VK_O); rightClickMenu.add(menuItem); // ----------- rightClickMenu.addSeparator(); // ----------- final TextAction pasteIntoAction = new TextAction("Paste") { @Override public void actionPerformed(ActionEvent e) { textComponent.paste(); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(pasteIntoAction); menuItem.setMnemonic(KeyEvent.VK_P); rightClickMenu.add(menuItem); // ----------- final TextAction pasteReplaceAllAction = new TextAction(" Replace All") { @Override public void actionPerformed(ActionEvent e) { textComponent.selectAll(); textComponent.paste(); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(pasteReplaceAllAction); menuItem.setFont(menuItem.getFont() .deriveFont(menuItem.getFont().getSize2D() * 0.75f)); menuItem.setMnemonic(KeyEvent.VK_O); rightClickMenu.add(menuItem); // ----------- final TextAction pasteInsertAtStartAction = new TextAction(" Insert at Start") { @Override public void actionPerformed(ActionEvent e) { textComponent.select(0, 0); textComponent.paste(); textComponent.select(0, 0); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(pasteInsertAtStartAction); menuItem.setFont(menuItem.getFont() .deriveFont(menuItem.getFont().getSize2D() * 0.75f)); menuItem.setMnemonic(KeyEvent.VK_B); rightClickMenu.add(menuItem); // ----------- final TextAction pasteAppendAtEndAction = new TextAction(" Append at End") { @Override public void actionPerformed(ActionEvent e) { final String text = textComponent.getText(); if (text == null) { textComponent.select(0, 0); } else { final int length = text.length(); textComponent.select(length, length); } textComponent.paste(); if (getFocusedComponent() != textComponent) { textComponent.grabFocus(); } } }; menuItem = new JMenuItem(pasteAppendAtEndAction); menuItem.setFont(menuItem.getFont() .deriveFont(menuItem.getFont().getSize2D() * 0.75f)); menuItem.setMnemonic(KeyEvent.VK_E); rightClickMenu.add(menuItem); return rightClickMenu; } static class ComponentQueue implements Runnable { public ComponentQueue() { } @Override public void run() { try { while (true) { final ArrayList arrayList = new ArrayList(); // block the thread -- wait until there is a component arrayList.add(componentsWaitingForRightMouseMenus.take()); componentsWaitingForRightMouseMenus.drainTo(arrayList); // short delay -- any more components right away? while (true) { final Component maybe = componentsWaitingForRightMouseMenus .poll(500L, TimeUnit.MILLISECONDS); if (maybe == null) { break; } else { arrayList.add(maybe); componentsWaitingForRightMouseMenus .drainTo(arrayList); } } // marshall the list of components to the UI thread // for further processing SwingUtilities.invokeLater(new Runnable() { @Override public void run() { for (final Component component : arrayList) { addRightMouseContextMenusPrivate(component); } } }); } } catch (InterruptedException ex) { Logger.getLogger(RightMouseMenuMaker.class.getName()) .log(Level.SEVERE, null, ex); } } } }

No comments:

Post a Comment