// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.tageditor; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.BorderLayout; import java.awt.Dialog; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Tag; import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList; import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; import org.openstreetmap.josm.plugins.tageditor.ac.AutoCompletionListViewer; import org.openstreetmap.josm.plugins.tageditor.editor.TagEditor; import org.openstreetmap.josm.plugins.tageditor.editor.TagEditorModel; import org.openstreetmap.josm.plugins.tageditor.preset.ui.IPresetSelectorListener; import org.openstreetmap.josm.plugins.tageditor.preset.ui.TabularPresetSelector; import org.openstreetmap.josm.plugins.tageditor.tagspec.ui.ITagSelectorListener; import org.openstreetmap.josm.plugins.tageditor.tagspec.ui.TabularTagSelector; import org.openstreetmap.josm.tools.ImageProvider; import org.openstreetmap.josm.tools.WindowGeometry; /** * The dialog for editing name/value-pairs (aka <em>tags</em>) associated with {@link OsmPrimitive}s. * */ @SuppressWarnings("serial") public class TagEditorDialog extends JDialog { //static private final Logger logger = Logger.getLogger(TagEditorDialog.class.getName()); /** the unique instance */ private static TagEditorDialog instance = null; /** * Access to the singleton instance * * @return the singleton instance of the dialog */ public static TagEditorDialog getInstance() { if (instance == null) { instance = new TagEditorDialog(); } return instance; } /** default preferred size */ public static final Dimension PREFERRED_SIZE = new Dimension(700, 500); /** the properties table */ private TagEditor tagEditor = null; /** the auto completion list viewer */ private AutoCompletionListViewer aclViewer = null; /** the cache of auto completion values used by the tag editor */ private AutoCompletionManager autocomplete = null; private OKAction okAction = null; private CancelAction cancelAction = null; /** * @return the tag editor model */ public TagEditorModel getModel() { return tagEditor.getModel(); } protected JPanel buildButtonRow() { JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER)); // the ok button // pnl.add(new JButton(okAction = new OKAction())); getModel().addPropertyChangeListener(okAction); // the cancel button // pnl.add(new JButton(cancelAction = new CancelAction())); return pnl; } protected JPanel buildTagGridPanel() { // create tag editor and inject an instance of the tag // editor model // tagEditor = new TagEditor(); // create the auto completion list viewer and connect it // to the tag editor // AutoCompletionList autoCompletionList = new AutoCompletionList(); aclViewer = new AutoCompletionListViewer(autoCompletionList); tagEditor.setAutoCompletionList(autoCompletionList); aclViewer.addAutoCompletionListListener(tagEditor); tagEditor.addComponentNotStoppingCellEditing(aclViewer); JPanel pnlTagGrid = new JPanel(); pnlTagGrid.setLayout(new BorderLayout()); pnlTagGrid.add(tagEditor, BorderLayout.CENTER); pnlTagGrid.add(aclViewer, BorderLayout.EAST); pnlTagGrid.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); JSplitPane splitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, tagEditor, aclViewer ); splitPane.setOneTouchExpandable(false); splitPane.setDividerLocation(600); pnlTagGrid.add(splitPane, BorderLayout.CENTER); return pnlTagGrid; } /** * build the GUI */ protected void build() { getContentPane().setLayout(new BorderLayout()); // basic UI prpoperties // setModal(true); setSize(PREFERRED_SIZE); setTitle(tr("JOSM Tag Editor Plugin")); JPanel pnlTagGrid = buildTagGridPanel(); // create the preset selector // TabularPresetSelector presetSelector = new TabularPresetSelector(); presetSelector.addPresetSelectorListener( new IPresetSelectorListener() { @Override public void itemSelected(TaggingPreset item) { tagEditor.stopEditing(); tagEditor.getModel().applyPreset(item); tagEditor.requestFocusInTopLeftCell(); } } ); JPanel pnlPresetSelector = new JPanel(); pnlPresetSelector.setLayout(new BorderLayout()); pnlPresetSelector.add(presetSelector, BorderLayout.CENTER); pnlPresetSelector.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); // create the tag selector // TabularTagSelector tagSelector = new TabularTagSelector(); tagSelector.addTagSelectorListener( new ITagSelectorListener() { @Override public void itemSelected(Tag pair) { tagEditor.stopEditing(); tagEditor.getModel().applyKeyValuePair(pair); tagEditor.requestFocusInTopLeftCell(); } } ); JPanel pnlTagSelector = new JPanel(); pnlTagSelector.setLayout(new BorderLayout()); pnlTagSelector.add(tagSelector, BorderLayout.CENTER); pnlTagSelector.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); // create the tabbed pane // JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.add(pnlPresetSelector, tr("Presets")); tabbedPane.add(pnlTagSelector, tr("Tags")); // create split pane // JSplitPane splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT, tabbedPane, pnlTagGrid ); splitPane.setOneTouchExpandable(true); splitPane.setDividerLocation(200); Dimension minimumSize = new Dimension(100, 50); presetSelector.setMinimumSize(minimumSize); pnlTagGrid.setMinimumSize(minimumSize); getContentPane().add(splitPane, BorderLayout.CENTER); getContentPane().add(buildButtonRow(), BorderLayout.SOUTH); addWindowListener( new WindowAdapter() { @Override public void windowActivated(WindowEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { getModel().ensureOneTag(); tagEditor.clearSelection(); tagEditor.requestFocusInTopLeftCell(); } }); } } ); // makes sure that 'Ctrl-Enter' in the properties table // and in the aclViewer is handled by okAction // getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( (KeyStroke) cancelAction.getValue(Action.ACCELERATOR_KEY), okAction.getValue(AbstractAction.NAME)); getRootPane().getActionMap().put(cancelAction.getValue(Action.NAME), cancelAction); getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( (KeyStroke) okAction.getValue(Action.ACCELERATOR_KEY), okAction.getValue(AbstractAction.NAME)); getRootPane().getActionMap().put(okAction.getValue(Action.NAME), okAction); // make sure the OK action is also enabled in sub components. I registered // the action in the input and action maps of the dialogs root pane and I expected // it to get triggered regardless of what subcomponent had focus, but it didn't. // aclViewer.installKeyAction(okAction); aclViewer.installKeyAction(cancelAction); presetSelector.installKeyAction(okAction); presetSelector.installKeyAction(cancelAction); } /** * constructor */ protected TagEditorDialog() { build(); } @Override public void setVisible(boolean visible) { if (visible) { new WindowGeometry( getClass().getName() + ".geometry", WindowGeometry.centerInWindow( Main.parent, PREFERRED_SIZE ) ).applySafe(this); } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775 new WindowGeometry(this).remember(getClass().getName() + ".geometry"); } super.setVisible(visible); } /** * start an editing session. This method should be called before the dialog * is shown on the screen, i.e. before {@link Dialog#setVisible(boolean)} is * called. */ public void startEditSession() { tagEditor.getModel().clearAppliedPresets(); tagEditor.getModel().initFromJOSMSelection(); autocomplete = Main.getLayerManager().getEditLayer().data.getAutoCompletionManager(); tagEditor.setAutoCompletionManager(autocomplete); getModel().ensureOneTag(); } class CancelAction extends AbstractAction { CancelAction() { putValue(NAME, tr("Cancel")); putValue(SMALL_ICON, ImageProvider.get("cancel")); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); putValue(SHORT_DESCRIPTION, tr("Abort tag editing and close dialog")); } @Override public void actionPerformed(ActionEvent arg0) { setVisible(false); } } class OKAction extends AbstractAction implements PropertyChangeListener { OKAction() { putValue(NAME, tr("OK")); putValue(SMALL_ICON, ImageProvider.get("ok")); putValue(SHORT_DESCRIPTION, tr("Apply edited tags and close dialog")); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl ENTER")); } @Override public void actionPerformed(ActionEvent e) { run(); } public void run() { tagEditor.stopEditing(); setVisible(false); tagEditor.getModel().updateJOSMSelection(); DataSet ds = Main.getLayerManager().getEditDataSet(); ds.fireSelectionChanged(); Main.parent.repaint(); // repaint all - drawing could have been changed } @Override public void propertyChange(PropertyChangeEvent evt) { if (!evt.getPropertyName().equals(TagEditorModel.PROP_DIRTY)) return; if (!evt.getNewValue().getClass().equals(Boolean.class)) return; boolean dirty = (Boolean) evt.getNewValue(); setEnabled(dirty); } } }