// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.turnrestrictions.editor; import static org.openstreetmap.josm.gui.help.HelpUtil.ht; import static org.openstreetmap.josm.tools.I18n.tr; import static org.openstreetmap.josm.tools.I18n.trn; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Observable; import java.util.Observer; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.KeyStroke; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.AutoScaleAction; import org.openstreetmap.josm.command.AddCommand; import org.openstreetmap.josm.command.ChangeCommand; import org.openstreetmap.josm.command.conflict.ConflictAddCommand; import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent; import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; import org.openstreetmap.josm.data.conflict.Conflict; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.gui.DefaultNameFormatter; import org.openstreetmap.josm.gui.HelpAwareOptionPane; import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec; import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor; import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction; import org.openstreetmap.josm.gui.help.HelpUtil; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.plugins.turnrestrictions.preferences.PreferenceKeys; import org.openstreetmap.josm.plugins.turnrestrictions.qa.IssuesView; import org.openstreetmap.josm.tools.CheckParameterUtil; import org.openstreetmap.josm.tools.ImageProvider; public class TurnRestrictionEditor extends JDialog implements NavigationControler { /** the property name for the current turn restriction * @link #setRelation(Relation) * @link #getRelation() */ public static final String TURN_RESTRICION_PROP = RelationEditor.class.getName() + ".turnRestriction"; /** the property name for the current relation snapshot * @link #getRelationSnapshot() */ public static final String TURN_RESTRICION_SNAPSHOT_PROP = RelationEditor.class.getName() + ".turnRestrictionSnapshot"; /** * The turn restriction this editor is working on */ protected Relation turnRestriction; /** * The version of the turn restriction when editing is started. This is * null if a new turn restriction is created. */ protected Relation turnRestrictionSnapshot; /** the data layer the turn restriction belongs to */ private OsmDataLayer layer; private JosmSelectionPanel pnlJosmSelection; private BasicEditorPanel pnlBasicEditor; private TurnRestrictionEditorModel editorModel; private JTabbedPane tpEditors; private PreferenceChangeHandler preferenceChangeHandler; /** * builds the panel with the OK and the Cancel button * * @return the panel with the OK and the Cancel button */ protected JPanel buildOkCancelButtonPanel() { JPanel pnl = new JPanel(); pnl.setLayout(new FlowLayout(FlowLayout.CENTER)); JButton b; pnl.add(b = new JButton(new OKAction())); b.setName("btnOK"); pnl.add(b = new JButton(new CancelAction())); b.setName("btnCancel"); pnl.add(b = new JButton(new ContextSensitiveHelpAction(ht("/Plugin/TurnRestrictions#TurnRestrictionEditor")))); b.setName("btnHelp"); return pnl; } /** * builds the panel which displays the JOSM selection */ protected JPanel buildJOSMSelectionPanel() { pnlJosmSelection = new JosmSelectionPanel(layer, editorModel.getJosmSelectionListModel()); return pnlJosmSelection; } /** * Builds the panel with the editor forms (the left panel in the split pane of * this dialog) */ protected JPanel buildEditorPanel() { JPanel pnl = new JPanel(new BorderLayout()); tpEditors = new JTabbedPane(); JScrollPane pane = new JScrollPane(pnlBasicEditor = new BasicEditorPanel(editorModel)); pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); tpEditors.add(pane); tpEditors.setTitleAt(0, tr("Basic")); tpEditors.setToolTipTextAt(0, tr("Edit basic attributes of a turn restriction")); tpEditors.add(new AdvancedEditorPanel(editorModel)); tpEditors.setTitleAt(1, tr("Advanced")); tpEditors.setToolTipTextAt(1, tr("Edit the raw tags and members of this turn restriction")); tpEditors.add(new IssuesView(editorModel.getIssuesModel())); tpEditors.setTitleAt(2, tr("Errors/Warnings")); tpEditors.setToolTipTextAt(2, tr("Show errors and warnings related to this turn restriction")); pnl.add(tpEditors, BorderLayout.CENTER); return pnl; } /** * Builds the content panel, i.e. the core area of the dialog with the editor * masks and the JOSM selection view */ protected JPanel buildContentPanel() { JPanel pnl = new JPanel(new BorderLayout()); final JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); pnl.add(sp, BorderLayout.CENTER); sp.setLeftComponent(buildEditorPanel()); sp.setRightComponent(buildJOSMSelectionPanel()); addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { // has to be called when the window is visible, otherwise // no effect sp.setDividerLocation(0.7); } }); return pnl; } /** * Creates the toolbar * * @return the toolbar */ protected JToolBar buildToolBar() { JToolBar tb = new JToolBar(); tb.setFloatable(false); tb.add(new ApplyAction()); tb.addSeparator(); DeleteAction actDelete = new DeleteAction(); tb.add(actDelete); addPropertyChangeListener(actDelete); tb.addSeparator(); SelectAction actSelect = new SelectAction(); tb.add(actSelect); addPropertyChangeListener(actSelect); ZoomToAction actZoomTo = new ZoomToAction(); tb.add(actZoomTo); addPropertyChangeListener(actZoomTo); return tb; } /** * builds the UI */ protected void build() { editorModel = new TurnRestrictionEditorModel(getLayer(), this); Container c = getContentPane(); c.setLayout(new BorderLayout()); c.add(buildToolBar(), BorderLayout.NORTH); c.add(buildContentPanel(), BorderLayout.CENTER); c.add(buildOkCancelButtonPanel(), BorderLayout.SOUTH); editorModel.getIssuesModel().addObserver(new IssuesModelObserver()); setSize(600, 600); } /** * Creates a new turn restriction editor * * @param owner the component relative to which the dialog is displayed * @param layer the {@link OsmDataLayer} in whose context a relation is edited. Must not be null. * @throws IllegalArgumentException thrown if layer is null */ public TurnRestrictionEditor(Component owner, OsmDataLayer layer) { this(owner, layer, null); } /** * Creates a new turn restriction editor * * @param owner the component relative to which the dialog is displayed * @param layer the {@link OsmDataLayer} in whose context a relation is edited. Must not be null. * @param turnRestriction the relation. Can be null if a new relation is to be edited. * @throws IllegalArgumentException thrown if layer is null */ public TurnRestrictionEditor(Component owner, OsmDataLayer layer, Relation turnRestriction) throws IllegalArgumentException { super(JOptionPane.getFrameForComponent(owner), false /* not modal */); CheckParameterUtil.ensureParameterNotNull(layer, "layer"); this.layer = layer; build(); setTurnRestriction(turnRestriction); } /** * Replies the currently edited turn restriction * * @return the currently edited relation */ protected Relation getTurnRestriction() { return turnRestriction; } /** * <p>Sets the currently edited turn restriction. Creates a snapshot of the current * state of the turn restriction. See {@link #getTurnRestrictionSnapshot()}</p> * * <p>{@code turnRestriction} can be null if a new restriction is created. A turn * restriction which isn't assigned to a data set is allowed too. If {@code turnRestriction} * is already assigned to a dataset, the dataset of {@link #getLayer()} is required, otherwise * a {@link IllegalArgumentException} is thrown.</p> * * @param turnRestriction the turn restriction * @throws IllegalArgumentException thrown if {@code turnRestriction} belongs to a different dataset than * that owned by the layer {@link #getLayer()} */ protected void setTurnRestriction(Relation turnRestriction) { if (turnRestriction == null) { editorModel.populate(new Relation()); } else if (turnRestriction.getDataSet() == null || turnRestriction.getDataSet() == getLayer().data) { editorModel.populate(turnRestriction); } else { throw new IllegalArgumentException(MessageFormat.format("turnRestriction must belong to layer ''{0}''", getLayer().getName())); } setTurnRestrictionSnapshot(turnRestriction == null ? null : new Relation(turnRestriction)); this.turnRestriction = turnRestriction; support.firePropertyChange(TURN_RESTRICION_PROP, null, this.turnRestriction); updateTitle(); } /** * updates the title of the turn restriction editor */ protected void updateTitle() { if (getTurnRestriction() == null || getTurnRestriction().getDataSet() == null) { setTitle(tr("Create a new turn restriction in layer ''{0}''", layer.getName())); } else if (getTurnRestriction().isNew()) { setTitle(tr("Edit a new turn restriction in layer ''{0}''", layer.getName())); } else { setTitle(tr("Edit turn restriction ''{0}'' in layer ''{1}''", Long.toString(turnRestriction.getId()), layer.getName())); } } /** * Replies the {@link OsmDataLayer} in whose context this relation editor is * open * * @return the {@link OsmDataLayer} in whose context this relation editor is * open */ protected OsmDataLayer getLayer() { return layer; } /** * Replies the state of the edited relation when the editor has been launched * * @return the state of the edited relation when the editor has been launched */ protected Relation getTurnRestrictionSnapshot() { return turnRestrictionSnapshot; } /** * Sets the turn restriction snapshot * * @param snapshot the snapshot */ protected void setTurnRestrictionSnapshot(Relation snapshot) { turnRestrictionSnapshot = snapshot; support.firePropertyChange(TURN_RESTRICION_SNAPSHOT_PROP, null, turnRestrictionSnapshot); } /** * <p>Replies true if the currently edited turn restriction has been changed elsewhere.</p> * * <p>In this case a turn restriction editor can't apply updates to the turn restriction * directly. Rather, it has to create a conflict.</p> * * @return true if the currently edited turn restriction has been changed elsewhere. */ protected boolean isDirtyTurnRestriction() { return !turnRestriction.hasEqualSemanticAttributes(turnRestrictionSnapshot); } /** * Replies the editor model for this editor */ public TurnRestrictionEditorModel getModel() { return editorModel; } @Override public void setVisible(boolean visible) { if (visible && !isVisible()) { pnlJosmSelection.wireListeners(); editorModel.registerAsEventListener(); Main.pref.addPreferenceChangeListener(this.preferenceChangeHandler = new PreferenceChangeHandler()); pnlBasicEditor.initIconSetFromPreferences(Main.pref); } else if (!visible && isVisible()) { pnlJosmSelection.unwireListeners(); editorModel.unregisterAsEventListener(); Main.pref.removePreferenceChangeListener(preferenceChangeHandler); } super.setVisible(visible); if (!visible) { dispose(); } } /* ----------------------------------------------------------------------- */ /* property change support */ /* ----------------------------------------------------------------------- */ private final PropertyChangeSupport support = new PropertyChangeSupport(this); @Override public void addPropertyChangeListener(PropertyChangeListener listener) { this.support.addPropertyChangeListener(listener); } @Override public void removePropertyChangeListener(PropertyChangeListener listener) { this.support.removePropertyChangeListener(listener); } /* ----------------------------------------------------------------------- */ /* interface NavigationControler */ /* ----------------------------------------------------------------------- */ @Override public void gotoBasicEditor() { tpEditors.setSelectedIndex(0); } @Override public void gotoAdvancedEditor() { tpEditors.setSelectedIndex(1); } @Override public void gotoBasicEditor(BasicEditorFokusTargets focusTarget) { tpEditors.setSelectedIndex(0); pnlBasicEditor.requestFocusfor(focusTarget); } /** * The abstract base action for applying the updates of a turn restriction * to the dataset. */ abstract class SavingAction extends AbstractAction { protected boolean confirmSaveDespiteOfErrorsAndWarnings() { int numErrors = editorModel.getIssuesModel().getNumErrors(); int numWarnings = editorModel.getIssuesModel().getNumWarnings(); if (numErrors + numWarnings == 0) return true; StringBuffer sb = new StringBuffer(); sb.append("<html>"); sb.append(trn( "There is still an unresolved error or warning identified for this turn restriction. " + "You are recommended to resolve this issue first.", "There are still {0} errors and/or warnings identified for this turn restriction. " + "You are recommended to resolve these issues first.", numErrors + numWarnings, numErrors + numWarnings )); sb.append("<br>"); sb.append(tr("Do you want to save anyway?")); ButtonSpec[] options = new ButtonSpec[] { new ButtonSpec( tr("Yes, save anyway"), ImageProvider.get("ok"), tr("Save the turn restriction despite of errors and/or warnings"), null // no specific help topic ), new ButtonSpec( tr("No, resolve issues first"), ImageProvider.get("cancel"), tr("Cancel saving and start resolving pending issues first"), null // no specific help topic ) }; int ret = HelpAwareOptionPane.showOptionDialog( JOptionPane.getFrameForComponent(TurnRestrictionEditor.this), sb.toString(), tr("Pending errors and warnings"), JOptionPane.WARNING_MESSAGE, null, // no special icon options, options[1], // cancel is default operation HelpUtil.ht("/Plugin/TurnRestrictions#PendingErrorsAndWarnings") ); return ret == 0 /* OK */; } /** * Replies the list of relation members in {@code r} which refer to * a deleted or invisible primitives. * * @param r the relation * @return the list of relation members in {@code r} which refer to * a deleted or invisible member */ protected List<RelationMember> getDeletedRelationMembers(Relation r) { List<RelationMember> ret = new ArrayList<>(); for (RelationMember rm: r.getMembers()) { if (rm.getMember().isDeleted() || !rm.getMember().isVisible()) { ret.add(rm); } } return ret; } /** * Removes all members referring to deleted or invisible primitives * from the turn restriction {@code tr}. * * @param tr the turn restriction */ protected void removeDeletedMembers(Relation tr) { List<RelationMember> members = tr.getMembers(); for (Iterator<RelationMember> it = members.iterator(); it.hasNext();) { RelationMember rm = it.next(); if (rm.getMember().isDeleted() || !rm.getMember().isVisible()) { it.remove(); } } tr.setMembers(members); } /** * <p>Asks the user how to proceed if a turn restriction refers to deleted or invisible * primitives.</p> * * <p>If this method returns true the respective members should be removed and the turn * restriction should be saved anyway. If it replies false, the turn restriction must not * be saved. </p> * * @param deletedMembers the list of members referring to deleted or invisible primitives * @return the confirmation */ protected boolean confirmSaveTurnRestrictionWithDeletePrimitives(List<RelationMember> deletedMembers) { StringBuffer sb = new StringBuffer(); sb.append("<html>"); sb.append(trn("This turn restriction refers to an object which was deleted outside " + "of this turn restriction editor:", "This turn restriction refers to {0} objects which were deleted outside " + "of this turn restriction editor:", deletedMembers.size(), deletedMembers.size())); sb.append("<ul>"); for (RelationMember rm: deletedMembers) { sb.append("<li>"); if (!rm.getRole().equals("")) { sb.append(rm.getRole()).append(": "); } sb.append(rm.getMember().getDisplayName(DefaultNameFormatter.getInstance())); sb.append("</li>"); } sb.append(tr("Updates to this turn restriction can''t be saved unless deleted members are removed.<br>" + "How to you want to proceed?")); ButtonSpec[] options = new ButtonSpec[] { new ButtonSpec( tr("Remove deleted members and save"), ImageProvider.get("ok"), tr("Remove deleted members and save"), null ), new ButtonSpec( tr("Cancel and return to editor"), ImageProvider.get("cancel"), tr("Cancel and return to editor"), null ) }; int ret = HelpAwareOptionPane.showOptionDialog( TurnRestrictionEditor.this, sb.toString(), tr("Deleted members in turn restriction"), JOptionPane.WARNING_MESSAGE, null, // no special icon options, options[1], // cancel is default null // FIXME: provide help topic ); return ret == 0 /* OK button */; } /** * apply updates to a new turn restriction */ protected boolean applyNewTurnRestriction() { Relation newTurnRestriction = new Relation(); editorModel.apply(newTurnRestriction); // If the user wanted to create a new turn restriction, but didn't add any members or // tags, don't add an empty relation if (newTurnRestriction.getMembersCount() == 0 && !newTurnRestriction.hasKeys()) return true; // check whether the turn restriction refers to new List<RelationMember> deletedMembers = getDeletedRelationMembers(newTurnRestriction); if (!deletedMembers.isEmpty()) { if (!confirmSaveTurnRestrictionWithDeletePrimitives(deletedMembers)) { return false; } removeDeletedMembers(newTurnRestriction); } Main.main.undoRedo.add(new AddCommand(getLayer(), newTurnRestriction)); // make sure everybody is notified about the changes // TurnRestrictionEditor.this.setTurnRestriction(newTurnRestriction); TurnRestrictionEditorManager.getInstance().updateContext( getLayer(), getTurnRestriction(), TurnRestrictionEditor.this ); return true; } /** * Apply the updates for an existing turn restriction which has been changed * outside of the turn restriction editor. * */ protected void applyExistingConflictingTurnRestriction() { Relation toUpdate = new Relation(getTurnRestriction()); editorModel.apply(toUpdate); Conflict<Relation> conflict = new Conflict<>(getTurnRestriction(), toUpdate); Main.main.undoRedo.add(new ConflictAddCommand(getLayer(), conflict)); } /** * Apply the updates for an existing turn restriction which has not been changed * outside of the turn restriction editor. */ protected void applyExistingNonConflictingTurnRestriction() { if (getTurnRestriction().getDataSet() == null) { editorModel.apply(getTurnRestriction()); Main.main.undoRedo.add(new AddCommand(getTurnRestriction())); } else { Relation toUpdate = new Relation(getTurnRestriction()); editorModel.apply(toUpdate); Main.main.undoRedo.add(new ChangeCommand(getTurnRestriction(), toUpdate)); } // this will refresh the snapshot and update the dialog title // setTurnRestriction(getTurnRestriction()); } protected boolean confirmClosingBecauseOfDirtyState() { ButtonSpec[] options = new ButtonSpec[] { new ButtonSpec( tr("Yes, create a conflict and close"), ImageProvider.get("ok"), tr("Create a conflict and close this turn restriction editor"), null /* no specific help topic */ ), new ButtonSpec( tr("No, continue editing"), ImageProvider.get("cancel"), tr("Return to the turn restriction editor and resume editing"), null /* no specific help topic */ ) }; int ret = HelpAwareOptionPane.showOptionDialog( Main.parent, tr("<html>This turn restriction has been changed outside of the editor.<br>" + "You cannot apply your changes and continue editing.<br>" + "<br>" + "Do you want to create a conflict and close the editor?</html>"), tr("Conflict in data"), JOptionPane.WARNING_MESSAGE, null, options, options[0], // OK is default null // FIXME: provide help topic ); return ret == 0; } protected void warnDoubleConflict() { JOptionPane.showMessageDialog( Main.parent, tr("<html>Layer ''{0}'' already has a conflict for object<br>" + "''{1}''.<br>" + "Please resolve this conflict first, then try again.</html>", getLayer().getName(), getTurnRestriction().getDisplayName(DefaultNameFormatter.getInstance()) ), tr("Already participating in a conflict"), JOptionPane.WARNING_MESSAGE ); } } class ApplyAction extends SavingAction { ApplyAction() { putValue(SHORT_DESCRIPTION, tr("Apply the current updates")); new ImageProvider("save").getResource().attachImageIcon(this); putValue(NAME, tr("Apply")); setEnabled(true); } public void run() { if (!confirmSaveDespiteOfErrorsAndWarnings()) { tpEditors.setSelectedIndex(2); // show the errors and warnings return; } if (getTurnRestriction() == null || getTurnRestriction().getDataSet() == null) { applyNewTurnRestriction(); return; } Relation toUpdate = new Relation(getTurnRestriction()); editorModel.apply(toUpdate); if (TurnRestrictionEditorModel.hasSameMembersAndTags(toUpdate, getTurnRestriction())) // nothing to update return; if (isDirtyTurnRestriction()) { if (confirmClosingBecauseOfDirtyState()) { if (getLayer().getConflicts().hasConflictForMy(getTurnRestriction())) { warnDoubleConflict(); return; } applyExistingConflictingTurnRestriction(); setVisible(false); } } else { applyExistingNonConflictingTurnRestriction(); } } @Override public void actionPerformed(ActionEvent e) { run(); } } class OKAction extends SavingAction { OKAction() { putValue(SHORT_DESCRIPTION, tr("Apply the updates and close the dialog")); new ImageProvider("ok").getResource().attachImageIcon(this); putValue(NAME, tr("OK")); setEnabled(true); } public void run() { if (!confirmSaveDespiteOfErrorsAndWarnings()) { tpEditors.setSelectedIndex(2); // show the errors and warnings return; } if (getTurnRestriction() == null || getTurnRestriction().getDataSet() == null) { // it's a new turn restriction. Try to save it and close the dialog if (applyNewTurnRestriction()) { setVisible(false); } return; } Relation toUpdate = new Relation(getTurnRestriction()); editorModel.apply(toUpdate); if (TurnRestrictionEditorModel.hasSameMembersAndTags(toUpdate, getTurnRestriction())) { // nothing to update setVisible(false); return; } if (isDirtyTurnRestriction()) { // the turn restriction this editor is working on has changed outside // of the editor. if (confirmClosingBecauseOfDirtyState()) { if (getLayer().getConflicts().hasConflictForMy(getTurnRestriction())) { warnDoubleConflict(); return; } applyExistingConflictingTurnRestriction(); } else return; } else { applyExistingNonConflictingTurnRestriction(); } setVisible(false); } @Override public void actionPerformed(ActionEvent e) { run(); } } /** * Action for canceling the current dialog */ class CancelAction extends AbstractAction { CancelAction() { putValue(SHORT_DESCRIPTION, tr("Cancel the updates and close the dialog")); new ImageProvider("cancel").getResource().attachImageIcon(this); putValue(NAME, tr("Cancel")); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ESCAPE")); TurnRestrictionEditor.this.getRootPane().registerKeyboardAction(this, KeyStroke.getKeyStroke("ESCAPE"), JComponent.WHEN_IN_FOCUSED_WINDOW); setEnabled(true); } @Override public void actionPerformed(ActionEvent e) { setVisible(false); } } class DeleteAction extends AbstractAction implements PropertyChangeListener { DeleteAction() { putValue(NAME, tr("Delete")); putValue(SHORT_DESCRIPTION, tr("Delete this turn restriction")); new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this); updateEnabledState(); } protected void updateEnabledState() { Relation tr = getTurnRestriction(); setEnabled(tr != null && tr.getDataSet() != null); } @Override public void actionPerformed(ActionEvent e) { Relation tr = getTurnRestriction(); if (tr == null || tr.getDataSet() == null) return; org.openstreetmap.josm.actions.mapmode.DeleteAction.deleteRelation( getLayer(), tr ); setVisible(false); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(TURN_RESTRICION_PROP)) { updateEnabledState(); } } } class SelectAction extends AbstractAction implements PropertyChangeListener { SelectAction() { putValue(NAME, tr("Select")); putValue(SHORT_DESCRIPTION, tr("Select this turn restriction")); new ImageProvider("dialogs", "select").getResource().attachImageIcon(this); updateEnabledState(); } protected void updateEnabledState() { Relation tr = getTurnRestriction(); setEnabled(tr != null && tr.getDataSet() != null); } @Override public void actionPerformed(ActionEvent e) { Relation tr = getTurnRestriction(); if (tr == null || tr.getDataSet() == null) return; getLayer().data.setSelected(tr); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(TURN_RESTRICION_PROP)) { updateEnabledState(); } } } class ZoomToAction extends AbstractAction implements PropertyChangeListener { ZoomToAction() { putValue(NAME, tr("Zoom to")); putValue(SHORT_DESCRIPTION, tr("Activate the layer this turn restriction belongs to and zoom to it")); new ImageProvider("dialogs/autoscale", "data").getResource().attachImageIcon(this); updateEnabledState(); } protected void updateEnabledState() { Relation tr = getTurnRestriction(); setEnabled(tr != null && tr.getDataSet() != null); } @Override public void actionPerformed(ActionEvent e) { if (Main.getLayerManager().getActiveLayer() != getLayer()) { Main.getLayerManager().setActiveLayer(getLayer()); } Relation tr = getTurnRestriction(); if (tr == null || tr.getDataSet() == null) return; getLayer().data.setSelected(tr); AutoScaleAction.zoomToSelection(); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(TURN_RESTRICION_PROP)) { updateEnabledState(); } } } class IssuesModelObserver implements Observer { @Override public void update(Observable o, Object arg) { int numWarnings = editorModel.getIssuesModel().getNumWarnings(); int numErrors = editorModel.getIssuesModel().getNumErrors(); String warningText = null; if (numWarnings > 0) { warningText = trn("{0} warning", "{0} warnings", numWarnings, numWarnings); } String errorText = null; if (numErrors > 0) { errorText = trn("{0} error", "{0} errors", numErrors, numErrors); } String title = ""; if (errorText != null) { title += errorText; } if (warningText != null) { if (title.length() > 0) { title += "/"; } title += warningText; } if (title.length() == 0) { title = tr("no issues"); } tpEditors.setTitleAt(2, title); tpEditors.setEnabledAt(2, numWarnings + numErrors > 0); } } /** * <p>Listens to changes of the preference {@link PreferenceKeys#ROAD_SIGNS} * and refreshes the set of road icons.</p> * * <p>Listens to changes of the preference {@link PreferenceKeys#SHOW_VIAS_IN_BASIC_EDITOR} * and toggles the visibility of the list of via-objects in the Basic * Editor.</p> * */ class PreferenceChangeHandler implements PreferenceChangedListener { public void refreshIconSet() { pnlBasicEditor.initIconSetFromPreferences(Main.pref); } @Override public void preferenceChanged(PreferenceChangeEvent evt) { if (evt.getKey().equals(PreferenceKeys.ROAD_SIGNS)) { refreshIconSet(); } else if (evt.getKey().equals(PreferenceKeys.SHOW_VIAS_IN_BASIC_EDITOR)) { pnlBasicEditor.initViasVisibilityFromPreferences(Main.pref); } } } }