Ticket #21227: 21227-3.patch

File 21227-3.patch, 58.6 KB (added by marcello@…, 5 years ago)

[PATCH] in relation editor filter autocompletion roles according to relation and member type

  • src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

     
    1414import java.awt.datatransfer.Clipboard;
    1515import java.awt.datatransfer.FlavorListener;
    1616import java.awt.event.ActionEvent;
    17 import java.awt.event.FocusAdapter;
    18 import java.awt.event.FocusEvent;
    1917import java.awt.event.InputEvent;
    2018import java.awt.event.KeyEvent;
    2119import java.awt.event.MouseAdapter;
     
    2725import java.util.Collection;
    2826import java.util.Collections;
    2927import java.util.EnumSet;
     28import java.util.HashMap;
     29import java.util.HashSet;
    3030import java.util.List;
     31import java.util.Map;
    3132import java.util.Set;
    3233import java.util.stream.Collectors;
    3334
     
    3536import javax.swing.BorderFactory;
    3637import javax.swing.InputMap;
    3738import javax.swing.JButton;
     39import javax.swing.JCheckBox;
    3840import javax.swing.JComponent;
    3941import javax.swing.JLabel;
    4042import javax.swing.JMenuItem;
     
    4547import javax.swing.JSplitPane;
    4648import javax.swing.JTabbedPane;
    4749import javax.swing.JTable;
     50import javax.swing.JTextField;
    4851import javax.swing.JToolBar;
    4952import javax.swing.KeyStroke;
     53import javax.swing.event.PopupMenuEvent;
     54import javax.swing.event.PopupMenuListener;
    5055
    5156import org.openstreetmap.josm.actions.JosmAction;
    5257import org.openstreetmap.josm.command.ChangeMembersCommand;
     
    5863import org.openstreetmap.josm.data.osm.Relation;
    5964import org.openstreetmap.josm.data.osm.RelationMember;
    6065import org.openstreetmap.josm.data.osm.Tag;
     66import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
    6167import org.openstreetmap.josm.data.validation.tests.RelationChecker;
    6268import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
    6369import org.openstreetmap.josm.gui.MainApplication;
     
    98104import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    99105import org.openstreetmap.josm.gui.tagging.TagEditorModel;
    100106import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
    101 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    102 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
     107import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBox;
     108import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel;
     109import org.openstreetmap.josm.gui.tagging.ac.AutoCompEvent;
     110import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener;
     111import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField;
    103112import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
    104113import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
    105114import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler;
    106115import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
    107116import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
     117import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role;
    108118import org.openstreetmap.josm.gui.util.WindowGeometry;
    109119import org.openstreetmap.josm.spi.preferences.Config;
    110120import org.openstreetmap.josm.tools.CheckParameterUtil;
     121import org.openstreetmap.josm.tools.GBC;
    111122import org.openstreetmap.josm.tools.InputMapUtils;
    112123import org.openstreetmap.josm.tools.Logging;
    113124import org.openstreetmap.josm.tools.Shortcut;
     
    117128 * This dialog is for editing relations.
    118129 * @since 343
    119130 */
    120 public class GenericRelationEditor extends RelationEditor implements CommandQueueListener {
     131public class GenericRelationEditor extends RelationEditor implements AutoCompListener, CommandQueueListener, PopupMenuListener {
     132    private static final String PREF_LASTROLE = "relation.editor.generic.lastrole";
     133    private static final String PREF_USE_ROLE_FILTER = "relation.editor.use_role_filter";
     134
    121135    /** the tag table and its model */
    122136    private final TagEditorPanel tagEditorPanel;
    123137    private final ReferringRelationsBrowser referrerBrowser;
     
    131145    private final SelectionTable selectionTable;
    132146    private final SelectionTableModel selectionTableModel;
    133147
    134     private final AutoCompletingTextField tfRole;
     148    private final AutoCompletionManager manager;
     149    private final AutoCompComboBox<AutoCompletionItem> cbRole;
    135150
    136151    /**
    137152     * the menu item in the windows menu. Required to properly hide on dialog close.
     
    172187
    173188    private Component selectedTabPane;
    174189    private JTabbedPane tabbedPane;
     190    private JCheckBox btnFilter = new JCheckBox(tr("Filter"));
    175191
    176192    /**
    177193     * Creates a new relation editor for the given relation. The relation will be saved if the user
     
    216232        populateModels(relation);
    217233        tagEditorPanel.getModel().ensureOneTag();
    218234
     235        manager = AutoCompletionManager.of(this.getLayer().data);
     236
    219237        // setting up the member table
    220         memberTable = new MemberTable(getLayer(), getRelation(), memberTableModel);
     238        AutoCompComboBox<AutoCompletionItem> cbRoleEditor = new AutoCompComboBox<>();
     239        cbRoleEditor.getEditorComponent().addAutoCompListener(this);
     240        cbRoleEditor.addPopupMenuListener(this);
     241        cbRoleEditor.setToolTipText(tr("Select a role for this relation member"));
     242        memberTable = new MemberTable(getLayer(), cbRoleEditor, memberTableModel);
    221243        memberTable.addMouseListener(new MemberTableDblClickAdapter());
    222244        memberTableModel.addMemberModelListener(memberTable);
    223245
    224         MemberRoleCellEditor ce = (MemberRoleCellEditor) memberTable.getColumnModel().getColumn(0).getCellEditor();
    225246        selectionTable = new SelectionTable(selectionTableModel, memberTableModel);
    226         selectionTable.setRowHeight(ce.getEditor().getPreferredSize().height);
     247        selectionTable.setRowHeight(cbRoleEditor.getPreferredSize().height);
    227248
    228249        LeftButtonToolbar leftButtonToolbar = new LeftButtonToolbar(new RelationEditorActionAccess());
    229         tfRole = buildRoleTextField(this);
     250        cbRole = new AutoCompComboBox<>();
     251        cbRole.getEditorComponent().addAutoCompListener(this);
     252        cbRole.addPopupMenuListener(this);
     253        cbRole.setText(Config.getPref().get(PREF_LASTROLE, ""));
     254        cbRole.setToolTipText(tr("Select a role"));
    230255
    231256        JSplitPane pane = buildSplitPane(
    232257                buildTagEditorPanel(tagEditorPanel),
    233                 buildMemberEditorPanel(leftButtonToolbar, new RelationEditorActionAccess()),
     258                buildMemberEditorPanel(leftButtonToolbar),
    234259                this);
    235260        pane.setPreferredSize(new Dimension(100, 100));
    236261
     
    310335                @Override
    311336                public void actionPerformed(ActionEvent e) {
    312337                    super.actionPerformed(e);
    313                     tfRole.requestFocusInWindow();
     338                    cbRole.requestFocusInWindow();
    314339                }
    315340            }, "PASTE_MEMBERS", key, getRootPane(), memberTable, selectionTable);
    316341        }
     
    446471    }
    447472
    448473    /**
    449      * builds the role text field
    450      * @param re relation editor
    451      * @return the role text field
    452      */
    453     protected static AutoCompletingTextField buildRoleTextField(final IRelationEditor re) {
    454         final AutoCompletingTextField tfRole = new AutoCompletingTextField(10);
    455         tfRole.setToolTipText(tr("Enter a role and apply it to the selected relation members"));
    456         tfRole.addFocusListener(new FocusAdapter() {
    457             @Override
    458             public void focusGained(FocusEvent e) {
    459                 tfRole.selectAll();
    460             }
    461         });
    462         tfRole.setAutoCompletionList(new AutoCompletionList());
    463         tfRole.addFocusListener(
    464                 new FocusAdapter() {
    465                     @Override
    466                     public void focusGained(FocusEvent e) {
    467                         AutoCompletionList list = tfRole.getAutoCompletionList();
    468                         if (list != null) {
    469                             list.clear();
    470                             AutoCompletionManager.of(re.getLayer().data).populateWithMemberRoles(list, re.getRelation());
    471                         }
    472                     }
    473                 }
    474         );
    475         tfRole.setText(Config.getPref().get("relation.editor.generic.lastrole", ""));
    476         return tfRole;
    477     }
    478 
    479     /**
    480474     * builds the panel for the relation member editor
    481475     * @param leftButtonToolbar left button toolbar
    482      * @param editorAccess The relation editor
    483476     *
    484477     * @return the panel for the relation member editor
    485478     */
    486     protected static JPanel buildMemberEditorPanel(
    487             LeftButtonToolbar leftButtonToolbar, IRelationEditorActionAccess editorAccess) {
     479    protected JPanel buildMemberEditorPanel(LeftButtonToolbar leftButtonToolbar) {
    488480        final JPanel pnl = new JPanel(new GridBagLayout());
    489         final JScrollPane scrollPane = new JScrollPane(editorAccess.getMemberTable());
     481        final JScrollPane scrollPane = new JScrollPane(memberTable);
    490482
    491483        GridBagConstraints gc = new GridBagConstraints();
    492484        gc.gridx = 0;
     
    518510        pnl.add(scrollPane, gc);
    519511
    520512        // --- role editing
    521         JPanel p3 = new JPanel(new FlowLayout(FlowLayout.LEFT));
    522         p3.add(new JLabel(tr("Apply Role:")));
    523         p3.add(editorAccess.getTextFieldRole());
    524         SetRoleAction setRoleAction = new SetRoleAction(editorAccess);
    525         editorAccess.getMemberTableModel().getSelectionModel().addListSelectionListener(setRoleAction);
    526         editorAccess.getTextFieldRole().getDocument().addDocumentListener(setRoleAction);
    527         editorAccess.getTextFieldRole().addActionListener(setRoleAction);
    528         editorAccess.getMemberTableModel().getSelectionModel().addListSelectionListener(
    529                 e -> editorAccess.getTextFieldRole().setEnabled(editorAccess.getMemberTable().getSelectedRowCount() > 0)
     513        JPanel p3 = new JPanel(new GridBagLayout());
     514        GBC gbc = GBC.std().fill(GridBagConstraints.NONE);
     515        JLabel lbl = new JLabel(tr("Role:"));
     516        p3.add(lbl, gbc);
     517
     518        p3.add(cbRole, gbc.insets(3, 3, 0, 3).fill(GridBagConstraints.HORIZONTAL));
     519
     520        SetRoleAction setRoleAction = new SetRoleAction(new RelationEditorActionAccess());
     521        memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction);
     522        cbRole.getEditorComponent().getDocument().addDocumentListener(setRoleAction);
     523        cbRole.getEditorComponent().addActionListener(setRoleAction);
     524        memberTableModel.getSelectionModel().addListSelectionListener(
     525                e -> cbRole.setEnabled(memberTable.getSelectedRowCount() > 0)
    530526        );
    531         editorAccess.getTextFieldRole().setEnabled(editorAccess.getMemberTable().getSelectedRowCount() > 0);
     527        cbRole.setEnabled(memberTable.getSelectedRowCount() > 0);
     528
    532529        JButton btnApply = new JButton(setRoleAction);
    533         btnApply.setPreferredSize(new Dimension(20, 20));
     530        int height = cbRole.getPreferredSize().height;
     531        btnApply.setPreferredSize(new Dimension(height, height));
    534532        btnApply.setText("");
    535         p3.add(btnApply);
     533        p3.add(btnApply, gbc.weight(0, 0).fill(GridBagConstraints.NONE));
    536534
     535        btnFilter.setToolTipText(tr("Filter role suggestions based on relation type"));
     536        btnFilter.setSelected(Config.getPref().getBoolean(PREF_USE_ROLE_FILTER, false));
     537        p3.add(btnFilter, gbc.span(GridBagConstraints.REMAINDER));
     538
     539        //
     540
    537541        gc.gridx = 1;
    538542        gc.gridy = 2;
    539543        gc.fill = GridBagConstraints.HORIZONTAL;
     
    562566        gc.anchor = GridBagConstraints.NORTHWEST;
    563567        gc.weightx = 0.0;
    564568        gc.weighty = 1.0;
    565         pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar(editorAccess),
     569        pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar(new RelationEditorActionAccess()),
    566570                ScrollViewport.VERTICAL_DIRECTION), gc);
    567571
    568572        gc.gridx = 1;
     
    570574        gc.weightx = 1.0;
    571575        gc.weighty = 1.0;
    572576        gc.fill = GridBagConstraints.BOTH;
    573         pnl2.add(buildSelectionTablePanel(editorAccess.getSelectionTable()), gc);
     577        pnl2.add(buildSelectionTablePanel(selectionTable), gc);
    574578
    575579        final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
    576580        splitPane.setLeftComponent(pnl);
    577581        splitPane.setRightComponent(pnl2);
    578582        splitPane.setOneTouchExpandable(false);
    579         if (editorAccess.getEditor() instanceof Window) {
    580             ((Window) editorAccess.getEditor()).addWindowListener(new WindowAdapter() {
    581                 @Override
    582                 public void windowOpened(WindowEvent e) {
    583                     // has to be called when the window is visible, otherwise no effect
    584                     splitPane.setDividerLocation(0.6);
    585                 }
    586             });
    587         }
     583        addWindowListener(new WindowAdapter() {
     584            @Override
     585            public void windowOpened(WindowEvent e) {
     586                // has to be called when the window is visible, otherwise no effect
     587                splitPane.setDividerLocation(0.6);
     588            }
     589        });
    588590
    589591        JPanel pnl3 = new JPanel(new BorderLayout());
    590592        pnl3.add(splitPane, BorderLayout.CENTER);
     
    754756                clipboard.addFlavorListener(listener);
    755757            }
    756758        } else {
     759            Config.getPref().put(PREF_LASTROLE, cbRole.getText());
     760            Config.getPref().putBoolean(PREF_USE_ROLE_FILTER, btnFilter.isSelected());
     761
    757762            // make sure all registered listeners are unregistered
    758763            //
    759764            memberTable.stopHighlighting();
     
    10391044        }
    10401045
    10411046        @Override
    1042         public AutoCompletingTextField getTextFieldRole() {
    1043             return tfRole;
     1047        public JTextField getTextFieldRole() {
     1048            return cbRole.getEditorComponent();
    10441049        }
    1045 
    10461050    }
    10471051
    10481052    @Override
     
    10541058            applyAction.updateEnabledState();
    10551059        }
    10561060    }
     1061
     1062    private void updateAutoCompModel(AutoCompComboBoxModel<AutoCompletionItem> model) {
     1063        Map<String, String> currentTags = new HashMap<>();
     1064        if (btnFilter.isSelected())
     1065            currentTags = tagEditorPanel.getModel().getTags();
     1066        Set<Role> currentRoles = new HashSet<>();
     1067        EnumSet<TaggingPresetType> selectedTypes = EnumSet.noneOf(TaggingPresetType.class);
     1068        for (int i = 0; i < memberTableModel.getRowCount(); ++i) {
     1069            RelationMember member = memberTableModel.getValue(i);
     1070            Role role = new Role();
     1071            role.key = member.getRole();
     1072            role.types = EnumSet.of(TaggingPresetType.forPrimitiveType(member.getDisplayType()));
     1073            currentRoles.add(role);
     1074        }
     1075        for (RelationMember member : memberTableModel.getSelectedMembers()) {
     1076            selectedTypes.add(TaggingPresetType.forPrimitiveType(member.getDisplayType()));
     1077        }
     1078        model.replaceAllElements(manager.getRolesForRelation(currentTags, currentRoles, selectedTypes));
     1079    }
     1080
     1081    @Override
     1082    @SuppressWarnings("unchecked")
     1083    public void autoCompBefore(AutoCompEvent e) {
     1084        AutoCompTextField<AutoCompletionItem> tf = (AutoCompTextField<AutoCompletionItem>) e.getSource();
     1085        String savedText = tf.getText();
     1086        updateAutoCompModel(tf.getModel());
     1087        tf.setText(savedText);
     1088    }
     1089
     1090    @Override
     1091    public void autoCompPerformed(AutoCompEvent e) {
     1092        // Not interested
     1093    }
     1094
     1095    @Override
     1096    @SuppressWarnings("unchecked")
     1097    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
     1098        AutoCompComboBox<AutoCompletionItem> cb = (AutoCompComboBox<AutoCompletionItem>) e.getSource();
     1099        String savedText = cb.getText();
     1100        updateAutoCompModel(cb.getModel());
     1101        cb.setText(savedText);
     1102    }
     1103
     1104    @Override
     1105    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
     1106        // Who cares?
     1107    }
     1108
     1109    @Override
     1110    public void popupMenuCanceled(PopupMenuEvent e) {
     1111        // Who cares?
     1112    }
    10571113}
  • src/org/openstreetmap/josm/gui/dialogs/relation/MemberRoleCellEditor.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.gui.dialogs.relation;
    3 
    4 import java.awt.Component;
    5 
    6 import javax.swing.AbstractCellEditor;
    7 import javax.swing.BorderFactory;
    8 import javax.swing.CellEditor;
    9 import javax.swing.JTable;
    10 import javax.swing.table.TableCellEditor;
    11 
    12 import org.openstreetmap.josm.data.osm.Relation;
    13 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    14 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
    15 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
    16 
    17 /**
    18  * The {@link CellEditor} for the role cell in the table. Supports autocompletion.
    19  */
    20 public class MemberRoleCellEditor extends AbstractCellEditor implements TableCellEditor {
    21     private final AutoCompletingTextField editor;
    22     private final AutoCompletionManager autoCompletionManager;
    23     private final transient Relation relation;
    24 
    25     /** user input is matched against this list of auto completion items */
    26     private final AutoCompletionList autoCompletionList;
    27 
    28     /**
    29      * Constructs a new {@code MemberRoleCellEditor}.
    30      * @param autoCompletionManager the auto completion manager. Must not be null
    31      * @param relation the relation. Can be null
    32      * @since 13675
    33      */
    34     public MemberRoleCellEditor(AutoCompletionManager autoCompletionManager, Relation relation) {
    35         this.autoCompletionManager = autoCompletionManager;
    36         this.relation = relation;
    37         editor = new AutoCompletingTextField(0, false);
    38         editor.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
    39         autoCompletionList = new AutoCompletionList();
    40         editor.setAutoCompletionList(autoCompletionList);
    41     }
    42 
    43     @Override
    44     public Component getTableCellEditorComponent(JTable table,
    45             Object value, boolean isSelected, int row, int column) {
    46 
    47         String role = (String) value;
    48         editor.setText(role);
    49         autoCompletionList.clear();
    50         autoCompletionManager.populateWithMemberRoles(autoCompletionList, relation);
    51         return editor;
    52     }
    53 
    54     @Override
    55     public Object getCellEditorValue() {
    56         return editor.getText();
    57     }
    58 
    59     /**
    60      * Returns the edit field for this cell editor.
    61      * @return the edit field for this cell editor
    62      */
    63     public AutoCompletingTextField getEditor() {
    64         return editor;
    65     }
    66 }
  • src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java

    Property changes on: src/org/openstreetmap/josm/gui/dialogs/relation/MemberRoleCellEditor.java
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    2020import javax.swing.SwingUtilities;
    2121import javax.swing.event.ListSelectionEvent;
    2222import javax.swing.event.ListSelectionListener;
     23import javax.swing.table.TableCellEditor;
    2324
    2425import org.openstreetmap.josm.actions.AbstractShowHistoryAction;
    2526import org.openstreetmap.josm.actions.AutoScaleAction;
     
    2728import org.openstreetmap.josm.actions.HistoryInfoAction;
    2829import org.openstreetmap.josm.actions.ZoomToAction;
    2930import org.openstreetmap.josm.data.osm.OsmPrimitive;
    30 import org.openstreetmap.josm.data.osm.Relation;
    3131import org.openstreetmap.josm.data.osm.RelationMember;
    3232import org.openstreetmap.josm.data.osm.Way;
    3333import org.openstreetmap.josm.gui.MainApplication;
     
    4141import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
    4242import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
    4343import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    44 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
    4544import org.openstreetmap.josm.gui.util.HighlightHelper;
    4645import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTable;
    4746import org.openstreetmap.josm.spi.preferences.Config;
     
    6059     * constructor for relation member table
    6160     *
    6261     * @param layer the data layer of the relation. Must not be null
    63      * @param relation the relation. Can be null
     62     * @param roleCellEditor the role editor combobox
    6463     * @param model the table model
    6564     */
    66     public MemberTable(OsmDataLayer layer, Relation relation, MemberTableModel model) {
    67         super(model, new MemberTableColumnModel(AutoCompletionManager.of(layer.data), relation), model.getSelectionModel());
     65    public MemberTable(OsmDataLayer layer, TableCellEditor roleCellEditor, MemberTableModel model) {
     66        super(model, new MemberTableColumnModel(roleCellEditor), model.getSelectionModel());
    6867        setLayer(layer);
    6968        model.addMemberModelListener(this);
    7069
    71         MemberRoleCellEditor ce = (MemberRoleCellEditor) getColumnModel().getColumn(0).getCellEditor();
    72         setRowHeight(ce.getEditor().getPreferredSize().height);
     70        setRowHeight(roleCellEditor.getTableCellEditorComponent(this, "", false, 0, 0).getPreferredSize().height);
    7371        setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    7472        setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    7573        putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
  • src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableColumnModel.java

     
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    66import javax.swing.table.DefaultTableColumnModel;
     7import javax.swing.table.TableCellEditor;
    78import javax.swing.table.TableColumn;
    89
    9 import org.openstreetmap.josm.data.osm.Relation;
    10 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
    11 
    1210/**
    1311 * This is the column model for the {@link MemberTable}
    1412 */
     
    1614
    1715    /**
    1816     * Constructs a new {@code MemberTableColumnModel}.
    19      * @param autoCompletionManager the auto completion manager. Must not be null
    20      * @param relation the relation. Can be null
     17     * @param roleCellEditor the role editor combobox
    2118     * @since 13675
    2219     */
    23     public MemberTableColumnModel(AutoCompletionManager autoCompletionManager, Relation relation) {
     20    public MemberTableColumnModel(TableCellEditor roleCellEditor) {
    2421        TableColumn col;
    2522
    2623        // column 0 - the member role
     
    2926        col.setResizable(true);
    3027        col.setPreferredWidth(100);
    3128        col.setCellRenderer(new MemberTableRoleCellRenderer());
    32         col.setCellEditor(new MemberRoleCellEditor(autoCompletionManager, relation));
     29        col.setCellEditor(roleCellEditor);
    3330        addColumn(col);
    3431
    3532        // column 1 - the member
  • src/org/openstreetmap/josm/gui/dialogs/relation/actions/IRelationEditorActionAccess.java

     
    22package org.openstreetmap.josm.gui.dialogs.relation.actions;
    33
    44import javax.swing.Action;
     5import javax.swing.JTextField;
    56
    67import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor;
    78import org.openstreetmap.josm.gui.dialogs.relation.MemberTable;
     
    910import org.openstreetmap.josm.gui.dialogs.relation.SelectionTable;
    1011import org.openstreetmap.josm.gui.dialogs.relation.SelectionTableModel;
    1112import org.openstreetmap.josm.gui.tagging.TagEditorModel;
    12 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    1313
    1414/**
    1515 * This interface provides access to the relation editor for actions.
     
    6969     * Get the text field that is used to edit the role.
    7070     * @return The role text field.
    7171     */
    72     AutoCompletingTextField getTextFieldRole();
     72    JTextField getTextFieldRole();
    7373
    7474    /**
    7575     * Tells the member table editor to stop editing and accept any partially edited value as the value of the editor.
  • src/org/openstreetmap/josm/gui/dialogs/relation/actions/SavingAction.java

     
    88import java.util.List;
    99
    1010import javax.swing.JOptionPane;
     11import javax.swing.JTextField;
    1112import javax.swing.SwingUtilities;
    1213
    1314import org.openstreetmap.josm.command.AddCommand;
     
    2728import org.openstreetmap.josm.gui.dialogs.relation.RelationDialogManager;
    2829import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
    2930import org.openstreetmap.josm.gui.tagging.TagEditorModel;
    30 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    3131import org.openstreetmap.josm.tools.ImageProvider;
    3232import org.openstreetmap.josm.tools.Utils;
    3333
     
    3838abstract class SavingAction extends AbstractRelationEditorAction {
    3939    private static final long serialVersionUID = 1L;
    4040
    41     protected final AutoCompletingTextField tfRole;
     41    protected final JTextField tfRole;
    4242
    4343    protected SavingAction(IRelationEditorActionAccess editorAccess, IRelationEditorUpdateOn... updateOn) {
    4444        super(editorAccess, updateOn);
  • src/org/openstreetmap/josm/gui/dialogs/relation/actions/SetRoleAction.java

     
    77import java.awt.event.ActionEvent;
    88
    99import javax.swing.JOptionPane;
     10import javax.swing.JTextField;
    1011import javax.swing.event.DocumentEvent;
    1112import javax.swing.event.DocumentListener;
    1213
    1314import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
    1415import org.openstreetmap.josm.gui.MainApplication;
    15 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    1616import org.openstreetmap.josm.tools.ImageProvider;
    1717import org.openstreetmap.josm.tools.Utils;
    1818
     
    2323public class SetRoleAction extends AbstractRelationEditorAction implements DocumentListener {
    2424    private static final long serialVersionUID = 1L;
    2525
    26     private final transient AutoCompletingTextField tfRole;
     26    private final transient JTextField tfRole;
    2727
    2828    /**
    2929     * Constructs a new {@code SetRoleAction}.
     
    3232    public SetRoleAction(IRelationEditorActionAccess editorAccess) {
    3333        super(editorAccess);
    3434        this.tfRole = editorAccess.getTextFieldRole();
    35         putValue(SHORT_DESCRIPTION, tr("Sets a role for the selected members"));
     35        putValue(SHORT_DESCRIPTION, tr("Apply the role to the selected members"));
    3636        new ImageProvider("apply").getResource().attachImageIcon(this);
    3737        putValue(NAME, tr("Apply Role"));
    3838        updateEnabledState();
  • src/org/openstreetmap/josm/gui/tagging/ac/AutoCompComboBox.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.tagging.ac;
    33
     4import java.awt.Component;
    45import java.awt.im.InputContext;
    56import java.util.Collection;
    67import java.util.Collections;
     8import java.util.EventObject;
    79import java.util.LinkedList;
    810import java.util.Locale;
    911
    1012import javax.swing.ComboBoxEditor;
     13import javax.swing.JTable;
     14import javax.swing.event.CellEditorListener;
     15import javax.swing.table.TableCellEditor;
    1116
     17import org.openstreetmap.josm.gui.util.CellEditorSupport;
    1218import org.openstreetmap.josm.gui.widgets.JosmComboBox;
    1319import org.openstreetmap.josm.tools.Logging;
    1420
     
    2430 * @param <E> the type of the combobox entries
    2531 * @since 18173
    2632 */
    27 public class AutoCompComboBox<E> extends JosmComboBox<E> implements AutoCompListener {
     33public class AutoCompComboBox<E> extends JosmComboBox<E> implements TableCellEditor, AutoCompListener {
    2834
    2935    /** force a different keyboard input locale for the editor */
    3036    private boolean useFixedLocale;
     
    4854        setEditable(true);
    4955        getEditorComponent().setModel(model);
    5056        getEditorComponent().addAutoCompListener(this);
     57        tableCellEditorSupport = new CellEditorSupport(this);
    5158    }
    5259
    5360    /**
     
    104111     * @param elems The string items to set
    105112     * @deprecated Has been moved to the model, where it belongs. Use
    106113     *     {@link org.openstreetmap.josm.gui.widgets.HistoryComboBoxModel#addAllStrings} instead. Probably you want to use
    107      *     {@link org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel.Preferences#load} and
    108      *     {@link org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel.Preferences#save}.
     114     *     {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#load} and
     115     *     {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#save}.
    109116     */
    110117    @Deprecated
    111118    public void setPossibleItems(Collection<E> elems) {
     
    122129     * @since 15011
    123130     * @deprecated Has been moved to the model, where it belongs. Use
    124131     *     {@link org.openstreetmap.josm.gui.widgets.HistoryComboBoxModel#addAllStrings} instead. Probably you want to use
    125      *     {@link org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel.Preferences#load} and
    126      *     {@link org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel.Preferences#save}.
     132     *     {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#load} and
     133     *     {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#save}.
    127134     */
    128135    @Deprecated
    129136    public void setPossibleItemsTopDown(Collection<E> elems) {
     
    201208    public void autoCompPerformed(AutoCompEvent e) {
    202209        autocomplete(e.getItem());
    203210    }
     211
     212    /* ------------------------------------------------------------------------------------ */
     213    /* TableCellEditor interface                                                            */
     214    /* ------------------------------------------------------------------------------------ */
     215
     216    private transient CellEditorSupport tableCellEditorSupport;
     217    private String originalValue;
     218
     219    @Override
     220    public void addCellEditorListener(CellEditorListener l) {
     221        tableCellEditorSupport.addCellEditorListener(l);
     222    }
     223
     224    protected void rememberOriginalValue(String value) {
     225        this.originalValue = value;
     226    }
     227
     228    protected void restoreOriginalValue() {
     229        setText(originalValue);
     230    }
     231
     232    @Override
     233    public void removeCellEditorListener(CellEditorListener l) {
     234        tableCellEditorSupport.removeCellEditorListener(l);
     235    }
     236
     237    @Override
     238    public void cancelCellEditing() {
     239        restoreOriginalValue();
     240        tableCellEditorSupport.fireEditingCanceled();
     241    }
     242
     243    @Override
     244    public Object getCellEditorValue() {
     245        return getText();
     246    }
     247
     248    @Override
     249    public boolean isCellEditable(EventObject anEvent) {
     250        return true;
     251    }
     252
     253    @Override
     254    public boolean shouldSelectCell(EventObject anEvent) {
     255        return true;
     256    }
     257
     258    @Override
     259    public boolean stopCellEditing() {
     260        tableCellEditorSupport.fireEditingStopped();
     261        return true;
     262    }
     263
     264    @Override
     265    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
     266        setText(value == null ? "" : value.toString());
     267        rememberOriginalValue(getText());
     268        return this;
     269    }
    204270}
  • src/org/openstreetmap/josm/gui/tagging/ac/AutoCompListener.java

     
    44import java.util.EventListener;
    55
    66/**
    7  * The listener interface for receiving autoComp events.
    8  * The class that is interested in processing an autoComp event
    9  * implements this interface, and the object created with that
    10  * class is registered with a component, using the component's
    11  * <code>addAutoCompListener</code> method. When the autoComp event
    12  * occurs, that object's <code>autoCompPerformed</code> method is
    13  * invoked.
     7 * The listener interface for receiving AutoCompEvent events.
     8 * <p>
     9 * The class that is interested in processing an {@link AutoCompEvent} implements this interface,
     10 * and the object created with that class is registered with an autocompleting component using the
     11 * autocompleting component's {@link AutoCompTextField#addAutoCompListener addAutoCompListener}
     12 * method.
     13 * <p>
     14 * Before the autocompletion searches for candidates, the listener's {@code autoCompBefore} method
     15 * is invoked. It can be used to initialize the {@link AutoCompComboBoxModel}. After the
     16 * autocompletion occured the listener's {@code autoCompPerformed} method is invoked. It is used eg.
     17 * for adjusting the selection of an {@link AutoCompComboBox} after its {@link AutoCompTextField}
     18 * has autocompleted.
    1419 *
    15  * @see AutoCompEvent
    1620 * @since 18221
    1721 */
    1822public interface AutoCompListener extends EventListener {
  • src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletionManager.java

     
    66import java.util.Collection;
    77import java.util.Collections;
    88import java.util.Comparator;
     9import java.util.EnumSet;
    910import java.util.HashMap;
    10 import java.util.HashSet;
    1111import java.util.LinkedHashSet;
    1212import java.util.List;
    1313import java.util.Map;
     
    4040import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
    4141import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    4242import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
     43import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
    4344import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
    4445import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role;
    4546import org.openstreetmap.josm.tools.CheckParameterUtil;
     
    4748import org.openstreetmap.josm.tools.Utils;
    4849
    4950/**
    50  * AutoCompletionManager holds a cache of keys with a list of
    51  * possible auto completion values for each key.
    52  *
     51 * AutoCompletionManager holds a cache of keys with a list of possible auto completion values for
     52 * each key.
     53 * <p>
    5354 * Each DataSet can be assigned one AutoCompletionManager instance such that
    5455 * <ol>
    5556 *   <li>any key used in a tag in the data set is part of the key list in the cache</li>
     
    5657 *   <li>any value used in a tag for a specific key is part of the autocompletion list of this key</li>
    5758 * </ol>
    5859 *
    59  * Building up auto completion lists should not
    60  * slow down tabbing from input field to input field. Looping through the complete
    61  * data set in order to build up the auto completion list for a specific input
    62  * field is not efficient enough, hence this cache.
    63  *
    64  * TODO: respect the relation type for member role autocompletion
     60 * Building up auto completion lists should not slow down tabbing from input field to input field.
     61 * Looping through the complete data set in order to build up the auto completion list for a
     62 * specific input field is not efficient enough, hence this cache.
    6563 */
    6664public class AutoCompletionManager implements DataSetListener {
    6765
     
    105103        }
    106104    }
    107105
     106    static final Comparator<AutoCompletionItem> ALPHABETIC_AUTOCOMPLETIONITEM_COMPARATOR =
     107        (o1, o2) -> String.CASE_INSENSITIVE_ORDER.compare(o1.getValue(), o2.getValue());
     108
     109
    108110    /** If the dirty flag is set true, a rebuild is necessary. */
    109111    protected boolean dirty;
    110112    /** The data set that is managed */
     
    128130    static final Set<UserInputTag> USER_INPUT_TAG_CACHE = new LinkedHashSet<>();
    129131
    130132    /**
    131      * the cached list of member roles
    132      * only accessed by getRoleCache(), rebuild() and cacheRelationMemberRoles()
    133      * use getRoleCache() accessor
     133     * The cached list of member roles by relation {@code type}.
     134     * <p>
     135     * Used by rebuild() and cacheRelationMemberRoles().  Use the {@link #getRoleCache} accessor.
    134136     */
    135     protected Set<String> roleCache;
     137    protected final MultiMap<String, Role> ROLE_CACHE = new MultiMap<>();
    136138
    137     /**
    138      * the same as roleCache but for the preset roles can be accessed directly
    139      */
    140     static final Set<String> PRESET_ROLE_CACHE = new HashSet<>();
    141 
    142139    private static final Map<DataSet, AutoCompletionManager> INSTANCES = new HashMap<>();
    143140
    144141    /**
     
    159156        return tagCache;
    160157    }
    161158
    162     protected Set<String> getRoleCache() {
     159    protected MultiMap<String, Role> getRoleCache() {
    163160        if (dirty) {
    164161            rebuild();
    165162            dirty = false;
    166163        }
    167         return roleCache;
     164        return ROLE_CACHE;
    168165    }
    169166
    170167    /**
     
    172169     */
    173170    protected void rebuild() {
    174171        tagCache = new MultiMap<>();
    175         roleCache = new HashSet<>();
     172        ROLE_CACHE.clear();
    176173        cachePrimitives(ds.allNonDeletedCompletePrimitives());
    177174    }
    178175
     
    196193    }
    197194
    198195    /**
     196     * Returns the relation type.
     197     * <p>
     198     * This is used to categorize the relations in the dataset.  A relation with the keys:
     199     * <ul>
     200     * <li>type=route
     201     * <li>route=hiking
     202     * </ul>
     203     * will return a relation type of {@code "route.hiking"}.
     204     *
     205     * @param tags the tags on the relation
     206     * @return the relation type or {@code ""}
     207     */
     208    private String getRelationType(Map<String, String> tags) {
     209        String type = tags.get("type");
     210        if (type == null) return "";
     211        String subtype = tags.get(type);
     212        if (subtype == null) return type;
     213        return type + "." + subtype;
     214    }
     215
     216    /**
    199217     * Caches all member roles of the relation <code>relation</code>
    200218     *
    201219     * @param relation the relation
    202220     */
    203221    protected void cacheRelationMemberRoles(Relation relation) {
    204         for (RelationMember m: relation.getMembers()) {
    205             if (m.hasRole()) {
    206                 roleCache.add(m.getRole());
     222        String type = getRelationType(relation.getKeys());
     223        for (RelationMember member: relation.getMembers()) {
     224            if (type != null && member.hasRole()) {
     225                Role role = new Role();
     226                role.key = member.getRole();
     227                role.types = EnumSet.of(TaggingPresetType.forPrimitiveType(member.getDisplayType()));
     228                ROLE_CACHE.put(type, role);
    207229            }
    208230        }
    209231    }
     
    263285     *
    264286     * @return the list of member roles
    265287     */
    266     public List<String> getMemberRoles() {
    267         return new ArrayList<>(getRoleCache());
     288    public List<Role> getMemberRoles() {
     289        return new ArrayList<>(getRoleCache().getAllValues());
    268290    }
    269291
    270292    /**
     
    273295     * @param list the list to populate
    274296     */
    275297    public void populateWithMemberRoles(AutoCompletionList list) {
    276         list.add(TaggingPresets.getPresetRoles(), AutoCompletionPriority.IS_IN_STANDARD);
    277         list.add(getRoleCache(), AutoCompletionPriority.IS_IN_DATASET);
     298        list.add(TaggingPresets.getPresetRoles().stream().map(r -> r.key).collect(Collectors.toList()), AutoCompletionPriority.IS_IN_STANDARD);
     299        list.add(getRoleCache().getAllValues().stream().map(r -> r.key).collect(Collectors.toList()), AutoCompletionPriority.IS_IN_DATASET);
    278300    }
    279301
    280302    /**
     
    303325    }
    304326
    305327    /**
     328     * Return true if the role may be applied to all of the types.
     329     * <p>
     330     * Returns true if {@code role.types} contains all elements of {@code types}.
     331     *
     332     * @param role The role.
     333     * @param types The types.
     334     * @return True if the role may be applied to all of the types.
     335     */
     336    private boolean appliesTo(Role role, EnumSet<TaggingPresetType> types) {
     337        return role.types.containsAll(types);
     338    }
     339
     340    /**
     341     * Returns all suitable roles for a given relation member.
     342     * <p>
     343     * This function implements the relation role filter. It gets the suitable roles from
     344     * <ul>
     345     * <li>matching tagging presets and from
     346     * <li>relations of the same type in the dataset.
     347     * </ul>
     348     * If no matching preset is found, returns all roles of all presets.
     349     *
     350     * @param keys current keys in the tag editor panel, used to determine the relation type
     351     * @param roles all roles of all members in the member editor panel
     352     * @param types the union of the types of the selected roles in the member editor panel
     353     * @return list of {@link AutoCompletionItem}s, alphabetically sorted
     354     * @since xxx
     355     */
     356    public List<AutoCompletionItem> getRolesForRelation(Map<String, String> keys, Set<Role> roles, EnumSet<TaggingPresetType> types) {
     357        Map<String, AutoCompletionPriority> map = new HashMap<>();
     358
     359        // always add the empty role
     360        map.merge("", AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith);
     361
     362        // harvest the dataset
     363        // the roles already present in the member editor
     364        for (Role role : roles) {
     365            if (appliesTo(role, types))
     366                map.merge(role.key, AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith);
     367        }
     368        // the roles on existing relations of the same type (or all relations)
     369        String type = getRelationType(keys);
     370        for (Role role : type.isEmpty() ? getRoleCache().getAllValues() : getRoleCache().getValues(type)) {
     371            if (appliesTo(role, types))
     372                map.merge(role.key, AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith);
     373        }
     374
     375        // harvest the presets
     376        Collection<TaggingPreset> presets = TaggingPresets.getMatchingPresets(null, keys, false);
     377        if (presets.isEmpty()) {
     378            presets = TaggingPresets.getTaggingPresets();
     379        }
     380        // all roles of all matched presets (or all presets)
     381        for (TaggingPreset preset : presets) {
     382            if (preset.roles != null) {
     383                for (Role role : preset.roles.roles) {
     384                    if (appliesTo(role, types))
     385                        map.merge(role.key, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith);
     386                }
     387            }
     388        }
     389        return map.entrySet().stream().map(e -> new AutoCompletionItem(e.getKey(), e.getValue()))
     390            .sorted(ALPHABETIC_AUTOCOMPLETIONITEM_COMPARATOR).collect(Collectors.toList());
     391    }
     392
     393    /**
    306394     * Populates the an {@link AutoCompletionList} with the currently cached tag keys
    307395     *
    308396     * @param list the list to populate
     
    503591                    MainApplication.getLayerManager().removeLayerChangeListener(this);
    504592                    dirty = true;
    505593                    tagCache = null;
    506                     roleCache = null;
     594                    ROLE_CACHE.clear();
    507595                    ds = null;
    508596                }
    509597            }
  • src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetItem.java

     
    55import static org.openstreetmap.josm.tools.I18n.trc;
    66
    77import java.io.File;
    8 import java.util.Arrays;
    98import java.util.Collection;
    109import java.util.Collections;
    1110import java.util.EnumSet;
     
    2221import org.openstreetmap.josm.data.osm.Tag;
    2322import org.openstreetmap.josm.data.preferences.BooleanProperty;
    2423import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
    25 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    26 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
    2724import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
    2825import org.openstreetmap.josm.gui.util.LruCache;
    2926import org.openstreetmap.josm.tools.ImageProvider;
     
    4441     */
    4542    protected static final BooleanProperty DISPLAY_KEYS_AS_HINT = new BooleanProperty("taggingpreset.display-keys-as-hint", true);
    4643
    47     protected void initAutoCompletionField(AutoCompletingTextField field, String... key) {
    48         initAutoCompletionField(field, Arrays.asList(key));
    49     }
    50 
    51     protected void initAutoCompletionField(AutoCompletingTextField field, List<String> keys) {
    52         DataSet data = OsmDataManager.getInstance().getEditDataSet();
    53         if (data == null) {
    54             return;
    55         }
    56         AutoCompletionList list = new AutoCompletionList();
    57         AutoCompletionManager.of(data).populateWithTagValues(list, keys);
    58         field.setAutoCompletionList(list);
    59     }
    60 
    6144    /**
    6245     * Returns all cached {@link AutoCompletionItem}s for given keys.
    6346     *
  • src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresets.java

     
    4545    /** cache for key/value pairs found in the preset */
    4646    private static final MultiMap<String, String> PRESET_TAG_CACHE = new MultiMap<>();
    4747    /** cache for roles found in the preset */
    48     private static final Set<String> PRESET_ROLE_CACHE = new HashSet<>();
     48    private static final Map<String, Role> PRESET_ROLE_CACHE = new HashMap<>();
    4949
    5050    /** The collection of listeners */
    5151    private static final Collection<TaggingPresetListener> listeners = new ArrayList<>();
     
    167167            Roles r = (Roles) item;
    168168            for (Role i : r.roles) {
    169169                if (i.key != null) {
    170                     PRESET_ROLE_CACHE.add(i.key);
     170                    PRESET_ROLE_CACHE.put(i.key, i);
    171171                }
    172172            }
    173173        } else if (item instanceof CheckGroup) {
     
    189189     * Replies a set of all roles in the tagging presets.
    190190     * @return a set of all roles in the tagging presets.
    191191     */
    192     public static Set<String> getPresetRoles() {
    193         return Collections.unmodifiableSet(PRESET_ROLE_CACHE);
     192    public static Set<Role> getPresetRoles() {
     193        return new HashSet<>(PRESET_ROLE_CACHE.values());
    194194    }
    195195
    196196    /**
  • src/org/openstreetmap/josm/gui/tagging/presets/items/Roles.java

     
    178178        }
    179179
    180180        @Override
     181        public int hashCode() {
     182            final int prime = 31;
     183            int result = 1;
     184            result = prime * result + ((key == null) ? 0 : key.hashCode());
     185            result = prime * result + ((types == null) ? 0 : types.hashCode());
     186            return result;
     187        }
     188
     189        @Override
     190        public boolean equals(Object obj) {
     191            if (this == obj)
     192                return true;
     193            if (obj == null)
     194                return false;
     195            if (getClass() != obj.getClass())
     196                return false;
     197            Role other = (Role) obj;
     198            if (key == null) {
     199                if (other.key != null)
     200                    return false;
     201            } else if (!key.equals(other.key))
     202                return false;
     203            if (types == null) {
     204                if (other.types != null)
     205                    return false;
     206            } else if (!types.equals(other.types))
     207                return false;
     208            return true;
     209        }
     210
     211        @Override
    181212        public String toString() {
    182213            return "Role [key=" + key + ", text=" + text + ']';
    183214        }
  • src/org/openstreetmap/josm/gui/widgets/HistoryComboBox.java

     
    3939     * @return the items as strings
    4040     * @deprecated Has been moved to the model, where it belongs. Use
    4141     *     {@link HistoryComboBoxModel#asStringList} instead. Probably you want to use
    42      *     {@link org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel.Preferences#load} and
    43      *     {@link org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel.Preferences#save}.
     42     *     {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#load} and
     43     *     {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#save}.
    4444     */
    4545    @Deprecated
    4646    public List<String> getHistory() {
  • src/org/openstreetmap/josm/gui/widgets/JosmComboBoxModel.java

     
    5454     * {@link javax.swing.DefaultComboBoxModel}.
    5555     *
    5656     * @param element the element to get the index of
    57      * @return an int representing the index position, where 0 is the first position
     57     * @return the index of the first occurrence of the specified element in this model,
     58     *         or -1 if this model does not contain the element
    5859     */
    5960    public int getIndexOf(E element) {
    6061        return elements.indexOf(element);
    6162    }
    6263
     64    protected void doAddElement(E element) {
     65        if (element != null && (maxSize == -1 || getSize() < maxSize)) {
     66            elements.add(element);
     67        }
     68    }
     69
    6370    //
    6471    // interface java.lang.Iterable
    6572    //
     
    7885     */
    7986    @Override
    8087    public void addElement(E element) {
    81         if (element != null && (maxSize == -1 || getSize() < maxSize)) {
    82             elements.add(element);
    83         }
     88        doAddElement(element);
     89        fireIntervalAdded(this, elements.size() - 1, elements.size() - 1);
    8490    }
    8591
    8692    @Override
    8793    public void removeElement(Object elem) {
    88         elements.remove(elem);
     94        int index = elements.indexOf(elem);
     95        if (elem == selected) {
     96            if (index == 0) {
     97                setSelectedItem(getSize() == 1 ? null : getElementAt(index + 1));
     98            } else {
     99                setSelectedItem(getElementAt(index - 1));
     100            }
     101        }
     102        if (elements.remove(elem))
     103            fireIntervalRemoved(this, index, index);
    89104    }
    90105
    91106    @Override
     
    114129            removeElementAt(getSize() - 1);
    115130        }
    116131        elements.add(index, element);
     132        fireIntervalAdded(this, index, index);
    117133    }
    118134
    119135    //
     
    166182     * @param elems The elements to add.
    167183     */
    168184    public void addAllElements(Collection<E> elems) {
    169         elems.forEach(e -> addElement(e));
     185        int index0 = elements.size();
     186        elems.forEach(e -> doAddElement(e));
     187        int index1 = elements.size() - 1;
     188        if (index0 <= index1)
     189            fireIntervalAdded(this, index0, index1);
    170190    }
    171191
    172192    /**
     
    177197     *               {@code String}.
    178198     */
    179199    public void addAllElements(Collection<String> strings, Function<String, E> buildE) {
    180         strings.forEach(s -> addElement(buildE.apply(s)));
     200        int index0 = elements.size();
     201        strings.forEach(s -> doAddElement(buildE.apply(s)));
     202        int index1 = elements.size() - 1;
     203        if (index0 <= index1)
     204            fireIntervalAdded(this, index0, index1);
    181205    }
    182206
    183207    /**
     208     * Replaces all current elements with elements from the collection.
     209     * <p>
     210     * This is the same as {@link #removeAllElements} followed by {@link #addAllElements} but
     211     * minimizes event firing and tries to keep the current selection.  Use this when all elements
     212     * are reinitialized programmatically like in an {@code autoCompBefore} event.
     213     *
     214     * @param newElements The new elements.
     215     */
     216    public void replaceAllElements(Collection<E> newElements) {
     217        Object oldSelected = selected;
     218        int index0 = elements.size();
     219        elements.clear();
     220        newElements.forEach(e -> doAddElement(e));
     221        int index1 = elements.size();
     222        int index2 = Math.min(index0, index1);
     223        if (0 < index2) {
     224            fireContentsChanged(this, 0, index2 - 1);
     225        }
     226        if (index2 < index0) {
     227            fireIntervalRemoved(this, index2, index0 - 1);
     228        }
     229        if (index2 < index1) {
     230            fireIntervalAdded(this, index2, index1 - 1);
     231        }
     232        // re-select the old selection if possible
     233        int index = elements.indexOf(oldSelected);
     234        setSelectedItem(index == -1 ? null : getElementAt(index));
     235    }
     236
     237    /**
    184238     * Adds an element to the top of the list.
    185239     * <p>
    186240     * If the element is already in the model, moves it to the top.  If the model gets too big,
     
    204258     */
    205259    public void removeAllElements() {
    206260        if (!elements.isEmpty()) {
    207             int firstIndex = 0;
    208261            int lastIndex = elements.size() - 1;
    209262            elements.clear();
    210263            selected = null;
    211             fireIntervalRemoved(this, firstIndex, lastIndex);
     264            fireIntervalRemoved(this, 0, lastIndex);
    212265        } else {
    213266            selected = null;
    214267        }
  • src/org/openstreetmap/josm/tools/MultiMap.java

     
    124124    }
    125125
    126126    /**
     127     * Like getValues, but returns all values for all keys.
     128     * @return the set of all values or an empty set
     129     */
     130    public Set<B> getAllValues() {
     131        return map.entrySet().stream().flatMap(e -> e.getValue().stream()).collect(Collectors.toSet());
     132    }
     133
     134    /**
    127135     * Returns {@code true} if this map contains no key-value mappings.
    128136     * @return {@code true} if this map contains no key-value mappings
    129137     * @see Map#isEmpty()
  • test/unit/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditorTest.java

     
    2020import org.openstreetmap.josm.data.osm.Way;
    2121import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2222import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
    23 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    2423import org.openstreetmap.josm.testutils.JOSMTestRules;
    2524import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
    2625
     
    123122        OsmDataLayer layer = new OsmDataLayer(ds, "test", null);
    124123        IRelationEditor re = newRelationEditor(relation, layer);
    125124
    126         AutoCompletingTextField tfRole = GenericRelationEditor.buildRoleTextField(re);
    127         assertNotNull(tfRole);
    128 
    129125        TagEditorPanel tagEditorPanel = new TagEditorPanel(relation, null);
    130126
    131127        JPanel top = GenericRelationEditor.buildTagEditorPanel(tagEditorPanel);
  • test/unit/org/openstreetmap/josm/gui/dialogs/relation/actions/AbstractRelationEditorActionTest.java

     
    1212import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1313import org.openstreetmap.josm.data.osm.Relation;
    1414import org.openstreetmap.josm.data.osm.Tag;
     15import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
    1516import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditorTest;
    1617import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor;
    1718import org.openstreetmap.josm.gui.dialogs.relation.MemberTable;
     
    2021import org.openstreetmap.josm.gui.dialogs.relation.SelectionTableModel;
    2122import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2223import org.openstreetmap.josm.gui.tagging.TagEditorModel;
    23 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
     24import org.openstreetmap.josm.gui.tagging.ac.AutoCompEvent;
     25import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener;
     26import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField;
    2427import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler;
    2528import org.openstreetmap.josm.testutils.JOSMTestRules;
    2629
     
    3134 * @author Michael Zangl
    3235 */
    3336@Disabled
    34 public abstract class AbstractRelationEditorActionTest {
     37public abstract class AbstractRelationEditorActionTest implements AutoCompListener {
    3538    /**
    3639     * Platform for tooltips.
    3740     */
     
    4649    private IRelationEditor editor;
    4750    private MemberTable memberTable;
    4851    private MemberTableModel memberTableModel;
    49     private AutoCompletingTextField tfRole;
     52    private AutoCompTextField<AutoCompletionItem> tfRole;
    5053    private TagEditorModel tagModel;
    5154
    5255    protected final IRelationEditorActionAccess relationEditorAccess = new IRelationEditorActionAccess() {
    5356
    5457        @Override
    55         public AutoCompletingTextField getTextFieldRole() {
     58        public AutoCompTextField<AutoCompletionItem> getTextFieldRole() {
    5659            return tfRole;
    5760        }
    5861
     
    109112        selectionTableModel = new SelectionTableModel(layer);
    110113        selectionTable = new SelectionTable(selectionTableModel, memberTableModel);
    111114        editor = GenericRelationEditorTest.newRelationEditor(orig, layer);
    112         tfRole = new AutoCompletingTextField();
     115        tfRole = new AutoCompTextField<>();
    113116        tagModel = new TagEditorModel();
    114         memberTable = new MemberTable(layer, editor.getRelation(), memberTableModel);
     117        memberTable = new MemberTable(layer, this, memberTableModel);
    115118    }
     119
     120    @Override
     121    public void autoCompBefore(AutoCompEvent e) {
     122    }
     123
     124    @Override
     125    public void autoCompPerformed(AutoCompEvent e) {
     126    }
    116127}