Ticket #11905: patch_multiple_image_core.patch

File patch_multiple_image_core.patch, 37.8 KB (added by francois2, 5 years ago)

Patch select multiple image in the core v2.1

  • src/org/openstreetmap/josm/actions/mapmode/SelectAction.java

    diff --git a/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java b/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
    index 48873a20c..e749ed44c 100644
    a b public class SelectAction extends MapMode implements ModifierExListener, KeyPres  
    539539            int dp = (int) lastMousePos.distance(e.getX(), e.getY());
    540540            if (dp < initialMoveThreshold)
    541541                return; // ignore small drags
    542             initialMoveThresholdExceeded = true; //no more ingnoring uintil nex mouse press
     542            initialMoveThresholdExceeded = true; //no more ignoring until next mouse press
    543543        }
    544544        if (e.getPoint().equals(lastMousePos))
    545545            return;
  • src/org/openstreetmap/josm/data/ImageData.java

    diff --git a/src/org/openstreetmap/josm/data/ImageData.java b/src/org/openstreetmap/josm/data/ImageData.java
    index f84bf3ed7..00a65420f 100644
    a b  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data;
    33
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
    46import java.util.ArrayList;
    57import java.util.Collections;
    68import java.util.List;
    public class ImageData {  
    2830         * Called when the selection change
    2931         * @param data the image data
    3032         */
    31         void selectedImageChanged(ImageData data);
     33        void selectedImagesChanged(ImageData data);
    3234    }
    3335
    3436    private final List<ImageEntry> data;
    3537
    36     private int selectedImageIndex = -1;
     38    private List<Integer> selectedImagesIndex = new ArrayList<>();
    3739
    3840    private final ListenerList<ImageDataUpdateListener> listeners = ListenerList.create();
    3941
    public class ImageData {  
    5557        } else {
    5658            this.data = new ArrayList<>();
    5759        }
     60        selectedImagesIndex.add(-1);
    5861    }
    5962
    6063    /**
    public class ImageData {  
    8689        data.addAll(otherData.getImages());
    8790        Collections.sort(data);
    8891
    89         final ImageEntry selected = otherData.getSelectedImage();
    90 
    9192        // Suppress the double photos.
    9293        if (data.size() > 1) {
    9394            ImageEntry prev = data.get(data.size() - 1);
    public class ImageData {  
    100101                }
    101102            }
    102103        }
    103         if (selected != null) {
    104             setSelectedImageIndex(data.indexOf(selected));
     104
     105        final List<ImageEntry> selected = otherData.getSelectedImages();
     106        if (!selected.isEmpty()) {
     107            setSelectedImageIndex(data.indexOf(selected.get(0)));
    105108        }
    106109    }
    107110
    108111    /**
    109      * Return the current selected image
    110      * @return the selected image as {@link ImageEntry} or null
     112     * Return the current selected images
     113     * @return the selected images as list {@link ImageEntry}
    111114     */
    112     public ImageEntry getSelectedImage() {
    113         if (selectedImageIndex > -1) {
    114             return data.get(selectedImageIndex);
     115    public List<ImageEntry> getSelectedImages() {
     116        List<ImageEntry> selected = new ArrayList<>(selectedImagesIndex.size());
     117        for (Integer i: selectedImagesIndex) {
     118            if (i != -1) {
     119                selected.add(data.get(i));
     120            }
    115121        }
    116         return null;
     122        return selected;
    117123    }
    118124
    119125    /**
    public class ImageData {  
    137143     * @return {@code true} is there is a next image, {@code false} otherwise
    138144     */
    139145    public boolean hasNextImage() {
    140         return selectedImageIndex != data.size() - 1;
     146        return (selectedImagesIndex.size() == 1 && selectedImagesIndex.get(0) != data.size() - 1);
    141147    }
    142148
    143149    /**
    public class ImageData {  
    145151     */
    146152    public void selectNextImage() {
    147153        if (hasNextImage()) {
    148             setSelectedImageIndex(selectedImageIndex + 1);
     154            setSelectedImageIndex(selectedImagesIndex.get(0) + 1);
    149155        }
    150156    }
    151157
    public class ImageData {  
    154160     * @return {@code true} is there is a previous image, {@code false} otherwise
    155161     */
    156162    public boolean hasPreviousImage() {
    157         return selectedImageIndex - 1 > -1;
     163        return (selectedImagesIndex.size() == 1 && selectedImagesIndex.get(0) - 1 > -1);
    158164    }
    159165
    160166    /**
    public class ImageData {  
    164170        if (data.isEmpty()) {
    165171            return;
    166172        }
    167         setSelectedImageIndex(Integer.max(0, selectedImageIndex - 1));
     173        setSelectedImageIndex(Integer.max(0, selectedImagesIndex.get(0) - 1));
    168174    }
    169175
    170176    /**
    public class ImageData {  
    175181        setSelectedImageIndex(data.indexOf(image));
    176182    }
    177183
     184    /**
     185     * Add image to the list of selected images
     186     * @param image {@link ImageEntry} the image to add
     187     */
     188    public void addImageToSelection(ImageEntry image) {
     189        int index = data.indexOf(image);
     190        if (selectedImagesIndex.get(0) == -1) {
     191            setSelectedImage(image);
     192        } else if (!selectedImagesIndex.contains(index)) {
     193            selectedImagesIndex.add(index);
     194            listeners.fireEvent(l -> l.selectedImagesChanged(this));
     195        }
     196    }
     197
     198    /**
     199     * Remove the image from the list of selected images
     200     * @param image {@link ImageEntry} the image to remove
     201     */
     202    public void removeImageToSelection(ImageEntry image) {
     203        int index = data.indexOf(image);
     204        selectedImagesIndex.remove(selectedImagesIndex.indexOf(index));
     205        if (selectedImagesIndex.isEmpty()) {
     206            selectedImagesIndex.add(-1);
     207        }
     208        listeners.fireEvent(l -> l.selectedImagesChanged(this));
     209    }
     210
    178211    /**
    179212     * Clear the selected image
    180213     */
    181     public void clearSelectedImage() {
     214    public void clearSelectedImages() {
    182215        setSelectedImageIndex(-1);
    183216    }
    184217
    public class ImageData {  
    187220    }
    188221
    189222    private void setSelectedImageIndex(int index, boolean forceTrigger) {
    190         if (index == selectedImageIndex && !forceTrigger) {
     223        if (selectedImagesIndex.size() > 1) {
     224            selectedImagesIndex.clear();
     225            selectedImagesIndex.add(-1);
     226        }
     227        if (index == selectedImagesIndex.get(0) && !forceTrigger) {
    191228            return;
    192229        }
    193         selectedImageIndex = index;
    194         listeners.fireEvent(l -> l.selectedImageChanged(this));
     230        selectedImagesIndex.set(0, index);
     231        listeners.fireEvent(l -> l.selectedImagesChanged(this));
    195232    }
    196233
    197234    /**
    198235     * Remove the current selected image from the list
    199236     */
    200237    public void removeSelectedImage() {
    201         data.remove(getSelectedImage());
    202         if (selectedImageIndex == data.size()) {
     238        List<ImageEntry> selected = getSelectedImages();
     239        if (selected.size() > 1) {
     240            throw new IllegalStateException(tr("Multiple images have been selected"));
     241        }
     242        if (selected.isEmpty()) {
     243            return;
     244        }
     245        data.remove(getSelectedImages().get(0));
     246        if (selectedImagesIndex.get(0) == data.size()) {
    203247            setSelectedImageIndex(data.size() - 1);
    204248        } else {
    205             setSelectedImageIndex(selectedImageIndex, true);
     249            setSelectedImageIndex(selectedImagesIndex.get(0), true);
    206250        }
    207251    }
    208252
     253    /**
     254     * Determines if the image is selected
     255     * @param image the {@link ImageEntry} image
     256     * @return {@code true} is the image is selected, {@code false} otherwise
     257     */
     258    public boolean isImageSelected(ImageEntry image) {
     259        int index = data.indexOf(image);
     260        return selectedImagesIndex.contains(index);
     261    }
     262
    209263    /**
    210264     * Remove the image from the list and trigger update listener
    211265     * @param img the {@link ImageEntry} to remove
  • src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java b/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
    index e8ab7fbb9..e9f4710a7 100644
    a b public class GeoImageLayer extends AbstractModifiableLayer implements  
    9898
    9999    /** Mouse position where the last image was selected. */
    100100    private Point lastSelPos;
     101    /** The mouse point */
     102    private Point startPoint;
    101103
    102104    /**
    103105     * Image cycle mode flag.
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    154156        this.data.addImageDataUpdateListener(this);
    155157    }
    156158
     159    private final class MouseListener extends MouseAdapter {
     160        private boolean isMapModeOk() {
     161            MapMode mapMode = MainApplication.getMap().mapMode;
     162            return mapMode == null || isSupportedMapMode(mapMode);
     163        }
     164
     165        @Override
     166        public void mousePressed(MouseEvent e) {
     167            if (e.getButton() != MouseEvent.BUTTON1)
     168                return;
     169            if (isVisible() && isMapModeOk()) {
     170                cycleModeArmed = true;
     171                invalidate();
     172                startPoint = e.getPoint();
     173            }
     174        }
     175
     176        @Override
     177        public void mouseReleased(MouseEvent ev) {
     178            if (ev.getButton() != MouseEvent.BUTTON1)
     179                return;
     180            if (!isVisible() || !isMapModeOk())
     181                return;
     182            if (!cycleModeArmed) {
     183                return;
     184            }
     185
     186            Rectangle hitBoxClick = new Rectangle((int) startPoint.getX() - 10, (int) startPoint.getY() - 10, 15, 15);
     187            if (!hitBoxClick.contains(ev.getPoint())) {
     188                return;
     189            }
     190
     191            Point mousePos = ev.getPoint();
     192            boolean cycle = cycleModeArmed && lastSelPos != null && lastSelPos.equals(mousePos);
     193            final boolean isShift = (ev.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK;
     194            final boolean isCtrl = (ev.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == MouseEvent.CTRL_DOWN_MASK;
     195            int idx = getPhotoIdxUnderMouse(ev, cycle);
     196            if (idx >= 0) {
     197                lastSelPos = mousePos;
     198                cycleModeArmed = false;
     199                ImageEntry img = data.getImages().get(idx);
     200                if (isShift) {
     201                    if (isCtrl && !data.getSelectedImages().isEmpty()) {
     202                        int idx2 = data.getImages().indexOf(data.getSelectedImages().get(data.getSelectedImages().size() - 1));
     203                        int startIndex = Math.min(idx, idx2);
     204                        int endIndex = Math.max(idx, idx2);
     205                        for (int i = startIndex; i <= endIndex; i++) {
     206                            data.addImageToSelection(data.getImages().get(i));
     207                        }
     208                    } else {
     209                        if (data.isImageSelected(img)) {
     210                            data.removeImageToSelection(img);
     211                        } else {
     212                            data.addImageToSelection(img);
     213                        }
     214                    }
     215                } else {
     216                    data.setSelectedImage(img);
     217                }
     218            }
     219        }
     220    }
     221
    157222    /**
    158223     * Loads a set of images, while displaying a dialog that indicates what the plugin is currently doing.
    159224     * In facts, this object is instantiated with a list of files. These files may be JPEG files or
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    496561                for (ImageEntry e : data.getImages()) {
    497562                    paintImage(e, mv, clip, tempG);
    498563                }
    499                 if (data.getSelectedImage() != null) {
     564                for (ImageEntry img: this.data.getSelectedImages()) {
    500565                    // Make sure the selected image is on top in case multiple images overlap.
    501                     paintImage(data.getSelectedImage(), mv, clip, tempG);
     566                    paintImage(img, mv, clip, tempG);
    502567                }
    503568                updateOffscreenBuffer = false;
    504569            }
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    515580            }
    516581        }
    517582
    518         ImageEntry e = data.getSelectedImage();
    519         if (e != null && e.getPos() != null) {
    520             Point p = mv.getPoint(e.getPos());
     583        for (ImageEntry e: data.getSelectedImages()) {
     584            if (e != null && e.getPos() != null) {
     585                Point p = mv.getPoint(e.getPos());
    521586
    522             int imgWidth;
    523             int imgHeight;
    524             if (useThumbs && e.hasThumbnail()) {
    525                 Dimension d = scaledDimension(e.getThumbnail());
    526                 if (d != null) {
    527                     imgWidth = d.width;
    528                     imgHeight = d.height;
     587                int imgWidth;
     588                int imgHeight;
     589                if (useThumbs && e.hasThumbnail()) {
     590                    Dimension d = scaledDimension(e.getThumbnail());
     591                    if (d != null) {
     592                        imgWidth = d.width;
     593                        imgHeight = d.height;
     594                    } else {
     595                        imgWidth = -1;
     596                        imgHeight = -1;
     597                    }
    529598                } else {
    530                     imgWidth = -1;
    531                     imgHeight = -1;
     599                    imgWidth = selectedIcon.getIconWidth();
     600                    imgHeight = selectedIcon.getIconHeight();
    532601                }
    533             } else {
    534                 imgWidth = selectedIcon.getIconWidth();
    535                 imgHeight = selectedIcon.getIconHeight();
    536             }
    537 
    538             if (e.getExifImgDir() != null) {
    539                 // Multiplier must be larger than sqrt(2)/2=0.71.
    540                 double arrowlength = Math.max(25, Math.max(imgWidth, imgHeight) * 0.85);
    541                 double arrowwidth = arrowlength / 1.4;
    542 
    543                 double dir = e.getExifImgDir();
    544                 // Rotate 90 degrees CCW
    545                 double headdir = (dir < 90) ? dir + 270 : dir - 90;
    546                 double leftdir = (headdir < 90) ? headdir + 270 : headdir - 90;
    547                 double rightdir = (headdir > 270) ? headdir - 270 : headdir + 90;
    548602
    549                 double ptx = p.x + Math.cos(Utils.toRadians(headdir)) * arrowlength;
    550                 double pty = p.y + Math.sin(Utils.toRadians(headdir)) * arrowlength;
    551 
    552                 double ltx = p.x + Math.cos(Utils.toRadians(leftdir)) * arrowwidth/2;
    553                 double lty = p.y + Math.sin(Utils.toRadians(leftdir)) * arrowwidth/2;
    554 
    555                 double rtx = p.x + Math.cos(Utils.toRadians(rightdir)) * arrowwidth/2;
    556                 double rty = p.y + Math.sin(Utils.toRadians(rightdir)) * arrowwidth/2;
    557 
    558                 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    559                 g.setColor(new Color(255, 255, 255, 192));
    560                 int[] xar = {(int) ltx, (int) ptx, (int) rtx, (int) ltx};
    561                 int[] yar = {(int) lty, (int) pty, (int) rty, (int) lty};
    562                 g.fillPolygon(xar, yar, 4);
    563                 g.setColor(Color.black);
    564                 g.setStroke(new BasicStroke(1.2f));
    565                 g.drawPolyline(xar, yar, 3);
    566             }
     603                if (e.getExifImgDir() != null) {
     604                    // Multiplier must be larger than sqrt(2)/2=0.71.
     605                    double arrowlength = Math.max(25, Math.max(imgWidth, imgHeight) * 0.85);
     606                    double arrowwidth = arrowlength / 1.4;
     607
     608                    double dir = e.getExifImgDir();
     609                    // Rotate 90 degrees CCW
     610                    double headdir = (dir < 90) ? dir + 270 : dir - 90;
     611                    double leftdir = (headdir < 90) ? headdir + 270 : headdir - 90;
     612                    double rightdir = (headdir > 270) ? headdir - 270 : headdir + 90;
     613
     614                    double ptx = p.x + Math.cos(Utils.toRadians(headdir)) * arrowlength;
     615                    double pty = p.y + Math.sin(Utils.toRadians(headdir)) * arrowlength;
     616
     617                    double ltx = p.x + Math.cos(Utils.toRadians(leftdir)) * arrowwidth/2;
     618                    double lty = p.y + Math.sin(Utils.toRadians(leftdir)) * arrowwidth/2;
     619
     620                    double rtx = p.x + Math.cos(Utils.toRadians(rightdir)) * arrowwidth/2;
     621                    double rty = p.y + Math.sin(Utils.toRadians(rightdir)) * arrowwidth/2;
     622
     623                    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
     624                    g.setColor(new Color(255, 255, 255, 192));
     625                    int[] xar = {(int) ltx, (int) ptx, (int) rtx, (int) ltx};
     626                    int[] yar = {(int) lty, (int) pty, (int) rty, (int) lty};
     627                    g.fillPolygon(xar, yar, 4);
     628                    g.setColor(Color.black);
     629                    g.setStroke(new BasicStroke(1.2f));
     630                    g.drawPolyline(xar, yar, 3);
     631                }
    567632
    568             if (useThumbs && e.hasThumbnail()) {
    569                 g.setColor(new Color(128, 0, 0, 122));
    570                 g.fillRect(p.x - imgWidth / 2, p.y - imgHeight / 2, imgWidth, imgHeight);
    571             } else {
    572                 selectedIcon.paintIcon(mv, g,
    573                         p.x - imgWidth / 2,
    574                         p.y - imgHeight / 2);
     633                if (useThumbs && e.hasThumbnail()) {
     634                    g.setColor(new Color(128, 0, 0, 122));
     635                    g.fillRect(p.x - imgWidth / 2, p.y - imgHeight / 2, imgWidth, imgHeight);
     636                } else {
     637                    selectedIcon.paintIcon(mv, g,
     638                            p.x - imgWidth / 2,
     639                            p.y - imgHeight / 2);
     640                }
    575641            }
    576642        }
    577643    }
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    587653     * Show current photo on map and in image viewer.
    588654     */
    589655    public void showCurrentPhoto() {
    590         if (data.getSelectedImage() != null) {
     656        if (!data.getSelectedImages().isEmpty()) {
    591657            clearOtherCurrentPhotos();
    592658        }
    593659        updateBufferAndRepaint();
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    635701     *               or {@code -1} if there is no image at the mouse position
    636702     */
    637703    private int getPhotoIdxUnderMouse(MouseEvent evt, boolean cycle) {
    638         ImageEntry selectedImage = data.getSelectedImage();
    639         int selectedIndex = data.getImages().indexOf(selectedImage);
     704        ImageEntry selectedImage = this.data.getSelectedImages().isEmpty() ? null : this.data.getSelectedImages().get(0);
     705        int selectedIndex = this.data.getImages().indexOf(selectedImage);
    640706
    641707        if (cycle && selectedImage != null) {
    642708            // Cycle loop is forward as that is the natural order.
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    701767        for (GeoImageLayer layer:
    702768                 MainApplication.getLayerManager().getLayersOfType(GeoImageLayer.class)) {
    703769            if (layer != this) {
    704                 layer.getImageData().clearSelectedImage();
     770                layer.getImageData().clearSelectedImages();
    705771            }
    706772        }
    707773    }
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    742808
    743809    @Override
    744810    public void hookUpMapView() {
    745         mouseAdapter = new MouseAdapter() {
    746             private boolean isMapModeOk() {
    747                 MapMode mapMode = MainApplication.getMap().mapMode;
    748                 return mapMode == null || isSupportedMapMode(mapMode);
    749             }
    750 
    751             @Override
    752             public void mousePressed(MouseEvent e) {
    753                 if (e.getButton() != MouseEvent.BUTTON1)
    754                     return;
    755                 if (isVisible() && isMapModeOk()) {
    756                     cycleModeArmed = true;
    757                     invalidate();
    758                 }
    759             }
    760 
    761             @Override
    762             public void mouseReleased(MouseEvent ev) {
    763                 if (ev.getButton() != MouseEvent.BUTTON1)
    764                     return;
    765                 if (!isVisible() || !isMapModeOk())
    766                     return;
    767 
    768                 Point mousePos = ev.getPoint();
    769                 boolean cycle = cycleModeArmed && lastSelPos != null && lastSelPos.equals(mousePos);
    770                 int idx = getPhotoIdxUnderMouse(ev, cycle);
    771                 if (idx >= 0) {
    772                     lastSelPos = mousePos;
    773                     cycleModeArmed = false;
    774                     data.setSelectedImage(data.getImages().get(idx));
    775                 }
    776             }
    777         };
     811        mouseAdapter = new MouseListener();
    778812
    779813        mouseMotionAdapter = new MouseMotionAdapter() {
    780814            @Override
    public class GeoImageLayer extends AbstractModifiableLayer implements  
    9681002    }
    9691003
    9701004    @Override
    971     public void selectedImageChanged(ImageData data) {
     1005    public void selectedImagesChanged(ImageData data) {
    9721006        showCurrentPhoto();
    9731007    }
    9741008
  • src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
    index cba56207b..80982233f 100644
    a b public class ImageDisplay extends JComponent implements Destroyable, PreferenceC  
    6666
    6767    private final ImgDisplayMouseListener imgMouseListener = new ImgDisplayMouseListener();
    6868
     69    private String emptyText;
     70
    6971    private String osdText;
    7072
    7173    private static final BooleanProperty AGPIFO_STYLE =
    public class ImageDisplay extends JComponent implements Destroyable, PreferenceC  
    697699        }
    698700    }
    699701
     702    /**
     703     * Set the message displayed when there is no image to display
     704     * By default it display a simple No image
     705     * @param emptyText the string to display
     706     * @since XXX
     707     */
     708    public void setEmptyText(String emptyText) {
     709        this.emptyText = emptyText;
     710    }
     711
    700712    /**
    701713     * Sets the On-Screen-Display text.
    702714     * @param text text to display on top of the image
    public class ImageDisplay extends JComponent implements Destroyable, PreferenceC  
    729741        Dimension size = getSize();
    730742        if (entry == null) {
    731743            g.setColor(Color.black);
    732             String noImageStr = tr("No image");
     744            if (this.emptyText == null) {
     745                this.emptyText = tr("No image");
     746            }
     747            String noImageStr = this.emptyText;
    733748            Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(noImageStr, g);
    734749            g.drawString(noImageStr,
    735750                    (int) ((size.width - noImageSize.getWidth()) / 2),
  • src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
    index aa3c74fe2..837036aac 100644
    a b import java.awt.event.KeyEvent;  
    1313import java.awt.event.WindowEvent;
    1414import java.text.DateFormat;
    1515import java.text.SimpleDateFormat;
     16import java.util.List;
    1617
    1718import javax.swing.Box;
    1819import javax.swing.JButton;
    public final class ImageViewerDialog extends ToggleDialog implements LayerChange  
    308309
    309310        @Override
    310311        public void actionPerformed(ActionEvent e) {
    311             if (currentData != null && currentData.getSelectedImage() != null) {
    312                 ImageEntry toDelete = currentData.getSelectedImage();
     312            if (currentData != null && !currentData.getSelectedImages().isEmpty()) {
     313                ImageEntry toDelete = currentData.getSelectedImages().get(0);
    313314
    314315                int result = new ExtendedDialog(
    315316                        MainApplication.getMainFrame(),
    public final class ImageViewerDialog extends ToggleDialog implements LayerChange  
    353354        @Override
    354355        public void actionPerformed(ActionEvent e) {
    355356            if (currentData != null) {
    356                 ClipboardUtils.copyString(currentData.getSelectedImage().getFile().toString());
     357                ClipboardUtils.copyString(currentData.getSelectedImages().get(0).getFile().toString());
    357358            }
    358359        }
    359360    }
    public final class ImageViewerDialog extends ToggleDialog implements LayerChange  
    371372        }
    372373    }
    373374
    374     /**
    375      * Displays image for the given data.
    376      * @param data geo image data
    377      * @param entry image entry
    378      */
    379     public static void showImage(ImageData data, ImageEntry entry) {
    380         getInstance().displayImage(data, entry);
    381     }
    382 
    383375    /**
    384376     * Enables (or disables) the "Previous" button.
    385377     * @param value {@code true} to enable the button, {@code false} otherwise
    public final class ImageViewerDialog extends ToggleDialog implements LayerChange  
    417409    /**
    418410     * Displays image for the given layer.
    419411     * @param data the image data
    420      * @param entry image entry
     412     * @param entries the list of {@link ImageEntry}
    421413     */
    422     public void displayImage(ImageData data, ImageEntry entry) {
     414    public void displayImage(ImageData data, List<ImageEntry> entries) {
    423415        boolean imageChanged;
     416        ImageEntry entry = null;
     417
     418        if (entries != null && entries.size() == 1) {
     419            entry = entries.get(0);
     420        }
    424421
    425422        synchronized (this) {
    426423            // TODO: pop up image dialog but don't load image again
    public final class ImageViewerDialog extends ToggleDialog implements LayerChange  
    478475            // if this method is called to reinitialize dialog content with a blank image,
    479476            // do not actually show the dialog again with a blank image if currently hidden (fix #10672)
    480477            setTitle(tr("Geotagged Images"));
    481             imgDisplay.setImage(null);
    482             imgDisplay.setOsdText("");
     478            imgDisplay.setEmptyText(null);
    483479            setNextEnabled(false);
    484480            setPreviousEnabled(false);
    485481            btnDelete.setEnabled(false);
    486482            btnDeleteFromDisk.setEnabled(false);
    487483            btnCopyPath.setEnabled(false);
     484            if (entries != null && entries.size() > 1) {
     485                imgDisplay.setEmptyText(tr("Multiple images selected"));
     486                btnFirst.setEnabled(!isFirstImageSelected(data));
     487                btnLast.setEnabled(!isLastImageSelected(data));
     488            }
     489            imgDisplay.setImage(null);
     490            imgDisplay.setOsdText("");
    488491            return;
    489492        }
    490493        if (!isDialogShowing()) {
    public final class ImageViewerDialog extends ToggleDialog implements LayerChange  
    498501        }
    499502    }
    500503
     504    private boolean isLastImageSelected(ImageData data) {
     505        return data.isImageSelected(data.getImages().get(data.getImages().size() - 1));
     506    }
     507
     508    private boolean isFirstImageSelected(ImageData data) {
     509        return data.isImageSelected(data.getImages().get(0));
     510    }
     511
    501512    /**
    502513     * When an image is closed, really close it and do not pop
    503514     * up the side dialog.
    public final class ImageViewerDialog extends ToggleDialog implements LayerChange  
    585596    }
    586597
    587598    @Override
    588     public void selectedImageChanged(ImageData data) {
    589         showImage(data, data.getSelectedImage());
     599    public void selectedImagesChanged(ImageData data) {
     600        displayImage(data, data.getSelectedImages());
    590601    }
    591602
    592603    @Override
    593604    public void imageDataUpdated(ImageData data) {
    594         showImage(data, data.getSelectedImage());
     605        displayImage(data, data.getSelectedImages());
    595606    }
    596607}
  • test/unit/org/openstreetmap/josm/data/ImageDataTest.java

    diff --git a/test/unit/org/openstreetmap/josm/data/ImageDataTest.java b/test/unit/org/openstreetmap/josm/data/ImageDataTest.java
    index 9fcc7c5e9..2f1a6f90c 100644
    a b package org.openstreetmap.josm.data;  
    33
    44import static org.junit.Assert.assertEquals;
    55import static org.junit.Assert.assertFalse;
    6 import static org.junit.Assert.assertNull;
    76import static org.junit.Assert.assertTrue;
    87
    98import java.io.File;
    public class ImageDataTest {  
    3231    }
    3332
    3433    @Test
    35     public void testWithullData() {
     34    public void testWithNullData() {
    3635        ImageData data = new ImageData();
    3736        assertEquals(0, data.getImages().size());
    38         assertNull(data.getSelectedImage());
     37        assertEquals(0, data.getSelectedImages().size());
    3938        data.selectFirstImage();
    40         assertNull(data.getSelectedImage());
     39        assertEquals(0, data.getSelectedImages().size());
    4140        data.selectLastImage();
    42         assertNull(data.getSelectedImage());
     41        assertEquals(0, data.getSelectedImages().size());
    4342        data.selectFirstImage();
    44         assertNull(data.getSelectedImage());
     43        assertEquals(0, data.getSelectedImages().size());
    4544        data.selectPreviousImage();
    46         assertNull(data.getSelectedImage());
     45        assertEquals(0, data.getSelectedImages().size());
    4746        assertFalse(data.hasNextImage());
    4847        assertFalse(data.hasPreviousImage());
    4948        data.removeSelectedImage();
    public class ImageDataTest {  
    8786
    8887        ImageData data = new ImageData(list);
    8988        data.selectFirstImage();
    90         assertEquals(list.get(0), data.getSelectedImage());
     89        assertEquals(1, data.getSelectedImages().size());
     90        assertEquals(list.get(0), data.getSelectedImages().get(0));
    9191    }
    9292
    9393    @Test
    public class ImageDataTest {  
    9797
    9898        ImageData data = new ImageData(list);
    9999        data.selectLastImage();
    100         assertEquals(list.get(1), data.getSelectedImage());
     100        assertEquals(1, data.getSelectedImages().size());
     101        assertEquals(list.get(1), data.getSelectedImages().get(0));
    101102    }
    102103
    103104    @Test
    public class ImageDataTest {  
    107108        ImageData data = new ImageData(list);
    108109        assertTrue(data.hasNextImage());
    109110        data.selectNextImage();
    110         assertEquals(list.get(0), data.getSelectedImage());
     111        assertEquals(1, data.getSelectedImages().size());
     112        assertEquals(list.get(0), data.getSelectedImages().get(0));
    111113        assertFalse(data.hasNextImage());
    112114        data.selectNextImage();
    113         assertEquals(list.get(0), data.getSelectedImage());
     115        assertEquals(list.get(0), data.getSelectedImages().get(0));
    114116    }
    115117
    116118    @Test
    public class ImageDataTest {  
    123125        data.selectLastImage();
    124126        assertTrue(data.hasPreviousImage());
    125127        data.selectPreviousImage();
    126         assertEquals(list.get(0), data.getSelectedImage());
     128        assertEquals(1, data.getSelectedImages().size());
     129        assertEquals(list.get(0), data.getSelectedImages().get(0));
    127130        data.selectPreviousImage();
    128         assertEquals(list.get(0), data.getSelectedImage());
     131        assertEquals(list.get(0), data.getSelectedImages().get(0));
    129132    }
    130133
    131134    @Test
    public class ImageDataTest {  
    134137
    135138        ImageData data = new ImageData(list);
    136139        data.setSelectedImage(list.get(0));
    137         assertEquals(list.get(0), data.getSelectedImage());
     140        assertEquals(1, data.getSelectedImages().size());
     141        assertEquals(list.get(0), data.getSelectedImages().get(0));
    138142    }
    139143
    140144    @Test
    141     public void testClearSelectedImage() {
     145    public void testClearSelectedImages() {
    142146        List<ImageEntry> list = getOneImage();
    143147
    144148        ImageData data = new ImageData(list);
    145149        data.setSelectedImage(list.get(0));
    146         data.clearSelectedImage();
    147         assertNull(data.getSelectedImage());
     150        data.clearSelectedImages();
     151        assertTrue(data.getSelectedImages().isEmpty());
    148152    }
    149153
    150154    @Test
    public class ImageDataTest {  
    153157        ImageData data = new ImageData(list);
    154158        ImageDataUpdateListener listener = new ImageDataUpdateListener() {
    155159            @Override
    156             public void selectedImageChanged(ImageData data) {}
     160            public void selectedImagesChanged(ImageData data) {}
    157161
    158162            @Override
    159163            public void imageDataUpdated(ImageData data) {}
    160164        };
    161165        new Expectations(listener) {{
    162             listener.selectedImageChanged(data); times = 1;
     166            listener.selectedImagesChanged(data); times = 1;
    163167        }};
    164168        data.addImageDataUpdateListener(listener);
    165169        data.selectFirstImage();
    public class ImageDataTest {  
    173177        data.selectFirstImage();
    174178        data.removeSelectedImage();
    175179        assertEquals(0, data.getImages().size());
    176         assertNull(data.getSelectedImage());
     180        assertEquals(0, data.getSelectedImages().size());
    177181    }
    178182
    179183    @Test
    public class ImageDataTest {  
    183187        ImageData data = new ImageData(list);
    184188        ImageDataUpdateListener listener = new ImageDataUpdateListener() {
    185189            @Override
    186             public void selectedImageChanged(ImageData data) {}
     190            public void selectedImagesChanged(ImageData data) {}
    187191
    188192            @Override
    189193            public void imageDataUpdated(ImageData data) {}
    190194        };
    191195        new Expectations(listener) {{
    192             listener.selectedImageChanged(data); times = 2;
     196            listener.selectedImagesChanged(data); times = 2;
    193197        }};
    194198        data.addImageDataUpdateListener(listener);
    195199        data.selectFirstImage();
    public class ImageDataTest {  
    202206        ImageData data = new ImageData(list);
    203207        ImageDataUpdateListener listener = new ImageDataUpdateListener() {
    204208            @Override
    205             public void selectedImageChanged(ImageData data) {}
     209            public void selectedImagesChanged(ImageData data) {}
    206210
    207211            @Override
    208212            public void imageDataUpdated(ImageData data) {}
    public class ImageDataTest {  
    237241
    238242        data.mergeFrom(data2);
    239243        assertEquals(3, data.getImages().size());
    240         assertEquals(list1.get(0), data.getSelectedImage());
     244        assertEquals(1, data.getSelectedImages().size());
     245        assertEquals(list1.get(0), data.getSelectedImages().get(0));
    241246    }
    242247
    243248    @Test
    public class ImageDataTest {  
    253258
    254259        data.mergeFrom(data2);
    255260        assertEquals(3, data.getImages().size());
    256         assertEquals(list2.get(0), data.getSelectedImage());
     261        assertEquals(1, data.getSelectedImages().size());
     262        assertEquals(list2.get(0), data.getSelectedImages().get(0));
     263    }
     264
     265    @Test
     266    public void testAddImageToSelection() {
     267        List<ImageEntry> list = getOneImage();
     268        list.add(new ImageEntry(new File("test2")));
     269
     270        ImageData data = new ImageData(list);
     271        data.addImageToSelection(list.get(0));
     272        data.addImageToSelection(list.get(0));
     273        assertEquals(1, data.getSelectedImages().size());
     274        data.addImageToSelection(list.get(1));
     275        assertEquals(2, data.getSelectedImages().size());
     276    }
     277
     278    @Test
     279    public void testRemoveImageToSelection() {
     280        List<ImageEntry> list = getOneImage();
     281        list.add(new ImageEntry());
     282
     283        ImageData data = new ImageData(list);
     284        data.selectLastImage();
     285        data.removeImageToSelection(list.get(1));
     286        assertEquals(0, data.getSelectedImages().size());
     287        data.selectFirstImage();
     288        assertEquals(1, data.getSelectedImages().size());
     289
     290    }
     291
     292    @Test
     293    public void testIsSelected() {
     294        List<ImageEntry> list = getOneImage();
     295        list.add(new ImageEntry(new File("test2")));
     296
     297        ImageData data = new ImageData(list);
     298        assertFalse(data.isImageSelected(list.get(0)));
     299        data.selectFirstImage();
     300        assertTrue(data.isImageSelected(list.get(0)));
     301        data.addImageToSelection(list.get(1));
     302        assertTrue(data.isImageSelected(list.get(0)));
     303        assertTrue(data.isImageSelected(list.get(1)));
     304        assertFalse(data.isImageSelected(new ImageEntry()));
     305    }
     306
     307    @Test
     308    public void testActionsWithMultipleImagesSelected() {
     309        List<ImageEntry> list = this.getOneImage();
     310        list.add(new ImageEntry(new File("test2")));
     311        list.add(new ImageEntry(new File("test3")));
     312        list.add(new ImageEntry(new File("test3")));
     313
     314        ImageData data = new ImageData(list);
     315        data.addImageToSelection(list.get(1));
     316        data.addImageToSelection(list.get(2));
     317
     318        assertFalse(data.hasNextImage());
     319        assertFalse(data.hasPreviousImage());
     320
     321        data.clearSelectedImages();
     322        assertEquals(0, data.getSelectedImages().size());
     323        data.addImageToSelection(list.get(1));
     324        data.selectFirstImage();
     325        assertEquals(1, data.getSelectedImages().size());
     326    }
     327
     328    @Test
     329    public void testTriggerListenerWhenNewImageIsSelectedAndRemoved() {
     330        List<ImageEntry> list = this.getOneImage();
     331        list.add(new ImageEntry());
     332        ImageData data = new ImageData(list);
     333        ImageDataUpdateListener listener = new ImageDataUpdateListener() {
     334            @Override
     335            public void selectedImagesChanged(ImageData data) {}
     336
     337            @Override
     338            public void imageDataUpdated(ImageData data) {}
     339        };
     340        new Expectations(listener) {{
     341            listener.selectedImagesChanged(data); times = 3;
     342        }};
     343        data.addImageDataUpdateListener(listener);
     344        data.selectFirstImage();
     345        data.addImageToSelection(list.get(1));
     346        data.removeImageToSelection(list.get(0));
    257347    }
    258348
    259349    @Test
    public class ImageDataTest {  
    287377
    288378        ImageDataUpdateListener listener = new ImageDataUpdateListener() {
    289379            @Override
    290             public void selectedImageChanged(ImageData data) {}
     380            public void selectedImagesChanged(ImageData data) {}
    291381
    292382            @Override
    293383            public void imageDataUpdated(ImageData data) {}
    public class ImageDataTest {  
    307397
    308398        ImageDataUpdateListener listener = new ImageDataUpdateListener() {
    309399            @Override
    310             public void selectedImageChanged(ImageData data) {}
     400            public void selectedImagesChanged(ImageData data) {}
    311401
    312402            @Override
    313403            public void imageDataUpdated(ImageData data) {}