Ticket #23305: 23305.patch

File 23305.patch, 11.6 KB (added by GerdP, 2 years ago)
  • src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java

     
    33
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
    6 import static org.openstreetmap.josm.tools.I18n.trn;
    76
    87import java.awt.BorderLayout;
    98import java.awt.Component;
     
    1615import java.beans.PropertyChangeEvent;
    1716import java.beans.PropertyChangeListener;
    1817import java.util.Collection;
     18import java.util.HashSet;
    1919import java.util.LinkedList;
    2020import java.util.List;
    2121import java.util.Set;
    22 import java.util.stream.Collectors;
    2322
    2423import javax.swing.AbstractAction;
    2524import javax.swing.Action;
     
    2625import javax.swing.JButton;
    2726import javax.swing.JDialog;
    2827import javax.swing.JLabel;
    29 import javax.swing.JOptionPane;
    3028import javax.swing.JPanel;
    3129import javax.swing.JSplitPane;
    3230
    33 import org.openstreetmap.josm.actions.ExpertToggleAction;
    3431import org.openstreetmap.josm.command.Command;
    3532import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
    3633import org.openstreetmap.josm.data.osm.Node;
     
    3835import org.openstreetmap.josm.data.osm.Relation;
    3936import org.openstreetmap.josm.data.osm.TagCollection;
    4037import org.openstreetmap.josm.data.osm.Way;
    41 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
    4238import org.openstreetmap.josm.gui.MainApplication;
    4339import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
    4440import org.openstreetmap.josm.gui.help.HelpUtil;
     
    4541import org.openstreetmap.josm.gui.util.GuiHelper;
    4642import org.openstreetmap.josm.gui.util.WindowGeometry;
    4743import org.openstreetmap.josm.gui.widgets.AutoAdjustingSplitPane;
     44import org.openstreetmap.josm.spi.preferences.Config;
    4845import org.openstreetmap.josm.tools.CheckParameterUtil;
    4946import org.openstreetmap.josm.tools.ImageProvider;
    5047import org.openstreetmap.josm.tools.InputMapUtils;
    51 import org.openstreetmap.josm.tools.StreamUtils;
    5248import org.openstreetmap.josm.tools.UserCancelException;
    53 import org.openstreetmap.josm.tools.Utils;
    5449
    5550/**
    5651 * This dialog helps to resolve conflicts occurring when ways are combined or
     
    471466     * by displaying if necessary a {@link CombinePrimitiveResolverDialog} to the user.
    472467     * This dialog will allow the user to choose conflict resolution actions.
    473468     *
    474      * Non-expert users are informed first of the meaning of these operations, allowing them to cancel.
    475      *
    476469     * @param tagsOfPrimitives The tag collection of the primitives to be combined.
    477470     *                         Should generally be equal to {@code TagCollection.unionOfAllPrimitives(primitives)}
    478471     * @param primitives The primitives to be combined
     
    496489        TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);
    497490
    498491        final Set<Relation> parentRelations = OsmPrimitive.getParentRelations(primitives);
    499 
    500         // Show information dialogs about conflicts to non-experts
    501         if (!ExpertToggleAction.isExpert()) {
    502             // Tag conflicts
    503             if (!completeWayTags.isApplicableToPrimitive()) {
    504                 informAboutTagConflicts(primitives, completeWayTags);
    505             }
    506             // Relation membership conflicts
    507             if (!parentRelations.isEmpty()) {
    508                 informAboutRelationMembershipConflicts(primitives, parentRelations);
    509             }
    510         }
    511 
    512492        final List<Command> cmds = new LinkedList<>();
    513493
    514494        final TagConflictResolverModel tagModel = new TagConflictResolverModel();
     
    516496
    517497        tagModel.populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues(), false);
    518498        relModel.populate(parentRelations, primitives, false);
    519         tagModel.prepareDefaultTagDecisions(false);
     499        if (Config.getPref().getBoolean("combine-conflict-precise", true)) {
     500            tagModel.prepareDefaultTagDecisions(getResolvableKeys(tagsOfPrimitives.getKeys(), primitives));
     501        } else {
     502            tagModel.prepareDefaultTagDecisions(false);
     503        }
    520504        relModel.prepareDefaultRelationDecisions(false);
    521505
    522506        if (tagModel.isResolvedCompletely() && relModel.isResolvedCompletely()) {
     
    566550    }
    567551
    568552    /**
    569      * Inform a non-expert user about what relation membership conflict resolution means.
    570      * @param primitives The primitives to be combined
    571      * @param parentRelations The parent relations of the primitives
    572      * @throws UserCancelException If the user cancels the dialog.
     553     * See #23305: Find those tag keys for which no conflict exists.
     554     * @param keysToDecide the keys of tags which might be shown in the conflict dialog
     555     * @param primitives the collection of primitives
     556     * @return the keys which can be resolved using the only available value
    573557     */
    574     protected static void informAboutRelationMembershipConflicts(
    575             final Collection<? extends OsmPrimitive> primitives,
    576             final Set<Relation> parentRelations) throws UserCancelException {
    577         /* I18n: object count < 2 is not possible */
    578         String msg = trn("You are about to combine {1} object, "
    579                 + "which is part of {0} relation:<br/>{2}"
    580                 + "Combining these objects may break this relation. If you are unsure, please cancel this operation.<br/>"
    581                 + "If you want to continue, you are shown a dialog to decide how to adapt the relation.<br/><br/>"
    582                 + "Do you want to continue?",
    583                 "You are about to combine {1} objects, "
    584                 + "which are part of {0} relations:<br/>{2}"
    585                 + "Combining these objects may break these relations. If you are unsure, please cancel this operation.<br/>"
    586                 + "If you want to continue, you are shown a dialog to decide how to adapt the relations.<br/><br/>"
    587                 + "Do you want to continue?",
    588                 parentRelations.size(), parentRelations.size(), primitives.size(),
    589                 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(parentRelations, 20));
    590 
    591         if (!ConditionalOptionPaneUtil.showConfirmationDialog(
    592                 "combine_tags",
    593                 MainApplication.getMainFrame(),
    594                 "<html>" + msg + "</html>",
    595                 tr("Combine confirmation"),
    596                 JOptionPane.YES_NO_OPTION,
    597                 JOptionPane.QUESTION_MESSAGE,
    598                 JOptionPane.YES_OPTION)) {
    599             throw new UserCancelException();
     558    private static Set<String> getResolvableKeys(Set<String> keysToDecide, Collection<? extends OsmPrimitive> primitives) {
     559        Set<String> easyKeys = new HashSet<>();
     560        // determine the number of objects which have any of the tags which require a decision
     561        int countTagged = 0;
     562        for (OsmPrimitive p : primitives) {
     563            for (String key : keysToDecide) {
     564                if (p.hasTag(key)) {
     565                    ++countTagged;
     566                    break;
     567                }
     568            }
    600569        }
    601     }
    602 
    603     /**
    604      * Inform a non-expert user about what tag conflict resolution means.
    605      * @param primitives The primitives to be combined
    606      * @param normalizedTags The normalized tag collection of the primitives to be combined
    607      * @throws UserCancelException If the user cancels the dialog.
    608      */
    609     protected static void informAboutTagConflicts(
    610             final Collection<? extends OsmPrimitive> primitives,
    611             final TagCollection normalizedTags) throws UserCancelException {
    612         String conflicts = normalizedTags.getKeysWithMultipleValues().stream().map(
    613                 key -> getKeyDescription(key, normalizedTags)).collect(StreamUtils.toHtmlList());
    614         String msg = /* for correct i18n of plural forms - see #9110 */ trn("You are about to combine {0} objects, "
    615                 + "but the following tags are used conflictingly:<br/>{1}"
    616                 + "If these objects are combined, the resulting object may have unwanted tags.<br/>"
    617                 + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>"
    618                 + "Do you want to continue?", "You are about to combine {0} objects, "
    619                 + "but the following tags are used conflictingly:<br/>{1}"
    620                 + "If these objects are combined, the resulting object may have unwanted tags.<br/>"
    621                 + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>"
    622                 + "Do you want to continue?",
    623                 primitives.size(), primitives.size(), conflicts);
    624 
    625         if (!ConditionalOptionPaneUtil.showConfirmationDialog(
    626                 "combine_tags",
    627                 MainApplication.getMainFrame(),
    628                 "<html>" + msg + "</html>",
    629                 tr("Combine confirmation"),
    630                 JOptionPane.YES_NO_OPTION,
    631                 JOptionPane.QUESTION_MESSAGE,
    632                 JOptionPane.YES_OPTION)) {
    633             throw new UserCancelException();
     570        for (String key : keysToDecide) {
     571            Set<String> values = new HashSet<>();
     572            int num = 0;
     573            for (OsmPrimitive p : primitives) {
     574                String val = p.get(key);
     575                if (val != null) {
     576                    num++;
     577                    values.add(val);
     578                }
     579            }
     580            if (values.size() == 1 && num == countTagged) {
     581                // there is only one value and all tagged objects have that value -> easy to solve
     582                easyKeys.add(key);
     583            }
    634584        }
     585        return easyKeys;
    635586    }
    636587
    637     private static String getKeyDescription(String key, TagCollection normalizedTags) {
    638         String values = normalizedTags.getValues(key)
    639                 .stream()
    640                 .map(x -> Utils.isEmpty(x) ? tr("<i>missing</i>") : x)
    641                 .collect(Collectors.joining(tr(", ")));
    642         return tr("{0} ({1})", key, values);
    643     }
    644 
    645588    @Override
    646589    public void dispose() {
    647590        setTargetPrimitive(null, false);
  • src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolverModel.java

     
    275275    /**
    276276     * Prepare the default decisions for the current model
    277277     * @param fireEvent {@code true} to call {@code fireTableDataChanged} (can be a slow operation)
    278      * @since 11626
     278     * @since 11627
    279279     */
    280280    void prepareDefaultTagDecisions(boolean fireEvent) {
    281281        for (MultiValueResolutionDecision decision: decisions.values()) {
     
    292292    }
    293293
    294294    /**
     295     * Prepare the default decisions for the current model.
     296     * @param decidedKeys set of tag keys for which the first value should be used
     297     * @since xxx
     298     */
     299    public void prepareDefaultTagDecisions(Set<String> decidedKeys) {
     300        for (MultiValueResolutionDecision decision : decisions.values()) {
     301            if (!decidedKeys.contains(decision.getKey()))
     302                continue;
     303            List<String> values = decision.getValues();
     304            if (!values.isEmpty()) {
     305                decision.keepOne(values.iterator().next());
     306            }
     307        }
     308        rebuild(false);
     309    }
     310
     311    /**
    295312     * Returns the set of keys in conflict.
    296313     * @return the set of keys in conflict.
    297314     * @since 6616