Ticket #21227: 21227-4.patch
| File 21227-4.patch, 125.9 KB (added by , 4 years ago) |
|---|
-
resources/images/in_dataset.svg
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 <svg 3 version="1.1" 4 width="16px" 5 height="16px" 6 id="svg79288" 7 sodipodi:docname="in_dataset.svg" 8 inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" 9 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 11 xmlns="http://www.w3.org/2000/svg" 12 xmlns:svg="http://www.w3.org/2000/svg"> 13 <defs 14 id="defs79292" /> 15 <sodipodi:namedview 16 id="namedview79290" 17 pagecolor="#ffffff" 18 bordercolor="#666666" 19 borderopacity="1.0" 20 inkscape:pageshadow="2" 21 inkscape:pageopacity="0.0" 22 inkscape:pagecheckerboard="0" 23 showgrid="true" 24 inkscape:zoom="89.875" 25 inkscape:cx="3.3769124" 26 inkscape:cy="8.0055633" 27 inkscape:window-width="3840" 28 inkscape:window-height="2085" 29 inkscape:window-x="0" 30 inkscape:window-y="0" 31 inkscape:window-maximized="1" 32 inkscape:current-layer="svg79288"> 33 <inkscape:grid 34 type="xygrid" 35 id="grid79357" 36 empspacing="8" 37 dotted="false" 38 originx="8" 39 originy="8" 40 spacingx="0.5" 41 spacingy="0.5" /> 42 </sodipodi:namedview> 43 <path 44 style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" 45 d="M 3,3 13,6 5,13 Z" 46 id="path80027" 47 sodipodi:nodetypes="cccc" /> 48 <path 49 style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#df421e;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 50 d="m 11.5,4.5 h 3 v 3 h -3 z" 51 id="path79392" /> 52 <path 53 style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#df421e;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 54 d="m 1.5,1.5 h 3 v 3 h -3 z" 55 id="path79392-7" /> 56 <path 57 style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#df421e;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 58 d="m 3.5,11.5 h 3 v 3 h -3 z" 59 id="path79392-4" /> 60 </svg> -
resources/images/in_standard.svg
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 <svg 3 inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" 4 sodipodi:docname="in_standard.svg" 5 id="svg12" 6 version="1.1" 7 width="16" 8 viewBox="0 0 16 16" 9 height="16" 10 enable-background="new 0 0 96 96" 11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 12 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 13 xmlns="http://www.w3.org/2000/svg" 14 xmlns:svg="http://www.w3.org/2000/svg" 15 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 16 xmlns:cc="http://creativecommons.org/ns#" 17 xmlns:dc="http://purl.org/dc/elements/1.1/"> 18 <metadata 19 id="metadata18"> 20 <rdf:RDF> 21 <cc:Work 22 rdf:about=""> 23 <dc:format>image/svg+xml</dc:format> 24 <dc:type 25 rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> 26 </cc:Work> 27 </rdf:RDF> 28 </metadata> 29 <defs 30 id="defs16" /> 31 <sodipodi:namedview 32 inkscape:current-layer="svg12" 33 inkscape:window-maximized="1" 34 inkscape:window-y="0" 35 inkscape:window-x="0" 36 inkscape:cy="9.5865506" 37 inkscape:cx="7.8530512" 38 inkscape:zoom="66.916666" 39 showgrid="true" 40 id="namedview14" 41 inkscape:window-height="2085" 42 inkscape:window-width="3840" 43 inkscape:pageshadow="2" 44 inkscape:pageopacity="0" 45 guidetolerance="10" 46 gridtolerance="10" 47 objecttolerance="10" 48 borderopacity="1" 49 bordercolor="#666666" 50 pagecolor="#ffffff" 51 inkscape:pagecheckerboard="0" 52 inkscape:lockguides="false"> 53 <inkscape:grid 54 id="grid843" 55 type="xygrid" 56 originx="0" 57 originy="0" 58 empspacing="4" /> 59 </sodipodi:namedview> 60 <text 61 xml:space="preserve" 62 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 63 x="11.570769" 64 y="11.741786" 65 id="text12471"><tspan 66 sodipodi:role="line" 67 id="tspan12469" 68 x="11.570769" 69 y="11.741786"></tspan></text> 70 <text 71 xml:space="preserve" 72 style="font-size:13.3333px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#df421e;fill-opacity:1;stroke:#df421e;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" 73 x="8.0417976" 74 y="11.405445" 75 id="text59954"><tspan 76 sodipodi:role="line" 77 id="tspan59952" 78 x="8.0417976" 79 y="11.405445">§</tspan></text> 80 </svg> -
src/org/openstreetmap/josm/actions/UploadAction.java
240 240 ChangesetUpdater.check(); 241 241 242 242 final UploadDialog dialog = UploadDialog.getUploadDialog(); 243 dialog.setUploadedPrimitives(apiData); 244 dialog.initLifeCycle(layer.getDataSet()); 243 dialog.initLifeCycle(layer.getDataSet(), apiData); 245 244 dialog.setVisible(true); 246 245 dialog.rememberUserInput(); 247 246 if (dialog.isCanceled()) { -
src/org/openstreetmap/josm/data/tagging/ac/AutoCompItemCellRenderer.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.tagging.ac; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.Color; 7 import java.awt.Component; 8 import java.awt.Font; 9 import java.util.Map; 10 11 import javax.swing.ImageIcon; 12 import javax.swing.JLabel; 13 import javax.swing.JList; 14 import javax.swing.ListCellRenderer; 15 16 import org.openstreetmap.josm.gui.widgets.JosmListCellRenderer; 17 import org.openstreetmap.josm.tools.ImageProvider; 18 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 19 20 /** 21 * A custom list cell renderer for autocompletion items that colorizes and adds the value count to 22 * some items. 23 * <p> 24 * See also: {@link AutoCompletionPriority#compareTo} 25 */ 26 public class AutoCompItemCellRenderer extends JosmListCellRenderer<AutoCompletionItem> { 27 /** The color used to render items found in the dataset. */ 28 public static final Color BGCOLOR_1 = new Color(254, 226, 214); 29 /** The color used to render items found in the standard */ 30 public static final Color BGCOLOR_2 = new Color(235, 255, 177); 31 32 protected Map<String, Integer> map; 33 private static final ImageIcon iconEmpty = ImageProvider.getEmpty(ImageSizes.POPUPMENU); 34 private static final ImageIcon iconDataSet = ImageProvider.get("in_dataset", ImageSizes.POPUPMENU); 35 private static final ImageIcon iconStandard = ImageProvider.get("in_standard", ImageSizes.POPUPMENU); 36 37 /** 38 * Constructs the cell renderer. 39 * 40 * @param component The component the renderer is attached to. JComboBox or JList. 41 * @param renderer The L&F renderer. Usually obtained by calling {@code getRenderer()} on {@code component}. 42 * @param map A map from key to count. 43 */ 44 public AutoCompItemCellRenderer(Component component, ListCellRenderer<? super AutoCompletionItem> renderer, Map<String, Integer> map) { 45 super(component, renderer); 46 this.map = map; 47 } 48 49 @Override 50 public Component getListCellRendererComponent(JList<? extends AutoCompletionItem> list, AutoCompletionItem value, 51 int index, boolean isSelected, boolean cellHasFocus) { 52 Integer count = null; 53 if (value == null) 54 value = new AutoCompletionItem("", AutoCompletionPriority.IS_IN_STANDARD); 55 56 // if there is a value count add it to the text 57 if (map != null) { 58 String text = value.toString(); 59 count = map.get(text); 60 if (count != null) { 61 value = new AutoCompletionItem(tr("{0} ({1})", text, count), value.getPriority()); 62 } 63 } 64 65 JLabel l = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 66 l.setIcon(iconEmpty); 67 if (value.getPriority().isInDataSet()) { 68 l.setIcon(iconDataSet); 69 } 70 if (value.getPriority().isInStandard()) { 71 l.setIcon(iconStandard); 72 } 73 l.setComponentOrientation(component.getComponentOrientation()); 74 if (count != null) { 75 l.setFont(l.getFont().deriveFont(Font.ITALIC + Font.BOLD)); 76 } 77 return l; 78 } 79 } -
src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
10 10 import java.awt.FlowLayout; 11 11 import java.awt.GridBagConstraints; 12 12 import java.awt.GridBagLayout; 13 import java.awt.Insets; 13 14 import java.awt.Window; 14 15 import java.awt.datatransfer.Clipboard; 15 16 import java.awt.datatransfer.FlavorListener; 16 17 import java.awt.event.ActionEvent; 17 import java.awt.event.FocusAdapter;18 import java.awt.event.FocusEvent;19 18 import java.awt.event.InputEvent; 20 19 import java.awt.event.KeyEvent; 21 20 import java.awt.event.MouseAdapter; … … 27 26 import java.util.Collection; 28 27 import java.util.Collections; 29 28 import java.util.EnumSet; 29 import java.util.HashMap; 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; 50 53 … … 58 61 import org.openstreetmap.josm.data.osm.Relation; 59 62 import org.openstreetmap.josm.data.osm.RelationMember; 60 63 import org.openstreetmap.josm.data.osm.Tag; 64 import org.openstreetmap.josm.data.tagging.ac.AutoCompItemCellRenderer; 65 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem; 66 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionPriority; 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.TagTable; 108 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBox; 109 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel; 103 110 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 111 import org.openstreetmap.josm.gui.tagging.ac.DefaultAutoCompListener; 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; … … 108 116 import org.openstreetmap.josm.gui.util.WindowGeometry; 109 117 import org.openstreetmap.josm.spi.preferences.Config; 110 118 import org.openstreetmap.josm.tools.CheckParameterUtil; 119 import org.openstreetmap.josm.tools.GBC; 111 120 import org.openstreetmap.josm.tools.InputMapUtils; 112 121 import org.openstreetmap.josm.tools.Logging; 113 122 import org.openstreetmap.josm.tools.Shortcut; … … 118 127 * @since 343 119 128 */ 120 129 public class GenericRelationEditor extends RelationEditor implements CommandQueueListener { 130 private static final String PREF_LASTROLE = "relation.editor.generic.lastrole"; 131 private static final String PREF_USE_ROLE_FILTER = "relation.editor.use_role_filter"; 132 121 133 /** the tag table and its model */ 122 134 private final TagEditorPanel tagEditorPanel; 123 135 private final ReferringRelationsBrowser referrerBrowser; … … 131 143 private final SelectionTable selectionTable; 132 144 private final SelectionTableModel selectionTableModel; 133 145 134 private final AutoCompletingTextField tfRole; 146 private final AutoCompletionManager manager; 147 private final AutoCompComboBox<AutoCompletionItem> cbRole; 135 148 136 149 /** 137 150 * the menu item in the windows menu. Required to properly hide on dialog close. … … 172 185 173 186 private Component selectedTabPane; 174 187 private JTabbedPane tabbedPane; 188 private JCheckBox btnFilter = new JCheckBox(tr("Filter")); 175 189 176 190 /** 177 191 * Creates a new relation editor for the given relation. The relation will be saved if the user … … 212 226 selectionTableModel.register(); 213 227 referrerModel = new ReferringRelationsBrowserModel(relation); 214 228 229 manager = AutoCompletionManager.of(this.getLayer().data); 230 215 231 tagEditorPanel = new TagEditorPanel(relation, presetHandler); 232 TagTable tagTable = tagEditorPanel.getTable(); 216 233 populateModels(relation); 217 234 tagEditorPanel.getModel().ensureOneTag(); 218 235 236 // setting up the tag table 237 AutoCompComboBox<AutoCompletionItem> keyEditor = new AutoCompComboBox<>(); 238 AutoCompComboBox<AutoCompletionItem> valueEditor = new AutoCompComboBox<>(); 239 KeyAutoCompManager keyAutoCompManager = new KeyAutoCompManager(); 240 ValueAutoCompManager valueAutoCompManager = new ValueAutoCompManager(); 241 keyEditor.getEditorComponent().setMaxTextLength(256); 242 keyEditor.getEditorComponent().setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 243 keyEditor.getEditorComponent().enableUndoRedo(false); 244 keyEditor.getEditorComponent().addAutoCompListener(keyAutoCompManager); 245 keyEditor.addPopupMenuListener(keyAutoCompManager); 246 keyEditor.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); 247 keyEditor.setRenderer(new AutoCompItemCellRenderer(keyEditor, keyEditor.getRenderer(), null)); 248 249 valueEditor.getEditorComponent().setMaxTextLength(-1); 250 valueEditor.getEditorComponent().setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 251 valueEditor.getEditorComponent().enableUndoRedo(false); 252 valueEditor.getEditorComponent().addAutoCompListener(valueAutoCompManager); 253 valueEditor.addPopupMenuListener(valueAutoCompManager); 254 valueEditor.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); 255 valueEditor.setRenderer(new AutoCompItemCellRenderer(valueEditor, valueEditor.getRenderer(), null)); 256 257 tagTable.setRowHeight(keyEditor.getEditorComponent().getPreferredSize().height); 258 tagTable.setKeyEditor(keyEditor); 259 tagTable.setValueEditor(valueEditor); 260 219 261 // setting up the member table 220 memberTable = new MemberTable(getLayer(), getRelation(), memberTableModel); 262 AutoCompComboBox<AutoCompletionItem> cbRoleEditor = new AutoCompComboBox<>(); 263 RoleAutoCompManager roleAutoCompManager = new RoleAutoCompManager(); 264 cbRoleEditor.getEditorComponent().addAutoCompListener(roleAutoCompManager); 265 cbRoleEditor.addPopupMenuListener(roleAutoCompManager); 266 cbRoleEditor.getEditorComponent().enableUndoRedo(false); 267 Insets insets = cbRoleEditor.getEditorComponent().getInsets(); 268 cbRoleEditor.getEditorComponent().setBorder(BorderFactory.createEmptyBorder(0, insets.left, 0, insets.right)); 269 cbRoleEditor.setToolTipText(tr("Select a role for this relation member")); 270 cbRoleEditor.setRenderer(new AutoCompItemCellRenderer(cbRoleEditor, cbRoleEditor.getRenderer(), null)); 271 272 int height = cbRoleEditor.getEditorComponent().getPreferredSize().height; 273 memberTable = new MemberTable(getLayer(), cbRoleEditor, memberTableModel); 221 274 memberTable.addMouseListener(new MemberTableDblClickAdapter()); 275 memberTable.setRowHeight(height); 222 276 memberTableModel.addMemberModelListener(memberTable); 223 277 224 MemberRoleCellEditor ce = (MemberRoleCellEditor) memberTable.getColumnModel().getColumn(0).getCellEditor();225 278 selectionTable = new SelectionTable(selectionTableModel, memberTableModel); 226 selectionTable.setRowHeight( ce.getEditor().getPreferredSize().height);279 selectionTable.setRowHeight(height); 227 280 228 281 LeftButtonToolbar leftButtonToolbar = new LeftButtonToolbar(new RelationEditorActionAccess()); 229 tfRole = buildRoleTextField(this); 282 cbRole = new AutoCompComboBox<>(); 283 cbRole.getEditorComponent().addAutoCompListener(roleAutoCompManager); 284 cbRole.addPopupMenuListener(roleAutoCompManager); 285 cbRole.setText(Config.getPref().get(PREF_LASTROLE, "")); 286 cbRole.setToolTipText(tr("Select a role")); 287 cbRole.setRenderer(new AutoCompItemCellRenderer(cbRole, cbRole.getRenderer(), null)); 230 288 231 289 JSplitPane pane = buildSplitPane( 232 290 buildTagEditorPanel(tagEditorPanel), 233 buildMemberEditorPanel(leftButtonToolbar , new RelationEditorActionAccess()),291 buildMemberEditorPanel(leftButtonToolbar), 234 292 this); 235 293 pane.setPreferredSize(new Dimension(100, 100)); 236 294 … … 310 368 @Override 311 369 public void actionPerformed(ActionEvent e) { 312 370 super.actionPerformed(e); 313 tfRole.requestFocusInWindow();371 cbRole.requestFocusInWindow(); 314 372 } 315 373 }, "PASTE_MEMBERS", key, getRootPane(), memberTable, selectionTable); 316 374 } … … 446 504 } 447 505 448 506 /** 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 507 * builds the panel for the relation member editor 481 508 * @param leftButtonToolbar left button toolbar 482 * @param editorAccess The relation editor483 509 * 484 510 * @return the panel for the relation member editor 485 511 */ 486 protected static JPanel buildMemberEditorPanel( 487 LeftButtonToolbar leftButtonToolbar, IRelationEditorActionAccess editorAccess) { 512 protected JPanel buildMemberEditorPanel(LeftButtonToolbar leftButtonToolbar) { 488 513 final JPanel pnl = new JPanel(new GridBagLayout()); 489 final JScrollPane scrollPane = new JScrollPane( editorAccess.getMemberTable());514 final JScrollPane scrollPane = new JScrollPane(memberTable); 490 515 491 516 GridBagConstraints gc = new GridBagConstraints(); 492 517 gc.gridx = 0; … … 518 543 pnl.add(scrollPane, gc); 519 544 520 545 // --- 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) 546 JPanel p3 = new JPanel(new GridBagLayout()); 547 GBC gbc = GBC.std().fill(GridBagConstraints.NONE); 548 JLabel lbl = new JLabel(tr("Role:")); 549 p3.add(lbl, gbc); 550 551 p3.add(cbRole, gbc.insets(3, 3, 0, 3).fill(GridBagConstraints.HORIZONTAL)); 552 553 SetRoleAction setRoleAction = new SetRoleAction(new RelationEditorActionAccess()); 554 memberTableModel.getSelectionModel().addListSelectionListener(setRoleAction); 555 cbRole.getEditorComponent().getDocument().addDocumentListener(setRoleAction); 556 cbRole.getEditorComponent().addActionListener(setRoleAction); 557 memberTableModel.getSelectionModel().addListSelectionListener( 558 e -> cbRole.setEnabled(memberTable.getSelectedRowCount() > 0) 530 559 ); 531 editorAccess.getTextFieldRole().setEnabled(editorAccess.getMemberTable().getSelectedRowCount() > 0); 560 cbRole.setEnabled(memberTable.getSelectedRowCount() > 0); 561 532 562 JButton btnApply = new JButton(setRoleAction); 533 btnApply.setPreferredSize(new Dimension(20, 20)); 563 int height = cbRole.getPreferredSize().height; 564 btnApply.setPreferredSize(new Dimension(height, height)); 534 565 btnApply.setText(""); 535 p3.add(btnApply );566 p3.add(btnApply, gbc.weight(0, 0).fill(GridBagConstraints.NONE)); 536 567 568 btnFilter.setToolTipText(tr("Filter suggestions based on context")); 569 btnFilter.setSelected(Config.getPref().getBoolean(PREF_USE_ROLE_FILTER, false)); 570 p3.add(btnFilter, gbc.span(GridBagConstraints.REMAINDER)); 571 572 // 573 537 574 gc.gridx = 1; 538 575 gc.gridy = 2; 539 576 gc.fill = GridBagConstraints.HORIZONTAL; … … 562 599 gc.anchor = GridBagConstraints.NORTHWEST; 563 600 gc.weightx = 0.0; 564 601 gc.weighty = 1.0; 565 pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar( editorAccess),602 pnl2.add(new ScrollViewport(buildSelectionControlButtonToolbar(new RelationEditorActionAccess()), 566 603 ScrollViewport.VERTICAL_DIRECTION), gc); 567 604 568 605 gc.gridx = 1; … … 570 607 gc.weightx = 1.0; 571 608 gc.weighty = 1.0; 572 609 gc.fill = GridBagConstraints.BOTH; 573 pnl2.add(buildSelectionTablePanel( editorAccess.getSelectionTable()), gc);610 pnl2.add(buildSelectionTablePanel(selectionTable), gc); 574 611 575 612 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); 576 613 splitPane.setLeftComponent(pnl); 577 614 splitPane.setRightComponent(pnl2); 578 615 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 } 616 addWindowListener(new WindowAdapter() { 617 @Override 618 public void windowOpened(WindowEvent e) { 619 // has to be called when the window is visible, otherwise no effect 620 splitPane.setDividerLocation(0.6); 621 } 622 }); 588 623 589 624 JPanel pnl3 = new JPanel(new BorderLayout()); 590 625 pnl3.add(splitPane, BorderLayout.CENTER); … … 739 774 if (isVisible() == visible) { 740 775 return; 741 776 } 742 if (visible) {743 tagEditorPanel.initAutoCompletion(getLayer());744 }745 777 super.setVisible(visible); 746 778 Clipboard clipboard = ClipboardUtils.getClipboard(); 747 779 if (visible) { … … 754 786 clipboard.addFlavorListener(listener); 755 787 } 756 788 } else { 789 Config.getPref().put(PREF_LASTROLE, cbRole.getText()); 790 Config.getPref().putBoolean(PREF_USE_ROLE_FILTER, btnFilter.isSelected()); 791 757 792 // make sure all registered listeners are unregistered 758 793 // 759 794 memberTable.stopHighlighting(); … … 1039 1074 } 1040 1075 1041 1076 @Override 1042 public AutoCompletingTextField getTextFieldRole() {1043 return tfRole;1077 public JTextField getTextFieldRole() { 1078 return cbRole.getEditorComponent(); 1044 1079 } 1045 1046 1080 } 1047 1081 1048 1082 @Override … … 1054 1088 applyAction.updateEnabledState(); 1055 1089 } 1056 1090 } 1091 1092 private class KeyAutoCompManager extends DefaultAutoCompListener<AutoCompletionItem> { 1093 @Override 1094 protected void updateAutoCompModel(AutoCompComboBoxModel<AutoCompletionItem> model) { 1095 Map<String, AutoCompletionPriority> map; 1096 Map<String, String> keys = tagEditorPanel.getModel().getTags(); 1097 Map<String, String> matchKeys = btnFilter.isSelected() ? keys : null; 1098 1099 map = AutoCompletionManager.merge( 1100 manager.getKeysForRelation(matchKeys), 1101 manager.getPresetKeys(EnumSet.of(TaggingPresetType.RELATION), matchKeys) 1102 ); 1103 1104 model.replaceAllElements(map.entrySet().stream().filter(e -> !keys.containsKey(e.getKey())) 1105 .map(e -> new AutoCompletionItem(e.getKey(), e.getValue())) 1106 .sorted(AutoCompletionManager.ALPHABETIC_COMPARATOR).collect(Collectors.toList())); 1107 } 1108 } 1109 1110 private class ValueAutoCompManager extends DefaultAutoCompListener<AutoCompletionItem> { 1111 @Override 1112 protected void updateAutoCompModel(AutoCompComboBoxModel<AutoCompletionItem> model) { 1113 Map<String, AutoCompletionPriority> map; 1114 Map<String, String> keys = btnFilter.isSelected() ? tagEditorPanel.getModel().getTags() : null; 1115 String key = (String) tagEditorPanel.getModel().getValueAt(tagEditorPanel.getTable().getEditingRow(), 0); 1116 1117 map = AutoCompletionManager.merge( 1118 manager.getValuesForRelation(keys, key), 1119 manager.getPresetValues(EnumSet.of(TaggingPresetType.RELATION), keys, key) 1120 ); 1121 1122 model.replaceAllElements(map.entrySet().stream() 1123 .map(e -> new AutoCompletionItem(e.getKey(), e.getValue())) 1124 .sorted(AutoCompletionManager.ALPHABETIC_COMPARATOR).collect(Collectors.toList())); 1125 } 1126 } 1127 1128 /** 1129 * Returns the roles currently edited in the members table. 1130 * @param types the preset types to include, (node / way / relation ...) or null to include all types 1131 * @return the roles currently edited in the members table. 1132 */ 1133 private Map<String, AutoCompletionPriority> getCurrentRoles(Collection<TaggingPresetType> types) { 1134 Map<String, AutoCompletionPriority> map = new HashMap<>(); 1135 for (int i = 0; i < memberTableModel.getRowCount(); ++i) { 1136 RelationMember member = memberTableModel.getValue(i); 1137 if (types == null || types.contains(TaggingPresetType.forPrimitiveType(member.getDisplayType()))) { 1138 map.merge(member.getRole(), AutoCompletionPriority.IS_IN_SELECTION, AutoCompletionPriority::mergeWith); 1139 } 1140 } 1141 return map; 1142 } 1143 1144 private class RoleAutoCompManager extends DefaultAutoCompListener<AutoCompletionItem> { 1145 @Override 1146 protected void updateAutoCompModel(AutoCompComboBoxModel<AutoCompletionItem> model) { 1147 Map<String, AutoCompletionPriority> map; 1148 Map<String, String> keys = btnFilter.isSelected() ? tagEditorPanel.getModel().getTags() : null; 1149 1150 EnumSet<TaggingPresetType> selectedTypes = EnumSet.noneOf(TaggingPresetType.class); 1151 for (RelationMember member : memberTableModel.getSelectedMembers()) { 1152 selectedTypes.add(TaggingPresetType.forPrimitiveType(member.getDisplayType())); 1153 } 1154 1155 map = AutoCompletionManager.merge( 1156 manager.getRolesForRelation(keys, selectedTypes), 1157 manager.getPresetRoles(keys, selectedTypes), 1158 getCurrentRoles(selectedTypes) 1159 ); 1160 1161 // turn into AutoCompletionItems 1162 model.replaceAllElements(map.entrySet().stream() 1163 .map(e -> new AutoCompletionItem(e.getKey(), e.getValue())) 1164 .sorted(AutoCompletionManager.ALPHABETIC_COMPARATOR).collect(Collectors.toList())); 1165 } 1166 } 1057 1167 } -
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); … … 83 81 if (!GraphicsEnvironment.isHeadless()) { 84 82 setTransferHandler(new MemberTransferHandler()); 85 83 setFillsViewportHeight(true); // allow drop on empty table 86 if (!GraphicsEnvironment.isHeadless()) { 87 setDragEnabled(true); 88 } 84 setDragEnabled(true); 89 85 setDropMode(DropMode.INSERT_ROWS); 90 86 } 91 87 } -
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/io/UploadDialog.java
18 18 import java.beans.PropertyChangeListener; 19 19 import java.lang.Character.UnicodeBlock; 20 20 import java.util.ArrayList; 21 import java.util. Collections;21 import java.util.Arrays; 22 22 import java.util.HashMap; 23 23 import java.util.List; 24 24 import java.util.Locale; … … 43 43 import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction; 44 44 import org.openstreetmap.josm.gui.help.HelpUtil; 45 45 import org.openstreetmap.josm.gui.tagging.TagEditorPanel; 46 import org.openstreetmap.josm.gui.tagging.TagTable; 47 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBox; 48 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel; 49 import org.openstreetmap.josm.gui.tagging.ac.DefaultAutoCompListener; 46 50 import org.openstreetmap.josm.gui.util.GuiHelper; 47 51 import org.openstreetmap.josm.gui.util.MultiLineFlowLayout; 48 52 import org.openstreetmap.josm.gui.util.WindowGeometry; … … 88 92 /** the model keeping the state of the changeset tags */ 89 93 private final transient UploadDialogModel model = new UploadDialogModel(); 90 94 91 private transient DataSet dataSet;92 93 95 /** 94 96 * Constructs a new {@code UploadDialog}. 95 97 */ … … 141 143 pnlTagEditor = new TagEditorPanel(model, null, Changeset.MAX_CHANGESET_TAG_LENGTH); 142 144 pnlTagEditorBorder.add(pnlTagEditor, BorderLayout.CENTER); 143 145 146 // setting up the tag table 147 TagTable tagTable = pnlTagEditor.getTable(); 148 AutoCompComboBox<String> keyEditor = new AutoCompComboBox<>(); 149 AutoCompComboBox<String> valueEditor = new AutoCompComboBox<>(); 150 KeyAutoCompManager keyAutoCompManager = new KeyAutoCompManager(); 151 ValueAutoCompManager valueAutoCompManager = new ValueAutoCompManager(); 152 keyEditor.getEditorComponent().setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH); 153 keyEditor.getEditorComponent().setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 154 keyEditor.getEditorComponent().enableUndoRedo(false); 155 keyEditor.getEditorComponent().addAutoCompListener(keyAutoCompManager); 156 keyEditor.addPopupMenuListener(keyAutoCompManager); 157 keyEditor.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); 158 159 valueEditor.getEditorComponent().setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH); 160 valueEditor.getEditorComponent().setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 161 valueEditor.getEditorComponent().enableUndoRedo(false); 162 valueEditor.getEditorComponent().addAutoCompListener(valueAutoCompManager); 163 valueEditor.addPopupMenuListener(valueAutoCompManager); 164 valueEditor.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); 165 166 tagTable.setRowHeight(keyEditor.getEditorComponent().getPreferredSize().height); 167 tagTable.setKeyEditor(keyEditor); 168 tagTable.setValueEditor(valueEditor); 169 144 170 pnlChangesetManagement = new ChangesetManagementPanel(); 145 171 pnlUploadStrategySelectionPanel = new UploadStrategySelectionPanel(); 146 172 pnlSettings.add(pnlChangesetManagement, GBC.eop().fill(GridBagConstraints.HORIZONTAL)); … … 228 254 * this in the constructor because the dialog is a singleton. 229 255 * 230 256 * @param dataSet The Dataset we want to upload 257 * @param toUpload The primitves to upload 231 258 * @since 18173 232 259 */ 233 public void initLifeCycle(DataSet dataSet ) {260 public void initLifeCycle(DataSet dataSet, APIDataSet toUpload) { 234 261 Map<String, String> map = new HashMap<>(); 235 this.dataSet = dataSet;236 262 pnlBasicUploadSettings.initLifeCycle(map); 237 263 pnlChangesetManagement.initLifeCycle(); 238 264 model.clear(); 239 model.putAll(map); // init with tags from history240 model.putAll( this.dataSet); // overwrite with tags from the dataset265 model.putAll(map); // init with tags from history 266 model.putAll(dataSet); // overwrite with tags from the dataset 241 267 242 268 tpConfigPanels.setSelectedIndex(0); 243 pnlTagEditor.initAutoCompletion(MainApplication.getLayerManager().getEditLayer());244 269 pnlUploadStrategySelectionPanel.initFromPreferences(); 245 270 246 271 // update the summary … … 247 272 UploadParameterSummaryPanel sumPnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel(); 248 273 sumPnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification()); 249 274 sumPnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload()); 250 }251 275 252 /**253 * Sets the collection of primitives to upload254 *255 * @param toUpload the dataset with the objects to upload. If null, assumes the empty256 * set of objects to upload257 *258 */259 public void setUploadedPrimitives(APIDataSet toUpload) {260 UploadParameterSummaryPanel sumPnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();261 if (toUpload == null) {262 if (pnlUploadedObjects != null) {263 List<OsmPrimitive> emptyList = Collections.emptyList();264 pnlUploadedObjects.setUploadedPrimitives(emptyList, emptyList, emptyList);265 sumPnl.setNumObjects(0);266 }267 return;268 }269 276 List<OsmPrimitive> l = toUpload.getPrimitives(); 270 277 pnlBasicUploadSettings.setUploadedPrimitives(l); 271 pnlUploadedObjects.setUploadedPrimitives( 272 toUpload.getPrimitivesToAdd(), 273 toUpload.getPrimitivesToUpdate(), 274 toUpload.getPrimitivesToDelete() 275 ); 278 pnlUploadedObjects.removeAll(); 279 pnlUploadedObjects.build(toUpload); 276 280 sumPnl.setNumObjects(l.size()); 277 281 pnlUploadStrategySelectionPanel.setNumUploadedObjects(l.size()); 278 282 } … … 512 516 } 513 517 } 514 518 519 private static class KeyAutoCompManager extends DefaultAutoCompListener<String> { 520 @Override 521 protected void updateAutoCompModel(AutoCompComboBoxModel<String> model) { 522 model.replaceAllElements(Arrays.asList("comment", "source", "review_requested", "created_by", "imagery_used", "locale")); 523 // FIXME add more tags from user upload history? 524 } 525 } 526 527 private class ValueAutoCompManager extends DefaultAutoCompListener<String> { 528 @Override 529 protected void updateAutoCompModel(AutoCompComboBoxModel<String> model) { 530 String key = (String) pnlTagEditor.getModel().getValueAt(pnlTagEditor.getTable().getEditingRow(), 0); 531 if ("comment".equals(key)) { 532 model.prefs(x ->x, x -> x).load(BasicUploadSettingsPanel.COMMENT_HISTORY_KEY); 533 return; 534 } 535 if ("source".equals(key)) { 536 model.prefs(x -> x, x -> x).load(BasicUploadSettingsPanel.SOURCE_HISTORY_KEY, BasicUploadSettingsPanel.getDefaultSources()); 537 return; 538 } 539 if ("review_requested".equals(key)) { 540 model.replaceAllElements(Arrays.asList("yes", "")); 541 return; 542 } 543 model.replaceAllElements(Arrays.asList("")); 544 } 545 } 546 515 547 /* -------------------------------------------------------------------------- */ 516 548 /* Interface PropertyChangeListener */ 517 549 /* -------------------------------------------------------------------------- */ … … 604 636 * @since 14251 605 637 */ 606 638 public void clean() { 607 setUploadedPrimitives(null); 608 dataSet = null; 639 pnlUploadedObjects.removeAll(); 609 640 } 610 641 } -
src/org/openstreetmap/josm/gui/io/UploadDialogModel.java
75 75 * @return the hashtags separated by ";" or null 76 76 */ 77 77 String findHashTags(String comment) { 78 String hash tags = String.join(";",78 String hashTags = String.join(";", 79 79 Arrays.stream(comment.split("\\s", -1)) 80 80 .map(s -> Utils.strip(s, ",;")) 81 81 .filter(s -> s.matches("#[a-zA-Z0-9][-_a-zA-Z0-9]+")) 82 82 .collect(Collectors.toList())); 83 return hash tags.isEmpty() ? null : hashtags;83 return hashTags.isEmpty() ? null : hashTags; 84 84 } 85 85 86 86 /** -
src/org/openstreetmap/josm/gui/io/UploadPrimitivesTask.java
401 401 // return to the upload dialog 402 402 // 403 403 toUpload.removeProcessed(processedPrimitives); 404 UploadDialog.getUploadDialog(). setUploadedPrimitives(toUpload);404 UploadDialog.getUploadDialog().initLifeCycle(null, toUpload); 405 405 UploadDialog.getUploadDialog().setVisible(true); 406 406 break; 407 407 } -
src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java
69 69 70 70 protected JPanel buildUploadStrategyPanel() { 71 71 JPanel pnl = new JPanel(new GridBagLayout()); 72 pnl.setBorder(BorderFactory.createTitledBorder(tr("Please select theupload strategy:")));72 pnl.setBorder(BorderFactory.createTitledBorder(tr("Please select an upload strategy:"))); 73 73 ButtonGroup bgStrategies = new ButtonGroup(); 74 74 rbStrategy = new EnumMap<>(UploadStrategy.class); 75 75 lblNumRequests = new EnumMap<>(UploadStrategy.class); -
src/org/openstreetmap/josm/gui/io/UploadedObjectsSummaryPanel.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.io; 3 3 4 import static org.openstreetmap.josm.tools.I18n.tr;5 4 import static org.openstreetmap.josm.tools.I18n.trn; 6 5 7 6 import java.awt.GridBagConstraints; … … 8 7 import java.awt.GridBagLayout; 9 8 import java.awt.event.MouseAdapter; 10 9 import java.awt.event.MouseEvent; 11 import java.util.ArrayList;12 10 import java.util.Collections; 13 11 import java.util.List; 14 import java.util.Optional;15 12 16 import javax.swing.AbstractListModel; 13 import javax.swing.BoxLayout; 14 import javax.swing.DefaultListModel; 17 15 import javax.swing.JLabel; 18 16 import javax.swing.JList; 19 17 import javax.swing.JPanel; … … 20 18 import javax.swing.JScrollPane; 21 19 22 20 import org.openstreetmap.josm.actions.AutoScaleAction; 21 import org.openstreetmap.josm.data.APIDataSet; 23 22 import org.openstreetmap.josm.data.osm.OsmPrimitive; 24 23 import org.openstreetmap.josm.gui.PrimitiveRenderer; 25 24 … … 28 27 * @since 2599 29 28 */ 30 29 public class UploadedObjectsSummaryPanel extends JPanel { 31 /** the list with the added primitives */ 32 private PrimitiveList lstAdd; 33 private JLabel lblAdd; 34 private JScrollPane spAdd; 35 /** the list with the updated primitives */ 36 private PrimitiveList lstUpdate; 37 private JLabel lblUpdate; 38 private JScrollPane spUpdate; 39 /** the list with the deleted primitives */ 40 private PrimitiveList lstDelete; 41 private JLabel lblDelete; 42 private JScrollPane spDelete; 30 /** 31 * Zooms to the primitive on double-click 32 */ 33 private static MouseAdapter mouseListener = new MouseAdapter() { 34 @Override 35 public void mouseClicked(MouseEvent evt) { 36 if (evt.getButton() == MouseEvent.BUTTON1 && evt.getClickCount() == 2) { 37 @SuppressWarnings("unchecked") 38 JList<OsmPrimitive> list = (JList<OsmPrimitive>) evt.getSource(); 39 int index = list.locationToIndex(evt.getPoint()); 40 AutoScaleAction.zoomTo(Collections.singleton(list.getModel().getElementAt(index))); 41 } 42 } 43 }; 43 44 44 45 /** 46 * A JLabel and a JList 47 */ 48 private static class ListPanel extends JPanel { 49 /** 50 * Constructor 51 * 52 * @param primitives the list of primitives 53 * @param singular the singular form of the label 54 * @param plural the plural form of the label 55 */ 56 ListPanel(List<OsmPrimitive> primitives, String singular, String plural) { 57 DefaultListModel<OsmPrimitive> model = new DefaultListModel<>(); 58 JList<OsmPrimitive> jList = new JList<>(model); 59 primitives.forEach(model::addElement); 60 jList.setCellRenderer(new PrimitiveRenderer()); 61 jList.addMouseListener(mouseListener); 62 JScrollPane scrollPane = new JScrollPane(jList); 63 JLabel label = new JLabel(trn(singular, plural, model.size(), model.size())); 64 label.setLabelFor(jList); 65 this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); 66 this.add(label); 67 this.add(scrollPane); 68 } 69 } 70 71 /** 45 72 * Constructs a new {@code UploadedObjectsSummaryPanel}. 46 73 */ 47 74 public UploadedObjectsSummaryPanel() { 48 build();75 super(new GridBagLayout()); 49 76 } 50 77 51 protected void build() {52 setLayout(new GridBagLayout());53 PrimitiveRenderer renderer = new PrimitiveRenderer();54 MouseAdapter mouseListener = new MouseAdapter() {55 @Override56 public void mouseClicked(MouseEvent evt) {57 if (evt.getButton() == MouseEvent.BUTTON1 && evt.getClickCount() == 2) {58 PrimitiveList list = (PrimitiveList) evt.getSource();59 int index = list.locationToIndex(evt.getPoint());60 AutoScaleAction.zoomTo(Collections.singleton(list.getModel().getElementAt(index)));61 }62 }63 };64 // initialize the three lists for uploaded primitives, but don't add them to the dialog yet, see setUploadedPrimitives()65 //66 lstAdd = new PrimitiveList();67 lstAdd.setCellRenderer(renderer);68 lstAdd.addMouseListener(mouseListener);69 lstAdd.setVisibleRowCount(Math.min(lstAdd.getModel().getSize(), 10));70 spAdd = new JScrollPane(lstAdd);71 lblAdd = new JLabel(tr("Objects to add:"));72 lblAdd.setLabelFor(lstAdd);73 74 lstUpdate = new PrimitiveList();75 lstUpdate.setCellRenderer(renderer);76 lstUpdate.addMouseListener(mouseListener);77 lstUpdate.setVisibleRowCount(Math.min(lstUpdate.getModel().getSize(), 10));78 spUpdate = new JScrollPane(lstUpdate);79 lblUpdate = new JLabel(tr("Objects to modify:"));80 lblUpdate.setLabelFor(lstUpdate);81 82 lstDelete = new PrimitiveList();83 lstDelete.setCellRenderer(renderer);84 lstDelete.addMouseListener(mouseListener);85 lstDelete.setVisibleRowCount(Math.min(lstDelete.getModel().getSize(), 10));86 spDelete = new JScrollPane(lstDelete);87 lblDelete = new JLabel(tr("Objects to delete:"));88 lblDelete.setLabelFor(lstDelete);89 }90 91 78 /** 92 * Sets the collections of primitives which will be uploaded79 * Builds the panel 93 80 * 94 * @param add the collection of primitives to add 95 * @param update the collection of primitives to update 96 * @param delete the collection of primitives to delete 81 * @param toUpload the primitives to upload 97 82 */ 98 public void setUploadedPrimitives(List<OsmPrimitive> add, List<OsmPrimitive> update, List<OsmPrimitive> delete) { 99 lstAdd.getPrimitiveListModel().setPrimitives(add); 100 lstUpdate.getPrimitiveListModel().setPrimitives(update); 101 lstDelete.getPrimitiveListModel().setPrimitives(delete); 102 103 GridBagConstraints gcLabel = new GridBagConstraints(); 104 gcLabel.fill = GridBagConstraints.HORIZONTAL; 105 gcLabel.weightx = 1.0; 106 gcLabel.weighty = 0.0; 107 gcLabel.anchor = GridBagConstraints.FIRST_LINE_START; 108 83 public void build(APIDataSet toUpload) { 109 84 GridBagConstraints gcList = new GridBagConstraints(); 110 85 gcList.fill = GridBagConstraints.BOTH; 111 86 gcList.weightx = 1.0; 112 87 gcList.weighty = 1.0; 113 88 gcList.anchor = GridBagConstraints.CENTER; 89 114 90 removeAll(); 115 int y = -1; 116 if (!add.isEmpty()) { 117 y++; 118 gcLabel.gridy = y; 119 lblAdd.setText(trn("{0} object to add:", "{0} objects to add:", add.size(), add.size())); 120 add(lblAdd, gcLabel); 121 y++; 122 gcList.gridy = y; 123 add(spAdd, gcList); 91 List<OsmPrimitive> list = toUpload.getPrimitivesToAdd(); 92 if (!list.isEmpty()) { 93 gcList.gridy++; 94 add(new ListPanel(list, "{0} object to add:", "{0} objects to add:"), gcList); 124 95 } 125 if (!update.isEmpty()) { 126 y++; 127 gcLabel.gridy = y; 128 lblUpdate.setText(trn("{0} object to modify:", "{0} objects to modify:", update.size(), update.size())); 129 add(lblUpdate, gcLabel); 130 y++; 131 gcList.gridy = y; 132 add(spUpdate, gcList); 96 list = toUpload.getPrimitivesToUpdate(); 97 if (!list.isEmpty()) { 98 gcList.gridy++; 99 add(new ListPanel(list, "{0} object to modify:", "{0} objects to modify:"), gcList); 133 100 } 134 if (!delete.isEmpty()) { 135 y++; 136 gcLabel.gridy = y; 137 lblDelete.setText(trn("{0} object to delete:", "{0} objects to delete:", delete.size(), delete.size())); 138 add(lblDelete, gcLabel); 139 y++; 140 gcList.gridy = y; 141 add(spDelete, gcList); 101 list = toUpload.getPrimitivesToDelete(); 102 if (!list.isEmpty()) { 103 gcList.gridy++; 104 add(new ListPanel(list, "{0} object to delete:", "{0} objects to delete:"), gcList); 142 105 } 143 106 revalidate(); 107 repaint(); 144 108 } 145 146 /**147 * Replies the number of objects to upload148 *149 * @return the number of objects to upload150 */151 public int getNumObjectsToUpload() {152 return lstAdd.getModel().getSize()153 + lstUpdate.getModel().getSize()154 + lstDelete.getModel().getSize();155 }156 157 /**158 * A simple list of OSM primitives.159 */160 static class PrimitiveList extends JList<OsmPrimitive> {161 /**162 * Constructs a new {@code PrimitiveList}.163 */164 PrimitiveList() {165 super(new PrimitiveListModel());166 }167 168 public PrimitiveListModel getPrimitiveListModel() {169 return (PrimitiveListModel) getModel();170 }171 }172 173 /**174 * A list model for a list of OSM primitives.175 */176 static class PrimitiveListModel extends AbstractListModel<OsmPrimitive> {177 private transient List<OsmPrimitive> primitives;178 179 /**180 * Constructs a new {@code PrimitiveListModel}.181 */182 PrimitiveListModel() {183 primitives = new ArrayList<>();184 }185 186 PrimitiveListModel(List<OsmPrimitive> primitives) {187 setPrimitives(primitives);188 }189 190 public void setPrimitives(List<OsmPrimitive> primitives) {191 this.primitives = Optional.ofNullable(primitives).orElseGet(ArrayList::new);192 fireContentsChanged(this, 0, getSize());193 }194 195 @Override196 public OsmPrimitive getElementAt(int index) {197 if (primitives == null) return null;198 return primitives.get(index);199 }200 201 @Override202 public int getSize() {203 if (primitives == null) return 0;204 return primitives.size();205 }206 }207 109 } -
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
1280 1280 @Override 1281 1281 public AbstractUploadDialog getUploadDialog() { 1282 1282 UploadDialog dialog = UploadDialog.getUploadDialog(); 1283 dialog. setUploadedPrimitives(new APIDataSet(data));1283 dialog.initLifeCycle(data, new APIDataSet(data)); 1284 1284 return dialog; 1285 1285 } 1286 1286 -
src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java
145 145 } 146 146 147 147 @Override 148 public Object getValueAt(int rowIndex, int columnIndex) { 149 if (rowIndex >= getRowCount()) 150 throw new IndexOutOfBoundsException("unexpected rowIndex: rowIndex=" + rowIndex); 151 152 return tags.get(rowIndex); 148 public Object getValueAt(int row, int col) { 149 if (row >= getRowCount()) 150 throw new IndexOutOfBoundsException("unexpected row: row=" + row); 151 if (col >= getColumnCount()) 152 throw new IndexOutOfBoundsException("unexpected col: col=" + col); 153 TagModel tag = get(row); 154 switch(col) { 155 case 0: 156 return tag.getName(); 157 case 1: 158 return tag.getValue(); 159 default: // Do nothing 160 } 161 return null; 153 162 } 154 163 155 164 @Override -
src/org/openstreetmap/josm/gui/tagging/TagEditorPanel.java
21 21 import org.openstreetmap.josm.gui.dialogs.properties.HelpAction; 22 22 import org.openstreetmap.josm.gui.dialogs.properties.HelpTagAction; 23 23 import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel; 24 import org.openstreetmap.josm.gui.layer.OsmDataLayer;25 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;26 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;27 24 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler; 28 25 import org.openstreetmap.josm.spi.preferences.Config; 29 import org.openstreetmap.josm.tools.CheckParameterUtil;30 26 31 27 /** 32 28 * TagEditorPanel is a {@link JPanel} which can be embedded as UI component in … … 90 86 JButton btn = new JButton(action); 91 87 pnl.add(btn); 92 88 btn.setMargin(new Insets(0, 0, 0, 0)); 93 tagTable.addComponentNotStoppingCellEditing(btn);94 89 } 95 90 96 91 /** … … 147 142 * @param presetHandler tagging preset handler 148 143 */ 149 144 public TagEditorPanel(OsmPrimitive primitive, TaggingPresetHandler presetHandler) { 150 this(new TagEditorModel().forPrimitive(primitive), presetHandler, 0);145 this(new TagEditorModel().forPrimitive(primitive), presetHandler, -1); 151 146 } 152 147 153 148 /** … … 188 183 } 189 184 190 185 /** 191 * Initializes the auto completion infrastructure used in this 192 * tag editor panel. {@code layer} is the data layer from whose data set 193 * tag values are proposed as auto completion items. 194 * 195 * @param layer the data layer. Must not be null. 196 * @throws IllegalArgumentException if {@code layer} is null 186 * Returns the JTable 187 * @return the JTable 197 188 */ 198 public void initAutoCompletion(OsmDataLayer layer) { 199 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 200 201 AutoCompletionManager autocomplete = AutoCompletionManager.of(layer.data); 202 AutoCompletionList acList = new AutoCompletionList(); 203 204 TagCellEditor editor = (TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor(); 205 editor.setAutoCompletionManager(autocomplete); 206 editor.setAutoCompletionList(acList); 207 editor = (TagCellEditor) tagTable.getColumnModel().getColumn(1).getCellEditor(); 208 editor.setAutoCompletionManager(autocomplete); 209 editor.setAutoCompletionList(acList); 189 public TagTable getTable() { 190 return tagTable; 210 191 } 211 192 212 193 @Override -
src/org/openstreetmap/josm/gui/tagging/TagTable.java
5 5 6 6 import java.awt.Component; 7 7 import java.awt.Dimension; 8 import java.awt.KeyboardFocusManager;9 import java.awt.Window;10 8 import java.awt.event.ActionEvent; 11 9 import java.awt.event.KeyEvent; 12 10 import java.beans.PropertyChangeEvent; 13 11 import java.beans.PropertyChangeListener; 14 12 import java.util.Collections; 15 import java.util.EventObject;16 import java.util.concurrent.CopyOnWriteArrayList;17 13 18 14 import javax.swing.AbstractAction; 19 15 import javax.swing.CellEditor; … … 21 17 import javax.swing.JTable; 22 18 import javax.swing.KeyStroke; 23 19 import javax.swing.ListSelectionModel; 24 import javax.swing.SwingUtilities;25 20 import javax.swing.event.ListSelectionEvent; 26 21 import javax.swing.event.ListSelectionListener; 27 import javax.swing.text.JTextComponent; 22 import javax.swing.table.DefaultTableCellRenderer; 23 import javax.swing.table.TableCellEditor; 28 24 29 25 import org.openstreetmap.josm.data.osm.Relation; 30 26 import org.openstreetmap.josm.data.osm.TagMap; 31 27 import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler; 32 28 import org.openstreetmap.josm.gui.tagging.TagEditorModel.EndEditListener; 33 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;34 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;35 29 import org.openstreetmap.josm.gui.widgets.JosmTable; 36 30 import org.openstreetmap.josm.tools.ImageProvider; 37 import org.openstreetmap.josm.tools.Logging;38 31 import org.openstreetmap.josm.tools.Utils; 39 32 40 33 /** … … 42 35 * @since 1762 43 36 */ 44 37 public class TagTable extends JosmTable implements EndEditListener { 45 /** the table cell editor used by this table */46 private TagCellEditor editor;47 38 private final TagEditorModel model; 48 39 private Component nextFocusComponent; 49 40 50 /** a list of components to which focus can be transferred without stopping51 * cell editing this table.52 */53 private final CopyOnWriteArrayList<Component> doNotStopCellEditingWhenFocused = new CopyOnWriteArrayList<>();54 private transient CellEditorRemover editorRemover;55 56 41 /** 57 42 * Action to be run when the user navigates to the next cell in the table, 58 43 * for instance by pressing TAB or ENTER. The action alters the standard … … 88 73 row++; 89 74 } else if (col == 1 && row == getRowCount()-1) { 90 75 // we are at the end. Append an empty row and move the focus to its second column 91 String key = ( (TagModel) model.getValueAt(row, 0)).getName();76 String key = (String) model.getValueAt(row, 0); 92 77 if (!Utils.isStripEmpty(key)) { 93 78 model.appendNewTag(); 94 79 col = 0; … … 243 228 cEditor.stopCellEditing(); 244 229 } 245 230 final int rowIdx = model.getRowCount()-1; 246 if (rowIdx < 0 || !Utils.isStripEmpty(( (TagModel) model.getValueAt(rowIdx, 0)).getName())) {231 if (rowIdx < 0 || !Utils.isStripEmpty((String) model.getValueAt(rowIdx, 0))) { 247 232 model.appendNewTag(); 248 233 } 249 234 requestFocusInCell(model.getRowCount()-1, 0); … … 322 307 } 323 308 324 309 /** 325 * initialize the table 326 * @param maxCharacters maximum number of characters allowed for keys and values, 0 for unlimited 310 * Creates a new tag table 311 * 312 * @param model the tag editor model 313 * @param maxCharacters maximum number of characters allowed for keys and values, -1 for unlimited 327 314 */ 328 protected final void init(final int maxCharacters) { 329 setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 315 public TagTable(TagEditorModel model, final int maxCharacters) { 316 super(model, new TagTableColumnModelBuilder(new DefaultTableCellRenderer(), tr("Key"), tr("Value")) 317 .setSelectionModel(model.getColumnSelectionModel()).build(), 318 model.getRowSelectionModel()); 319 320 this.model = model; 321 model.setEndEditListener(this); 322 323 setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); 330 324 setRowSelectionAllowed(true); 331 325 setColumnSelectionAllowed(true); 332 326 setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 327 putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); 333 328 334 329 // make ENTER behave like TAB 335 330 // … … 354 349 getActionMap().put("addTag", addAction); 355 350 356 351 pasteAction = new PasteAction(); 352 } 357 353 358 // create the table cell editor and set it to key and value columns 359 // 360 TagCellEditor tmpEditor = new TagCellEditor(maxCharacters); 361 setRowHeight(tmpEditor.getEditor().getPreferredSize().height); 362 setTagCellEditor(tmpEditor); 354 /** 355 * Sets a TableCellEditor for the keys column. 356 * @param editor the editor to set 357 */ 358 public void setKeyEditor(TableCellEditor editor) { 359 getColumnModel().getColumn(0).setCellEditor(editor); 363 360 } 364 361 365 362 /** 366 * Creates a new tag table 367 * 368 * @param model the tag editor model 369 * @param maxCharacters maximum number of characters allowed for keys and values, 0 for unlimited 363 * Sets a TableCellEditor for the values column. 364 * @param editor the editor to set 370 365 */ 371 public TagTable(TagEditorModel model, final int maxCharacters) { 372 super(model, new TagTableColumnModelBuilder(new TagCellRenderer(), tr("Key"), tr("Value")) 373 .setSelectionModel(model.getColumnSelectionModel()).build(), 374 model.getRowSelectionModel()); 375 this.model = model; 376 model.setEndEditListener(this); 377 init(maxCharacters); 366 public void setValueEditor(TableCellEditor editor) { 367 getColumnModel().getColumn(1).setCellEditor(editor); 378 368 } 379 369 380 370 @Override 371 public boolean getDragEnabled() { 372 // fix for comboboxes flashing when clicking the cell where the arrow button will be 373 // maybe a late focus request wants to focus the cb when the popup is already open? 374 // see: BasicTableUI#adjustSelection and mouseReleasedDND 375 return true; 376 } 377 378 @Override 381 379 public Dimension getPreferredSize() { 382 380 return getPreferredFullWidthSize(); 383 381 } … … 400 398 } 401 399 402 400 /** 403 * Sets the editor autocompletion list404 * @param autoCompletionList autocompletion list405 */406 public void setAutoCompletionList(AutoCompletionList autoCompletionList) {407 if (autoCompletionList == null)408 return;409 if (editor != null) {410 editor.setAutoCompletionList(autoCompletionList);411 }412 }413 414 /**415 * Sets the autocompletion manager that should be used for editing the cells416 * @param autocomplete The {@link AutoCompletionManager}417 */418 public void setAutoCompletionManager(AutoCompletionManager autocomplete) {419 if (autocomplete == null) {420 Logging.warn("argument autocomplete should not be null. Aborting.");421 Logging.error(new Exception());422 return;423 }424 if (editor != null) {425 editor.setAutoCompletionManager(autocomplete);426 }427 }428 429 /**430 * Gets the {@link AutoCompletionList} the cell editor is synchronized with431 * @return The list432 */433 public AutoCompletionList getAutoCompletionList() {434 if (editor != null)435 return editor.getAutoCompletionList();436 else437 return null;438 }439 440 /**441 401 * Sets the next component to request focus after navigation (with tab or enter). 442 402 * @param nextFocusComponent next component to request focus after navigation (with tab or enter) 443 403 */ … … 446 406 } 447 407 448 408 /** 449 * Gets the editor that is used for the table cells450 * @return The editor that is used when the user wants to enter text into a cell451 */452 public TagCellEditor getTableCellEditor() {453 return editor;454 }455 456 /**457 * Inject a tag cell editor in the tag table458 *459 * @param editor tag cell editor460 */461 public void setTagCellEditor(TagCellEditor editor) {462 endCellEditing();463 this.editor = editor;464 getColumnModel().getColumn(0).setCellEditor(editor);465 getColumnModel().getColumn(1).setCellEditor(editor);466 }467 468 /**469 409 * Request the focus in a specific cell 470 410 * @param row The row index 471 411 * @param col The column index … … 475 415 editCellAt(row, col); 476 416 Component c = getEditorComponent(); 477 417 if (c != null) { 478 if (!c.requestFocusInWindow()) { 479 Logging.warn("Unable to request focus for " + c); 480 } 481 if (c instanceof JTextComponent) { 482 ((JTextComponent) c).selectAll(); 483 } 418 c.requestFocusInWindow(); 484 419 } 485 // there was a bug here - on older 1.6 Java versions Tab was not working486 // after such activation. In 1.7 it works OK,487 // previous solution of using awt.Robot was resetting mouse speed on Windows488 420 } 489 421 490 /**491 * Marks a component that may be focused without stopping the cell editing492 * @param component The component493 */494 public void addComponentNotStoppingCellEditing(Component component) {495 if (component == null) return;496 doNotStopCellEditingWhenFocused.addIfAbsent(component);497 }498 499 /**500 * Removes a component added with {@link #addComponentNotStoppingCellEditing(Component)}501 * @param component The component502 */503 public void removeComponentNotStoppingCellEditing(Component component) {504 if (component == null) return;505 doNotStopCellEditingWhenFocused.remove(component);506 }507 508 422 @Override 509 public boolean editCellAt(int row, int column, EventObject e) {510 511 // a snipped copied from the Java 1.5 implementation of JTable512 //513 if (cellEditor != null && !cellEditor.stopCellEditing())514 return false;515 516 if (row < 0 || row >= getRowCount() ||517 column < 0 || column >= getColumnCount())518 return false;519 520 if (!isCellEditable(row, column))521 return false;522 523 // make sure our custom implementation of CellEditorRemover is created524 if (editorRemover == null) {525 KeyboardFocusManager fm =526 KeyboardFocusManager.getCurrentKeyboardFocusManager();527 editorRemover = new CellEditorRemover(fm);528 fm.addPropertyChangeListener("permanentFocusOwner", editorRemover);529 }530 531 // delegate to the default implementation532 return super.editCellAt(row, column, e);533 }534 535 @Override536 423 public void endCellEditing() { 537 if (isEditing()) { 538 CellEditor cEditor = getCellEditor(); 539 if (cEditor != null) { 540 // First attempt to commit. If this does not work, cancel. 541 cEditor.stopCellEditing(); 424 TableCellEditor cEditor = getCellEditor(); 425 if (cEditor != null) { 426 // First attempt to commit. If this does not work, cancel. 427 if (!cEditor.stopCellEditing()) { 542 428 cEditor.cancelCellEditing(); 543 429 } 544 430 } 545 431 } 546 432 547 @Override548 public void removeEditor() {549 // make sure we unregister our custom implementation of CellEditorRemover550 KeyboardFocusManager.getCurrentKeyboardFocusManager().551 removePropertyChangeListener("permanentFocusOwner", editorRemover);552 editorRemover = null;553 super.removeEditor();554 }555 556 @Override557 public void removeNotify() {558 // make sure we unregister our custom implementation of CellEditorRemover559 KeyboardFocusManager.getCurrentKeyboardFocusManager().560 removePropertyChangeListener("permanentFocusOwner", editorRemover);561 editorRemover = null;562 super.removeNotify();563 }564 565 /**566 * This is a custom implementation of the CellEditorRemover used in JTable567 * to handle the client property <code>terminateEditOnFocusLost</code>.568 *569 * This implementation also checks whether focus is transferred to one of a list570 * of dedicated components, see {@link TagTable#doNotStopCellEditingWhenFocused}.571 * A typical example for such a component is a button in {@link TagEditorPanel}572 * which isn't a child component of {@link TagTable} but which should respond to573 * to focus transfer in a similar way to a child of TagTable.574 *575 */576 class CellEditorRemover implements PropertyChangeListener {577 private final KeyboardFocusManager focusManager;578 579 CellEditorRemover(KeyboardFocusManager fm) {580 this.focusManager = fm;581 }582 583 @Override584 public void propertyChange(PropertyChangeEvent ev) {585 if (!isEditing())586 return;587 588 Component c = focusManager.getPermanentFocusOwner();589 while (c != null) {590 if (c == TagTable.this)591 // focus remains inside the table592 return;593 if (doNotStopCellEditingWhenFocused.contains(c))594 // focus remains on one of the associated components595 return;596 else if (c instanceof Window) {597 if (c == SwingUtilities.getRoot(TagTable.this) && !getCellEditor().stopCellEditing()) {598 getCellEditor().cancelCellEditing();599 }600 break;601 }602 c = c.getParent();603 }604 }605 }606 433 } -
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; 5 import java.awt.event.MouseEvent; 4 6 import java.awt.im.InputContext; 5 7 import java.util.Collection; 6 8 import java.util.Collections; 9 import java.util.EventObject; 7 10 import java.util.LinkedList; 8 11 import java.util.Locale; 9 12 10 13 import javax.swing.ComboBoxEditor; 14 import javax.swing.JTable; 15 import javax.swing.event.CellEditorListener; 16 import javax.swing.table.TableCellEditor; 11 17 18 import org.openstreetmap.josm.gui.util.CellEditorSupport; 12 19 import org.openstreetmap.josm.gui.widgets.JosmComboBox; 13 20 import org.openstreetmap.josm.tools.Logging; 14 21 … … 24 31 * @param <E> the type of the combobox entries 25 32 * @since 18173 26 33 */ 27 public class AutoCompComboBox<E> extends JosmComboBox<E> implements AutoCompListener {34 public class AutoCompComboBox<E> extends JosmComboBox<E> implements TableCellEditor, AutoCompListener { 28 35 29 36 /** force a different keyboard input locale for the editor */ 30 37 private boolean useFixedLocale; … … 48 55 setEditable(true); 49 56 getEditorComponent().setModel(model); 50 57 getEditorComponent().addAutoCompListener(this); 58 tableCellEditorSupport = new CellEditorSupport(this); 51 59 } 52 60 53 61 /** … … 104 112 * @param elems The string items to set 105 113 * @deprecated Has been moved to the model, where it belongs. Use 106 114 * {@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}.115 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#load} and 116 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#save}. 109 117 */ 110 118 @Deprecated 111 119 public void setPossibleItems(Collection<E> elems) { … … 122 130 * @since 15011 123 131 * @deprecated Has been moved to the model, where it belongs. Use 124 132 * {@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}.133 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#load} and 134 * {@link org.openstreetmap.josm.gui.widgets.JosmComboBoxModel.Preferences#save}. 127 135 */ 128 136 @Deprecated 129 137 public void setPossibleItemsTopDown(Collection<E> elems) { … … 201 209 public void autoCompPerformed(AutoCompEvent e) { 202 210 autocomplete(e.getItem()); 203 211 } 212 213 /* ------------------------------------------------------------------------------------ */ 214 /* TableCellEditor interface */ 215 /* ------------------------------------------------------------------------------------ */ 216 217 private transient CellEditorSupport tableCellEditorSupport; 218 private String originalValue; 219 220 @Override 221 public void addCellEditorListener(CellEditorListener l) { 222 tableCellEditorSupport.addCellEditorListener(l); 223 } 224 225 protected void rememberOriginalValue(String value) { 226 this.originalValue = value; 227 } 228 229 protected void restoreOriginalValue() { 230 setText(originalValue); 231 } 232 233 @Override 234 public void removeCellEditorListener(CellEditorListener l) { 235 tableCellEditorSupport.removeCellEditorListener(l); 236 } 237 238 @Override 239 public void cancelCellEditing() { 240 restoreOriginalValue(); 241 tableCellEditorSupport.fireEditingCanceled(); 242 } 243 244 @Override 245 public Object getCellEditorValue() { 246 return getText(); 247 } 248 249 /** 250 * Returns true if <code>anEvent</code> is <b>not</b> a <code>MouseEvent</code>. Otherwise, it 251 * returns true if the necessary number of clicks have occurred, and returns false otherwise. 252 * 253 * @param anEvent the event 254 * @return true if cell is ready for editing, false otherwise 255 * @see #shouldSelectCell 256 */ 257 @Override 258 public boolean isCellEditable(EventObject anEvent) { 259 if (anEvent instanceof MouseEvent) { 260 return ((MouseEvent) anEvent).getClickCount() >= 1; 261 } 262 return true; 263 } 264 265 @Override 266 public boolean shouldSelectCell(EventObject anEvent) { 267 if (anEvent instanceof MouseEvent) { 268 MouseEvent e = (MouseEvent) anEvent; 269 return e.getID() != MouseEvent.MOUSE_DRAGGED; 270 } 271 return true; 272 } 273 274 @Override 275 public boolean stopCellEditing() { 276 tableCellEditorSupport.fireEditingStopped(); 277 return true; 278 } 279 280 @Override 281 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 282 setText(value == null ? "" : value.toString()); 283 rememberOriginalValue(getText()); 284 return this; 285 } 204 286 } -
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; … … 16 16 import java.util.Set; 17 17 import java.util.function.Function; 18 18 import java.util.stream.Collectors; 19 import java.util.stream.Stream; 19 20 20 21 import org.openstreetmap.josm.data.osm.DataSet; 21 22 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 40 41 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; 41 42 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 42 43 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 44 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem; 45 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType; 43 46 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; 47 import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem; 44 48 import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role; 45 49 import org.openstreetmap.josm.tools.CheckParameterUtil; 46 50 import org.openstreetmap.josm.tools.MultiMap; … … 47 51 import org.openstreetmap.josm.tools.Utils; 48 52 49 53 /** 50 * AutoCompletionManager holds a cache of keys with a list of 51 * possible auto completion values foreach key.52 * 54 * AutoCompletionManager holds a cache of keys with a list of possible auto completion values for 55 * each key. 56 * <p> 53 57 * Each DataSet can be assigned one AutoCompletionManager instance such that 54 58 * <ol> 55 59 * <li>any key used in a tag in the data set is part of the key list in the cache</li> … … 56 60 * <li>any value used in a tag for a specific key is part of the autocompletion list of this key</li> 57 61 * </ol> 58 62 * 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 63 * Building up auto completion lists should not slow down tabbing from input field to input field. 64 * Looping through the complete data set in order to build up the auto completion list for a 65 * specific input field is not efficient enough, hence this cache. 65 66 */ 66 67 public class AutoCompletionManager implements DataSetListener { 67 68 … … 105 106 } 106 107 } 107 108 109 /** 110 * Compares two AutoCompletionItems alphabetically. 111 */ 112 public static final Comparator<AutoCompletionItem> ALPHABETIC_COMPARATOR = 113 (ac1, ac2) -> String.CASE_INSENSITIVE_ORDER.compare(ac1.getValue(), ac2.getValue()); 114 108 115 /** If the dirty flag is set true, a rebuild is necessary. */ 109 116 protected boolean dirty; 110 117 /** The data set that is managed */ … … 115 122 * only accessed by getTagCache(), rebuild() and cachePrimitiveTags() 116 123 * use getTagCache() accessor 117 124 */ 118 protected MultiMap<String, String> tagCache;125 protected final MultiMap<String, String> TAG_CACHE = new MultiMap<>(); 119 126 120 127 /** 121 * the same as tagCache but for the preset keys and values can be accessed directly122 */123 static final MultiMap<String, String> PRESET_TAG_CACHE = new MultiMap<>();124 125 /**126 128 * Cache for tags that have been entered by the user. 127 129 */ 128 130 static final Set<UserInputTag> USER_INPUT_TAG_CACHE = new LinkedHashSet<>(); 129 131 130 132 /** 131 * the cached list of member roles 132 * only accessed by getRoleCache(), rebuild() and cacheRelationMemberRoles() 133 * use getRoleCache() accessor 133 * The cached relations by {@link #getRelationType(Map) relation type}. 134 134 */ 135 protected Set<String> roleCache;135 protected final MultiMap<String, Relation> RELATION_CACHE = new MultiMap<>(); 136 136 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 137 private static final Map<DataSet, AutoCompletionManager> INSTANCES = new HashMap<>(); 143 138 139 private static final Map<String, String> EMPTY_MAP = Collections.emptyMap(); 140 144 141 /** 145 142 * Constructs a new {@code AutoCompletionManager}. 146 143 * @param ds data set … … 156 153 rebuild(); 157 154 dirty = false; 158 155 } 159 return tagCache;156 return TAG_CACHE; 160 157 } 161 158 162 protected Set<String> getRoleCache() {159 protected MultiMap<String, Relation> getRelationCache() { 163 160 if (dirty) { 164 161 rebuild(); 165 162 dirty = false; 166 163 } 167 return roleCache;164 return RELATION_CACHE; 168 165 } 169 166 170 167 /** … … 171 168 * initializes the cache from the primitives in the dataset 172 169 */ 173 170 protected void rebuild() { 174 tagCache = new MultiMap<>();175 roleCache = new HashSet<>();171 TAG_CACHE.clear(); 172 RELATION_CACHE.clear(); 176 173 cachePrimitives(ds.allNonDeletedCompletePrimitives()); 177 174 } 178 175 … … 180 177 for (OsmPrimitive primitive : primitives) { 181 178 cachePrimitiveTags(primitive); 182 179 if (primitive instanceof Relation) { 183 cacheRelationMemberRoles((Relation) primitive); 180 Relation rel = (Relation) primitive; 181 RELATION_CACHE.put(getRelationType(rel.getKeys()), rel); 184 182 } 185 183 } 186 184 } … … 192 190 * @param primitive an OSM primitive 193 191 */ 194 192 protected void cachePrimitiveTags(OsmPrimitive primitive) { 195 primitive.visitKeys((p, key, value) -> tagCache.put(key, value));193 primitive.visitKeys((p, key, value) -> TAG_CACHE.put(key, value)); 196 194 } 197 195 198 196 /** 199 * Caches all member roles of the relation <code>relation</code> 197 * Returns the relation type. 198 * <p> 199 * This is used to categorize the relations in the dataset. A relation with the keys: 200 * <ul> 201 * <li>type=route 202 * <li>route=hiking 203 * </ul> 204 * will return a relation type of {@code "route.hiking"}. 200 205 * 201 * @param relation the relation 206 * @param tags the tags on the relation 207 * @return the relation type or {@code ""} 202 208 */ 203 pr otected void cacheRelationMemberRoles(Relation relation) {204 for (RelationMember m: relation.getMembers()) {205 if (m.hasRole()) {206 roleCache.add(m.getRole());207 }208 }209 private String getRelationType(Map<String, String> tags) { 210 String type = tags.get("type"); 211 if (type == null) return ""; 212 String subtype = tags.get(type); 213 if (subtype == null) return type; 214 return type + "." + subtype; 209 215 } 210 216 211 217 /** 218 * Construct a role out of a relation member 219 * 220 * @param member the relation member 221 * @return the Role 222 */ 223 protected Role mkRole(RelationMember member) { 224 Role role = new Role(); 225 role.key = member.getRole(); 226 role.types = EnumSet.of(TaggingPresetType.forPrimitiveType(member.getDisplayType())); 227 return role; 228 } 229 230 /** 212 231 * Remembers user input for the given key/value. 213 232 * @param key Tag key 214 233 * @param value Tag value … … 259 278 } 260 279 261 280 /** 262 * Replies the list of member roles 281 * Returns a collection of all member roles in the dataset. 282 * <p> 283 * Member roles are distinct on role name and primitive type they apply to. So there will be a 284 * role "platform" for nodes and a role "platform" for ways. 263 285 * 264 * @return the listof member roles286 * @return the collection of member roles 265 287 */ 266 public List<String> getMemberRoles() { 267 return new ArrayList<>(getRoleCache()); 288 public Set<Role> getAllMemberRoles() { 289 return getRelationCache().getAllValues().stream() 290 .flatMap(rel -> rel.getMembers().stream()).map(r -> mkRole(r)).collect(Collectors.toSet()); 268 291 } 269 292 270 293 /** 294 * Returns a collection of all roles in the dataset for one relation type. 295 * <p> 296 * Member roles are distinct on role name and primitive type they apply to. So there will be a 297 * role "platform" for nodes and a role "platform" for ways. 298 * 299 * @param type the {@link #getRelationType(Map) relation type} 300 * @return the collection of member roles 301 */ 302 public Set<Role> getMemberRoles(String type) { 303 Set<Relation> relations = getRelationCache().get(type); 304 if (relations == null) 305 return Collections.emptySet(); 306 return relations.stream().flatMap(rel -> rel.getMembers().stream()).map(r -> mkRole(r)).collect(Collectors.toSet()); 307 } 308 309 /** 271 310 * Populates the {@link AutoCompletionList} with the currently cached member roles. 272 311 * 273 312 * @param list the list to populate 274 313 */ 275 314 public void populateWithMemberRoles(AutoCompletionList list) { 276 list.add(TaggingPresets.getPresetRoles() , AutoCompletionPriority.IS_IN_STANDARD);277 list.add(get RoleCache(), AutoCompletionPriority.IS_IN_DATASET);315 list.add(TaggingPresets.getPresetRoles().stream().map(r -> r.key).collect(Collectors.toList()), AutoCompletionPriority.IS_IN_STANDARD); 316 list.add(getAllMemberRoles().stream().map(role -> role.key).collect(Collectors.toSet()), AutoCompletionPriority.IS_IN_DATASET); 278 317 } 279 318 280 319 /** … … 303 342 } 304 343 305 344 /** 345 * Merges two or more {@code Map<String, AutoCompletionPriority>}. The result will have the 346 * priorities merged. 347 * 348 * @param maps two or more maps to merge 349 * @return the merged map 350 */ 351 @SafeVarargs 352 public static final Map<String, AutoCompletionPriority> merge(Map<String, AutoCompletionPriority>... maps) { 353 return Stream.of(maps).flatMap(m -> m.entrySet().stream()) 354 .collect(Collectors.toMap(Entry::getKey, Entry::getValue, AutoCompletionPriority::mergeWith)); 355 } 356 357 /** 358 * Return true if the role may be applied to all of the types. 359 * <p> 360 * Returns true if {@code role.types} contains all elements of {@code types}. 361 * 362 * @param role The role. 363 * @param types The types. 364 * @return True if the role may be applied to all of the types. 365 */ 366 private boolean appliesTo(Role role, Collection<TaggingPresetType> types) { 367 return role.types.containsAll(types); 368 } 369 370 /** 371 * Returns key suggestions for a given relation type. 372 * <p> 373 * Returns all keys in the dataset used on a given {@link #getRelationType(Map) relation type}. 374 * 375 * @param tags current tags in the tag editor panel, used to determine the relation type 376 * @return the suggestions 377 */ 378 public Map<String, AutoCompletionPriority> getKeysForRelation(Map<String, String> tags) { 379 Map<String, AutoCompletionPriority> map = new HashMap<>(); 380 Set<Relation> relations = tags != null ? getRelationCache().get(getRelationType(tags)) : getRelationCache().getAllValues(); 381 if (relations == null) 382 return map; 383 return relations.stream().flatMap(rel -> rel.getKeys().entrySet().stream()).map(e -> e.getKey()) 384 .collect(Collectors.toMap(k -> k, v -> AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith)); 385 } 386 387 /** 388 * Returns value suggestions for a given relation type and key. 389 * <p> 390 * Returns all values in the dataset used with a given key on a given 391 * {@link #getRelationType(Map) relation type}. 392 * 393 * @param tags current tags in the tag editor panel, used to determine the relation type 394 * @param key the key to get values for 395 * @return the suggestions 396 */ 397 public Map<String, AutoCompletionPriority> getValuesForRelation(Map<String, String> tags, String key) { 398 Map<String, AutoCompletionPriority> map = new HashMap<>(); 399 Set<Relation> relations = tags != null ? getRelationCache().get(getRelationType(tags)) : getRelationCache().getAllValues(); 400 if (relations == null) 401 return map; 402 return relations.stream().map(rel -> rel.get(key)).filter(e -> e != null) 403 .collect(Collectors.toMap(k -> k, v -> AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith)); 404 } 405 406 /** 407 * Returns role suggestions for a given relation type. 408 * <p> 409 * Returns all roles in the dataset for a given {@link TaggingPresetType role type} used with a given 410 * {@link #getRelationType(Map) relation type}. 411 * 412 * @param tags current tags in the tag editor panel, used to determine the relation type 413 * @param roleTypes all roles returned will match all of the types in this set. 414 * @return the suggestions 415 */ 416 public Map<String, AutoCompletionPriority> getRolesForRelation(Map<String, String> tags, EnumSet<TaggingPresetType> roleTypes) { 417 Map<String, AutoCompletionPriority> map = new HashMap<>(); 418 Set<Relation> relations = tags != null ? getRelationCache().get(getRelationType(tags)) : getRelationCache().getAllValues(); 419 if (relations == null) 420 return map; 421 return relations.stream().flatMap(rel -> rel.getMembers().stream()) 422 .map(member -> mkRole(member)).filter(role -> appliesTo(role, roleTypes)) 423 .collect(Collectors.toMap(k -> k.key, v -> AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith)); 424 } 425 426 /** 427 * Returns all presets of type {@code types} matched by {@code tags}. 428 * 429 * @param types the preset types to include, (node / way / relation ...) or null to include all types 430 * @param tags match presets using these tags or null to match all presets 431 * @return the matched presets 432 */ 433 private Collection<TaggingPreset> getPresets(Collection<TaggingPresetType> types, Map<String, String> tags) { 434 if (tags == null) 435 tags = EMPTY_MAP; 436 437 Collection<TaggingPreset> presets = TaggingPresets.getMatchingPresets(types, tags, false); 438 if (presets.isEmpty()) { 439 presets = TaggingPresets.getTaggingPresets(); 440 } 441 return presets; 442 } 443 444 /** 445 * Returns all keys found in the presets matched by {@code tags}. 446 * 447 * @param types the preset types to include, (node / way / relation ...) or null to include all types 448 * @param tags match presets using these tags or null to match all presets 449 * @return the suggested keys 450 * @since xxx 451 */ 452 public Map<String, AutoCompletionPriority> getPresetKeys(Collection<TaggingPresetType> types, Map<String, String> tags) { 453 Map<String, AutoCompletionPriority> map = new HashMap<>(); 454 455 for (TaggingPreset preset : getPresets(types, tags)) { 456 for (TaggingPresetItem item : preset.data) { 457 if (item instanceof KeyedItem) { 458 map.merge(((KeyedItem) item).key, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith); 459 } 460 } 461 } 462 return map; 463 } 464 465 /** 466 * Returns all values for {@code key} found in the presets matched by {@code tags}. 467 * 468 * @param types the preset types to include, (node / way / relation ...) or null to include all types 469 * @param tags match presets using these tags or null to match all presets 470 * @param key the key to return values for 471 * @return the suggested values 472 * @since xxx 473 */ 474 public Map<String, AutoCompletionPriority> getPresetValues(Collection<TaggingPresetType> types, Map<String, String> tags, String key) { 475 Map<String, AutoCompletionPriority> map = new HashMap<>(); 476 477 for (TaggingPreset preset : getPresets(types, tags)) { 478 for (TaggingPresetItem item : preset.data) { 479 if (item instanceof KeyedItem) { 480 KeyedItem keyedItem = (KeyedItem) item; 481 if (keyedItem.key.equals(key)) { 482 for (String value : keyedItem.getValues()) { 483 map.merge(value, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith); 484 } 485 } 486 } 487 } 488 } 489 return map; 490 } 491 492 /** 493 * Returns all roles found in the presets matched by {@code tags}. 494 * 495 * @param tags match presets using these tags or null to match all presets 496 * @param roleTypes the role types to include, (node / way / relation ...) or null to include all types 497 * @return the suggested roles 498 * @since xxx 499 */ 500 public Map<String, AutoCompletionPriority> getPresetRoles(Map<String, String> tags, Collection<TaggingPresetType> roleTypes) { 501 Map<String, AutoCompletionPriority> map = new HashMap<>(); 502 503 for (TaggingPreset preset : getPresets(EnumSet.of(TaggingPresetType.RELATION), tags)) { 504 if (preset.roles != null) { 505 for (Role role : preset.roles.roles) { 506 if (appliesTo(role, roleTypes)) 507 map.merge(role.key, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith); 508 } 509 } 510 } 511 return map; 512 } 513 514 /** 515 * Returns all cached {@link AutoCompletionItem}s for given keys. 516 * 517 * @param keys retrieve the items for these keys 518 * @return the currently cached items, sorted by priority and alphabet 519 * @since 18221 520 */ 521 public List<AutoCompletionItem> getAllValuesForKeys(List<String> keys) { 522 Map<String, AutoCompletionPriority> map = new HashMap<>(); 523 524 for (String key : keys) { 525 for (String value : TaggingPresets.getPresetValues(key)) { 526 map.merge(value, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith); 527 } 528 for (String value : getDataValues(key)) { 529 map.merge(value, AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith); 530 } 531 for (String value : getUserInputValues(key)) { 532 map.merge(value, AutoCompletionPriority.UNKNOWN, AutoCompletionPriority::mergeWith); 533 } 534 } 535 return map.entrySet().stream().map(e -> new AutoCompletionItem(e.getKey(), e.getValue())) 536 .sorted(ALPHABETIC_COMPARATOR).collect(Collectors.toList()); 537 } 538 539 /** 306 540 * Populates the an {@link AutoCompletionList} with the currently cached tag keys 307 541 * 308 542 * @param list the list to populate … … 345 579 } 346 580 347 581 /** 348 * Returns all cached {@link AutoCompletionItem}s for given keys.349 *350 * @param keys retrieve the items for these keys351 * @return the currently cached items, sorted by priority and alphabet352 * @since 18221353 */354 public List<AutoCompletionItem> getAllForKeys(List<String> keys) {355 Map<String, AutoCompletionPriority> map = new HashMap<>();356 357 for (String key : keys) {358 for (String value : TaggingPresets.getPresetValues(key)) {359 map.merge(value, AutoCompletionPriority.IS_IN_STANDARD, AutoCompletionPriority::mergeWith);360 }361 for (String value : getDataValues(key)) {362 map.merge(value, AutoCompletionPriority.IS_IN_DATASET, AutoCompletionPriority::mergeWith);363 }364 for (String value : getUserInputValues(key)) {365 map.merge(value, AutoCompletionPriority.UNKNOWN, AutoCompletionPriority::mergeWith);366 }367 }368 return map.entrySet().stream().map(e -> new AutoCompletionItem(e.getKey(), e.getValue())).sorted().collect(Collectors.toList());369 }370 371 /**372 582 * Returns the currently cached tag keys. 373 583 * @return a set of tag keys 374 584 * @since 12859 … … 502 712 ds.removeDataSetListener(AutoCompletionManager.this); 503 713 MainApplication.getLayerManager().removeLayerChangeListener(this); 504 714 dirty = true; 505 tagCache = null;506 roleCache = null;715 TAG_CACHE.clear(); 716 RELATION_CACHE.clear(); 507 717 ds = null; 508 718 } 509 719 } -
src/org/openstreetmap/josm/gui/tagging/ac/DefaultAutoCompListener.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.tagging.ac; 3 4 import javax.swing.event.PopupMenuEvent; 5 import javax.swing.event.PopupMenuListener; 6 7 /** 8 * A default autocompletion listener. 9 * @param <E> the type of the {@code AutoCompComboBox<E>} or {@code AutoCompTextField<E>} 10 */ 11 public class DefaultAutoCompListener<E> implements AutoCompListener, PopupMenuListener { 12 protected void updateAutoCompModel(AutoCompComboBoxModel<E> model) { 13 } 14 15 @Override 16 public void autoCompBefore(AutoCompEvent e) { 17 AutoCompTextField<E> tf = toTextField(e); 18 String savedText = tf.getText(); 19 updateAutoCompModel(tf.getModel()); 20 tf.setText(savedText); 21 } 22 23 @Override 24 public void autoCompPerformed(AutoCompEvent e) { 25 } 26 27 @Override 28 public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 29 AutoCompComboBox<E> cb = toComboBox(e); 30 String savedText = cb.getText(); 31 updateAutoCompModel(cb.getModel()); 32 cb.setText(savedText); 33 } 34 35 @Override 36 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 37 } 38 39 @Override 40 public void popupMenuCanceled(PopupMenuEvent e) { 41 } 42 43 /** 44 * Returns the AutoCompTextField that sent the request. 45 * @param e The AutoCompEvent 46 * @return the AutoCompTextField 47 */ 48 @SuppressWarnings("unchecked") 49 public AutoCompTextField<E> toTextField(AutoCompEvent e) { 50 return (AutoCompTextField<E>) e.getSource(); 51 } 52 53 /** 54 * Returns the AutoCompComboBox that sent the request. 55 * @param e The AutoCompEvent 56 * @return the AutoCompComboBox 57 */ 58 @SuppressWarnings("unchecked") 59 public AutoCompComboBox<E> toComboBox(PopupMenuEvent e) { 60 return (AutoCompComboBox<E>) e.getSource(); 61 } 62 } -
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 * … … 70 53 if (data == null) { 71 54 return Collections.emptyList(); 72 55 } 73 return AutoCompletionManager.of(data).getAll ForKeys(keys);56 return AutoCompletionManager.of(data).getAllValuesForKeys(keys); 74 57 } 75 58 76 59 /** -
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/JosmComboBox.java
59 59 /** greyed text to display in the editor when the selected value is empty */ 60 60 private String hint; 61 61 62 private boolean fakeWidth; 63 62 64 /** 63 65 * Creates a {@code JosmComboBox} with a {@link JosmComboBoxModel} data model. 64 66 * The default data model is an empty list of objects. … … 293 295 } 294 296 295 297 /** 298 * Make popup wider than combobox. 299 */ 300 @Override 301 public Dimension getSize() { 302 Dimension dim = super.getSize(); 303 if (fakeWidth) 304 dim.width = Math.max(getPreferredSize().width, dim.width); 305 return dim; 306 } 307 308 /** 309 * Helper to make popup wider than combobox. 310 */ 311 @Override 312 public void doLayout() { 313 try { 314 fakeWidth = false; 315 super.doLayout(); 316 } finally { 317 fakeWidth = true; 318 } 319 } 320 321 /** 296 322 * Get the dropdown list component 297 323 * 298 324 * @return the list or null … … 383 409 int freeAbove = bounds.y - screenBounds.y; 384 410 int freeBelow = (screenBounds.y + screenBounds.height) - (bounds.y + bounds.height); 385 411 386 try { 387 // First try an implementation-dependent method to get the exact number. 388 @SuppressWarnings("unchecked") 389 JList<E> jList = getList(); 390 412 int maxRowCount = 8; // default 413 @SuppressWarnings("unchecked") 414 JList<E> jList = getList(); 415 if (jList != null) { 391 416 // Calculate the free space available on screen 392 417 Insets insets = jList.getInsets(); 393 418 // A small fudge factor that accounts for the displacement of the popup relative to the … … 394 419 // combobox and the popup shadow. 395 420 int fudge = 4; 396 421 int free = Math.max(freeAbove, freeBelow) - (insets.top + insets.bottom) - fudge; 397 if (jList.getParent() instanceof JScrollPane) { 398 JScrollPane scroller = (JScrollPane) jList.getParent(); 422 Component parent = getParent(); 423 if (parent instanceof JScrollPane) { 424 JScrollPane scroller = (JScrollPane) parent; 399 425 Border border = scroller.getViewportBorder(); 400 426 if (border != null) { 401 427 insets = border.getBorderInsets(null); … … 418 444 if (h >= free) 419 445 break; 420 446 } 421 setMaximumRowCount(i); 422 // Logging.debug("free = {0}, h = {1}, i = {2}, bounds = {3}, screenBounds = {4}", free, h, i, bounds, screenBounds); 423 } catch (Exception ex) { 424 setMaximumRowCount(8); // the default 447 maxRowCount = i; 425 448 } 449 // Logging.debug("free = {0}, h = {1}, i = {2}, bounds = {3}, screenBounds = {4}", free, h, i, bounds, screenBounds); 450 setMaximumRowCount(maxRowCount); 426 451 } 427 452 428 453 @Override -
src/org/openstreetmap/josm/gui/widgets/JosmComboBoxModel.java
205 205 } 206 206 207 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 /** 208 238 * Adds an element to the top of the list. 209 239 * <p> 210 240 * If the element is already in the model, moves it to the top. If the model gets too big, -
src/org/openstreetmap/josm/gui/widgets/JosmTextField.java
42 42 */ 43 43 public class JosmTextField extends JTextField implements Destroyable, ComponentListener, FocusListener, PropertyChangeListener { 44 44 45 private finalPopupMenuLauncher launcher;45 private PopupMenuLauncher launcher; 46 46 private String hint; 47 47 private Icon icon; 48 48 private Point iconPos; … … 241 241 } 242 242 243 243 /** 244 * Enables / disables the undo / redo functionality. 245 * @param undoRedo enable if true 246 */ 247 public void enableUndoRedo(boolean undoRedo) { 248 launcher = TextContextualPopupMenu.enableMenuFor(this, undoRedo); 249 } 250 251 /** 244 252 * Empties the internal undo manager. 245 253 * @since 14977 246 254 */ -
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 }
