Changeset 10869 in josm for trunk/src


Ignore:
Timestamp:
2016-08-21T23:15:36+02:00 (3 years ago)
Author:
Don-vip
Message:

fix #13402 - Shift+P (make parallel ways copy) causes crash (patch by michael2402, modified) - regression - gsoc-core

Location:
trunk/src/org/openstreetmap/josm
Files:
2 edited

Legend:

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

    r10853 r10869  
    1414import java.awt.event.MouseEvent;
    1515import java.util.Collection;
     16import java.util.Collections;
     17import java.util.EnumMap;
     18import java.util.EnumSet;
    1619import java.util.LinkedHashSet;
     20import java.util.Map;
     21import java.util.Optional;
    1722import java.util.Set;
     23import java.util.stream.Stream;
    1824
    1925import javax.swing.JOptionPane;
     
    2127import org.openstreetmap.josm.Main;
    2228import org.openstreetmap.josm.data.Bounds;
    23 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
    2429import org.openstreetmap.josm.data.SystemOfMeasurement;
    2530import org.openstreetmap.josm.data.coor.EastNorth;
     
    2833import org.openstreetmap.josm.data.osm.Way;
    2934import org.openstreetmap.josm.data.osm.WaySegment;
     35import org.openstreetmap.josm.data.osm.visitor.paint.MapPath2D;
    3036import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
     37import org.openstreetmap.josm.data.preferences.AbstractToStringProperty;
     38import org.openstreetmap.josm.data.preferences.BooleanProperty;
     39import org.openstreetmap.josm.data.preferences.CachingProperty;
    3140import org.openstreetmap.josm.data.preferences.ColorProperty;
     41import org.openstreetmap.josm.data.preferences.DoubleProperty;
     42import org.openstreetmap.josm.data.preferences.IntegerProperty;
     43import org.openstreetmap.josm.data.preferences.StringProperty;
    3244import org.openstreetmap.josm.gui.MapFrame;
    3345import org.openstreetmap.josm.gui.MapView;
     
    3850import org.openstreetmap.josm.gui.util.GuiHelper;
    3951import org.openstreetmap.josm.gui.util.ModifierListener;
     52import org.openstreetmap.josm.tools.CheckParameterUtil;
    4053import org.openstreetmap.josm.tools.Geometry;
    4154import org.openstreetmap.josm.tools.ImageProvider;
     
    8093 */
    8194public class ParallelWayAction extends MapMode implements ModifierListener, MapViewPaintable {
     95
     96    private static final StringProperty HELPER_LINE_STROKE = new StringProperty(prefKey("stroke.hepler-line"), "1");
     97    private static final StringProperty REF_LINE_STROKE = new StringProperty(prefKey("stroke.ref-line"), "1 2 2");
     98
     99    // @formatter:off
     100    // CHECKSTYLE.OFF: SingleSpaceSeparator
     101    private static final CachingProperty<Double> SNAP_THRESHOLD         = new DoubleProperty(prefKey("snap-threshold-percent"), 0.70).cached();
     102    private static final CachingProperty<Boolean> SNAP_DEFAULT          = new BooleanProperty(prefKey("snap-default"),      true).cached();
     103    private static final CachingProperty<Boolean> COPY_TAGS_DEFAULT     = new BooleanProperty(prefKey("copy-tags-default"), true).cached();
     104    private static final CachingProperty<Integer> INITIAL_MOVE_DELAY    = new IntegerProperty(prefKey("initial-move-delay"), 200).cached();
     105    private static final CachingProperty<Double> SNAP_DISTANCE_METRIC   = new DoubleProperty(prefKey("snap-distance-metric"), 0.5).cached();
     106    private static final CachingProperty<Double> SNAP_DISTANCE_IMPERIAL = new DoubleProperty(prefKey("snap-distance-imperial"), 1).cached();
     107    private static final CachingProperty<Double> SNAP_DISTANCE_CHINESE  = new DoubleProperty(prefKey("snap-distance-chinese"), 1).cached();
     108    private static final CachingProperty<Double> SNAP_DISTANCE_NAUTICAL = new DoubleProperty(prefKey("snap-distance-nautical"), 0.1).cached();
     109    private static final CachingProperty<Color> MAIN_COLOR = new ColorProperty(marktr("make parallel helper line"), (Color) null).cached();
     110
     111    private static final CachingProperty<Map<Modifier, Boolean>> SNAP_MODIFIER_COMBO
     112            = new KeyboardModifiersProperty(prefKey("snap-modifier-combo"),             "?sC").cached();
     113    private static final CachingProperty<Map<Modifier, Boolean>> COPY_TAGS_MODIFIER_COMBO
     114            = new KeyboardModifiersProperty(prefKey("copy-tags-modifier-combo"),        "As?").cached();
     115    private static final CachingProperty<Map<Modifier, Boolean>> ADD_TO_SELECTION_MODIFIER_COMBO
     116            = new KeyboardModifiersProperty(prefKey("add-to-selection-modifier-combo"), "aSc").cached();
     117    private static final CachingProperty<Map<Modifier, Boolean>> TOGGLE_SELECTED_MODIFIER_COMBO
     118            = new KeyboardModifiersProperty(prefKey("toggle-selection-modifier-combo"), "asC").cached();
     119    private static final CachingProperty<Map<Modifier, Boolean>> SET_SELECTED_MODIFIER_COMBO
     120            = new KeyboardModifiersProperty(prefKey("set-selection-modifier-combo"),    "asc").cached();
     121    // CHECKSTYLE.ON: SingleSpaceSeparator
     122    // @formatter:on
    82123
    83124    private enum Mode {
     
    89130    private Mode mode;
    90131    private boolean copyTags;
    91     private boolean copyTagsDefault;
    92132
    93133    private boolean snap;
    94     private boolean snapDefault;
    95 
    96     private double snapThreshold;
    97     private double snapDistanceMetric;
    98     private double snapDistanceImperial;
    99     private double snapDistanceChinese;
    100     private double snapDistanceNautical;
    101 
    102     private transient ModifiersSpec snapModifierCombo;
    103     private transient ModifiersSpec copyTagsModifierCombo;
    104     private transient ModifiersSpec addToSelectionModifierCombo;
    105     private transient ModifiersSpec toggleSelectedModifierCombo;
    106     private transient ModifiersSpec setSelectedModifierCombo;
    107 
    108     private int initialMoveDelay;
    109134
    110135    private final MapView mv;
     
    124149    private transient Stroke helpLineStroke;
    125150    private transient Stroke refLineStroke;
    126     private Color mainColor;
    127151
    128152    /**
     
    137161        putValue("help", ht("/Action/Parallel"));
    138162        mv = mapFrame.mapView;
    139         updateModeLocalPreferences();
    140         Main.pref.addPreferenceChangeListener(this);
    141163    }
    142164
     
    146168        setMode(Mode.NORMAL);
    147169        pWays = null;
    148         updateAllPreferences(); // All default values should've been set now
    149170
    150171        super.enterMode();
     
    154175        mv.addTemporaryLayer(this);
    155176
    156         helpLineStroke = GuiHelper.getCustomizedStroke(getStringPref("stroke.hepler-line", "1"));
    157         refLineStroke = GuiHelper.getCustomizedStroke(getStringPref("stroke.ref-line", "1 2 2"));
    158         mainColor = new ColorProperty(marktr("make parallel helper line"), (Color) null).get();
    159         if (mainColor == null)
    160             mainColor = PaintColors.SELECTED.get();
     177        helpLineStroke = GuiHelper.getCustomizedStroke(HELPER_LINE_STROKE.get());
     178        refLineStroke = GuiHelper.getCustomizedStroke(REF_LINE_STROKE.get());
    161179
    162180        //// Needed to update the mouse cursor if modifiers are changed when the mouse is motionless
     
    200218    }
    201219
    202     // Separated due to "race condition" between default values
    203     private void updateAllPreferences() {
    204         updateModeLocalPreferences();
    205     }
    206 
    207     private void updateModeLocalPreferences() {
    208         // @formatter:off
    209         // CHECKSTYLE.OFF: SingleSpaceSeparator
    210         snapThreshold        = Main.pref.getDouble(prefKey("snap-threshold-percent"), 0.70);
    211         snapDefault          = Main.pref.getBoolean(prefKey("snap-default"),      true);
    212         copyTagsDefault      = Main.pref.getBoolean(prefKey("copy-tags-default"), true);
    213         initialMoveDelay     = Main.pref.getInteger(prefKey("initial-move-delay"), 200);
    214         snapDistanceMetric   = Main.pref.getDouble(prefKey("snap-distance-metric"), 0.5);
    215         snapDistanceImperial = Main.pref.getDouble(prefKey("snap-distance-imperial"), 1);
    216         snapDistanceChinese  = Main.pref.getDouble(prefKey("snap-distance-chinese"), 1);
    217         snapDistanceNautical = Main.pref.getDouble(prefKey("snap-distance-nautical"), 0.1);
    218 
    219         snapModifierCombo           = new ModifiersSpec(getStringPref("snap-modifier-combo",             "?sC"));
    220         copyTagsModifierCombo       = new ModifiersSpec(getStringPref("copy-tags-modifier-combo",        "As?"));
    221         addToSelectionModifierCombo = new ModifiersSpec(getStringPref("add-to-selection-modifier-combo", "aSc"));
    222         toggleSelectedModifierCombo = new ModifiersSpec(getStringPref("toggle-selection-modifier-combo", "asC"));
    223         setSelectedModifierCombo    = new ModifiersSpec(getStringPref("set-selection-modifier-combo",    "asc"));
    224         // CHECKSTYLE.ON: SingleSpaceSeparator
    225         // @formatter:on
    226     }
    227 
    228220    @Override
    229221    public boolean layerIsSupported(Layer layer) {
     
    253245        switch (mode) {
    254246        case NORMAL:
    255             if (matchesCurrentModifiers(setSelectedModifierCombo)) {
     247            if (matchesCurrentModifiers(SET_SELECTED_MODIFIER_COMBO)) {
    256248                newCursor = ImageProvider.getCursor("normal", "parallel");
    257             } else if (matchesCurrentModifiers(addToSelectionModifierCombo)) {
     249            } else if (matchesCurrentModifiers(ADD_TO_SELECTION_MODIFIER_COMBO)) {
    258250                newCursor = ImageProvider.getCursor("normal", "parallel_add");
    259             } else if (matchesCurrentModifiers(toggleSelectedModifierCombo)) {
     251            } else if (matchesCurrentModifiers(TOGGLE_SELECTED_MODIFIER_COMBO)) {
    260252                newCursor = ImageProvider.getCursor("normal", "parallel_remove");
    261253            }
     
    325317            Way nearestWay = mv.getNearestWay(e.getPoint(), OsmPrimitive::isSelectable);
    326318            if (nearestWay == null) {
    327                 if (matchesCurrentModifiers(setSelectedModifierCombo)) {
     319                if (matchesCurrentModifiers(SET_SELECTED_MODIFIER_COMBO)) {
    328320                    clearSourceWays();
    329321                }
     
    332324            }
    333325            boolean isSelected = nearestWay.isSelected();
    334             if (matchesCurrentModifiers(addToSelectionModifierCombo)) {
     326            if (matchesCurrentModifiers(ADD_TO_SELECTION_MODIFIER_COMBO)) {
    335327                if (!isSelected) {
    336328                    addSourceWay(nearestWay);
    337329                }
    338             } else if (matchesCurrentModifiers(toggleSelectedModifierCombo)) {
     330            } else if (matchesCurrentModifiers(TOGGLE_SELECTED_MODIFIER_COMBO)) {
    339331                if (isSelected) {
    340332                    removeSourceWay(nearestWay);
     
    342334                    addSourceWay(nearestWay);
    343335                }
    344             } else if (matchesCurrentModifiers(setSelectedModifierCombo)) {
     336            } else if (matchesCurrentModifiers(SET_SELECTED_MODIFIER_COMBO)) {
    345337                clearSourceWays();
    346338                addSourceWay(nearestWay);
     
    380372        }
    381373
    382         if ((System.currentTimeMillis() - mousePressedTime) < initialMoveDelay)
     374        if ((System.currentTimeMillis() - mousePressedTime) < INITIAL_MOVE_DELAY.get())
    383375            return;
    384376        // Assuming this event only is emitted when the mouse has moved
     
    409401        double snappedRealD = realD;
    410402
    411         // TODO: abuse of isToTheRightSideOfLine function.
    412         boolean toTheRight = Geometry.isToTheRightSideOfLine(referenceSegment.getFirstNode(),
     403        boolean toTheRight = Geometry.angleIsClockwise(
    413404                referenceSegment.getFirstNode(), referenceSegment.getSecondNode(), new Node(enp));
    414405
     
    419410            SystemOfMeasurement som = SystemOfMeasurement.getSystemOfMeasurement();
    420411            if (som.equals(SystemOfMeasurement.CHINESE)) {
    421                 snapDistance = snapDistanceChinese * SystemOfMeasurement.CHINESE.aValue;
     412                snapDistance = SNAP_DISTANCE_CHINESE.get() * SystemOfMeasurement.CHINESE.aValue;
    422413            } else if (som.equals(SystemOfMeasurement.IMPERIAL)) {
    423                 snapDistance = snapDistanceImperial * SystemOfMeasurement.IMPERIAL.aValue;
     414                snapDistance = SNAP_DISTANCE_IMPERIAL.get() * SystemOfMeasurement.IMPERIAL.aValue;
    424415            } else if (som.equals(SystemOfMeasurement.NAUTICAL_MILE)) {
    425                 snapDistance = snapDistanceNautical * SystemOfMeasurement.NAUTICAL_MILE.aValue;
     416                snapDistance = SNAP_DISTANCE_NAUTICAL.get() * SystemOfMeasurement.NAUTICAL_MILE.aValue;
    426417            } else {
    427                 snapDistance = snapDistanceMetric; // Metric system by default
     418                snapDistance = SNAP_DISTANCE_METRIC.get(); // Metric system by default
    428419            }
    429420            double closestWholeUnit;
     
    434425                closestWholeUnit = realD + (snapDistance-modulo);
    435426            }
    436             if (Math.abs(closestWholeUnit - realD) < (snapThreshold * snapDistance)) {
     427            if (Math.abs(closestWholeUnit - realD) < (SNAP_THRESHOLD.get() * snapDistance)) {
    437428                snappedRealD = closestWholeUnit;
    438429            } else {
     
    453444    }
    454445
    455     private boolean matchesCurrentModifiers(ModifiersSpec spec) {
    456         return spec.matchWithKnown(alt, shift, ctrl);
     446    private boolean matchesCurrentModifiers(CachingProperty<Map<Modifier, Boolean>> spec) {
     447        return matchesCurrentModifiers(spec.get());
     448    }
     449
     450    private boolean matchesCurrentModifiers(Map<Modifier, Boolean> spec) {
     451        EnumSet<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
     452        if (ctrl) {
     453            modifiers.add(Modifier.CTRL);
     454        }
     455        if (alt) {
     456            modifiers.add(Modifier.ALT);
     457        }
     458        if (shift) {
     459            modifiers.add(Modifier.SHIFT);
     460        }
     461        return spec.entrySet().stream().allMatch(entry -> modifiers.contains(entry.getKey()) == entry.getValue().booleanValue());
    457462    }
    458463
     
    460465    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
    461466        if (mode == Mode.DRAGGING) {
    462             // sanity checks
    463             if (mv == null)
    464                 return;
     467            CheckParameterUtil.ensureParameterNotNull(mv, "mv");
     468
     469            Color mainColor = MAIN_COLOR.get();
     470            if (mainColor == null) {
     471                mainColor = PaintColors.SELECTED.get();
     472            }
    465473
    466474            // FIXME: should clip the line (gets insanely slow when zoomed in on a very long line
    467475            g.setStroke(refLineStroke);
    468476            g.setColor(mainColor);
    469             Point p1 = mv.getPoint(referenceSegment.getFirstNode().getEastNorth());
    470             Point p2 = mv.getPoint(referenceSegment.getSecondNode().getEastNorth());
    471             g.drawLine(p1.x, p1.y, p2.x, p2.y);
     477            MapPath2D line = new MapPath2D();
     478            line.moveTo(mv.getState().getPointFor(referenceSegment.getFirstNode()));
     479            line.lineTo(mv.getState().getPointFor(referenceSegment.getSecondNode()));
     480            g.draw(line);
    472481
    473482            g.setStroke(helpLineStroke);
    474483            g.setColor(mainColor);
    475             p1 = mv.getPoint(helperLineStart);
    476             p2 = mv.getPoint(helperLineEnd);
    477             g.drawLine(p1.x, p1.y, p2.x, p2.y);
     484            line = new MapPath2D();
     485            line.moveTo(mv.getState().getPointFor(helperLineStart));
     486            line.lineTo(mv.getState().getPointFor(helperLineEnd));
     487            g.draw(line);
    478488        }
    479489    }
    480490
    481491    private boolean isModifiersValidForDragMode() {
    482         return (!alt && !shift && !ctrl) || matchesCurrentModifiers(snapModifierCombo)
    483                 || matchesCurrentModifiers(copyTagsModifierCombo);
     492        return (!alt && !shift && !ctrl) || matchesCurrentModifiers(SNAP_MODIFIER_COMBO)
     493                || matchesCurrentModifiers(COPY_TAGS_MODIFIER_COMBO);
    484494    }
    485495
    486496    private void updateFlagsOnlyChangeableOnPress() {
    487         copyTags = copyTagsDefault != matchesCurrentModifiers(copyTagsModifierCombo);
     497        copyTags = COPY_TAGS_DEFAULT.get().booleanValue() != matchesCurrentModifiers(COPY_TAGS_MODIFIER_COMBO);
    488498    }
    489499
    490500    private void updateFlagsChangeableAlways() {
    491         snap = snapDefault != matchesCurrentModifiers(snapModifierCombo);
     501        snap = SNAP_DEFAULT.get().booleanValue() != matchesCurrentModifiers(SNAP_MODIFIER_COMBO);
    492502    }
    493503
     
    564574    }
    565575
    566     private static String getStringPref(String subKey, String def) {
    567         return Main.pref.get(prefKey(subKey), def);
    568     }
    569 
    570     @Override
    571     public void preferenceChanged(PreferenceChangeEvent e) {
    572         if (e.getKey().startsWith(prefKey(""))) {
    573             updateAllPreferences();
    574         }
    575     }
    576 
    577     @Override
    578     public void destroy() {
    579         super.destroy();
    580         Main.pref.removePreferenceChangeListener(this);
     576    /**
     577     * A property that holds the keyboard modifiers.
     578     * @author Michael Zangl
     579     * @since 10869
     580     */
     581    private static class KeyboardModifiersProperty extends AbstractToStringProperty<Map<Modifier, Boolean>> {
     582
     583        KeyboardModifiersProperty(String key, String defaultValue) {
     584            super(key, createFromString(defaultValue));
     585        }
     586
     587        KeyboardModifiersProperty(String key, Map<Modifier, Boolean> defaultValue) {
     588            super(key, defaultValue);
     589        }
     590
     591        @Override
     592        protected String toString(Map<Modifier, Boolean> t) {
     593            StringBuilder sb = new StringBuilder();
     594            for (Modifier mod : Modifier.values()) {
     595                Boolean val = t.get(mod);
     596                if (val == null) {
     597                    sb.append('?');
     598                } else if (val) {
     599                    sb.append(Character.toUpperCase(mod.shortChar));
     600                } else {
     601                    sb.append(mod.shortChar);
     602                }
     603            }
     604            return sb.toString();
     605        }
     606
     607        @Override
     608        protected Map<Modifier, Boolean> fromString(String string) {
     609            return createFromString(string);
     610        }
     611
     612        private static Map<Modifier, Boolean> createFromString(String string) {
     613            Map<Modifier, Boolean> ret = new EnumMap<>(Modifier.class);
     614            for (char c : string.toCharArray()) {
     615                if (c == '?') {
     616                    continue;
     617                }
     618                Optional<Modifier> mod = Modifier.findWithShortCode(c);
     619                if (mod.isPresent()) {
     620                    ret.put(mod.get(), Character.isUpperCase(c));
     621                } else {
     622                    Main.debug("Ignoring unknown modifier {0}", c);
     623                }
     624            }
     625            return Collections.unmodifiableMap(ret);
     626        }
     627    }
     628
     629    private enum Modifier {
     630        CTRL('c'),
     631        ALT('a'),
     632        SHIFT('s');
     633
     634        private final char shortChar;
     635
     636        Modifier(char shortChar) {
     637            this.shortChar = Character.toLowerCase(shortChar);
     638        }
     639
     640        /**
     641         * Find the modifier with the given short code
     642         * @param charCode The short code
     643         * @return The modifier
     644         */
     645        public static Optional<Modifier> findWithShortCode(int charCode) {
     646            return Stream.of(values()).filter(m -> m.shortChar == Character.toLowerCase(charCode)).findAny();
     647        }
    581648    }
    582649}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/PaintColors.java

    r10824 r10869  
    6161    }
    6262
     63    /**
     64     * Gets the default value for this color.
     65     * @return The default value
     66     */
    6367    public Color getDefaultValue() {
    6468        return property.getDefaultValue();
    6569    }
    6670
     71    /**
     72     * Get the given color
     73     * @return The color
     74     */
    6775    public Color get() {
    6876        return property.get();
Note: See TracChangeset for help on using the changeset viewer.