Ticket #21227: 21227-2.patch
| File 21227-2.patch, 47.9 KB (added by , 5 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
27 27 import java.util.Collection; 28 28 import java.util.Collections; 29 29 import java.util.EnumSet; 30 import java.util.HashSet; 30 31 import java.util.List; 31 32 import java.util.Set; 32 33 import java.util.stream.Collectors; … … 45 46 import javax.swing.JSplitPane; 46 47 import javax.swing.JTabbedPane; 47 48 import javax.swing.JTable; 49 import javax.swing.JTextField; 48 50 import javax.swing.JToolBar; 49 51 import javax.swing.KeyStroke; 52 import javax.swing.event.PopupMenuEvent; 53 import javax.swing.event.PopupMenuListener; 50 54 51 55 import org.openstreetmap.josm.actions.JosmAction; 52 56 import org.openstreetmap.josm.command.ChangeMembersCommand; … … 58 62 import org.openstreetmap.josm.data.osm.Relation; 59 63 import org.openstreetmap.josm.data.osm.RelationMember; 60 64 import org.openstreetmap.josm.data.osm.Tag; 65 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem; 61 66 import org.openstreetmap.josm.data.validation.tests.RelationChecker; 62 67 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 63 68 import org.openstreetmap.josm.gui.MainApplication; … … 98 103 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 99 104 import org.openstreetmap.josm.gui.tagging.TagEditorModel; 100 105 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; 106 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBox; 107 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel; 108 import org.openstreetmap.josm.gui.tagging.ac.AutoCompEvent; 109 import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener; 110 import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField; 103 111 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 104 112 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 105 113 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler; 106 114 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType; 107 115 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; 116 import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role; 108 117 import org.openstreetmap.josm.gui.util.WindowGeometry; 109 118 import org.openstreetmap.josm.spi.preferences.Config; 110 119 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 117 126 * This dialog is for editing relations. 118 127 * @since 343 119 128 */ 120 public class GenericRelationEditor extends RelationEditor implements CommandQueueListener {129 public class GenericRelationEditor extends RelationEditor implements AutoCompListener, CommandQueueListener, PopupMenuListener { 121 130 /** the tag table and its model */ 122 131 private final TagEditorPanel tagEditorPanel; 123 132 private final ReferringRelationsBrowser referrerBrowser; … … 131 140 private final SelectionTable selectionTable; 132 141 private final SelectionTableModel selectionTableModel; 133 142 134 private final AutoCompletingTextField tfRole; 143 private final AutoCompletionManager manager; 144 private final AutoCompComboBox<AutoCompletionItem> cbRole; 135 145 136 146 /** 137 147 * the menu item in the windows menu. Required to properly hide on dialog close. … … 216 226 populateModels(relation); 217 227 tagEditorPanel.getModel().ensureOneTag(); 218 228 229 manager = AutoCompletionManager.of(this.getLayer().data); 230 219 231 // setting up the member table 220 memberTable = new MemberTable(getLayer(), getRelation(), memberTableModel);232 memberTable = new MemberTable(getLayer(), this, memberTableModel); 221 233 memberTable.addMouseListener(new MemberTableDblClickAdapter()); 222 234 memberTableModel.addMemberModelListener(memberTable); 223 235 … … 226 238 selectionTable.setRowHeight(ce.getEditor().getPreferredSize().height); 227 239 228 240 LeftButtonToolbar leftButtonToolbar = new LeftButtonToolbar(new RelationEditorActionAccess()); 229 tfRole = buildRoleTextField(this); 241 cbRole = buildRoleComboBox(); 242 cbRole.getEditorComponent().addAutoCompListener(this); 243 cbRole.addPopupMenuListener(this); 230 244 231 245 JSplitPane pane = buildSplitPane( 232 246 buildTagEditorPanel(tagEditorPanel), 233 buildMemberEditorPanel(leftButtonToolbar , new RelationEditorActionAccess()),247 buildMemberEditorPanel(leftButtonToolbar), 234 248 this); 235 249 pane.setPreferredSize(new Dimension(100, 100)); 236 250 … … 310 324 @Override 311 325 public void actionPerformed(ActionEvent e) { 312 326 super.actionPerformed(e); 313 tfRole.requestFocusInWindow();327 cbRole.requestFocusInWindow(); 314 328 } 315 329 }, "PASTE_MEMBERS", key, getRootPane(), memberTable, selectionTable); 316 330 } … … 446 460 } 447 461 448 462 /** 449 * builds the role text field 450 * @param re relation editor 463 * builds a role text field 451 464 * @return the role text field 452 465 */ 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() { 466 protected static AutoCompComboBox<AutoCompletionItem> buildRoleComboBox() { 467 final AutoCompComboBox<AutoCompletionItem> cbRole = new AutoCompComboBox<>(); 468 cbRole.getEditorComponent().setColumns(10); 469 cbRole.setToolTipText(tr("Enter a role and apply it to the selected relation members")); 470 cbRole.addFocusListener(new FocusAdapter() { 457 471 @Override 458 472 public void focusGained(FocusEvent e) { 459 tfRole.selectAll();473 cbRole.getEditorComponent().selectAll(); 460 474 } 461 475 }); 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; 476 cbRole.setText(Config.getPref().get("relation.editor.generic.lastrole", "")); 477 return cbRole; 477 478 } 478 479 479 480 /** 480 481 * builds the panel for the relation member editor 481 482 * @param leftButtonToolbar left button toolbar 482 * @param editorAccess The relation editor483 483 * 484 484 * @return the panel for the relation member editor 485 485 */ 486 protected static JPanel buildMemberEditorPanel( 487 LeftButtonToolbar leftButtonToolbar, IRelationEditorActionAccess editorAccess) { 486 protected JPanel buildMemberEditorPanel(LeftButtonToolbar leftButtonToolbar) { 488 487 final JPanel pnl = new JPanel(new GridBagLayout()); 489 final JScrollPane scrollPane = new JScrollPane( editorAccess.getMemberTable());488 final JScrollPane scrollPane = new JScrollPane(memberTable); 490 489 491 490 GridBagConstraints gc = new GridBagConstraints(); 492 491 gc.gridx = 0; … … 520 519 // --- role editing 521 520 JPanel p3 = new JPanel(new FlowLayout(FlowLayout.LEFT)); 522 521 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)522 p3.add(cbRole); 523 SetRoleAction setRoleAction = new SetRoleAction(new RelationEditorActionAccess()); 524 memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction); 525 cbRole.getEditorComponent().getDocument().addDocumentListener(setRoleAction); 526 cbRole.getEditorComponent().addActionListener(setRoleAction); 527 memberTableModel.getSelectionModel().addListSelectionListener( 528 e -> cbRole.setEnabled(memberTable.getSelectedRowCount() > 0) 530 529 ); 531 editorAccess.getTextFieldRole().setEnabled(editorAccess.getMemberTable().getSelectedRowCount() > 0); 530 cbRole.setEnabled(memberTable.getSelectedRowCount() > 0); 531 532 532 JButton btnApply = new JButton(setRoleAction); 533 btnApply.setPreferredSize(new Dimension(20, 20)); 533 int height = cbRole.getPreferredSize().height; 534 btnApply.setPreferredSize(new Dimension(height, height)); 534 535 btnApply.setText(""); 535 536 p3.add(btnApply); 536 537 … … 562 563 gc.anchor = GridBagConstraints.NORTHWEST; 563 564 gc.weightx = 0.0; 564 565 gc.weighty = 1.0; 565 pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar( editorAccess),566 pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar(new RelationEditorActionAccess()), 566 567 ScrollViewport.VERTICAL_DIRECTION), gc); 567 568 568 569 gc.gridx = 1; … … 570 571 gc.weightx = 1.0; 571 572 gc.weighty = 1.0; 572 573 gc.fill = GridBagConstraints.BOTH; 573 pnl2.add(buildSelectionTablePanel( editorAccess.getSelectionTable()), gc);574 pnl2.add(buildSelectionTablePanel(selectionTable), gc); 574 575 575 576 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); 576 577 splitPane.setLeftComponent(pnl); 577 578 splitPane.setRightComponent(pnl2); 578 579 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 } 580 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 }); 588 587 589 588 JPanel pnl3 = new JPanel(new BorderLayout()); 590 589 pnl3.add(splitPane, BorderLayout.CENTER); … … 1039 1038 } 1040 1039 1041 1040 @Override 1042 public AutoCompletingTextField getTextFieldRole() {1043 return tfRole;1041 public JTextField getTextFieldRole() { 1042 return cbRole.getEditorComponent(); 1044 1043 } 1045 1046 1044 } 1047 1045 1048 1046 @Override … … 1054 1052 applyAction.updateEnabledState(); 1055 1053 } 1056 1054 } 1055 1056 private void initAutoCompModel(AutoCompComboBoxModel<AutoCompletionItem> model) { 1057 Set<Role> currentRoles = new HashSet<>(); 1058 EnumSet<TaggingPresetType> selectedTypes = EnumSet.noneOf(TaggingPresetType.class); 1059 for (int i = 0; i < memberTableModel.getRowCount(); ++i) { 1060 RelationMember member = memberTableModel.getValue(i); 1061 Role role = new Role(); 1062 role.key = member.getRole(); 1063 role.types = EnumSet.of(TaggingPresetType.forPrimitiveType(member.getDisplayType())); 1064 currentRoles.add(role); 1065 } 1066 for (RelationMember member : memberTableModel.getSelectedMembers()) { 1067 selectedTypes.add(TaggingPresetType.forPrimitiveType(member.getDisplayType())); 1068 } 1069 model.removeAllElements(); 1070 model.addAllElements(manager.getRolesForRelation(tagEditorPanel.getModel().getTags(), currentRoles, selectedTypes)); 1071 } 1072 1073 @Override 1074 @SuppressWarnings("unchecked") 1075 public void autoCompBefore(AutoCompEvent e) { 1076 AutoCompTextField<AutoCompletionItem> tf = (AutoCompTextField<AutoCompletionItem>) e.getSource(); 1077 String savedText = tf.getText(); 1078 initAutoCompModel(tf.getModel()); 1079 tf.setText(savedText); 1080 } 1081 1082 @Override 1083 public void autoCompPerformed(AutoCompEvent e) { 1084 // Not interested 1085 } 1086 1087 @Override 1088 @SuppressWarnings("unchecked") 1089 public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 1090 AutoCompComboBox<AutoCompletionItem> cb = (AutoCompComboBox<AutoCompletionItem>) e.getSource(); 1091 String savedText = cb.getText(); 1092 initAutoCompModel(((AutoCompComboBox<AutoCompletionItem>) e.getSource()).getModel()); 1093 cb.setText(savedText); 1094 } 1095 1096 @Override 1097 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 1098 // Who cares? 1099 } 1100 1101 @Override 1102 public void popupMenuCanceled(PopupMenuEvent e) { 1103 // Who cares? 1104 } 1057 1105 } -
src/org/openstreetmap/josm/gui/dialogs/relation/MemberRoleCellEditor.java
9 9 import javax.swing.JTable; 10 10 import javax.swing.table.TableCellEditor; 11 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; 12 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem; 13 import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener; 14 import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField; 16 15 17 16 /** 18 17 * The {@link CellEditor} for the role cell in the table. Supports autocompletion. 19 18 */ 20 19 public class MemberRoleCellEditor extends AbstractCellEditor implements TableCellEditor { 21 private final AutoCompletingTextField editor; 22 private final AutoCompletionManager autoCompletionManager; 23 private final transient Relation relation; 20 private final AutoCompTextField<AutoCompletionItem> editor; 24 21 25 /** user input is matched against this list of auto completion items */26 private final AutoCompletionList autoCompletionList;27 28 22 /** 29 23 * Constructs a new {@code MemberRoleCellEditor}. 30 * @param autoCompletionManager the auto completion manager. Must not be null 31 * @param relation the relation. Can be null 24 * @param listener the autocompletion listener 32 25 * @since 13675 33 26 */ 34 public MemberRoleCellEditor(AutoCompletionManager autoCompletionManager, Relation relation) { 35 this.autoCompletionManager = autoCompletionManager; 36 this.relation = relation; 37 editor = new AutoCompletingTextField(0, false); 27 public MemberRoleCellEditor(AutoCompListener listener) { 28 editor = new AutoCompTextField<>(0, false); 38 29 editor.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 39 autoCompletionList = new AutoCompletionList(); 40 editor.setAutoCompletionList(autoCompletionList); 30 editor.addAutoCompListener(listener); 41 31 } 42 32 43 33 @Override 44 34 public Component getTableCellEditorComponent(JTable table, 45 35 Object value, boolean isSelected, int row, int column) { 46 47 36 String role = (String) value; 48 37 editor.setText(role); 49 autoCompletionList.clear();50 autoCompletionManager.populateWithMemberRoles(autoCompletionList, relation);51 38 return editor; 52 39 } 53 40 … … 60 47 * Returns the edit field for this cell editor. 61 48 * @return the edit field for this cell editor 62 49 */ 63 public AutoComp letingTextFieldgetEditor() {50 public AutoCompTextField<AutoCompletionItem> getEditor() { 64 51 return editor; 65 52 } 66 53 } -
src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java
27 27 import org.openstreetmap.josm.actions.HistoryInfoAction; 28 28 import org.openstreetmap.josm.actions.ZoomToAction; 29 29 import org.openstreetmap.josm.data.osm.OsmPrimitive; 30 import org.openstreetmap.josm.data.osm.Relation;31 30 import org.openstreetmap.josm.data.osm.RelationMember; 32 31 import org.openstreetmap.josm.data.osm.Way; 33 32 import org.openstreetmap.josm.gui.MainApplication; … … 41 40 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent; 42 41 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; 43 42 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 44 import org.openstreetmap.josm.gui.tagging.ac.AutoComp letionManager;43 import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener; 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 relation the relation. Can be null62 * @param listener the autocompletion listener 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, AutoCompListener listener, MemberTableModel model) { 66 super(model, new MemberTableColumnModel(listener), model.getSelectionModel()); 68 67 setLayer(layer); 69 68 model.addMemberModelListener(this); 70 69 -
src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableColumnModel.java
6 6 import javax.swing.table.DefaultTableColumnModel; 7 7 import javax.swing.table.TableColumn; 8 8 9 import org.openstreetmap.josm.data.osm.Relation; 10 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 9 import org.openstreetmap.josm.gui.tagging.ac.AutoCompListener; 11 10 12 11 /** 13 12 * This is the column model for the {@link MemberTable} … … 16 15 17 16 /** 18 17 * Constructs a new {@code MemberTableColumnModel}. 19 * @param autoCompletionManager the auto completion manager. Must not be null 20 * @param relation the relation. Can be null 18 * @param listener the autocompletion listener 21 19 * @since 13675 22 20 */ 23 public MemberTableColumnModel(AutoComp letionManager autoCompletionManager, Relation relation) {21 public MemberTableColumnModel(AutoCompListener listener) { 24 22 TableColumn col; 25 23 26 24 // column 0 - the member role … … 29 27 col.setResizable(true); 30 28 col.setPreferredWidth(100); 31 29 col.setCellRenderer(new MemberTableRoleCellRenderer()); 32 col.setCellEditor(new MemberRoleCellEditor( autoCompletionManager, relation));30 col.setCellEditor(new MemberRoleCellEditor(listener)); 33 31 addColumn(col); 34 32 35 33 // 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}. -
src/org/openstreetmap/josm/gui/tagging/ac/AutoCompComboBox.java
104 104 * @param elems The string items to set 105 105 * @deprecated Has been moved to the model, where it belongs. Use 106 106 * {@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}.107 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#load} and 108 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#save}. 109 109 */ 110 110 @Deprecated 111 111 public void setPossibleItems(Collection<E> elems) { … … 122 122 * @since 15011 123 123 * @deprecated Has been moved to the model, where it belongs. Use 124 124 * {@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}.125 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#load} and 126 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#save}. 127 127 */ 128 128 @Deprecated 129 129 public void setPossibleItemsTopDown(Collection<E> elems) { -
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 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 // the empty role 360 map.merge("", AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith); 361 // the roles already present in the member editor 362 for (Role role : roles) { 363 if (appliesTo(role, types)) 364 map.merge(role.key, AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith); 365 } 366 // the roles on existing relations of the same type 367 String type = getRelationType(keys); 368 if (type != null) { 369 for (Role role : getRoleCache().getValues(type)) { 370 if (appliesTo(role, types)) 371 map.merge(role.key, AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith); 372 } 373 } 374 Collection<TaggingPreset> presets = TaggingPresets.getMatchingPresets(null, keys, false); 375 if (presets.isEmpty()) { 376 // no preset matched, all roles of all presets 377 for (Role role : TaggingPresets.getPresetRoles()) { 378 if (appliesTo(role, types)) 379 map.merge(role.key, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith); 380 } 381 for (Role role : getRoleCache().getAllValues()) { 382 if (appliesTo(role, types)) 383 map.merge(role.key, AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith); 384 } 385 } else { 386 // all roles of all matched presets 387 for (TaggingPreset preset : presets) { 388 if (preset.roles != null) { 389 for (Role role : preset.roles.roles) { 390 if (appliesTo(role, types)) 391 map.merge(role.key, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith); 392 } 393 } 394 } 395 } 396 return map.entrySet().stream().map(e -> new AutoCompletionItem(e.getKey(), e.getValue())) 397 .sorted(ALPHABETIC_AUTOCOMPLETIONITEM_COMPARATOR).collect(Collectors.toList()); 398 } 399 400 /** 306 401 * Populates the an {@link AutoCompletionList} with the currently cached tag keys 307 402 * 308 403 * @param list the list to populate … … 503 598 MainApplication.getLayerManager().removeLayerChangeListener(this); 504 599 dirty = true; 505 600 tagCache = null; 506 roleCache = null;601 ROLE_CACHE.clear(); 507 602 ds = null; 508 603 } 509 604 } -
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/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
18 18 import org.openstreetmap.josm.data.osm.OsmPrimitive; 19 19 import org.openstreetmap.josm.data.osm.Relation; 20 20 import org.openstreetmap.josm.data.osm.Way; 21 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem; 21 22 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 22 23 import org.openstreetmap.josm.gui.tagging.TagEditorPanel; 23 import org.openstreetmap.josm.gui.tagging.ac.AutoComp letingTextField;24 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBox; 24 25 import org.openstreetmap.josm.testutils.JOSMTestRules; 25 26 import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker; 26 27 … … 123 124 OsmDataLayer layer = new OsmDataLayer(ds, "test", null); 124 125 IRelationEditor re = newRelationEditor(relation, layer); 125 126 126 AutoComp letingTextField tfRole = GenericRelationEditor.buildRoleTextField(re);127 assertNotNull( tfRole);127 AutoCompComboBox<AutoCompletionItem> cbRole = GenericRelationEditor.buildRoleComboBox(); 128 assertNotNull(cbRole); 128 129 129 130 TagEditorPanel tagEditorPanel = new TagEditorPanel(relation, null); 130 131 -
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 }
