001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.plugins.streetside.gui.imageinfo; 003 004import java.awt.Font; 005import java.awt.GridBagConstraints; 006import java.awt.GridBagLayout; 007import java.awt.Image; 008import java.awt.Insets; 009import java.awt.datatransfer.StringSelection; 010import java.awt.image.BufferedImage; 011import java.util.Collection; 012 013import javax.swing.ImageIcon; 014import javax.swing.JCheckBox; 015import javax.swing.JLabel; 016import javax.swing.JPanel; 017import javax.swing.JTextPane; 018 019import org.apache.log4j.Logger; 020import org.openstreetmap.josm.data.osm.DataSelectionListener; 021import org.openstreetmap.josm.data.osm.OsmPrimitive; 022import org.openstreetmap.josm.data.osm.Tag; 023import org.openstreetmap.josm.data.osm.event.SelectionEventManager; 024import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener; 025import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 026import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage; 027import org.openstreetmap.josm.plugins.streetside.StreetsideDataListener; 028import org.openstreetmap.josm.plugins.streetside.StreetsideImage; 029import org.openstreetmap.josm.plugins.streetside.gui.boilerplate.SelectableLabel; 030import org.openstreetmap.josm.plugins.streetside.gui.boilerplate.StreetsideButton; 031import org.openstreetmap.josm.plugins.streetside.model.UserProfile; 032import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties; 033import org.openstreetmap.josm.plugins.streetside.utils.StreetsideURL; 034import org.openstreetmap.josm.tools.I18n; 035 036public final class ImageInfoPanel extends ToggleDialog implements StreetsideDataListener, DataSelectionListener { 037 private static final long serialVersionUID = 4141847503072417190L; 038 039 final static Logger logger = Logger.getLogger(ImageInfoPanel.class); 040 041 private static ImageInfoPanel instance; 042 private static final ImageIcon EMPTY_USER_AVATAR = new ImageIcon(new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB)); 043 044 private final JLabel numDetectionsLabel; 045 private final JCheckBox showDetectionsCheck; 046 private final JLabel usernameLabel; 047 private final JTextPane imgKeyValue; 048 private final WebLinkAction imgLinkAction; 049 private final ClipboardAction copyImgKeyAction; 050 private final AddTagToPrimitiveAction addStreetsideTagAction; 051 private final JTextPane seqKeyValue; 052 053 private ValueChangeListener<Boolean> imageLinkChangeListener; 054 055 private ImageInfoPanel() { 056 super( 057 I18n.tr("Streetside Image info"), 058 "streetside-info", 059 I18n.tr("Displays detail information on the currently selected Streetside image"), 060 null, 061 150 062 ); 063 SelectionEventManager.getInstance().addSelectionListener(this); 064 065 numDetectionsLabel = new JLabel(); 066 numDetectionsLabel.setFont(numDetectionsLabel.getFont().deriveFont(Font.PLAIN)); 067 068 showDetectionsCheck = new JCheckBox(I18n.tr("Show detections on top of image")); 069 showDetectionsCheck.setSelected(StreetsideProperties.SHOW_DETECTED_SIGNS.get()); 070 showDetectionsCheck.addActionListener( 071 action -> StreetsideProperties.SHOW_DETECTED_SIGNS.put(showDetectionsCheck.isSelected()) 072 ); 073 StreetsideProperties.SHOW_DETECTED_SIGNS.addListener( 074 valueChange -> showDetectionsCheck.setSelected(StreetsideProperties.SHOW_DETECTED_SIGNS.get()) 075 ); 076 077 usernameLabel = new JLabel(); 078 usernameLabel.setFont(usernameLabel.getFont().deriveFont(Font.PLAIN)); 079 080 imgKeyValue = new SelectableLabel(); 081 082 imgLinkAction = new WebLinkAction(I18n.tr("View in browser"), null); 083 084 copyImgKeyAction = new ClipboardAction(I18n.tr("Copy key"), null); 085 StreetsideButton copyButton = new StreetsideButton(copyImgKeyAction, true); 086 copyImgKeyAction.setPopupParent(copyButton); 087 088 addStreetsideTagAction = new AddTagToPrimitiveAction(I18n.tr("Add Streetside tag")); 089 090 JPanel imgKey = new JPanel(); 091 imgKey.add(imgKeyValue); 092 imgKey.add(copyButton); 093 JPanel imgButtons = new JPanel(); 094 imgButtons.add(new StreetsideButton(imgLinkAction, true)); 095 imgButtons.add(new StreetsideButton(addStreetsideTagAction, true)); 096 seqKeyValue = new SelectableLabel(); 097 098 JPanel root = new JPanel(new GridBagLayout()); 099 GridBagConstraints gbc = new GridBagConstraints(); 100 gbc.insets = new Insets(0, 5, 0, 5); 101 102 // Left column 103 gbc.gridx = 0; 104 gbc.gridy = 0; 105 gbc.anchor = GridBagConstraints.LINE_END; 106 gbc.gridwidth = 1; 107 gbc.gridheight = 2; 108 root.add(new JLabel(I18n.tr("Image detections")), gbc); 109 gbc.gridy += 2; 110 gbc.gridheight = 1; 111 root.add(new JLabel(I18n.tr("User")), gbc); 112 gbc.gridy++; 113 root.add(new JLabel(I18n.tr("Image actions")), gbc); 114 gbc.gridy++; 115 root.add(new JLabel(I18n.tr("Image key")), gbc); 116 gbc.gridy++; 117 root.add(new JLabel(I18n.tr("Sequence key")), gbc); 118 119 // Right column 120 gbc.weightx = 1; 121 gbc.gridx++; 122 gbc.gridy = 0; 123 gbc.anchor = GridBagConstraints.LINE_START; 124 root.add(numDetectionsLabel, gbc); 125 gbc.gridy++; 126 root.add(showDetectionsCheck, gbc); 127 gbc.gridy++; 128 root.add(usernameLabel, gbc); 129 gbc.gridy++; 130 root.add(imgButtons, gbc); 131 gbc.gridy++; 132 root.add(imgKey, gbc); 133 gbc.gridy++; 134 root.add(seqKeyValue, gbc); 135 136 createLayout(root, true, null); 137 selectedImageChanged(null, null); 138 } 139 140 public static ImageInfoPanel getInstance() { 141 synchronized (ImageInfoPanel.class) { 142 if (instance == null) { 143 instance = new ImageInfoPanel(); 144 } 145 return instance; 146 } 147 } 148 149 /** 150 * Destroys the unique instance of the class. 151 */ 152 public static synchronized void destroyInstance() { 153 instance = null; 154 } 155 156 /* (non-Javadoc) 157 * @see org.openstreetmap.josm.gui.dialogs.ToggleDialog#stateChanged() 158 */ 159 @Override 160 protected void stateChanged() { 161 super.stateChanged(); 162 if (isDialogShowing()) { // If the user opens the dialog once, no longer show the help message 163 StreetsideProperties.IMAGEINFO_HELP_COUNTDOWN.put(0); 164 } 165 } 166 167 /* (non-Javadoc) 168 * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#imagesAdded() 169 */ 170 @Override 171 public void imagesAdded() { 172 // Method is not needed, but enforcesd by the interface StreetsideDataListener 173 } 174 175 /* (non-Javadoc) 176 * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#selectedImageChanged(org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage, org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage) 177 */ 178 @Override 179 public synchronized void selectedImageChanged(final StreetsideAbstractImage oldImage, final StreetsideAbstractImage newImage) { 180 logger.info(String.format( 181 "Selected Streetside image changed from %s to %s.", 182 oldImage instanceof StreetsideImage ? ((StreetsideImage) oldImage).getId() : "‹none›", 183 newImage instanceof StreetsideImage ? ((StreetsideImage) newImage).getId() : "‹none›" 184 )); 185 186 numDetectionsLabel.setText(I18n.tr("{0} detections", newImage instanceof StreetsideImage ? /*((StreetsideImage) newImage).getDetections().size()*/0 : 0)); 187 imgKeyValue.setEnabled(newImage instanceof StreetsideImage); 188 final String newImageKey = newImage instanceof StreetsideImage ? ((StreetsideImage) newImage).getId(): null; 189 if (newImageKey != null) { 190 imageLinkChangeListener = b -> imgLinkAction.setURL( 191 /*StreetsideProperties.IMAGE_LINK_TO_BLUR_EDITOR.get() 192 ? StreetsideURL.MainWebsite.blurEditImage(newImageKey) 193 :*/ StreetsideURL.MainWebsite.browseImage(newImageKey) 194 ); 195 imageLinkChangeListener.valueChanged(null); 196 StreetsideProperties.IMAGE_LINK_TO_BLUR_EDITOR.addListener(imageLinkChangeListener); 197 198 imgKeyValue.setText(newImageKey); 199 copyImgKeyAction.setContents(new StringSelection(newImageKey)); 200 addStreetsideTagAction.setTag(new Tag("streetside", newImageKey)); 201 } else { 202 if (imageLinkChangeListener != null) { 203 StreetsideProperties.IMAGE_LINK_TO_BLUR_EDITOR.removeListener(imageLinkChangeListener); 204 imageLinkChangeListener = null; 205 } 206 imgLinkAction.setURL(null); 207 208 imgKeyValue.setText('‹' + I18n.tr("image has no key") + '›'); 209 copyImgKeyAction.setContents(null); 210 addStreetsideTagAction.setTag(null); 211 } 212 213 final UserProfile user = newImage instanceof StreetsideImage ? ((StreetsideImage) newImage).getUser() : null; 214 usernameLabel.setEnabled(user != null); 215 if (user != null) { 216 usernameLabel.setText(user.getUsername()); 217 usernameLabel.setIcon(new ImageIcon(user.getAvatar().getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH))); 218 } else { 219 usernameLabel.setText("‹" + I18n.tr("unknown user") + "›"); 220 usernameLabel.setIcon(EMPTY_USER_AVATAR); 221 } 222 223 final boolean partOfSequence = newImage != null && newImage.getSequence() != null && newImage.getSequence().getId() != null; 224 seqKeyValue.setEnabled(partOfSequence); 225 if (partOfSequence) { 226 seqKeyValue.setText(newImage.getSequence().getId()); 227 } else { 228 seqKeyValue.setText('‹' + I18n.tr("sequence has no id") + '›'); 229 } 230 } 231 232 /* (non-Javadoc) 233 * @see org.openstreetmap.josm.data.SelectionChangedListener#selectionChanged(java.util.Collection) 234 */ 235 @Override 236 public synchronized void selectionChanged(final SelectionChangeEvent event) { 237 final Collection<? extends OsmPrimitive> sel = event.getSelection(); 238 if (StreetsideProperties.DEBUGING_ENABLED.get()) { 239 logger.debug(String.format("Selection changed. %d primitives are selected.", sel == null ? 0 : sel.size())); 240 } 241 addStreetsideTagAction.setTarget(sel != null && sel.size() == 1 ? sel.iterator().next() : null); 242 } 243}