001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside.gui;
003
004import java.awt.BorderLayout;
005import java.awt.Color;
006import java.awt.GridBagConstraints;
007import java.awt.GridBagLayout;
008import java.awt.event.ActionEvent;
009import java.io.IOException;
010import java.io.InputStream;
011
012import javax.imageio.ImageIO;
013import javax.swing.AbstractAction;
014import javax.swing.BorderFactory;
015import javax.swing.Box;
016import javax.swing.BoxLayout;
017import javax.swing.ImageIcon;
018import javax.swing.JButton;
019import javax.swing.JCheckBox;
020import javax.swing.JComboBox;
021import javax.swing.JLabel;
022import javax.swing.JPanel;
023import javax.swing.JScrollPane;
024import javax.swing.JSpinner;
025import javax.swing.SpinnerNumberModel;
026import javax.swing.SwingUtilities;
027
028import org.apache.log4j.Logger;
029import org.openstreetmap.josm.actions.ExpertToggleAction;
030import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
031import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
032import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
033import org.openstreetmap.josm.plugins.streetside.StreetsidePlugin;
034import org.openstreetmap.josm.plugins.streetside.gui.boilerplate.StreetsideButton;
035import org.openstreetmap.josm.plugins.streetside.io.download.StreetsideDownloader.DOWNLOAD_MODE;
036import org.openstreetmap.josm.plugins.streetside.oauth.OAuthPortListener;
037import org.openstreetmap.josm.plugins.streetside.oauth.StreetsideLoginListener;
038import org.openstreetmap.josm.plugins.streetside.oauth.StreetsideUser;
039import org.openstreetmap.josm.plugins.streetside.utils.StreetsideColorScheme;
040import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties;
041import org.openstreetmap.josm.tools.GBC;
042import org.openstreetmap.josm.tools.I18n;
043
044/**
045 * Creates the preferences panel for the plugin.
046 *
047 * @author nokutu
048 *
049 */
050public class StreetsidePreferenceSetting implements SubPreferenceSetting, StreetsideLoginListener {
051
052  final static Logger logger = Logger.getLogger(StreetsidePreferenceSetting.class);
053
054  private final JComboBox<String> downloadModeComboBox = new JComboBox<>(new String[]{
055      DOWNLOAD_MODE.VISIBLE_AREA.getLabel(),
056      DOWNLOAD_MODE.OSM_AREA.getLabel(),
057      DOWNLOAD_MODE.MANUAL_ONLY.getLabel()
058  });
059
060  private final JCheckBox displayHour =
061    new JCheckBox(I18n.tr("Display hour when the picture was taken"), StreetsideProperties.DISPLAY_HOUR.get());
062  private final JCheckBox format24 =
063    new JCheckBox(I18n.tr("Use 24 hour format"), StreetsideProperties.TIME_FORMAT_24.get());
064  private final JCheckBox moveTo =
065    new JCheckBox(I18n.tr("Move to picture''s location with next/previous buttons"), StreetsideProperties.MOVE_TO_IMG.get());
066  private final JCheckBox hoverEnabled =
067    new JCheckBox(I18n.tr("Preview images when hovering its icon"), StreetsideProperties.HOVER_ENABLED.get());
068  private final JCheckBox cutOffSeq =
069    new JCheckBox(I18n.tr("Cut off sequences at download bounds"), StreetsideProperties.CUT_OFF_SEQUENCES_AT_BOUNDS.get());
070  private final JCheckBox imageLinkToBlurEditor =
071    new JCheckBox(
072      I18n.tr("When opening Streetside image in web browser, show the blur editor instead of the image viewer"),
073      StreetsideProperties.IMAGE_LINK_TO_BLUR_EDITOR.get()
074    );
075  private final JCheckBox developer =
076    new JCheckBox(I18n.tr("Enable experimental beta-features (might be unstable)"), StreetsideProperties.DEVELOPER.get());
077  private final SpinnerNumberModel preFetchSize = new SpinnerNumberModel(
078    StreetsideProperties.PRE_FETCH_IMAGE_COUNT.get().intValue(),
079    0,
080    Integer.MAX_VALUE,
081    1
082  );
083  private final JButton loginButton = new StreetsideButton(new LoginAction(this));
084  private final JButton logoutButton = new StreetsideButton(new LogoutAction());
085  private final JLabel loginLabel = new JLabel();
086  private final JPanel loginPanel = new JPanel();
087
088  @Override
089  public TabPreferenceSetting getTabPreferenceSetting(PreferenceTabbedPane gui) {
090    return gui.getDisplayPreference();
091  }
092
093  @Override
094  public void addGui(PreferenceTabbedPane gui) {
095    JPanel container = new JPanel(new BorderLayout());
096
097    loginPanel.setLayout(new BoxLayout(loginPanel, BoxLayout.LINE_AXIS));
098    loginPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
099    loginPanel.setBackground(StreetsideColorScheme.TOOLBAR_DARK_GREY);
100    JLabel brandImage = new JLabel();
101    try (InputStream is = StreetsidePreferenceSetting.class.getResourceAsStream("/images/streetside-logo-white.png")) {
102      if (is != null) {
103        brandImage.setIcon(new ImageIcon(ImageIO.read(is)));
104      } else {
105        logger.warn("Could not load Streetside brand image!");
106      }
107    } catch (IOException e) {
108      logger.warn("While reading Streetside brand image, an IO-exception occured!");
109    }
110    loginPanel.add(brandImage, 0);
111    loginPanel.add(Box.createHorizontalGlue(), 1);
112    loginLabel.setForeground(Color.WHITE);
113    loginLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
114    loginPanel.add(loginLabel, 2);
115    loginPanel.add(loginButton, 3);
116    onLogout();
117    container.add(loginPanel, BorderLayout.NORTH);
118
119    JPanel mainPanel = new JPanel();
120    mainPanel.setLayout(new GridBagLayout());
121    mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
122
123    downloadModeComboBox.setSelectedItem(DOWNLOAD_MODE.fromPrefId(StreetsideProperties.DOWNLOAD_MODE.get()).getLabel());
124
125    JPanel downloadModePanel = new JPanel();
126    downloadModePanel.add(new JLabel(I18n.tr("Download mode")));
127    downloadModePanel.add(downloadModeComboBox);
128    mainPanel.add(downloadModePanel, GBC.eol());
129
130    mainPanel.add(displayHour, GBC.eol());
131    mainPanel.add(format24, GBC.eol());
132    mainPanel.add(moveTo, GBC.eol());
133    mainPanel.add(hoverEnabled, GBC.eol());
134    mainPanel.add(cutOffSeq, GBC.eol());
135    mainPanel.add(imageLinkToBlurEditor, GBC.eol());
136
137    final JPanel preFetchPanel = new JPanel();
138    preFetchPanel.add(new JLabel(I18n.tr("Number of images to be pre-fetched (forwards and backwards)")));
139    final JSpinner spinner = new JSpinner(preFetchSize);
140    final JSpinner.DefaultEditor editor = new JSpinner.NumberEditor(spinner);
141    editor.getTextField().setColumns(3);
142    spinner.setEditor(editor);
143    preFetchPanel.add(spinner);
144    mainPanel.add(preFetchPanel, GBC.eol());
145
146    if (ExpertToggleAction.isExpert() || developer.isSelected()) {
147      mainPanel.add(developer, GBC.eol());
148    }
149    StreetsideColorScheme.styleAsDefaultPanel(
150      mainPanel, downloadModePanel, displayHour, format24, moveTo, hoverEnabled, cutOffSeq, imageLinkToBlurEditor, developer, preFetchPanel
151    );
152    mainPanel.add(Box.createVerticalGlue(), GBC.eol().fill(GridBagConstraints.BOTH));
153
154    container.add(mainPanel, BorderLayout.CENTER);
155
156    synchronized (gui.getDisplayPreference().getTabPane()) {
157      gui.getDisplayPreference().addSubTab(this, "Streetside", new JScrollPane(container));
158      gui.getDisplayPreference().getTabPane().setIconAt(gui.getDisplayPreference().getTabPane().getTabCount()-1, StreetsidePlugin.LOGO.setSize(12, 12).get());
159    }
160
161    new Thread(() -> {
162      String username = StreetsideUser.getUsername();
163      if (username != null) {
164        SwingUtilities.invokeLater(() -> onLogin(StreetsideUser.getUsername()));
165      }
166    }).start();
167  }
168
169  @Override
170  public void onLogin(final String username) {
171    loginPanel.remove(loginButton);
172    loginPanel.add(logoutButton, 3);
173    loginLabel.setText(I18n.tr("You are logged in as ''{0}''.", username));
174    loginPanel.revalidate();
175    loginPanel.repaint();
176  }
177
178  @Override
179  public void onLogout() {
180    loginPanel.remove(logoutButton);
181    loginPanel.add(loginButton, 3);
182    loginLabel.setText(I18n.tr("You are currently not logged in."));
183    loginPanel.revalidate();
184    loginPanel.repaint();
185  }
186
187  @SuppressWarnings("PMD.ShortMethodName")
188  @Override
189  public boolean ok() {
190    StreetsideProperties.DOWNLOAD_MODE.put(DOWNLOAD_MODE.fromLabel(downloadModeComboBox.getSelectedItem().toString()).getPrefId());
191    StreetsideProperties.DISPLAY_HOUR.put(displayHour.isSelected());
192    StreetsideProperties.TIME_FORMAT_24.put(format24.isSelected());
193    StreetsideProperties.MOVE_TO_IMG.put(moveTo.isSelected());
194    StreetsideProperties.HOVER_ENABLED.put(hoverEnabled.isSelected());
195    StreetsideProperties.CUT_OFF_SEQUENCES_AT_BOUNDS.put(cutOffSeq.isSelected());
196    StreetsideProperties.IMAGE_LINK_TO_BLUR_EDITOR.put(imageLinkToBlurEditor.isSelected());
197    StreetsideProperties.DEVELOPER.put(developer.isSelected());
198    StreetsideProperties.PRE_FETCH_IMAGE_COUNT.put(preFetchSize.getNumber().intValue());
199
200    //Restart is never required
201    return false;
202  }
203
204  @Override
205  public boolean isExpert() {
206    return false;
207  }
208
209  /**
210   * Opens the StreetsideOAuthUI window and lets the user log in.
211   *
212   * @author nokutu
213   *
214   */
215  private static class LoginAction extends AbstractAction {
216
217    private static final long serialVersionUID = 8743119160917296506L;
218
219        private final transient StreetsideLoginListener callback;
220
221    LoginAction(StreetsideLoginListener loginCallback) {
222      // TODO tr( RRH
223      super(I18n.tr("Login"));
224      callback = loginCallback;
225    }
226
227    @Override
228    public void actionPerformed(ActionEvent arg0) {
229      OAuthPortListener portListener = new OAuthPortListener(callback);
230      portListener.start();
231      // user authentication not supported for Streetside (Mapillary relic)
232    }
233  }
234
235  /**
236   * Logs the user out.
237   *
238   * @author nokutu
239   *
240   */
241  private class LogoutAction extends AbstractAction {
242
243    private static final long serialVersionUID = -4146587895393766981L;
244
245        private LogoutAction() {
246      super(I18n.tr("Logout"));
247    }
248
249    @Override
250    public void actionPerformed(ActionEvent arg0) {
251      StreetsideUser.reset();
252      onLogout();
253    }
254  }
255}