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