001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.widgets; 003 004import java.awt.Component; 005import java.awt.Point; 006import java.awt.event.FocusEvent; 007import java.awt.event.FocusListener; 008import java.awt.event.MouseAdapter; 009import java.awt.event.MouseEvent; 010 011import javax.swing.JList; 012import javax.swing.JPopupMenu; 013import javax.swing.JTable; 014import javax.swing.JTree; 015import javax.swing.SwingUtilities; 016import javax.swing.tree.TreePath; 017 018/** 019 * Utility class that helps to display popup menus on mouse events. 020 * @since 2688 021 */ 022public class PopupMenuLauncher extends MouseAdapter { 023 protected JPopupMenu menu; 024 private final boolean checkEnabled; 025 026 /** 027 * Creates a new {@link PopupMenuLauncher} with no defined menu. 028 * It is then needed to override the {@link #launch} method. 029 * @see #launch(MouseEvent) 030 */ 031 public PopupMenuLauncher() { 032 this(null); 033 } 034 035 /** 036 * Creates a new {@link PopupMenuLauncher} with the given menu. 037 * @param menu The popup menu to display 038 */ 039 public PopupMenuLauncher(JPopupMenu menu) { 040 this(menu, false); 041 } 042 043 /** 044 * Creates a new {@link PopupMenuLauncher} with the given menu. 045 * @param menu The popup menu to display 046 * @param checkEnabled if {@code true}, the popup menu will only be displayed if the component triggering the mouse event is enabled 047 * @since 5886 048 */ 049 public PopupMenuLauncher(JPopupMenu menu, boolean checkEnabled) { 050 this.menu = menu; 051 this.checkEnabled = checkEnabled; 052 } 053 054 @Override 055 public void mousePressed(MouseEvent e) { 056 processEvent(e); 057 } 058 059 @Override 060 public void mouseReleased(MouseEvent e) { 061 processEvent(e); 062 } 063 064 private void processEvent(MouseEvent e) { 065 if (e.isPopupTrigger() && (!checkEnabled || e.getComponent().isEnabled())) { 066 launch(e); 067 } 068 } 069 070 /** 071 * Launches the popup menu according to the given mouse event. 072 * This method needs to be overriden if the default constructor has been called. 073 * @param evt A mouse event 074 */ 075 public void launch(final MouseEvent evt) { 076 if (evt != null) { 077 final Component component = evt.getComponent(); 078 if (checkSelection(component, evt.getPoint())) { 079 checkFocusAndShowMenu(component, evt); 080 } 081 } 082 } 083 084 protected boolean checkSelection(Component component, Point p) { 085 if (component instanceof JList) { 086 return checkListSelection((JList<?>) component, p) > -1; 087 } else if (component instanceof JTable) { 088 return checkTableSelection((JTable) component, p) > -1; 089 } else if (component instanceof JTree) { 090 return checkTreeSelection((JTree) component, p) != null; 091 } 092 return true; 093 } 094 095 protected void checkFocusAndShowMenu(final Component component, final MouseEvent evt) { 096 if (component != null && component.isFocusable() && !component.hasFocus() && component.requestFocusInWindow()) { 097 component.addFocusListener(new FocusListener() { 098 @Override 099 public void focusLost(FocusEvent e) {} 100 101 @Override 102 public void focusGained(FocusEvent e) { 103 showMenu(evt); 104 component.removeFocusListener(this); 105 } 106 }); 107 } else { 108 showMenu(evt); 109 } 110 } 111 112 protected void showMenu(MouseEvent evt) { 113 if (menu != null && evt != null) { 114 menu.show(evt.getComponent(), evt.getX(), evt.getY()); 115 } 116 } 117 118 protected int checkListSelection(JList<?> list, Point p) { 119 int idx = list.locationToIndex(p); 120 if (idx >= 0 && idx < list.getModel().getSize() && list.getSelectedIndices().length < 2 && !list.isSelectedIndex(idx)) { 121 list.setSelectedIndex(idx); 122 } 123 return idx; 124 } 125 126 protected int checkTableSelection(JTable table, Point p) { 127 int row = table.rowAtPoint(p); 128 if (row >= 0 && row < table.getRowCount() && table.getSelectedRowCount() < 2 && table.getSelectedRow() != row) { 129 table.getSelectionModel().setSelectionInterval(row, row); 130 } 131 return row; 132 } 133 134 protected TreePath checkTreeSelection(JTree tree, Point p) { 135 TreePath path = tree.getPathForLocation(p.x, p.y); 136 if (path != null && tree.getSelectionCount() < 2 && !tree.isPathSelected(path)) { 137 tree.setSelectionPath(path); 138 } 139 return path; 140 } 141 142 protected static boolean isDoubleClick(MouseEvent e) { 143 return e != null && SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2; 144 } 145 146 /** 147 * @return the popup menu if defined, {@code null} otherwise. 148 * @since 5884 149 */ 150 public final JPopupMenu getMenu() { 151 return menu; 152 } 153}