// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.conflict.pair.properties; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.text.DecimalFormat; import java.util.List; import java.util.Observable; import java.util.Observer; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import org.openstreetmap.josm.data.conflict.Conflict; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.gui.DefaultNameFormatter; import org.openstreetmap.josm.gui.conflict.pair.IConflictResolver; import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType; import org.openstreetmap.josm.tools.ImageProvider; /** * This class represents a UI component for resolving conflicts in some properties * of {@see OsmPrimitive}. * */ public class PropertiesMerger extends JPanel implements Observer, IConflictResolver { private static DecimalFormat COORD_FORMATTER = new DecimalFormat("###0.0000000"); public final static Color BGCOLOR_NO_CONFLICT = new Color(234,234,234); public final static Color BGCOLOR_UNDECIDED = new Color(255,197,197); public final static Color BGCOLOR_DECIDED = new Color(217,255,217); private JLabel lblMyVersion; private JLabel lblMergedVersion; private JLabel lblTheirVersion; private JLabel lblMyCoordinates; private JLabel lblMergedCoordinates; private JLabel lblTheirCoordinates; private JLabel lblMyDeletedState; private JLabel lblMergedDeletedState; private JLabel lblTheirDeletedState; private JLabel lblMyVisibleState; private JLabel lblMergedVisibleState; private JLabel lblTheirVisibleState; private JLabel lblMyReferrers; private JLabel lblTheirReferrers; private final PropertiesMergeModel model; protected JLabel buildValueLabel(String name) { JLabel lbl = new JLabel(); lbl.setName(name); lbl.setHorizontalAlignment(JLabel.CENTER); lbl.setOpaque(true); lbl.setBorder(BorderFactory.createLoweredBevelBorder()); return lbl; } protected void buildHeaderRow() { GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 1; gc.gridy = 0; gc.gridwidth = 1; gc.gridheight = 1; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; gc.insets = new Insets(10,0,10,0); lblMyVersion = new JLabel(tr("My version")); lblMyVersion.setToolTipText(tr("Properties in my dataset, i.e. the local dataset")); add(lblMyVersion, gc); gc.gridx = 3; gc.gridy = 0; lblMergedVersion = new JLabel(tr("Merged version")); lblMergedVersion.setToolTipText(tr("Properties in the merged element. They will replace properties in my elements when merge decisions are applied.")); add(lblMergedVersion, gc); gc.gridx = 5; gc.gridy = 0; lblTheirVersion = new JLabel(tr("Their version")); lblTheirVersion.setToolTipText(tr("Properties in their dataset, i.e. the server dataset")); add(lblTheirVersion, gc); } protected void buildCoordinateConflictRows() { GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 0; gc.gridy = 1; gc.gridwidth = 1; gc.gridheight = 1; gc.fill = GridBagConstraints.HORIZONTAL; gc.anchor = GridBagConstraints.LINE_START; gc.weightx = 0.0; gc.weighty = 0.0; gc.insets = new Insets(0,5,0,5); add(new JLabel(tr("Coordinates:")), gc); gc.gridx = 1; gc.gridy = 1; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblMyCoordinates = buildValueLabel("label.mycoordinates"), gc); gc.gridx = 2; gc.gridy = 1; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; KeepMyCoordinatesAction actKeepMyCoordinates = new KeepMyCoordinatesAction(); model.addObserver(actKeepMyCoordinates); JButton btnKeepMyCoordinates = new JButton(actKeepMyCoordinates); btnKeepMyCoordinates.setName("button.keepmycoordinates"); add(btnKeepMyCoordinates, gc); gc.gridx = 3; gc.gridy = 1; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblMergedCoordinates = buildValueLabel("label.mergedcoordinates"), gc); gc.gridx = 4; gc.gridy = 1; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; KeepTheirCoordinatesAction actKeepTheirCoordinates = new KeepTheirCoordinatesAction(); model.addObserver(actKeepTheirCoordinates); JButton btnKeepTheirCoordinates = new JButton(actKeepTheirCoordinates); add(btnKeepTheirCoordinates, gc); gc.gridx = 5; gc.gridy = 1; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblTheirCoordinates = buildValueLabel("label.theircoordinates"), gc); // --------------------------------------------------- gc.gridx = 3; gc.gridy = 2; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; UndecideCoordinateConflictAction actUndecideCoordinates = new UndecideCoordinateConflictAction(); model.addObserver(actUndecideCoordinates); JButton btnUndecideCoordinates = new JButton(actUndecideCoordinates); add(btnUndecideCoordinates, gc); } protected void buildDeletedStateConflictRows() { GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 0; gc.gridy = 3; gc.gridwidth = 1; gc.gridheight = 1; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.LINE_START; gc.weightx = 0.0; gc.weighty = 0.0; gc.insets = new Insets(0,5,0,5); add(new JLabel(tr("Deleted State:")), gc); gc.gridx = 1; gc.gridy = 3; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblMyDeletedState = buildValueLabel("label.mydeletedstate"), gc); gc.gridx = 2; gc.gridy = 3; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; KeepMyDeletedStateAction actKeepMyDeletedState = new KeepMyDeletedStateAction(); model.addObserver(actKeepMyDeletedState); JButton btnKeepMyDeletedState = new JButton(actKeepMyDeletedState); btnKeepMyDeletedState.setName("button.keepmydeletedstate"); add(btnKeepMyDeletedState, gc); gc.gridx = 3; gc.gridy = 3; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblMergedDeletedState = buildValueLabel("label.mergeddeletedstate"), gc); gc.gridx = 4; gc.gridy = 3; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; KeepTheirDeletedStateAction actKeepTheirDeletedState = new KeepTheirDeletedStateAction(); model.addObserver(actKeepTheirDeletedState); JButton btnKeepTheirDeletedState = new JButton(actKeepTheirDeletedState); btnKeepTheirDeletedState.setName("button.keeptheirdeletedstate"); add(btnKeepTheirDeletedState, gc); gc.gridx = 5; gc.gridy = 3; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblTheirDeletedState = buildValueLabel("label.theirdeletedstate"), gc); // --------------------------------------------------- gc.gridx = 3; gc.gridy = 4; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; UndecideDeletedStateConflictAction actUndecideDeletedState = new UndecideDeletedStateConflictAction(); model.addObserver(actUndecideDeletedState); JButton btnUndecideDeletedState = new JButton(actUndecideDeletedState); btnUndecideDeletedState.setName("button.undecidedeletedstate"); add(btnUndecideDeletedState, gc); } protected void buildVisibleStateRows() { GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 0; gc.gridy = 5; gc.gridwidth = 1; gc.gridheight = 1; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.LINE_START; gc.weightx = 0.0; gc.weighty = 0.0; gc.insets = new Insets(0,5,0,5); add(new JLabel(tr("Visible State:")), gc); gc.gridx = 1; gc.gridy = 5; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblMyVisibleState = buildValueLabel("label.myvisiblestate"), gc); gc.gridx = 2; gc.gridy = 5; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; KeepMyVisibleStateAction actKeepMyVisibleState = new KeepMyVisibleStateAction(); model.addObserver(actKeepMyVisibleState); JButton btnKeepMyVisibleState = new JButton(actKeepMyVisibleState); btnKeepMyVisibleState.setName("button.keepmyvisiblestate"); add(btnKeepMyVisibleState, gc); gc.gridx = 3; gc.gridy = 5; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblMergedVisibleState = buildValueLabel("label.mergedvisiblestate"), gc); gc.gridx = 4; gc.gridy = 5; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; KeepTheirVisibleStateAction actKeepTheirVisibleState = new KeepTheirVisibleStateAction(); model.addObserver(actKeepTheirVisibleState); JButton btnKeepTheirVisibleState = new JButton(actKeepTheirVisibleState); btnKeepTheirVisibleState.setName("button.keeptheirvisiblestate"); add(btnKeepTheirVisibleState, gc); gc.gridx = 5; gc.gridy = 5; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblTheirVisibleState = buildValueLabel("label.theirvisiblestate"), gc); // --------------------------------------------------- gc.gridx = 3; gc.gridy = 6; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.0; gc.weighty = 0.0; UndecideVisibleStateConflictAction actUndecideVisibleState = new UndecideVisibleStateConflictAction(); model.addObserver(actUndecideVisibleState); JButton btnUndecideVisibleState = new JButton(actUndecideVisibleState); btnUndecideVisibleState.setName("button.undecidevisiblestate"); add(btnUndecideVisibleState, gc); } protected void buildReferrersRow() { GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 0; gc.gridy = 7; gc.gridwidth = 1; gc.gridheight = 1; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.LINE_START; gc.weightx = 0.0; gc.weighty = 0.0; gc.insets = new Insets(0,5,0,5); add(new JLabel(tr("Referenced by:")), gc); gc.gridx = 1; gc.gridy = 7; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblMyReferrers = buildValueLabel("label.myreferrers"), gc); gc.gridx = 5; gc.gridy = 7; gc.fill = GridBagConstraints.BOTH; gc.anchor = GridBagConstraints.CENTER; gc.weightx = 0.33; gc.weighty = 0.0; add(lblTheirReferrers = buildValueLabel("label.theirreferrers"), gc); } protected void build() { setLayout(new GridBagLayout()); buildHeaderRow(); buildCoordinateConflictRows(); buildDeletedStateConflictRows(); buildVisibleStateRows(); buildReferrersRow(); } public PropertiesMerger() { model = new PropertiesMergeModel(); model.addObserver(this); build(); } public String coordToString(LatLon coord) { if (coord == null) return tr("(none)"); StringBuilder sb = new StringBuilder(); sb.append("(") .append(COORD_FORMATTER.format(coord.lat())) .append(",") .append(COORD_FORMATTER.format(coord.lon())) .append(")"); return sb.toString(); } public String deletedStateToString(Boolean deleted) { if (deleted == null) return tr("(none)"); if (deleted) return tr("deleted"); else return tr("not deleted"); } public String visibleStateToString(Boolean visible) { if (visible == null) return tr("(none)"); if (visible) return tr("visible (on the server)"); else return tr("not visible (on the server)"); } public String visibleStateToStringMerged(Boolean visible) { if (visible == null) return tr("(none)"); if (visible) return tr("Keep a clone of the local version"); else return tr("Physically delete from local dataset"); } public String referrersToString(List<OsmPrimitive> referrers) { if (referrers.isEmpty()) return tr("(none)"); String str = "<html>"; for (OsmPrimitive r: referrers) { str = str + r.getDisplayName(DefaultNameFormatter.getInstance()) + "<br>"; } str = str + "</html>"; return str; } protected void updateCoordinates() { lblMyCoordinates.setText(coordToString(model.getMyCoords())); lblMergedCoordinates.setText(coordToString(model.getMergedCoords())); lblTheirCoordinates.setText(coordToString(model.getTheirCoords())); if (! model.hasCoordConflict()) { lblMyCoordinates.setBackground(BGCOLOR_NO_CONFLICT); lblMergedCoordinates.setBackground(BGCOLOR_NO_CONFLICT); lblTheirCoordinates.setBackground(BGCOLOR_NO_CONFLICT); } else { if (!model.isDecidedCoord()) { lblMyCoordinates.setBackground(BGCOLOR_UNDECIDED); lblMergedCoordinates.setBackground(BGCOLOR_NO_CONFLICT); lblTheirCoordinates.setBackground(BGCOLOR_UNDECIDED); } else { lblMyCoordinates.setBackground( model.isCoordMergeDecision(MergeDecisionType.KEEP_MINE) ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT ); lblMergedCoordinates.setBackground(BGCOLOR_DECIDED); lblTheirCoordinates.setBackground( model.isCoordMergeDecision(MergeDecisionType.KEEP_THEIR) ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT ); } } } protected void updateDeletedState() { lblMyDeletedState.setText(deletedStateToString(model.getMyDeletedState())); lblMergedDeletedState.setText(deletedStateToString(model.getMergedDeletedState())); lblTheirDeletedState.setText(deletedStateToString(model.getTheirDeletedState())); if (! model.hasDeletedStateConflict()) { lblMyDeletedState.setBackground(BGCOLOR_NO_CONFLICT); lblMergedDeletedState.setBackground(BGCOLOR_NO_CONFLICT); lblTheirDeletedState.setBackground(BGCOLOR_NO_CONFLICT); } else { if (!model.isDecidedDeletedState()) { lblMyDeletedState.setBackground(BGCOLOR_UNDECIDED); lblMergedDeletedState.setBackground(BGCOLOR_NO_CONFLICT); lblTheirDeletedState.setBackground(BGCOLOR_UNDECIDED); } else { lblMyDeletedState.setBackground( model.isDeletedStateDecision(MergeDecisionType.KEEP_MINE) ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT ); lblMergedDeletedState.setBackground(BGCOLOR_DECIDED); lblTheirDeletedState.setBackground( model.isDeletedStateDecision(MergeDecisionType.KEEP_THEIR) ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT ); } } } protected void updateVisibleState() { lblMyVisibleState.setText(visibleStateToString(model.getMyVisibleState())); lblMergedVisibleState.setText(visibleStateToStringMerged(model.getMergedVisibleState())); lblTheirVisibleState.setText(visibleStateToString(model.getTheirVisibleState())); if (! model.hasVisibleStateConflict()) { lblMyVisibleState.setBackground(BGCOLOR_NO_CONFLICT); lblMergedVisibleState.setBackground(BGCOLOR_NO_CONFLICT); lblTheirVisibleState.setBackground(BGCOLOR_NO_CONFLICT); } else { if (!model.isDecidedVisibleState()) { lblMyVisibleState.setBackground(BGCOLOR_UNDECIDED); lblMergedVisibleState.setBackground(BGCOLOR_NO_CONFLICT); lblTheirVisibleState.setBackground(BGCOLOR_UNDECIDED); } else { lblMyVisibleState.setBackground( model.isVisibleStateDecision(MergeDecisionType.KEEP_MINE) ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT ); lblMergedVisibleState.setBackground(BGCOLOR_DECIDED); lblTheirVisibleState.setBackground( model.isVisibleStateDecision(MergeDecisionType.KEEP_THEIR) ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT ); } } } protected void updateReferrers() { lblMyReferrers.setText(referrersToString(model.getMyReferrers())); lblMyReferrers.setBackground(BGCOLOR_NO_CONFLICT); lblTheirReferrers.setText(referrersToString(model.getTheirReferrers())); lblTheirReferrers.setBackground(BGCOLOR_NO_CONFLICT); } public void update(Observable o, Object arg) { updateCoordinates(); updateDeletedState(); updateVisibleState(); updateReferrers(); } public PropertiesMergeModel getModel() { return model; } class KeepMyCoordinatesAction extends AbstractAction implements Observer { public KeepMyCoordinatesAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine")); putValue(Action.SHORT_DESCRIPTION, tr("Keep my coordiates")); } public void actionPerformed(ActionEvent e) { model.decideCoordsConflict(MergeDecisionType.KEEP_MINE); } public void update(Observable o, Object arg) { setEnabled(model.hasCoordConflict() && ! model.isDecidedCoord()); } } class KeepTheirCoordinatesAction extends AbstractAction implements Observer { public KeepTheirCoordinatesAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir")); putValue(Action.SHORT_DESCRIPTION, tr("Keep their coordiates")); } public void actionPerformed(ActionEvent e) { model.decideCoordsConflict(MergeDecisionType.KEEP_THEIR); } public void update(Observable o, Object arg) { setEnabled(model.hasCoordConflict() && ! model.isDecidedCoord()); } } class UndecideCoordinateConflictAction extends AbstractAction implements Observer { public UndecideCoordinateConflictAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide")); putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between different coordinates")); } public void actionPerformed(ActionEvent e) { model.decideCoordsConflict(MergeDecisionType.UNDECIDED); } public void update(Observable o, Object arg) { setEnabled(model.hasCoordConflict() && model.isDecidedCoord()); } } class KeepMyDeletedStateAction extends AbstractAction implements Observer { public KeepMyDeletedStateAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine")); putValue(Action.SHORT_DESCRIPTION, tr("Keep my deleted state")); } public void actionPerformed(ActionEvent e) { model.decideDeletedStateConflict(MergeDecisionType.KEEP_MINE); } public void update(Observable o, Object arg) { setEnabled(model.hasDeletedStateConflict() && ! model.isDecidedDeletedState()); } } class KeepTheirDeletedStateAction extends AbstractAction implements Observer { public KeepTheirDeletedStateAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir")); putValue(Action.SHORT_DESCRIPTION, tr("Keep their deleted state")); } public void actionPerformed(ActionEvent e) { model.decideDeletedStateConflict(MergeDecisionType.KEEP_THEIR); } public void update(Observable o, Object arg) { setEnabled(model.hasDeletedStateConflict() && ! model.isDecidedDeletedState()); } } class UndecideDeletedStateConflictAction extends AbstractAction implements Observer { public UndecideDeletedStateConflictAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide")); putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between deleted state")); } public void actionPerformed(ActionEvent e) { model.decideDeletedStateConflict(MergeDecisionType.UNDECIDED); } public void update(Observable o, Object arg) { setEnabled(model.hasDeletedStateConflict() && model.isDecidedDeletedState()); } } class KeepMyVisibleStateAction extends AbstractAction implements Observer { public KeepMyVisibleStateAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine")); putValue(Action.SHORT_DESCRIPTION, tr("Keep my visible state")); } public void actionPerformed(ActionEvent e) { if (confirmKeepMine()) { model.decideVisibleStateConflict(MergeDecisionType.KEEP_MINE); } } public void update(Observable o, Object arg) { setEnabled(model.hasVisibleStateConflict() && ! model.isDecidedVisibleState()); } protected boolean confirmKeepMine() { String [] options = { tr("Yes, reset the id"), tr("No, abort") }; int ret = JOptionPane.showOptionDialog( null, tr("<html>To keep your local version, JOSM<br>" + "has to reset the id of primitive {0} to 0.<br>" + "On the next upload the server will assign<br>" + "it a new id.<br>" + "Do yo agree?</html>", model.getMyPrimitive().getId() ), tr("Reset id to 0"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[1] ); return ret == JOptionPane.YES_OPTION; } } class KeepTheirVisibleStateAction extends AbstractAction implements Observer { public KeepTheirVisibleStateAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir")); putValue(Action.SHORT_DESCRIPTION, tr("Keep their visible state")); } public void actionPerformed(ActionEvent e) { if (confirmKeepTheir()){ model.decideVisibleStateConflict(MergeDecisionType.KEEP_THEIR); } } public void update(Observable o, Object arg) { setEnabled(model.hasVisibleStateConflict() && ! model.isDecidedVisibleState()); } protected boolean confirmKeepTheir() { String [] options = { tr("Yes, purge it"), tr("No, abort") }; int ret = JOptionPane.showOptionDialog( null, tr("<html>JOSM will have to remove your local primitive with id {0}<br>" + "from the dataset.<br>" + "Do you agree?</html>", model.getMyPrimitive().getId() ), tr("Remove from dataset"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[1] ); return ret == JOptionPane.YES_OPTION; } } class UndecideVisibleStateConflictAction extends AbstractAction implements Observer { public UndecideVisibleStateConflictAction() { putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide")); putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between visible state")); } public void actionPerformed(ActionEvent e) { model.decideVisibleStateConflict(MergeDecisionType.UNDECIDED); } public void update(Observable o, Object arg) { setEnabled(model.hasVisibleStateConflict() && model.isDecidedVisibleState()); } } public void deletePrimitive(boolean deleted) { if (deleted) { if (model.getMergedCoords() == null) { model.decideCoordsConflict(MergeDecisionType.KEEP_MINE); } } else { model.decideCoordsConflict(MergeDecisionType.UNDECIDED); } } public void populate(Conflict<? extends OsmPrimitive> conflict) { model.populate(conflict); } }