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;
012import javax.swing.AbstractAction;
013
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.dialogs.properties.PresetListPanel.PresetHandler;
023import org.openstreetmap.josm.gui.layer.OsmDataLayer;
024import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
025import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
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 *
034 */
035public class TagEditorPanel extends JPanel {
036    /** the tag editor model */
037    private TagEditorModel model;
038    /** the tag table */
039    private TagTable tagTable;
040
041    private PresetListPanel presetListPanel;
042    private final PresetHandler presetHandler;
043
044    private AutoCompletionManager autocomplete;
045
046    /**
047     * builds the panel with the table for editing tags
048     *
049     * @return the panel
050     */
051    protected JPanel buildTagTableEditorPanel() {
052        JPanel pnl = new JPanel();
053        tagTable = new TagTable(model);
054        pnl.setLayout(new BorderLayout());
055        pnl.add(new JScrollPane(tagTable), BorderLayout.CENTER);
056        if (presetHandler != null) {
057            presetListPanel = new PresetListPanel();
058            pnl.add(presetListPanel, BorderLayout.NORTH);
059        }
060        return pnl;
061    }
062
063    public void setNextFocusComponent(Component nextFocusComponent) {
064        tagTable.setNextFocusComponent(nextFocusComponent);
065    }
066
067    /**
068     * builds the panel with the button row
069     *
070     * @return the panel
071     */
072    protected JPanel buildButtonsPanel() {
073        JPanel pnl = new JPanel();
074        pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS));
075
076        // add action
077        //
078        JButton btn;
079        pnl.add(btn = new JButton(tagTable.getAddAction()));
080        btn.setMargin(new Insets(0,0,0,0));
081        tagTable.addComponentNotStoppingCellEditing(btn);
082
083        // delete action
084        pnl.add(btn = new JButton(tagTable.getDeleteAction()));
085        btn.setMargin(new Insets(0,0,0,0));
086        tagTable.addComponentNotStoppingCellEditing(btn);
087        
088        // paste action
089        pnl.add(btn = new JButton(tagTable.getPasteAction()));
090        btn.setMargin(new Insets(0,0,0,0));
091        tagTable.addComponentNotStoppingCellEditing(btn);
092        return pnl;
093    }
094
095    public AbstractAction getPasteAction() {
096        return tagTable.getPasteAction();
097    }
098
099    /**
100     * builds the GUI
101     */
102    protected 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 public void focusGained(FocusEvent e) {
137                tagTable.requestFocusInCell(0, 0);
138            }
139        });
140    }
141
142    /**
143     * Creates a new tag editor panel. The editor model is created
144     * internally and can be retrieved with {@link #getModel()}.
145     */
146    public TagEditorPanel(PresetHandler presetHandler) {
147        this(null, presetHandler);
148    }
149
150    /**
151     * Creates a new tag editor panel with a supplied model. If
152     * {@code model} is null, a new model is created.
153     *
154     * @param model the tag editor model
155     */
156    public TagEditorPanel(TagEditorModel model, PresetHandler presetHandler) {
157        this.model = model;
158        this.presetHandler = presetHandler;
159        if (this.model == null) {
160            this.model = new TagEditorModel();
161        }
162        build();
163    }
164
165    /**
166     * Replies the tag editor model used by this panel.
167     *
168     * @return the tag editor model used by this panel
169     */
170    public TagEditorModel getModel() {
171        return model;
172    }
173
174    /**
175     * Initializes the auto completion infrastructure used in this
176     * tag editor panel. {@code layer} is the data layer from whose data set
177     * tag values are proposed as auto completion items.
178     *
179     * @param layer the data layer. Must not be null.
180     * @throws IllegalArgumentException thrown if {@code layer} is null
181     */
182    public void initAutoCompletion(OsmDataLayer layer) throws IllegalArgumentException{
183        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
184
185        autocomplete = layer.data.getAutoCompletionManager();
186        AutoCompletionList acList = new AutoCompletionList();
187
188        TagCellEditor editor = ((TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor());
189        editor.setAutoCompletionManager(autocomplete);
190        editor.setAutoCompletionList(acList);
191        editor = ((TagCellEditor) tagTable.getColumnModel().getColumn(1).getCellEditor());
192        editor.setAutoCompletionManager(autocomplete);
193        editor.setAutoCompletionList(acList);
194    }
195
196    @Override
197    public void setEnabled(boolean enabled) {
198        tagTable.setEnabled(enabled);
199        super.setEnabled(enabled);
200    }
201
202    private void updatePresets() {
203        presetListPanel.updatePresets(
204                EnumSet.of(TaggingPresetType.RELATION),
205                model.getTags(), presetHandler);
206        validate();
207    }
208}