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