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