Changeset 2220 in josm for trunk


Ignore:
Timestamp:
2009-09-30T20:00:57+02:00 (15 years ago)
Author:
Gubaer
Message:

fixed #3556: False positive conflicts when merging nodes
fixed #3460: show only conflicting tags at conflict resolution dialog
fixed #3103: resolve conflicts after combining just one conflict

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

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r2210 r2220  
    22package org.openstreetmap.josm.actions;
    33
     4import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.combineTigerTags;
     5import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing;
     6import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing;
    47import static org.openstreetmap.josm.tools.I18n.tr;
    58
     
    3033import org.openstreetmap.josm.data.osm.Relation;
    3134import org.openstreetmap.josm.data.osm.RelationMember;
    32 import org.openstreetmap.josm.data.osm.Tag;
    3335import org.openstreetmap.josm.data.osm.TagCollection;
    34 import org.openstreetmap.josm.data.osm.TigerUtils;
    3536import org.openstreetmap.josm.data.osm.Way;
    3637import org.openstreetmap.josm.gui.ExtendedDialog;
     
    3839import org.openstreetmap.josm.tools.Pair;
    3940import org.openstreetmap.josm.tools.Shortcut;
    40 
    4141/**
    4242 * Combines multiple ways into one.
     
    8888    }
    8989
    90     protected static void completeTagCollectionWithMissingTags(TagCollection tc, Collection<? extends OsmPrimitive> merged) {
    91         for (String key: tc.getKeys()) {
    92             // make sure the empty value is in the tag set if a tag is not present
    93             // on all merged nodes
    94             //
    95             for (OsmPrimitive p: merged) {
    96                 if (p.get(key) == null) {
    97                     tc.add(new Tag(key)); // add a tag with key and empty value
    98                 }
    99             }
    100         }
    101         // remove irrelevant tags
    102         //
    103         tc.removeByKey("created_by");
    104     }
    105 
    106     /**
    107      * Combines tags from TIGER data
    108      *
    109      * @param tc the tag collection
    110      */
    111     protected static void combineTigerTags(TagCollection tc) {
    112         for (String key: tc.getKeys()) {
    113             if (TigerUtils.isTigerTag(key)) {
    114                 tc.setUniqueForKey(key, TigerUtils.combineTags(key, tc.getValues(key)));
    115             }
    116         }
    117     }
    118 
    119     protected static void completeTagCollectionForEditing(TagCollection tc) {
    120         for (String key: tc.getKeys()) {
    121             // make sure the empty value is in the tag set such that we can delete the tag
    122             // in the conflict dialog if necessary
    123             //
    124             tc.add(new Tag(key,""));
    125         }
    126     }
    12790
    12891    public void combineWays(Collection<Way> ways) {
     
    170133        TagCollection completeWayTags = new TagCollection(wayTags);
    171134        combineTigerTags(completeWayTags);
    172         completeTagCollectionWithMissingTags(completeWayTags, ways);
     135        normalizeTagCollectionBeforeEditing(completeWayTags, ways);
    173136        TagCollection tagsToEdit = new TagCollection(completeWayTags);
    174137        completeTagCollectionForEditing(tagsToEdit);
    175138
    176139        CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
    177         dialog.getTagConflictResolverModel().populate(tagsToEdit);
     140        dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues());
    178141        dialog.setTargetPrimitive(targetWay);
    179142        dialog.getRelationMemberConflictResolverModel().populate(
  • trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java

    r2202 r2220  
    22package org.openstreetmap.josm.actions;
    33
     4import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.combineTigerTags;
     5import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing;
     6import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing;
    47import static org.openstreetmap.josm.tools.I18n.tr;
    58
     
    2326import org.openstreetmap.josm.data.osm.Node;
    2427import org.openstreetmap.josm.data.osm.OsmPrimitive;
    25 import org.openstreetmap.josm.data.osm.Tag;
    2628import org.openstreetmap.josm.data.osm.TagCollection;
    27 import org.openstreetmap.josm.data.osm.TigerUtils;
    2829import org.openstreetmap.josm.data.osm.Way;
    2930import org.openstreetmap.josm.data.osm.BackreferencedDataSet.RelationToChildReference;
     
    5960        }
    6061
    61 
    6262        Node targetNode = selectTargetNode(selectedNodes);
    6363        Command cmd = mergeNodes(Main.main.getEditLayer(), selectedNodes, targetNode);
     
    6565            Main.main.undoRedo.add(cmd);
    6666            Main.main.getEditLayer().data.setSelected(targetNode);
    67         }
    68     }
    69 
    70     protected static void completeTagCollectionWithMissingTags(TagCollection tc, Collection<Node> mergedNodes) {
    71         for (String key: tc.getKeys()) {
    72             // make sure the empty value is in the tag set if a tag is not present
    73             // on all merged nodes
    74             //
    75             for (Node n: mergedNodes) {
    76                 if (n.get(key) == null) {
    77                     tc.add(new Tag(key)); // add a tag with key and empty value
    78                 }
    79             }
    80         }
    81         // remove irrelevant tags
    82         //
    83         tc.removeByKey("created_by");
    84     }
    85 
    86     protected static void completeTagCollectionForEditing(TagCollection tc) {
    87         for (String key: tc.getKeys()) {
    88             // make sure the empty value is in the tag set such that we can delete the tag
    89             // in the conflict dialog if necessary
    90             //
    91             tc.add(new Tag(key,""));
    92         }
    93     }
    94 
    95     /**
    96      * Combines tags from TIGER data
    97      *
    98      * @param tc the tag collection
    99      */
    100     protected static void combineTigerTags(TagCollection tc) {
    101         for (String key: tc.getKeys()) {
    102             if (TigerUtils.isTigerTag(key)) {
    103                 tc.setUniqueForKey(key, TigerUtils.combineTags(key, tc.getValues(key)));
    104             }
    10567        }
    10668    }
     
    220182
    221183    /**
    222      * Merges the nodes in <code>node</code> onto one of the nodes. Uses the dataset
     184     * Merges the nodes in <code>nodes</code> onto one of the nodes. Uses the dataset
    223185     * managed by <code>layer</code> as reference. <code>backreferences</code> is a precomputed
    224186     * collection of all parent/child references in the dataset.
     
    250212        TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes);
    251213        combineTigerTags(nodeTags);
    252         completeTagCollectionWithMissingTags(nodeTags, nodes);
     214        normalizeTagCollectionBeforeEditing(nodeTags, nodes);
    253215        TagCollection nodeTagsToEdit = new TagCollection(nodeTags);
    254216        completeTagCollectionForEditing(nodeTagsToEdit);
     
    257219        //
    258220        CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
    259         dialog.getTagConflictResolverModel().populate(nodeTagsToEdit);
     221        dialog.getTagConflictResolverModel().populate(nodeTagsToEdit, nodeTags.getKeysWithMultipleValues());
    260222        dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences);
    261223        dialog.setTargetPrimitive(targetNode);
    262224        dialog.prepareDefaultDecisions();
     225        // conflict resolution is necessary if there are conflicts in the merged tags
     226        // or if at least one of the merged nodes is referred to by a relation
     227        //
    263228        if (! nodeTags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) {
    264229            dialog.setVisible(true);
     
    283248        if (wayFixCommands == null)
    284249            return null;
    285         else {
    286             cmds.addAll(wayFixCommands);
    287         }
     250        cmds.addAll(wayFixCommands);
    288251
    289252        // build the commands
     
    299262        return cmd;
    300263    }
    301 
    302264
    303265    /**
  • trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java

    r2070 r2220  
    3838
    3939    static private List<Class<? extends OsmPrimitive>> osmPrimitiveClasses;
    40     {
     40    static {
    4141        osmPrimitiveClasses = new ArrayList<Class<? extends OsmPrimitive>>();
    4242        osmPrimitiveClasses.add(Node.class);
     
    6060
    6161    /**
    62      * Replies the subset  of {@see OsmPrimitive}s of <code>type</code> from <code>superSet</code>.
    63      *
    64      * @param <T>
    65      * @param superSet  the super set of primitives
    66      * @param type  the type
    67      * @return
    68      */
    69     protected <T extends OsmPrimitive> Collection<? extends OsmPrimitive> getSubcollectionByType(Collection<? extends OsmPrimitive> superSet, Class<T> type) {
    70         Collection<OsmPrimitive> ret = new ArrayList<OsmPrimitive>();
    71         for (OsmPrimitive p : superSet) {
    72             if (type.isInstance(p)) {
    73                 ret.add(p);
    74             }
    75         }
    76         return ret;
    77     }
    78 
    79     /**
    8062     * Replies all primitives of type <code>type</code> in the current selection.
    8163     *
     
    8567     */
    8668    protected <T extends OsmPrimitive> Collection<? extends OsmPrimitive> getSourcePrimitivesByType(Class<T> type) {
    87         return getSubcollectionByType(Main.pasteBuffer.getSelected(), type);
     69        return OsmPrimitive.getFilteredList(Main.pasteBuffer.getSelected(), type);
    8870    }
    8971
     
    145127        HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
    146128        for (Class<? extends OsmPrimitive> type: osmPrimitiveClasses) {
    147             int count = getSubcollectionByType(getEditLayer().data.getSelected(), type).size();
     129            int count = OsmPrimitive.getFilteredList(getEditLayer().data.getSelected(), type).size();
    148130            if (count > 0) {
    149131                ret.put(OsmPrimitiveType.from(type), count);
     
    173155            // no tags found to paste. Abort.
    174156            return;
    175 
    176157
    177158        if (!tc.isApplicableToPrimitive()) {
     
    202183     * <code>selection</code>
    203184     */
    204     protected <T extends OsmPrimitive> boolean hasTargetPrimitives(Collection<? extends OsmPrimitive> selection, Class<T> type) {
    205         return !getSubcollectionByType(selection, type).isEmpty();
     185    protected <T extends OsmPrimitive> boolean hasTargetPrimitives(Collection<OsmPrimitive> selection, Class<T> type) {
     186        return !OsmPrimitive.getFilteredList(selection, type).isEmpty();
    206187    }
    207188
     
    212193     * @return true if this a heterogeneous source can be pasted without conflicts to targets
    213194     */
    214     protected boolean canPasteFromHeterogeneousSourceWithoutConflict(Collection<? extends OsmPrimitive> targets) {
     195    protected boolean canPasteFromHeterogeneousSourceWithoutConflict(Collection<OsmPrimitive> targets) {
    215196        if (hasTargetPrimitives(targets, Node.class)) {
    216197            TagCollection tc = TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(Node.class));
     
    237218     * @param targets the collection of target primitives
    238219     */
    239     protected void pasteFromHeterogeneousSource(Collection<? extends OsmPrimitive> targets) {
     220    protected void pasteFromHeterogeneousSource(Collection<OsmPrimitive> targets) {
    240221        if (canPasteFromHeterogeneousSourceWithoutConflict(targets)) {
    241222            if (hasSourceTagsByType(Node.class) && hasTargetPrimitives(targets, Node.class)) {
     
    264245                return;
    265246            if (hasSourceTagsByType(Node.class) && hasTargetPrimitives(targets, Node.class)) {
    266                 Command cmd = buildChangeCommand(getSubcollectionByType(targets, Node.class), dialog.getResolution(OsmPrimitiveType.NODE));
     247                Command cmd = buildChangeCommand(OsmPrimitive.getFilteredList(targets, Node.class), dialog.getResolution(OsmPrimitiveType.NODE));
    267248                Main.main.undoRedo.add(cmd);
    268249            }
    269250            if (hasSourceTagsByType(Way.class) && hasTargetPrimitives(targets, Way.class)) {
    270                 Command cmd = buildChangeCommand(getSubcollectionByType(targets, Way.class), dialog.getResolution(OsmPrimitiveType.WAY));
     251                Command cmd = buildChangeCommand(OsmPrimitive.getFilteredList(targets, Way.class), dialog.getResolution(OsmPrimitiveType.WAY));
    271252                Main.main.undoRedo.add(cmd);
    272253            }
    273254            if (hasSourceTagsByType(Relation.class) && hasTargetPrimitives(targets, Relation.class)) {
    274                 Command cmd = buildChangeCommand(getSubcollectionByType(targets, Relation.class), dialog.getResolution(OsmPrimitiveType.RELATION));
     255                Command cmd = buildChangeCommand(OsmPrimitive.getFilteredList(targets, Relation.class), dialog.getResolution(OsmPrimitiveType.RELATION));
    275256                Main.main.undoRedo.add(cmd);
    276257            }
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java

    r2157 r2220  
    88import java.awt.FlowLayout;
    99import java.awt.event.ActionEvent;
     10import java.awt.event.HierarchyBoundsListener;
     11import java.awt.event.HierarchyEvent;
    1012import java.awt.event.WindowAdapter;
    1113import java.awt.event.WindowEvent;
     
    3638
    3739/**
    38  * This dialog helps to resolve conflicts occurring when ways are combined ore
    39  * primitives are merged.
     40 * This dialog helps to resolve conflicts occurring when ways are combined or
     41 * nodes are merged.
    4042 *
    4143 * There is a singleton instance of this dialog which can be retrieved using
     
    5759 * merged to, see {@see #setTargetPrimitive(OsmPrimitive)}.
    5860 *
    59  * After the dialog closed use {@see #isCancelled()} to check whether the user cancelled
     61 * After the dialog is closed use {@see #isCancelled()} to check whether the user canceled
    6062 * the dialog. If it wasn't canceled you may build a collection of {@see Command} objects
    6163 * which reflect the conflict resolution decisions the user made in the dialog:
     
    6668public class CombinePrimitiveResolverDialog extends JDialog {
    6769
     70    /** the unique instance of the dialog */
    6871    static private CombinePrimitiveResolverDialog instance;
    6972
     73    /**
     74     * Replies the unique instance of the dialog
     75     *
     76     * @return the unique instance of the dialog
     77     */
    7078    public static CombinePrimitiveResolverDialog getInstance() {
    7179        if (instance == null) {
     
    7583    }
    7684
    77     private JSplitPane spTagConflictTypes;
     85    private AutoAdjustingSplitPane spTagConflictTypes;
    7886    private TagConflictResolver pnlTagConflictResolver;
    7987    private RelationMemberConflictResolver pnlRelationMemberConflictResolver;
     
    8290    private OsmPrimitive targetPrimitive;
    8391
    84 
    85 
     92    /**
     93     * Replies the target primitive the collection of primitives is merged
     94     * or combined to.
     95     *
     96     * @return the target primitive
     97     */
    8698    public OsmPrimitive getTargetPrimitmive() {
    8799        return targetPrimitive;
    88100    }
    89101
     102    /**
     103     * Sets the primitive the collection of primitives is merged or combined
     104     * to.
     105     *
     106     * @param primitive the target primitive
     107     */
    90108    public void setTargetPrimitive(OsmPrimitive primitive) {
    91109        this.targetPrimitive = primitive;
     
    118136        getContentPane().setLayout(new BorderLayout());
    119137        updateTitle();
    120         spTagConflictTypes = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     138        spTagConflictTypes = new AutoAdjustingSplitPane(JSplitPane.VERTICAL_SPLIT);
    121139        spTagConflictTypes.setTopComponent(buildTagConflictResolverPanel());
    122140        spTagConflictTypes.setBottomComponent(buildRelationMemberConflictResolverPanel());
     
    218236            }
    219237        }
    220         model.refresh();
     238        model.rebuild();
    221239    }
    222240
     
    245263        TagConflictResolverModel tagModel = getTagConflictResolverModel();
    246264        getContentPane().removeAll();
     265
    247266        if (relModel.getNumDecisions() > 0 && tagModel.getNumDecisions() > 0) {
     267            // display both, the dialog for resolving relation conflicts and for resolving
     268            // tag conflicts
    248269            spTagConflictTypes.setTopComponent(pnlTagConflictResolver);
    249270            spTagConflictTypes.setBottomComponent(pnlRelationMemberConflictResolver);
    250271            getContentPane().add(spTagConflictTypes, BorderLayout.CENTER);
    251272        } else if (relModel.getNumDecisions() > 0) {
     273            // relation conflicts only
     274            //
    252275            getContentPane().add(pnlRelationMemberConflictResolver, BorderLayout.CENTER);
    253276        } else if (tagModel.getNumDecisions() >0) {
     277            // tag conflicts only
     278            //
    254279            getContentPane().add(pnlTagConflictResolver, BorderLayout.CENTER);
    255280        } else {
    256281            getContentPane().add(buildEmptyConflictsPanel(), BorderLayout.CENTER);
    257282        }
     283
    258284        getContentPane().add(pnlButtons, BorderLayout.SOUTH);
    259285        validate();
     
    348374        }
    349375    }
     376
     377    class AutoAdjustingSplitPane extends JSplitPane implements PropertyChangeListener, HierarchyBoundsListener {
     378        private double dividerLocation;
     379
     380        public AutoAdjustingSplitPane(int newOrientation) {
     381            super(newOrientation);
     382            addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY,this);
     383            addHierarchyBoundsListener(this);
     384        }
     385
     386        public void ancestorResized(HierarchyEvent e) {
     387            setDividerLocation((int)(dividerLocation * getHeight()));
     388        }
     389
     390        public void ancestorMoved(HierarchyEvent e) {
     391            // do nothing
     392        }
     393
     394        public void propertyChange(PropertyChangeEvent evt) {
     395            if (evt.getPropertyName().equals(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
     396                int newVal = (Integer)evt.getNewValue();
     397                if (getHeight() != 0) {
     398                    dividerLocation = (double)newVal / (double)getHeight();
     399                }
     400            }
     401        }
     402    }
    350403}
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/MultiValueCellRenderer.java

    r2164 r2220  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.awt.Color;
    67import java.awt.Component;
    78import java.awt.Font;
     
    2021 * This is a {@see TableCellRenderer} for {@see MultiValueResolutionDecision}s.
    2122 *
    22 
    2323 */
    2424public class MultiValueCellRenderer extends JLabel implements TableCellRenderer {
     25
     26    public final static Color BGCOLOR_UNDECIDED = new Color(255,197,197);
    2527
    2628    private ImageIcon iconDecided;
     
    3638    }
    3739
    38     protected void renderColors(boolean selected) {
     40    protected void renderColors(MultiValueResolutionDecision decision, boolean selected) {
    3941        if (selected) {
    4042            setForeground(UIManager.getColor("Table.selectionForeground"));
    4143            setBackground(UIManager.getColor("Table.selectionBackground"));
    42         } else {
    43             setForeground(UIManager.getColor("Table.foreground"));
    44             setBackground(UIManager.getColor("Table.background"));
     44        } else{
     45            switch(decision.getDecisionType()) {
     46                case UNDECIDED:
     47                    setForeground(UIManager.getColor("Table.foreground"));
     48                    setBackground(BGCOLOR_UNDECIDED);
     49                    break;
     50                case KEEP_NONE:
     51                    setForeground(UIManager.getColor("Panel.foreground"));
     52                    setBackground(UIManager.getColor("Panel.background"));
     53                    break;
     54                default:
     55                    setForeground(UIManager.getColor("Table.foreground"));
     56                    setBackground(UIManager.getColor("Table.background"));
     57                    break;
     58            }
    4559        }
    4660    }
     
    4963        model.removeAllElements();
    5064        switch(decision.getDecisionType()) {
    51         case UNDECIDED:
    52             model.addElement(tr("Choose a value"));
    53             setFont(getFont().deriveFont(Font.ITALIC));
    54             setToolTipText(tr("Please decided which values to keep"));
    55             cbDecisionRenderer.setSelectedIndex(0);
    56             break;
    57         case KEEP_ONE:
    58             model.addElement(decision.getChosenValue());
    59             setToolTipText(tr("Value ''{0}'' is going to be applied for key ''{1}''", decision.getChosenValue(), decision.getKey()));
    60             cbDecisionRenderer.setSelectedIndex(0);
    61             break;
    62         case KEEP_NONE:
    63             model.addElement(tr("deleted"));
    64             setFont(getFont().deriveFont(Font.ITALIC));
    65             setToolTipText(tr("The key ''{0}'' and all its values are going to be removed", decision.getKey()));
    66             cbDecisionRenderer.setSelectedIndex(0);
    67             break;
    68         case KEEP_ALL:
    69             model.addElement(decision.getChosenValue());
    70             setToolTipText(tr("All values joined as ''{0}'' are going to be applied for key ''{1}''", decision.getChosenValue(), decision.getKey()));
    71             cbDecisionRenderer.setSelectedIndex(0);
    72             break;
     65            case UNDECIDED:
     66                model.addElement(tr("Choose a value"));
     67                setFont(getFont().deriveFont(Font.ITALIC));
     68                setToolTipText(tr("Please decide which values to keep"));
     69                cbDecisionRenderer.setSelectedIndex(0);
     70                break;
     71            case KEEP_ONE:
     72                model.addElement(decision.getChosenValue());
     73                setToolTipText(tr("Value ''{0}'' is going to be applied for key ''{1}''", decision.getChosenValue(), decision.getKey()));
     74                cbDecisionRenderer.setSelectedIndex(0);
     75                break;
     76            case KEEP_NONE:
     77                model.addElement(tr("deleted"));
     78                setFont(getFont().deriveFont(Font.ITALIC));
     79                setToolTipText(tr("The key ''{0}'' and all its values are going to be removed", decision.getKey()));
     80                cbDecisionRenderer.setSelectedIndex(0);
     81                break;
     82            case KEEP_ALL:
     83                model.addElement(decision.getChosenValue());
     84                setToolTipText(tr("All values joined as ''{0}'' are going to be applied for key ''{1}''", decision.getChosenValue(), decision.getKey()));
     85                cbDecisionRenderer.setSelectedIndex(0);
     86                break;
    7387        }
    7488    }
     
    8498
    8599        reset();
    86         renderColors(isSelected);
    87100        MultiValueResolutionDecision decision = (MultiValueResolutionDecision)value;
     101        renderColors(decision,isSelected);
    88102        switch(column) {
    89         case 0:
    90             if (decision.isDecided()) {
    91                 setIcon(iconDecided);
    92             } else {
    93                 setIcon(iconUndecided);
    94             }
    95             return this;
     103            case 0:
     104                if (decision.isDecided()) {
     105                    setIcon(iconDecided);
     106                } else {
     107                    setIcon(iconUndecided);
     108                }
     109                return this;
    96110
    97         case 1:
    98             setText(decision.getKey());
    99             return this;
     111            case 1:
     112                setText(decision.getKey());
     113                return this;
    100114
    101         case 2:
    102             renderValue(decision);
    103             return cbDecisionRenderer;
     115            case 2:
     116                renderValue(decision);
     117                return cbDecisionRenderer;
    104118        }
    105119        return this;
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java

    r2115 r2220  
    107107    }
    108108
    109 
    110109    protected JPanel buildButtonPanel() {
    111110        JPanel pnl = new JPanel();
     
    135134    }
    136135
     136    /**
     137     * Initializes the conflict resolver for a specific type of primitives
     138     *
     139     * @param type the type of primitives
     140     * @param tc the tags belonging to this type of primitives
     141     * @param targetStatistics histogram of paste targets, number of primitives of each type in the paste target
     142     */
    137143    protected void initResolver(OsmPrimitiveType type, TagCollection tc, Map<OsmPrimitiveType,Integer> targetStatistics) {
    138         resolvers.get(type).getModel().populate(tc);
     144        resolvers.get(type).getModel().populate(tc,tc.getKeysWithMultipleValues());
     145        resolvers.get(type).getModel().prepareDefaultTagDecisions();
    139146        if (!tc.isEmpty() && targetStatistics.get(type) != null && targetStatistics.get(type) > 0) {
    140147            tpResolvers.add(PANE_TITLES.get(type), resolvers.get(type));
     
    142149    }
    143150
    144     protected String formatStatisticsMessage(OsmPrimitiveType type, int count) {
    145         String msg = "";
    146         switch(type) {
    147             case NODE: msg= trn("{0} node", "{0} nodes", count, count); break;
    148             case WAY: msg= trn("{0} way", "{0} ways", count, count); break;
    149             case RELATION: msg= trn("{0} relation", "{0} relations", count, count); break;
    150         }
    151         return msg;
    152     }
    153 
     151    /**
     152     * Populates the conflict resolver with one tag collection
     153     *
     154     * @param tagsForAllPrimitives  the tag collection
     155     * @param sourceStatistics histogram of tag source, number of primitives of each type in the source
     156     * @param targetStatistics histogram of paste targets, number of primitives of each type in the paste target
     157     */
    154158    public void populate(TagCollection tagsForAllPrimitives, Map<OsmPrimitiveType, Integer> sourceStatistics, Map<OsmPrimitiveType,Integer> targetStatistics) {
    155159        mode = Mode.RESOLVING_ONE_TAGCOLLECTION_ONLY;
     
    157161        sourceStatistics = sourceStatistics == null ? new HashMap<OsmPrimitiveType, Integer>() :sourceStatistics;
    158162        targetStatistics = targetStatistics == null ? new HashMap<OsmPrimitiveType, Integer>() : targetStatistics;
    159         allPrimitivesResolver.getModel().populate(tagsForAllPrimitives);
     163
     164        // init the resolver
     165        //
     166        allPrimitivesResolver.getModel().populate(tagsForAllPrimitives,tagsForAllPrimitives.getKeysWithMultipleValues());
     167        allPrimitivesResolver.getModel().prepareDefaultTagDecisions();
     168
     169        // prepare the dialog with one tag resolver
    160170        pnlTagResolver.setLayout(new BorderLayout());
    161171        pnlTagResolver.removeAll();
     
    183193    }
    184194
     195    /**
     196     * Populate the tag conflict resolver with tags for each type of primitives
     197     *
     198     * @param tagsForNodes the tags belonging to nodes in the paste source
     199     * @param tagsForWays the tags belonging to way in the paste source
     200     * @param tagsForRelations the tags belonging to relations in the paste source
     201     * @param sourceStatistics histogram of tag source, number of primitives of each type in the source
     202     * @param targetStatistics histogram of paste targets, number of primitives of each type in the paste target
     203     */
    185204    public void populate(TagCollection tagsForNodes, TagCollection tagsForWays, TagCollection tagsForRelations, Map<OsmPrimitiveType,Integer> sourceStatistics, Map<OsmPrimitiveType, Integer> targetStatistics) {
    186205        tagsForNodes = (tagsForNodes == null) ? new TagCollection() : tagsForNodes;
     
    426445        }
    427446
    428         protected void renderFrom(StatisticsInfo info) {
    429             if (info == null) return;
    430             if (info.sourceInfo == null) return;
    431             if (info.sourceInfo.isEmpty()) return;
    432             if (info.sourceInfo.size() == 1) {
    433                 setIcon(ImageProvider.get(info.sourceInfo.keySet().iterator().next()));
     447        protected void renderStatistics(Map<OsmPrimitiveType, Integer> stat) {
     448            if (stat == null) return;
     449            if (stat.isEmpty()) return;
     450            if (stat.size() == 1) {
     451                setIcon(ImageProvider.get(stat.keySet().iterator().next()));
    434452            } else {
    435453                setIcon(ImageProvider.get("data", "object"));
    436454            }
    437455            String text = "";
    438             for (OsmPrimitiveType type: info.sourceInfo.keySet()) {
    439                 int numPrimitives = info.sourceInfo.get(type) == null ? 0 : info.sourceInfo.get(type);
     456            for (OsmPrimitiveType type: stat.keySet()) {
     457                int numPrimitives = stat.get(type) == null ? 0 : stat.get(type);
    440458                if (numPrimitives == 0) {
    441459                    continue;
     
    452470        }
    453471
     472        protected void renderFrom(StatisticsInfo info) {
     473            renderStatistics(info.sourceInfo);
     474        }
     475
    454476        protected void renderTo(StatisticsInfo info) {
    455             if (info == null) return;
    456             if (info.targetInfo == null) return;
    457             if (info.targetInfo.isEmpty()) return;
    458             if (info.targetInfo.size() == 1) {
    459                 setIcon(ImageProvider.get(info.targetInfo.keySet().iterator().next()));
    460             } else {
    461                 setIcon(ImageProvider.get("data", "object"));
    462             }
    463             String text = "";
    464             for (OsmPrimitiveType type: info.targetInfo.keySet()) {
    465                 int numPrimitives = info.targetInfo.get(type) == null ? 0 : info.targetInfo.get(type);
    466                 if (numPrimitives == 0) {
    467                     continue;
    468                 }
    469                 String msg = "";
    470                 switch(type) {
    471                     case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
    472                     case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
    473                     case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
    474                 }
    475                 text = text.equals("") ? msg : text + ", " + msg;
    476             }
    477             setText(text);
     477            renderStatistics(info.targetInfo);
    478478        }
    479479
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolver.java

    r2008 r2220  
    22package org.openstreetmap.josm.gui.conflict.tags;
    33
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
    46import java.awt.BorderLayout;
     7import java.awt.GridBagConstraints;
     8import java.awt.GridBagLayout;
    59
     10import javax.swing.BorderFactory;
     11import javax.swing.JCheckBox;
     12import javax.swing.JLabel;
    613import javax.swing.JPanel;
    714import javax.swing.JScrollPane;
     15import javax.swing.event.ChangeEvent;
     16import javax.swing.event.ChangeListener;
    817
     18import org.openstreetmap.josm.Main;
     19
     20/**
     21 * This is a UI widget for resolving tag conflicts, i.e. differences of the tag values
     22 * of multiple {@see OsmPrimitive}s.
     23 *
     24 *
     25 */
    926public class TagConflictResolver extends JPanel {
    1027
     28    /** the model for the tag conflict resolver */
    1129    private TagConflictResolverModel model;
     30    /** selects wheter only tags with conflicts are displayed */
     31    private JCheckBox cbShowTagsWithConflictsOnly;
     32
     33    protected JPanel buildInfoPanel() {
     34        JPanel pnl = new JPanel();
     35        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     36        pnl.setLayout(new GridBagLayout());
     37        GridBagConstraints gc = new GridBagConstraints();
     38        gc.fill = GridBagConstraints.BOTH;
     39        gc.weighty = 1.0;
     40        gc.weightx = 1.0;
     41        gc.anchor = GridBagConstraints.LINE_START;
     42        pnl.add(new JLabel(tr("<html>Please select the values to keep for the following tags.</html>")), gc);
     43
     44        gc.gridy = 1;
     45        gc.fill = GridBagConstraints.HORIZONTAL;
     46        gc.weighty = 0.0;
     47        pnl.add(cbShowTagsWithConflictsOnly = new JCheckBox(tr("Show tags with conflicts only")), gc);
     48        cbShowTagsWithConflictsOnly.addChangeListener(
     49                new ChangeListener() {
     50                    public void stateChanged(ChangeEvent e) {
     51                        model.setShowTagsWithConflictsOnly(cbShowTagsWithConflictsOnly.isSelected());
     52                    }
     53                }
     54        );
     55        cbShowTagsWithConflictsOnly.setSelected(
     56                Main.pref.getBoolean(getClass().getName() + ".showTagsWithConflictsOnly", false)
     57        );
     58        return pnl;
     59    }
     60
     61    /**
     62     * Remembers the current settings in the global preferences
     63     *
     64     */
     65    public void rememberPreferences() {
     66        Main.pref.put(getClass().getName() + ".showTagsWithConflictsOnly", cbShowTagsWithConflictsOnly.isSelected());
     67    }
    1268
    1369    protected void build() {
    1470        setLayout(new BorderLayout());
     71        add(buildInfoPanel(), BorderLayout.NORTH);
    1572        add(new JScrollPane(new TagConflictResolverTable(model)), BorderLayout.CENTER);
    1673    }
     74
    1775    public TagConflictResolver() {
    1876        this.model = new TagConflictResolverModel();
     
    2078    }
    2179
     80    /**
     81     * Replies the model used by this dialog
     82     *
     83     * @return the model
     84     */
    2285    public TagConflictResolverModel getModel() {
    2386        return model;
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolverModel.java

    r2181 r2220  
    1010import java.util.Comparator;
    1111import java.util.HashMap;
     12import java.util.HashSet;
    1213import java.util.List;
     14import java.util.Set;
    1315
    1416import javax.swing.table.DefaultTableModel;
     
    2022
    2123    private TagCollection tags;
    22     private List<String> keys;
     24    private List<String> displayedKeys;
     25    private Set<String> keysWithConflicts;
    2326    private HashMap<String, MultiValueResolutionDecision> decisions;
    2427    private int numConflicts;
    2528    private PropertyChangeSupport support;
     29    private boolean showTagsWithConflictsOnly = false;
    2630
    2731    public TagConflictResolverModel() {
     
    3741        support.removePropertyChangeListener(listener);
    3842    }
    39 
    4043
    4144    protected void setNumConflicts(int numConflicts) {
     
    5962    protected void sort() {
    6063        Collections.sort(
    61                 keys,
     64                displayedKeys,
    6265                new Comparator<String>() {
    63                     public int compare(String o1, String o2) {
    64                         if (decisions.get(o1).isDecided() && ! decisions.get(o2).isDecided())
     66                    public int compare(String key1, String key2) {
     67                        if (decisions.get(key1).isDecided() && ! decisions.get(key2).isDecided())
    6568                            return 1;
    66                         else if (!decisions.get(o1).isDecided() && decisions.get(o2).isDecided())
     69                        else if (!decisions.get(key1).isDecided() && decisions.get(key2).isDecided())
    6770                            return -1;
    68                         return o1.compareTo(o2);
     71                        return key1.compareTo(key2);
    6972                    }
    7073                }
     
    7275    }
    7376
    74     protected void init() {
    75         keys.clear();
    76         keys.addAll(tags.getKeys());
     77    /**
     78     * initializes the model from the current tags
     79     *
     80     */
     81    protected void rebuild() {
     82        if (tags == null) return;
    7783        for(String key: tags.getKeys()) {
    7884            MultiValueResolutionDecision decision = new MultiValueResolutionDecision(tags.getTagsFor(key));
    79             decisions.put(key,decision);
    80         }
     85            if (decisions.get(key) == null) {
     86                decisions.put(key,decision);
     87            }
     88        }
     89        displayedKeys.clear();
     90        Set<String> keys = tags.getKeys();
     91        if (showTagsWithConflictsOnly) {
     92            keys.retainAll(keysWithConflicts);
     93            for (String key: tags.getKeys()) {
     94                if (!decisions.get(key).isDecided() && !keys.contains(key)) {
     95                    keys.add(key);
     96                }
     97            }
     98        }
     99        displayedKeys.addAll(keys);
    81100        refreshNumConflicts();
    82     }
    83 
    84     public void populate(TagCollection tags) {
     101        sort();
     102        fireTableDataChanged();
     103    }
     104
     105    /**
     106     * Populates the model with the tags for which conflicts are to be resolved.
     107     *
     108     * @param tags  the tag collection with the tags. Must not be null.
     109     * @param keysWithConflicts the set of tag keys with conflicts
     110     * @throws IllegalArgumentException thrown if tags is null
     111     */
     112    public void populate(TagCollection tags, Set<String> keysWithConflicts) {
    85113        if (tags == null)
    86114            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "tags"));
    87115        this.tags = tags;
    88         keys = new ArrayList<String>();
     116        displayedKeys = new ArrayList<String>();
     117        this.keysWithConflicts = keysWithConflicts == null ? new HashSet<String>() : keysWithConflicts;
    89118        decisions = new HashMap<String, MultiValueResolutionDecision>();
    90         init();
    91         sort();
    92         fireTableDataChanged();
    93     }
    94 
     119        rebuild();
     120    }
    95121
    96122    @Override
    97123    public int getRowCount() {
    98         if (keys == null) return 0;
    99         return keys.size();
     124        if (displayedKeys == null) return 0;
     125        return displayedKeys.size();
    100126    }
    101127
    102128    @Override
    103129    public Object getValueAt(int row, int column) {
    104         return decisions.get(keys.get(row));
     130        return decisions.get(displayedKeys.get(row));
    105131    }
    106132
     
    112138    @Override
    113139    public void setValueAt(Object value, int row, int column) {
    114         MultiValueResolutionDecision decision = decisions.get(keys.get(row));
     140        MultiValueResolutionDecision decision = decisions.get(displayedKeys.get(row));
    115141        if (value instanceof String) {
    116142            decision.keepOne((String)value);
     
    150176    public TagCollection getResolution() {
    151177        TagCollection tc = new TagCollection();
    152         for (String key: keys) {
     178        for (String key: displayedKeys) {
    153179            tc.add(decisions.get(key).getResolution());
    154180        }
     
    157183
    158184    public MultiValueResolutionDecision getDecision(int row) {
    159         return decisions.get(keys.get(row));
    160     }
    161 
    162     public void refresh() {
    163         fireTableDataChanged();
    164         refreshNumConflicts();
    165     }
     185        return decisions.get(displayedKeys.get(row));
     186    }
     187
     188    /**
     189     * Sets whether all tags or only tags with conflicts are displayed
     190     *
     191     * @param showTagsWithConflictsOnly if true, only tags with conflicts are displayed
     192     */
     193    public void setShowTagsWithConflictsOnly(boolean showTagsWithConflictsOnly) {
     194        this.showTagsWithConflictsOnly = showTagsWithConflictsOnly;
     195        rebuild();
     196    }
     197
     198    /**
     199     * Prepare the default decisions for the current model
     200     *
     201     */
     202    public void prepareDefaultTagDecisions() {
     203        for (MultiValueResolutionDecision decision: decisions.values()) {
     204            List<String> values = decision.getValues();
     205            values.remove("");
     206            if (values.size() == 1) {
     207                decision.keepOne(values.get(0));
     208            } else {
     209                decision.keepAll();
     210            }
     211        }
     212        rebuild();
     213    }
     214
    166215}
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolverTable.java

    r2008 r2220  
    66
    77import javax.swing.AbstractAction;
     8import javax.swing.JComboBox;
    89import javax.swing.JComponent;
    910import javax.swing.JTable;
     
    3940
    4041        ((MultiValueCellEditor)getColumnModel().getColumn(2).getCellEditor()).addNavigationListeners(this);
     42
     43        setRowHeight((int)new JComboBox().getPreferredSize().getHeight());
    4144    }
    4245
Note: See TracChangeset for help on using the changeset viewer.