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