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

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

see #15229 - deprecate Main.parent and Main itself

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