source: subversion/applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/OhePlugin.java

Last change on this file was 34535, checked in by donvip, 8 months ago

update to JOSM 14153

  • Property svn:executable set to *
File size: 16.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.plugins.ohe;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Component;
8import java.awt.Font;
9import java.awt.GridBagLayout;
10import java.awt.event.ActionEvent;
11import java.awt.event.KeyEvent;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.Locale;
15import java.util.Map;
16import java.util.Map.Entry;
17import java.util.TreeMap;
18import java.util.Vector;
19import java.util.regex.Matcher;
20import java.util.regex.Pattern;
21
22import javax.swing.AbstractAction;
23import javax.swing.ButtonGroup;
24import javax.swing.JCheckBox;
25import javax.swing.JComboBox;
26import javax.swing.JDialog;
27import javax.swing.JLabel;
28import javax.swing.JOptionPane;
29import javax.swing.JPanel;
30import javax.swing.JRadioButton;
31import javax.swing.JScrollPane;
32import javax.swing.JTable;
33import javax.swing.ListSelectionModel;
34import javax.swing.table.DefaultTableCellRenderer;
35import javax.swing.table.DefaultTableModel;
36
37import org.openstreetmap.josm.actions.JosmAction;
38import org.openstreetmap.josm.command.ChangePropertyCommand;
39import org.openstreetmap.josm.command.Command;
40import org.openstreetmap.josm.command.SequenceCommand;
41import org.openstreetmap.josm.data.UndoRedoHandler;
42import org.openstreetmap.josm.data.osm.OsmPrimitive;
43import org.openstreetmap.josm.gui.MainApplication;
44import org.openstreetmap.josm.gui.MainMenu;
45import org.openstreetmap.josm.gui.layer.OsmDataLayer;
46import org.openstreetmap.josm.plugins.Plugin;
47import org.openstreetmap.josm.plugins.PluginInformation;
48import org.openstreetmap.josm.plugins.ohe.gui.OheDialogPanel;
49import org.openstreetmap.josm.spi.preferences.Config;
50import org.openstreetmap.josm.tools.GBC;
51import org.openstreetmap.josm.tools.Shortcut;
52
53/**
54 * Opening hours editor plugin.
55 */
56public class OhePlugin extends Plugin {
57   
58    /**
59     * Strings for choosing which key of an object with given tags should be
60     * edited, the order is referencing the preference of the keys, String[] ->
61     * {key, value, key-to-edit} key and value can contain regular expressions
62     */
63    private final String[][] TAG_EDIT_STRINGS = new String[][] {
64            {"opening_hours", ".*", "opening_hours"},
65            {"collection_times", ".*", "collection_times"},
66            {"collection_times:local", ".*", "collection_times:local"},
67            {"shop", ".*", "opening_hours"},
68            {"amenity", "post_box", "collection_times"},
69            {"amenity", "recycling", "collection_times"},
70            {"amenity", ".*", "opening_hours"},
71            {"lit", ".*", "lit"},
72            {"highway", ".*", "lit"} };
73
74    /**
75     * Will be invoked by JOSM to bootstrap the plugin
76     *
77     * @param info information about the plugin and its local installation
78     */
79    public OhePlugin(PluginInformation info) {
80        super(info);
81        MainMenu.add(MainApplication.getMenu().dataMenu, new OheMenuAction(), false, 0);
82    }
83
84    /**
85     * this Action is used for calling the OpeningsHourEditor, the selected
86     * objects in the active datalayer are edited
87     */
88    class OheMenuAction extends JosmAction {
89        private static final long serialVersionUID = 1456257438391417756L;
90
91        OheMenuAction() {
92            super(tr("Edit opening hours"), "opening_hours.png",
93                    tr("Edit time-tag of selected element in a graphical interface"), Shortcut.registerShortcut(
94                            "tools:opening_hourseditor", tr("Tool: {0}", tr("Edit opening hours")), KeyEvent.VK_O,
95                            Shortcut.ALT_CTRL_SHIFT), true);
96        }
97
98        @Override
99        protected void updateEnabledState() {
100            if (getLayerManager().getEditDataSet() == null) {
101                // if there is no current dataset, then the action is disabled
102                setEnabled(false);
103            } else {
104                updateEnabledState(getLayerManager().getEditDataSet().getSelected());
105            }
106        }
107
108        @Override
109        protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
110            // only enable the action if something is selected
111            setEnabled(selection != null && !selection.isEmpty());
112        }
113
114        @SuppressWarnings("unchecked")
115        @Override
116        public void actionPerformed(ActionEvent evt) {
117            // fetch active Layer
118            OsmDataLayer osmlayer = getLayerManager().getEditLayer();
119            if (osmlayer == null)
120                return;
121            Collection<OsmPrimitive> selection = osmlayer.data.getSelected();
122
123            // handling of multiple objects and their tags
124            // copied from
125            // org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog[rev4079][line802]
126            Map<String, Integer> keyCount = new HashMap<>();
127            Map<String, Map<String, Integer>> valueCount = new TreeMap<>();
128            for (OsmPrimitive osm : selection) {
129                for (String key : osm.keySet()) {
130                    String value = osm.get(key);
131                    keyCount.put(key, keyCount.containsKey(key) ? keyCount.get(key) + 1 : 1);
132                    if (valueCount.containsKey(key)) {
133                        Map<String, Integer> v = valueCount.get(key);
134                        v.put(value, v.containsKey(value) ? v.get(value) + 1 : 1);
135                    } else {
136                        TreeMap<String, Integer> v = new TreeMap<>();
137                        v.put(value, 1);
138                        valueCount.put(key, v);
139                    }
140                }
141            }
142
143            DefaultTableModel propertyData = new DefaultTableModel() {
144                @Override
145                public boolean isCellEditable(int row, int column) {
146                    return false;
147                }
148
149                @Override
150                public Class<?> getColumnClass(int columnIndex) {
151                    return String.class;
152                }
153            };
154            propertyData.setColumnIdentifiers(new String[] {tr("Key"), tr("Value")});
155            for (Entry<String, Map<String, Integer>> e : valueCount.entrySet()) {
156                int count = 0;
157                for (Entry<String, Integer> e1 : e.getValue().entrySet()) {
158                    count += e1.getValue();
159                }
160                if (count < selection.size()) {
161                    e.getValue().put("", selection.size() - count);
162                }
163                propertyData.addRow(new Object[] {e.getKey(), e.getValue()});
164            }
165            final JTable propertyTable = new JTable(propertyData);
166            propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
167                @Override
168                public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
169                        boolean hasFocus, int row, int column) {
170                    Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
171                    if (value == null)
172                        return this;
173                    if (c instanceof JLabel) {
174                        String str = null;
175                        if (value instanceof String) {
176                            str = (String) value;
177                        } else if (value instanceof Map<?, ?>) {
178                            Map<?, ?> v = (Map<?, ?>) value;
179                            if (v.size() != 1) {
180                                str = tr("<different>");
181                                c.setFont(c.getFont().deriveFont(Font.ITALIC));
182                            } else {
183                                final Map.Entry<?, ?> entry = v.entrySet().iterator().next();
184                                str = (String) entry.getKey();
185                            }
186                        }
187                        ((JLabel) c).setText(str);
188                    }
189                    return c;
190                }
191            });
192            // end copy
193
194            // showing the tags in a dialog
195            propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
196            JScrollPane sp = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
197                    JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
198            sp.setViewportView(propertyTable);
199
200            final JComboBox<String> newTagField = new JComboBox<>(new String[]{
201                    "opening_hours", "collection_times", "collection_times:local", "service_times", "lit"});
202
203            JRadioButton editButton = new JRadioButton(new AbstractAction(tr("edit existing tag")) {
204                @Override
205                public void actionPerformed(ActionEvent e) {
206                    propertyTable.setEnabled(true);
207                    newTagField.setEnabled(false);
208                }
209            });
210            JRadioButton newButton = new JRadioButton(new AbstractAction(tr("edit new tag")) {
211                @Override
212                public void actionPerformed(ActionEvent e) {
213                    propertyTable.setEnabled(false);
214                    newTagField.setEnabled(true);
215                }
216            });
217            ButtonGroup group = new ButtonGroup();
218            group.add(newButton);
219            group.add(editButton);
220
221            // search through the tags and choose which one should be selected
222            String preSelectedKey = "";
223            searchLoop: for (String[] pattern : TAG_EDIT_STRINGS) {
224                Pattern keyPattern = Pattern.compile(pattern[0]);
225                Pattern valuePattern = Pattern.compile(pattern[1]);
226                for (int i = 0; i < propertyData.getRowCount(); ++i) {
227                    Matcher keyMatcher = keyPattern.matcher((String) propertyData.getValueAt(i, 0));
228                    if (keyMatcher.matches()) {
229                        Object value = propertyData.getValueAt(i, 1);
230                        if (value instanceof String && valuePattern.matcher((String) value).matches()) {
231                            preSelectedKey = pattern[2];
232                            break searchLoop;
233                        } else if (value instanceof Map<?, ?>) {
234                            for (String v : ((Map<String, Integer>) value).keySet()) {
235                                if (valuePattern.matcher(v).matches()) {
236                                    preSelectedKey = pattern[2];
237                                    break searchLoop;
238                                }
239                            }
240                        }
241                    }
242                }
243            }
244            int preSelectedRow = -1;
245            for (int i = 0; i < propertyData.getRowCount(); ++i) {
246                if (preSelectedKey.equals(propertyData.getValueAt(i, 0))) {
247                    preSelectedRow = i;
248                }
249            }
250            if (preSelectedRow != -1) {
251                propertyTable.setEnabled(true);
252                newTagField.setEnabled(false);
253                propertyTable.setRowSelectionInterval(preSelectedRow, preSelectedRow);
254                editButton.setSelected(true);
255            } else {
256                propertyTable.setEnabled(false);
257                newTagField.setEnabled(true);
258                newTagField.setSelectedItem(preSelectedKey);
259                newButton.setSelected(true);
260            }
261
262            // load the preference for the clocksystem (12h/24h)
263            ClockSystem clockSystem = ClockSystem.valueOf(Config.getPref().get("ohe.clocksystem",
264                    ClockSystem.getClockSystem(Locale.getDefault()).toString()));
265
266            JCheckBox useTwelveHourClock = new JCheckBox(tr("Display clock in 12h mode."),
267                    clockSystem == ClockSystem.TWELVE_HOURS);
268
269            JPanel dlgPanel = new JPanel(new GridBagLayout());
270            dlgPanel.add(editButton, GBC.std().anchor(GBC.WEST));
271            dlgPanel.add(sp, GBC.eol().fill(GBC.BOTH));
272            dlgPanel.add(newButton, GBC.std().anchor(GBC.WEST));
273            dlgPanel.add(newTagField, GBC.eol().fill(GBC.HORIZONTAL));
274            dlgPanel.add(useTwelveHourClock, GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
275
276            JOptionPane optionPane = new JOptionPane(dlgPanel, JOptionPane.QUESTION_MESSAGE,
277                    JOptionPane.OK_CANCEL_OPTION);
278            JDialog dlg = optionPane.createDialog(MainApplication.getMainFrame(), tr("Choose key"));
279            dlg.pack();
280            dlg.setResizable(true);
281            dlg.setVisible(true);
282
283            Object answer = optionPane.getValue();
284            String keyToEdit = null;
285            Object valuesToEdit = "";
286            if (answer != null && answer != JOptionPane.UNINITIALIZED_VALUE
287                    && (answer instanceof Integer && (Integer) answer == JOptionPane.OK_OPTION))
288                if (editButton.isSelected() && propertyTable.getSelectedRow() != -1) {
289                    keyToEdit = (String) propertyData.getValueAt(propertyTable.getSelectedRow(), 0);
290                    valuesToEdit = propertyData.getValueAt(propertyTable.getSelectedRow(), 1);
291                } else if (newButton.isSelected()) {
292                    keyToEdit = newTagField.getSelectedItem().toString();
293                }
294            if (keyToEdit == null)
295                return;
296
297            // save the value for the clocksystem (12h/24h)
298            Config.getPref().put("ohe.clocksystem", (useTwelveHourClock.isSelected() ? ClockSystem.TWELVE_HOURS
299                    : ClockSystem.TWENTYFOUR_HOURS).toString());
300
301            OheDialogPanel panel = new OheDialogPanel(OhePlugin.this, keyToEdit, valuesToEdit,
302                    useTwelveHourClock.isSelected() ? ClockSystem.TWELVE_HOURS : ClockSystem.TWENTYFOUR_HOURS);
303
304            optionPane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
305            dlg = optionPane.createDialog(MainApplication.getMainFrame(), tr("Edit"));
306            dlg.setResizable(true);
307            dlg.setVisible(true);
308
309            String[] changedKeyValuePair = null;
310            answer = optionPane.getValue();
311            if (!(answer == null || answer == JOptionPane.UNINITIALIZED_VALUE 
312              || (answer instanceof Integer && (Integer) answer != JOptionPane.OK_OPTION))) {
313                changedKeyValuePair = panel.getChangedKeyValuePair();
314            }
315            if (changedKeyValuePair == null)
316                return;
317            String key = changedKeyValuePair[0].trim();
318            String newkey = changedKeyValuePair[1].trim();
319            String value = changedKeyValuePair[2].trim();
320
321            if (value.equals("")) {
322                value = null; // delete the key
323            }
324            if (newkey.equals("")) {
325                newkey = key;
326                value = null; // delete the key instead
327            }
328            if (key.equals(newkey) && tr("<different>").equals(value))
329                return;
330            if (key.equals(newkey) || value == null) {
331                UndoRedoHandler.getInstance().add(new ChangePropertyCommand(selection, newkey, value));
332            } else {
333                Collection<Command> commands = new Vector<>();
334                commands.add(new ChangePropertyCommand(selection, key, null));
335                if (value.equals(tr("<different>"))) {
336                    HashMap<String, Vector<OsmPrimitive>> map = new HashMap<>();
337                    for (OsmPrimitive osm : selection) {
338                        String val = osm.get(key);
339                        if (val != null) {
340                            if (map.containsKey(val)) {
341                                map.get(val).add(osm);
342                            } else {
343                                Vector<OsmPrimitive> v = new Vector<>();
344                                v.add(osm);
345                                map.put(val, v);
346                            }
347                        }
348                    }
349                    for (Entry<String, Vector<OsmPrimitive>> e : map.entrySet()) {
350                        commands.add(new ChangePropertyCommand(e.getValue(), newkey, e.getKey()));
351                    }
352                } else {
353                    commands.add(new ChangePropertyCommand(selection, newkey, value));
354                }
355                UndoRedoHandler.getInstance().add(new SequenceCommand(trn("Change properties of up to {0} object",
356                        "Change properties of up to {0} objects", selection.size(), selection.size()), commands));
357            }
358        }
359    }
360}
Note: See TracBrowser for help on using the repository browser.