Ticket #21605: 21605.1.patch

File 21605.1.patch, 10.9 KB (added by taylor.smock, 3 years ago)

Better UI (shows preview of images)

  • src/org/openstreetmap/josm/data/ImageData.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/data/ImageData.java b/src/org/openstreetmap/josm/data/ImageData.java
    a b  
    1010import org.openstreetmap.josm.data.coor.LatLon;
    1111import org.openstreetmap.josm.data.gpx.GpxImageEntry;
    1212import org.openstreetmap.josm.data.osm.QuadBuckets;
     13import org.openstreetmap.josm.gui.layer.Layer;
    1314import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry;
    1415import org.openstreetmap.josm.tools.ListenerList;
    1516
     
    4142
    4243    private final ListenerList<ImageDataUpdateListener> listeners = ListenerList.create();
    4344    private final QuadBuckets<ImageEntry> geoImages = new QuadBuckets<>();
     45    private Layer layer;
     46
    4447
    4548    /**
    4649     * Construct a new image container without images
     
    375378        notifyImageUpdate();
    376379    }
    377380
     381    /**
     382     * Set the layer for use with {@link org.openstreetmap.josm.gui.layer.geoimage.ImageViewerDialog#displayImages(Layer, List)}
     383     * @param layer The layer to use for organization
     384     * @since xxx
     385     */
     386    public void setLayer(Layer layer) {
     387        this.layer = layer;
     388    }
     389
     390    /**
     391     * Get the layer that this data is associated with. May be {@code null}.
     392     * @return The layer this data is associated with.
     393     * @since xxx
     394     */
     395    public Layer getLayer() {
     396        return this.layer;
     397    }
     398
     399
    378400    /**
    379401     * Add a listener that listens to image data changes
    380402     * @param listener the {@link ImageDataUpdateListener}
  • src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java b/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
    a b  
    171171        this.gpxData = gpxData;
    172172        this.useThumbs = useThumbs;
    173173        this.data.addImageDataUpdateListener(this);
     174        this.data.setLayer(this);
    174175    }
    175176
    176177    private final class ImageMouseListener extends MouseAdapter {
     
    231232                    }
    232233                } else {
    233234                    data.setSelectedImage(img);
     235                    ImageViewerDialog.getInstance().displayImages(GeoImageLayer.this, Collections.singletonList(img));
    234236                }
    235237            }
    236238        }
     
    521523     * Show current photo on map and in image viewer.
    522524     */
    523525    public void showCurrentPhoto() {
    524         if (data.getSelectedImage() != null) {
    525             clearOtherCurrentPhotos();
    526         }
    527526        updateBufferAndRepaint();
    528527    }
    529528
     
    628627        }
    629628    }
    630629
    631     /**
    632      * Clears the currentPhoto of the other GeoImageLayer's. Otherwise there could be multiple selected photos.
    633      */
    634     private void clearOtherCurrentPhotos() {
    635         for (GeoImageLayer layer:
    636                  MainApplication.getLayerManager().getLayersOfType(GeoImageLayer.class)) {
    637             if (layer != this) {
    638                 layer.getImageData().clearSelectedImage();
    639             }
    640         }
    641     }
    642 
    643630    /**
    644631     * Registers a map mode for which the functionality of this layer should be available.
    645632     * @param mapMode Map mode to be registered
  • src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
    a b  
    88import java.awt.BorderLayout;
    99import java.awt.Component;
    1010import java.awt.Dimension;
     11import java.awt.FlowLayout;
    1112import java.awt.GridBagConstraints;
    1213import java.awt.GridBagLayout;
    1314import java.awt.event.ActionEvent;
    1415import java.awt.event.KeyEvent;
    1516import java.awt.event.WindowEvent;
     17import java.awt.image.BufferedImage;
    1618import java.io.IOException;
    1719import java.io.Serializable;
    1820import java.time.ZoneOffset;
     
    2123import java.util.ArrayList;
    2224import java.util.Arrays;
    2325import java.util.Collections;
     26import java.util.HashMap;
    2427import java.util.List;
     28import java.util.Map;
    2529import java.util.Objects;
    2630import java.util.Optional;
    2731import java.util.concurrent.Future;
     
    3034
    3135import javax.swing.AbstractAction;
    3236import javax.swing.Box;
     37import javax.swing.ImageIcon;
    3338import javax.swing.JButton;
     39import javax.swing.JComponent;
    3440import javax.swing.JLabel;
    3541import javax.swing.JOptionPane;
    3642import javax.swing.JPanel;
     
    5258import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
    5359import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
    5460import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
     61import org.openstreetmap.josm.gui.layer.MainLayerManager;
    5562import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
    5663import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
    5764import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
     
    120127    private JButton btnOpenExternal;
    121128    private JButton btnDeleteFromDisk;
    122129    private JToggleButton tbCentre;
     130    /** The layer tab (used to select images when multiple layers provide images, makes for easy switching) */
     131    private JPanel layers;
    123132
    124133    private ImageViewerDialog() {
    125134        super(tr("Geotagged Images"), "geoimage", tr("Display geotagged images"), Shortcut.registerShortcut("tools:geotagged",
     
    152161
    153162    private void build() {
    154163        JPanel content = new JPanel(new BorderLayout());
     164        this.layers = new JPanel(new FlowLayout(FlowLayout.LEADING));
     165        content.add(layers, BorderLayout.NORTH);
    155166
    156167        content.add(imgDisplay, BorderLayout.CENTER);
    157168
     
    213224        createLayout(content, false, null);
    214225    }
    215226
     227    private void updateLayers() {
     228        if (this.tabbedEntries.size() <= 1) {
     229            this.layers.setVisible(false);
     230        } else {
     231            final IImageEntry<?> current;
     232            synchronized (this) {
     233                current = this.currentEntry;
     234            }
     235            this.layers.setVisible(true);
     236            // Remove all old components
     237            this.layers.removeAll();
     238            MainLayerManager layerManager = MainApplication.getLayerManager();
     239            List<Layer> invalidLayers = this.tabbedEntries.keySet().stream().filter(layer -> !layerManager.containsLayer(layer))
     240                    .collect(Collectors.toList());
     241            // We need to do multiple calls to avoid ConcurrentModificationExceptions
     242            invalidLayers.forEach(this.tabbedEntries::remove);
     243            List<JButton> layerButtons = new ArrayList<>(this.tabbedEntries.size());
     244            for (Map.Entry<Layer, List<IImageEntry<?>>> entry : this.tabbedEntries.entrySet()) {
     245                JButton layerButton = new JButton(tr(entry.getKey().getLabel()));
     246                layerButton.addActionListener(l -> this.displayImages(entry.getKey(), entry.getValue()));
     247                if (!this.isDocked && entry.getValue().size() == 1) {
     248                    try {
     249                        BufferedImage image = entry.getValue().get(0).read(ImageProvider.ImageSizes.SETTINGS_TAB.getImageDimension());
     250                        ImageIcon imageIcon = new ImageIcon(image);
     251                        layerButton.setIcon(imageIcon);
     252                    } catch (IOException e) {
     253                        Logging.trace(e);
     254                    }
     255                }
     256                layerButtons.add(layerButton);
     257                if (entry.getValue().contains(current)) {
     258                    // Disable button since it is currently selected. Also fixes an issue where clicking on the layer with the current image
     259                    // would prevent updates
     260                    layerButton.setEnabled(false);
     261                }
     262            }
     263            layerButtons.forEach(this.layers::add);
     264            int maxPreferredHeight = layerButtons.stream().mapToInt(JComponent::getHeight).max().orElse(Integer.MIN_VALUE);
     265            if (maxPreferredHeight > 0) {
     266                layerButtons.forEach(button -> button.setPreferredSize(new Dimension(button.getPreferredSize().width, maxPreferredHeight)));
     267            }
     268            this.layers.invalidate();
     269        }
     270    }
     271
     272
    216273    @Override
    217274    public void destroy() {
    218275        MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
     
    551608        return wasEnabled;
    552609    }
    553610
     611    /** Used for tabbed panes */
     612    private final transient Map<Layer, List<IImageEntry<?>>> tabbedEntries = new HashMap<>();
    554613    private transient IImageEntry<? extends IImageEntry<?>> currentEntry;
    555614
    556615    /**
     
    578637     * @since 18246
    579638     */
    580639    public void displayImages(List<IImageEntry<?>> entries) {
     640        this.displayImages((Layer) null, entries);
     641    }
     642
     643    /**
     644     * Displays images for the given layer.
     645     * @param layer The layer to use for the tab ui
     646     * @param entries image entries
     647     * @since xxx
     648     */
     649    public void displayImages(Layer layer, List<IImageEntry<?>> entries) {
    581650        boolean imageChanged;
    582651        IImageEntry<?> entry = entries != null && entries.size() == 1 ? entries.get(0) : null;
    583652
     
    598667            }
    599668        }
    600669
     670        if (entries == null || entries.isEmpty()) {
     671            this.tabbedEntries.remove(layer);
     672        } else {
     673            this.tabbedEntries.put(layer, entries);
     674        }
     675        this.updateLayers();
    601676        if (entry != null) {
    602677            this.updateButtonsNonNullEntry(entry, imageChanged);
    603678        } else {
     
    730805        if (btnCollapse != null) {
    731806            btnCollapse.setVisible(!isDocked);
    732807        }
     808        this.updateLayers();
    733809    }
    734810
    735811    /**
     
    819895
    820896    @Override
    821897    public void selectedImageChanged(ImageData data) {
    822         displayImages(new ArrayList<>(data.getSelectedImages()));
     898        displayImages(data.getLayer(), new ArrayList<>(data.getSelectedImages()));
    823899    }
    824900
    825901    @Override
    826902    public void imageDataUpdated(ImageData data) {
    827         displayImages(new ArrayList<>(data.getSelectedImages()));
     903        displayImages(data.getLayer(), new ArrayList<>(data.getSelectedImages()));
    828904    }
    829905}