Ticket #21227: 21227-3.patch
| File 21227-3.patch, 58.6 KB (added by , 5 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
14 14 import java.awt.datatransfer.Clipboard; 15 15 import java.awt.datatransfer.FlavorListener; 16 16 import java.awt.event.ActionEvent; 17 import java.awt.event.FocusAdapter;18 import java.awt.event.FocusEvent;19 17 import java.awt.event.InputEvent; 20 18 import java.awt.event.KeyEvent; 21 19 import java.awt.event.MouseAdapter; … … 27 25 import java.util.Collection; 28 26 import java.util.Collections; 29 27 import java.util.EnumSet; 28 import java.util.HashMap; 29 import java.util.HashSet; 30 30 import java.util.List; 31 import java.util.Map; 31 32 import java.util.Set; 32 33 import java.util.stream.Collectors; 33 34 … … 35 36 import javax.swing.BorderFactory; 36 37 import javax.swing.InputMap; 37 38 import javax.swing.JButton; 39 import javax.swing.JCheckBox; 38 40 import javax.swing.JComponent; 39 41 import javax.swing.JLabel; 40 42 import javax.swing.JMenuItem; … … 45 47 import javax.swing.JSplitPane; 46 48 import javax.swing.JTabbedPane; 47 49 import javax.swing.JTable; 50 import javax.swing.JTextField; 48 51 import javax.swing.JToolBar; 49 52 import javax.swing.KeyStroke; 53 import javax.swing.event.PopupMenuEvent; 54 import javax.swing.event.PopupMenuListener; 50 55 51 56 import org.openstreetmap.josm.actions.JosmAction; 52 57 import org.openstreetmap.josm.command.ChangeMembersCommand; … … 58 63 import org.openstreetmap.josm.data.osm.Relation; 59 64 import org.openstreetmap.josm.data.osm.RelationMember; 60 65 import org.openstreetmap.josm.data.osm.Tag; 66 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem; 61 67 import org.openstreetmap.josm.data.validation.tests.RelationChecker; 62 68 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 63 69 import org.openstreetmap.josm.gui.MainApplication; … … 98 104 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 99 105 import org.openstreetmap.josm.gui.tagging.TagEditorModel; 100 106 import org.openstreetmap.josm.gui.tagging.TagEditorPanel; 101 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField; 102 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList; 107 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBox; 108 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel; 109 import org.openstreetmap.josm.gui.tagging.ac.AutoCompEvent; 110 import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener; 111 import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField; 103 112 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 104 113 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 105 114 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler; 106 115 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType; 107 116 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; 117 import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role; 108 118 import org.openstreetmap.josm.gui.util.WindowGeometry; 109 119 import org.openstreetmap.josm.spi.preferences.Config; 110 120 import org.openstreetmap.josm.tools.CheckParameterUtil; 121 import org.openstreetmap.josm.tools.GBC; 111 122 import org.openstreetmap.josm.tools.InputMapUtils; 112 123 import org.openstreetmap.josm.tools.Logging; 113 124 import org.openstreetmap.josm.tools.Shortcut; … … 117 128 * This dialog is for editing relations. 118 129 * @since 343 119 130 */ 120 public class GenericRelationEditor extends RelationEditor implements CommandQueueListener { 131 public 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 121 135 /** the tag table and its model */ 122 136 private final TagEditorPanel tagEditorPanel; 123 137 private final ReferringRelationsBrowser referrerBrowser; … … 131 145 private final SelectionTable selectionTable; 132 146 private final SelectionTableModel selectionTableModel; 133 147 134 private final AutoCompletingTextField tfRole; 148 private final AutoCompletionManager manager; 149 private final AutoCompComboBox<AutoCompletionItem> cbRole; 135 150 136 151 /** 137 152 * the menu item in the windows menu. Required to properly hide on dialog close. … … 172 187 173 188 private Component selectedTabPane; 174 189 private JTabbedPane tabbedPane; 190 private JCheckBox btnFilter = new JCheckBox(tr("Filter")); 175 191 176 192 /** 177 193 * Creates a new relation editor for the given relation. The relation will be saved if the user … … 216 232 populateModels(relation); 217 233 tagEditorPanel.getModel().ensureOneTag(); 218 234 235 manager = AutoCompletionManager.of(this.getLayer().data); 236 219 237 // 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); 221 243 memberTable.addMouseListener(new MemberTableDblClickAdapter()); 222 244 memberTableModel.addMemberModelListener(memberTable); 223 245 224 MemberRoleCellEditor ce = (MemberRoleCellEditor) memberTable.getColumnModel().getColumn(0).getCellEditor();225 246 selectionTable = new SelectionTable(selectionTableModel, memberTableModel); 226 selectionTable.setRowHeight(c e.getEditor().getPreferredSize().height);247 selectionTable.setRowHeight(cbRoleEditor.getPreferredSize().height); 227 248 228 249 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")); 230 255 231 256 JSplitPane pane = buildSplitPane( 232 257 buildTagEditorPanel(tagEditorPanel), 233 buildMemberEditorPanel(leftButtonToolbar , new RelationEditorActionAccess()),258 buildMemberEditorPanel(leftButtonToolbar), 234 259 this); 235 260 pane.setPreferredSize(new Dimension(100, 100)); 236 261 … … 310 335 @Override 311 336 public void actionPerformed(ActionEvent e) { 312 337 super.actionPerformed(e); 313 tfRole.requestFocusInWindow();338 cbRole.requestFocusInWindow(); 314 339 } 315 340 }, "PASTE_MEMBERS", key, getRootPane(), memberTable, selectionTable); 316 341 } … … 446 471 } 447 472 448 473 /** 449 * builds the role text field450 * @param re relation editor451 * @return the role text field452 */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 @Override458 public void focusGained(FocusEvent e) {459 tfRole.selectAll();460 }461 });462 tfRole.setAutoCompletionList(new AutoCompletionList());463 tfRole.addFocusListener(464 new FocusAdapter() {465 @Override466 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 /**480 474 * builds the panel for the relation member editor 481 475 * @param leftButtonToolbar left button toolbar 482 * @param editorAccess The relation editor483 476 * 484 477 * @return the panel for the relation member editor 485 478 */ 486 protected static JPanel buildMemberEditorPanel( 487 LeftButtonToolbar leftButtonToolbar, IRelationEditorActionAccess editorAccess) { 479 protected JPanel buildMemberEditorPanel(LeftButtonToolbar leftButtonToolbar) { 488 480 final JPanel pnl = new JPanel(new GridBagLayout()); 489 final JScrollPane scrollPane = new JScrollPane( editorAccess.getMemberTable());481 final JScrollPane scrollPane = new JScrollPane(memberTable); 490 482 491 483 GridBagConstraints gc = new GridBagConstraints(); 492 484 gc.gridx = 0; … … 518 510 pnl.add(scrollPane, gc); 519 511 520 512 // --- 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) 530 526 ); 531 editorAccess.getTextFieldRole().setEnabled(editorAccess.getMemberTable().getSelectedRowCount() > 0); 527 cbRole.setEnabled(memberTable.getSelectedRowCount() > 0); 528 532 529 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)); 534 532 btnApply.setText(""); 535 p3.add(btnApply );533 p3.add(btnApply, gbc.weight(0, 0).fill(GridBagConstraints.NONE)); 536 534 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 537 541 gc.gridx = 1; 538 542 gc.gridy = 2; 539 543 gc.fill = GridBagConstraints.HORIZONTAL; … … 562 566 gc.anchor = GridBagConstraints.NORTHWEST; 563 567 gc.weightx = 0.0; 564 568 gc.weighty = 1.0; 565 pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar( editorAccess),569 pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar(new RelationEditorActionAccess()), 566 570 ScrollViewport.VERTICAL_DIRECTION), gc); 567 571 568 572 gc.gridx = 1; … … 570 574 gc.weightx = 1.0; 571 575 gc.weighty = 1.0; 572 576 gc.fill = GridBagConstraints.BOTH; 573 pnl2.add(buildSelectionTablePanel( editorAccess.getSelectionTable()), gc);577 pnl2.add(buildSelectionTablePanel(selectionTable), gc); 574 578 575 579 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); 576 580 splitPane.setLeftComponent(pnl); 577 581 splitPane.setRightComponent(pnl2); 578 582 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 }); 588 590 589 591 JPanel pnl3 = new JPanel(new BorderLayout()); 590 592 pnl3.add(splitPane, BorderLayout.CENTER); … … 754 756 clipboard.addFlavorListener(listener); 755 757 } 756 758 } else { 759 Config.getPref().put(PREF_LASTROLE, cbRole.getText()); 760 Config.getPref().putBoolean(PREF_USE_ROLE_FILTER, btnFilter.isSelected()); 761 757 762 // make sure all registered listeners are unregistered 758 763 // 759 764 memberTable.stopHighlighting(); … … 1039 1044 } 1040 1045 1041 1046 @Override 1042 public AutoCompletingTextField getTextFieldRole() {1043 return tfRole;1047 public JTextField getTextFieldRole() { 1048 return cbRole.getEditorComponent(); 1044 1049 } 1045 1046 1050 } 1047 1051 1048 1052 @Override … … 1054 1058 applyAction.updateEnabledState(); 1055 1059 } 1056 1060 } 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 } 1057 1113 } -
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 null31 * @param relation the relation. Can be null32 * @since 1367533 */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 @Override44 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 @Override55 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 editor62 */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
20 20 import javax.swing.SwingUtilities; 21 21 import javax.swing.event.ListSelectionEvent; 22 22 import javax.swing.event.ListSelectionListener; 23 import javax.swing.table.TableCellEditor; 23 24 24 25 import org.openstreetmap.josm.actions.AbstractShowHistoryAction; 25 26 import org.openstreetmap.josm.actions.AutoScaleAction; … … 27 28 import org.openstreetmap.josm.actions.HistoryInfoAction; 28 29 import org.openstreetmap.josm.actions.ZoomToAction; 29 30 import org.openstreetmap.josm.data.osm.OsmPrimitive; 30 import org.openstreetmap.josm.data.osm.Relation;31 31 import org.openstreetmap.josm.data.osm.RelationMember; 32 32 import org.openstreetmap.josm.data.osm.Way; 33 33 import org.openstreetmap.josm.gui.MainApplication; … … 41 41 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent; 42 42 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; 43 43 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 44 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;45 44 import org.openstreetmap.josm.gui.util.HighlightHelper; 46 45 import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTable; 47 46 import org.openstreetmap.josm.spi.preferences.Config; … … 60 59 * constructor for relation member table 61 60 * 62 61 * @param layer the data layer of the relation. Must not be null 63 * @param r elation the relation. Can be null62 * @param roleCellEditor the role editor combobox 64 63 * @param model the table model 65 64 */ 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()); 68 67 setLayer(layer); 69 68 model.addMemberModelListener(this); 70 69 71 MemberRoleCellEditor ce = (MemberRoleCellEditor) getColumnModel().getColumn(0).getCellEditor(); 72 setRowHeight(ce.getEditor().getPreferredSize().height); 70 setRowHeight(roleCellEditor.getTableCellEditorComponent(this, "", false, 0, 0).getPreferredSize().height); 73 71 setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); 74 72 setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 75 73 putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); -
src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableColumnModel.java
4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 6 import javax.swing.table.DefaultTableColumnModel; 7 import javax.swing.table.TableCellEditor; 7 8 import javax.swing.table.TableColumn; 8 9 9 import org.openstreetmap.josm.data.osm.Relation;10 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;11 12 10 /** 13 11 * This is the column model for the {@link MemberTable} 14 12 */ … … 16 14 17 15 /** 18 16 * 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 21 18 * @since 13675 22 19 */ 23 public MemberTableColumnModel( AutoCompletionManager autoCompletionManager, Relation relation) {20 public MemberTableColumnModel(TableCellEditor roleCellEditor) { 24 21 TableColumn col; 25 22 26 23 // column 0 - the member role … … 29 26 col.setResizable(true); 30 27 col.setPreferredWidth(100); 31 28 col.setCellRenderer(new MemberTableRoleCellRenderer()); 32 col.setCellEditor( new MemberRoleCellEditor(autoCompletionManager, relation));29 col.setCellEditor(roleCellEditor); 33 30 addColumn(col); 34 31 35 32 // column 1 - the member -
src/org/openstreetmap/josm/gui/dialogs/relation/actions/IRelationEditorActionAccess.java
2 2 package org.openstreetmap.josm.gui.dialogs.relation.actions; 3 3 4 4 import javax.swing.Action; 5 import javax.swing.JTextField; 5 6 6 7 import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor; 7 8 import org.openstreetmap.josm.gui.dialogs.relation.MemberTable; … … 9 10 import org.openstreetmap.josm.gui.dialogs.relation.SelectionTable; 10 11 import org.openstreetmap.josm.gui.dialogs.relation.SelectionTableModel; 11 12 import org.openstreetmap.josm.gui.tagging.TagEditorModel; 12 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;13 13 14 14 /** 15 15 * This interface provides access to the relation editor for actions. … … 69 69 * Get the text field that is used to edit the role. 70 70 * @return The role text field. 71 71 */ 72 AutoCompletingTextField getTextFieldRole();72 JTextField getTextFieldRole(); 73 73 74 74 /** 75 75 * 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
8 8 import java.util.List; 9 9 10 10 import javax.swing.JOptionPane; 11 import javax.swing.JTextField; 11 12 import javax.swing.SwingUtilities; 12 13 13 14 import org.openstreetmap.josm.command.AddCommand; … … 27 28 import org.openstreetmap.josm.gui.dialogs.relation.RelationDialogManager; 28 29 import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor; 29 30 import org.openstreetmap.josm.gui.tagging.TagEditorModel; 30 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;31 31 import org.openstreetmap.josm.tools.ImageProvider; 32 32 import org.openstreetmap.josm.tools.Utils; 33 33 … … 38 38 abstract class SavingAction extends AbstractRelationEditorAction { 39 39 private static final long serialVersionUID = 1L; 40 40 41 protected final AutoCompletingTextField tfRole;41 protected final JTextField tfRole; 42 42 43 43 protected SavingAction(IRelationEditorActionAccess editorAccess, IRelationEditorUpdateOn... updateOn) { 44 44 super(editorAccess, updateOn); -
src/org/openstreetmap/josm/gui/dialogs/relation/actions/SetRoleAction.java
7 7 import java.awt.event.ActionEvent; 8 8 9 9 import javax.swing.JOptionPane; 10 import javax.swing.JTextField; 10 11 import javax.swing.event.DocumentEvent; 11 12 import javax.swing.event.DocumentListener; 12 13 13 14 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 14 15 import org.openstreetmap.josm.gui.MainApplication; 15 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;16 16 import org.openstreetmap.josm.tools.ImageProvider; 17 17 import org.openstreetmap.josm.tools.Utils; 18 18 … … 23 23 public class SetRoleAction extends AbstractRelationEditorAction implements DocumentListener { 24 24 private static final long serialVersionUID = 1L; 25 25 26 private final transient AutoCompletingTextField tfRole;26 private final transient JTextField tfRole; 27 27 28 28 /** 29 29 * Constructs a new {@code SetRoleAction}. … … 32 32 public SetRoleAction(IRelationEditorActionAccess editorAccess) { 33 33 super(editorAccess); 34 34 this.tfRole = editorAccess.getTextFieldRole(); 35 putValue(SHORT_DESCRIPTION, tr(" Sets a role forthe selected members"));35 putValue(SHORT_DESCRIPTION, tr("Apply the role to the selected members")); 36 36 new ImageProvider("apply").getResource().attachImageIcon(this); 37 37 putValue(NAME, tr("Apply Role")); 38 38 updateEnabledState(); -
src/org/openstreetmap/josm/gui/tagging/ac/AutoCompComboBox.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.tagging.ac; 3 3 4 import java.awt.Component; 4 5 import java.awt.im.InputContext; 5 6 import java.util.Collection; 6 7 import java.util.Collections; 8 import java.util.EventObject; 7 9 import java.util.LinkedList; 8 10 import java.util.Locale; 9 11 10 12 import javax.swing.ComboBoxEditor; 13 import javax.swing.JTable; 14 import javax.swing.event.CellEditorListener; 15 import javax.swing.table.TableCellEditor; 11 16 17 import org.openstreetmap.josm.gui.util.CellEditorSupport; 12 18 import org.openstreetmap.josm.gui.widgets.JosmComboBox; 13 19 import org.openstreetmap.josm.tools.Logging; 14 20 … … 24 30 * @param <E> the type of the combobox entries 25 31 * @since 18173 26 32 */ 27 public class AutoCompComboBox<E> extends JosmComboBox<E> implements AutoCompListener {33 public class AutoCompComboBox<E> extends JosmComboBox<E> implements TableCellEditor, AutoCompListener { 28 34 29 35 /** force a different keyboard input locale for the editor */ 30 36 private boolean useFixedLocale; … … 48 54 setEditable(true); 49 55 getEditorComponent().setModel(model); 50 56 getEditorComponent().addAutoCompListener(this); 57 tableCellEditorSupport = new CellEditorSupport(this); 51 58 } 52 59 53 60 /** … … 104 111 * @param elems The string items to set 105 112 * @deprecated Has been moved to the model, where it belongs. Use 106 113 * {@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} and108 * {@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}. 109 116 */ 110 117 @Deprecated 111 118 public void setPossibleItems(Collection<E> elems) { … … 122 129 * @since 15011 123 130 * @deprecated Has been moved to the model, where it belongs. Use 124 131 * {@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} and126 * {@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}. 127 134 */ 128 135 @Deprecated 129 136 public void setPossibleItemsTopDown(Collection<E> elems) { … … 201 208 public void autoCompPerformed(AutoCompEvent e) { 202 209 autocomplete(e.getItem()); 203 210 } 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 } 204 270 } -
src/org/openstreetmap/josm/gui/tagging/ac/AutoCompListener.java
4 4 import java.util.EventListener; 5 5 6 6 /** 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. 14 19 * 15 * @see AutoCompEvent16 20 * @since 18221 17 21 */ 18 22 public interface AutoCompListener extends EventListener { -
src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletionManager.java
6 6 import java.util.Collection; 7 7 import java.util.Collections; 8 8 import java.util.Comparator; 9 import java.util.EnumSet; 9 10 import java.util.HashMap; 10 import java.util.HashSet;11 11 import java.util.LinkedHashSet; 12 12 import java.util.List; 13 13 import java.util.Map; … … 40 40 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; 41 41 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 42 42 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 43 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType; 43 44 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; 44 45 import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role; 45 46 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 47 48 import org.openstreetmap.josm.tools.Utils; 48 49 49 50 /** 50 * AutoCompletionManager holds a cache of keys with a list of 51 * possible auto completion values foreach key.52 * 51 * AutoCompletionManager holds a cache of keys with a list of possible auto completion values for 52 * each key. 53 * <p> 53 54 * Each DataSet can be assigned one AutoCompletionManager instance such that 54 55 * <ol> 55 56 * <li>any key used in a tag in the data set is part of the key list in the cache</li> … … 56 57 * <li>any value used in a tag for a specific key is part of the autocompletion list of this key</li> 57 58 * </ol> 58 59 * 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. 65 63 */ 66 64 public class AutoCompletionManager implements DataSetListener { 67 65 … … 105 103 } 106 104 } 107 105 106 static final Comparator<AutoCompletionItem> ALPHABETIC_AUTOCOMPLETIONITEM_COMPARATOR = 107 (o1, o2) -> String.CASE_INSENSITIVE_ORDER.compare(o1.getValue(), o2.getValue()); 108 109 108 110 /** If the dirty flag is set true, a rebuild is necessary. */ 109 111 protected boolean dirty; 110 112 /** The data set that is managed */ … … 128 130 static final Set<UserInputTag> USER_INPUT_TAG_CACHE = new LinkedHashSet<>(); 129 131 130 132 /** 131 * the cached list of member roles132 * only accessed by getRoleCache(), rebuild() and cacheRelationMemberRoles()133 * use getRoleCache() accessor133 * The cached list of member roles by relation {@code type}. 134 * <p> 135 * Used by rebuild() and cacheRelationMemberRoles(). Use the {@link #getRoleCache} accessor. 134 136 */ 135 protected Set<String> roleCache;137 protected final MultiMap<String, Role> ROLE_CACHE = new MultiMap<>(); 136 138 137 /**138 * the same as roleCache but for the preset roles can be accessed directly139 */140 static final Set<String> PRESET_ROLE_CACHE = new HashSet<>();141 142 139 private static final Map<DataSet, AutoCompletionManager> INSTANCES = new HashMap<>(); 143 140 144 141 /** … … 159 156 return tagCache; 160 157 } 161 158 162 protected Set<String> getRoleCache() {159 protected MultiMap<String, Role> getRoleCache() { 163 160 if (dirty) { 164 161 rebuild(); 165 162 dirty = false; 166 163 } 167 return roleCache;164 return ROLE_CACHE; 168 165 } 169 166 170 167 /** … … 172 169 */ 173 170 protected void rebuild() { 174 171 tagCache = new MultiMap<>(); 175 roleCache = new HashSet<>();172 ROLE_CACHE.clear(); 176 173 cachePrimitives(ds.allNonDeletedCompletePrimitives()); 177 174 } 178 175 … … 196 193 } 197 194 198 195 /** 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 /** 199 217 * Caches all member roles of the relation <code>relation</code> 200 218 * 201 219 * @param relation the relation 202 220 */ 203 221 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); 207 229 } 208 230 } 209 231 } … … 263 285 * 264 286 * @return the list of member roles 265 287 */ 266 public List< String> getMemberRoles() {267 return new ArrayList<>(getRoleCache() );288 public List<Role> getMemberRoles() { 289 return new ArrayList<>(getRoleCache().getAllValues()); 268 290 } 269 291 270 292 /** … … 273 295 * @param list the list to populate 274 296 */ 275 297 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); 278 300 } 279 301 280 302 /** … … 303 325 } 304 326 305 327 /** 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 /** 306 394 * Populates the an {@link AutoCompletionList} with the currently cached tag keys 307 395 * 308 396 * @param list the list to populate … … 503 591 MainApplication.getLayerManager().removeLayerChangeListener(this); 504 592 dirty = true; 505 593 tagCache = null; 506 roleCache = null;594 ROLE_CACHE.clear(); 507 595 ds = null; 508 596 } 509 597 } -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetItem.java
5 5 import static org.openstreetmap.josm.tools.I18n.trc; 6 6 7 7 import java.io.File; 8 import java.util.Arrays;9 8 import java.util.Collection; 10 9 import java.util.Collections; 11 10 import java.util.EnumSet; … … 22 21 import org.openstreetmap.josm.data.osm.Tag; 23 22 import org.openstreetmap.josm.data.preferences.BooleanProperty; 24 23 import 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;27 24 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 28 25 import org.openstreetmap.josm.gui.util.LruCache; 29 26 import org.openstreetmap.josm.tools.ImageProvider; … … 44 41 */ 45 42 protected static final BooleanProperty DISPLAY_KEYS_AS_HINT = new BooleanProperty("taggingpreset.display-keys-as-hint", true); 46 43 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 61 44 /** 62 45 * Returns all cached {@link AutoCompletionItem}s for given keys. 63 46 * -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresets.java
45 45 /** cache for key/value pairs found in the preset */ 46 46 private static final MultiMap<String, String> PRESET_TAG_CACHE = new MultiMap<>(); 47 47 /** 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<>(); 49 49 50 50 /** The collection of listeners */ 51 51 private static final Collection<TaggingPresetListener> listeners = new ArrayList<>(); … … 167 167 Roles r = (Roles) item; 168 168 for (Role i : r.roles) { 169 169 if (i.key != null) { 170 PRESET_ROLE_CACHE. add(i.key);170 PRESET_ROLE_CACHE.put(i.key, i); 171 171 } 172 172 } 173 173 } else if (item instanceof CheckGroup) { … … 189 189 * Replies a set of all roles in the tagging presets. 190 190 * @return a set of all roles in the tagging presets. 191 191 */ 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()); 194 194 } 195 195 196 196 /** -
src/org/openstreetmap/josm/gui/tagging/presets/items/Roles.java
178 178 } 179 179 180 180 @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 181 212 public String toString() { 182 213 return "Role [key=" + key + ", text=" + text + ']'; 183 214 } -
src/org/openstreetmap/josm/gui/widgets/HistoryComboBox.java
39 39 * @return the items as strings 40 40 * @deprecated Has been moved to the model, where it belongs. Use 41 41 * {@link HistoryComboBoxModel#asStringList} instead. Probably you want to use 42 * {@link org.openstreetmap.josm.gui. tagging.ac.AutoCompComboBoxModel.Preferences#load} and43 * {@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}. 44 44 */ 45 45 @Deprecated 46 46 public List<String> getHistory() { -
src/org/openstreetmap/josm/gui/widgets/JosmComboBoxModel.java
54 54 * {@link javax.swing.DefaultComboBoxModel}. 55 55 * 56 56 * @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 58 59 */ 59 60 public int getIndexOf(E element) { 60 61 return elements.indexOf(element); 61 62 } 62 63 64 protected void doAddElement(E element) { 65 if (element != null && (maxSize == -1 || getSize() < maxSize)) { 66 elements.add(element); 67 } 68 } 69 63 70 // 64 71 // interface java.lang.Iterable 65 72 // … … 78 85 */ 79 86 @Override 80 87 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); 84 90 } 85 91 86 92 @Override 87 93 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); 89 104 } 90 105 91 106 @Override … … 114 129 removeElementAt(getSize() - 1); 115 130 } 116 131 elements.add(index, element); 132 fireIntervalAdded(this, index, index); 117 133 } 118 134 119 135 // … … 166 182 * @param elems The elements to add. 167 183 */ 168 184 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); 170 190 } 171 191 172 192 /** … … 177 197 * {@code String}. 178 198 */ 179 199 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); 181 205 } 182 206 183 207 /** 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 /** 184 238 * Adds an element to the top of the list. 185 239 * <p> 186 240 * If the element is already in the model, moves it to the top. If the model gets too big, … … 204 258 */ 205 259 public void removeAllElements() { 206 260 if (!elements.isEmpty()) { 207 int firstIndex = 0;208 261 int lastIndex = elements.size() - 1; 209 262 elements.clear(); 210 263 selected = null; 211 fireIntervalRemoved(this, firstIndex, lastIndex);264 fireIntervalRemoved(this, 0, lastIndex); 212 265 } else { 213 266 selected = null; 214 267 } -
src/org/openstreetmap/josm/tools/MultiMap.java
124 124 } 125 125 126 126 /** 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 /** 127 135 * Returns {@code true} if this map contains no key-value mappings. 128 136 * @return {@code true} if this map contains no key-value mappings 129 137 * @see Map#isEmpty() -
test/unit/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditorTest.java
20 20 import org.openstreetmap.josm.data.osm.Way; 21 21 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 22 22 import org.openstreetmap.josm.gui.tagging.TagEditorPanel; 23 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;24 23 import org.openstreetmap.josm.testutils.JOSMTestRules; 25 24 import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker; 26 25 … … 123 122 OsmDataLayer layer = new OsmDataLayer(ds, "test", null); 124 123 IRelationEditor re = newRelationEditor(relation, layer); 125 124 126 AutoCompletingTextField tfRole = GenericRelationEditor.buildRoleTextField(re);127 assertNotNull(tfRole);128 129 125 TagEditorPanel tagEditorPanel = new TagEditorPanel(relation, null); 130 126 131 127 JPanel top = GenericRelationEditor.buildTagEditorPanel(tagEditorPanel); -
test/unit/org/openstreetmap/josm/gui/dialogs/relation/actions/AbstractRelationEditorActionTest.java
12 12 import org.openstreetmap.josm.data.osm.OsmPrimitive; 13 13 import org.openstreetmap.josm.data.osm.Relation; 14 14 import org.openstreetmap.josm.data.osm.Tag; 15 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem; 15 16 import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditorTest; 16 17 import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor; 17 18 import org.openstreetmap.josm.gui.dialogs.relation.MemberTable; … … 20 21 import org.openstreetmap.josm.gui.dialogs.relation.SelectionTableModel; 21 22 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 22 23 import org.openstreetmap.josm.gui.tagging.TagEditorModel; 23 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField; 24 import org.openstreetmap.josm.gui.tagging.ac.AutoCompEvent; 25 import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener; 26 import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField; 24 27 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler; 25 28 import org.openstreetmap.josm.testutils.JOSMTestRules; 26 29 … … 31 34 * @author Michael Zangl 32 35 */ 33 36 @Disabled 34 public abstract class AbstractRelationEditorActionTest {37 public abstract class AbstractRelationEditorActionTest implements AutoCompListener { 35 38 /** 36 39 * Platform for tooltips. 37 40 */ … … 46 49 private IRelationEditor editor; 47 50 private MemberTable memberTable; 48 51 private MemberTableModel memberTableModel; 49 private AutoComp letingTextFieldtfRole;52 private AutoCompTextField<AutoCompletionItem> tfRole; 50 53 private TagEditorModel tagModel; 51 54 52 55 protected final IRelationEditorActionAccess relationEditorAccess = new IRelationEditorActionAccess() { 53 56 54 57 @Override 55 public AutoComp letingTextFieldgetTextFieldRole() {58 public AutoCompTextField<AutoCompletionItem> getTextFieldRole() { 56 59 return tfRole; 57 60 } 58 61 … … 109 112 selectionTableModel = new SelectionTableModel(layer); 110 113 selectionTable = new SelectionTable(selectionTableModel, memberTableModel); 111 114 editor = GenericRelationEditorTest.newRelationEditor(orig, layer); 112 tfRole = new AutoComp letingTextField();115 tfRole = new AutoCompTextField<>(); 113 116 tagModel = new TagEditorModel(); 114 memberTable = new MemberTable(layer, editor.getRelation(), memberTableModel);117 memberTable = new MemberTable(layer, this, memberTableModel); 115 118 } 119 120 @Override 121 public void autoCompBefore(AutoCompEvent e) { 122 } 123 124 @Override 125 public void autoCompPerformed(AutoCompEvent e) { 126 } 116 127 }
