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.Collections; 014import java.util.Comparator; 015import java.util.List; 016import java.util.Objects; 017 018import javax.swing.Action; 019import javax.swing.JMenu; 020import javax.swing.JMenuItem; 021import javax.swing.JPopupMenu; 022import javax.swing.JSeparator; 023 024import org.openstreetmap.josm.Main; 025import org.openstreetmap.josm.tools.AlphanumComparator; 026 027public class TaggingPresetMenu extends TaggingPreset { 028 public JMenu menu; // set by TaggingPresets 029 030 private static class PresetTextComparator implements Comparator<JMenuItem>, Serializable { 031 @Override 032 public int compare(JMenuItem o1, JMenuItem o2) { 033 if (Main.main.menu.presetSearchAction.equals(o1.getAction())) 034 return -1; 035 else if (Main.main.menu.presetSearchAction.equals(o2.getAction())) 036 return 1; 037 else 038 return AlphanumComparator.getInstance().compare(o1.getText(), o2.getText()); 039 } 040 } 041 042 /** 043 * {@code TaggingPresetMenu} are considered equivalent if (and only if) their {@link #getRawName()} match. 044 */ 045 @Override 046 public boolean equals(Object o) { 047 if (this == o) return true; 048 if (o == null || getClass() != o.getClass()) return false; 049 TaggingPresetMenu that = (TaggingPresetMenu) o; 050 return Objects.equals(getRawName(), that.getRawName()); 051 } 052 053 @Override 054 public int hashCode() { 055 return Objects.hash(getRawName()); 056 } 057 058 @Override 059 public void setDisplayName() { 060 putValue(Action.NAME, getName()); 061 /** Tooltips should be shown for the toolbar buttons, but not in the menu. */ 062 putValue(OPTIONAL_TOOLTIP_TEXT, group != null ? 063 tr("Preset group {1} / {0}", getLocaleName(), group.getName()) : 064 tr("Preset group {0}", getLocaleName())); 065 putValue("toolbar", "tagginggroup_" + getRawName()); 066 } 067 068 private static Component copyMenuComponent(Component menuComponent) { 069 if (menuComponent instanceof JMenu) { 070 JMenu menu = (JMenu) menuComponent; 071 JMenu result = new JMenu(menu.getAction()); 072 for (Component item:menu.getMenuComponents()) { 073 result.add(copyMenuComponent(item)); 074 } 075 result.setText(menu.getText()); 076 return result; 077 } else if (menuComponent instanceof JMenuItem) { 078 JMenuItem menuItem = (JMenuItem) menuComponent; 079 JMenuItem result = new JMenuItem(menuItem.getAction()); 080 result.setText(menuItem.getText()); 081 return result; 082 } else if (menuComponent instanceof JSeparator) { 083 return new JSeparator(); 084 } else { 085 return menuComponent; 086 } 087 } 088 089 @Override 090 public void actionPerformed(ActionEvent e) { 091 Object s = e.getSource(); 092 if (menu != null && s instanceof Component) { 093 JPopupMenu pm = new JPopupMenu(getName()); 094 for (Component c : menu.getMenuComponents()) { 095 pm.add(copyMenuComponent(c)); 096 } 097 PointerInfo pointerInfo = MouseInfo.getPointerInfo(); 098 if (pointerInfo != null) { 099 Point p = pointerInfo.getLocation(); 100 pm.show(Main.parent, p.x-Main.parent.getX(), p.y-Main.parent.getY()); 101 } 102 } 103 } 104 105 /** 106 * Sorts the menu items using the translated item text 107 */ 108 public void sortMenu() { 109 TaggingPresetMenu.sortMenu(this.menu); 110 } 111 112 /** 113 * Sorts the menu items using the translated item text 114 * @param menu menu to sort 115 */ 116 public static void sortMenu(JMenu menu) { 117 Component[] items = menu.getMenuComponents(); 118 PresetTextComparator comp = new PresetTextComparator(); 119 List<JMenuItem> sortarray = new ArrayList<>(); 120 int lastSeparator = 0; 121 for (int i = 0; i < items.length; i++) { 122 Object item = items[i]; 123 if (item instanceof JMenu) { 124 sortMenu((JMenu) item); 125 } 126 if (item instanceof JMenuItem) { 127 sortarray.add((JMenuItem) item); 128 if (i == items.length-1) { 129 handleMenuItem(menu, comp, sortarray, lastSeparator); 130 sortarray = new ArrayList<>(); 131 lastSeparator = 0; 132 } 133 } else if (item instanceof JSeparator) { 134 handleMenuItem(menu, comp, sortarray, lastSeparator); 135 sortarray = new ArrayList<>(); 136 lastSeparator = i; 137 } 138 } 139 } 140 141 private static void handleMenuItem(JMenu menu, PresetTextComparator comp, List<JMenuItem> sortarray, int lastSeparator) { 142 Collections.sort(sortarray, comp); 143 int pos = 0; 144 for (JMenuItem menuItem : sortarray) { 145 int oldPos; 146 if (lastSeparator == 0) { 147 oldPos = pos; 148 } else { 149 oldPos = pos+lastSeparator+1; 150 } 151 menu.add(menuItem, oldPos); 152 pos++; 153 } 154 } 155}