Changeset 5636 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2012-12-27T17:40:48+01:00 (12 years ago)
Author:
akks
Message:

see #8024: Recent tag list int Alt-A: Double-Click=apply and close, Shift-Click=apply and continue
TagEditHelper refactoring: please check that there is no regressions

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelper.java

    r5633 r5636  
    7171 */
    7272 class TagEditHelper {
    73         private final DefaultTableModel propertyData;
    74         private final Map<String, Map<String, Integer>> valueCount;
    75        
    76         private String changedKey;
    77        
    78         private String lastAddKey = null;
    79         private String lastAddValue = null;
    80 
    81         public static final int DEFAULT_LRU_TAGS_NUMBER = 5;
    82         public static final int MAX_LRU_TAGS_NUMBER = 9;
    83        
    84         private String objKey;
    85    
    86         Comparator<AutoCompletionListItem> defaultACItemComparator = new Comparator<AutoCompletionListItem>() {
    87             public int compare(AutoCompletionListItem o1, AutoCompletionListItem o2) {
    88                 return String.CASE_INSENSITIVE_ORDER.compare(o1.getValue(), o2.getValue());
    89             }
    90         };
     73    private final DefaultTableModel propertyData;
     74    private final Map<String, Map<String, Integer>> valueCount;
     75
     76    private String changedKey;
     77
     78    private String lastAddKey = null;
     79    private String lastAddValue = null;
     80
     81    public static final int DEFAULT_LRU_TAGS_NUMBER = 5;
     82    public static final int MAX_LRU_TAGS_NUMBER = 9;
     83
     84    private String objKey;
     85
     86    Comparator<AutoCompletionListItem> defaultACItemComparator = new Comparator<AutoCompletionListItem>() {
     87        public int compare(AutoCompletionListItem o1, AutoCompletionListItem o2) {
     88            return String.CASE_INSENSITIVE_ORDER.compare(o1.getValue(), o2.getValue());
     89        }
     90    };
    9191
    9292   
     
    120120        if (sel.isEmpty()) return;
    121121
    122         JPanel p = new JPanel(new GridBagLayout());
    123         p.add(new JLabel("<html>"+trn("This will change up to {0} object.",
    124                 "This will change up to {0} objects.", sel.size(),sel.size())
    125                 +"<br><br>"+tr("Please select a key")), GBC.eol().fill(GBC.HORIZONTAL));
    126         final AutoCompletingComboBox keys = new AutoCompletingComboBox();
    127         AutoCompletionManager autocomplete = Main.main.getEditLayer().data.getAutoCompletionManager();
    128         List<AutoCompletionListItem> keyList = autocomplete.getKeys();
    129 
    130         AutoCompletionListItem itemToSelect = null;
    131         // remove the object's tag keys from the list
    132         Iterator<AutoCompletionListItem> iter = keyList.iterator();
    133         while (iter.hasNext()) {
    134             AutoCompletionListItem item = iter.next();
    135             if (item.getValue().equals(lastAddKey)) {
    136                 itemToSelect = item;
    137             }
    138             for (int i = 0; i < propertyData.getRowCount(); ++i) {
    139                 if (item.getValue().equals(propertyData.getValueAt(i, 0))) {
    140                     if (itemToSelect == item) {
    141                         itemToSelect = null;
    142                     }
    143                     iter.remove();
    144                     break;
    145                 }
    146             }
    147         }
    148 
    149         Collections.sort(keyList, defaultACItemComparator);
    150         keys.setPossibleACItems(keyList);
    151         keys.setEditable(true);
    152 
    153         p.add(keys, GBC.eop().fill());
    154 
    155         p.add(new JLabel(tr("Please select a value")), GBC.eol());
    156         final AutoCompletingComboBox values = new AutoCompletingComboBox();
    157         values.setEditable(true);
    158         p.add(values, GBC.eop().fill());
    159         if (itemToSelect != null) {
    160             keys.setSelectedItem(itemToSelect);
    161             if (lastAddValue != null) {
    162                 values.setSelectedItem(lastAddValue);
    163             }
    164         }
    165 
    166         FocusAdapter focus = addFocusAdapter(keys, values, autocomplete, defaultACItemComparator);
    167         // fire focus event in advance or otherwise the popup list will be too small at first
    168         focus.focusGained(null);
    169 
    170         int recentTagsToShow = Main.pref.getInteger("properties.recently-added-tags", DEFAULT_LRU_TAGS_NUMBER);
    171         if (recentTagsToShow > MAX_LRU_TAGS_NUMBER) {
    172             recentTagsToShow = MAX_LRU_TAGS_NUMBER;
    173         }
    174         List<JosmAction> recentTagsActions = new ArrayList<JosmAction>();
    175         suggestRecentlyAddedTags(p, keys, values, recentTagsActions, recentTagsToShow, focus);
    176 
    177         JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
     122        final AddTagsPanel p = new AddTagsPanel(sel);
     123
     124        final JOptionPane optionPane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
    178125            @Override public void selectInitialValue() {
    179126                // save unix system selection (middle mouse paste)
     
    181128                if(sysSel != null) {
    182129                    Transferable old = sysSel.getContents(null);
    183                     keys.requestFocusInWindow();
    184                     keys.getEditor().selectAll();
     130                    p.keys.requestFocusInWindow();
     131                    p.keys.getEditor().selectAll();
    185132                    sysSel.setContents(old, null);
    186133                } else {
    187                     keys.requestFocusInWindow();
    188                     keys.getEditor().selectAll();
     134                    p.keys.requestFocusInWindow();
     135                    p.keys.getEditor().selectAll();
    189136                }
    190137            }
    191138        };
    192         JDialog dialog = pane.createDialog(Main.parent, tr("Add value?"));
     139        final JDialog dialog = optionPane.createDialog(Main.parent, tr("Add value?"));
    193140        dialog.setModalityType(Dialog.ModalityType.DOCUMENT_MODAL);
     141        // We specify what to do if panel wants to close the entire dialog
     142        p.setCloseActionListener(new ActionListener() {
     143            public void actionPerformed(ActionEvent e) {
     144                dialog.setVisible(false);
     145                optionPane.setValue(JOptionPane.OK_OPTION);
     146            }
     147        });
    194148        dialog.setVisible(true);
    195149       
    196         for (JosmAction action : recentTagsActions) {
    197             action.destroy();
    198         }
    199 
    200         if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
     150        p.destroyActions();
     151       
     152        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(optionPane.getValue()))
    201153            return;
    202         String key = keys.getEditor().getItem().toString().trim();
    203         String value = values.getEditor().getItem().toString().trim();
    204         if (key.isEmpty() || value.isEmpty())
     154        p.performTagAdding();
     155    }
     156   
     157    /**
     158     * Create a focus handling adapter and apply in to the editor component of value
     159     * autocompletion box.
     160     * @param keys Box for keys entering and autocompletion
     161     * @param values Box for values entering and autocompletion
     162     * @param autocomplete Manager handling the autocompletion
     163     * @param comparator Class to decide what values are offered on autocompletion
     164     * @return The created adapter
     165     */
     166    private FocusAdapter addFocusAdapter(final AutoCompletingComboBox keys, final AutoCompletingComboBox values,
     167            final AutoCompletionManager autocomplete, final Comparator<AutoCompletionListItem> comparator) {
     168        // get the combo box' editor component
     169        JTextComponent editor = (JTextComponent)values.getEditor()
     170                .getEditorComponent();
     171        // Refresh the values model when focus is gained
     172        FocusAdapter focus = new FocusAdapter() {
     173            @Override public void focusGained(FocusEvent e) {
     174                String key = keys.getEditor().getItem().toString();
     175
     176                List<AutoCompletionListItem> valueList = autocomplete.getValues(getAutocompletionKeys(key));
     177                Collections.sort(valueList, comparator);
     178
     179                values.setPossibleACItems(valueList);
     180                objKey=key;
     181            }
     182        };
     183        editor.addFocusListener(focus);
     184        return focus;
     185    }
     186   
     187    /**
     188    * Edit the value in the properties table row
     189    * @param row The row of the table from which the value is edited.
     190    */
     191    public void editProperty(final int row) {
     192        changedKey = null;
     193        Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
     194        if (sel.isEmpty()) return;
     195
     196        String key = propertyData.getValueAt(row, 0).toString();
     197        objKey=key;
     198       
     199        @SuppressWarnings("unchecked")
     200        final EditTagsPanel p = new EditTagsPanel(key, row, sel,
     201                (Map<String, Integer>) propertyData.getValueAt(row, 1));
     202
     203        final JOptionPane optionPane = new JOptionPane(p, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
     204            @Override public void selectInitialValue() {
     205                // save unix system selection (middle mouse paste)
     206                Clipboard sysSel = Toolkit.getDefaultToolkit().getSystemSelection();
     207                if(sysSel != null) {
     208                    Transferable old = sysSel.getContents(null);
     209                    p.values.requestFocusInWindow();
     210                    p.values.getEditor().selectAll();
     211                    sysSel.setContents(old, null);
     212                } else {
     213                    p.values.requestFocusInWindow();
     214                    p.values.getEditor().selectAll();
     215                }
     216            }
     217        };
     218       
     219        final JDialog dialog = optionPane.createDialog(Main.parent, trn("Change value?", "Change values?", p.getValueCount()));
     220        dialog.setModalityType(Dialog.ModalityType.DOCUMENT_MODAL);
     221        Dimension dlgSize = dialog.getSize();
     222        if(dlgSize.width > Main.parent.getSize().width) {
     223            dlgSize.width = Math.max(250, Main.parent.getSize().width);
     224            dialog.setSize(dlgSize);
     225        }
     226        dialog.setLocationRelativeTo(Main.parent);
     227       
     228        // Auto-close dialog when user selects a value from list
     229        p.values.getEditor().addActionListener(new ActionListener() {
     230            public void actionPerformed(ActionEvent e) {
     231                dialog.setVisible(false);
     232                optionPane.setValue(JOptionPane.OK_OPTION);
     233            }
     234        });
     235
     236        dialog.setVisible(true);
     237       
     238         if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(optionPane.getValue())) {
    205239            return;
    206         lastAddKey = key;
    207         lastAddValue = value;
    208         recentTags.put(new Tag(key, value), null);
    209         Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, value));
    210         changedKey = key;
    211     }
    212    
    213     private void suggestRecentlyAddedTags(JPanel p, final AutoCompletingComboBox keys, final AutoCompletingComboBox values, List<JosmAction> tagsActions, int tagsToShow, final FocusAdapter focus) {
    214         if (tagsToShow > 0 && !recentTags.isEmpty()) {
    215             p.add(new JLabel(tr("Recently added tags")), GBC.eol());
     240         }
     241
     242        p.performTagEdit();
     243
     244    }
     245   
     246    /**
     247     * If during last editProperty call user changed the key name, this key will be returned
     248     * Elsewhere, returns null.
     249     */
     250    public String getChangedKey() {
     251        return changedKey;
     252    }
     253   
     254    public void resetChangedKey() {
     255        changedKey = null;
     256    }
     257
     258    /**
     259     * For a given key k, return a list of keys which are used as keys for
     260     * auto-completing values to increase the search space.
     261     * @param key the key k
     262     * @return a list of keys
     263     */
     264    private static List<String> getAutocompletionKeys(String key) {
     265        if ("name".equals(key) || "addr:street".equals(key))
     266            return Arrays.asList("addr:street", "name");
     267        else
     268            return Arrays.asList(key);
     269    }
     270
     271   
     272    class EditTagsPanel extends JPanel {
     273        final AutoCompletingComboBox keys;
     274        final AutoCompletingComboBox values;
     275        String oldValue;
     276        final String key;
     277        final Collection<OsmPrimitive> sel;
     278        final Map<String, Integer> m;
     279           
     280        public EditTagsPanel(final String key, final int row, Collection<OsmPrimitive> sel, Map<String, Integer> countMap) {
     281            super(new BorderLayout());
     282            this.key = key;
     283            this.sel = sel;
     284            m = countMap;
     285   
     286 
     287            String msg = "<html>"+trn("This will change {0} object.",
     288                    "This will change up to {0} objects.", sel.size(), sel.size())
     289                    +"<br><br>("+tr("An empty value deletes the tag.", key)+")</html>";
     290
     291            add(new JLabel(msg), BorderLayout.NORTH);
     292
     293            JPanel p = new JPanel(new GridBagLayout());
     294            add(p, BorderLayout.CENTER);
     295
     296            AutoCompletionManager autocomplete = Main.main.getEditLayer().data.getAutoCompletionManager();
     297            List<AutoCompletionListItem> keyList = autocomplete.getKeys();
     298            Collections.sort(keyList, defaultACItemComparator);
     299
     300            keys = new AutoCompletingComboBox(key);
     301            keys.setPossibleACItems(keyList);
     302            keys.setEditable(true);
     303            keys.setSelectedItem(key);
     304
     305            p.add(new JLabel(tr("Key")), GBC.std());
     306            p.add(Box.createHorizontalStrut(10), GBC.std());
     307            p.add(keys, GBC.eol().fill(GBC.HORIZONTAL));
     308
     309           
     310            Comparator<AutoCompletionListItem> usedValuesAwareComparator = new Comparator<AutoCompletionListItem>() {
     311
     312                @Override
     313                public int compare(AutoCompletionListItem o1, AutoCompletionListItem o2) {
     314                    boolean c1 = m.containsKey(o1.getValue());
     315                    boolean c2 = m.containsKey(o2.getValue());
     316                    if (c1 == c2)
     317                        return String.CASE_INSENSITIVE_ORDER.compare(o1.getValue(), o2.getValue());
     318                    else if (c1)
     319                        return -1;
     320                    else
     321                        return +1;
     322                }
     323            };
     324
     325            List<AutoCompletionListItem> valueList = autocomplete.getValues(getAutocompletionKeys(key));
     326            Collections.sort(valueList, usedValuesAwareComparator);
     327
     328            final String selection= m.size()!=1?tr("<different>"):m.entrySet().iterator().next().getKey();
     329
     330            values = new AutoCompletingComboBox(selection);
     331            values.setRenderer(new DefaultListCellRenderer() {
     332                @Override public Component getListCellRendererComponent(JList list,
     333                        Object value, int index, boolean isSelected,  boolean cellHasFocus){
     334                    Component c = super.getListCellRendererComponent(list, value,
     335                            index, isSelected, cellHasFocus);
     336                    if (c instanceof JLabel) {
     337                        String str = ((AutoCompletionListItem) value).getValue();
     338                        if (valueCount.containsKey(objKey)) {
     339                            Map<String, Integer> m = valueCount.get(objKey);
     340                            if (m.containsKey(str)) {
     341                                str = tr("{0} ({1})", str, m.get(str));
     342                                c.setFont(c.getFont().deriveFont(Font.ITALIC + Font.BOLD));
     343                            }
     344                        }
     345                        ((JLabel) c).setText(str);
     346                    }
     347                    return c;
     348                }
     349            });
     350
     351            values.setEditable(true);
     352            values.setPossibleACItems(valueList);
     353            values.setSelectedItem(selection);
     354            values.getEditor().setItem(selection);
     355            p.add(new JLabel(tr("Value")), GBC.std());
     356            p.add(Box.createHorizontalStrut(10), GBC.std());
     357            p.add(values, GBC.eol().fill(GBC.HORIZONTAL));
     358            addFocusAdapter(keys, values, autocomplete, usedValuesAwareComparator);
     359           
     360        }
     361       
     362        private int getValueCount() {
     363            return m.size();
     364        }
     365
     366        /**
     367         * Edit tags of multiple selected objects according to selected ComboBox values
     368         * If value == "", tag will be deleted
     369         * Confirmations may be needed.
     370         */
     371        private void performTagEdit() {
     372            String value = values.getEditor().getItem().toString().trim();
     373            // is not Java 1.5
     374            //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC);
     375            if (value.equals("")) {
     376                value = null; // delete the key
     377            }
     378            String newkey = keys.getEditor().getItem().toString().trim();
     379            //newkey = java.text.Normalizer.normalize(newkey, java.text.Normalizer.Form.NFC);
     380            if (newkey.equals("")) {
     381                newkey = key;
     382                value = null; // delete the key instead
     383            }
     384            if (key.equals(newkey) && tr("<different>").equals(value))
     385                return;
     386            if (key.equals(newkey) || value == null) {
     387                Main.main.undoRedo.add(new ChangePropertyCommand(sel, newkey, value));
     388            } else {
     389                for (OsmPrimitive osm: sel) {
     390                    if(osm.get(newkey) != null) {
     391                        ExtendedDialog ed = new ExtendedDialog(
     392                                Main.parent,
     393                                tr("Overwrite key"),
     394                                new String[]{tr("Replace"), tr("Cancel")});
     395                        ed.setButtonIcons(new String[]{"purge", "cancel"});
     396                        ed.setContent(tr("You changed the key from ''{0}'' to ''{1}''.\n"
     397                                + "The new key is already used, overwrite values?", key, newkey));
     398                        ed.setCancelButton(2);
     399                        ed.toggleEnable("overwriteEditKey");
     400                        ed.showDialog();
     401
     402                        if (ed.getValue() != 1)
     403                            return;
     404                        break;
     405                    }
     406                }
     407                Collection<Command> commands=new Vector<Command>();
     408                commands.add(new ChangePropertyCommand(sel, key, null));
     409                if (value.equals(tr("<different>"))) {
     410                    HashMap<String, Vector<OsmPrimitive>> map=new HashMap<String, Vector<OsmPrimitive>>();
     411                    for (OsmPrimitive osm: sel) {
     412                        String val=osm.get(key);
     413                        if(val != null)
     414                        {
     415                            if (map.containsKey(val)) {
     416                                map.get(val).add(osm);
     417                            } else {
     418                                Vector<OsmPrimitive> v = new Vector<OsmPrimitive>();
     419                                v.add(osm);
     420                                map.put(val, v);
     421                            }
     422                        }
     423                    }
     424                    for (Map.Entry<String, Vector<OsmPrimitive>> e: map.entrySet()) {
     425                        commands.add(new ChangePropertyCommand(e.getValue(), newkey, e.getKey()));
     426                    }
     427                } else {
     428                    commands.add(new ChangePropertyCommand(sel, newkey, value));
     429                }
     430                Main.main.undoRedo.add(new SequenceCommand(
     431                        trn("Change properties of up to {0} object",
     432                                "Change properties of up to {0} objects", sel.size(), sel.size()),
     433                                commands));
     434            }
     435
     436            changedKey = newkey;
     437        }
     438       
     439       
     440    }
     441   
     442    class AddTagsPanel extends JPanel {
     443        final AutoCompletingComboBox keys = new AutoCompletingComboBox();
     444        final AutoCompletingComboBox values = new AutoCompletingComboBox();
     445        List<JosmAction> recentTagsActions = new ArrayList<JosmAction>();
     446        Collection<OsmPrimitive> sel;
     447        private ActionListener autoCloseActionListener;
     448       
     449        public AddTagsPanel(Collection<OsmPrimitive> sel) {
     450            super(new GridBagLayout());
     451            this.sel = sel;
     452            add(new JLabel("<html>"+trn("This will change up to {0} object.",
     453                "This will change up to {0} objects.", sel.size(),sel.size())
     454                +"<br><br>"+tr("Please select a key")), GBC.eol().fill(GBC.HORIZONTAL));
     455
     456            AutoCompletionManager autocomplete = Main.main.getEditLayer().data.getAutoCompletionManager();
     457            List<AutoCompletionListItem> keyList = autocomplete.getKeys();
     458
     459            AutoCompletionListItem itemToSelect = null;
     460            // remove the object's tag keys from the list
     461            Iterator<AutoCompletionListItem> iter = keyList.iterator();
     462            while (iter.hasNext()) {
     463                AutoCompletionListItem item = iter.next();
     464                if (item.getValue().equals(lastAddKey)) {
     465                    itemToSelect = item;
     466                }
     467                for (int i = 0; i < propertyData.getRowCount(); ++i) {
     468                    if (item.getValue().equals(propertyData.getValueAt(i, 0))) {
     469                        if (itemToSelect == item) {
     470                            itemToSelect = null;
     471                        }
     472                        iter.remove();
     473                        break;
     474                    }
     475                }
     476            }
     477
     478            Collections.sort(keyList, defaultACItemComparator);
     479            keys.setPossibleACItems(keyList);
     480            keys.setEditable(true);
     481
     482            add(keys, GBC.eop().fill());
     483
     484            add(new JLabel(tr("Please select a value")), GBC.eol());
     485            values.setEditable(true);
     486            add(values, GBC.eop().fill());
     487            if (itemToSelect != null) {
     488                keys.setSelectedItem(itemToSelect);
     489                if (lastAddValue != null) {
     490                    values.setSelectedItem(lastAddValue);
     491                }
     492            }
     493
     494            FocusAdapter focus = addFocusAdapter(keys, values, autocomplete, defaultACItemComparator);
     495            // fire focus event in advance or otherwise the popup list will be too small at first
     496            focus.focusGained(null);
     497
     498            int recentTagsToShow = Main.pref.getInteger("properties.recently-added-tags", DEFAULT_LRU_TAGS_NUMBER);
     499            if (recentTagsToShow > MAX_LRU_TAGS_NUMBER) {
     500                recentTagsToShow = MAX_LRU_TAGS_NUMBER;
     501            }
     502            suggestRecentlyAddedTags(recentTagsToShow, focus);
     503        }
     504
     505        private void suggestRecentlyAddedTags(int tagsToShow, final FocusAdapter focus) {
     506            if (!(tagsToShow > 0 && !recentTags.isEmpty()))
     507                return;
     508
     509            add(new JLabel(tr("Recently added tags")), GBC.eol());
    216510           
    217511            int count = 1;
     
    234528                    }
    235529                };
    236                 tagsActions.add(action);
    237                 // Disable action if its key is already set on the object (the key being absent from the keys list for this reason
    238                 // performing this action leads to autocomplete to the next key (see #7671 comments)
    239                 for (int j = 0; j < propertyData.getRowCount(); ++j) {
    240                     if (t.getKey().equals(propertyData.getValueAt(j, 0))) {
    241                         action.setEnabled(false);
    242                         break;
    243                     }
    244                 }
     530                recentTagsActions.add(action);
     531                disableTagIfNeeded(t, action);
    245532                // Find and display icon
    246533                ImageIcon icon = MapPaintStyles.getNodeIcon(t, false); // Filters deprecated icon
     
    250537                GridBagConstraints gbc = new GridBagConstraints();
    251538                gbc.ipadx = 5;
    252                 p.add(new JLabel(action.isEnabled() ? icon : GuiHelper.getDisabledIcon(icon)), gbc);
     539                add(new JLabel(action.isEnabled() ? icon : GuiHelper.getDisabledIcon(icon)), gbc);
    253540                // Create tag label
    254541                final String color = action.isEnabled() ? "" : "; color:gray";
     
    258545                if (action.isEnabled()) {
    259546                    // Register action
    260                     p.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), actionShortcutKey);
    261                     p.getActionMap().put(actionShortcutKey, action);
     547                    getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), actionShortcutKey);
     548                    getActionMap().put(actionShortcutKey, action);
    262549                    // Make the tag label clickable and set tooltip to the action description (this displays also the keyboard shortcut)
    263550                    tagLabel.setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION));
     
    267554                        public void mouseClicked(MouseEvent e) {
    268555                            action.actionPerformed(null);
     556                            // add tags and close window on double-click
     557                            if (e.getClickCount()>1) {
     558                                performTagAdding();
     559                                if (autoCloseActionListener!=null)
     560                                    autoCloseActionListener.actionPerformed(null);
     561                            }
     562                            // add tags on Shift-Click
     563                            if (e.isShiftDown()) {
     564                                performTagAdding();
     565                            }
    269566                        }
    270567                    });
     
    278575                JPanel tagPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
    279576                tagPanel.add(tagLabel);
    280                 p.add(tagPanel, GBC.eol().fill(GBC.HORIZONTAL));
    281             }
    282         }
    283     }
    284 
    285     /**
    286      * Create a focus handling adapter and apply in to the editor component of value
    287      * autocompletion box.
    288      * @param keys Box for keys entering and autocompletion
    289      * @param values Box for values entering and autocompletion
    290      * @param autocomplete Manager handling the autocompletion
    291      * @param comparator Class to decide what values are offered on autocompletion
    292      * @return The created adapter
    293      */
    294     private FocusAdapter addFocusAdapter(final AutoCompletingComboBox keys, final AutoCompletingComboBox values,
    295             final AutoCompletionManager autocomplete, final Comparator<AutoCompletionListItem> comparator) {
    296         // get the combo box' editor component
    297         JTextComponent editor = (JTextComponent)values.getEditor()
    298                 .getEditorComponent();
    299         // Refresh the values model when focus is gained
    300         FocusAdapter focus = new FocusAdapter() {
    301             @Override public void focusGained(FocusEvent e) {
    302                 String key = keys.getEditor().getItem().toString();
    303 
    304                 List<AutoCompletionListItem> valueList = autocomplete.getValues(getAutocompletionKeys(key));
    305                 Collections.sort(valueList, comparator);
    306 
    307                 values.setPossibleACItems(valueList);
    308                 objKey=key;
    309             }
    310         };
    311         editor.addFocusListener(focus);
    312         return focus;
    313     }
    314    
     577                add(tagPanel, GBC.eol().fill(GBC.HORIZONTAL));
     578            }
     579        }
     580
     581        public void destroyActions() {
     582            for (JosmAction action : recentTagsActions) {
     583                action.destroy();
     584            }
     585        }
     586
     587
    315588        /**
    316      * Edit the value in the properties table row
    317      * @param row The row of the table from which the value is edited.
    318      */
    319     public void editProperty(final int row) {
    320         changedKey = null;
    321         Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
    322         if (sel.isEmpty()) return;
    323 
    324         String key = propertyData.getValueAt(row, 0).toString();
    325         objKey=key;
    326 
    327         String msg = "<html>"+trn("This will change {0} object.",
    328                 "This will change up to {0} objects.", sel.size(), sel.size())
    329                 +"<br><br>("+tr("An empty value deletes the tag.", key)+")</html>";
    330 
    331         JPanel panel = new JPanel(new BorderLayout());
    332         panel.add(new JLabel(msg), BorderLayout.NORTH);
    333 
    334         JPanel p = new JPanel(new GridBagLayout());
    335         panel.add(p, BorderLayout.CENTER);
    336 
    337         AutoCompletionManager autocomplete = Main.main.getEditLayer().data.getAutoCompletionManager();
    338         List<AutoCompletionListItem> keyList = autocomplete.getKeys();
    339         Collections.sort(keyList, defaultACItemComparator);
    340 
    341         final AutoCompletingComboBox keys = new AutoCompletingComboBox(key);
    342         keys.setPossibleACItems(keyList);
    343         keys.setEditable(true);
    344         keys.setSelectedItem(key);
    345 
    346         p.add(new JLabel(tr("Key")), GBC.std());
    347         p.add(Box.createHorizontalStrut(10), GBC.std());
    348         p.add(keys, GBC.eol().fill(GBC.HORIZONTAL));
    349 
    350         @SuppressWarnings("unchecked")
    351         final Map<String, Integer> m = (Map<String, Integer>) propertyData.getValueAt(row, 1);
    352 
    353         Comparator<AutoCompletionListItem> usedValuesAwareComparator = new Comparator<AutoCompletionListItem>() {
    354 
    355             @Override
    356             public int compare(AutoCompletionListItem o1, AutoCompletionListItem o2) {
    357                 boolean c1 = m.containsKey(o1.getValue());
    358                 boolean c2 = m.containsKey(o2.getValue());
    359                 if (c1 == c2)
    360                     return String.CASE_INSENSITIVE_ORDER.compare(o1.getValue(), o2.getValue());
    361                 else if (c1)
    362                     return -1;
    363                 else
    364                     return +1;
    365             }
    366         };
    367 
    368         List<AutoCompletionListItem> valueList = autocomplete.getValues(getAutocompletionKeys(key));
    369         Collections.sort(valueList, usedValuesAwareComparator);
    370 
    371         final String selection= m.size()!=1?tr("<different>"):m.entrySet().iterator().next().getKey();
    372        
    373         final AutoCompletingComboBox values = new AutoCompletingComboBox(selection);
    374         values.setRenderer(new DefaultListCellRenderer() {
    375             @Override public Component getListCellRendererComponent(JList list,
    376                     Object value, int index, boolean isSelected,  boolean cellHasFocus){
    377                 Component c = super.getListCellRendererComponent(list, value,
    378                         index, isSelected, cellHasFocus);
    379                 if (c instanceof JLabel) {
    380                     String str = ((AutoCompletionListItem) value).getValue();
    381                     if (valueCount.containsKey(objKey)) {
    382                         Map<String, Integer> m = valueCount.get(objKey);
    383                         if (m.containsKey(str)) {
    384                             str = tr("{0} ({1})", str, m.get(str));
    385                             c.setFont(c.getFont().deriveFont(Font.ITALIC + Font.BOLD));
    386                         }
    387                     }
    388                     ((JLabel) c).setText(str);
    389                 }
    390                 return c;
    391             }
    392         });
    393        
    394         values.setEditable(true);
    395         values.setPossibleACItems(valueList);
    396         values.setSelectedItem(selection);
    397         values.getEditor().setItem(selection);
    398         p.add(new JLabel(tr("Value")), GBC.std());
    399         p.add(Box.createHorizontalStrut(10), GBC.std());
    400         p.add(values, GBC.eol().fill(GBC.HORIZONTAL));
    401         addFocusAdapter(keys, values, autocomplete, usedValuesAwareComparator);
    402 
    403         final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
    404             @Override public void selectInitialValue() {
    405                 // save unix system selection (middle mouse paste)
    406                 Clipboard sysSel = Toolkit.getDefaultToolkit().getSystemSelection();
    407                 if(sysSel != null) {
    408                     Transferable old = sysSel.getContents(null);
    409                     values.requestFocusInWindow();
    410                     values.getEditor().selectAll();
    411                     sysSel.setContents(old, null);
    412                 } else {
    413                     values.requestFocusInWindow();
    414                     values.getEditor().selectAll();
    415                 }
    416             }
    417         };
    418         final JDialog dlg = optionPane.createDialog(Main.parent, trn("Change value?", "Change values?", m.size()));
    419         dlg.setModalityType(Dialog.ModalityType.DOCUMENT_MODAL);
    420         Dimension dlgSize = dlg.getSize();
    421         if(dlgSize.width > Main.parent.getSize().width) {
    422             dlgSize.width = Math.max(250, Main.parent.getSize().width);
    423             dlg.setSize(dlgSize);
    424         }
    425         dlg.setLocationRelativeTo(Main.parent);
    426         values.getEditor().addActionListener(new ActionListener() {
    427             public void actionPerformed(ActionEvent e) {
    428                 dlg.setVisible(false);
    429                 optionPane.setValue(JOptionPane.OK_OPTION);
    430             }
    431         });
    432 
    433         String oldValue = values.getEditor().getItem().toString();
    434         dlg.setVisible(true);
    435 
    436         Object answer = optionPane.getValue();
    437         if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
    438                 (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
    439             values.getEditor().setItem(oldValue);
    440             return;
    441         }
    442 
    443         String value = values.getEditor().getItem().toString().trim();
    444         // is not Java 1.5
    445         //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC);
    446         if (value.equals("")) {
    447             value = null; // delete the key
    448         }
    449         String newkey = keys.getEditor().getItem().toString().trim();
    450         //newkey = java.text.Normalizer.normalize(newkey, java.text.Normalizer.Form.NFC);
    451         if (newkey.equals("")) {
    452             newkey = key;
    453             value = null; // delete the key instead
    454         }
    455         if (key.equals(newkey) && tr("<different>").equals(value))
    456             return;
    457         if (key.equals(newkey) || value == null) {
    458             Main.main.undoRedo.add(new ChangePropertyCommand(sel, newkey, value));
    459         } else {
    460             for (OsmPrimitive osm: sel) {
    461                 if(osm.get(newkey) != null) {
    462                     ExtendedDialog ed = new ExtendedDialog(
    463                             Main.parent,
    464                             tr("Overwrite key"),
    465                             new String[]{tr("Replace"), tr("Cancel")});
    466                     ed.setButtonIcons(new String[]{"purge", "cancel"});
    467                     ed.setContent(tr("You changed the key from ''{0}'' to ''{1}''.\n"
    468                             + "The new key is already used, overwrite values?", key, newkey));
    469                     ed.setCancelButton(2);
    470                     ed.toggleEnable("overwriteEditKey");
    471                     ed.showDialog();
    472 
    473                     if (ed.getValue() != 1)
    474                         return;
     589         * Read tags from comboboxes and add it to all selected objects
     590         */
     591        public void performTagAdding() {
     592            String key = keys.getEditor().getItem().toString().trim();
     593            String value = values.getEditor().getItem().toString().trim();
     594            if (key.isEmpty() || value.isEmpty()) return;
     595            lastAddKey = key;
     596            lastAddValue = value;
     597            recentTags.put(new Tag(key, value), null);
     598            Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, value));
     599            changedKey = key;
     600        }
     601
     602        private void disableTagIfNeeded(final Tag t, final JosmAction action) {
     603            // Disable action if its key is already set on the object (the key being absent from the keys list for this reason
     604            // performing this action leads to autocomplete to the next key (see #7671 comments)
     605            for (int j = 0; j < propertyData.getRowCount(); ++j) {
     606                if (t.getKey().equals(propertyData.getValueAt(j, 0))) {
     607                    action.setEnabled(false);
    475608                    break;
    476609                }
    477610            }
    478             Collection<Command> commands=new Vector<Command>();
    479             commands.add(new ChangePropertyCommand(sel, key, null));
    480             if (value.equals(tr("<different>"))) {
    481                 HashMap<String, Vector<OsmPrimitive>> map=new HashMap<String, Vector<OsmPrimitive>>();
    482                 for (OsmPrimitive osm: sel) {
    483                     String val=osm.get(key);
    484                     if(val != null)
    485                     {
    486                         if (map.containsKey(val)) {
    487                             map.get(val).add(osm);
    488                         } else {
    489                             Vector<OsmPrimitive> v = new Vector<OsmPrimitive>();
    490                             v.add(osm);
    491                             map.put(val, v);
    492                         }
    493                     }
    494                 }
    495                 for (Map.Entry<String, Vector<OsmPrimitive>> e: map.entrySet()) {
    496                     commands.add(new ChangePropertyCommand(e.getValue(), newkey, e.getKey()));
    497                 }
    498             } else {
    499                 commands.add(new ChangePropertyCommand(sel, newkey, value));
    500             }
    501             Main.main.undoRedo.add(new SequenceCommand(
    502                     trn("Change properties of up to {0} object",
    503                             "Change properties of up to {0} objects", sel.size(), sel.size()),
    504                             commands));
    505         }
    506        
    507         changedKey = newkey;
    508     }
    509    
    510     /**
    511      * If during last editProperty call user changed the key name, this key will be returned
    512      * Elsewhere, returns null.
    513      */
    514     public String getChangedKey() {
    515         return changedKey;
    516     }
    517    
    518     public void resetChangedKey() {
    519         changedKey = null;
    520     }
    521            
    522     /**
    523      * For a given key k, return a list of keys which are used as keys for
    524      * auto-completing values to increase the search space.
    525      * @param key the key k
    526      * @return a list of keys
    527      */
    528     private static List<String> getAutocompletionKeys(String key) {
    529         if ("name".equals(key) || "addr:street".equals(key))
    530             return Arrays.asList("addr:street", "name");
    531         else
    532             return Arrays.asList(key);
    533     }
    534    
    535 
     611        }
     612
     613        /**
     614         * @param autoCloseActionListener will be called when we need to close the entire dialog
     615         */
     616        public void setCloseActionListener(ActionListener autoCloseActionListener) {
     617            this.autoCloseActionListener = autoCloseActionListener;
     618        }
     619    }
    536620}
    537621   
Note: See TracChangeset for help on using the changeset viewer.