Ticket #21469: 21469.patch
File 21469.patch, 20.5 KB (added by , 3 years ago) |
---|
-
src/org/openstreetmap/josm/data/imagery/street_level/IImageEntry.java
diff --git a/src/org/openstreetmap/josm/data/imagery/street_level/IImageEntry.java b/src/org/openstreetmap/josm/data/imagery/street_level/IImageEntry.java index 1a23e094f8..f2ee9ab8b5 100644
a b public interface IImageEntry<I extends IImageEntry<I>> { 25 25 * @param imageViewerDialog The image viewer to update 26 26 */ 27 27 default void selectNextImage(final ImageViewerDialog imageViewerDialog) { 28 imageViewerDialog.displayImage(this.getNextImage());28 this.selectImage(imageViewerDialog, this.getNextImage()); 29 29 } 30 30 31 31 /** … … public interface IImageEntry<I extends IImageEntry<I>> { 39 39 * @param imageViewerDialog The image viewer to update 40 40 */ 41 41 default void selectPreviousImage(final ImageViewerDialog imageViewerDialog) { 42 imageViewerDialog.displayImage(this.getPreviousImage());42 this.selectImage(imageViewerDialog, this.getPreviousImage()); 43 43 } 44 44 45 45 /** … … public interface IImageEntry<I extends IImageEntry<I>> { 53 53 * @param imageViewerDialog The image viewer to update 54 54 */ 55 55 default void selectFirstImage(final ImageViewerDialog imageViewerDialog) { 56 imageViewerDialog.displayImage(this.getFirstImage());56 this.selectImage(imageViewerDialog, this.getFirstImage()); 57 57 } 58 58 59 59 /** … … public interface IImageEntry<I extends IImageEntry<I>> { 67 67 * @param imageViewerDialog The image viewer to update 68 68 */ 69 69 default void selectLastImage(final ImageViewerDialog imageViewerDialog) { 70 imageViewerDialog.displayImage(this.getLastImage()); 70 this.selectImage(imageViewerDialog, this.getLastImage()); 71 } 72 73 /** 74 * Select a specific image 75 * @param imageViewerDialog The image viewer to update 76 * @param entry The image to select 77 * @since xxx 78 */ 79 default void selectImage(final ImageViewerDialog imageViewerDialog, final IImageEntry<?> entry) { 80 imageViewerDialog.displayImage(entry); 71 81 } 72 82 73 83 /** -
src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java index 7ea6371f62..0f227a37e6 100644
a b public class ImageEntry extends GpxImageEntry implements IImageEntry<ImageEntry> 141 141 return this.dataSet.getNextImage(); 142 142 } 143 143 144 @Override145 public void selectNextImage(final ImageViewerDialog imageViewerDialog) {146 IImageEntry.super.selectNextImage(imageViewerDialog);147 this.dataSet.setSelectedImage(this.getNextImage());148 }149 150 144 @Override 151 145 public ImageEntry getPreviousImage() { 152 146 return this.dataSet.getPreviousImage(); 153 147 } 154 148 155 @Override156 public void selectPreviousImage(ImageViewerDialog imageViewerDialog) {157 IImageEntry.super.selectPreviousImage(imageViewerDialog);158 this.dataSet.setSelectedImage(this.getPreviousImage());159 }160 161 149 @Override 162 150 public ImageEntry getFirstImage() { 163 151 return this.dataSet.getFirstImage(); 164 152 } 165 153 166 154 @Override 167 public void selectFirstImage(ImageViewerDialog imageViewerDialog) { 168 IImageEntry.super.selectFirstImage(imageViewerDialog); 169 this.dataSet.setSelectedImage(this.getFirstImage()); 155 public void selectImage(ImageViewerDialog imageViewerDialog, IImageEntry<?> entry) { 156 IImageEntry.super.selectImage(imageViewerDialog, entry); 157 if (entry instanceof ImageEntry) { 158 this.dataSet.setSelectedImage((ImageEntry) entry); 159 } 170 160 } 171 161 172 162 @Override … … public class ImageEntry extends GpxImageEntry implements IImageEntry<ImageEntry> 174 164 return this.dataSet.getLastImage(); 175 165 } 176 166 177 @Override178 public void selectLastImage(ImageViewerDialog imageViewerDialog) {179 IImageEntry.super.selectLastImage(imageViewerDialog);180 this.dataSet.setSelectedImage(this.getLastImage());181 }182 183 167 @Override 184 168 public boolean isRemoveSupported() { 185 169 return true; -
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 209a549158..008bdd32fc 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.layer.geoimage; 3 3 4 import static org.openstreetmap.josm.tools.I18n.marktr; 4 5 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import static org.openstreetmap.josm.tools.I18n.trn; 6 7 … … import java.awt.GridBagLayout; 12 13 import java.awt.event.ActionEvent; 13 14 import java.awt.event.KeyEvent; 14 15 import java.awt.event.WindowEvent; 16 import java.io.Serializable; 15 17 import java.time.ZoneOffset; 16 18 import java.time.format.DateTimeFormatter; 17 19 import java.time.format.FormatStyle; 18 20 import java.util.ArrayList; 21 import java.util.Arrays; 19 22 import java.util.Collections; 20 23 import java.util.List; 24 import java.util.Objects; 21 25 import java.util.Optional; 22 26 import java.util.concurrent.Future; 27 import java.util.function.UnaryOperator; 23 28 import java.util.stream.Collectors; 24 29 25 30 import javax.swing.AbstractAction; … … import org.openstreetmap.josm.tools.date.DateUtils; 59 64 * Dialog to view and manipulate geo-tagged images from a {@link GeoImageLayer}. 60 65 */ 61 66 public 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"; 62 69 63 70 private final ImageryFilterSettings imageryFilterSettings = new ImageryFilterSettings(); 64 71 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 131 138 private static JButton createNavigationButton(AbstractAction action, Dimension buttonDim) { 132 139 JButton btn = createButton(action, buttonDim); 133 140 btn.setEnabled(false); 141 action.addPropertyChangeListener(l -> { 142 if ("enabled".equals(l.getPropertyName())) { 143 btn.setEnabled(action.isEnabled()); 144 } 145 }); 134 146 return btn; 135 147 } 136 148 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 215 227 dialog = null; 216 228 } 217 229 218 private class ImageNextAction extends JosmAction { 219 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); 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; 223 245 } 224 246 225 247 @Override 226 public void actionPerformed(ActionEvent e) { 227 if (ImageViewerDialog.this.currentEntry != null) { 228 ImageViewerDialog.this.currentEntry.selectNextImage(ImageViewerDialog.this); 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(); 229 261 } 230 262 } 263 264 SerializableUnaryOperator<IImageEntry<?>> getSupplier() { 265 return this.supplier; 266 } 267 268 @Override 269 protected void updateEnabledState() { 270 this.setEnabled(this.getSupplier().apply(ImageViewerDialog.this.currentEntry) != null); 271 } 231 272 } 232 273 233 private class ImagePreviousAction extends JosmAction { 274 private class ImageNextAction extends ImageAction { 275 ImageNextAction() { 276 super(null, new ImageProvider(DIALOG_FOLDER, "next"), tr("Next"), Shortcut.registerShortcut( 277 "geoimage:next", tr(GEOIMAGE_FILLER, tr("Show next Image")), KeyEvent.VK_PAGE_DOWN, Shortcut.DIRECT), 278 false, null, false, IImageEntry::getNextImage); 279 } 280 } 281 282 private class ImagePreviousAction extends ImageAction { 234 283 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); 284 super(null, new ImageProvider(DIALOG_FOLDER, "previous"), tr("Previous"), Shortcut.registerShortcut( 285 "geoimage:previous", tr(GEOIMAGE_FILLER, tr("Show previous Image")), KeyEvent.VK_PAGE_UP, Shortcut.DIRECT), 286 false, null, false, IImageEntry::getPreviousImage); 287 } 288 } 289 290 /** This class exists to remember the last entry, and go back if clicked again when it would not otherwise be enabled */ 291 private abstract class ImageRememberAction extends ImageAction { 292 private final ImageProvider defaultIcon; 293 transient IImageEntry<?> last; 294 ImageRememberAction(String name, ImageProvider icon, String tooltip, Shortcut shortcut, 295 boolean registerInToolbar, String toolbarId, boolean installAdaptors, SerializableUnaryOperator<IImageEntry<?>> supplier) { 296 super(name, icon, tooltip, shortcut, registerInToolbar, toolbarId, installAdaptors, supplier); 297 this.defaultIcon = icon; 298 } 299 300 public void updateIcon() { 301 if (this.last != null) { 302 new ImageProvider(DIALOG_FOLDER, "history").getResource().attachImageIcon(this, true); 303 } else { 304 this.defaultIcon.getResource().attachImageIcon(this, true); 305 } 238 306 } 239 307 240 308 @Override 241 public void actionPerformed(ActionEvent e) { 242 if (ImageViewerDialog.this.currentEntry != null) { 243 ImageViewerDialog.this.currentEntry.selectPreviousImage(ImageViewerDialog.this); 309 public void actionPerformed(ActionEvent event) { 310 final IImageEntry<?> current = ImageViewerDialog.this.currentEntry; 311 final IImageEntry<?> expected = this.supplier.apply(current); 312 if (current != null) { 313 IImageEntry<?> nextEntry = this.getSupplier().apply(current); 314 current.selectImage(ImageViewerDialog.this, nextEntry); 315 } 316 this.resetRememberActions(); 317 if (!Objects.equals(current, expected)) { 318 this.last = current; 319 } else { 320 this.last = null; 244 321 } 322 this.updateEnabledState(); 245 323 } 246 }247 324 248 private class ImageFirstAction extends JosmAction { 249 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); 325 @Override 326 protected void updateEnabledState() { 327 final IImageEntry<?> current = ImageViewerDialog.this.currentEntry; 328 final IImageEntry<?> nextEntry = this.getSupplier().apply(current); 329 if (this.last == null && nextEntry != null && nextEntry.equals(current)) { 330 this.setEnabled(false); 331 } else { 332 super.updateEnabledState(); 333 } 334 this.updateIcon(); 253 335 } 254 336 255 337 @Override 256 public void actionPerformed(ActionEvent e) {257 if ( ImageViewerDialog.this.currentEntry!= null) {258 ImageViewerDialog.this.currentEntry.selectFirstImage(ImageViewerDialog.this);338 SerializableUnaryOperator<IImageEntry<?>> getSupplier() { 339 if (this.last != null) { 340 return entry -> this.last; 259 341 } 342 return super.getSupplier(); 260 343 } 261 344 } 262 345 263 private class Image LastAction extends JosmAction {264 Image LastAction() {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 );346 private class ImageFirstAction extends ImageRememberAction { 347 ImageFirstAction() { 348 super(null, new ImageProvider(DIALOG_FOLDER, "first"), tr("First"), Shortcut.registerShortcut( 349 "geoimage:first", tr(GEOIMAGE_FILLER, tr("Show first Image")), KeyEvent.VK_HOME, Shortcut.DIRECT), 350 false, null, false, IImageEntry::getFirstImage); 268 351 } 352 } 269 353 270 @Override271 public void actionPerformed(ActionEvent e) {272 if (ImageViewerDialog.this.currentEntry != null) {273 ImageViewerDialog.this.currentEntry.selectLastImage(ImageViewerDialog.this);274 }354 private class ImageLastAction extends ImageRememberAction { 355 ImageLastAction() { 356 super(null, new ImageProvider(DIALOG_FOLDER, "last"), tr("Last"), Shortcut.registerShortcut( 357 "geoimage:last", tr(GEOIMAGE_FILLER, tr("Show last Image")), KeyEvent.VK_END, Shortcut.DIRECT), 358 false, null, false, IImageEntry::getLastImage); 275 359 } 276 360 } 277 361 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 293 377 294 378 private class ImageZoomAction extends JosmAction { 295 379 ImageZoomAction() { 296 super(null, new ImageProvider( "dialogs", "zoom-best-fit"), tr("Zoom best fit and 1:1"), null,380 super(null, new ImageProvider(DIALOG_FOLDER, "zoom-best-fit"), tr("Zoom best fit and 1:1"), null, 297 381 false, null, false); 298 382 } 299 383 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 305 389 306 390 private class ImageRemoveAction extends JosmAction { 307 391 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),392 super(null, new ImageProvider(DIALOG_FOLDER, "delete"), tr("Remove photo from layer"), Shortcut.registerShortcut( 393 "geoimage:deleteimagefromlayer", tr(GEOIMAGE_FILLER, tr("Remove photo from layer")), KeyEvent.VK_DELETE, Shortcut.SHIFT), 310 394 false, null, false); 311 395 } 312 396 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 323 407 324 408 private class ImageRemoveFromDiskAction extends JosmAction { 325 409 ImageRemoveFromDiskAction() { 326 super(null, new ImageProvider( "dialogs", "geoimage/deletefromdisk"), tr("Delete image file from disk"),410 super(null, new ImageProvider(DIALOG_FOLDER, "geoimage/deletefromdisk"), tr("Delete image file from disk"), 327 411 Shortcut.registerShortcut("geoimage:deletefilefromdisk", 328 tr( "Geoimage: {0}", tr("Delete image file from disk")), KeyEvent.VK_DELETE, Shortcut.CTRL_SHIFT),412 tr(GEOIMAGE_FILLER, tr("Delete image file from disk")), KeyEvent.VK_DELETE, Shortcut.CTRL_SHIFT), 329 413 false, null, false); 330 414 } 331 415 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 383 467 private class ImageCopyPathAction extends JosmAction { 384 468 ImageCopyPathAction() { 385 469 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),470 "geoimage:copypath", tr(GEOIMAGE_FILLER, tr("Copy image path")), KeyEvent.VK_C, Shortcut.ALT_CTRL_SHIFT), 387 471 false, null, false); 388 472 } 389 473 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 397 481 398 482 private class ImageCollapseAction extends JosmAction { 399 483 ImageCollapseAction() { 400 super(null, new ImageProvider( "dialogs", "collapse"), tr("Move dialog to the side pane"), null,484 super(null, new ImageProvider(DIALOG_FOLDER, "collapse"), tr("Move dialog to the side pane"), null, 401 485 false, null, false); 402 486 } 403 487 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 413 497 * @param value {@code true} to enable the button, {@code false} otherwise 414 498 */ 415 499 public void setPreviousEnabled(boolean value) { 416 btnFirst.setEnabled(value); 500 this.imageFirstAction.updateEnabledState(); 501 this.btnFirst.setEnabled(value || this.imageFirstAction.isEnabled()); 417 502 btnPrevious.setEnabled(value); 418 503 } 419 504 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 423 508 */ 424 509 public void setNextEnabled(boolean value) { 425 510 btnNext.setEnabled(value); 426 btnLast.setEnabled(value); 511 this.imageLastAction.updateEnabledState(); 512 this.btnLast.setEnabled(value || this.imageLastAction.isEnabled()); 427 513 } 428 514 429 515 /** … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 439 525 return wasEnabled; 440 526 } 441 527 442 private transient IImageEntry<? > currentEntry;528 private transient IImageEntry<? extends IImageEntry<?>> currentEntry; 443 529 444 530 /** 445 531 * Displays a single image for the given layer. … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 479 565 } 480 566 481 567 currentEntry = entry; 568 569 for (ImageAction action : Arrays.asList(this.imageFirstAction, this.imagePreviousAction, 570 this.imageNextAction, this.imageLastAction)) { 571 action.updateEnabledState(); 572 } 482 573 } 483 574 484 575 if (entry != null) { … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 527 618 * @param imageChanged {@code true} if it is not the same image as the previous image. 528 619 */ 529 620 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 536 621 if (imageChanged) { 537 622 cancelLoadingImage(); 538 623 // Set only if the image is new to preserve zoom and position if the same image is redisplayed 539 624 // (e.g. to update the OSD). 540 625 imgLoadingFuture = imgDisplay.setImage(entry); 541 626 } 627 628 // Update buttons after setting the new entry 629 setNextEnabled(entry.getNextImage() != null); 630 setPreviousEnabled(entry.getPreviousImage() != null); 631 btnDelete.setEnabled(entry.isRemoveSupported()); 632 btnDeleteFromDisk.setEnabled(entry.isDeleteSupported() && entry.isRemoveSupported()); 633 btnCopyPath.setEnabled(true); 634 542 635 setTitle(tr("Geotagged Images") + (!entry.getDisplayName().isEmpty() ? " - " + entry.getDisplayName() : "")); 543 636 StringBuilder osd = new StringBuilder(entry.getDisplayName()); 544 637 if (entry.getElevation() != null) {