Ignore:
Timestamp:
2009-06-23T22:03:37+02:00 (15 years ago)
Author:
Gubaer
Message:

new: MultiFetchServerObjectReader using APIs Multi Fetch method
update: now uses Multi Fetch to check for deleted primitives on the server
update: now uses Multi Fetch to update the selected primitives with the state from the server
fixed: cleaned up merging in MergeVisitor
new: conflict resolution dialog; now resolves conflicts due to different visibilities
new: replacement for realEqual() on OsmPrimitive and derived classes; realEqual now @deprecated
fixed: cleaning up OsmReader
fixed: progress indication in OsmApi

Location:
trunk/src/org/openstreetmap/josm/gui
Files:
1 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/conflict/ConflictResolver.java

    r1654 r1690  
    2323import org.openstreetmap.josm.gui.conflict.nodes.NodeListMergeModel;
    2424import org.openstreetmap.josm.gui.conflict.nodes.NodeListMerger;
     25import org.openstreetmap.josm.gui.conflict.properties.OperationCancelledException;
    2526import org.openstreetmap.josm.gui.conflict.properties.PropertiesMergeModel;
    2627import org.openstreetmap.josm.gui.conflict.properties.PropertiesMerger;
     
    145146        this.their =  their;
    146147        propertiesMerger.getModel().populate(my, their);
     148        if (propertiesMerger.getModel().hasVisibleStateConflict()) {
     149            tabbedPane.setEnabledAt(1, false);
     150            tabbedPane.setEnabledAt(2, false);
     151            tabbedPane.setEnabledAt(3, false);
     152            return;
     153        }
    147154        tabbedPane.setEnabledAt(0, true);
    148155        tagMerger.getModel().populate(my, their);
     
    165172            tabbedPane.setEnabledAt(3, true);
    166173        }
     174
    167175    }
    168176
     
    173181     * @return the resolution command
    174182     */
    175     public Command buildResolveCommand() {
     183    public Command buildResolveCommand() throws OperationCancelledException {
    176184        ArrayList<Command> commands = new ArrayList<Command>();
    177         if (tagMerger.getModel().getNumResolvedConflicts() > 0) {
    178             commands.add(tagMerger.getModel().buildResolveCommand(my, their));
    179         }
    180         commands.addAll(propertiesMerger.getModel().buildResolveCommand(my, their));
    181         if (my instanceof Way && nodeListMerger.getModel().isFrozen()) {
    182             NodeListMergeModel model  =(NodeListMergeModel)nodeListMerger.getModel();
    183             commands.add(model.buildResolveCommand((Way)my, (Way)their));
    184         } else if (my instanceof Relation && relationMemberMerger.getModel().isFrozen()) {
    185             RelationMemberListMergeModel model  =(RelationMemberListMergeModel)relationMemberMerger.getModel();
    186             commands.add(model.buildResolveCommand((Relation)my, (Relation)their));
    187         }
    188         if (isResolvedCompletely()) {
    189             commands.add(
    190                     new VersionConflictResolveCommand(my, their)
    191             );
     185        if (propertiesMerger.getModel().hasVisibleStateConflict()) {
     186            if (propertiesMerger.getModel().isDecidedVisibleState()) {
     187                commands.addAll(propertiesMerger.getModel().buildResolveCommand(my, their));
     188            }
     189        } else {
     190            if (tagMerger.getModel().getNumResolvedConflicts() > 0) {
     191                commands.add(tagMerger.getModel().buildResolveCommand(my, their));
     192            }
     193            commands.addAll(propertiesMerger.getModel().buildResolveCommand(my, their));
     194            if (my instanceof Way && nodeListMerger.getModel().isFrozen()) {
     195                NodeListMergeModel model  =(NodeListMergeModel)nodeListMerger.getModel();
     196                commands.add(model.buildResolveCommand((Way)my, (Way)their));
     197            } else if (my instanceof Relation && relationMemberMerger.getModel().isFrozen()) {
     198                RelationMemberListMergeModel model  =(RelationMemberListMergeModel)relationMemberMerger.getModel();
     199                commands.add(model.buildResolveCommand((Relation)my, (Relation)their));
     200            }
     201            if (isResolvedCompletely()) {
     202                commands.add(
     203                        new VersionConflictResolveCommand(my, their)
     204                );
     205            }
    192206        }
    193207        return new SequenceCommand(tr("Conflict Resolution"), commands);
  • trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModel.java

    r1669 r1690  
    33
    44import static org.openstreetmap.josm.gui.conflict.MergeDecisionType.UNDECIDED;
     5import static org.openstreetmap.josm.tools.I18n.tr;
    56
    67import java.beans.PropertyChangeListener;
    78import java.beans.PropertyChangeSupport;
     9import java.io.IOException;
     10import java.net.HttpURLConnection;
    811import java.util.ArrayList;
     12import java.util.HashMap;
    913import java.util.List;
    1014import java.util.Observable;
    1115
     16import javax.swing.JOptionPane;
     17import javax.swing.text.html.HTML;
     18
     19import org.openstreetmap.josm.Main;
    1220import org.openstreetmap.josm.command.Command;
    1321import org.openstreetmap.josm.command.CoordinateConflictResolveCommand;
    1422import org.openstreetmap.josm.command.DeletedStateConflictResolveCommand;
     23import org.openstreetmap.josm.command.PurgePrimitivesCommand;
     24import org.openstreetmap.josm.command.UndeletePrimitivesCommand;
    1525import org.openstreetmap.josm.data.coor.LatLon;
     26import org.openstreetmap.josm.data.osm.DataSet;
    1627import org.openstreetmap.josm.data.osm.Node;
    1728import org.openstreetmap.josm.data.osm.OsmPrimitive;
     29import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     30import org.openstreetmap.josm.data.osm.Relation;
     31import org.openstreetmap.josm.data.osm.RelationMember;
     32import org.openstreetmap.josm.data.osm.Way;
     33import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    1834import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
     35import org.openstreetmap.josm.gui.conflict.properties.PropertiesMerger.KeepMyVisibleStateAction;
     36import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
     37import org.openstreetmap.josm.io.OsmApi;
     38import org.openstreetmap.josm.io.OsmApiException;
     39import org.openstreetmap.josm.io.OsmServerObjectReader;
     40import org.openstreetmap.josm.io.OsmTransferException;
     41import org.xml.sax.SAXException;
    1942
    2043/**
    2144 * This is the model for resolving conflicts in the properties of the
    2245 * {@see OsmPrimitive}s. In particular, it represents conflicts in the coordiates of {@see Node}s and
    23  * the deleted state of {@see OsmPrimitive}s.
     46 * the deleted or visible state of {@see OsmPrimitive}s.
    2447 *
    2548 * This model is an {@see Observable}. It notifies registered {@see Observer}s whenever the
     
    3154 * @see Node#getCoor()
    3255 * @see OsmPrimitive#deleted
     56 * @see OsmPrimitive#visible
    3357 *
    3458 */
     
    3761    static public final String RESOLVED_COMPLETELY_PROP = PropertiesMergeModel.class.getName() + ".resolvedCompletely";
    3862
     63    private OsmPrimitive my;
     64
    3965    private LatLon myCoords;
    4066    private LatLon theirCoords;
     
    4369    private boolean myDeletedState;
    4470    private boolean theirDeletedState;
     71    private boolean myVisibleState;
     72    private boolean theirVisibleState;
    4573    private MergeDecisionType deletedMergeDecision;
     74    private MergeDecisionType visibleMergeDecision;
    4675    private final PropertyChangeSupport support;
    4776    private boolean resolvedCompletely;
     
    91120
    92121    /**
     122     * replies true if there is a  conflict in the visible state and if this conflict is
     123     * resolved
     124     *
     125     * @return true if there is a conflict in the visible state and if this conflict is
     126     * resolved; false, otherwise
     127     */
     128    public boolean isDecidedVisibleState() {
     129        return ! visibleMergeDecision.equals(UNDECIDED);
     130    }
     131
     132    /**
    93133     * replies true if the current decision for the coordinate conflict is <code>decision</code>
    94134     *
     
    111151
    112152    /**
     153     * replies true if the current decision for the visible state conflict is <code>decision</code>
     154     *
     155     * @return true if the current decision for the visible state conflict is <code>decision</code>;
     156     *  false, otherwise
     157     */
     158    public boolean isVisibleStateDecision(MergeDecisionType decision) {
     159        return visibleMergeDecision.equals(decision);
     160    }
     161    /**
    113162     * populates the model with the differences between my and their version
    114163     *
     
    117166     */
    118167    public void populate(OsmPrimitive my, OsmPrimitive their) {
     168        this.my = my;
    119169        if (my instanceof Node) {
    120170            myCoords = ((Node)my).getCoor();
     
    128178        theirDeletedState = their.deleted;
    129179
     180        myVisibleState = my.visible;
     181        theirVisibleState = their.visible;
     182
    130183        coordMergeDecision = UNDECIDED;
    131184        deletedMergeDecision = UNDECIDED;
     185        visibleMergeDecision = UNDECIDED;
    132186        setChanged();
    133187        notifyObservers();
     
    209263    }
    210264
    211     public void decideDeletedStateConflict(MergeDecisionType decision) {
     265
     266    /**
     267     * replies my visible state,
     268     * @return my visible state
     269     */
     270    public Boolean getMyVisibleState() {
     271        return myVisibleState;
     272    }
     273
     274    /**
     275     * replies their visible state,
     276     * @return their visible state
     277     */
     278    public  Boolean getTheirVisibleState() {
     279        return theirVisibleState;
     280    }
     281
     282    /**
     283     * replies the merged visible state; null, if the merge decision is
     284     * {@see MergeDecisionType#UNDECIDED}.
     285     *
     286     * @return the merged visible state
     287     */
     288    public Boolean getMergedVisibleState() {
     289        switch(visibleMergeDecision) {
     290        case KEEP_MINE: return myVisibleState;
     291        case KEEP_THEIR: return theirVisibleState;
     292        case UNDECIDED: return null;
     293        }
     294        // should not happen
     295        return null;
     296    }
     297
     298    /**
     299     * decides the conflict between two deleted states
     300     * @param decision the decision (must not be null)
     301     *
     302     * @throws IllegalArgumentException thrown, if decision is null
     303     */
     304    public void decideDeletedStateConflict(MergeDecisionType decision) throws IllegalArgumentException{
     305        if (decision == null)
     306            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "decision"));
    212307        this.deletedMergeDecision = decision;
     308        setChanged();
     309        notifyObservers();
     310        fireCompletelyResolved();
     311    }
     312
     313    /**
     314     * decides the conflict between two visible states
     315     * @param decision the decision (must not be null)
     316     *
     317     * @throws IllegalArgumentException thrown, if decision is null
     318     */
     319    public void decideVisibleStateConflict(MergeDecisionType decision) throws IllegalArgumentException {
     320        if (decision == null)
     321            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "decision"));
     322        this.visibleMergeDecision = decision;
    213323        setChanged();
    214324        notifyObservers();
     
    242352
    243353    /**
     354     * replies true if my and their primitive have a conflict between
     355     * their visible states
     356     *
     357     * @return true if my and their primitive have a conflict between
     358     * their visible states
     359     */
     360    public boolean hasVisibleStateConflict() {
     361        return myVisibleState != theirVisibleState;
     362    }
     363
     364    /**
    244365     * replies true if all conflict in this model are resolved
    245366     *
     
    254375            ret = ret && ! deletedMergeDecision.equals(UNDECIDED);
    255376        }
     377        if (hasVisibleStateConflict()) {
     378            ret = ret && ! visibleMergeDecision.equals(UNDECIDED);
     379        }
    256380        return ret;
    257381    }
     
    264388     * @return the list of commands
    265389     */
    266     public List<Command> buildResolveCommand(OsmPrimitive my, OsmPrimitive their) {
     390    public List<Command> buildResolveCommand(OsmPrimitive my, OsmPrimitive their) throws OperationCancelledException{
    267391        ArrayList<Command> cmds = new ArrayList<Command>();
     392        if (hasVisibleStateConflict() && isDecidedVisibleState()) {
     393            if (isVisibleStateDecision(MergeDecisionType.KEEP_MINE)) {
     394                try {
     395                    UndeletePrimitivesCommand cmd = createUndeletePrimitiveCommand(my);
     396                    if (cmd == null)
     397                        throw new OperationCancelledException();
     398                    cmds.add(cmd);
     399                } catch(OsmTransferException e) {
     400                    handleExceptionWhileBuildingCommand(e);
     401                    throw new OperationCancelledException(e);
     402                }
     403            } else if (isVisibleStateDecision(MergeDecisionType.KEEP_THEIR)) {
     404                cmds.add(new PurgePrimitivesCommand(my));
     405            }
     406        }
    268407        if (hasCoordConflict() && isDecidedCoord()) {
    269408            cmds.add(new CoordinateConflictResolveCommand((Node)my, (Node)their, coordMergeDecision));
     
    274413        return cmds;
    275414    }
     415
     416    public OsmPrimitive getMyPrimitive() {
     417        return my;
     418    }
     419
     420    /**
     421     *
     422     * @param id
     423     */
     424    protected void handleExceptionWhileBuildingCommand(Exception e) {
     425        e.printStackTrace();
     426        String msg = e.getMessage() != null ? e.getMessage() : e.toString();
     427        msg = msg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
     428        JOptionPane.showMessageDialog(
     429                Main.parent,
     430                tr("<html>An error occurred while communicating with the server<br>"
     431                        + "Details: {0}</html>",
     432                        msg
     433                ),
     434                tr("Communication with server failed"),
     435                JOptionPane.ERROR_MESSAGE
     436        );
     437    }
     438
     439    /**
     440     * User has decided to keep his local version of a primitive which had been deleted
     441     * on the server
     442     *
     443     * @param id the primitive id
     444     */
     445    protected UndeletePrimitivesCommand createUndeletePrimitiveCommand(OsmPrimitive my) throws OsmTransferException {
     446        if (my instanceof Node)
     447            return createUndeleteNodeCommand((Node)my);
     448        else if (my instanceof Way)
     449            return createUndeleteWayCommand((Way)my);
     450        else if (my instanceof Relation)
     451            return createUndeleteRelationCommand((Relation)my);
     452        return null;
     453    }
     454    /**
     455     * Undelete a node which is already deleted on the server. The API
     456     * doesn't offer a call for "undeleting" a node. We therefore create
     457     * a clone of the node which we flag as new. On the next upload the
     458     * server will assign the node a new id.
     459     *
     460     * @param node the node to undelete
     461     */
     462    protected UndeletePrimitivesCommand  createUndeleteNodeCommand(Node node) {
     463        return new UndeletePrimitivesCommand(node);
     464    }
     465
     466    /**
     467     * displays a confirmation message. The user has to confirm that additional dependent
     468     * nodes should be undeleted too.
     469     *
     470     * @param way  the way
     471     * @param dependent a list of dependent nodes which have to be undelete too
     472     * @return true, if the user confirms; false, otherwise
     473     */
     474    protected boolean confirmUndeleteDependentPrimitives(Way way, ArrayList<OsmPrimitive> dependent) {
     475        String [] options = {
     476                tr("Yes, undelete them too"),
     477                tr("No, cancel operation")
     478        };
     479        int ret = JOptionPane.showOptionDialog(
     480                Main.parent,
     481                tr("<html>There are {0} additional nodes used by way {1}<br>"
     482                        + "which are deleted on the server.<br>"
     483                        + "<br>"
     484                        + "Do you want to undelete these nodes too?</html>",
     485                        Long.toString(dependent.size()), Long.toString(way.id)),
     486                        tr("Undelete additional nodes?"),
     487                        JOptionPane.YES_NO_OPTION,
     488                        JOptionPane.QUESTION_MESSAGE,
     489                        null,
     490                        options,
     491                        options[0]
     492        );
     493
     494        switch(ret) {
     495        case JOptionPane.CLOSED_OPTION: return false;
     496        case JOptionPane.YES_OPTION: return true;
     497        case JOptionPane.NO_OPTION: return false;
     498        }
     499        return false;
     500
     501    }
     502
     503    protected boolean confirmUndeleteDependentPrimitives(Relation r, ArrayList<OsmPrimitive> dependent) {
     504        String [] options = {
     505                tr("Yes, undelete them too"),
     506                tr("No, cancel operation")
     507        };
     508        int ret = JOptionPane.showOptionDialog(
     509                Main.parent,
     510                tr("<html>There are {0} additional primitives referred to by relation {1}<br>"
     511                        + "which are deleted on the server.<br>"
     512                        + "<br>"
     513                        + "Do you want to undelete them too?</html>",
     514                        Long.toString(dependent.size()), Long.toString(r.id)),
     515                        tr("Undelete dependent primitives?"),
     516                        JOptionPane.YES_NO_OPTION,
     517                        JOptionPane.QUESTION_MESSAGE,
     518                        null,
     519                        options,
     520                        options[0]
     521        );
     522
     523        switch(ret) {
     524        case JOptionPane.CLOSED_OPTION: return false;
     525        case JOptionPane.YES_OPTION: return true;
     526        case JOptionPane.NO_OPTION: return false;
     527        }
     528        return false;
     529
     530    }
     531
     532    /**
     533     * Creates the undelete command for a way which is already deleted on the server.
     534     *
     535     * This method also checks whether there are additional nodes referred to by
     536     * this way which are deleted on the server too.
     537     *
     538     * @param way the way to undelete
     539     * @return the undelete command
     540     * @see #createUndeleteNodeCommand(Node)
     541     */
     542    protected UndeletePrimitivesCommand createUndeleteWayCommand(final Way way) throws OsmTransferException {
     543
     544        HashMap<Long,OsmPrimitive> candidates = new HashMap<Long,OsmPrimitive>();
     545        for (Node n : way.nodes) {
     546            if (n.id > 0 && ! candidates.values().contains(n)) {
     547                candidates.put(n.id, n);
     548            }
     549        }
     550        MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
     551        reader.append(candidates.values());
     552        DataSet ds = reader.parseOsm();
     553
     554        ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>();
     555        for (OsmPrimitive their : ds.allPrimitives()) {
     556            if (candidates.keySet().contains(their.id) && ! their.visible) {
     557                toDelete.add(candidates.get(their.id));
     558            }
     559        }
     560        if (!toDelete.isEmpty()) {
     561            if (! confirmUndeleteDependentPrimitives(way, toDelete))
     562                // FIXME: throw exception ?
     563                return null;
     564        }
     565        toDelete.add(way);
     566        return new UndeletePrimitivesCommand(toDelete);
     567    }
     568
     569    /**
     570     * Creates an undelete command for a relation which is already deleted on the server.
     571     *
     572     * This method  checks whether there are additional primitives referred to by
     573     * this relation which are already deleted on the server.
     574     *
     575     * @param r the relation
     576     * @return the undelete command
     577     * @see #createUndeleteNodeCommand(Node)
     578     */
     579    protected UndeletePrimitivesCommand createUndeleteRelationCommand(final Relation r) throws OsmTransferException {
     580
     581        HashMap<Long,OsmPrimitive> candidates = new HashMap<Long, OsmPrimitive>();
     582        for (RelationMember m : r.members) {
     583            if (m.member.id > 0 && !candidates.values().contains(m.member)) {
     584                candidates.put(m.member.id,m.member);
     585            }
     586        }
     587
     588        MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
     589        reader.append(candidates.values());
     590        DataSet ds = reader.parseOsm();
     591
     592        ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>();
     593        for (OsmPrimitive their : ds.allPrimitives()) {
     594            if (candidates.keySet().contains(their.id) && ! their.visible) {
     595                toDelete.add(candidates.get(their.id));
     596            }
     597        }
     598        if (!toDelete.isEmpty()) {
     599            if (! confirmUndeleteDependentPrimitives(r, toDelete))
     600                // FIXME: throw exception ?
     601                return null;
     602        }
     603        toDelete.add(r);
     604        return new UndeletePrimitivesCommand(toDelete);
     605    }
     606
    276607}
  • trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMerger.java

    r1676 r1690  
    1818import javax.swing.JButton;
    1919import javax.swing.JLabel;
     20import javax.swing.JOptionPane;
    2021import javax.swing.JPanel;
    21 
     22import javax.swing.SwingUtilities;
     23
     24import org.openstreetmap.josm.Main;
    2225import org.openstreetmap.josm.data.coor.LatLon;
     26import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    2327import org.openstreetmap.josm.gui.conflict.MergeDecisionType;
    2428import org.openstreetmap.josm.tools.ImageProvider;
     
    4852    private JLabel lblTheirDeletedState;
    4953
     54    private JLabel lblMyVisibleState;
     55    private JLabel lblMergedVisibleState;
     56    private JLabel lblTheirVisibleState;
     57
    5058    private final PropertiesMergeModel model;
    5159
     
    5967    }
    6068
    61     protected void build() {
    62         setLayout(new GridBagLayout());
     69    protected void buildHeaderRow() {
    6370        GridBagConstraints gc = new GridBagConstraints();
    6471
    65         // ------------------
    6672        gc.gridx = 1;
    6773        gc.gridy = 0;
     
    8894        lblTheirVersion.setToolTipText(tr("Properties in their dataset, i.e. the server dataset"));
    8995        add(lblTheirVersion, gc);
    90 
    91         // --------------------------------
     96    }
     97
     98    protected void buildCoordinateConflictRows() {
     99        GridBagConstraints gc = new GridBagConstraints();
     100
    92101        gc.gridx = 0;
    93102        gc.gridy = 1;
     
    161170        JButton btnUndecideCoordinates = new JButton(actUndecideCoordinates);
    162171        add(btnUndecideCoordinates, gc);
    163         // ---------------------------------------------------
     172    }
     173
     174    protected void buildDeletedStateConflictRows() {
     175        GridBagConstraints gc = new GridBagConstraints();
    164176
    165177        gc.gridx = 0;
     
    191203        model.addObserver(actKeepMyDeletedState);
    192204        JButton btnKeepMyDeletedState = new JButton(actKeepMyDeletedState);
    193         btnKeepMyCoordinates.setName("button.keepmydeletedstate");
     205        btnKeepMyDeletedState.setName("button.keepmydeletedstate");
    194206        add(btnKeepMyDeletedState, gc);
    195207
     
    212224        model.addObserver(actKeepTheirDeletedState);
    213225        JButton btnKeepTheirDeletedState = new JButton(actKeepTheirDeletedState);
    214         btnKeepMyCoordinates.setName("button.keeptheirdeletedstate");
     226        btnKeepTheirDeletedState.setName("button.keeptheirdeletedstate");
    215227        add(btnKeepTheirDeletedState, gc);
    216228
     
    233245        model.addObserver(actUndecideDeletedState);
    234246        JButton btnUndecideDeletedState = new JButton(actUndecideDeletedState);
    235         btnKeepMyCoordinates.setName("button.undecidedeletedstate");
     247        btnUndecideDeletedState.setName("button.undecidedeletedstate");
    236248        add(btnUndecideDeletedState, gc);
    237 
     249    }
     250
     251    protected void buildVisibleStateRows() {
     252        GridBagConstraints gc = new GridBagConstraints();
     253
     254        gc.gridx = 0;
     255        gc.gridy = 5;
     256        gc.gridwidth = 1;
     257        gc.gridheight = 1;
     258        gc.fill = GridBagConstraints.BOTH;
     259        gc.anchor = GridBagConstraints.LINE_START;
     260        gc.weightx = 0.0;
     261        gc.weighty = 0.0;
     262        gc.insets = new Insets(0,5,0,5);
     263        add(new JLabel(tr("Visible State:")), gc);
     264
     265        gc.gridx = 1;
     266        gc.gridy = 5;
     267        gc.fill = GridBagConstraints.BOTH;
     268        gc.anchor = GridBagConstraints.CENTER;
     269        gc.weightx = 0.33;
     270        gc.weighty = 0.0;
     271        add(lblMyVisibleState = buildValueLabel("label.myvisiblestate"), gc);
     272
     273        gc.gridx = 2;
     274        gc.gridy = 5;
     275        gc.fill = GridBagConstraints.NONE;
     276        gc.anchor = GridBagConstraints.CENTER;
     277        gc.weightx = 0.0;
     278        gc.weighty = 0.0;
     279        KeepMyVisibleStateAction actKeepMyVisibleState = new KeepMyVisibleStateAction();
     280        model.addObserver(actKeepMyVisibleState);
     281        JButton btnKeepMyVisibleState = new JButton(actKeepMyVisibleState);
     282        btnKeepMyVisibleState.setName("button.keepmyvisiblestate");
     283        add(btnKeepMyVisibleState, gc);
     284
     285        gc.gridx = 3;
     286        gc.gridy = 5;
     287        gc.fill = GridBagConstraints.BOTH;
     288        gc.anchor = GridBagConstraints.CENTER;
     289        gc.weightx = 0.33;
     290        gc.weighty = 0.0;
     291        add(lblMergedVisibleState = buildValueLabel("label.mergedvisiblestate"), gc);
     292
     293        gc.gridx = 4;
     294        gc.gridy = 5;
     295        gc.fill = GridBagConstraints.NONE;
     296        gc.anchor = GridBagConstraints.CENTER;
     297        gc.weightx = 0.0;
     298        gc.weighty = 0.0;
     299        KeepTheirVisibleStateAction actKeepTheirVisibleState = new KeepTheirVisibleStateAction();
     300        model.addObserver(actKeepTheirVisibleState);
     301        JButton btnKeepTheirVisibleState = new JButton(actKeepTheirVisibleState);
     302        btnKeepTheirVisibleState.setName("button.keeptheirvisiblestate");
     303        add(btnKeepTheirVisibleState, gc);
     304
     305        gc.gridx = 5;
     306        gc.gridy = 5;
     307        gc.fill = GridBagConstraints.BOTH;
     308        gc.anchor = GridBagConstraints.CENTER;
     309        gc.weightx = 0.33;
     310        gc.weighty = 0.0;
     311        add(lblTheirVisibleState = buildValueLabel("label.theirvisiblestate"), gc);
     312
     313        // ---------------------------------------------------
     314        gc.gridx = 3;
     315        gc.gridy = 6;
     316        gc.fill = GridBagConstraints.NONE;
     317        gc.anchor = GridBagConstraints.CENTER;
     318        gc.weightx = 0.0;
     319        gc.weighty = 0.0;
     320        UndecideVisibleStateConflictAction actUndecideVisibleState = new UndecideVisibleStateConflictAction();
     321        model.addObserver(actUndecideVisibleState);
     322        JButton btnUndecideVisibleState = new JButton(actUndecideVisibleState);
     323        btnUndecideVisibleState.setName("button.undecidevisiblestate");
     324        add(btnUndecideVisibleState, gc);
     325    }
     326
     327    protected void build() {
     328        setLayout(new GridBagLayout());
     329        buildHeaderRow();
     330        buildCoordinateConflictRows();
     331        buildDeletedStateConflictRows();
     332        buildVisibleStateRows();
    238333    }
    239334
     
    265360    }
    266361
    267     protected void updateCoordiates() {
     362    public String visibleStateToString(Boolean visible) {
     363        if (visible == null)
     364            return tr("(none)");
     365        if (visible)
     366            return tr("visible (on the server)");
     367        else
     368            return tr("not visible (on the server)");
     369    }
     370
     371    public String visibleStateToStringMerged(Boolean visible) {
     372        if (visible == null)
     373            return tr("(none)");
     374        if (visible)
     375            return tr("Keep a clone of the local version");
     376        else
     377            return tr("Physically delete from local dataset");
     378    }
     379
     380    protected void updateCoordinates() {
    268381        lblMyCoordinates.setText(coordToString(model.getMyCoords()));
    269382        lblMergedCoordinates.setText(coordToString(model.getMergedCoords()));
     
    320433    }
    321434
     435    protected void updateVisibleState() {
     436        lblMyVisibleState.setText(visibleStateToString(model.getMyVisibleState()));
     437        lblMergedVisibleState.setText(visibleStateToStringMerged(model.getMergedVisibleState()));
     438        lblTheirVisibleState.setText(visibleStateToString(model.getTheirVisibleState()));
     439
     440        if (! model.hasVisibleStateConflict()) {
     441            lblMyVisibleState.setBackground(BGCOLOR_NO_CONFLICT);
     442            lblMergedVisibleState.setBackground(BGCOLOR_NO_CONFLICT);
     443            lblTheirVisibleState.setBackground(BGCOLOR_NO_CONFLICT);
     444        } else {
     445            if (!model.isDecidedVisibleState()) {
     446                lblMyVisibleState.setBackground(BGCOLOR_UNDECIDED);
     447                lblMergedVisibleState.setBackground(BGCOLOR_NO_CONFLICT);
     448                lblTheirVisibleState.setBackground(BGCOLOR_UNDECIDED);
     449            } else {
     450                lblMyVisibleState.setBackground(
     451                        model.isVisibleStateDecision(MergeDecisionType.KEEP_MINE)
     452                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT
     453                );
     454                lblMergedVisibleState.setBackground(BGCOLOR_DECIDED);
     455                lblTheirVisibleState.setBackground(
     456                        model.isVisibleStateDecision(MergeDecisionType.KEEP_THEIR)
     457                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT
     458                );
     459            }
     460        }
     461    }
     462
    322463    public void update(Observable o, Object arg) {
    323         updateCoordiates();
     464        updateCoordinates();
    324465        updateDeletedState();
     466        updateVisibleState();
    325467    }
    326468
     
    418560        }
    419561    }
     562
     563    class KeepMyVisibleStateAction extends AbstractAction implements Observer {
     564        public KeepMyVisibleStateAction() {
     565            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine"));
     566            putValue(Action.SHORT_DESCRIPTION, tr("Keep my visible state"));
     567        }
     568
     569        public void actionPerformed(ActionEvent e) {
     570            if (confirmKeepMine()) {
     571                model.decideVisibleStateConflict(MergeDecisionType.KEEP_MINE);
     572            }
     573        }
     574
     575        public void update(Observable o, Object arg) {
     576            setEnabled(model.hasVisibleStateConflict() && ! model.isDecidedVisibleState());
     577        }
     578
     579        protected boolean confirmKeepMine() {
     580            String [] options = {
     581                    tr("Yes, reset the id"),
     582                    tr("No, abort")
     583            };
     584            int ret = JOptionPane.showOptionDialog(
     585                    null,
     586                    tr("<html>To keep your local version, JOSM<br>"
     587                            + "has to reset the id of {0} {1} to 0.<br>"
     588                            + "On the next upload the server will assign<br>"
     589                            + "it a new id.<br>"
     590                            + "Do yo agree?</html>",
     591                            OsmPrimitiveType.from(model.getMyPrimitive()).getLocalizedDisplayNamePlural(),
     592                            model.getMyPrimitive().id
     593                    ),
     594                    tr("Reset id to 0"),
     595                    JOptionPane.YES_NO_OPTION,
     596                    JOptionPane.QUESTION_MESSAGE,
     597                    null,
     598                    options,
     599                    options[1]
     600            );
     601            return ret == JOptionPane.YES_OPTION;
     602        }
     603    }
     604
     605    class KeepTheirVisibleStateAction extends AbstractAction implements Observer {
     606        public KeepTheirVisibleStateAction() {
     607            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir"));
     608            putValue(Action.SHORT_DESCRIPTION, tr("Keep their visible state"));
     609        }
     610
     611        public void actionPerformed(ActionEvent e) {
     612            if (confirmKeepTheir()){
     613                model.decideVisibleStateConflict(MergeDecisionType.KEEP_THEIR);
     614            }
     615        }
     616
     617        public void update(Observable o, Object arg) {
     618            setEnabled(model.hasVisibleStateConflict() && ! model.isDecidedVisibleState());
     619        }
     620
     621        protected boolean confirmKeepTheir() {
     622            String [] options = {
     623                    tr("Yes, purge it"),
     624                    tr("No, abort")
     625            };
     626            int ret = JOptionPane.showOptionDialog(
     627                    null,
     628                    tr("<html>JOSM will have to remove your local primitive with id {0}<br>"
     629                            + "from the dataset.<br>"
     630                            + "Do you agree?</html>",
     631                            model.getMyPrimitive().id
     632                    ),
     633                    tr("Remove from dataset"),
     634                    JOptionPane.YES_NO_OPTION,
     635                    JOptionPane.QUESTION_MESSAGE,
     636                    null,
     637                    options,
     638                    options[1]
     639            );
     640            return ret == JOptionPane.YES_OPTION;
     641        }
     642    }
     643
     644    class UndecideVisibleStateConflictAction extends AbstractAction implements Observer {
     645        public UndecideVisibleStateConflictAction() {
     646            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide"));
     647            putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between visible state"));
     648        }
     649
     650        public void actionPerformed(ActionEvent e) {
     651            model.decideVisibleStateConflict(MergeDecisionType.UNDECIDED);
     652        }
     653
     654        public void update(Observable o, Object arg) {
     655            setEnabled(model.hasVisibleStateConflict() && model.isDecidedVisibleState());
     656        }
     657    }
    420658}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictResolutionDialog.java

    r1677 r1690  
    2323import org.openstreetmap.josm.command.Command;
    2424import org.openstreetmap.josm.gui.conflict.ConflictResolver;
     25import org.openstreetmap.josm.gui.conflict.properties.OperationCancelledException;
    2526import org.openstreetmap.josm.tools.ImageProvider;
    2627
     
    190191                    return;
    191192            }
    192             Command cmd = resolver.buildResolveCommand();
    193             Main.main.undoRedo.add(cmd);
     193            try {
     194                Command cmd = resolver.buildResolveCommand();
     195                Main.main.undoRedo.add(cmd);
     196            } catch(OperationCancelledException e) {
     197                // do nothing. Exception already reported
     198            }
    194199            setVisible(false);
    195200        }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

    r1670 r1690  
    527527                    final MergeVisitor visitor = new MergeVisitor(Main.main
    528528                            .editLayer().data, dataSet);
    529                     for (final OsmPrimitive osm : dataSet.allPrimitives()) {
    530                         osm.visit(visitor);
    531                     }
    532                     visitor.fixReferences();
     529                    visitor.merge();
    533530
    534531                    // copy the merged layer's data source info
     
    538535                    Main.main.editLayer().fireDataChange();
    539536
    540                     if (visitor.conflicts.isEmpty())
     537                    if (visitor.getConflicts().isEmpty())
    541538                        return;
    542539                    final ConflictDialog dlg = Main.map.conflictDialog;
    543                     dlg.add(visitor.conflicts);
     540                    dlg.add(visitor.getConflicts());
    544541                    JOptionPane.showMessageDialog(Main.parent,
    545542                            tr("There were conflicts during import."));
  • trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    r1677 r1690  
    245245    public void mergeFrom(final DataSet from) {
    246246        final MergeVisitor visitor = new MergeVisitor(data,from);
    247         for (final OsmPrimitive osm : from.allPrimitives()) {
    248             osm.visit(visitor);
    249         }
    250         visitor.fixReferences();
     247        visitor.merge();
    251248
    252249        Area a = data.getDataSourceArea();
     
    274271        Main.map.mapView.repaint();
    275272
    276         if (visitor.conflicts.isEmpty())
     273        if (visitor.getConflicts().isEmpty())
    277274            return;
    278275        final ConflictDialog dlg = Main.map.conflictDialog;
    279         dlg.add(visitor.conflicts);
    280         JOptionPane.showMessageDialog(Main.parent,tr("There were {0} conflicts during import.", visitor.conflicts.size()));
     276        dlg.add(visitor.getConflicts());
     277        JOptionPane.showMessageDialog(Main.parent,tr("There were {0} conflicts during import.", visitor.getConflicts().size()));
    281278        if (!dlg.isVisible()) {
    282279            dlg.action.actionPerformed(new ActionEvent(this, 0, ""));
Note: See TracChangeset for help on using the changeset viewer.