// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.tagging; import java.awt.BorderLayout; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import javax.swing.AbstractAction; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList; import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler; import org.openstreetmap.josm.tools.CheckParameterUtil; /** * TagEditorPanel is a {@link JPanel} which can be embedded as UI component in * UIs. It provides a spreadsheet like tabular control for editing tag names * and tag values. Two action buttons are placed on the left, one for adding * a new tag and one for deleting the currently selected tags. * @since 2040 */ public class TagEditorPanel extends JPanel { /** the tag editor model */ private TagEditorModel model; /** the tag table */ private final TagTable tagTable; private PresetListPanel presetListPanel; private final transient TaggingPresetHandler presetHandler; /** * builds the panel with the table for editing tags * * @return the panel */ protected JPanel buildTagTableEditorPanel() { JPanel pnl = new JPanel(new BorderLayout()); pnl.add(new JScrollPane(tagTable), BorderLayout.CENTER); if (presetHandler != null) { presetListPanel = new PresetListPanel(); pnl.add(presetListPanel, BorderLayout.NORTH); } return pnl; } /** * Sets the next component to request focus after navigation (with tab or enter). * @param nextFocusComponent next component to request focus after navigation (with tab or enter) * @see TagTable#setNextFocusComponent */ public void setNextFocusComponent(Component nextFocusComponent) { tagTable.setNextFocusComponent(nextFocusComponent); } /** * builds the panel with the button row * * @return the panel */ protected JPanel buildButtonsPanel() { JPanel pnl = new JPanel(); pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS)); buildButton(pnl, tagTable.getAddAction()); buildButton(pnl, tagTable.getDeleteAction()); buildButton(pnl, tagTable.getPasteAction()); return pnl; } private void buildButton(JPanel pnl, AbstractAction action) { JButton btn = new JButton(action); pnl.add(btn); btn.setMargin(new Insets(0, 0, 0, 0)); tagTable.addComponentNotStoppingCellEditing(btn); } /** * Returns the paste action. * @return the paste action */ public AbstractAction getPasteAction() { return tagTable.getPasteAction(); } /** * builds the GUI */ protected final void build() { setLayout(new GridBagLayout()); JPanel tablePanel = buildTagTableEditorPanel(); JPanel buttonPanel = buildButtonsPanel(); GridBagConstraints gc = new GridBagConstraints(); // -- buttons panel // gc.fill = GridBagConstraints.VERTICAL; gc.weightx = 0.0; gc.weighty = 1.0; gc.anchor = GridBagConstraints.NORTHWEST; add(buttonPanel, gc); // -- the panel with the editor table // gc.gridx = 1; gc.fill = GridBagConstraints.BOTH; gc.weightx = 1.0; gc.weighty = 1.0; gc.anchor = GridBagConstraints.CENTER; add(tablePanel, gc); if (presetHandler != null) { model.addTableModelListener(e -> updatePresets()); } addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { tagTable.requestFocusInCell(0, 0); } }); } /** * Creates a new tag editor panel. The editor model is created * internally and can be retrieved with {@link #getModel()}. * @param primitive primitive to consider * @param presetHandler tagging preset handler */ public TagEditorPanel(OsmPrimitive primitive, TaggingPresetHandler presetHandler) { this(new TagEditorModel().forPrimitive(primitive), presetHandler, 0); } /** * Creates a new tag editor panel with a supplied model. If {@code model} is null, a new model is created. * * @param model the tag editor model * @param presetHandler tagging preset handler * @param maxCharacters maximum number of characters allowed, 0 for unlimited */ public TagEditorPanel(TagEditorModel model, TaggingPresetHandler presetHandler, final int maxCharacters) { this.model = model; this.presetHandler = presetHandler; if (this.model == null) { this.model = new TagEditorModel(); } this.tagTable = new TagTable(this.model, maxCharacters); build(); } /** * Replies the tag editor model used by this panel. * * @return the tag editor model used by this panel */ public TagEditorModel getModel() { return model; } /** * Initializes the auto completion infrastructure used in this * tag editor panel. {@code layer} is the data layer from whose data set * tag values are proposed as auto completion items. * * @param layer the data layer. Must not be null. * @throws IllegalArgumentException if {@code layer} is null */ public void initAutoCompletion(OsmDataLayer layer) { CheckParameterUtil.ensureParameterNotNull(layer, "layer"); AutoCompletionManager autocomplete = layer.data.getAutoCompletionManager(); AutoCompletionList acList = new AutoCompletionList(); TagCellEditor editor = (TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor(); editor.setAutoCompletionManager(autocomplete); editor.setAutoCompletionList(acList); editor = (TagCellEditor) tagTable.getColumnModel().getColumn(1).getCellEditor(); editor.setAutoCompletionManager(autocomplete); editor.setAutoCompletionList(acList); } @Override public void setEnabled(boolean enabled) { tagTable.setEnabled(enabled); super.setEnabled(enabled); } private void updatePresets() { presetListPanel.updatePresets( model.getTaggingPresetTypes(), model.getTags(), presetHandler); validate(); } }