source: josm/trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java@ 11452

Last change on this file since 11452 was 11452, checked in by Don-vip, 7 years ago

sonar - fb-contrib:SEO_SUBOPTIMAL_EXPRESSION_ORDER - Performance - Method orders expressions in a conditional in a sub optimal way

  • Property svn:eol-style set to native
File size: 18.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer.geoimage;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.GridBagConstraints;
10import java.awt.GridBagLayout;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.awt.event.WindowEvent;
14import java.text.DateFormat;
15
16import javax.swing.AbstractAction;
17import javax.swing.Box;
18import javax.swing.ImageIcon;
19import javax.swing.JButton;
20import javax.swing.JComponent;
21import javax.swing.JPanel;
22import javax.swing.JToggleButton;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
26import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
27import org.openstreetmap.josm.gui.layer.Layer;
28import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
29import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
30import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
31import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
32import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
33import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
34import org.openstreetmap.josm.tools.ImageProvider;
35import org.openstreetmap.josm.tools.Shortcut;
36import org.openstreetmap.josm.tools.date.DateUtils;
37
38public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener {
39
40 private static final String COMMAND_ZOOM = "zoom";
41 private static final String COMMAND_CENTERVIEW = "centre";
42 private static final String COMMAND_NEXT = "next";
43 private static final String COMMAND_REMOVE = "remove";
44 private static final String COMMAND_REMOVE_FROM_DISK = "removefromdisk";
45 private static final String COMMAND_PREVIOUS = "previous";
46 private static final String COMMAND_COLLAPSE = "collapse";
47 private static final String COMMAND_FIRST = "first";
48 private static final String COMMAND_LAST = "last";
49 private static final String COMMAND_COPY_PATH = "copypath";
50
51 private final ImageDisplay imgDisplay = new ImageDisplay();
52 private boolean centerView;
53
54 // Only one instance of that class is present at one time
55 private static volatile ImageViewerDialog dialog;
56
57 private boolean collapseButtonClicked;
58
59 static void newInstance() {
60 dialog = new ImageViewerDialog();
61 }
62
63 /**
64 * Replies the unique instance of this dialog
65 * @return the unique instance
66 */
67 public static ImageViewerDialog getInstance() {
68 if (dialog == null)
69 throw new AssertionError("a new instance needs to be created first");
70 return dialog;
71 }
72
73 private JButton btnNext;
74 private JButton btnPrevious;
75 private JButton btnCollapse;
76 private JToggleButton tbCentre;
77
78 private ImageViewerDialog() {
79 super(tr("Geotagged Images"), "geoimage", tr("Display geotagged images"), Shortcut.registerShortcut("tools:geotagged",
80 tr("Tool: {0}", tr("Display geotagged images")), KeyEvent.VK_Y, Shortcut.DIRECT), 200);
81 build();
82 Main.getLayerManager().addActiveLayerChangeListener(this);
83 Main.getLayerManager().addLayerChangeListener(this);
84 }
85
86 private void build() {
87 JPanel content = new JPanel(new BorderLayout());
88
89 content.add(imgDisplay, BorderLayout.CENTER);
90
91 Dimension buttonDim = new Dimension(26, 26);
92
93 ImageAction prevAction = new ImageAction(COMMAND_PREVIOUS, ImageProvider.get("dialogs", "previous"), tr("Previous"));
94 btnPrevious = new JButton(prevAction);
95 btnPrevious.setPreferredSize(buttonDim);
96 Shortcut scPrev = Shortcut.registerShortcut(
97 "geoimage:previous", tr("Geoimage: {0}", tr("Show previous Image")), KeyEvent.VK_PAGE_UP, Shortcut.DIRECT);
98 final String previousImage = "Previous Image";
99 Main.registerActionShortcut(prevAction, scPrev);
100 btnPrevious.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(scPrev.getKeyStroke(), previousImage);
101 btnPrevious.getActionMap().put(previousImage, prevAction);
102 btnPrevious.setEnabled(false);
103
104 final String removePhoto = tr("Remove photo from layer");
105 ImageAction delAction = new ImageAction(COMMAND_REMOVE, ImageProvider.get("dialogs", "delete"), removePhoto);
106 JButton btnDelete = new JButton(delAction);
107 btnDelete.setPreferredSize(buttonDim);
108 Shortcut scDelete = Shortcut.registerShortcut(
109 "geoimage:deleteimagefromlayer", tr("Geoimage: {0}", tr("Remove photo from layer")), KeyEvent.VK_DELETE, Shortcut.SHIFT);
110 Main.registerActionShortcut(delAction, scDelete);
111 btnDelete.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(scDelete.getKeyStroke(), removePhoto);
112 btnDelete.getActionMap().put(removePhoto, delAction);
113
114 ImageAction delFromDiskAction = new ImageAction(COMMAND_REMOVE_FROM_DISK,
115 ImageProvider.get("dialogs", "geoimage/deletefromdisk"), tr("Delete image file from disk"));
116 JButton btnDeleteFromDisk = new JButton(delFromDiskAction);
117 btnDeleteFromDisk.setPreferredSize(buttonDim);
118 Shortcut scDeleteFromDisk = Shortcut.registerShortcut(
119 "geoimage:deletefilefromdisk", tr("Geoimage: {0}", tr("Delete File from disk")), KeyEvent.VK_DELETE, Shortcut.CTRL_SHIFT);
120 final String deleteImage = "Delete image file from disk";
121 Main.registerActionShortcut(delFromDiskAction, scDeleteFromDisk);
122 btnDeleteFromDisk.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(scDeleteFromDisk.getKeyStroke(), deleteImage);
123 btnDeleteFromDisk.getActionMap().put(deleteImage, delFromDiskAction);
124
125 ImageAction copyPathAction = new ImageAction(COMMAND_COPY_PATH, ImageProvider.get("copy"), tr("Copy image path"));
126 JButton btnCopyPath = new JButton(copyPathAction);
127 btnCopyPath.setPreferredSize(buttonDim);
128 Shortcut scCopyPath = Shortcut.registerShortcut(
129 "geoimage:copypath", tr("Geoimage: {0}", tr("Copy image path")), KeyEvent.VK_C, Shortcut.ALT_CTRL_SHIFT);
130 final String copyImage = "Copy image path";
131 Main.registerActionShortcut(copyPathAction, scCopyPath);
132 btnCopyPath.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(scCopyPath.getKeyStroke(), copyImage);
133 btnCopyPath.getActionMap().put(copyImage, copyPathAction);
134
135 ImageAction nextAction = new ImageAction(COMMAND_NEXT, ImageProvider.get("dialogs", "next"), tr("Next"));
136 btnNext = new JButton(nextAction);
137 btnNext.setPreferredSize(buttonDim);
138 Shortcut scNext = Shortcut.registerShortcut(
139 "geoimage:next", tr("Geoimage: {0}", tr("Show next Image")), KeyEvent.VK_PAGE_DOWN, Shortcut.DIRECT);
140 final String nextImage = "Next Image";
141 Main.registerActionShortcut(nextAction, scNext);
142 btnNext.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(scNext.getKeyStroke(), nextImage);
143 btnNext.getActionMap().put(nextImage, nextAction);
144 btnNext.setEnabled(false);
145
146 Main.registerActionShortcut(
147 new ImageAction(COMMAND_FIRST, null, null),
148 Shortcut.registerShortcut(
149 "geoimage:first", tr("Geoimage: {0}", tr("Show first Image")), KeyEvent.VK_HOME, Shortcut.DIRECT)
150 );
151 Main.registerActionShortcut(
152 new ImageAction(COMMAND_LAST, null, null),
153 Shortcut.registerShortcut(
154 "geoimage:last", tr("Geoimage: {0}", tr("Show last Image")), KeyEvent.VK_END, Shortcut.DIRECT)
155 );
156
157 tbCentre = new JToggleButton(new ImageAction(COMMAND_CENTERVIEW,
158 ImageProvider.get("dialogs", "centreview"), tr("Center view")));
159 tbCentre.setPreferredSize(buttonDim);
160
161 JButton btnZoomBestFit = new JButton(new ImageAction(COMMAND_ZOOM,
162 ImageProvider.get("dialogs", "zoom-best-fit"), tr("Zoom best fit and 1:1")));
163 btnZoomBestFit.setPreferredSize(buttonDim);
164
165 btnCollapse = new JButton(new ImageAction(COMMAND_COLLAPSE,
166 ImageProvider.get("dialogs", "collapse"), tr("Move dialog to the side pane")));
167 btnCollapse.setPreferredSize(new Dimension(20, 20));
168 btnCollapse.setAlignmentY(Component.TOP_ALIGNMENT);
169
170 JPanel buttons = new JPanel();
171 buttons.add(btnPrevious);
172 buttons.add(btnNext);
173 buttons.add(Box.createRigidArea(new Dimension(7, 0)));
174 buttons.add(tbCentre);
175 buttons.add(btnZoomBestFit);
176 buttons.add(Box.createRigidArea(new Dimension(7, 0)));
177 buttons.add(btnDelete);
178 buttons.add(btnDeleteFromDisk);
179 buttons.add(Box.createRigidArea(new Dimension(7, 0)));
180 buttons.add(btnCopyPath);
181
182 JPanel bottomPane = new JPanel(new GridBagLayout());
183 GridBagConstraints gc = new GridBagConstraints();
184 gc.gridx = 0;
185 gc.gridy = 0;
186 gc.anchor = GridBagConstraints.CENTER;
187 gc.weightx = 1;
188 bottomPane.add(buttons, gc);
189
190 gc.gridx = 1;
191 gc.gridy = 0;
192 gc.anchor = GridBagConstraints.PAGE_END;
193 gc.weightx = 0;
194 bottomPane.add(btnCollapse, gc);
195
196 content.add(bottomPane, BorderLayout.SOUTH);
197
198 createLayout(content, false, null);
199 }
200
201 @Override
202 public void destroy() {
203 Main.getLayerManager().removeActiveLayerChangeListener(this);
204 Main.getLayerManager().removeLayerChangeListener(this);
205 super.destroy();
206 }
207
208 class ImageAction extends AbstractAction {
209 private final String action;
210
211 ImageAction(String action, ImageIcon icon, String toolTipText) {
212 this.action = action;
213 putValue(SHORT_DESCRIPTION, toolTipText);
214 putValue(SMALL_ICON, icon);
215 }
216
217 @Override
218 public void actionPerformed(ActionEvent e) {
219 if (COMMAND_NEXT.equals(action)) {
220 if (currentLayer != null) {
221 currentLayer.showNextPhoto();
222 }
223 } else if (COMMAND_PREVIOUS.equals(action)) {
224 if (currentLayer != null) {
225 currentLayer.showPreviousPhoto();
226 }
227 } else if (COMMAND_FIRST.equals(action) && currentLayer != null) {
228 currentLayer.showFirstPhoto();
229 } else if (COMMAND_LAST.equals(action) && currentLayer != null) {
230 currentLayer.showLastPhoto();
231 } else if (COMMAND_CENTERVIEW.equals(action)) {
232 final JToggleButton button = (JToggleButton) e.getSource();
233 centerView = button.isEnabled() && button.isSelected();
234 if (centerView && currentEntry != null && currentEntry.getPos() != null) {
235 Main.map.mapView.zoomTo(currentEntry.getPos());
236 }
237 } else if (COMMAND_ZOOM.equals(action)) {
238 imgDisplay.zoomBestFitOrOne();
239 } else if (COMMAND_REMOVE.equals(action)) {
240 if (currentLayer != null) {
241 currentLayer.removeCurrentPhoto();
242 }
243 } else if (COMMAND_REMOVE_FROM_DISK.equals(action)) {
244 if (currentLayer != null) {
245 currentLayer.removeCurrentPhotoFromDisk();
246 }
247 } else if (COMMAND_COPY_PATH.equals(action)) {
248 if (currentLayer != null) {
249 currentLayer.copyCurrentPhotoPath();
250 }
251 } else if (COMMAND_COLLAPSE.equals(action)) {
252 collapseButtonClicked = true;
253 detachedDialog.getToolkit().getSystemEventQueue().postEvent(new WindowEvent(detachedDialog, WindowEvent.WINDOW_CLOSING));
254 }
255 }
256 }
257
258 public static void showImage(GeoImageLayer layer, ImageEntry entry) {
259 getInstance().displayImage(layer, entry);
260 if (layer != null) {
261 layer.checkPreviousNextButtons();
262 } else {
263 setPreviousEnabled(false);
264 setNextEnabled(false);
265 }
266 }
267
268 /**
269 * Enables (or disables) the "Previous" button.
270 * @param value {@code true} to enable the button, {@code false} otherwise
271 */
272 public static void setPreviousEnabled(boolean value) {
273 getInstance().btnPrevious.setEnabled(value);
274 }
275
276 /**
277 * Enables (or disables) the "Next" button.
278 * @param value {@code true} to enable the button, {@code false} otherwise
279 */
280 public static void setNextEnabled(boolean value) {
281 getInstance().btnNext.setEnabled(value);
282 }
283
284 /**
285 * Enables (or disables) the "Center view" button.
286 * @param value {@code true} to enable the button, {@code false} otherwise
287 * @return the old enabled value. Can be used to restore the original enable state
288 */
289 public static synchronized boolean setCentreEnabled(boolean value) {
290 final ImageViewerDialog instance = getInstance();
291 final boolean wasEnabled = instance.tbCentre.isEnabled();
292 instance.tbCentre.setEnabled(value);
293 instance.tbCentre.getAction().actionPerformed(new ActionEvent(instance.tbCentre, 0, null));
294 return wasEnabled;
295 }
296
297 private transient GeoImageLayer currentLayer;
298 private transient ImageEntry currentEntry;
299
300 public void displayImage(GeoImageLayer layer, ImageEntry entry) {
301 boolean imageChanged;
302
303 synchronized (this) {
304 // TODO: pop up image dialog but don't load image again
305
306 imageChanged = currentEntry != entry;
307
308 if (centerView && entry != null && Main.isDisplayingMapView() && entry.getPos() != null) {
309 Main.map.mapView.zoomTo(entry.getPos());
310 }
311
312 currentLayer = layer;
313 currentEntry = entry;
314 }
315
316 if (entry != null) {
317 if (imageChanged) {
318 // Set only if the image is new to preserve zoom and position if the same image is redisplayed
319 // (e.g. to update the OSD).
320 imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
321 }
322 setTitle(tr("Geotagged Images") + (entry.getFile() != null ? " - " + entry.getFile().getName() : ""));
323 StringBuilder osd = new StringBuilder(entry.getFile() != null ? entry.getFile().getName() : "");
324 if (entry.getElevation() != null) {
325 osd.append(tr("\nAltitude: {0} m", Math.round(entry.getElevation())));
326 }
327 if (entry.getSpeed() != null) {
328 osd.append(tr("\nSpeed: {0} km/h", Math.round(entry.getSpeed())));
329 }
330 if (entry.getExifImgDir() != null) {
331 osd.append(tr("\nDirection {0}\u00b0", Math.round(entry.getExifImgDir())));
332 }
333 DateFormat dtf = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM);
334 if (entry.hasExifTime()) {
335 osd.append(tr("\nEXIF time: {0}", dtf.format(entry.getExifTime())));
336 }
337 if (entry.hasGpsTime()) {
338 osd.append(tr("\nGPS time: {0}", dtf.format(entry.getGpsTime())));
339 }
340
341 imgDisplay.setOsdText(osd.toString());
342 } else {
343 // if this method is called to reinitialize dialog content with a blank image,
344 // do not actually show the dialog again with a blank image if currently hidden (fix #10672)
345 setTitle(tr("Geotagged Images"));
346 imgDisplay.setImage(null, null);
347 imgDisplay.setOsdText("");
348 return;
349 }
350 if (!isDialogShowing()) {
351 setIsDocked(false); // always open a detached window when an image is clicked and dialog is closed
352 showDialog();
353 } else {
354 if (isDocked && isCollapsed) {
355 expand();
356 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, this);
357 }
358 }
359 }
360
361 /**
362 * When an image is closed, really close it and do not pop
363 * up the side dialog.
364 */
365 @Override
366 protected boolean dockWhenClosingDetachedDlg() {
367 if (collapseButtonClicked) {
368 collapseButtonClicked = false;
369 return true;
370 }
371 return false;
372 }
373
374 @Override
375 protected void stateChanged() {
376 super.stateChanged();
377 if (btnCollapse != null) {
378 btnCollapse.setVisible(!isDocked);
379 }
380 }
381
382 /**
383 * Returns whether an image is currently displayed
384 * @return If image is currently displayed
385 */
386 public boolean hasImage() {
387 return currentEntry != null;
388 }
389
390 /**
391 * Returns the currently displayed image.
392 * @return Currently displayed image or {@code null}
393 * @since 6392
394 */
395 public static ImageEntry getCurrentImage() {
396 return getInstance().currentEntry;
397 }
398
399 /**
400 * Returns the layer associated with the image.
401 * @return Layer associated with the image
402 * @since 6392
403 */
404 public static GeoImageLayer getCurrentLayer() {
405 return getInstance().currentLayer;
406 }
407
408 /**
409 * Returns whether the center view is currently active.
410 * @return {@code true} if the center view is active, {@code false} otherwise
411 * @since 9416
412 */
413 public static boolean isCenterView() {
414 return getInstance().centerView;
415 }
416
417 @Override
418 public void layerAdded(LayerAddEvent e) {
419 showLayer(e.getAddedLayer());
420 }
421
422 @Override
423 public void layerRemoving(LayerRemoveEvent e) {
424 // Clear current image and layer if current layer is deleted
425 if (currentLayer != null && currentLayer.equals(e.getRemovedLayer())) {
426 showImage(null, null);
427 }
428 // Check buttons state in case of layer merging
429 if (currentLayer != null && e.getRemovedLayer() instanceof GeoImageLayer) {
430 currentLayer.checkPreviousNextButtons();
431 }
432 }
433
434 @Override
435 public void layerOrderChanged(LayerOrderChangeEvent e) {
436 // ignored
437 }
438
439 @Override
440 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
441 showLayer(e.getSource().getActiveLayer());
442 }
443
444 private void showLayer(Layer newLayer) {
445 if (currentLayer == null && newLayer instanceof GeoImageLayer) {
446 ((GeoImageLayer) newLayer).showFirstPhoto();
447 }
448 }
449
450}
Note: See TracBrowser for help on using the repository browser.