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