    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}