Changeset 11434 in josm for trunk


Ignore:
Timestamp:
2017-01-06T21:06:38+01:00 (8 years ago)
Author:
Don-vip
Message:

fix #14181 - GeoImageLayer: Improvements of display of selected photo in map (patch by holgermappt)

File:
1 edited

Legend:

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

    r11425 r11434  
    1717import java.awt.event.MouseAdapter;
    1818import java.awt.event.MouseEvent;
     19import java.awt.event.MouseMotionAdapter;
    1920import java.awt.image.BufferedImage;
    2021import java.beans.PropertyChangeEvent;
     
    9899
    99100    private MouseAdapter mouseAdapter;
     101    private MouseMotionAdapter mouseMotionAdapter;
    100102    private MapModeChangeListener mapModeListener;
     103
     104    /** Mouse position where the last image was selected. */
     105    private Point lastSelPos;
     106
     107    /**
     108     * Image cycle mode flag.
     109     * It is possible that a mouse button release triggers multiple mouseReleased() events.
     110     * To prevent the cycling in such a case we wait for the next mouse button press event
     111     * before it is cycled to the next image.
     112     */
     113    private boolean cycleModeArmed;
    101114
    102115    /**
     
    468481    }
    469482
     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
    470510    @Override
    471511    public void paint(Graphics2D g, MapView mv, Bounds bounds) {
     
    495535                if (data != null) {
    496536                    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);
     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);
    514542                    }
    515543                }
     
    602630
    603631    /**
     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    /**
    604646     * Shows next photo.
    605647     */
     
    610652                currentPhoto = data.size() - 1;
    611653            }
    612             ImageViewerDialog.showImage(this, data.get(currentPhoto));
    613654        } else {
    614655            currentPhoto = -1;
    615656        }
    616         Main.map.repaint();
     657        showCurrentPhoto();
    617658    }
    618659
     
    626667                currentPhoto = 0;
    627668            }
    628             ImageViewerDialog.showImage(this, data.get(currentPhoto));
    629669        } else {
    630670            currentPhoto = -1;
    631671        }
    632         Main.map.repaint();
     672        showCurrentPhoto();
    633673    }
    634674
     
    639679        if (data != null && !data.isEmpty()) {
    640680            currentPhoto = 0;
    641             ImageViewerDialog.showImage(this, data.get(currentPhoto));
    642681        } else {
    643682            currentPhoto = -1;
    644683        }
    645         Main.map.repaint();
     684        showCurrentPhoto();
    646685    }
    647686
     
    652691        if (data != null && !data.isEmpty()) {
    653692            currentPhoto = data.size() - 1;
    654             ImageViewerDialog.showImage(this, data.get(currentPhoto));
    655693        } else {
    656694            currentPhoto = -1;
    657695        }
    658         Main.map.repaint();
     696        showCurrentPhoto();
    659697    }
    660698
     
    670708                currentPhoto = data.size() - 1;
    671709            }
    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();
    679711        }
    680712    }
     
    703735                    currentPhoto = data.size() - 1;
    704736                }
    705                 if (currentPhoto >= 0) {
    706                     ImageViewerDialog.showImage(this, data.get(currentPhoto));
    707                 } else {
    708                     ImageViewerDialog.showImage(this, null);
    709                 }
    710737
    711738                if (Utils.deleteFile(toDelete.getFile())) {
     
    720747                }
    721748
    722                 updateOffscreenBuffer = true;
    723                 Main.map.repaint();
     749                showCurrentPhoto();
    724750            }
    725751        }
     
    744770
    745771    /**
     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
     774     * @param evt Mouse event
     775     * @return {@code true} if the photo matches the mouse position, {@code false} otherwise
     776     */
     777    private boolean isPhotoIdxUnderMouse(int idx, MouseEvent evt) {
     778        if (idx >= 0 && data != null && idx < data.size()) {
     779            ImageEntry img = data.get(idx);
     780            if (img.getPos() != null) {
     781                Point imgCenter = Main.map.mapView.getPoint(img.getPos());
     782                Rectangle imgRect;
     783                if (useThumbs && img.hasThumbnail()) {
     784                    Dimension imgDim = scaledDimension(img.getThumbnail());
     785                    if (imgDim != null) {
     786                        imgRect = new Rectangle(imgCenter.x - imgDim.width / 2,
     787                                                imgCenter.y - imgDim.height / 2,
     788                                                imgDim.width, imgDim.height);
     789                    } else {
     790                        imgRect = null;
     791                    }
     792                } else {
     793                    imgRect = new Rectangle(imgCenter.x - icon.getIconWidth() / 2,
     794                                            imgCenter.y - icon.getIconHeight() / 2,
     795                                            icon.getIconWidth(), icon.getIconHeight());
     796                }
     797                if (imgRect != null && imgRect.contains(evt.getPoint())) {
     798                    return true;
     799                }
     800            }
     801        }
     802        return false;
     803    }
     804
     805    /**
     806     * Returns index of the image that matches the position of the mouse event.
     807     * @param evt    Mouse event
     808     * @param cycle  Set to {@code true} to cycle through the photos at the
     809     *               current mouse position if multiple icons or thumbnails overlap.
     810     *               If set to {@code false} the topmost photo will be used.
     811     * @return       Image index at mouse position, range 0 .. size-1,
     812     *               or {@code -1} if there is no image at the mouse position
     813     */
     814    private int getPhotoIdxUnderMouse(MouseEvent evt, boolean cycle) {
     815        if (data != null) {
     816            if (cycle && currentPhoto >= 0) {
     817                // Cycle loop is forward as that is the natural order.
     818                // Loop 1: One after current photo up to last one.
     819                for (int idx = currentPhoto + 1; idx < data.size(); ++idx) {
     820                    if (isPhotoIdxUnderMouse(idx, evt)) {
     821                        return idx;
     822                    }
     823                }
     824                // Loop 2: First photo up to current one.
     825                for (int idx = 0; idx <= currentPhoto; ++idx) {
     826                    if (isPhotoIdxUnderMouse(idx, evt)) {
     827                        return idx;
     828                    }
     829                }
     830            } else {
     831                // Check for current photo first, i.e. keep it selected if it is under the mouse.
     832                if (currentPhoto >= 0 && isPhotoIdxUnderMouse(currentPhoto, evt)) {
     833                    return currentPhoto;
     834                }
     835                // Loop from last to first to prefer topmost image.
     836                for (int idx = data.size() - 1; idx >= 0; --idx) {
     837                    if (isPhotoIdxUnderMouse(idx, evt)) {
     838                        return idx;
     839                    }
     840                }
     841            }
     842        }
     843        return -1;
     844    }
     845
     846    /**
     847     * Returns index of the image that matches the position of the mouse event.
     848     * The topmost photo is picked if multiple icons or thumbnails overlap.
     849     * @param evt Mouse event
     850     * @return Image index at mouse position, range 0 .. size-1,
     851     *         or {@code -1} if there is no image at the mouse position
     852     */
     853    private int getPhotoIdxUnderMouse(MouseEvent evt) {
     854        return getPhotoIdxUnderMouse(evt, false);
     855    }
     856
     857    /**
    746858     * Returns the image that matches the position of the mouse event.
     859     * The topmost photo is picked of multiple icons or thumbnails overlap.
    747860     * @param evt Mouse event
    748861     * @return Image at mouse position, or {@code null} if there is no image at the mouse position
     
    750863     */
    751864    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;
    760                 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;
    766                 } else {
    767                     r = new Rectangle(p.x - icon.getIconWidth() / 2,
    768                                       p.y - icon.getIconHeight() / 2,
    769                                       icon.getIconWidth(),
    770                                       icon.getIconHeight());
    771                 }
    772                 if (r != null && r.contains(evt.getPoint())) {
    773                     return img;
    774                 }
    775             }
    776         }
    777         return null;
     865        int idx = getPhotoIdxUnderMouse(evt);
     866        if (idx >= 0) {
     867            return data.get(idx);
     868        } else {
     869            return null;
     870        }
    778871    }
    779872
     
    849942                if (isVisible() && isMapModeOk()) {
    850943                    Main.map.mapView.repaint();
     944                    cycleModeArmed = true;
    851945                }
    852946            }
     
    859953                    return;
    860954
    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                     }
    887                 }
     955                Point mousePos = ev.getPoint();
     956                boolean cycle = cycleModeArmed && lastSelPos != null && lastSelPos.equals(mousePos);
     957                int idx = getPhotoIdxUnderMouse(ev, cycle);
     958                if (idx >= 0) {
     959                    lastSelPos = mousePos;
     960                    cycleModeArmed = false;
     961                    currentPhoto = idx;
     962                    showCurrentPhoto();
     963                }
     964            }
     965        };
     966
     967        mouseMotionAdapter = new MouseMotionAdapter() {
     968            @Override
     969            public void mouseMoved(MouseEvent evt) {
     970                lastSelPos = null;
     971            }
     972
     973            @Override
     974            public void mouseDragged(MouseEvent evt) {
     975                lastSelPos = null;
    888976            }
    889977        };
     
    892980            if (newMapMode == null || isSupportedMapMode(newMapMode)) {
    893981                Main.map.mapView.addMouseListener(mouseAdapter);
     982                Main.map.mapView.addMouseMotionListener(mouseMotionAdapter);
    894983            } else {
    895984                Main.map.mapView.removeMouseListener(mouseAdapter);
     985                Main.map.mapView.removeMouseMotionListener(mouseMotionAdapter);
    896986            }
    897987        };
     
    9181008                    stopLoadThumbs();
    9191009                    Main.map.mapView.removeMouseListener(mouseAdapter);
     1010                    Main.map.mapView.removeMouseMotionListener(mouseMotionAdapter);
    9201011                    MapFrame.removeMapModeChangeListener(mapModeListener);
    9211012                    currentPhoto = -1;
Note: See TracChangeset for help on using the changeset viewer.