// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.dialogs; import static org.openstreetmap.josm.gui.help.HelpUtil.ht; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.gui.DefaultNameFormatter; import org.openstreetmap.josm.gui.ExtendedDialog; import org.openstreetmap.josm.gui.conflict.pair.ConflictResolver; import org.openstreetmap.josm.gui.help.HelpBrowser; import org.openstreetmap.josm.gui.help.HelpUtil; import org.openstreetmap.josm.tools.ImageProvider; /** * This is an extended dialog for resolving conflict between {@link OsmPrimitive}s. * @since 1622 */ public class ConflictResolutionDialog extends ExtendedDialog implements PropertyChangeListener { /** the conflict resolver component */ private final ConflictResolver resolver = new ConflictResolver(); private final JLabel titleLabel = new JLabel("", null, JLabel.CENTER); private final ApplyResolutionAction applyResolutionAction = new ApplyResolutionAction(); private boolean isRegistered; /** * Constructs a new {@code ConflictResolutionDialog}. * @param parent parent component */ public ConflictResolutionDialog(Component parent) { // We define our own actions, but need to give a hint about number of buttons super(parent, tr("Resolve conflicts"), new String[] {null, null, null}); setDefaultButton(1); setCancelButton(2); build(); pack(); if (getInsets().top > 0) { titleLabel.setVisible(false); } } @Override public void removeNotify() { super.removeNotify(); unregisterListeners(); } @Override public void addNotify() { super.addNotify(); registerListeners(); } private synchronized void registerListeners() { if (!isRegistered) { resolver.addPropertyChangeListener(applyResolutionAction); resolver.registerListeners(); isRegistered = true; } } private synchronized void unregisterListeners() { // See #13479 - See https://bugs.openjdk.java.net/browse/JDK-4387314 // Owner window keep a list of owned windows, and does not remove the references when the child is disposed. // There's no easy way to remove ourselves from this list, so we must keep track of register state if (isRegistered) { resolver.removePropertyChangeListener(applyResolutionAction); resolver.unregisterListeners(); isRegistered = false; } } /** * builds the GUI */ protected void build() { JPanel p = new JPanel(new BorderLayout()); p.add(titleLabel, BorderLayout.NORTH); updateTitle(); resolver.setName("panel.conflictresolver"); p.add(resolver, BorderLayout.CENTER); resolver.addPropertyChangeListener(this); HelpUtil.setHelpContext(this.getRootPane(), ht("Dialog/Conflict")); setContent(p); } @Override protected Action createButtonAction(int i) { switch (i) { case 0: return applyResolutionAction; case 1: return new CancelAction(); case 2: return new HelpAction(); default: return super.createButtonAction(i); } } /** * Replies the conflict resolver component. * @return the conflict resolver component */ public ConflictResolver getConflictResolver() { return resolver; } /** * Action for canceling conflict resolution */ class CancelAction extends AbstractAction { CancelAction() { putValue(Action.SHORT_DESCRIPTION, tr("Cancel conflict resolution and close the dialog")); putValue(Action.NAME, tr("Cancel")); new ImageProvider("cancel").getResource().attachImageIcon(this); setEnabled(true); } @Override public void actionPerformed(ActionEvent evt) { buttonAction(2, evt); } } /** * Action for canceling conflict resolution */ static class HelpAction extends AbstractAction { HelpAction() { putValue(Action.SHORT_DESCRIPTION, tr("Show help information")); putValue(Action.NAME, tr("Help")); new ImageProvider("help").getResource().attachImageIcon(this); setEnabled(true); } @Override public void actionPerformed(ActionEvent evt) { HelpBrowser.setUrlForHelpTopic(ht("/Dialog/Conflict")); } } /** * Action for applying resolved differences in a conflict * */ class ApplyResolutionAction extends AbstractAction implements PropertyChangeListener { ApplyResolutionAction() { putValue(Action.SHORT_DESCRIPTION, tr("Apply resolved conflicts and close the dialog")); putValue(Action.NAME, tr("Apply Resolution")); new ImageProvider("dialogs", "conflict").getResource().attachImageIcon(this); updateEnabledState(); } protected void updateEnabledState() { setEnabled(resolver.isResolvedCompletely()); } @Override public void actionPerformed(ActionEvent evt) { if (!resolver.isResolvedCompletely()) { Object[] options = { tr("Close anyway"), tr("Continue resolving")}; int ret = JOptionPane.showOptionDialog(Main.parent, tr("<html>You did not finish to merge the differences in this conflict.<br>" + "Conflict resolutions will not be applied unless all differences<br>" + "are resolved.<br>" + "Click <strong>{0}</strong> to close anyway.<strong> Already<br>" + "resolved differences will not be applied.</strong><br>" + "Click <strong>{1}</strong> to return to resolving conflicts.</html>", options[0].toString(), options[1].toString() ), tr("Conflict not resolved completely"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[1] ); switch(ret) { case JOptionPane.YES_OPTION: buttonAction(1, evt); break; default: return; } } Main.main.undoRedo.add(resolver.buildResolveCommand()); buttonAction(1, evt); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(ConflictResolver.RESOLVED_COMPLETELY_PROP)) { updateEnabledState(); } } } protected void updateTitle() { updateTitle(null); } protected void updateTitle(OsmPrimitive my) { if (my == null) { setTitle(tr("Resolve conflicts")); } else { setTitle(tr("Resolve conflicts for ''{0}''", my.getDisplayName(DefaultNameFormatter.getInstance()))); } } @Override public void setTitle(String title) { super.setTitle(title); if (titleLabel != null) { titleLabel.setText(title); } } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(ConflictResolver.MY_PRIMITIVE_PROP)) { updateTitle((OsmPrimitive) evt.getNewValue()); } } }