001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.tagging; 003 004import java.awt.BorderLayout; 005import java.awt.Component; 006import java.awt.GridBagConstraints; 007import java.awt.GridBagLayout; 008import java.awt.Insets; 009import java.awt.event.FocusAdapter; 010import java.awt.event.FocusEvent; 011 012import javax.swing.AbstractAction; 013import javax.swing.BoxLayout; 014import javax.swing.JButton; 015import javax.swing.JPanel; 016import javax.swing.JScrollPane; 017 018import org.openstreetmap.josm.data.osm.OsmPrimitive; 019import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel; 020import org.openstreetmap.josm.gui.layer.OsmDataLayer; 021import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList; 022import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 023import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler; 024import org.openstreetmap.josm.tools.CheckParameterUtil; 025 026/** 027 * TagEditorPanel is a {@link JPanel} which can be embedded as UI component in 028 * UIs. It provides a spreadsheet like tabular control for editing tag names 029 * and tag values. Two action buttons are placed on the left, one for adding 030 * a new tag and one for deleting the currently selected tags. 031 * @since 2040 032 */ 033public class TagEditorPanel extends JPanel { 034 /** the tag editor model */ 035 private TagEditorModel model; 036 /** the tag table */ 037 private final TagTable tagTable; 038 039 private PresetListPanel presetListPanel; 040 private final transient TaggingPresetHandler presetHandler; 041 042 /** 043 * builds the panel with the table for editing tags 044 * 045 * @return the panel 046 */ 047 protected JPanel buildTagTableEditorPanel() { 048 JPanel pnl = new JPanel(new BorderLayout()); 049 pnl.add(new JScrollPane(tagTable), BorderLayout.CENTER); 050 if (presetHandler != null) { 051 presetListPanel = new PresetListPanel(); 052 pnl.add(presetListPanel, BorderLayout.NORTH); 053 } 054 return pnl; 055 } 056 057 /** 058 * Sets the next component to request focus after navigation (with tab or enter). 059 * @param nextFocusComponent next component to request focus after navigation (with tab or enter) 060 * @see TagTable#setNextFocusComponent 061 */ 062 public void setNextFocusComponent(Component nextFocusComponent) { 063 tagTable.setNextFocusComponent(nextFocusComponent); 064 } 065 066 /** 067 * builds the panel with the button row 068 * 069 * @return the panel 070 */ 071 protected JPanel buildButtonsPanel() { 072 JPanel pnl = new JPanel(); 073 pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS)); 074 075 buildButton(pnl, tagTable.getAddAction()); 076 buildButton(pnl, tagTable.getDeleteAction()); 077 buildButton(pnl, tagTable.getPasteAction()); 078 079 return pnl; 080 } 081 082 private void buildButton(JPanel pnl, AbstractAction action) { 083 JButton btn = new JButton(action); 084 pnl.add(btn); 085 btn.setMargin(new Insets(0, 0, 0, 0)); 086 tagTable.addComponentNotStoppingCellEditing(btn); 087 } 088 089 /** 090 * Returns the paste action. 091 * @return the paste action 092 */ 093 public AbstractAction getPasteAction() { 094 return tagTable.getPasteAction(); 095 } 096 097 /** 098 * builds the GUI 099 */ 100 protected final void build() { 101 setLayout(new GridBagLayout()); 102 JPanel tablePanel = buildTagTableEditorPanel(); 103 JPanel buttonPanel = buildButtonsPanel(); 104 105 GridBagConstraints gc = new GridBagConstraints(); 106 107 // -- buttons panel 108 // 109 gc.fill = GridBagConstraints.VERTICAL; 110 gc.weightx = 0.0; 111 gc.weighty = 1.0; 112 gc.anchor = GridBagConstraints.NORTHWEST; 113 add(buttonPanel, gc); 114 115 // -- the panel with the editor table 116 // 117 gc.gridx = 1; 118 gc.fill = GridBagConstraints.BOTH; 119 gc.weightx = 1.0; 120 gc.weighty = 1.0; 121 gc.anchor = GridBagConstraints.CENTER; 122 add(tablePanel, gc); 123 124 if (presetHandler != null) { 125 model.addTableModelListener(e -> updatePresets()); 126 } 127 128 addFocusListener(new FocusAdapter() { 129 @Override 130 public void focusGained(FocusEvent e) { 131 tagTable.requestFocusInCell(0, 0); 132 } 133 }); 134 } 135 136 /** 137 * Creates a new tag editor panel. The editor model is created 138 * internally and can be retrieved with {@link #getModel()}. 139 * @param primitive primitive to consider 140 * @param presetHandler tagging preset handler 141 */ 142 public TagEditorPanel(OsmPrimitive primitive, TaggingPresetHandler presetHandler) { 143 this(new TagEditorModel().forPrimitive(primitive), presetHandler, 0); 144 } 145 146 /** 147 * Creates a new tag editor panel with a supplied model. If {@code model} is null, a new model is created. 148 * 149 * @param model the tag editor model 150 * @param presetHandler tagging preset handler 151 * @param maxCharacters maximum number of characters allowed, 0 for unlimited 152 */ 153 public TagEditorPanel(TagEditorModel model, TaggingPresetHandler presetHandler, final int maxCharacters) { 154 this.model = model; 155 this.presetHandler = presetHandler; 156 if (this.model == null) { 157 this.model = new TagEditorModel(); 158 } 159 this.tagTable = new TagTable(this.model, maxCharacters); 160 build(); 161 } 162 163 /** 164 * Replies the tag editor model used by this panel. 165 * 166 * @return the tag editor model used by this panel 167 */ 168 public TagEditorModel getModel() { 169 return model; 170 } 171 172 /** 173 * Initializes the auto completion infrastructure used in this 174 * tag editor panel. {@code layer} is the data layer from whose data set 175 * tag values are proposed as auto completion items. 176 * 177 * @param layer the data layer. Must not be null. 178 * @throws IllegalArgumentException if {@code layer} is null 179 */ 180 public void initAutoCompletion(OsmDataLayer layer) { 181 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 182 183 AutoCompletionManager autocomplete = layer.data.getAutoCompletionManager(); 184 AutoCompletionList acList = new AutoCompletionList(); 185 186 TagCellEditor editor = (TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor(); 187 editor.setAutoCompletionManager(autocomplete); 188 editor.setAutoCompletionList(acList); 189 editor = (TagCellEditor) tagTable.getColumnModel().getColumn(1).getCellEditor(); 190 editor.setAutoCompletionManager(autocomplete); 191 editor.setAutoCompletionList(acList); 192 } 193 194 @Override 195 public void setEnabled(boolean enabled) { 196 tagTable.setEnabled(enabled); 197 super.setEnabled(enabled); 198 } 199 200 private void updatePresets() { 201 presetListPanel.updatePresets( 202 model.getTaggingPresetTypes(), 203 model.getTags(), presetHandler); 204 validate(); 205 } 206}