source: josm/trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java@ 12903

Last change on this file since 12903 was 12903, checked in by Don-vip, 7 years ago

fix #15267 - fix various usability problems with new download dialog

  • Property svn:eol-style set to native
File size: 25.5 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[237]2package org.openstreetmap.josm.gui.download;
3
[2529]4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
[237]5import static org.openstreetmap.josm.tools.I18n.tr;
6
[2331]7import java.awt.BorderLayout;
8import java.awt.Component;
9import java.awt.Dimension;
10import java.awt.FlowLayout;
[1811]11import java.awt.GridBagLayout;
[1329]12import java.awt.event.ActionEvent;
13import java.awt.event.InputEvent;
14import java.awt.event.KeyEvent;
[2331]15import java.awt.event.WindowAdapter;
16import java.awt.event.WindowEvent;
[237]17import java.util.ArrayList;
18import java.util.List;
[12574]19import java.util.Optional;
[12706]20import java.util.stream.Collectors;
[12652]21import java.util.stream.IntStream;
[237]22
[1307]23import javax.swing.AbstractAction;
[12652]24import javax.swing.Icon;
[10424]25import javax.swing.JButton;
[237]26import javax.swing.JCheckBox;
[2331]27import javax.swing.JComponent;
28import javax.swing.JDialog;
[237]29import javax.swing.JLabel;
30import javax.swing.JPanel;
[12652]31import javax.swing.JSplitPane;
[237]32import javax.swing.JTabbedPane;
[1307]33import javax.swing.KeyStroke;
[12684]34import javax.swing.event.ChangeEvent;
35import javax.swing.event.ChangeListener;
[237]36
37import org.openstreetmap.josm.Main;
[4906]38import org.openstreetmap.josm.actions.ExpertToggleAction;
[1307]39import org.openstreetmap.josm.data.Bounds;
[11270]40import org.openstreetmap.josm.data.preferences.BooleanProperty;
41import org.openstreetmap.josm.data.preferences.IntegerProperty;
[12706]42import org.openstreetmap.josm.data.preferences.StringProperty;
[12630]43import org.openstreetmap.josm.gui.MainApplication;
[237]44import org.openstreetmap.josm.gui.MapView;
[10604]45import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
[2331]46import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
47import org.openstreetmap.josm.gui.help.HelpUtil;
[10035]48import org.openstreetmap.josm.gui.util.GuiHelper;
[12678]49import org.openstreetmap.josm.gui.util.WindowGeometry;
[7434]50import org.openstreetmap.josm.io.OnlineResource;
[1326]51import org.openstreetmap.josm.plugins.PluginHandler;
[12846]52import org.openstreetmap.josm.spi.preferences.Config;
[237]53import org.openstreetmap.josm.tools.GBC;
[2331]54import org.openstreetmap.josm.tools.ImageProvider;
[5200]55import org.openstreetmap.josm.tools.InputMapUtils;
[12684]56import org.openstreetmap.josm.tools.JosmRuntimeException;
[12878]57import org.openstreetmap.josm.tools.ListenerList;
[12620]58import org.openstreetmap.josm.tools.Logging;
[1307]59import org.openstreetmap.josm.tools.OsmUrlToBounds;
[237]60
61/**
[12652]62 * Dialog displayed to the user to download mapping data.
[237]63 */
[10378]64public class DownloadDialog extends JDialog {
[12652]65
[11277]66 private static final IntegerProperty DOWNLOAD_TAB = new IntegerProperty("download.tab", 0);
[12706]67 private static final StringProperty DOWNLOAD_SOURCE_TAB = new StringProperty("download.source.tab", OSMDownloadSource.SIMPLE_NAME);
[11277]68 private static final BooleanProperty DOWNLOAD_AUTORUN = new BooleanProperty("download.autorun", false);
69 private static final BooleanProperty DOWNLOAD_NEWLAYER = new BooleanProperty("download.newlayer", false);
[11658]70 private static final BooleanProperty DOWNLOAD_ZOOMTODATA = new BooleanProperty("download.zoomtodata", true);
[11270]71
[2331]72 /** the unique instance of the download dialog */
[6883]73 private static DownloadDialog instance;
[2512]74
[2331]75 /**
76 * Replies the unique instance of the download dialog
[2512]77 *
[2331]78 * @return the unique instance of the download dialog
79 */
[8126]80 public static synchronized DownloadDialog getInstance() {
[2529]81 if (instance == null) {
[2331]82 instance = new DownloadDialog(Main.parent);
[2529]83 }
[2331]84 return instance;
[2512]85 }
[806]86
[12878]87 protected static final ListenerList<DownloadSourceListener> downloadSourcesListeners = ListenerList.create();
88 protected static final List<DownloadSource<?>> downloadSources = new ArrayList<>();
89 static {
90 // add default download sources
91 addDownloadSource(new OSMDownloadSource());
92 addDownloadSource(new OverpassDownloadSource());
93 }
94
[8308]95 protected final transient List<DownloadSelection> downloadSelections = new ArrayList<>();
[5097]96 protected final JTabbedPane tpDownloadAreaSelectors = new JTabbedPane();
[12706]97 protected final DownloadSourceTabs downloadSourcesTab = new DownloadSourceTabs();
[12652]98
[5097]99 protected JCheckBox cbNewLayer;
100 protected JCheckBox cbStartup;
[11658]101 protected JCheckBox cbZoomToDownloadedData;
[12652]102 protected SlippyMapChooser slippyMapChooser;
103 protected JPanel mainPanel;
[12705]104 protected DownloadDialogSplitPane dialogSplit;
[12652]105
106 /*
107 * Keep the reference globally to avoid having it garbage collected
108 */
109 protected final transient ExpertToggleAction.ExpertModeChangeListener expertListener =
110 getExpertModeListenerForDownloadSources();
[8840]111 protected transient Bounds currentBounds;
[5097]112 protected boolean canceled;
[806]113
[12652]114 protected JButton btnDownload;
115 protected JButton btnCancel;
116 protected JButton btnHelp;
[806]117
[12652]118 /**
119 * Builds the main panel of the dialog.
120 * @return The panel of the dialog.
121 */
[6890]122 protected final JPanel buildMainPanel() {
[12652]123 mainPanel = new JPanel(new GridBagLayout());
[806]124
[8932]125 // must be created before hook
126 slippyMapChooser = new SlippyMapChooser();
127
[1169]128 // predefined download selections
[6365]129 downloadSelections.add(slippyMapChooser);
[1392]130 downloadSelections.add(new BookmarkSelection());
[1169]131 downloadSelections.add(new BoundingBoxSelection());
[1392]132 downloadSelections.add(new PlaceSelection());
[1169]133 downloadSelections.add(new TileSelection());
[806]134
[1169]135 // add selections from plugins
[1326]136 PluginHandler.addDownloadSelection(downloadSelections);
[806]137
[12652]138 // register all default download selections
[12653]139 for (DownloadSelection s : downloadSelections) {
140 s.addGui(this);
[1169]141 }
[2512]142
[12684]143 // allow to collapse the panes, but reserve some space for tabs
144 downloadSourcesTab.setMinimumSize(new Dimension(0, 25));
[12652]145 tpDownloadAreaSelectors.setMinimumSize(new Dimension(0, 0));
[806]146
[12705]147 dialogSplit = new DownloadDialogSplitPane(
[12652]148 downloadSourcesTab,
149 tpDownloadAreaSelectors);
[1038]150
[12684]151 ChangeListener tabChangedListener = getDownloadSourceTabChangeListener();
152 tabChangedListener.stateChanged(new ChangeEvent(downloadSourcesTab));
153 downloadSourcesTab.addChangeListener(tabChangedListener);
154
[12652]155 mainPanel.add(dialogSplit, GBC.eol().fill());
[3104]156
[3057]157 cbNewLayer = new JCheckBox(tr("Download as new layer"));
158 cbNewLayer.setToolTipText(tr("<html>Select to download data into a new data layer.<br>"
159 +"Unselect to download into the currently active data layer.</html>"));
160
[4906]161 cbStartup = new JCheckBox(tr("Open this dialog on startup"));
[8509]162 cbStartup.setToolTipText(
[8510]163 tr("<html>Autostart ''Download from OSM'' dialog every time JOSM is started.<br>" +
164 "You can open it manually from File menu or toolbar.</html>"));
[11270]165 cbStartup.addActionListener(e -> DOWNLOAD_AUTORUN.put(cbStartup.isSelected()));
[4906]166
[11658]167 cbZoomToDownloadedData = new JCheckBox(tr("Zoom to downloaded data"));
168 cbZoomToDownloadedData.setToolTipText(tr("Select to zoom to entire newly downloaded data."));
169
[12652]170 mainPanel.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5, 5, 5, 5));
171 mainPanel.add(cbStartup, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));
172 mainPanel.add(cbZoomToDownloadedData, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));
[3057]173
[11658]174 ExpertToggleAction.addVisibilitySwitcher(cbZoomToDownloadedData);
175
[12684]176 mainPanel.add(new JLabel(), GBC.eol()); // place info label at a new line
177 JLabel infoLabel = new JLabel(
178 tr("Use left click&drag to select area, arrows or right mouse button to scroll map, wheel or +/- to zoom."));
179 mainPanel.add(infoLabel, GBC.eol().anchor(GBC.CENTER).insets(0, 0, 0, 0));
180
181 ExpertToggleAction.addExpertModeChangeListener(isExpert -> infoLabel.setVisible(!isExpert), true);
182
[12652]183 return mainPanel;
[2331]184 }
[2512]185
[12652]186 /**
187 * Builds the button pane of the dialog.
188 * @return The button panel of the dialog.
189 */
[6890]190 protected final JPanel buildButtonPanel() {
[12652]191 btnDownload = new JButton(new DownloadAction());
192 btnCancel = new JButton(new CancelAction());
193 btnHelp = new JButton(
194 new ContextSensitiveHelpAction(getRootPane().getClientProperty("help").toString()));
195
[9543]196 JPanel pnl = new JPanel(new FlowLayout());
[2512]197
[10179]198 pnl.add(btnDownload);
[12652]199 pnl.add(btnCancel);
200 pnl.add(btnHelp);
201
[5200]202 InputMapUtils.enableEnter(btnDownload);
203 InputMapUtils.enableEnter(btnCancel);
[12652]204 InputMapUtils.addEscapeAction(getRootPane(), btnCancel.getAction());
205 InputMapUtils.enableEnter(btnHelp);
[2347]206
[12652]207 InputMapUtils.addEnterActionWhenAncestor(cbNewLayer, btnDownload.getAction());
208 InputMapUtils.addEnterActionWhenAncestor(cbStartup, btnDownload.getAction());
209 InputMapUtils.addEnterActionWhenAncestor(cbZoomToDownloadedData, btnDownload.getAction());
[2347]210
[2512]211 return pnl;
[2331]212 }
[2512]213
[8071]214 /**
215 * Constructs a new {@code DownloadDialog}.
216 * @param parent the parent component
217 */
[2331]218 public DownloadDialog(Component parent) {
[8713]219 this(parent, ht("/Action/Download"));
220 }
221
222 /**
223 * Constructs a new {@code DownloadDialog}.
224 * @param parent the parent component
225 * @param helpTopic the help topic to assign
226 */
227 public DownloadDialog(Component parent, String helpTopic) {
[10035]228 super(GuiHelper.getFrameForComponent(parent), tr("Download"), ModalityType.DOCUMENT_MODAL);
[8713]229 HelpUtil.setHelpContext(getRootPane(), helpTopic);
[2331]230 getContentPane().setLayout(new BorderLayout());
231 getContentPane().add(buildMainPanel(), BorderLayout.CENTER);
232 getContentPane().add(buildButtonPanel(), BorderLayout.SOUTH);
[2512]233
[2331]234 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
[12523]235 KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK), "checkClipboardContents");
[1329]236
[2331]237 getRootPane().getActionMap().put("checkClipboardContents", new AbstractAction() {
[6084]238 @Override
[1329]239 public void actionPerformed(ActionEvent e) {
[10604]240 String clip = ClipboardUtils.getClipboardStringContent();
[4380]241 if (clip == null) {
242 return;
243 }
244 Bounds b = OsmUrlToBounds.parse(clip);
245 if (b != null) {
246 boundingBoxChanged(new Bounds(b), null);
247 }
[1329]248 }
249 });
[2331]250 addWindowListener(new WindowEventHandler());
[12652]251 ExpertToggleAction.addExpertModeChangeListener(expertListener);
[2327]252 restoreSettings();
[12684]253
254 // if no bounding box is selected make sure it is still propagated.
255 if (currentBounds == null) {
256 boundingBoxChanged(null, null);
257 }
[1169]258 }
[806]259
[1169]260 /**
261 * Distributes a "bounding box changed" from one DownloadSelection
[12652]262 * object to the others, so they may update or clear their input fields. Also informs
263 * download sources about the change, so they can react on it.
[9230]264 * @param b new current bounds
[1169]265 *
266 * @param eventSource - the DownloadSelection object that fired this notification.
267 */
[2512]268 public void boundingBoxChanged(Bounds b, DownloadSelection eventSource) {
[2327]269 this.currentBounds = b;
[1169]270 for (DownloadSelection s : downloadSelections) {
[2142]271 if (s != eventSource) {
[2332]272 s.setDownloadArea(currentBounds);
[2142]273 }
[1169]274 }
[12652]275
[12706]276 for (AbstractDownloadSourcePanel<?> ds : downloadSourcesTab.getAllPanels()) {
277 ds.boudingBoxChanged(b);
[12652]278 }
[1169]279 }
[2512]280
[2327]281 /**
[8470]282 * Starts download for the given bounding box
283 * @param b bounding box to download
[2344]284 */
285 public void startDownload(Bounds b) {
286 this.currentBounds = b;
[12652]287 startDownload();
[2344]288 }
[2512]289
[2344]290 /**
[12652]291 * Starts download.
[1169]292 */
[12652]293 public void startDownload() {
294 btnDownload.doClick();
[1169]295 }
[2512]296
[2327]297 /**
[2512]298 * Replies true if the user requires to download into a new layer
299 *
300 * @return true if the user requires to download into a new layer
[2327]301 */
302 public boolean isNewLayerRequired() {
303 return cbNewLayer.isSelected();
304 }
[2512]305
[2327]306 /**
[11658]307 * Replies true if the user requires to zoom to new downloaded data
308 *
309 * @return true if the user requires to zoom to new downloaded data
310 * @since 11658
311 */
312 public boolean isZoomToDownloadedDataRequired() {
313 return cbZoomToDownloadedData.isSelected();
314 }
315
316 /**
[12652]317 * Determines if the dialog autorun is enabled in preferences.
[12684]318 * @return {@code true} if the download dialog must be open at startup, {@code false} otherwise.
[12652]319 */
320 public static boolean isAutorunEnabled() {
321 return DOWNLOAD_AUTORUN.get();
322 }
323
324 /**
[12684]325 * Adds a new download area selector to the download dialog.
[2512]326 *
[12684]327 * @param selector the download are selector.
328 * @param displayName the display name of the selector.
[2327]329 */
330 public void addDownloadAreaSelector(JPanel selector, String displayName) {
331 tpDownloadAreaSelectors.add(displayName, selector);
332 }
[2512]333
[2327]334 /**
[12684]335 * Adds a new download source to the download dialog if it is not added.
[12652]336 *
337 * @param downloadSource The download source to be added.
338 * @param <T> The type of the download data.
[12684]339 * @throws JosmRuntimeException If the download source is already added. Note, download sources are
340 * compared by their reference.
[12878]341 * @since 12878
[12652]342 */
[12878]343 public static <T> void addDownloadSource(DownloadSource<T> downloadSource) {
[12684]344 if (downloadSources.contains(downloadSource)) {
345 throw new JosmRuntimeException("The download source you are trying to add already exists.");
346 }
347
348 downloadSources.add(downloadSource);
[12878]349 downloadSourcesListeners.fireEvent(l -> l.downloadSourceAdded(downloadSource));
[12652]350 }
351
352 /**
[12684]353 * Refreshes the tile sources.
[6364]354 * @since 6364
355 */
356 public final void refreshTileSources() {
357 if (slippyMapChooser != null) {
358 slippyMapChooser.refreshTileSources();
359 }
360 }
[7434]361
[6364]362 /**
[6509]363 * Remembers the current settings in the download dialog.
[2327]364 */
365 public void rememberSettings() {
[11270]366 DOWNLOAD_TAB.put(tpDownloadAreaSelectors.getSelectedIndex());
[12903]367 downloadSourcesTab.getAllPanels().forEach(AbstractDownloadSourcePanel::rememberSettings);
[12706]368 downloadSourcesTab.getSelectedPanel().ifPresent(panel -> DOWNLOAD_SOURCE_TAB.put(panel.getSimpleName()));
[11270]369 DOWNLOAD_NEWLAYER.put(cbNewLayer.isSelected());
[11658]370 DOWNLOAD_ZOOMTODATA.put(cbZoomToDownloadedData.isSelected());
[2327]371 if (currentBounds != null) {
[12846]372 Config.getPref().put("osm-download.bounds", currentBounds.encodeAsString(";"));
[2327]373 }
374 }
[2512]375
[6509]376 /**
377 * Restores the previous settings in the download dialog.
378 */
[2327]379 public void restoreSettings() {
[11270]380 cbNewLayer.setSelected(DOWNLOAD_NEWLAYER.get());
[8444]381 cbStartup.setSelected(isAutorunEnabled());
[11658]382 cbZoomToDownloadedData.setSelected(DOWNLOAD_ZOOMTODATA.get());
[2512]383
[12652]384 try {
385 tpDownloadAreaSelectors.setSelectedIndex(DOWNLOAD_TAB.get());
386 } catch (IndexOutOfBoundsException e) {
[12654]387 Logging.trace(e);
[12652]388 tpDownloadAreaSelectors.setSelectedIndex(0);
389 }
390
[12900]391 downloadSourcesTab.getAllPanels().forEach(AbstractDownloadSourcePanel::restoreSettings);
[12706]392 downloadSourcesTab.setSelected(DOWNLOAD_SOURCE_TAB.get());
[12652]393
[12630]394 if (MainApplication.isDisplayingMapView()) {
395 MapView mv = MainApplication.getMap().mapView;
[2327]396 currentBounds = new Bounds(
397 mv.getLatLon(0, mv.getHeight()),
[2512]398 mv.getLatLon(mv.getWidth(), 0)
[2529]399 );
[8510]400 boundingBoxChanged(currentBounds, null);
[8342]401 } else {
[6509]402 Bounds bounds = getSavedDownloadBounds();
403 if (bounds != null) {
404 currentBounds = bounds;
405 boundingBoxChanged(currentBounds, null);
406 }
407 }
408 }
[7434]409
[6509]410 /**
411 * Returns the previously saved bounding box from preferences.
[12684]412 * @return The bounding box saved in preferences if any, {@code null} otherwise.
[6509]413 * @since 6509
414 */
415 public static Bounds getSavedDownloadBounds() {
[12846]416 String value = Config.getPref().get("osm-download.bounds");
[6509]417 if (!value.isEmpty()) {
[2327]418 try {
[6509]419 return new Bounds(value, ";");
420 } catch (IllegalArgumentException e) {
[12620]421 Logging.warn(e);
[2327]422 }
423 }
[6509]424 return null;
[2327]425 }
[2512]426
[6509]427 /**
[9230]428 * Automatically opens the download dialog, if autorun is enabled.
429 * @see #isAutorunEnabled
430 */
[4906]431 public static void autostartIfNeeded() {
432 if (isAutorunEnabled()) {
[12643]433 MainApplication.getMenu().download.actionPerformed(null);
[4906]434 }
435 }
436
[2327]437 /**
[12574]438 * Returns an {@link Optional} of the currently selected download area.
439 * @return An {@link Optional} of the currently selected download area.
440 * @since 12574 Return type changed to optional
[2327]441 */
[12574]442 public Optional<Bounds> getSelectedDownloadArea() {
443 return Optional.ofNullable(currentBounds);
[2327]444 }
[2512]445
[2331]446 @Override
447 public void setVisible(boolean visible) {
448 if (visible) {
449 new WindowGeometry(
450 getClass().getName() + ".geometry",
451 WindowGeometry.centerInWindow(
452 getParent(),
[8510]453 new Dimension(1000, 600)
[2331]454 )
[2824]455 ).applySafe(this);
[5998]456 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
[2331]457 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
458 }
459 super.setVisible(visible);
460 }
461
462 /**
463 * Replies true if the dialog was canceled
[2512]464 *
[2331]465 * @return true if the dialog was canceled
466 */
467 public boolean isCanceled() {
468 return canceled;
469 }
470
[12652]471 /**
472 * Gets the global settings of the download dialog.
473 * @return The {@link DownloadSettings} object that describes the current state of
474 * the download dialog.
475 */
476 public DownloadSettings getDownloadSettings() {
[12655]477 return new DownloadSettings(currentBounds, isNewLayerRequired(), isZoomToDownloadedDataRequired());
[12652]478 }
479
[2331]480 protected void setCanceled(boolean canceled) {
481 this.canceled = canceled;
482 }
[2512]483
[12652]484 /**
485 * Adds the download source to the download sources tab.
486 * @param downloadSource The download source to be added.
487 * @param <T> The type of the download data.
488 */
[12684]489 protected <T> void addNewDownloadSourceTab(DownloadSource<T> downloadSource) {
[12900]490 downloadSourcesTab.addPanel(downloadSource.createPanel(this));
[12652]491 }
492
493 /**
494 * Creates listener that removes/adds download sources from/to {@code downloadSourcesTab}
495 * depending on the current mode.
496 * @return The expert mode listener.
497 */
498 private ExpertToggleAction.ExpertModeChangeListener getExpertModeListenerForDownloadSources() {
[12706]499 return downloadSourcesTab::updateExpert;
[12652]500 }
501
502 /**
[12684]503 * Creates a listener that reacts on tab switches for {@code downloadSourcesTab} in order
504 * to adjust proper division of the dialog according to user saved preferences or minimal size
505 * of the panel.
506 * @return A listener to adjust dialog division.
507 */
508 private ChangeListener getDownloadSourceTabChangeListener() {
[12706]509 return ec -> downloadSourcesTab.getSelectedPanel().ifPresent(
510 panel -> dialogSplit.setPolicy(panel.getSizingPolicy()));
[12684]511 }
512
513 /**
[12652]514 * Action that is executed when the cancel button is pressed.
515 */
[2331]516 class CancelAction extends AbstractAction {
[8836]517 CancelAction() {
[2331]518 putValue(NAME, tr("Cancel"));
[10424]519 new ImageProvider("cancel").getResource().attachImageIcon(this);
[2512]520 putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and to abort downloading"));
[2331]521 }
[2512]522
[12655]523 /**
524 * Cancels the download
525 */
[2331]526 public void run() {
[12706]527 rememberSettings();
[2331]528 setCanceled(true);
[2512]529 setVisible(false);
[2331]530 }
[2512]531
[6084]532 @Override
[2331]533 public void actionPerformed(ActionEvent e) {
[12706]534 Optional<AbstractDownloadSourcePanel<?>> panel = downloadSourcesTab.getSelectedPanel();
535 run();
536 panel.ifPresent(AbstractDownloadSourcePanel::checkCancel);
[2512]537 }
[2331]538 }
539
[12652]540 /**
541 * Action that is executed when the download button is pressed.
542 */
[2331]543 class DownloadAction extends AbstractAction {
[8836]544 DownloadAction() {
[2331]545 putValue(NAME, tr("Download"));
[10424]546 new ImageProvider("download").getResource().attachImageIcon(this);
[3976]547 putValue(SHORT_DESCRIPTION, tr("Click to download the currently selected area"));
[7434]548 setEnabled(!Main.isOffline(OnlineResource.OSM_API));
[2331]549 }
[2512]550
[12655]551 /**
[12684]552 * Starts the download and closes the dialog, if all requirements for the current download source are met.
553 * Otherwise the download is not started and the dialog remains visible.
[12655]554 */
[2344]555 public void run() {
[12706]556 rememberSettings();
557 downloadSourcesTab.getSelectedPanel().ifPresent(panel -> {
[12652]558 DownloadSettings downloadSettings = getDownloadSettings();
[12706]559 if (panel.checkDownload(downloadSettings)) {
[12652]560 setCanceled(false);
561 setVisible(false);
[12706]562 panel.triggerDownload(downloadSettings);
[12652]563 }
[12706]564 });
[2344]565 }
[2512]566
[6084]567 @Override
[2344]568 public void actionPerformed(ActionEvent e) {
569 run();
[2512]570 }
[2331]571 }
[2512]572
[2331]573 class WindowEventHandler extends WindowAdapter {
574 @Override
575 public void windowClosing(WindowEvent e) {
576 new CancelAction().run();
[2344]577 }
578
579 @Override
580 public void windowActivated(WindowEvent e) {
581 btnDownload.requestFocusInWindow();
[2512]582 }
[2331]583 }
[12705]584
585 /**
[12706]586 * A special tabbed pane for {@link AbstractDownloadSourcePanel}s
587 * @author Michael Zangl
588 * @since 12706
589 */
[12900]590 private class DownloadSourceTabs extends JTabbedPane implements DownloadSourceListener {
[12706]591 private final List<AbstractDownloadSourcePanel<?>> allPanels = new ArrayList<>();
592
[12878]593 DownloadSourceTabs() {
594 downloadSources.forEach(this::downloadSourceAdded);
595 downloadSourcesListeners.addListener(this);
596 }
597
[12706]598 List<AbstractDownloadSourcePanel<?>> getAllPanels() {
599 return allPanels;
600 }
601
602 List<AbstractDownloadSourcePanel<?>> getVisiblePanels() {
603 return IntStream.range(0, getTabCount())
604 .mapToObj(this::getComponentAt)
605 .map(p -> (AbstractDownloadSourcePanel<?>) p)
606 .collect(Collectors.toList());
607 }
608
609 void setSelected(String simpleName) {
610 getVisiblePanels().stream()
611 .filter(panel -> simpleName.equals(panel.getSimpleName()))
612 .findFirst()
613 .ifPresent(this::setSelectedComponent);
614 }
615
616 void updateExpert(boolean isExpert) {
617 updateTabs();
618 }
619
620 void addPanel(AbstractDownloadSourcePanel<?> panel) {
621 allPanels.add(panel);
622 updateTabs();
623 }
624
625 private void updateTabs() {
626 // Not the best performance, but we don't do it often
627 removeAll();
628
629 boolean isExpert = ExpertToggleAction.isExpert();
630 allPanels.stream()
631 .filter(panel -> isExpert || !panel.getDownloadSource().onlyExpert())
632 .forEach(panel -> addTab(panel.getDownloadSource().getLabel(), panel.getIcon(), panel));
633 }
634
635 Optional<AbstractDownloadSourcePanel<?>> getSelectedPanel() {
636 return Optional.ofNullable((AbstractDownloadSourcePanel<?>) getSelectedComponent());
637 }
638
639 @Override
640 public void insertTab(String title, Icon icon, Component component, String tip, int index) {
641 if (!(component instanceof AbstractDownloadSourcePanel)) {
642 throw new IllegalArgumentException("Can only add AbstractDownloadSourcePanels");
643 }
644 super.insertTab(title, icon, component, tip, index);
645 }
[12878]646
647 @Override
648 public void downloadSourceAdded(DownloadSource<?> source) {
[12900]649 addPanel(source.createPanel(DownloadDialog.this));
[12878]650 }
[12706]651 }
652
653 /**
[12705]654 * A special split pane that acts according to a {@link DownloadSourceSizingPolicy}
655 *
656 * It attempts to size the top tab content correctly.
657 *
658 * @author Michael Zangl
659 * @since 12705
660 */
661 private static class DownloadDialogSplitPane extends JSplitPane {
662 private DownloadSourceSizingPolicy policy;
[12733]663 private final JTabbedPane topComponent;
[12705]664
665 DownloadDialogSplitPane(JTabbedPane newTopComponent, Component newBottomComponent) {
666 super(VERTICAL_SPLIT, newTopComponent, newBottomComponent);
667 this.topComponent = newTopComponent;
668 }
669
670 public void setPolicy(DownloadSourceSizingPolicy policy) {
671 this.policy = policy;
672
673 super.setDividerLocation(policy.getComponentHeight() + computeOffset());
674 setDividerSize(policy.isHeightAdjustable() ? 10 : 0);
675 setEnabled(policy.isHeightAdjustable());
676 }
677
678 @Override
679 public void doLayout() {
680 // We need to force this height before the layout manager is run.
681 // We cannot do this in the setDividerLocation, since the offset cannot be computed there.
682 int offset = computeOffset();
683 if (policy.isHeightAdjustable()) {
684 policy.storeHeight(Math.max(getDividerLocation() - offset, 0));
685 }
686 super.setDividerLocation(policy.getComponentHeight() + offset);
687 super.doLayout();
688 }
689
690 /**
691 * @return The difference between the content height and the divider location
692 */
693 private int computeOffset() {
694 Component selectedComponent = topComponent.getSelectedComponent();
695 return topComponent.getHeight() - (selectedComponent == null ? 0 : selectedComponent.getHeight());
696 }
697 }
[12575]698}
Note: See TracBrowser for help on using the repository browser.