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}