Ignore:
Timestamp:
2021-10-26T00:51:59+02:00 (2 years ago)
Author:
Don-vip
Message:

fix #21469 - Make First/Last buttons in image display window remember which images they come from (patch by taylor.smock):

  • Fixes an issue where non-deletable/removable objects did not disable the delete buttons
  • Adds a property change listener for actions so that the button they are part of is updated when the action is enabled/disabled
  • Adds some super classes (ImageAction/ImageRememberAction) to de-duplicate some code.
  • Attempts to keep the behavior from setNextEnabled/setPreviousEnabled intact as much as possible
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java

    r18278 r18290  
    22package org.openstreetmap.josm.gui.layer.geoimage;
    33
     4import static org.openstreetmap.josm.tools.I18n.marktr;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56import static org.openstreetmap.josm.tools.I18n.trn;
     
    1314import java.awt.event.KeyEvent;
    1415import java.awt.event.WindowEvent;
     16import java.io.Serializable;
    1517import java.time.ZoneOffset;
    1618import java.time.format.DateTimeFormatter;
    1719import java.time.format.FormatStyle;
    1820import java.util.ArrayList;
     21import java.util.Arrays;
    1922import java.util.Collections;
    2023import java.util.List;
     24import java.util.Objects;
    2125import java.util.Optional;
    2226import java.util.concurrent.Future;
     27import java.util.function.UnaryOperator;
    2328import java.util.stream.Collectors;
    2429
     
    6065 */
    6166public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener, ImageDataUpdateListener {
     67    private static final String GEOIMAGE_FILLER = marktr("Geoimage: {0}");
     68    private static final String DIALOG_FOLDER = "dialogs";
    6269
    6370    private final ImageryFilterSettings imageryFilterSettings = new ImageryFilterSettings();
     
    132139        JButton btn = createButton(action, buttonDim);
    133140        btn.setEnabled(false);
     141        action.addPropertyChangeListener(l -> {
     142            if ("enabled".equals(l.getPropertyName())) {
     143                btn.setEnabled(action.isEnabled());
     144            }
     145        });
    134146        return btn;
    135147    }
     
    216228    }
    217229
    218     private class ImageNextAction extends JosmAction {
     230    /**
     231     * This literally exists to silence sonarlint complaints.
     232     */
     233    @FunctionalInterface
     234    private interface SerializableUnaryOperator<I> extends UnaryOperator<I>, Serializable {
     235    }
     236
     237    private abstract class ImageAction extends JosmAction {
     238        final SerializableUnaryOperator<IImageEntry<?>> supplier;
     239        ImageAction(String name, ImageProvider icon, String tooltip, Shortcut shortcut,
     240                boolean registerInToolbar, String toolbarId, boolean installAdaptors,
     241                final SerializableUnaryOperator<IImageEntry<?>> supplier) {
     242            super(name, icon, tooltip, shortcut, registerInToolbar, toolbarId, installAdaptors);
     243            Objects.requireNonNull(supplier);
     244            this.supplier = supplier;
     245        }
     246
     247        @Override
     248        public void actionPerformed(ActionEvent event) {
     249            final IImageEntry<?> entry = ImageViewerDialog.this.currentEntry;
     250            if (entry != null) {
     251                IImageEntry<?> nextEntry = this.getSupplier().apply(entry);
     252                entry.selectImage(ImageViewerDialog.this, nextEntry);
     253            }
     254            this.resetRememberActions();
     255        }
     256
     257        void resetRememberActions() {
     258            for (ImageRememberAction action : Arrays.asList(ImageViewerDialog.this.imageLastAction, ImageViewerDialog.this.imageFirstAction)) {
     259                action.last = null;
     260                action.updateEnabledState();
     261            }
     262        }
     263
     264        SerializableUnaryOperator<IImageEntry<?>> getSupplier() {
     265            return this.supplier;
     266        }
     267
     268        @Override
     269        protected void updateEnabledState() {
     270            final IImageEntry<?> entry = ImageViewerDialog.this.currentEntry;
     271            this.setEnabled(entry != null && this.getSupplier().apply(entry) != null);
     272        }
     273    }
     274
     275    private class ImageNextAction extends ImageAction {
    219276        ImageNextAction() {
    220             super(null, new ImageProvider("dialogs", "next"), tr("Next"), Shortcut.registerShortcut(
    221                     "geoimage:next", tr("Geoimage: {0}", tr("Show next Image")), KeyEvent.VK_PAGE_DOWN, Shortcut.DIRECT),
    222                   false, null, false);
    223         }
    224 
    225         @Override
    226         public void actionPerformed(ActionEvent e) {
    227             if (ImageViewerDialog.this.currentEntry != null) {
    228                 ImageViewerDialog.this.currentEntry.selectNextImage(ImageViewerDialog.this);
    229             }
    230         }
    231     }
    232 
    233     private class ImagePreviousAction extends JosmAction {
     277            super(null, new ImageProvider(DIALOG_FOLDER, "next"), tr("Next"), Shortcut.registerShortcut(
     278                    "geoimage:next", tr(GEOIMAGE_FILLER, tr("Show next Image")), KeyEvent.VK_PAGE_DOWN, Shortcut.DIRECT),
     279                  false, null, false, IImageEntry::getNextImage);
     280        }
     281    }
     282
     283    private class ImagePreviousAction extends ImageAction {
    234284        ImagePreviousAction() {
    235             super(null, new ImageProvider("dialogs", "previous"), tr("Previous"), Shortcut.registerShortcut(
    236                     "geoimage:previous", tr("Geoimage: {0}", tr("Show previous Image")), KeyEvent.VK_PAGE_UP, Shortcut.DIRECT),
    237                   false, null, false);
    238         }
    239 
    240         @Override
    241         public void actionPerformed(ActionEvent e) {
    242             if (ImageViewerDialog.this.currentEntry != null) {
    243                 ImageViewerDialog.this.currentEntry.selectPreviousImage(ImageViewerDialog.this);
    244             }
    245         }
    246     }
    247 
    248     private class ImageFirstAction extends JosmAction {
     285            super(null, new ImageProvider(DIALOG_FOLDER, "previous"), tr("Previous"), Shortcut.registerShortcut(
     286                    "geoimage:previous", tr(GEOIMAGE_FILLER, tr("Show previous Image")), KeyEvent.VK_PAGE_UP, Shortcut.DIRECT),
     287                  false, null, false, IImageEntry::getPreviousImage);
     288        }
     289    }
     290
     291    /** This class exists to remember the last entry, and go back if clicked again when it would not otherwise be enabled */
     292    private abstract class ImageRememberAction extends ImageAction {
     293        private final ImageProvider defaultIcon;
     294        transient IImageEntry<?> last;
     295        ImageRememberAction(String name, ImageProvider icon, String tooltip, Shortcut shortcut,
     296                boolean registerInToolbar, String toolbarId, boolean installAdaptors, SerializableUnaryOperator<IImageEntry<?>> supplier) {
     297            super(name, icon, tooltip, shortcut, registerInToolbar, toolbarId, installAdaptors, supplier);
     298            this.defaultIcon = icon;
     299        }
     300
     301        public void updateIcon() {
     302            if (this.last != null) {
     303                new ImageProvider(DIALOG_FOLDER, "history").getResource().attachImageIcon(this, true);
     304            } else {
     305                this.defaultIcon.getResource().attachImageIcon(this, true);
     306            }
     307        }
     308
     309        @Override
     310        public void actionPerformed(ActionEvent event) {
     311            final IImageEntry<?> current = ImageViewerDialog.this.currentEntry;
     312            final IImageEntry<?> expected = this.supplier.apply(current);
     313            if (current != null) {
     314                IImageEntry<?> nextEntry = this.getSupplier().apply(current);
     315                current.selectImage(ImageViewerDialog.this, nextEntry);
     316            }
     317            this.resetRememberActions();
     318            if (!Objects.equals(current, expected)) {
     319                this.last = current;
     320            } else {
     321                this.last = null;
     322            }
     323            this.updateEnabledState();
     324        }
     325
     326        @Override
     327        protected void updateEnabledState() {
     328            final IImageEntry<?> current = ImageViewerDialog.this.currentEntry;
     329            final IImageEntry<?> nextEntry = current != null ? this.getSupplier().apply(current) : null;
     330            if (this.last == null && nextEntry != null && nextEntry.equals(current)) {
     331                this.setEnabled(false);
     332            } else {
     333                super.updateEnabledState();
     334            }
     335            this.updateIcon();
     336        }
     337
     338        @Override
     339        SerializableUnaryOperator<IImageEntry<?>> getSupplier() {
     340            if (this.last != null) {
     341                return entry -> this.last;
     342            }
     343            return super.getSupplier();
     344        }
     345    }
     346
     347    private class ImageFirstAction extends ImageRememberAction {
    249348        ImageFirstAction() {
    250             super(null, new ImageProvider("dialogs", "first"), tr("First"), Shortcut.registerShortcut(
    251                     "geoimage:first", tr("Geoimage: {0}", tr("Show first Image")), KeyEvent.VK_HOME, Shortcut.DIRECT),
    252                   false, null, false);
    253         }
    254 
    255         @Override
    256         public void actionPerformed(ActionEvent e) {
    257             if (ImageViewerDialog.this.currentEntry != null) {
    258                 ImageViewerDialog.this.currentEntry.selectFirstImage(ImageViewerDialog.this);
    259             }
    260         }
    261     }
    262 
    263     private class ImageLastAction extends JosmAction {
     349            super(null, new ImageProvider(DIALOG_FOLDER, "first"), tr("First"), Shortcut.registerShortcut(
     350                    "geoimage:first", tr(GEOIMAGE_FILLER, tr("Show first Image")), KeyEvent.VK_HOME, Shortcut.DIRECT),
     351                  false, null, false, IImageEntry::getFirstImage);
     352        }
     353    }
     354
     355    private class ImageLastAction extends ImageRememberAction {
    264356        ImageLastAction() {
    265             super(null, new ImageProvider("dialogs", "last"), tr("Last"), Shortcut.registerShortcut(
    266                     "geoimage:last", tr("Geoimage: {0}", tr("Show last Image")), KeyEvent.VK_END, Shortcut.DIRECT),
    267                   false, null, false);
    268         }
    269 
    270         @Override
    271         public void actionPerformed(ActionEvent e) {
    272             if (ImageViewerDialog.this.currentEntry != null) {
    273                 ImageViewerDialog.this.currentEntry.selectLastImage(ImageViewerDialog.this);
    274             }
     357            super(null, new ImageProvider(DIALOG_FOLDER, "last"), tr("Last"), Shortcut.registerShortcut(
     358                    "geoimage:last", tr(GEOIMAGE_FILLER, tr("Show last Image")), KeyEvent.VK_END, Shortcut.DIRECT),
     359                  false, null, false, IImageEntry::getLastImage);
    275360        }
    276361    }
     
    294379    private class ImageZoomAction extends JosmAction {
    295380        ImageZoomAction() {
    296             super(null, new ImageProvider("dialogs", "zoom-best-fit"), tr("Zoom best fit and 1:1"), null,
     381            super(null, new ImageProvider(DIALOG_FOLDER, "zoom-best-fit"), tr("Zoom best fit and 1:1"), null,
    297382                  false, null, false);
    298383        }
     
    306391    private class ImageRemoveAction extends JosmAction {
    307392        ImageRemoveAction() {
    308             super(null, new ImageProvider("dialogs", "delete"), tr("Remove photo from layer"), Shortcut.registerShortcut(
    309                     "geoimage:deleteimagefromlayer", tr("Geoimage: {0}", tr("Remove photo from layer")), KeyEvent.VK_DELETE, Shortcut.SHIFT),
     393            super(null, new ImageProvider(DIALOG_FOLDER, "delete"), tr("Remove photo from layer"), Shortcut.registerShortcut(
     394                    "geoimage:deleteimagefromlayer", tr(GEOIMAGE_FILLER, tr("Remove photo from layer")), KeyEvent.VK_DELETE, Shortcut.SHIFT),
    310395                  false, null, false);
    311396        }
     
    324409    private class ImageRemoveFromDiskAction extends JosmAction {
    325410        ImageRemoveFromDiskAction() {
    326             super(null, new ImageProvider("dialogs", "geoimage/deletefromdisk"), tr("Delete image file from disk"),
     411            super(null, new ImageProvider(DIALOG_FOLDER, "geoimage/deletefromdisk"), tr("Delete image file from disk"),
    327412                    Shortcut.registerShortcut("geoimage:deletefilefromdisk",
    328                             tr("Geoimage: {0}", tr("Delete image file from disk")), KeyEvent.VK_DELETE, Shortcut.CTRL_SHIFT),
     413                            tr(GEOIMAGE_FILLER, tr("Delete image file from disk")), KeyEvent.VK_DELETE, Shortcut.CTRL_SHIFT),
    329414                    false, null, false);
    330415        }
     
    384469        ImageCopyPathAction() {
    385470            super(null, new ImageProvider("copy"), tr("Copy image path"), Shortcut.registerShortcut(
    386                     "geoimage:copypath", tr("Geoimage: {0}", tr("Copy image path")), KeyEvent.VK_C, Shortcut.ALT_CTRL_SHIFT),
     471                    "geoimage:copypath", tr(GEOIMAGE_FILLER, tr("Copy image path")), KeyEvent.VK_C, Shortcut.ALT_CTRL_SHIFT),
    387472                  false, null, false);
    388473        }
     
    398483    private class ImageCollapseAction extends JosmAction {
    399484        ImageCollapseAction() {
    400             super(null, new ImageProvider("dialogs", "collapse"), tr("Move dialog to the side pane"), null,
     485            super(null, new ImageProvider(DIALOG_FOLDER, "collapse"), tr("Move dialog to the side pane"), null,
    401486                  false, null, false);
    402487        }
     
    414499     */
    415500    public void setPreviousEnabled(boolean value) {
    416         btnFirst.setEnabled(value);
     501        this.imageFirstAction.updateEnabledState();
     502        this.btnFirst.setEnabled(value || this.imageFirstAction.isEnabled());
    417503        btnPrevious.setEnabled(value);
    418504    }
     
    424510    public void setNextEnabled(boolean value) {
    425511        btnNext.setEnabled(value);
    426         btnLast.setEnabled(value);
     512        this.imageLastAction.updateEnabledState();
     513        this.btnLast.setEnabled(value || this.imageLastAction.isEnabled());
    427514    }
    428515
     
    440527    }
    441528
    442     private transient IImageEntry<?> currentEntry;
     529    private transient IImageEntry<? extends IImageEntry<?>> currentEntry;
    443530
    444531    /**
     
    480567
    481568            currentEntry = entry;
     569
     570            for (ImageAction action : Arrays.asList(this.imageFirstAction, this.imagePreviousAction,
     571                    this.imageNextAction, this.imageLastAction)) {
     572                action.updateEnabledState();
     573            }
    482574        }
    483575
     
    528620     */
    529621    private void updateButtonsNonNullEntry(IImageEntry<?> entry, boolean imageChanged) {
    530         setNextEnabled(entry.getNextImage() != null);
    531         setPreviousEnabled(entry.getPreviousImage() != null);
    532         btnDelete.setEnabled(true);
    533         btnDeleteFromDisk.setEnabled(entry.getFile() != null);
    534         btnCopyPath.setEnabled(true);
    535 
    536622        if (imageChanged) {
    537623            cancelLoadingImage();
     
    540626            imgLoadingFuture = imgDisplay.setImage(entry);
    541627        }
     628
     629        // Update buttons after setting the new entry
     630        setNextEnabled(entry.getNextImage() != null);
     631        setPreviousEnabled(entry.getPreviousImage() != null);
     632        btnDelete.setEnabled(entry.isRemoveSupported());
     633        btnDeleteFromDisk.setEnabled(entry.isDeleteSupported() && entry.isRemoveSupported());
     634        btnCopyPath.setEnabled(true);
     635
    542636        setTitle(tr("Geotagged Images") + (!entry.getDisplayName().isEmpty() ? " - " + entry.getDisplayName() : ""));
    543637        StringBuilder osd = new StringBuilder(entry.getDisplayName());
Note: See TracChangeset for help on using the changeset viewer.