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