Ticket #17050: refactor_geoimagelayer.2.patch
File refactor_geoimagelayer.2.patch, 55.9 KB (added by , 5 years ago) |
---|
-
new file 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 new file mode 100644 index 000000000..8a579e9d7
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data; 3 4 import java.util.ArrayList; 5 import java.util.Collections; 6 import java.util.List; 7 8 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry; 9 import org.openstreetmap.josm.tools.ListenerList; 10 11 /** 12 * Class to hold {@link ImageEntry} and the current selection 13 * @since xxx 14 */ 15 public class ImageData { 16 /** 17 * A listener that is informed when the current selection change 18 */ 19 public interface ImageDataUpdateListener { 20 /** 21 * Called when the selection change 22 * @param data the image data 23 */ 24 void selectedImageChanged(ImageData data); 25 } 26 27 private final List<ImageEntry> data; 28 29 private int selectedImageIndex = -1; 30 31 private final ListenerList<ImageDataUpdateListener> listeners = ListenerList.create(); 32 33 /** 34 * Construct a new image container without images 35 */ 36 public ImageData() { 37 this(null); 38 } 39 40 /** 41 * Construct a new image container with a list of images 42 * @param data the list of {@link ImageEntry} 43 */ 44 public ImageData(List<ImageEntry> data) { 45 if (data != null) { 46 Collections.sort(data); 47 this.data = data; 48 } else { 49 this.data = new ArrayList<>(); 50 } 51 } 52 53 /** 54 * Returns the images 55 * @return the images 56 */ 57 public List<ImageEntry> getImages() { 58 return this.data; 59 } 60 61 /** 62 * Determines if one image has modified GPS data. 63 * @return {@code true} if data has been modified; {@code false}, otherwise 64 */ 65 public boolean isModified() { 66 for (ImageEntry e : data) { 67 if (e.hasNewGpsData()) { 68 return true; 69 } 70 } 71 return false; 72 } 73 74 /** 75 * Merge 2 ImageData 76 * @param data {@link ImageData} 77 */ 78 public void mergeFrom(ImageData data) { 79 this.data.addAll(data.getImages()); 80 Collections.sort(this.data); 81 82 final ImageEntry selected = data.getSelectedImage(); 83 84 // Suppress the double photos. 85 if (this.data.size() > 1) { 86 ImageEntry cur; 87 ImageEntry prev = this.data.get(this.data.size() - 1); 88 for (int i = this.data.size() - 2; i >= 0; i--) { 89 cur = this.data.get(i); 90 if (cur.getFile().equals(prev.getFile())) { 91 this.data.remove(i); 92 } else { 93 prev = cur; 94 } 95 } 96 } 97 if (selected != null) { 98 this.setSelectedImageIndex(this.data.indexOf(selected)); 99 } 100 } 101 102 /** 103 * Return the current selected image 104 * @return the selected image as {@link ImageEntry} or null 105 */ 106 public ImageEntry getSelectedImage() { 107 if (this.selectedImageIndex > -1) { 108 return data.get(this.selectedImageIndex); 109 } 110 return null; 111 } 112 113 /** 114 * Select the first image of the sequence 115 */ 116 public void selectFirstImage() { 117 if (!data.isEmpty()) { 118 this.setSelectedImageIndex(0); 119 } 120 } 121 122 /** 123 * Select the last image of the sequence 124 */ 125 public void selectLastImage() { 126 this.setSelectedImageIndex(data.size() - 1); 127 } 128 129 /** 130 * Check if there is a next image in the sequence 131 * @return {@code true} is there is a next image, {@code false} otherwise 132 */ 133 public boolean hasNextImage() { 134 return (this.selectedImageIndex != data.size() - 1); 135 } 136 137 /** 138 * Select the next image of the sequence 139 */ 140 public void selectNextImage() { 141 if (this.hasNextImage()) { 142 this.setSelectedImageIndex(this.selectedImageIndex + 1); 143 } 144 } 145 146 /** 147 * Check if there is a previous image in the sequence 148 * @return {@code true} is there is a previous image, {@code false} otherwise 149 */ 150 public boolean hasPreviousImage() { 151 return this.selectedImageIndex - 1 > -1; 152 } 153 154 /** 155 * Select the previous image of the sequence 156 */ 157 public void selectPreviousImage() { 158 if (data.isEmpty()) { 159 return; 160 } 161 this.setSelectedImageIndex(Integer.max(0, this.selectedImageIndex - 1)); 162 } 163 164 /** 165 * Select as the selected the given image 166 * @param image the selected image 167 */ 168 public void setSelectedImage(ImageEntry image) { 169 this.setSelectedImageIndex(this.data.indexOf(image)); 170 } 171 172 /** 173 * Clear the selected image 174 */ 175 public void clearSelectedImage() { 176 this.setSelectedImageIndex(-1); 177 } 178 179 private void setSelectedImageIndex(int index) { 180 this.setSelectedImageIndex(index, false); 181 } 182 183 private void setSelectedImageIndex(int index, boolean forceTrigger) { 184 if (index == this.selectedImageIndex && !forceTrigger) { 185 return; 186 } 187 this.selectedImageIndex = index; 188 listeners.fireEvent(l -> l.selectedImageChanged(this)); 189 } 190 191 /** 192 * Remove the current selected image from the list 193 */ 194 public void removeSelectedImage() { 195 data.remove(this.getSelectedImage()); 196 if (this.selectedImageIndex == data.size()) { 197 this.setSelectedImageIndex(data.size() - 1); 198 } else { 199 this.setSelectedImageIndex(this.selectedImageIndex, true); 200 } 201 } 202 203 /** 204 * Add a listener that listens to image data changes 205 * @param listener the {@link ImageDataUpdateListener} 206 */ 207 public void addImageDataUpdateListener(ImageDataUpdateListener listener) { 208 listeners.addListener(listener); 209 } 210 211 /** 212 * Removes a listener that listens to image data changes 213 * @param listener The listener 214 */ 215 public void removeImageDataUpdateListener(ImageDataUpdateListener listener) { 216 listeners.removeListener(listener); 217 } 218 } -
src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java b/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java index 9badaffd1..2488f409c 100644
a b public class CorrelateGpxWithImages extends AbstractAction { 178 178 break; 179 179 case CANCEL: 180 180 if (yLayer != null) { 181 if (yLayer.data != null) { 182 for (ImageEntry ie : yLayer.data) { 183 ie.discardTmp(); 184 } 181 for (ImageEntry ie : yLayer.getImageData().getImages()) { 182 ie.discardTmp(); 185 183 } 184 186 185 yLayer.updateBufferAndRepaint(); 187 186 } 188 187 break; … … public class CorrelateGpxWithImages extends AbstractAction { 216 215 MainApplication.getMap().mapView.zoomTo(bbox); 217 216 } 218 217 219 if (yLayer.data != null) { 220 for (ImageEntry ie : yLayer.data) { 221 ie.applyTmp(); 222 } 218 219 for (ImageEntry ie : yLayer.getImageData().getImages()) { 220 ie.applyTmp(); 223 221 } 224 222 223 225 224 yLayer.updateBufferAndRepaint(); 226 225 227 226 break; … … public class CorrelateGpxWithImages extends AbstractAction { 645 644 JList<String> imgList = new JList<>(new AbstractListModel<String>() { 646 645 @Override 647 646 public String getElementAt(int i) { 648 return yLayer. data.get(i).getFile().getName();647 return yLayer.getImageData().getImages().get(i).getFile().getName(); 649 648 } 650 649 651 650 @Override 652 651 public int getSize() { 653 return yLayer. data != null ? yLayer.data.size() : 0;652 return yLayer.getImageData().getImages().size(); 654 653 } 655 654 }); 656 655 imgList.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 657 656 imgList.getSelectionModel().addListSelectionListener(evt -> { 658 657 int index = imgList.getSelectedIndex(); 659 imgDisp.setImage(yLayer.data.get(index)); 660 Date date = yLayer.data.get(index).getExifTime(); 658 ImageEntry img = yLayer.getImageData().getImages().get(index); 659 imgDisp.setImage(img); 660 Date date = img.getExifTime(); 661 661 if (date != null) { 662 662 DateFormat df = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM); 663 663 lbExifTime.setText(df.format(date)); … … public class CorrelateGpxWithImages extends AbstractAction { 1041 1041 1042 1042 // The selection of images we are about to correlate may have changed. 1043 1043 // So reset all images. 1044 if (yLayer.data != null) { 1045 for (ImageEntry ie: yLayer.data) { 1046 ie.discardTmp(); 1047 } 1044 for (ImageEntry ie: yLayer.getImageData().getImages()) { 1045 ie.discardTmp(); 1048 1046 } 1049 1047 1050 1048 // Construct a list of images that have a date, and sort them on the date. … … public class CorrelateGpxWithImages extends AbstractAction { 1300 1298 * @return matching images 1301 1299 */ 1302 1300 private List<ImageEntry> getSortedImgList(boolean exif, boolean tagged) { 1303 if (yLayer.data == null) { 1304 return Collections.emptyList(); 1305 } 1306 List<ImageEntry> dateImgLst = new ArrayList<>(yLayer.data.size()); 1307 for (ImageEntry e : yLayer.data) { 1301 List<ImageEntry> dateImgLst = new ArrayList<>(yLayer.getImageData().getImages().size()); 1302 for (ImageEntry e : yLayer.getImageData().getImages()) { 1308 1303 if (!e.hasExifTime()) { 1309 1304 continue; 1310 1305 } -
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 efbdac4fd..a31e0f00f 100644
a b import java.io.IOException; 23 23 import java.util.ArrayList; 24 24 import java.util.Arrays; 25 25 import java.util.Collection; 26 import java.util.Collections;27 26 import java.util.HashSet; 28 27 import java.util.LinkedHashSet; 29 28 import java.util.LinkedList; … … import java.util.concurrent.Executors; 34 33 35 34 import javax.swing.Action; 36 35 import javax.swing.Icon; 37 import javax.swing.JLabel;38 36 import javax.swing.JOptionPane; 39 import javax.swing.SwingConstants;40 37 41 38 import org.openstreetmap.josm.actions.LassoModeAction; 42 39 import org.openstreetmap.josm.actions.RenameLayerAction; 43 40 import org.openstreetmap.josm.actions.mapmode.MapMode; 44 41 import org.openstreetmap.josm.actions.mapmode.SelectAction; 45 42 import org.openstreetmap.josm.data.Bounds; 43 import org.openstreetmap.josm.data.ImageData; 44 import org.openstreetmap.josm.data.ImageData.ImageDataUpdateListener; 46 45 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 47 import org.openstreetmap.josm.gui.ExtendedDialog;48 46 import org.openstreetmap.josm.gui.MainApplication; 49 47 import org.openstreetmap.josm.gui.MapFrame; 50 48 import org.openstreetmap.josm.gui.MapFrame.MapModeChangeListener; 51 49 import org.openstreetmap.josm.gui.MapView; 52 50 import org.openstreetmap.josm.gui.NavigatableComponent; 53 51 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 54 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;55 52 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 56 53 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 57 54 import org.openstreetmap.josm.gui.io.importexport.JpgImporter; … … import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToNextMarker; 62 59 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker; 63 60 import org.openstreetmap.josm.gui.layer.Layer; 64 61 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; 65 import org.openstreetmap.josm.gui.util.GuiHelper;66 62 import org.openstreetmap.josm.tools.ImageProvider; 67 63 import org.openstreetmap.josm.tools.Logging; 68 64 import org.openstreetmap.josm.tools.Utils; … … import org.openstreetmap.josm.tools.Utils; 71 67 * Layer displaying geottaged pictures. 72 68 */ 73 69 public class GeoImageLayer extends AbstractModifiableLayer implements 74 JumpToMarkerLayer, NavigatableComponent.ZoomChangeListener {70 JumpToMarkerLayer, NavigatableComponent.ZoomChangeListener, ImageDataUpdateListener { 75 71 76 72 private static List<Action> menuAdditions = new LinkedList<>(); 77 73 78 74 private static volatile List<MapMode> supportedMapModes; 79 75 80 List<ImageEntry>data;76 private final ImageData data; 81 77 GpxLayer gpxLayer; 82 78 83 79 private final Icon icon = ImageProvider.get("dialogs/geoimage/photo-marker"); 84 80 private final Icon selectedIcon = ImageProvider.get("dialogs/geoimage/photo-marker-selected"); 85 81 86 private int currentPhoto = -1;87 88 82 boolean useThumbs; 89 83 private final ExecutorService thumbsLoaderExecutor = 90 84 Executors.newSingleThreadExecutor(Utils.newThreadFactory("thumbnail-loader-%d", Thread.MIN_PRIORITY)); … … public class GeoImageLayer extends AbstractModifiableLayer implements 151 145 */ 152 146 public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer, final String name, boolean useThumbs) { 153 147 super(name != null ? name : tr("Geotagged Images")); 154 if (data != null) { 155 Collections.sort(data); 156 } 157 this.data = data; 148 this.data = new ImageData(data); 158 149 this.gpxLayer = gpxLayer; 159 150 this.useThumbs = useThumbs; 151 this.data.addImageDataUpdateListener(this); 160 152 } 161 153 162 154 /** … … public class GeoImageLayer extends AbstractModifiableLayer implements 218 210 e.extractExif(); 219 211 entries.add(e); 220 212 } 213 221 214 layer = new GeoImageLayer(entries, gpxLayer); 222 215 files.clear(); 223 216 } … … public class GeoImageLayer extends AbstractModifiableLayer implements 292 285 if (layer != null) { 293 286 MainApplication.getLayerManager().addLayer(layer); 294 287 295 if (!canceled && layer.data != null && !layer.data.isEmpty()) {288 if (!canceled && !layer.getImageData().getImages().isEmpty()) { 296 289 boolean noGeotagFound = true; 297 for (ImageEntry e : layer. data) {290 for (ImageEntry e : layer.getImageData().getImages()) { 298 291 if (e.getPos() != null) { 299 292 noGeotagFound = false; 300 293 } … … public class GeoImageLayer extends AbstractModifiableLayer implements 356 349 private String infoText() { 357 350 int tagged = 0; 358 351 int newdata = 0; 359 int n = 0; 360 if (data != null) { 361 n = data.size(); 362 for (ImageEntry e : data) { 363 if (e.getPos() != null) { 364 tagged++; 365 } 366 if (e.hasNewGpsData()) { 367 newdata++; 368 } 352 int n = data.getImages().size(); 353 for (ImageEntry e : data.getImages()) { 354 if (e.getPos() != null) { 355 tagged++; 356 } 357 if (e.hasNewGpsData()) { 358 newdata++; 369 359 } 370 360 } 371 361 return "<html>" … … public class GeoImageLayer extends AbstractModifiableLayer implements 391 381 */ 392 382 @Override 393 383 public boolean isModified() { 394 if (data != null) { 395 for (ImageEntry e : data) { 396 if (e.hasNewGpsData()) { 397 return true; 398 } 399 } 400 } 401 return false; 384 return this.data.isModified(); 402 385 } 403 386 404 387 @Override … … public class GeoImageLayer extends AbstractModifiableLayer implements 417 400 stopLoadThumbs(); 418 401 l.stopLoadThumbs(); 419 402 420 final ImageEntry selected = l.data != null && l.currentPhoto >= 0 ? l.data.get(l.currentPhoto) : null; 421 422 if (l.data != null) { 423 data.addAll(l.data); 424 } 425 Collections.sort(data); 426 427 // Suppress the double photos. 428 if (data.size() > 1) { 429 ImageEntry cur; 430 ImageEntry prev = data.get(data.size() - 1); 431 for (int i = data.size() - 2; i >= 0; i--) { 432 cur = data.get(i); 433 if (cur.getFile().equals(prev.getFile())) { 434 data.remove(i); 435 } else { 436 prev = cur; 437 } 438 } 439 } 440 441 if (selected != null && !data.isEmpty()) { 442 GuiHelper.runInEDTAndWait(() -> { 443 for (int i = 0; i < data.size(); i++) { 444 if (selected.equals(data.get(i))) { 445 currentPhoto = i; 446 ImageViewerDialog.showImage(this, data.get(i)); 447 break; 448 } 449 } 450 }); 451 } 403 this.data.mergeFrom(l.getImageData()); 452 404 453 405 setName(l.getName()); 454 406 thumbsLoaded &= l.thumbsLoaded; … … public class GeoImageLayer extends AbstractModifiableLayer implements 530 482 tempG.fillRect(0, 0, width, height); 531 483 tempG.setComposite(saveComp); 532 484 533 if (data != null) { 534 for (ImageEntry e : data) { 535 paintImage(e, mv, clip, tempG); 536 } 537 if (currentPhoto >= 0 && currentPhoto < data.size()) { 538 // Make sure the selected image is on top in case multiple images overlap. 539 paintImage(data.get(currentPhoto), mv, clip, tempG); 540 } 485 for (ImageEntry e : this.data.getImages()) { 486 paintImage(e, mv, clip, tempG); 487 } 488 if (this.data.getSelectedImage() != null) { 489 // Make sure the selected image is on top in case multiple images overlap. 490 paintImage(this.data.getSelectedImage(), mv, clip, tempG); 541 491 } 542 492 updateOffscreenBuffer = false; 543 493 } 544 494 g.drawImage(offscreenBuffer, 0, 0, null); 545 } else if (data != null){546 for (ImageEntry e : data ) {495 } else { 496 for (ImageEntry e : data.getImages()) { 547 497 if (e.getPos() == null) { 548 498 continue; 549 499 } … … public class GeoImageLayer extends AbstractModifiableLayer implements 554 504 } 555 505 } 556 506 557 if (currentPhoto >= 0 && currentPhoto < data.size()) { 558 ImageEntry e = data.get(currentPhoto); 559 507 ImageEntry e = data.getSelectedImage(); 508 if (e != null) { 560 509 if (e.getPos() != null) { 561 510 Point p = mv.getPoint(e.getPos()); 562 511 … … public class GeoImageLayer extends AbstractModifiableLayer implements 621 570 622 571 @Override 623 572 public void visitBoundingBox(BoundingXYVisitor v) { 624 for (ImageEntry e : data ) {573 for (ImageEntry e : data.getImages()) { 625 574 v.visit(e.getPos()); 626 575 } 627 576 } … … public class GeoImageLayer extends AbstractModifiableLayer implements 630 579 * Show current photo on map and in image viewer. 631 580 */ 632 581 public void showCurrentPhoto() { 633 clearOtherCurrentPhotos(); 634 if (currentPhoto >= 0) { 635 ImageViewerDialog.showImage(this, data.get(currentPhoto)); 636 } else { 637 ImageViewerDialog.showImage(this, null); 582 if (data.getSelectedImage() != null) { 583 clearOtherCurrentPhotos(); 638 584 } 639 585 updateBufferAndRepaint(); 640 586 } 641 587 642 /**643 * Shows next photo.644 */645 public void showNextPhoto() {646 if (data != null && !data.isEmpty()) {647 currentPhoto++;648 if (currentPhoto >= data.size()) {649 currentPhoto = data.size() - 1;650 }651 } else {652 currentPhoto = -1;653 }654 showCurrentPhoto();655 }656 657 /**658 * Shows previous photo.659 */660 public void showPreviousPhoto() {661 if (data != null && !data.isEmpty()) {662 currentPhoto--;663 if (currentPhoto < 0) {664 currentPhoto = 0;665 }666 } else {667 currentPhoto = -1;668 }669 showCurrentPhoto();670 }671 672 /**673 * Shows first photo.674 */675 public void showFirstPhoto() {676 if (data != null && !data.isEmpty()) {677 currentPhoto = 0;678 } else {679 currentPhoto = -1;680 }681 showCurrentPhoto();682 }683 684 /**685 * Shows last photo.686 */687 public void showLastPhoto() {688 if (data != null && !data.isEmpty()) {689 currentPhoto = data.size() - 1;690 } else {691 currentPhoto = -1;692 }693 showCurrentPhoto();694 }695 696 public void checkPreviousNextButtons() {697 ImageViewerDialog.setNextEnabled(data != null && currentPhoto < data.size() - 1);698 ImageViewerDialog.setPreviousEnabled(currentPhoto > 0);699 }700 701 public void removeCurrentPhoto() {702 if (data != null && !data.isEmpty() && currentPhoto >= 0 && currentPhoto < data.size()) {703 data.remove(currentPhoto);704 if (currentPhoto >= data.size()) {705 currentPhoto = data.size() - 1;706 }707 showCurrentPhoto();708 }709 }710 711 public void removeCurrentPhotoFromDisk() {712 ImageEntry toDelete;713 if (data != null && !data.isEmpty() && currentPhoto >= 0 && currentPhoto < data.size()) {714 toDelete = data.get(currentPhoto);715 716 int result = new ExtendedDialog(717 MainApplication.getMainFrame(),718 tr("Delete image file from disk"),719 tr("Cancel"), tr("Delete"))720 .setButtonIcons("cancel", "dialogs/delete")721 .setContent(new JLabel(tr("<html><h3>Delete the file {0} from disk?<p>The image file will be permanently lost!</h3></html>",722 toDelete.getFile().getName()), ImageProvider.get("dialogs/geoimage/deletefromdisk"), SwingConstants.LEFT))723 .toggleEnable("geoimage.deleteimagefromdisk")724 .setCancelButton(1)725 .setDefaultButton(2)726 .showDialog()727 .getValue();728 729 if (result == 2) {730 data.remove(currentPhoto);731 if (currentPhoto >= data.size()) {732 currentPhoto = data.size() - 1;733 }734 735 if (Utils.deleteFile(toDelete.getFile())) {736 Logging.info("File "+toDelete.getFile()+" deleted. ");737 } else {738 JOptionPane.showMessageDialog(739 MainApplication.getMainFrame(),740 tr("Image file could not be deleted."),741 tr("Error"),742 JOptionPane.ERROR_MESSAGE743 );744 }745 746 showCurrentPhoto();747 }748 }749 }750 751 public void copyCurrentPhotoPath() {752 if (data != null && !data.isEmpty() && currentPhoto >= 0 && currentPhoto < data.size()) {753 ClipboardUtils.copyString(data.get(currentPhoto).getFile().toString());754 }755 }756 757 /**758 * Removes a photo from the list of images by index.759 * @param idx Image index760 * @since 6392761 */762 public void removePhotoByIdx(int idx) {763 if (idx >= 0 && data != null && idx < data.size()) {764 data.remove(idx);765 }766 }767 768 588 /** 769 589 * Check if the position of the mouse event is within the rectangle of the photo icon or thumbnail. 770 * @param idx Image index, range 0 .. size-1590 * @param idx the image index 771 591 * @param evt Mouse event 772 592 * @return {@code true} if the photo matches the mouse position, {@code false} otherwise 773 593 */ 774 594 private boolean isPhotoIdxUnderMouse(int idx, MouseEvent evt) { 775 if (idx >= 0 && data != null && idx < data.size()) { 776 ImageEntry img = data.get(idx); 777 if (img.getPos() != null) { 778 Point imgCenter = MainApplication.getMap().mapView.getPoint(img.getPos()); 779 Rectangle imgRect; 780 if (useThumbs && img.hasThumbnail()) { 781 Dimension imgDim = scaledDimension(img.getThumbnail()); 782 if (imgDim != null) { 783 imgRect = new Rectangle(imgCenter.x - imgDim.width / 2, 784 imgCenter.y - imgDim.height / 2, 785 imgDim.width, imgDim.height); 786 } else { 787 imgRect = null; 788 } 595 ImageEntry img = this.data.getImages().get(idx); 596 if (img.getPos() != null) { 597 Point imgCenter = MainApplication.getMap().mapView.getPoint(img.getPos()); 598 Rectangle imgRect; 599 if (useThumbs && img.hasThumbnail()) { 600 Dimension imgDim = scaledDimension(img.getThumbnail()); 601 if (imgDim != null) { 602 imgRect = new Rectangle(imgCenter.x - imgDim.width / 2, 603 imgCenter.y - imgDim.height / 2, 604 imgDim.width, imgDim.height); 789 605 } else { 790 imgRect = new Rectangle(imgCenter.x - icon.getIconWidth() / 2, 791 imgCenter.y - icon.getIconHeight() / 2, 792 icon.getIconWidth(), icon.getIconHeight()); 793 } 794 if (imgRect != null && imgRect.contains(evt.getPoint())) { 795 return true; 606 imgRect = null; 796 607 } 608 } else { 609 imgRect = new Rectangle(imgCenter.x - icon.getIconWidth() / 2, 610 imgCenter.y - icon.getIconHeight() / 2, 611 icon.getIconWidth(), icon.getIconHeight()); 612 } 613 if (imgRect != null && imgRect.contains(evt.getPoint())) { 614 return true; 797 615 } 798 616 } 799 617 return false; … … public class GeoImageLayer extends AbstractModifiableLayer implements 809 627 * or {@code -1} if there is no image at the mouse position 810 628 */ 811 629 private int getPhotoIdxUnderMouse(MouseEvent evt, boolean cycle) { 812 if (data != null) { 813 if (cycle && currentPhoto >= 0) { 814 // Cycle loop is forward as that is the natural order. 815 // Loop 1: One after current photo up to last one. 816 for (int idx = currentPhoto + 1; idx < data.size(); ++idx) { 817 if (isPhotoIdxUnderMouse(idx, evt)) { 818 return idx; 819 } 820 } 821 // Loop 2: First photo up to current one. 822 for (int idx = 0; idx <= currentPhoto; ++idx) { 823 if (isPhotoIdxUnderMouse(idx, evt)) { 824 return idx; 825 } 630 ImageEntry selectedImage = this.data.getSelectedImage(); 631 int selectedIndex = this.data.getImages().indexOf(selectedImage); 632 633 if (cycle && selectedImage != null) { 634 // Cycle loop is forward as that is the natural order. 635 // Loop 1: One after current photo up to last one. 636 for (int idx = selectedIndex + 1; idx < this.data.getImages().size(); ++idx) { 637 if (isPhotoIdxUnderMouse(idx, evt)) { 638 return idx; 826 639 } 827 } else { 828 // Check for current photo first, i.e. keep it selected if it is under the mouse. 829 if (currentPhoto >= 0 && isPhotoIdxUnderMouse(currentPhoto, evt)) { 830 return currentPhoto; 640 } 641 // Loop 2: First photo up to current one. 642 for (int idx = 0; idx <= selectedIndex; ++idx) { 643 if (isPhotoIdxUnderMouse(idx, evt)) { 644 return idx; 831 645 } 832 // Loop from last to first to prefer topmost image. 833 for (int idx = data.size() - 1; idx >= 0; --idx) { 834 if (isPhotoIdxUnderMouse(idx, evt)) { 835 return idx; 836 } 646 } 647 } else { 648 // Check for current photo first, i.e. keep it selected if it is under the mouse. 649 if (selectedImage != null && isPhotoIdxUnderMouse(selectedIndex, evt)) { 650 return selectedIndex; 651 } 652 // Loop from last to first to prefer topmost image. 653 for (int idx = this.data.getImages().size() - 1; idx >= 0; --idx) { 654 if (isPhotoIdxUnderMouse(idx, evt)) { 655 return idx; 837 656 } 838 657 } 839 658 } … … public class GeoImageLayer extends AbstractModifiableLayer implements 861 680 public ImageEntry getPhotoUnderMouse(MouseEvent evt) { 862 681 int idx = getPhotoIdxUnderMouse(evt); 863 682 if (idx >= 0) { 864 return data.get(idx);683 return this.data.getImages().get(idx); 865 684 } else { 866 685 return null; 867 686 } 868 687 } 869 688 870 /**871 * Clears the currentPhoto, i.e. remove select marker, and optionally repaint.872 * @param repaint Repaint flag873 * @since 6392874 */875 public void clearCurrentPhoto(boolean repaint) {876 currentPhoto = -1;877 if (repaint) {878 updateBufferAndRepaint();879 }880 }881 882 689 /** 883 690 * Clears the currentPhoto of the other GeoImageLayer's. Otherwise there could be multiple selected photos. 884 691 */ … … public class GeoImageLayer extends AbstractModifiableLayer implements 886 693 for (GeoImageLayer layer: 887 694 MainApplication.getLayerManager().getLayersOfType(GeoImageLayer.class)) { 888 695 if (layer != this) { 889 layer. clearCurrentPhoto(false);696 layer.getImageData().clearSelectedImage(); 890 697 } 891 698 } 892 699 } … … public class GeoImageLayer extends AbstractModifiableLayer implements 947 754 public void mouseReleased(MouseEvent ev) { 948 755 if (ev.getButton() != MouseEvent.BUTTON1) 949 756 return; 950 if ( data == null ||!isVisible() || !isMapModeOk())757 if (!isVisible() || !isMapModeOk()) 951 758 return; 952 759 953 760 Point mousePos = ev.getPoint(); … … public class GeoImageLayer extends AbstractModifiableLayer implements 956 763 if (idx >= 0) { 957 764 lastSelPos = mousePos; 958 765 cycleModeArmed = false; 959 currentPhoto = idx; 960 showCurrentPhoto(); 766 data.setSelectedImage(data.getImages().get(idx)); 961 767 } 962 768 } 963 769 }; … … public class GeoImageLayer extends AbstractModifiableLayer implements 1012 818 mapView.removeMouseMotionListener(mouseMotionAdapter); 1013 819 MapFrame.removeMapModeChangeListener(mapModeListener); 1014 820 MainApplication.getLayerManager().removeActiveLayerChangeListener(activeLayerChangeListener); 1015 currentPhoto = -1; 1016 if (data != null) { 1017 data.clear(); 1018 } 1019 data = null; 821 this.data.removeImageDataUpdateListener(this); 1020 822 } 1021 823 1022 824 @Override … … public class GeoImageLayer extends AbstractModifiableLayer implements 1082 884 * @return List of images in layer 1083 885 */ 1084 886 public List<ImageEntry> getImages() { 1085 return data == null ? Collections.<ImageEntry>emptyList() : new ArrayList<>(data); 887 return new ArrayList<>(this.data.getImages()); 888 } 889 890 /** 891 * Returns the image data store being used by this layer 892 * @return imageData 893 * @since xxx 894 */ 895 public ImageData getImageData() { 896 return data; 1086 897 } 1087 898 1088 899 /** … … public class GeoImageLayer extends AbstractModifiableLayer implements 1095 906 1096 907 @Override 1097 908 public void jumpToNextMarker() { 1098 showNextPhoto();909 this.data.selectNextImage(); 1099 910 } 1100 911 1101 912 @Override 1102 913 public void jumpToPreviousMarker() { 1103 showPreviousPhoto();914 this.data.selectPreviousImage(); 1104 915 } 1105 916 1106 917 /** … … public class GeoImageLayer extends AbstractModifiableLayer implements 1127 938 } 1128 939 invalidate(); 1129 940 } 941 942 @Override 943 public void selectedImageChanged(ImageData data) { 944 this.showCurrentPhoto(); 945 } 1130 946 } -
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 53cf7ee8a..35f300db2 100644
a b import java.awt.event.KeyEvent; 13 13 import java.awt.event.WindowEvent; 14 14 import java.text.DateFormat; 15 15 import java.text.SimpleDateFormat; 16 import java.util.Objects; 16 17 17 18 import javax.swing.Box; 18 19 import javax.swing.JButton; 20 import javax.swing.JLabel; 21 import javax.swing.JOptionPane; 19 22 import javax.swing.JPanel; 20 23 import javax.swing.JToggleButton; 24 import javax.swing.SwingConstants; 21 25 22 26 import org.openstreetmap.josm.actions.JosmAction; 27 import org.openstreetmap.josm.data.ImageData; 28 import org.openstreetmap.josm.data.ImageData.ImageDataUpdateListener; 29 import org.openstreetmap.josm.gui.ExtendedDialog; 23 30 import org.openstreetmap.josm.gui.MainApplication; 31 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils; 24 32 import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action; 25 33 import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 26 34 import org.openstreetmap.josm.gui.layer.Layer; … … import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; 31 39 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent; 32 40 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; 33 41 import org.openstreetmap.josm.tools.ImageProvider; 42 import org.openstreetmap.josm.tools.Logging; 34 43 import org.openstreetmap.josm.tools.Shortcut; 44 import org.openstreetmap.josm.tools.Utils; 35 45 import org.openstreetmap.josm.tools.date.DateUtils; 36 46 37 47 /** 38 48 * Dialog to view and manipulate geo-tagged images from a {@link GeoImageLayer}. 39 49 */ 40 public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener {50 public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener, ImageDataUpdateListener { 41 51 42 52 private final ImageZoomAction imageZoomAction = new ImageZoomAction(); 43 53 private final ImageCenterViewAction imageCenterViewAction = new ImageCenterViewAction(); … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 79 89 private JButton btnPrevious; 80 90 private JButton btnFirst; 81 91 private JButton btnCollapse; 92 private JButton btnDelete; 93 private JButton btnCopyPath; 94 private JButton btnDeleteFromDisk; 82 95 private JToggleButton tbCentre; 83 96 84 97 private ImageViewerDialog() { … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 87 100 build(); 88 101 MainApplication.getLayerManager().addActiveLayerChangeListener(this); 89 102 MainApplication.getLayerManager().addLayerChangeListener(this); 103 for (Layer l: MainApplication.getLayerManager().getLayers()) { 104 this.registerOnLayer(l); 105 } 90 106 } 91 107 92 108 private static JButton createNavigationButton(JosmAction action, Dimension buttonDim) { … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 106 122 btnFirst = createNavigationButton(imageFirstAction, buttonDim); 107 123 btnPrevious = createNavigationButton(imagePreviousAction, buttonDim); 108 124 109 JButtonbtnDelete = new JButton(imageRemoveAction);125 btnDelete = new JButton(imageRemoveAction); 110 126 btnDelete.setPreferredSize(buttonDim); 111 127 112 JButtonbtnDeleteFromDisk = new JButton(imageRemoveFromDiskAction);128 btnDeleteFromDisk = new JButton(imageRemoveFromDiskAction); 113 129 btnDeleteFromDisk.setPreferredSize(buttonDim); 114 130 115 JButtonbtnCopyPath = new JButton(imageCopyPathAction);131 btnCopyPath = new JButton(imageCopyPathAction); 116 132 btnCopyPath.setPreferredSize(buttonDim); 117 133 118 134 btnNext = createNavigationButton(imageNextAction, buttonDim); … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 189 205 190 206 @Override 191 207 public void actionPerformed(ActionEvent e) { 192 if (current Layer!= null) {193 current Layer.showNextPhoto();208 if (currentData != null) { 209 currentData.selectNextImage(); 194 210 } 195 211 } 196 212 } … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 204 220 205 221 @Override 206 222 public void actionPerformed(ActionEvent e) { 207 if (current Layer!= null) {208 current Layer.showPreviousPhoto();223 if (currentData != null) { 224 currentData.selectPreviousImage(); 209 225 } 210 226 } 211 227 } … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 219 235 220 236 @Override 221 237 public void actionPerformed(ActionEvent e) { 222 if (current Layer!= null) {223 current Layer.showFirstPhoto();238 if (currentData != null) { 239 currentData.selectFirstImage(); 224 240 } 225 241 } 226 242 } … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 234 250 235 251 @Override 236 252 public void actionPerformed(ActionEvent e) { 237 if (current Layer!= null) {238 current Layer.showLastPhoto();253 if (currentData != null) { 254 currentData.selectLastImage(); 239 255 } 240 256 } 241 257 } … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 277 293 278 294 @Override 279 295 public void actionPerformed(ActionEvent e) { 280 if (current Layer!= null) {281 current Layer.removeCurrentPhoto();296 if (currentData != null) { 297 currentData.removeSelectedImage(); 282 298 } 283 299 } 284 300 } … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 293 309 294 310 @Override 295 311 public void actionPerformed(ActionEvent e) { 296 if (currentLayer != null) { 297 currentLayer.removeCurrentPhotoFromDisk(); 312 if (currentData != null && currentData.getSelectedImage() != null) { 313 ImageEntry toDelete = currentData.getSelectedImage(); 314 315 int result = new ExtendedDialog( 316 MainApplication.getMainFrame(), 317 tr("Delete image file from disk"), 318 tr("Cancel"), tr("Delete")) 319 .setButtonIcons("cancel", "dialogs/delete") 320 .setContent(new JLabel(tr("<html><h3>Delete the file {0} from disk?" 321 + "<p>The image file will be permanently lost!</h3></html>", 322 toDelete.getFile().getName()), ImageProvider.get("dialogs/geoimage/deletefromdisk"), SwingConstants.LEFT)) 323 .toggleEnable("geoimage.deleteimagefromdisk") 324 .setCancelButton(1) 325 .setDefaultButton(2) 326 .showDialog() 327 .getValue(); 328 329 if (result == 2) { 330 currentData.removeSelectedImage(); 331 332 if (Utils.deleteFile(toDelete.getFile())) { 333 Logging.info("File "+toDelete.getFile()+" deleted. "); 334 } else { 335 JOptionPane.showMessageDialog( 336 MainApplication.getMainFrame(), 337 tr("Image file could not be deleted."), 338 tr("Error"), 339 JOptionPane.ERROR_MESSAGE 340 ); 341 } 342 } 298 343 } 299 344 } 300 345 } … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 308 353 309 354 @Override 310 355 public void actionPerformed(ActionEvent e) { 311 if (current Layer!= null) {312 currentLayer.copyCurrentPhotoPath();356 if (currentData != null) { 357 ClipboardUtils.copyString(currentData.getSelectedImage().getFile().toString()); 313 358 } 314 359 } 315 360 } … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 329 374 330 375 /** 331 376 * Displays image for the given layer. 332 * @param layergeo image layer377 * @param data geo image layer 333 378 * @param entry image entry 334 379 */ 335 public static void showImage(GeoImageLayer layer, ImageEntry entry) { 336 getInstance().displayImage(layer, entry); 337 if (layer != null) { 338 layer.checkPreviousNextButtons(); 339 } else { 340 setPreviousEnabled(false); 341 setNextEnabled(false); 342 } 380 public static void showImage(ImageData data, ImageEntry entry) { 381 getInstance().displayImage(data, entry); 343 382 } 344 383 345 384 /** 346 385 * Enables (or disables) the "Previous" button. 347 386 * @param value {@code true} to enable the button, {@code false} otherwise 348 387 */ 349 public staticvoid setPreviousEnabled(boolean value) {350 getInstance().btnFirst.setEnabled(value);351 getInstance().btnPrevious.setEnabled(value);388 public void setPreviousEnabled(boolean value) { 389 this.btnFirst.setEnabled(value); 390 this.btnPrevious.setEnabled(value); 352 391 } 353 392 354 393 /** 355 394 * Enables (or disables) the "Next" button. 356 395 * @param value {@code true} to enable the button, {@code false} otherwise 357 396 */ 358 public staticvoid setNextEnabled(boolean value) {359 getInstance().btnNext.setEnabled(value);360 getInstance().btnLast.setEnabled(value);397 public void setNextEnabled(boolean value) { 398 this.btnNext.setEnabled(value); 399 this.btnLast.setEnabled(value); 361 400 } 362 401 363 402 /** … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 373 412 return wasEnabled; 374 413 } 375 414 376 private transient GeoImageLayer currentLayer;415 private transient ImageData currentData; 377 416 private transient ImageEntry currentEntry; 378 417 379 418 /** 380 419 * Displays image for the given layer. 381 * @param layer geo image layer420 * @param data the image data 382 421 * @param entry image entry 383 422 */ 384 public void displayImage( GeoImageLayer layer, ImageEntry entry) {423 public void displayImage(ImageData data, ImageEntry entry) { 385 424 boolean imageChanged; 386 425 387 426 synchronized (this) { … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 393 432 MainApplication.getMap().mapView.zoomTo(entry.getPos()); 394 433 } 395 434 396 current Layer = layer;435 currentData = data; 397 436 currentEntry = entry; 398 437 } 399 438 439 400 440 if (entry != null) { 441 Objects.requireNonNull(data, "data cannot be null!"); 442 this.setNextEnabled(data.hasNextImage()); 443 this.setPreviousEnabled(data.hasPreviousImage()); 444 btnDelete.setEnabled(true); 445 btnDeleteFromDisk.setEnabled(true); 446 btnCopyPath.setEnabled(true); 447 401 448 if (imageChanged) { 402 449 // Set only if the image is new to preserve zoom and position if the same image is redisplayed 403 450 // (e.g. to update the OSD). … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 436 483 setTitle(tr("Geotagged Images")); 437 484 imgDisplay.setImage(null); 438 485 imgDisplay.setOsdText(""); 486 this.setNextEnabled(false); 487 this.setPreviousEnabled(false); 488 btnDelete.setEnabled(false); 489 btnDeleteFromDisk.setEnabled(false); 490 btnCopyPath.setEnabled(false); 439 491 return; 440 492 } 441 493 if (!isDialogShowing()) { … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 487 539 return getInstance().currentEntry; 488 540 } 489 541 490 /**491 * Returns the layer associated with the image.492 * @return Layer associated with the image493 * @since 6392494 */495 public static GeoImageLayer getCurrentLayer() {496 return getInstance().currentLayer;497 }498 499 542 /** 500 543 * Returns whether the center view is currently active. 501 544 * @return {@code true} if the center view is active, {@code false} otherwise … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 507 550 508 551 @Override 509 552 public void layerAdded(LayerAddEvent e) { 553 this.registerOnLayer(e.getAddedLayer()); 510 554 showLayer(e.getAddedLayer()); 511 555 } 512 556 513 557 @Override 514 558 public void layerRemoving(LayerRemoveEvent e) { 515 // Clear current image and layer if current layer is deleted 516 if (currentLayer != null && currentLayer.equals(e.getRemovedLayer())) { 517 showImage(null, null); 518 } 519 // Check buttons state in case of layer merging 520 if (currentLayer != null && e.getRemovedLayer() instanceof GeoImageLayer) { 521 currentLayer.checkPreviousNextButtons(); 559 if (e.getRemovedLayer() instanceof GeoImageLayer) { 560 if (((GeoImageLayer) e.getRemovedLayer()).getImageData() == currentData) { 561 displayImage(null, null); 562 } 563 ((GeoImageLayer) e.getRemovedLayer()).getImageData().removeImageDataUpdateListener(this); 522 564 } 523 565 } 524 566 … … public final class ImageViewerDialog extends ToggleDialog implements LayerChange 532 574 showLayer(e.getSource().getActiveLayer()); 533 575 } 534 576 577 private void registerOnLayer(Layer layer) { 578 if (layer instanceof GeoImageLayer) { 579 ((GeoImageLayer) layer).getImageData().addImageDataUpdateListener(this); 580 } 581 } 582 535 583 private void showLayer(Layer newLayer) { 536 if (current Layer== null && newLayer instanceof GeoImageLayer) {537 ((GeoImageLayer) newLayer). showFirstPhoto();584 if (currentData == null && newLayer instanceof GeoImageLayer) { 585 ((GeoImageLayer) newLayer).getImageData().selectFirstImage(); 538 586 } 539 587 } 588 589 @Override 590 public void selectedImageChanged(ImageData data) { 591 showImage(data, data.getSelectedImage()); 592 } 540 593 } -
src/org/openstreetmap/josm/gui/layer/geoimage/ShowThumbnailAction.java
diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ShowThumbnailAction.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ShowThumbnailAction.java index 7a7b62791..5f75cad5f 100644
a b public class ShowThumbnailAction extends AbstractAction implements LayerAction { 49 49 * {@code false} otherwise 50 50 */ 51 51 private static boolean enabled(GeoImageLayer layer) { 52 return layer.data != null && !layer.data.isEmpty();52 return !layer.getImageData().getImages().isEmpty(); 53 53 } 54 54 55 55 /** Create actual menu entry and define if it is enabled or not. */ -
src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java
diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java index a49bbc8a3..0ead613c5 100644
a b public class ThumbsLoader implements Runnable { 51 51 * @param layer geoimage layer 52 52 */ 53 53 public ThumbsLoader(GeoImageLayer layer) { 54 this(new ArrayList<>(layer. data), layer);54 this(new ArrayList<>(layer.getImageData().getImages()), layer); 55 55 } 56 56 57 57 /** -
new file 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 new file mode 100644 index 000000000..8ce314ff0
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data; 3 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertFalse; 6 import static org.junit.Assert.assertNull; 7 import static org.junit.Assert.assertTrue; 8 9 import java.io.File; 10 import java.util.ArrayList; 11 import java.util.Collections; 12 import java.util.List; 13 14 import org.junit.Test; 15 import org.openstreetmap.josm.data.ImageData.ImageDataUpdateListener; 16 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry; 17 18 import mockit.Expectations; 19 import mockit.Mock; 20 import mockit.MockUp; 21 22 /** 23 * Unit tests for class {@link ImageData}. 24 */ 25 public class ImageDataTest { 26 27 private List<ImageEntry> getOneImage() { 28 ArrayList<ImageEntry> list = new ArrayList<>(); 29 list.add(new ImageEntry(new File("test"))); 30 return list; 31 } 32 33 @Test 34 public void testWithullData() { 35 ImageData data = new ImageData(); 36 assertEquals(0, data.getImages().size()); 37 assertNull(data.getSelectedImage()); 38 data.selectFirstImage(); 39 assertNull(data.getSelectedImage()); 40 data.selectLastImage(); 41 assertNull(data.getSelectedImage()); 42 data.selectFirstImage(); 43 assertNull(data.getSelectedImage()); 44 data.selectPreviousImage(); 45 assertNull(data.getSelectedImage()); 46 assertFalse(data.hasNextImage()); 47 assertFalse(data.hasPreviousImage()); 48 data.removeSelectedImage(); 49 } 50 51 @Test 52 public void testmageEntryWithImages() { 53 assertEquals(1, new ImageData(this.getOneImage()).getImages().size()); 54 } 55 56 @Test 57 public void testSortData() { 58 List<ImageEntry> list = this.getOneImage(); 59 60 new Expectations(Collections.class) {{ 61 Collections.sort(list); 62 }}; 63 64 new ImageData(list); 65 } 66 67 @Test 68 public void testIsModifiedFalse() { 69 assertFalse(new ImageData(this.getOneImage()).isModified()); 70 } 71 72 @Test 73 public void testIsModifiedTrue() { 74 List<ImageEntry> list = this.getOneImage(); 75 76 new Expectations(list.get(0)) {{ 77 list.get(0).hasNewGpsData(); result = true; 78 }}; 79 80 assertTrue(new ImageData(list).isModified()); 81 } 82 83 @Test 84 public void testSelectFirstImage() { 85 List<ImageEntry> list = this.getOneImage(); 86 87 ImageData data = new ImageData(list); 88 data.selectFirstImage(); 89 assertEquals(list.get(0), data.getSelectedImage()); 90 } 91 92 @Test 93 public void testSelectLastImage() { 94 List<ImageEntry> list = this.getOneImage(); 95 list.add(new ImageEntry()); 96 97 ImageData data = new ImageData(list); 98 data.selectLastImage(); 99 assertEquals(list.get(1), data.getSelectedImage()); 100 } 101 102 @Test 103 public void testSelectNextImage() { 104 List<ImageEntry> list = this.getOneImage(); 105 106 ImageData data = new ImageData(list); 107 assertTrue(data.hasNextImage()); 108 data.selectNextImage(); 109 assertEquals(list.get(0), data.getSelectedImage()); 110 assertFalse(data.hasNextImage()); 111 data.selectNextImage(); 112 assertEquals(list.get(0), data.getSelectedImage()); 113 } 114 115 @Test 116 public void testSelectPreviousImage() { 117 List<ImageEntry> list = this.getOneImage(); 118 list.add(new ImageEntry()); 119 120 ImageData data = new ImageData(list); 121 assertFalse(data.hasPreviousImage()); 122 data.selectLastImage(); 123 assertTrue(data.hasPreviousImage()); 124 data.selectPreviousImage(); 125 assertEquals(list.get(0), data.getSelectedImage()); 126 data.selectPreviousImage(); 127 assertEquals(list.get(0), data.getSelectedImage()); 128 } 129 130 @Test 131 public void testSetSelectedImage() { 132 List<ImageEntry> list = this.getOneImage(); 133 134 ImageData data = new ImageData(list); 135 data.setSelectedImage(list.get(0)); 136 assertEquals(list.get(0), data.getSelectedImage()); 137 } 138 139 @Test 140 public void testClearSelectedImage() { 141 List<ImageEntry> list = this.getOneImage(); 142 143 ImageData data = new ImageData(list); 144 data.setSelectedImage(list.get(0)); 145 data.clearSelectedImage(); 146 assertNull(data.getSelectedImage()); 147 } 148 149 @Test 150 public void testSelectionListener() { 151 List<ImageEntry> list = this.getOneImage(); 152 ImageData data = new ImageData(list); 153 ImageDataUpdateListener listener = new ImageDataUpdateListener() { 154 @Override 155 public void selectedImageChanged(ImageData data) {} 156 }; 157 new Expectations(listener) {{ 158 listener.selectedImageChanged(data); times = 1; 159 }}; 160 data.addImageDataUpdateListener(listener); 161 data.selectFirstImage(); 162 data.selectFirstImage(); 163 } 164 165 @Test 166 public void testRemoveSelectedImage() { 167 List<ImageEntry> list = this.getOneImage(); 168 ImageData data = new ImageData(list); 169 data.selectFirstImage(); 170 data.removeSelectedImage(); 171 assertEquals(0, data.getImages().size()); 172 assertNull(data.getSelectedImage()); 173 } 174 175 @Test 176 public void testRemoveSelectedWithImageTriggerListener() { 177 List<ImageEntry> list = this.getOneImage(); 178 list.add(new ImageEntry()); 179 ImageData data = new ImageData(list); 180 ImageDataUpdateListener listener = new ImageDataUpdateListener() { 181 @Override 182 public void selectedImageChanged(ImageData data) {} 183 }; 184 new Expectations(listener) {{ 185 listener.selectedImageChanged(data); times = 2; 186 }}; 187 data.addImageDataUpdateListener(listener); 188 data.selectFirstImage(); 189 data.removeSelectedImage(); 190 } 191 192 @Test 193 public void testMergeFrom() { 194 ImageEntry image = new ImageEntry(new File("test2")); 195 List<ImageEntry> list1 = this.getOneImage(); 196 list1.add(image); 197 List<ImageEntry> list2 = this.getOneImage(); 198 list2.add(new ImageEntry(new File("test3"))); 199 200 ImageData data = new ImageData(list1); 201 data.setSelectedImage(list1.get(0)); 202 ImageData data2 = new ImageData(list2); 203 204 new MockUp<Collections>() { 205 @Mock 206 public void sort(List<ImageEntry> o) { 207 list1.remove(image); 208 list1.add(image); 209 } 210 }; 211 212 data.mergeFrom(data2); 213 assertEquals(3, data.getImages().size()); 214 assertEquals(list1.get(0), data.getSelectedImage()); 215 } 216 217 @Test 218 public void testMergeFromSelectedImage() { 219 ImageEntry image = new ImageEntry(new File("test2")); 220 List<ImageEntry> list1 = this.getOneImage(); 221 list1.add(image); 222 List<ImageEntry> list2 = this.getOneImage(); 223 224 ImageData data = new ImageData(list1); 225 ImageData data2 = new ImageData(list2); 226 data2.setSelectedImage(list2.get(0)); 227 228 data.mergeFrom(data2); 229 assertEquals(3, data.getImages().size()); 230 assertEquals(list2.get(0), data.getSelectedImage()); 231 } 232 }