// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.turnrestrictions.list; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import javax.swing.AbstractAction; import javax.swing.JCheckBox; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.AutoScaleAction; import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent; import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.gui.SideButton; import org.openstreetmap.josm.gui.dialogs.ToggleDialog; import org.openstreetmap.josm.gui.help.HelpUtil; import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent; import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; import org.openstreetmap.josm.plugins.turnrestrictions.TurnRestrictionBuilder; import org.openstreetmap.josm.plugins.turnrestrictions.editor.TurnRestrictionEditor; import org.openstreetmap.josm.plugins.turnrestrictions.editor.TurnRestrictionEditorManager; import org.openstreetmap.josm.plugins.turnrestrictions.preferences.PreferenceKeys; import org.openstreetmap.josm.tools.ImageProvider; /** * This is the toggle dialog for turn restrictions. The user can switch between * two lists of turn restrictions: * <ol> * <li>the list of turn restrictions in the current data set</li> * <li>the list of turn restrictions related to OSM objects in the current selection</li> * </ol> * */ public class TurnRestrictionsListDialog extends ToggleDialog { /** checkbox for switching between the two list views */ private JCheckBox cbInSelectionOnly; /** the view for the turn restrictions in the current data set */ private TurnRestrictionsInDatasetView pnlTurnRestrictionsInDataSet; /** the view for the turn restrictions related to the current selection */ private TurnRestrictionsInSelectionView pnlTurnRestrictionsInSelection; /** three actions */ private NewAction actNew; private EditAction actEdit; private DeleteAction actDelete; private SelectSelectedTurnRestrictions actSelectSelectedTurnRestrictions; private ZoomToAction actZoomTo; private SwitchListViewHandler switchListViewHandler; private AbstractTurnRestrictionsListView currentListView = null; /** the main content panel in this toggle dialog */ private JPanel pnlContent; private PreferenceChangeHandler preferenceChangeHandler; @Override public void showNotify() { pnlTurnRestrictionsInDataSet.registerAsListener(); pnlTurnRestrictionsInSelection.registerAsListener(); Main.getLayerManager().addActiveLayerChangeListener(actNew); actNew.updateEnabledState(); Main.pref.addPreferenceChangeListener(preferenceChangeHandler); preferenceChangeHandler.refreshIconSet(); } @Override public void hideNotify() { pnlTurnRestrictionsInDataSet.unregisterAsListener(); pnlTurnRestrictionsInSelection.unregisterAsListener(); Main.getLayerManager().removeActiveLayerChangeListener(actNew); Main.pref.removePreferenceChangeListener(preferenceChangeHandler); } /** * Builds the UI */ protected void build() { JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); pnl.setBorder(null); pnl.add(cbInSelectionOnly = new JCheckBox(tr("Only participating in selection"))); cbInSelectionOnly.setToolTipText(tr( "<html>Select to display turn restrictions related to object in the current selection only.<br>" + "Deselect to display all turn restrictions in the current data set.</html>")); pnlContent = new JPanel(new BorderLayout(0, 0)); pnlContent.setBorder(null); pnlContent.add(pnl, BorderLayout.NORTH); actNew = new NewAction(); actEdit = new EditAction(); actDelete = new DeleteAction(); actSelectSelectedTurnRestrictions = new SelectSelectedTurnRestrictions(); actZoomTo = new ZoomToAction(); createLayout(pnlContent, false, Arrays.asList(new SideButton[] { new SideButton(actNew, false /* don't show the name */), new SideButton(actEdit, false /* don't show the name */), new SideButton(actDelete, false /* don't show the name */) })); // create the two list views pnlTurnRestrictionsInDataSet = new TurnRestrictionsInDatasetView(); pnlTurnRestrictionsInSelection = new TurnRestrictionsInSelectionView(); // wire the handler for switching between list views switchListViewHandler = new SwitchListViewHandler(); switchListViewHandler.activateListView(pnlTurnRestrictionsInDataSet); cbInSelectionOnly.addItemListener(switchListViewHandler); // wire the popup menu launcher to the two turn restriction lists TurnRestrictionsPopupLauncher launcher = new TurnRestrictionsPopupLauncher(); pnlTurnRestrictionsInDataSet.getList().addMouseListener(launcher); pnlTurnRestrictionsInSelection.getList().addMouseListener(launcher); preferenceChangeHandler = new PreferenceChangeHandler(); } /** * Constructor */ public TurnRestrictionsListDialog() { super( tr("Turn Restrictions"), "turnrestrictions", tr("Display and manage turn restrictions in the current data set"), null, // no shortcut 150 // default height ); build(); HelpUtil.setHelpContext(this, HelpUtil.ht("/Plugin/TurnRestrictions#TurnRestrictionToggleDialog")); } /** * Switches between the two list view. */ class SwitchListViewHandler implements ItemListener { public void activateListView(AbstractTurnRestrictionsListView view) { if (currentListView != null) { currentListView.removeListSelectionListener(actEdit); currentListView.removeListSelectionListener(actDelete); currentListView.removeListSelectionListener(actSelectSelectedTurnRestrictions); currentListView.removeListSelectionListener(actZoomTo); pnlContent.remove(currentListView); } pnlContent.add(view, BorderLayout.CENTER); currentListView = view; view.addListSelectionListener(actEdit); view.addListSelectionListener(actDelete); view.addListSelectionListener(actSelectSelectedTurnRestrictions); view.addListSelectionListener(actZoomTo); actEdit.updateEnabledState(); actDelete.updateEnabledState(); actSelectSelectedTurnRestrictions.updateEnabledState(); actZoomTo.updateEnabledState(); currentListView.revalidate(); currentListView.repaint(); } @Override public void itemStateChanged(ItemEvent e) { switch(e.getStateChange()) { case ItemEvent.SELECTED: activateListView(pnlTurnRestrictionsInSelection); break; case ItemEvent.DESELECTED: activateListView(pnlTurnRestrictionsInDataSet); break; } } } /** * The edit action * */ class EditAction extends AbstractAction implements ListSelectionListener { EditAction() { putValue(SHORT_DESCRIPTION, tr("Open an editor for the selected turn restriction")); new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this, true); putValue(NAME, tr("Edit")); setEnabled(false); } protected Collection<RelationMember> getMembersForCurrentSelection(Relation r) { Collection<RelationMember> members = new HashSet<>(); Collection<OsmPrimitive> selection = Main.getLayerManager().getEditLayer().data.getSelected(); for (RelationMember member: r.getMembers()) { if (selection.contains(member.getMember())) { members.add(member); } } return members; } public void launchEditor(Relation toEdit) { if (toEdit == null) return; OsmDataLayer layer = Main.getLayerManager().getEditLayer(); TurnRestrictionEditorManager manager = TurnRestrictionEditorManager.getInstance(); TurnRestrictionEditor editor = manager.getEditorForRelation(layer, toEdit); if (editor != null) { editor.setVisible(true); editor.toFront(); } else { editor = new TurnRestrictionEditor( TurnRestrictionsListDialog.this, layer, toEdit); manager.positionOnScreen(editor); manager.register(layer, toEdit, editor); editor.setVisible(true); } } @Override public void actionPerformed(ActionEvent e) { if (!isEnabled()) return; List<Relation> toEdit = currentListView.getModel().getSelectedTurnRestrictions(); if (toEdit.size() != 1) return; launchEditor(toEdit.get(0)); } public void updateEnabledState() { setEnabled(currentListView != null && currentListView.getModel().getSelectedTurnRestrictions().size() == 1); } @Override public void valueChanged(ListSelectionEvent e) { updateEnabledState(); } } /** * The delete action * */ class DeleteAction extends AbstractAction implements ListSelectionListener { class AbortException extends Exception {} DeleteAction() { putValue(SHORT_DESCRIPTION, tr("Delete the selected turn restriction")); new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this, true); putValue(NAME, tr("Delete")); setEnabled(false); } protected void deleteRelation(Relation toDelete) { if (toDelete == null) return; org.openstreetmap.josm.actions.mapmode.DeleteAction.deleteRelation( Main.getLayerManager().getEditLayer(), toDelete ); } @Override public void actionPerformed(ActionEvent e) { if (!isEnabled()) return; List<Relation> toDelete = currentListView.getModel().getSelectedTurnRestrictions(); for (Relation r: toDelete) { deleteRelation(r); } } public void updateEnabledState() { setEnabled(currentListView != null && !currentListView.getModel().getSelectedTurnRestrictions().isEmpty()); } @Override public void valueChanged(ListSelectionEvent e) { updateEnabledState(); } } /** * The action for creating a new turn restriction * */ class NewAction extends AbstractAction implements ActiveLayerChangeListener { NewAction() { putValue(SHORT_DESCRIPTION, tr("Create a new turn restriction")); new ImageProvider("new").getResource().attachImageIcon(this, true); putValue(NAME, tr("New")); updateEnabledState(); } public void run() { OsmDataLayer layer = Main.getLayerManager().getEditLayer(); if (layer == null) return; Relation tr = new TurnRestrictionBuilder().buildFromSelection(layer); TurnRestrictionEditor editor = new TurnRestrictionEditor(TurnRestrictionsListDialog.this, layer, tr); TurnRestrictionEditorManager.getInstance().positionOnScreen(editor); TurnRestrictionEditorManager.getInstance().register(layer, tr, editor); editor.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { run(); } public void updateEnabledState() { setEnabled(Main.getLayerManager().getEditLayer() != null); } @Override public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) { updateEnabledState(); } } /** * Sets the selection of the current data set to the currently selected * turn restrictions. * */ class SelectSelectedTurnRestrictions extends AbstractAction implements ListSelectionListener { class AbortException extends Exception {} SelectSelectedTurnRestrictions() { putValue(SHORT_DESCRIPTION, tr("Set the current JOSM selection to the selected turn restrictions")); new ImageProvider("selectall").getResource().attachImageIcon(this); putValue(NAME, tr("Select in current data layer")); setEnabled(false); } @Override public void actionPerformed(ActionEvent e) { if (!isEnabled()) return; List<Relation> toSelect = currentListView.getModel().getSelectedTurnRestrictions(); if (toSelect.isEmpty()) return; OsmDataLayer layer = Main.getLayerManager().getEditLayer(); if (layer == null) return; layer.data.setSelected(toSelect); } public void updateEnabledState() { setEnabled(currentListView != null && !currentListView.getModel().getSelectedTurnRestrictions().isEmpty()); } @Override public void valueChanged(ListSelectionEvent e) { updateEnabledState(); } } /** * Sets the selection of the current data set to the currently selected * turn restrictions. * */ class ZoomToAction extends AbstractAction implements ListSelectionListener { class AbortException extends Exception {} ZoomToAction() { putValue(SHORT_DESCRIPTION, tr("Zoom to the currently selected turn restrictions")); new ImageProvider("dialogs/autoscale/selection").getResource().attachImageIcon(this); putValue(NAME, tr("Zoom to")); setEnabled(false); } @Override public void actionPerformed(ActionEvent e) { if (!isEnabled()) return; List<Relation> toSelect = currentListView.getModel().getSelectedTurnRestrictions(); if (toSelect.isEmpty()) return; OsmDataLayer layer = Main.getLayerManager().getEditLayer(); if (layer == null) return; layer.data.setSelected(toSelect); new AutoScaleAction("selection").autoScale(); } public void updateEnabledState() { setEnabled(currentListView != null && !currentListView.getModel().getSelectedTurnRestrictions().isEmpty()); } @Override public void valueChanged(ListSelectionEvent e) { updateEnabledState(); } } /** * The launcher for the popup menu. * */ class TurnRestrictionsPopupLauncher extends PopupMenuLauncher { @Override public void launch(MouseEvent evt) { JList<Relation> lst = currentListView.getList(); if (lst.getSelectedIndices().length == 0) { int idx = lst.locationToIndex(evt.getPoint()); if (idx >= 0) { lst.getSelectionModel().addSelectionInterval(idx, idx); } } TurnRestrictionsPopupMenu popup = new TurnRestrictionsPopupMenu(); popup.show(lst, evt.getX(), evt.getY()); } } /** * The popup menu * */ class TurnRestrictionsPopupMenu extends JPopupMenu { TurnRestrictionsPopupMenu() { add(actNew); add(actEdit); add(actDelete); addSeparator(); add(actSelectSelectedTurnRestrictions); add(actZoomTo); } } /** * Listens the changes of the preference {@link PreferenceKeys#ROAD_SIGNS} * and refreshes the set of road icons * */ class PreferenceChangeHandler implements PreferenceChangedListener { public void refreshIconSet() { pnlTurnRestrictionsInDataSet.initIconSetFromPreferences(Main.pref); pnlTurnRestrictionsInSelection.initIconSetFromPreferences(Main.pref); repaint(); } @Override public void preferenceChanged(PreferenceChangeEvent evt) { if (!evt.getKey().equals(PreferenceKeys.ROAD_SIGNS)) return; refreshIconSet(); } } }