001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.tagging.presets; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.MouseInfo; 008import java.awt.Point; 009import java.awt.PointerInfo; 010import java.awt.event.ActionEvent; 011import java.io.Serializable; 012import java.util.ArrayList; 013import java.util.Comparator; 014import java.util.List; 015import java.util.Objects; 016 017import javax.swing.Action; 018import javax.swing.JMenu; 019import javax.swing.JMenuItem; 020import javax.swing.JPopupMenu; 021import javax.swing.JSeparator; 022 023import org.openstreetmap.josm.gui.MainApplication; 024import org.openstreetmap.josm.gui.MainFrame; 025import org.openstreetmap.josm.tools.AlphanumComparator; 026import org.openstreetmap.josm.tools.Logging; 027 028/** 029 * Menu that groups several presets from one topic. 030 * <p> 031 * Used, to create the nested directory structure in the preset main menu entry. 032 */ 033public class TaggingPresetMenu extends TaggingPreset { 034 public JMenu menu; // set by TaggingPresets 035 036 private static class PresetTextComparator implements Comparator<JMenuItem>, Serializable { 037 private static final long serialVersionUID = 1L; 038 @Override 039 public int compare(JMenuItem o1, JMenuItem o2) { 040 if (MainApplication.getMenu().presetSearchAction.equals(o1.getAction())) 041 return -1; 042 else if (MainApplication.getMenu().presetSearchAction.equals(o2.getAction())) 043 return 1; 044 else 045 return AlphanumComparator.getInstance().compare(o1.getText(), o2.getText()); 046 } 047 } 048 049 /** 050 * {@code TaggingPresetMenu} are considered equivalent if (and only if) their {@link #getRawName()} match. 051 */ 052 @Override 053 public boolean equals(Object o) { 054 if (this == o) return true; 055 if (o == null || getClass() != o.getClass()) return false; 056 TaggingPresetMenu that = (TaggingPresetMenu) o; 057 return Objects.equals(getRawName(), that.getRawName()); 058 } 059 060 @Override 061 public int hashCode() { 062 return Objects.hash(getRawName()); 063 } 064 065 @Override 066 public void setDisplayName() { 067 putValue(Action.NAME, getName()); 068 /** Tooltips should be shown for the toolbar buttons, but not in the menu. */ 069 putValue(OPTIONAL_TOOLTIP_TEXT, group != null ? 070 tr("Preset group {1} / {0}", getLocaleName(), group.getName()) : 071 tr("Preset group {0}", getLocaleName())); 072 putValue("toolbar", "tagginggroup_" + getRawName()); 073 } 074 075 private static Component copyMenuComponent(Component menuComponent) { 076 if (menuComponent instanceof JMenu) { 077 JMenu menu = (JMenu) menuComponent; 078 JMenu result = new JMenu(menu.getAction()); 079 for (Component item:menu.getMenuComponents()) { 080 result.add(copyMenuComponent(item)); 081 } 082 result.setText(menu.getText()); 083 return result; 084 } else if (menuComponent instanceof JMenuItem) { 085 JMenuItem menuItem = (JMenuItem) menuComponent; 086 JMenuItem result = new JMenuItem(menuItem.getAction()); 087 result.setText(menuItem.getText()); 088 return result; 089 } else if (menuComponent instanceof JSeparator) { 090 return new JSeparator(); 091 } else { 092 return menuComponent; 093 } 094 } 095 096 @Override 097 public void actionPerformed(ActionEvent e) { 098 Object s = e.getSource(); 099 if (menu != null && s instanceof Component) { 100 JPopupMenu pm = new JPopupMenu(getName()); 101 for (Component c : menu.getMenuComponents()) { 102 pm.add(copyMenuComponent(c)); 103 } 104 try { 105 PointerInfo pointerInfo = MouseInfo.getPointerInfo(); 106 if (pointerInfo != null) { 107 Point p = pointerInfo.getLocation(); 108 MainFrame parent = MainApplication.getMainFrame(); 109 if (parent.isShowing()) { 110 pm.show(parent, p.x-parent.getX(), p.y-parent.getY()); 111 } 112 } 113 } catch (SecurityException ex) { 114 Logging.log(Logging.LEVEL_ERROR, "Unable to get mouse pointer info", ex); 115 } 116 } 117 } 118 119 /** 120 * Sorts the menu items using the translated item text 121 */ 122 public void sortMenu() { 123 TaggingPresetMenu.sortMenu(this.menu); 124 } 125 126 /** 127 * Sorts the menu items using the translated item text 128 * @param menu menu to sort 129 */ 130 public static void sortMenu(JMenu menu) { 131 Component[] items = menu.getMenuComponents(); 132 PresetTextComparator comp = new PresetTextComparator(); 133 List<JMenuItem> sortarray = new ArrayList<>(); 134 int lastSeparator = 0; 135 for (int i = 0; i < items.length; i++) { 136 Object item = items[i]; 137 if (item instanceof JMenu) { 138 sortMenu((JMenu) item); 139 } 140 if (item instanceof JMenuItem) { 141 sortarray.add((JMenuItem) item); 142 if (i == items.length-1) { 143 handleMenuItem(menu, comp, sortarray, lastSeparator); 144 sortarray = new ArrayList<>(); 145 lastSeparator = 0; 146 } 147 } else if (item instanceof JSeparator) { 148 handleMenuItem(menu, comp, sortarray, lastSeparator); 149 sortarray = new ArrayList<>(); 150 lastSeparator = i; 151 } 152 } 153 } 154 155 private static void handleMenuItem(JMenu menu, PresetTextComparator comp, List<JMenuItem> sortarray, int lastSeparator) { 156 sortarray.sort(comp); 157 int pos = 0; 158 for (JMenuItem menuItem : sortarray) { 159 int oldPos; 160 if (lastSeparator == 0) { 161 oldPos = pos; 162 } else { 163 oldPos = pos+lastSeparator+1; 164 } 165 menu.add(menuItem, oldPos); 166 pos++; 167 } 168 } 169}