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