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; 011import java.util.EnumSet; 012 013import javax.swing.AbstractAction; 014import javax.swing.BoxLayout; 015import javax.swing.JButton; 016import javax.swing.JPanel; 017import javax.swing.JScrollPane; 018import javax.swing.event.TableModelEvent; 019import javax.swing.event.TableModelListener; 020 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.gui.tagging.presets.TaggingPresetType; 027import org.openstreetmap.josm.tools.CheckParameterUtil; 028 029/** 030 * TagEditorPanel is a {@link JPanel} which can be embedded as UI component in 031 * UIs. It provides a spreadsheet like tabular control for editing tag names 032 * and tag values. Two action buttons are placed on the left, one for adding 033 * a new tag and one for deleting the currently selected tags. 034 * 035 */ 036public class TagEditorPanel extends JPanel { 037 /** the tag editor model */ 038 private TagEditorModel model; 039 /** the tag table */ 040 private final TagTable tagTable; 041 042 private PresetListPanel presetListPanel; 043 private final transient TaggingPresetHandler presetHandler; 044 045 /** 046 * builds the panel with the table for editing tags 047 * 048 * @return the panel 049 */ 050 protected JPanel buildTagTableEditorPanel() { 051 JPanel pnl = new JPanel(); 052 pnl.setLayout(new BorderLayout()); 053 pnl.add(new JScrollPane(tagTable), BorderLayout.CENTER); 054 if (presetHandler != null) { 055 presetListPanel = new PresetListPanel(); 056 pnl.add(presetListPanel, BorderLayout.NORTH); 057 } 058 return pnl; 059 } 060 061 /** 062 * Sets the next component to request focus after navigation (with tab or enter). 063 * @param nextFocusComponent next component to request focus after navigation (with tab or enter) 064 * @see TagTable#setNextFocusComponent 065 */ 066 public void setNextFocusComponent(Component nextFocusComponent) { 067 tagTable.setNextFocusComponent(nextFocusComponent); 068 } 069 070 /** 071 * builds the panel with the button row 072 * 073 * @return the panel 074 */ 075 protected JPanel buildButtonsPanel() { 076 JPanel pnl = new JPanel(); 077 pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS)); 078 079 // add action 080 // 081 JButton btn; 082 pnl.add(btn = new JButton(tagTable.getAddAction())); 083 btn.setMargin(new Insets(0, 0, 0, 0)); 084 tagTable.addComponentNotStoppingCellEditing(btn); 085 086 // delete action 087 pnl.add(btn = new JButton(tagTable.getDeleteAction())); 088 btn.setMargin(new Insets(0, 0, 0, 0)); 089 tagTable.addComponentNotStoppingCellEditing(btn); 090 091 // paste action 092 pnl.add(btn = new JButton(tagTable.getPasteAction())); 093 btn.setMargin(new Insets(0, 0, 0, 0)); 094 tagTable.addComponentNotStoppingCellEditing(btn); 095 return pnl; 096 } 097 098 /** 099 * Returns the paste action. 100 * @return the paste action 101 */ 102 public AbstractAction getPasteAction() { 103 return tagTable.getPasteAction(); 104 } 105 106 /** 107 * builds the GUI 108 */ 109 protected final void build() { 110 setLayout(new GridBagLayout()); 111 JPanel tablePanel = buildTagTableEditorPanel(); 112 JPanel buttonPanel = buildButtonsPanel(); 113 114 GridBagConstraints gc = new GridBagConstraints(); 115 116 // -- buttons panel 117 // 118 gc.fill = GridBagConstraints.VERTICAL; 119 gc.weightx = 0.0; 120 gc.weighty = 1.0; 121 gc.anchor = GridBagConstraints.NORTHWEST; 122 add(buttonPanel, gc); 123 124 // -- the panel with the editor table 125 // 126 gc.gridx = 1; 127 gc.fill = GridBagConstraints.BOTH; 128 gc.weightx = 1.0; 129 gc.weighty = 1.0; 130 gc.anchor = GridBagConstraints.CENTER; 131 add(tablePanel, gc); 132 133 if (presetHandler != null) { 134 model.addTableModelListener(new TableModelListener() { 135 @Override 136 public void tableChanged(TableModelEvent e) { 137 updatePresets(); 138 } 139 }); 140 } 141 142 addFocusListener(new FocusAdapter() { 143 @Override public void focusGained(FocusEvent e) { 144 tagTable.requestFocusInCell(0, 0); 145 } 146 }); 147 } 148 149 /** 150 * Creates a new tag editor panel. The editor model is created 151 * internally and can be retrieved with {@link #getModel()}. 152 * @param presetHandler tagging preset handler 153 */ 154 public TagEditorPanel(TaggingPresetHandler presetHandler) { 155 this(null, 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 EnumSet.of(TaggingPresetType.RELATION), 215 model.getTags(), presetHandler); 216 validate(); 217 } 218}