Ticket #12642: 12642_updated.patch
| File 12642_updated.patch, 20.4 KB (added by , 10 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: images\dialogs\layerlist\visibility.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream
7 7 import java.awt.Component; 8 8 import java.awt.Dimension; 9 9 import java.awt.Font; 10 import java.awt.GridBagLayout; 10 11 import java.awt.Point; 11 12 import java.awt.Rectangle; 12 13 import java.awt.event.ActionEvent; … … 18 19 import java.lang.ref.WeakReference; 19 20 import java.util.ArrayList; 20 21 import java.util.Arrays; 22 import java.util.Collection; 21 23 import java.util.Collections; 22 24 import java.util.List; 23 25 import java.util.concurrent.CopyOnWriteArrayList; 24 26 25 27 import javax.swing.AbstractAction; 28 import javax.swing.BorderFactory; 26 29 import javax.swing.DefaultCellEditor; 27 30 import javax.swing.DefaultListSelectionModel; 28 31 import javax.swing.ImageIcon; … … 30 33 import javax.swing.JComponent; 31 34 import javax.swing.JLabel; 32 35 import javax.swing.JMenuItem; 36 import javax.swing.JPanel; 33 37 import javax.swing.JPopupMenu; 34 38 import javax.swing.JSlider; 35 39 import javax.swing.JTable; … … 66 70 import org.openstreetmap.josm.gui.widgets.JosmTextField; 67 71 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 68 72 import org.openstreetmap.josm.tools.CheckParameterUtil; 73 import org.openstreetmap.josm.tools.GBC; 69 74 import org.openstreetmap.josm.tools.ImageProvider; 70 75 import org.openstreetmap.josm.tools.InputMapUtils; 71 76 import org.openstreetmap.josm.tools.MultikeyActionsHandler; … … 263 268 MultikeyActionsHandler.getInstance().addAction(showHideLayerAction); 264 269 adaptTo(showHideLayerAction, selectionModel); 265 270 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); 277 275 278 276 // -- delete layer action 279 277 DeleteLayerAction deleteLayerAction = new DeleteLayerAction(); … … 300 298 new SideButton(moveUpAction, false), 301 299 new SideButton(moveDownAction, false), 302 300 new SideButton(activateLayerAction, false), 303 new SideButton(showHideLayerAction, false), 304 opacityButton, 305 gammaButton, 301 visibilityButton, 306 302 new SideButton(deleteLayerAction, false) 307 303 )); 308 304 … … 528 524 } 529 525 530 526 /** 531 * Abstract action which allows to adjust a double value using a slider 527 * This is a menu that includes all settings for the layer visibility. It combines gamma/opacity sliders and the visible-checkbox. 528 * 529 * @author Michael Zangl 532 530 */ 533 public abstract static class AbstractLayerPropertySliderAction extends AbstractAction implements IEnabledStateUpdating, LayerAction { 534 protected final LayerListModel model; 535 protected final JPopupMenu popup; 536 protected final JSlider slider; 537 private final double factor; 531 public static final class LayerVisibilityAction extends AbstractAction implements IEnabledStateUpdating, LayerAction { 532 protected static final int SLIDER_STEPS = 100; 533 private static final double MAX_GAMMA_FACTOR = 2; 534 private final LayerListModel model; 535 private final JPopupMenu popup; 536 private JSlider opacitySlider; 537 private JSlider gammaSlider; 538 538 private SideButton sideButton; 539 private JCheckBox visibilityCheckbox; 539 540 540 protected AbstractLayerPropertySliderAction(LayerListModel model, String name, final double factor) { 541 super(name); 541 /** 542 * Creates a new {@link LayerVisibilityAction} 543 * @param model The list to get the selection from. 544 */ 545 public LayerVisibilityAction(LayerListModel model) { 542 546 this.model = model; 543 this.factor = factor;544 updateEnabledState();545 546 547 popup = new JPopupMenu(); 547 slider = new JSlider(JSlider.VERTICAL); 548 slider.addChangeListener(new ChangeListener() { 548 549 // just to add a border 550 JPanel content = new JPanel(); 551 popup.add(content); 552 content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 553 content.setLayout(new GridBagLayout()); 554 555 putValue(SMALL_ICON, ImageProvider.get("dialogs/layerlist", "visibility")); 556 putValue(SHORT_DESCRIPTION, tr("Change visibility of the selected layer.")); 557 558 visibilityCheckbox = new JCheckBox(tr("Show layer")); 559 visibilityCheckbox.addChangeListener(new ChangeListener() { 549 560 @Override 550 561 public void stateChanged(ChangeEvent e) { 551 setV alue(slider.getValue() / factor);562 setVisible(visibilityCheckbox.isSelected()); 552 563 } 553 564 }); 554 popup.add(slider); 565 content.add(visibilityCheckbox, GBC.eop()); 566 567 content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "transparency")), GBC.std().span(1, 2).insets(0, 0, 5, 0)); 568 content.add(new JLabel(tr("Opacity")), GBC.eol()); 569 opacitySlider = new JSlider(JSlider.HORIZONTAL); 570 opacitySlider.setMaximum(SLIDER_STEPS); 571 opacitySlider.addChangeListener(new ChangeListener() { 572 @Override 573 public void stateChanged(ChangeEvent e) { 574 setOpacityValue(readOpacityValue(), opacitySlider.getValueIsAdjusting()); 575 } 576 }); 577 opacitySlider.setToolTipText(tr("Adjust opacity of the layer.")); 578 content.add(opacitySlider, GBC.eop()); 579 580 content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "gamma")), GBC.std().span(1, 2).insets(0, 0, 5, 0)); 581 content.add(new JLabel(tr("Gamma")), GBC.eol()); 582 gammaSlider = new JSlider(JSlider.HORIZONTAL); 583 gammaSlider.setMaximum(SLIDER_STEPS); 584 gammaSlider.addChangeListener(new ChangeListener() { 585 @Override 586 public void stateChanged(ChangeEvent e) { 587 setGammaValue(readGammaValue()); 588 } 589 }); 590 gammaSlider.setToolTipText(tr("Adjust gamma value of the layer.")); 591 content.add(gammaSlider, GBC.eol()); 555 592 } 556 593 557 protected abstract void setValue(double value); 594 protected double readOpacityValue() { 595 return (double) opacitySlider.getValue() / SLIDER_STEPS; 596 } 558 597 559 protected abstract double getValue(); 598 protected double readGammaValue() { 599 return (double) gammaSlider.getValue() / SLIDER_STEPS * MAX_GAMMA_FACTOR; 600 } 560 601 561 /** 562 * Sets the corresponding side button. 563 * @param sideButton the corresponding side button 564 */ 565 final void setCorrespondingSideButton(SideButton sideButton) { 566 this.sideButton = sideButton; 602 protected void setVisible(boolean visible) { 603 for (Layer l : model.getSelectedLayers()) { 604 l.setVisible(visible); 605 } 606 updateValues(); 607 } 608 609 protected void setOpacityValue(double value, boolean adjusting) { 610 if (value <= 0 && !adjusting) { 611 setVisible(false); 612 } else { 613 for (Layer l : model.getSelectedLayers()) { 614 l.setOpacity(value); 615 } 616 } 617 } 618 619 protected void setGammaValue(double value) { 620 for (ImageryLayer imageryLayer : Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class)) { 621 imageryLayer.setGamma(value); 622 } 567 623 } 568 624 569 625 @Override 570 626 public void actionPerformed(ActionEvent e) { 571 slider.setValue((int) (getValue() * factor));627 updateValues(); 572 628 if (e.getSource() == sideButton) { 573 629 popup.show(sideButton, 0, sideButton.getHeight()); 574 630 } else { … … 578 634 } 579 635 } 580 636 581 @Override 582 public Component createMenuComponent() { 583 return new JMenuItem(this); 584 } 585 } 637 protected void updateValues() { 638 List<Layer> layers = model.getSelectedLayers(); 586 639 587 /** 588 * Action which allows to change the opacity of one or more layers. 589 */ 590 public static final class LayerOpacityAction extends AbstractLayerPropertySliderAction { 591 private transient Layer layer; 640 visibilityCheckbox.setEnabled(!layers.isEmpty()); 641 boolean allVisible = true; 642 boolean allHidden = true; 643 for (Layer l : layers) { 644 allVisible &= l.isVisible(); 645 allHidden &= !l.isVisible(); 646 } 647 // TODO: Indicate tristate. 648 visibilityCheckbox.setSelected(allVisible && !allHidden); 592 649 593 /** 594 * Creates a {@link LayerOpacityAction} which allows to change the opacity of one or more layers. 595 * 596 * @param model layer list model 597 * @param layer the layer. Must not be null. 598 * @throws IllegalArgumentException if layer is null 599 */ 600 public LayerOpacityAction(LayerListModel model, Layer layer) { 601 this(model); 602 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 603 this.layer = layer; 604 updateEnabledState(); 605 } 650 updateOpacitySlider(layers, allHidden); 606 651 607 /** 608 * Creates a {@link ShowHideLayerAction} which will toggle the visibility of the currently selected layers 609 * @param model layer list model 610 */ 611 public LayerOpacityAction(LayerListModel model) { 612 super(model, tr("Opacity"), 100); 613 putValue(SHORT_DESCRIPTION, tr("Adjust opacity of the layer.")); 614 putValue(SMALL_ICON, ImageProvider.get("dialogs/layerlist", "transparency")); 652 updateGammaSlider(layers, allHidden); 615 653 } 616 654 617 @Override 618 protected void setValue(double value) { 619 if (!isEnabled()) return; 620 if (layer != null) { 621 layer.setOpacity(value); 655 private void updateGammaSlider(List<Layer> layers, boolean allHidden) { 656 Collection<ImageryLayer> gammaLayers = Utils.filteredCollection(layers, ImageryLayer.class); 657 if (gammaLayers.isEmpty() || allHidden) { 658 gammaSlider.setEnabled(false); 622 659 } else { 623 for (Layer l : model.getSelectedLayers()) {624 l.setOpacity(value);625 }660 gammaSlider.setEnabled(true); 661 double gamma = gammaLayers.iterator().next().getGamma(); 662 gammaSlider.setValue((int) (gamma * SLIDER_STEPS / MAX_GAMMA_FACTOR)); 626 663 } 627 664 } 628 665 629 @Override630 protected double getValue() {631 if (layer != null)632 return layer.getOpacity();633 else {666 private void updateOpacitySlider(List<Layer> layers, boolean allHidden) { 667 if (layers.isEmpty() || allHidden) { 668 opacitySlider.setEnabled(false); 669 } else { 670 opacitySlider.setEnabled(true); 634 671 double opacity = 0; 635 List<Layer> layers = model.getSelectedLayers();636 672 for (Layer l : layers) { 637 673 opacity += l.getOpacity(); 638 674 } 639 return opacity / layers.size(); 640 } 641 } 642 643 @Override 644 public void updateEnabledState() { 645 if (layer == null) { 646 setEnabled(!model.getSelectedLayers().isEmpty()); 647 } else { 648 setEnabled(true); 675 opacity /= layers.size(); 676 if (opacity == 0) { 677 opacity = 1; 678 setOpacityValue(opacity, false); 679 } 680 opacitySlider.setValue((int) (opacity * SLIDER_STEPS)); 649 681 } 650 682 } 651 683 652 684 @Override 653 685 public boolean supportLayers(List<Layer> layers) { 654 return true; 655 } 656 } 657 658 /** 659 * Action which allows to change the gamma of one imagery layer. 660 */ 661 public static final class LayerGammaAction extends AbstractLayerPropertySliderAction { 662 663 /** 664 * Constructs a new {@code LayerGammaAction}. 665 * @param model layer list model 666 */ 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")); 671 } 672 673 @Override 674 protected void setValue(double value) { 675 for (ImageryLayer imageryLayer : Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class)) { 676 imageryLayer.setGamma(value); 677 } 686 return !layers.isEmpty(); 678 687 } 679 688 680 689 @Override 681 p rotected double getValue() {682 return Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class).iterator().next().getGamma();690 public Component createMenuComponent() { 691 return new JMenuItem(this); 683 692 } 684 693 685 694 @Override 686 695 public void updateEnabledState() { 687 setEnabled(! Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class).isEmpty());696 setEnabled(!model.getSelectedLayers().isEmpty()); 688 697 } 689 698 690 @Override 691 public boolean supportLayers(List<Layer> layers) { 692 return !Utils.filteredCollection(layers, ImageryLayer.class).isEmpty(); 699 /** 700 * Sets the corresponding side button. 701 * @param sideButton the corresponding side button 702 */ 703 void setCorrespondingSideButton(SideButton sideButton) { 704 this.sideButton = sideButton; 693 705 } 694 706 } 695 707 -
src/org/openstreetmap/josm/gui/layer/Layer.java
360 360 return visible && opacity != 0; 361 361 } 362 362 363 /** 364 * Gets the opacity of the layer, in range 0...1 365 * @return The opacity 366 */ 363 367 public double getOpacity() { 364 368 return opacity; 365 369 } 366 370 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 */ 367 376 public void setOpacity(double opacity) { 368 377 if (!(opacity >= 0 && opacity <= 1)) 369 378 throw new IllegalArgumentException("Opacity value must be between 0 and 1"); -
test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java
2 2 package org.openstreetmap.josm.gui.dialogs; 3 3 4 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertFalse; 5 6 import static org.junit.Assert.assertTrue; 6 7 7 8 import org.junit.BeforeClass; 8 9 import org.junit.Test; 9 10 import org.openstreetmap.josm.JOSMFixture; 10 11 import org.openstreetmap.josm.Main; 11 import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerGammaAction;12 12 import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerListModel; 13 import org.openstreetmap.josm.gui.dialogs.LayerListDialog.Layer OpacityAction;13 import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerVisibilityAction; 14 14 import org.openstreetmap.josm.gui.layer.TMSLayer; 15 15 import org.openstreetmap.josm.gui.layer.TMSLayerTest; 16 16 … … 28 28 } 29 29 30 30 /** 31 * Unit test of {@link Layer GammaAction} class.31 * Unit test of {@link LayerVisibilityAction} class. 32 32 */ 33 33 @Test 34 public void testLayer GammaAction() {34 public void testLayerVisibilityAction() { 35 35 TMSLayer layer = TMSLayerTest.createTmsLayer(); 36 36 try { 37 Main.map.mapView.addLayer(layer);38 37 LayerListModel model = LayerListDialog.getInstance().getModel(); 39 Layer GammaAction action = new LayerGammaAction(model);38 LayerVisibilityAction action = new LayerVisibilityAction(model); 40 39 action.updateEnabledState(); 41 assertTrue(action.isEnabled()); 42 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); 46 } finally { 47 Main.map.mapView.removeLayer(layer); 48 } 49 } 40 assertFalse(action.isEnabled()); 50 41 51 /**52 * Unit test of {@link LayerOpacityAction} class.53 */54 @Test55 public void testLayerOpacityAction() {56 TMSLayer layer = TMSLayerTest.createTmsLayer();57 try {58 42 Main.map.mapView.addLayer(layer); 59 LayerListModel model = LayerListDialog.getInstance().getModel();60 LayerOpacityAction action = new LayerOpacityAction(model);61 43 action.updateEnabledState(); 62 44 assertTrue(action.isEnabled()); 63 45 assertTrue(action.supportLayers(model.getSelectedLayers())); 64 assertEquals(1.0, action.getValue(), 1e-15); 65 action.setValue(0.5); 66 assertEquals(0.5, action.getValue(), 1e-15); 46 47 // now check values 48 action.updateValues(); 49 assertEquals(1.0, action.readOpacityValue(), 1e-15); 50 assertEquals(1.0, action.readGammaValue(), 1e-15); 51 52 action.setOpacityValue(.5, false); 53 action.setGammaValue(1.5); 54 action.updateValues(); 55 56 assertEquals(0.5, action.readOpacityValue(), 1e-15); 57 assertEquals(1.5, action.readGammaValue(), 1e-15); 58 59 action.setVisible(false); 60 action.updateValues(); 61 assertFalse(layer.isVisible()); 62 63 action.setVisible(true); 64 action.updateValues(); 65 assertTrue(layer.isVisible()); 66 67 // layer stays visible during adjust 68 action.setOpacityValue(0, true); 69 assertEquals(0, layer.getOpacity(), 1e-15); 70 layer.setOpacity(.1); // to make layer.isVisible work 71 assertTrue(layer.isVisible()); 72 layer.setOpacity(0); 73 74 action.setOpacityValue(0, false); 75 assertEquals(0, layer.getOpacity(), 1e-15); 76 layer.setOpacity(.1); // to make layer.isVisible work 77 assertFalse(layer.isVisible()); 78 layer.setOpacity(0); 79 action.updateValues(); 80 81 // Opacity reset when it was 0 and user set layer to visible. 82 action.setVisible(true); 83 action.updateValues(); 84 assertEquals(1.0, action.readOpacityValue(), 1e-15); 85 assertEquals(1.0, layer.getOpacity(), 1e-15); 86 67 87 } finally { 68 88 Main.map.mapView.removeLayer(layer); 69 89 }
