Ticket #12642: patch-layer-filter-settings.patch

File patch-layer-filter-settings.patch, 19.1 KB (added by michael2402, 8 years ago)
  • src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java

    diff --git a/images/dialogs/layerlist/visibility.png b/images/dialogs/layerlist/visibility.png
    new file mode 100644
    index 0000000..dd019e8
    Binary files /dev/null and b/images/dialogs/layerlist/visibility.png differ
    diff --git a/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java b/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
    index af5f61c..793abfb 100644
    a b import java.awt.Color;  
    77import java.awt.Component;
    88import java.awt.Dimension;
    99import java.awt.Font;
     10import java.awt.GridBagLayout;
    1011import java.awt.Point;
    1112import java.awt.Rectangle;
    1213import java.awt.event.ActionEvent;
    import java.beans.PropertyChangeListener;  
    1819import java.lang.ref.WeakReference;
    1920import java.util.ArrayList;
    2021import java.util.Arrays;
     22import java.util.Collection;
    2123import java.util.Collections;
    2224import java.util.List;
    2325import java.util.concurrent.CopyOnWriteArrayList;
    2426
    2527import javax.swing.AbstractAction;
     28import javax.swing.BorderFactory;
    2629import javax.swing.DefaultCellEditor;
    2730import javax.swing.DefaultListSelectionModel;
    2831import javax.swing.ImageIcon;
    import javax.swing.JCheckBox;  
    3033import javax.swing.JComponent;
    3134import javax.swing.JLabel;
    3235import javax.swing.JMenuItem;
     36import javax.swing.JPanel;
    3337import javax.swing.JPopupMenu;
    3438import javax.swing.JSlider;
    3539import javax.swing.JTable;
    import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField  
    6670import org.openstreetmap.josm.gui.widgets.JosmTextField;
    6771import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
    6872import org.openstreetmap.josm.tools.CheckParameterUtil;
     73import org.openstreetmap.josm.tools.GBC;
    6974import org.openstreetmap.josm.tools.ImageProvider;
    7075import org.openstreetmap.josm.tools.InputMapUtils;
    7176import org.openstreetmap.josm.tools.MultikeyActionsHandler;
    public class LayerListDialog extends ToggleDialog {  
    263268        MultikeyActionsHandler.getInstance().addAction(showHideLayerAction);
    264269        adaptTo(showHideLayerAction, selectionModel);
    265270
    266         // -- layer opacity action
    267         LayerOpacityAction layerOpacityAction = new LayerOpacityAction(model);
    268         adaptTo(layerOpacityAction, selectionModel);
    269         SideButton opacityButton = new SideButton(layerOpacityAction, false);
    270         layerOpacityAction.setCorrespondingSideButton(opacityButton);
    271 
    272         // -- layer gamma action
    273         LayerGammaAction layerGammaAction = new LayerGammaAction(model);
    274         adaptTo(layerGammaAction, selectionModel);
    275         SideButton gammaButton = new SideButton(layerGammaAction, false);
    276         layerGammaAction.setCorrespondingSideButton(gammaButton);
     271        LayerVisibilityAction visibilityAction = new LayerVisibilityAction(model);
     272        adaptTo(visibilityAction, selectionModel);
     273        SideButton visibilityButton = new SideButton(visibilityAction, false);
     274        visibilityAction.setCorrespondingSideButton(visibilityButton);
    277275
    278276        // -- delete layer action
    279277        DeleteLayerAction deleteLayerAction = new DeleteLayerAction();
    public class LayerListDialog extends ToggleDialog {  
    300298                new SideButton(moveUpAction, false),
    301299                new SideButton(moveDownAction, false),
    302300                new SideButton(activateLayerAction, false),
    303                 new SideButton(showHideLayerAction, false),
    304                 opacityButton,
    305                 gammaButton,
     301                visibilityButton,
    306302                new SideButton(deleteLayerAction, false)
    307303        ));
    308304
    public class LayerListDialog extends ToggleDialog {  
    605601        }
    606602
    607603        /**
    608          * Creates a {@link ShowHideLayerAction} which will toggle the visibility of the currently selected layers
     604         * Creates a {@link LayerOpacityAction} which will toggle the visibility of layers
    609605         * @param model layer list model
    610606         */
    611         public LayerOpacityAction(LayerListModel model) {
     607        private LayerOpacityAction(LayerListModel model) {
    612608            super(model, tr("Opacity"), 100);
    613609            putValue(SHORT_DESCRIPTION, tr("Adjust opacity of the layer."));
    614610            putValue(SMALL_ICON, ImageProvider.get("dialogs/layerlist", "transparency"));
    public class LayerListDialog extends ToggleDialog {  
    616612
    617613        @Override
    618614        protected void setValue(double value) {
    619             if (!isEnabled()) return;
    620             if (layer != null) {
    621                 layer.setOpacity(value);
    622             } else {
    623                 for (Layer l : model.getSelectedLayers()) {
    624                     l.setOpacity(value);
    625                 }
    626             }
     615            layer.setOpacity(value);
    627616        }
    628617
    629618        @Override
    630619        protected double getValue() {
    631             if (layer != null)
    632                 return layer.getOpacity();
    633             else {
    634                 double opacity = 0;
    635                 List<Layer> layers = model.getSelectedLayers();
    636                 for (Layer l : layers) {
    637                     opacity += l.getOpacity();
    638                 }
    639                 return opacity / layers.size();
    640             }
     620            return layer.getOpacity();
    641621        }
    642622
    643623        @Override
    644624        public void updateEnabledState() {
    645             if (layer == null) {
    646                 setEnabled(!model.getSelectedLayers().isEmpty());
    647             } else {
    648                 setEnabled(true);
    649             }
     625            setEnabled(true);
    650626        }
    651627
    652628        @Override
    public class LayerListDialog extends ToggleDialog {  
    656632    }
    657633
    658634    /**
    659      * Action which allows to change the gamma of one imagery layer.
     635     * This is a menu that includes all settings for the layer visibility. It combines gamma/opacity sliders and the visible-checkbox.
     636     *
     637     * @author Michael Zangl
    660638     */
    661     public static final class LayerGammaAction extends AbstractLayerPropertySliderAction {
     639    public static final class LayerVisibilityAction extends AbstractAction implements IEnabledStateUpdating, LayerAction {
     640        protected static final int SLIDER_STEPS = 100;
     641        private static final double MAX_GAMMA_FACTOR = 2;
     642        private final LayerListModel model;
     643        private final JPopupMenu popup;
     644        private JSlider opacitySlider;
     645        private JSlider gammaSlider;
     646        private SideButton sideButton;
     647        private JCheckBox visibilityCheckbox;
    662648
    663649        /**
    664          * Constructs a new {@code LayerGammaAction}.
    665          * @param model layer list model
     650         * Creates a new {@link LayerVisibilityAction}
     651         * @param model The list to get the selection from.
    666652         */
    667         public LayerGammaAction(LayerListModel model) {
    668             super(model, tr("Gamma"), 50);
    669             putValue(SHORT_DESCRIPTION, tr("Adjust gamma value of the layer."));
    670             putValue(SMALL_ICON, ImageProvider.get("dialogs/layerlist", "gamma"));
     653        public LayerVisibilityAction(LayerListModel model) {
     654            this.model = model;
     655            popup = new JPopupMenu();
     656
     657            // just to add a border
     658            JPanel content = new JPanel();
     659            popup.add(content);
     660            content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
     661            content.setLayout(new GridBagLayout());
     662
     663            putValue(SMALL_ICON, ImageProvider.get("dialogs/layerlist", "visibility"));
     664            putValue(SHORT_DESCRIPTION, tr("Change visibility of the selected layer."));
     665
     666            visibilityCheckbox = new JCheckBox(tr("Show layer"));
     667            visibilityCheckbox.addChangeListener(new ChangeListener() {
     668                @Override
     669                public void stateChanged(ChangeEvent e) {
     670                    setVisible(visibilityCheckbox.isSelected());
     671                }
     672            });
     673            content.add(visibilityCheckbox, GBC.eop());
     674
     675            content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "transparency")), GBC.std().span(1, 2).insets(0, 0, 5, 0));
     676            content.add(new JLabel(tr("Opacity")), GBC.eol());
     677            opacitySlider = new JSlider(JSlider.HORIZONTAL);
     678            opacitySlider.setMaximum(SLIDER_STEPS);
     679            opacitySlider.addChangeListener(new ChangeListener() {
     680                @Override
     681                public void stateChanged(ChangeEvent e) {
     682                    setOpacityValue(readOpacityValue(), opacitySlider.getValueIsAdjusting());
     683                }
     684            });
     685            opacitySlider.setToolTipText(tr("Adjust opacity of the layer."));
     686            content.add(opacitySlider, GBC.eop());
     687
     688            content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "gamma")), GBC.std().span(1, 2).insets(0, 0, 5, 0));
     689            content.add(new JLabel(tr("Gamma")), GBC.eol());
     690            gammaSlider = new JSlider(JSlider.HORIZONTAL);
     691            gammaSlider.setMaximum(SLIDER_STEPS);
     692            gammaSlider.addChangeListener(new ChangeListener() {
     693                @Override
     694                public void stateChanged(ChangeEvent e) {
     695                    setGammaValue(readGammaValue());
     696                }
     697            });
     698            gammaSlider.setToolTipText(tr("Adjust gamma value of the layer."));
     699            content.add(gammaSlider, GBC.eol());
    671700        }
    672701
    673         @Override
    674         protected void setValue(double value) {
     702        protected double readOpacityValue() {
     703            return (double) opacitySlider.getValue() / SLIDER_STEPS;
     704        }
     705
     706        protected double readGammaValue() {
     707            return (double) gammaSlider.getValue() / SLIDER_STEPS * MAX_GAMMA_FACTOR;
     708        }
     709
     710        protected void setVisible(boolean visible) {
     711            for (Layer l : model.getSelectedLayers()) {
     712                l.setVisible(visible);
     713            }
     714            updateValues();
     715        }
     716
     717        protected void setOpacityValue(double value, boolean adjusting) {
     718            if (value <= 0 && !adjusting) {
     719                setVisible(false);
     720            } else {
     721                for (Layer l : model.getSelectedLayers()) {
     722                    l.setOpacity(value);
     723                }
     724            }
     725        }
     726
     727        protected void setGammaValue(double value) {
    675728            for (ImageryLayer imageryLayer : Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class)) {
    676729                imageryLayer.setGamma(value);
    677730            }
    678731        }
    679732
    680733        @Override
    681         protected double getValue() {
    682             return Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class).iterator().next().getGamma();
     734        public void actionPerformed(ActionEvent e) {
     735            updateValues();
     736            if (e.getSource() == sideButton) {
     737                popup.show(sideButton, 0, sideButton.getHeight());
     738            } else {
     739                // Action can be trigger either by opacity button or by popup menu (in case toggle buttons are hidden).
     740                // In that case, show it in the middle of screen (because opacityButton is not visible)
     741                popup.show(Main.parent, Main.parent.getWidth() / 2, (Main.parent.getHeight() - popup.getHeight()) / 2);
     742            }
    683743        }
    684744
    685         @Override
    686         public void updateEnabledState() {
    687             setEnabled(!Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class).isEmpty());
     745        protected void updateValues() {
     746            List<Layer> layers = model.getSelectedLayers();
     747
     748            visibilityCheckbox.setEnabled(!layers.isEmpty());
     749            boolean allVisible = true;
     750            boolean allHidden = true;
     751            for (Layer l : layers) {
     752                allVisible &= l.isVisible();
     753                allHidden &= !l.isVisible();
     754            }
     755            // TODO: Indicate tristate.
     756            visibilityCheckbox.setSelected(allVisible && !allHidden);
     757
     758            updateOpacitySlider(layers, allHidden);
     759
     760            updateGammaSlider(layers, allHidden);
     761        }
     762
     763        private void updateGammaSlider(List<Layer> layers, boolean allHidden) {
     764            Collection<ImageryLayer> gammaLayers = Utils.filteredCollection(layers, ImageryLayer.class);
     765            if (gammaLayers.isEmpty() || allHidden) {
     766                gammaSlider.setEnabled(false);
     767            } else {
     768                gammaSlider.setEnabled(true);
     769                double gamma = gammaLayers.iterator().next().getGamma();
     770                gammaSlider.setValue((int) (gamma * SLIDER_STEPS / MAX_GAMMA_FACTOR));
     771            }
     772        }
     773
     774        private void updateOpacitySlider(List<Layer> layers, boolean allHidden) {
     775            if (layers.isEmpty() || allHidden) {
     776                opacitySlider.setEnabled(false);
     777            } else {
     778                opacitySlider.setEnabled(true);
     779                double opacity = 0;
     780                for (Layer l : layers) {
     781                    opacity += l.getOpacity();
     782                }
     783                opacity /= layers.size();
     784                if (opacity == 0) {
     785                    opacity = 1;
     786                    setOpacityValue(opacity, false);
     787                }
     788                opacitySlider.setValue((int) (opacity * SLIDER_STEPS));
     789            }
    688790        }
    689791
    690792        @Override
    691793        public boolean supportLayers(List<Layer> layers) {
    692             return !Utils.filteredCollection(layers, ImageryLayer.class).isEmpty();
     794            return !layers.isEmpty();
     795        }
     796
     797        @Override
     798        public Component createMenuComponent() {
     799            return new JMenuItem(this);
     800        }
     801
     802        @Override
     803        public void updateEnabledState() {
     804            setEnabled(!model.getSelectedLayers().isEmpty());
     805        }
     806
     807        /**
     808         * Sets the corresponding side button.
     809         * @param sideButton the corresponding side button
     810         */
     811        void setCorrespondingSideButton(SideButton sideButton) {
     812            this.sideButton = sideButton;
    693813        }
    694814    }
    695815
  • src/org/openstreetmap/josm/gui/layer/Layer.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/Layer.java b/src/org/openstreetmap/josm/gui/layer/Layer.java
    index 26842aa..d8fdd5c 100644
    a b public abstract class Layer implements Destroyable, MapViewPaintable, Projection  
    360360        return visible && opacity != 0;
    361361    }
    362362
     363    /**
     364     * Gets the opacity of the layer, in range 0...1
     365     * @return The opacity
     366     */
    363367    public double getOpacity() {
    364368        return opacity;
    365369    }
    366370
     371    /**
     372     * Sets the opacity of the layer, in range 0...1
     373     * @param opacity The opacity
     374     * @throws IllegalArgumentException if the opacity is out of range
     375     */
    367376    public void setOpacity(double opacity) {
    368377        if (!(opacity >= 0 && opacity <= 1))
    369378            throw new IllegalArgumentException("Opacity value must be between 0 and 1");
  • test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java

    diff --git a/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java b/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java
    index 74100c7..33b7e6f 100644
    a b  
    22package org.openstreetmap.josm.gui.dialogs;
    33
    44import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertFalse;
    56import static org.junit.Assert.assertTrue;
    67
    78import org.junit.BeforeClass;
    89import org.junit.Test;
    910import org.openstreetmap.josm.JOSMFixture;
    1011import org.openstreetmap.josm.Main;
    11 import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerGammaAction;
    1212import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerListModel;
    1313import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerOpacityAction;
     14import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerVisibilityAction;
    1415import org.openstreetmap.josm.gui.layer.TMSLayer;
    1516import org.openstreetmap.josm.gui.layer.TMSLayerTest;
    1617
    public class LayerListDialogTest {  
    2829    }
    2930
    3031    /**
    31      * Unit test of {@link LayerGammaAction} class.
     32     * Unit test of {@link LayerVisibilityAction} class.
    3233     */
    3334    @Test
    34     public void testLayerGammaAction() {
     35    public void testLayerVisibilityAction() {
    3536        TMSLayer layer = TMSLayerTest.createTmsLayer();
    3637        try {
    37             Main.map.mapView.addLayer(layer);
    3838            LayerListModel model = LayerListDialog.getInstance().getModel();
    39             LayerGammaAction action = new LayerGammaAction(model);
     39            LayerVisibilityAction action = new LayerVisibilityAction(model);
     40            action.updateEnabledState();
     41            assertFalse(action.isEnabled());
     42
     43            Main.map.mapView.addLayer(layer);
    4044            action.updateEnabledState();
    4145            assertTrue(action.isEnabled());
    4246            assertTrue(action.supportLayers(model.getSelectedLayers()));
    43             assertEquals(1.0, action.getValue(), 1e-15);
    44             action.setValue(0.5);
    45             assertEquals(0.5, action.getValue(), 1e-15);
     47
     48            // now check values
     49            action.updateValues();
     50            assertEquals(1.0, action.readOpacityValue(), 1e-15);
     51            assertEquals(1.0, action.readGammaValue(), 1e-15);
     52
     53            action.setOpacityValue(.5, false);
     54            action.setGammaValue(1.5);
     55            action.updateValues();
     56
     57            assertEquals(0.5, action.readOpacityValue(), 1e-15);
     58            assertEquals(1.5, action.readGammaValue(), 1e-15);
     59
     60            action.setVisible(false);
     61            action.updateValues();
     62            assertFalse(layer.isVisible());
     63
     64            action.setVisible(true);
     65            action.updateValues();
     66            assertTrue(layer.isVisible());
     67
     68            // layer stays visible during adjust
     69            action.setOpacityValue(0, true);
     70            assertEquals(0, layer.getOpacity(), 1e-15);
     71            layer.setOpacity(.1); // to make layer.isVisible work
     72            assertTrue(layer.isVisible());
     73            layer.setOpacity(0);
     74
     75            action.setOpacityValue(0, false);
     76            assertEquals(0, layer.getOpacity(), 1e-15);
     77            layer.setOpacity(.1); // to make layer.isVisible work
     78            assertFalse(layer.isVisible());
     79            layer.setOpacity(0);
     80            action.updateValues();
     81
     82            // Opacity reset when it was 0 and user set layer to visible.
     83            action.setVisible(true);
     84            action.updateValues();
     85            assertEquals(1.0, action.readOpacityValue(), 1e-15);
     86            assertEquals(1.0, layer.getOpacity(), 1e-15);
     87
    4688        } finally {
    4789            Main.map.mapView.removeLayer(layer);
    4890        }
    4991    }
    5092
    5193    /**
    52      * Unit test of {@link LayerOpacityAction} class.
     94     * Unit test of {@link LayerOpacityAction} class for single layer.
    5395     */
    5496    @Test
    5597    public void testLayerOpacityAction() {
    public class LayerListDialogTest {  
    5799        try {
    58100            Main.map.mapView.addLayer(layer);
    59101            LayerListModel model = LayerListDialog.getInstance().getModel();
    60             LayerOpacityAction action = new LayerOpacityAction(model);
     102            LayerOpacityAction action = new LayerOpacityAction(model, layer);
    61103            action.updateEnabledState();
    62104            assertTrue(action.isEnabled());
    63105            assertTrue(action.supportLayers(model.getSelectedLayers()));