Ticket #14181: GeoImageLayer_select.patch
| File GeoImageLayer_select.patch, 16.4 KB (added by , 9 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
16 16 import java.awt.RenderingHints; 17 17 import java.awt.event.MouseAdapter; 18 18 import java.awt.event.MouseEvent; 19 import java.awt.event.MouseMotionAdapter; 19 20 import java.awt.image.BufferedImage; 20 21 import java.beans.PropertyChangeEvent; 21 22 import java.beans.PropertyChangeListener; … … 97 98 boolean updateOffscreenBuffer = true; 98 99 99 100 private MouseAdapter mouseAdapter; 101 private MouseMotionAdapter mouseMotionAdapter; 100 102 private MapModeChangeListener mapModeListener; 101 103 104 /** Mouse position where the last image was selected. */ 105 private Point lastSelPos = null; 102 106 /** 107 * Image cycle mode flag. 108 * It is possible that a mouse button release triggers multiple 109 * mouseReleased() events. To prevent the cycling in such a case we wait 110 * for the next mouse button press event before it is cycled to the next 111 * image. 112 */ 113 private boolean cycleModeArmed = false; 114 115 /** 103 116 * Constructs a new {@code GeoImageLayer}. 104 117 * @param data The list of images to display 105 118 * @param gpxLayer The associated GPX layer … … 467 480 (int) Math.round(f * thumb.getHeight(null))); 468 481 } 469 482 483 /** 484 * Paint one image. 485 * @param e Image to be painted 486 * @param mv Map view 487 * @param clip Bounding rectangle of the current clipping area 488 * @param tempG Temporary offscreen buffer 489 */ 490 private void paintImage(ImageEntry e, MapView mv, Rectangle clip, Graphics2D tempG) { 491 if (e.getPos() == null) { 492 return; 493 } 494 Point p = mv.getPoint(e.getPos()); 495 if (e.hasThumbnail()) { 496 Dimension d = scaledDimension(e.getThumbnail()); 497 if (d != null) { 498 Rectangle target = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height); 499 if (clip.intersects(target)) { 500 tempG.drawImage(e.getThumbnail(), target.x, target.y, target.width, target.height, null); 501 } 502 } 503 } else { // thumbnail not loaded yet 504 icon.paintIcon(mv, tempG, 505 p.x - icon.getIconWidth() / 2, 506 p.y - icon.getIconHeight() / 2); 507 } 508 } 509 470 510 @Override 471 511 public void paint(Graphics2D g, MapView mv, Bounds bounds) { 472 512 int width = mv.getWidth(); … … 494 534 495 535 if (data != null) { 496 536 for (ImageEntry e : data) { 497 if (e.getPos() == null) { 498 continue; 499 } 500 Point p = mv.getPoint(e.getPos()); 501 if (e.hasThumbnail()) { 502 Dimension d = scaledDimension(e.getThumbnail()); 503 if (d != null) { 504 Rectangle target = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height); 505 if (clip.intersects(target)) { 506 tempG.drawImage(e.getThumbnail(), target.x, target.y, target.width, target.height, null); 507 } 508 } 509 } else { // thumbnail not loaded yet 510 icon.paintIcon(mv, tempG, 511 p.x - icon.getIconWidth() / 2, 512 p.y - icon.getIconHeight() / 2); 513 } 537 paintImage(e, mv, clip, tempG); 514 538 } 539 if (currentPhoto >= 0 && currentPhoto < data.size()) { 540 // Make sure the selected image is on top in case multiple images overlap. 541 paintImage(data.get(currentPhoto), mv, clip, tempG); 542 } 515 543 } 516 544 updateOffscreenBuffer = false; 517 545 } … … 601 629 } 602 630 603 631 /** 632 * Show current photo on map and in image viewer. 633 */ 634 public void showCurrentPhoto() { 635 clearOtherCurrentPhotos(); 636 if (currentPhoto >= 0) { 637 ImageViewerDialog.showImage(this, data.get(currentPhoto)); 638 } else { 639 ImageViewerDialog.showImage(this, null); 640 } 641 updateOffscreenBuffer = true; 642 Main.map.repaint(); 643 } 644 645 /** 604 646 * Shows next photo. 605 647 */ 606 648 public void showNextPhoto() { … … 609 651 if (currentPhoto >= data.size()) { 610 652 currentPhoto = data.size() - 1; 611 653 } 612 ImageViewerDialog.showImage(this, data.get(currentPhoto));613 654 } else { 614 655 currentPhoto = -1; 615 656 } 616 Main.map.repaint();657 showCurrentPhoto(); 617 658 } 618 659 619 660 /** … … 625 666 if (currentPhoto < 0) { 626 667 currentPhoto = 0; 627 668 } 628 ImageViewerDialog.showImage(this, data.get(currentPhoto));629 669 } else { 630 670 currentPhoto = -1; 631 671 } 632 Main.map.repaint();672 showCurrentPhoto(); 633 673 } 634 674 635 675 /** … … 638 678 public void showFirstPhoto() { 639 679 if (data != null && !data.isEmpty()) { 640 680 currentPhoto = 0; 641 ImageViewerDialog.showImage(this, data.get(currentPhoto));642 681 } else { 643 682 currentPhoto = -1; 644 683 } 645 Main.map.repaint();684 showCurrentPhoto(); 646 685 } 647 686 648 687 /** … … 651 690 public void showLastPhoto() { 652 691 if (data != null && !data.isEmpty()) { 653 692 currentPhoto = data.size() - 1; 654 ImageViewerDialog.showImage(this, data.get(currentPhoto));655 693 } else { 656 694 currentPhoto = -1; 657 695 } 658 Main.map.repaint();696 showCurrentPhoto(); 659 697 } 660 698 661 699 public void checkPreviousNextButtons() { … … 669 707 if (currentPhoto >= data.size()) { 670 708 currentPhoto = data.size() - 1; 671 709 } 672 if (currentPhoto >= 0) { 673 ImageViewerDialog.showImage(this, data.get(currentPhoto)); 674 } else { 675 ImageViewerDialog.showImage(this, null); 676 } 677 updateOffscreenBuffer = true; 678 Main.map.repaint(); 710 showCurrentPhoto(); 679 711 } 680 712 } 681 713 … … 702 734 if (currentPhoto >= data.size()) { 703 735 currentPhoto = data.size() - 1; 704 736 } 705 if (currentPhoto >= 0) {706 ImageViewerDialog.showImage(this, data.get(currentPhoto));707 } else {708 ImageViewerDialog.showImage(this, null);709 }710 737 711 738 if (Utils.deleteFile(toDelete.getFile())) { 712 739 Main.info("File "+toDelete.getFile()+" deleted. "); … … 719 746 ); 720 747 } 721 748 722 updateOffscreenBuffer = true; 723 Main.map.repaint(); 749 showCurrentPhoto(); 724 750 } 725 751 } 726 752 } … … 743 769 } 744 770 745 771 /** 746 * Returns the image that matches the position of the mouse event. 772 * Check if the position of the mouse event is within the rectangle of the photo icon or thumbnail. 773 * @param idx Image index, range 0 .. size-1 747 774 * @param evt Mouse event 748 * @return Image at mouse position, or {@code null} if there is no image at the mouse position749 * @since 6392775 * @return {@code true} if the photo matches the mouse position, 776 * {@code false} otherwise 750 777 */ 751 public ImageEntry getPhotoUnderMouse(MouseEvent evt) { 752 if (data != null) { 753 for (int idx = data.size() - 1; idx >= 0; --idx) { 754 ImageEntry img = data.get(idx); 755 if (img.getPos() == null) { 756 continue; 757 } 758 Point p = Main.map.mapView.getPoint(img.getPos()); 759 Rectangle r; 778 private boolean isPhotoIdxUnderMouse(int idx, MouseEvent evt) { 779 if (idx >= 0 && data != null && idx < data.size()) { 780 ImageEntry img = data.get(idx); 781 if (img.getPos() != null) { 782 Point imgCenter = Main.map.mapView.getPoint(img.getPos()); 783 Rectangle imgRect; 760 784 if (useThumbs && img.hasThumbnail()) { 761 Dimension d = scaledDimension(img.getThumbnail()); 762 if (d != null) 763 r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height); 764 else 765 r = null; 785 Dimension imgDim = scaledDimension(img.getThumbnail()); 786 if (imgDim != null) { 787 imgRect = new Rectangle(imgCenter.x - imgDim.width / 2, 788 imgCenter.y - imgDim.height / 2, 789 imgDim.width, 790 imgDim.height); 791 } else { 792 imgRect = null; 793 } 766 794 } else { 767 r = new Rectangle(p.x - icon.getIconWidth() / 2,768 p.y - icon.getIconHeight() / 2,769 icon.getIconWidth(),770 icon.getIconHeight());795 imgRect = new Rectangle(imgCenter.x - icon.getIconWidth() / 2, 796 imgCenter.y - icon.getIconHeight() / 2, 797 icon.getIconWidth(), 798 icon.getIconHeight()); 771 799 } 772 if ( r != null && r.contains(evt.getPoint())) {773 return img;800 if (imgRect != null && imgRect.contains(evt.getPoint())) { 801 return true; 774 802 } 775 803 } 776 804 } 777 return null;805 return false; 778 806 } 779 807 780 808 /** 809 * Returns index of the image that matches the position of the mouse event. 810 * @param evt Mouse event 811 * @param cycle Set to {@code true} to cycle through the photos at the 812 * current mouse position if multiple icons or thumbnails 813 * overlap. If set to {@code false} the topmost photo will 814 * be used. 815 * @return Image index at mouse position, range 0 .. size-1, 816 * or {@code -1} if there is no image at the mouse position 817 */ 818 private int getPhotoIdxUnderMouse(MouseEvent evt, boolean cycle) { 819 if (data != null) { 820 if (cycle && currentPhoto >= 0) { 821 // Cycle loop is forward as that is the natural order. 822 // Loop 1: One after current photo up to last one. 823 for (int idx = currentPhoto + 1; idx < data.size(); ++idx) { 824 if (isPhotoIdxUnderMouse(idx, evt)) { 825 return idx; 826 } 827 } 828 // Loop 2: First photo up to current one. 829 for (int idx = 0; idx <= currentPhoto; ++idx) { 830 if (isPhotoIdxUnderMouse(idx, evt)) { 831 return idx; 832 } 833 } 834 } else { 835 // Check for current photo first, i.e. keep it selected if it 836 // is under the mouse. 837 if (currentPhoto >= 0 && isPhotoIdxUnderMouse(currentPhoto, evt)) { 838 return currentPhoto; 839 } 840 // Loop from last to first to prefer topmost image. 841 for (int idx = data.size() - 1; idx >= 0; --idx) { 842 if (isPhotoIdxUnderMouse(idx, evt)) { 843 return idx; 844 } 845 } 846 } 847 } 848 return -1; 849 } 850 851 /** 852 * Returns index of the image that matches the position of the mouse event. 853 * The topmost photo is picked if multiple icons or thumbnails overlap. 854 * @param evt Mouse event 855 * @return Image index at mouse position, range 0 .. size-1, 856 * or {@code -1} if there is no image at the mouse position 857 */ 858 private int getPhotoIdxUnderMouse(MouseEvent evt) { 859 return getPhotoIdxUnderMouse(evt, false); 860 } 861 862 /** 863 * Returns the image that matches the position of the mouse event. 864 * The topmost photo is picked of multiple icons or thumbnails overlap. 865 * @param evt Mouse event 866 * @return Image at mouse position, or {@code null} if there is no image at the mouse position 867 * @since 6392 868 */ 869 public ImageEntry getPhotoUnderMouse(MouseEvent evt) { 870 int idx = getPhotoIdxUnderMouse(evt); 871 if (idx >= 0) { 872 return data.get(idx); 873 } else { 874 return null; 875 } 876 } 877 878 /** 781 879 * Clears the currentPhoto, i.e. remove select marker, and optionally repaint. 782 880 * @param repaint Repaint flag 783 881 * @since 6392 … … 848 946 return; 849 947 if (isVisible() && isMapModeOk()) { 850 948 Main.map.mapView.repaint(); 949 cycleModeArmed = true; 851 950 } 852 951 } 853 952 … … 858 957 if (data == null || !isVisible() || !isMapModeOk()) 859 958 return; 860 959 861 for (int i = data.size() - 1; i >= 0; --i) { 862 ImageEntry e = data.get(i); 863 if (e.getPos() == null) { 864 continue; 865 } 866 Point p = Main.map.mapView.getPoint(e.getPos()); 867 Rectangle r; 868 if (useThumbs && e.hasThumbnail()) { 869 Dimension d = scaledDimension(e.getThumbnail()); 870 if (d != null) 871 r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height); 872 else 873 r = null; 874 } else { 875 r = new Rectangle(p.x - icon.getIconWidth() / 2, 876 p.y - icon.getIconHeight() / 2, 877 icon.getIconWidth(), 878 icon.getIconHeight()); 879 } 880 if (r != null && r.contains(ev.getPoint())) { 881 clearOtherCurrentPhotos(); 882 currentPhoto = i; 883 ImageViewerDialog.showImage(GeoImageLayer.this, e); 884 Main.map.repaint(); 885 break; 886 } 960 Point mousePos = ev.getPoint(); 961 boolean cycle = cycleModeArmed && lastSelPos != null && lastSelPos.equals(mousePos); 962 int idx = getPhotoIdxUnderMouse(ev, cycle); 963 if (idx >= 0) { 964 lastSelPos = mousePos; 965 cycleModeArmed = false; 966 currentPhoto = idx; 967 showCurrentPhoto(); 887 968 } 888 969 } 889 970 }; 890 971 972 mouseMotionAdapter = new MouseMotionAdapter() { 973 @Override 974 public void mouseMoved(MouseEvent evt) { 975 lastSelPos = null; 976 } 977 978 @Override 979 public void mouseDragged(MouseEvent evt) { 980 lastSelPos = null; 981 } 982 }; 983 891 984 mapModeListener = (oldMapMode, newMapMode) -> { 892 985 if (newMapMode == null || isSupportedMapMode(newMapMode)) { 893 986 Main.map.mapView.addMouseListener(mouseAdapter); 987 Main.map.mapView.addMouseMotionListener(mouseMotionAdapter); 894 988 } else { 895 989 Main.map.mapView.removeMouseListener(mouseAdapter); 990 Main.map.mapView.removeMouseMotionListener(mouseMotionAdapter); 896 991 } 897 992 }; 898 993 … … 917 1012 if (e.getRemovedLayer() == GeoImageLayer.this) { 918 1013 stopLoadThumbs(); 919 1014 Main.map.mapView.removeMouseListener(mouseAdapter); 1015 Main.map.mapView.removeMouseMotionListener(mouseMotionAdapter); 920 1016 MapFrame.removeMapModeChangeListener(mapModeListener); 921 1017 currentPhoto = -1; 922 1018 if (data != null) {
