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 // add action 078 // 079 JButton btn; 080 pnl.add(btn = new JButton(tagTable.getAddAction())); 081 btn.setMargin(new Insets(0, 0, 0, 0)); 082 tagTable.addComponentNotStoppingCellEditing(btn); 083 084 // delete action 085 pnl.add(btn = new JButton(tagTable.getDeleteAction())); 086 btn.setMargin(new Insets(0, 0, 0, 0)); 087 tagTable.addComponentNotStoppingCellEditing(btn); 088 089 // paste action 090 pnl.add(btn = new JButton(tagTable.getPasteAction())); 091 btn.setMargin(new Insets(0, 0, 0, 0)); 092 tagTable.addComponentNotStoppingCellEditing(btn); 093 return pnl; 094 } 095 096 /** 097 * Returns the paste action. 098 * @return the paste action 099 */ 100 public AbstractAction getPasteAction() { 101 return tagTable.getPasteAction(); 102 } 103 104 /** 105 * builds the GUI 106 */ 107 protected final void build() { 108 setLayout(new GridBagLayout()); 109 JPanel tablePanel = buildTagTableEditorPanel(); 110 JPanel buttonPanel = buildButtonsPanel(); 111 112 GridBagConstraints gc = new GridBagConstraints(); 113 114 // -- buttons panel 115 // 116 gc.fill = GridBagConstraints.VERTICAL; 117 gc.weightx = 0.0; 118 gc.weighty = 1.0; 119 gc.anchor = GridBagConstraints.NORTHWEST; 120 add(buttonPanel, gc); 121 122 // -- the panel with the editor table 123 // 124 gc.gridx = 1; 125 gc.fill = GridBagConstraints.BOTH; 126 gc.weightx = 1.0; 127 gc.weighty = 1.0; 128 gc.anchor = GridBagConstraints.CENTER; 129 add(tablePanel, gc); 130 131 if (presetHandler != null) { 132 model.addTableModelListener(new TableModelListener() { 133 @Override 134 public void tableChanged(TableModelEvent e) { 135 updatePresets(); 136 } 137 }); 138 } 139 140 addFocusListener(new FocusAdapter() { 141 @Override 142 public void focusGained(FocusEvent e) { 143 tagTable.requestFocusInCell(0, 0); 144 } 145 }); 146 } 147 148 /** 149 * Creates a new tag editor panel. The editor model is created 150 * internally and can be retrieved with {@link #getModel()}. 151 * @param primitive primitive to consider 152 * @param presetHandler tagging preset handler 153 */ 154 public TagEditorPanel(OsmPrimitive primitive, TaggingPresetHandler presetHandler) { 155 this(new TagEditorModel().forPrimitive(primitive), presetHandler, 0); 156 } 157 158 /** 159 * Creates a new tag editor panel with a supplied model. If {@code model} is null, a new model is created. 160 * 161 * @param model the tag editor model 162 * @param presetHandler tagging preset handler 163 * @param maxCharacters maximum number of characters allowed, 0 for unlimited 164 */ 165 public TagEditorPanel(TagEditorModel model, TaggingPresetHandler presetHandler, final int maxCharacters) { 166 this.model = model; 167 this.presetHandler = presetHandler; 168 if (this.model == null) { 169 this.model = new TagEditorModel(); 170 } 171 this.tagTable = new TagTable(this.model, maxCharacters); 172 build(); 173 } 174 175 /** 176 * Replies the tag editor model used by this panel. 177 * 178 * @return the tag editor model used by this panel 179 */ 180 public TagEditorModel getModel() { 181 return model; 182 } 183 184 /** 185 * Initializes the auto completion infrastructure used in this 186 * tag editor panel. {@code layer} is the data layer from whose data set 187 * tag values are proposed as auto completion items. 188 * 189 * @param layer the data layer. Must not be null. 190 * @throws IllegalArgumentException if {@code layer} is null 191 */ 192 public void initAutoCompletion(OsmDataLayer layer) { 193 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 194 195 AutoCompletionManager autocomplete = layer.data.getAutoCompletionManager(); 196 AutoCompletionList acList = new AutoCompletionList(); 197 198 TagCellEditor editor = (TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor(); 199 editor.setAutoCompletionManager(autocomplete); 200 editor.setAutoCompletionList(acList); 201 editor = (TagCellEditor) tagTable.getColumnModel().getColumn(1).getCellEditor(); 202 editor.setAutoCompletionManager(autocomplete); 203 editor.setAutoCompletionList(acList); 204 } 205 206 @Override 207 public void setEnabled(boolean enabled) { 208 tagTable.setEnabled(enabled); 209 super.setEnabled(enabled); 210 } 211 212 private void updatePresets() { 213 presetListPanel.updatePresets( 214 model.getTaggingPresetTypes(), 215 model.getTags(), presetHandler); 216 validate(); 217 } 218}