// License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.gui.download;
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.SideButton;
import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
import org.openstreetmap.josm.gui.help.HelpUtil;
import org.openstreetmap.josm.plugins.PluginHandler;
import org.openstreetmap.josm.tools.GBC;
import org.openstreetmap.josm.tools.ImageProvider;
import org.openstreetmap.josm.tools.OsmUrlToBounds;
import org.openstreetmap.josm.tools.WindowGeometry;
/**
*
*/
public class DownloadDialog extends JDialog {
@SuppressWarnings("unused")
static private final Logger logger = Logger.getLogger(DownloadDialog.class.getName());
/** the unique instance of the download dialog */
static private DownloadDialog instance;
/**
* Replies the unique instance of the download dialog
*
* @return the unique instance of the download dialog
*/
static public DownloadDialog getInstance() {
if (instance == null) {
instance = new DownloadDialog(Main.parent);
}
return instance;
}
private final List<DownloadSelection> downloadSelections = new ArrayList<DownloadSelection>();
private final JTabbedPane tpDownloadAreaSelectors = new JTabbedPane();
private JCheckBox cbNewLayer;
private final JLabel sizeCheck = new JLabel();
private Bounds currentBounds = null;
private boolean canceled;
private JCheckBox cbDownloadOsmData;
private JCheckBox cbDownloadGpxData;
/** the download action and button */
private DownloadAction actDownload;
private SideButton btnDownload;
private void makeCheckBoxRespondToEnter(JCheckBox cb) {
cb.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "doDownload");
cb.getActionMap().put("doDownload", actDownload);
}
public JPanel buildMainPanel() {
// generic constraints used by different components
GridBagConstraints gridBagConstraints;
JPanel pnl = new JPanel();
pnl.setLayout(new GridBagLayout());
// adding the download tasks
pnl.add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5,5,1,5));
cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
pnl.add(cbDownloadOsmData, GBC.std().insets(1,5,1,5));
cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
pnl.add(cbDownloadGpxData, GBC.eol().insets(5,5,1,5));
// predefined download selections
downloadSelections.add(new SlippyMapChooser());
downloadSelections.add(new BookmarkSelection());
downloadSelections.add(new BoundingBoxSelection());
downloadSelections.add(new PlaceSelection());
downloadSelections.add(new TileSelection());
// add selections from plugins
PluginHandler.addDownloadSelection(downloadSelections);
// now everybody may add their tab to the tabbed pane
// (not done right away to allow plugins to remove one of
// the default selectors!)
for (DownloadSelection s : downloadSelections) {
s.addGui(this);
}
pnl.add(tpDownloadAreaSelectors, GBC.eol().fill());
try {
tpDownloadAreaSelectors.setSelectedIndex(Main.pref.getInteger("download.tab", 0));
} catch (Exception ex) {
Main.pref.putInteger("download.tab", 0);
}
Font labelFont = sizeCheck.getFont();
sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
cbNewLayer = new JCheckBox(tr("Download as new layer"));
cbNewLayer.setToolTipText(tr("<html>Select to download data into a new data layer.<br>"
+"Unselect to download into the currently active data layer.</html>"));
pnl.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5,5,5,5));
pnl.add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5,5,5,5));
return pnl;
}
protected JPanel buildButtonPanel() {
JPanel pnl = new JPanel();
pnl.setLayout(new FlowLayout());
// -- download button
pnl.add(btnDownload = new SideButton(actDownload = new DownloadAction()));
btnDownload.setFocusable(true);
btnDownload.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "download");
btnDownload.getActionMap().put("download",actDownload);
makeCheckBoxRespondToEnter(cbDownloadGpxData);
makeCheckBoxRespondToEnter(cbDownloadOsmData);
makeCheckBoxRespondToEnter(cbNewLayer);
// -- cancel button
SideButton btnCancel;
CancelAction actCancel = new CancelAction();
pnl.add(btnCancel = new SideButton(actCancel));
btnCancel.setFocusable(true);
btnCancel.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
btnCancel.getActionMap().put("enter",actCancel);
// -- cancel on ESC
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "cancel");
getRootPane().getActionMap().put("cancel", actCancel);
// -- help button
SideButton btnHelp;
pnl.add(btnHelp = new SideButton(new ContextSensitiveHelpAction(ht("/Dialog/DownloadDialog"))));
btnHelp.setFocusable(true);
btnHelp.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
btnHelp.getActionMap().put("enter",btnHelp.getAction());
return pnl;
}
public DownloadDialog(Component parent) {
super(JOptionPane.getFrameForComponent(parent),tr("Download"), true /* modal */);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(buildMainPanel(), BorderLayout.CENTER);
getContentPane().add(buildButtonPanel(), BorderLayout.SOUTH);
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK), "checkClipboardContents");
getRootPane().getActionMap().put("checkClipboardContents", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
checkClipboardContents();
}
});
HelpUtil.setHelpContext(getRootPane(), ht("/Dialog/DownloadDialog"));
addWindowListener(new WindowEventHandler());
restoreSettings();
}
private void checkClipboardContents() {
String result = "";
Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
if(contents == null || !contents.isDataFlavorSupported(DataFlavor.stringFlavor))
return;
try {
result = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch(Exception ex) {
return;
}
Bounds b = OsmUrlToBounds.parse(result);
if (b != null) {
boundingBoxChanged(new Bounds(b),null);
}
}
private void updateSizeCheck() {
if (currentBounds == null) {
sizeCheck.setText(tr("No area selected yet"));
sizeCheck.setForeground(Color.darkGray);
} else if (currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area", 0.25)) {
sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
sizeCheck.setForeground(Color.red);
} else {
sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
sizeCheck.setForeground(Color.darkGray);
}
}
/**
* Distributes a "bounding box changed" from one DownloadSelection
* object to the others, so they may update or clear their input
* fields.
*
* @param eventSource - the DownloadSelection object that fired this notification.
*/
public void boundingBoxChanged(Bounds b, DownloadSelection eventSource) {
this.currentBounds = b;
for (DownloadSelection s : downloadSelections) {
if (s != eventSource) {
s.setDownloadArea(currentBounds);
}
}
updateSizeCheck();
}
/**
* Invoked by
* @param b
*/
public void startDownload(Bounds b) {
this.currentBounds = b;
actDownload.run();
}
/**
* Replies true if the user selected to download OSM data
*
* @return true if the user selected to download OSM data
*/
public boolean isDownloadOsmData() {
return cbDownloadOsmData.isSelected();
}
/**
* Replies true if the user selected to download GPX data
*
* @return true if the user selected to download GPX data
*/
public boolean isDownloadGpxData() {
return cbDownloadGpxData.isSelected();
}
/**
* Replies true if the user requires to download into a new layer
*
* @return true if the user requires to download into a new layer
*/
public boolean isNewLayerRequired() {
return cbNewLayer.isSelected();
}
/**
* Adds a new download area selector to the download dialog
*
* @param selector the download are selector
* @param displayName the display name of the selector
*/
public void addDownloadAreaSelector(JPanel selector, String displayName) {
tpDownloadAreaSelectors.add(displayName, selector);
}
/**
* Remembers the current settings in the download dialog
*
*/
public void rememberSettings() {
Main.pref.put("download.tab", Integer.toString(tpDownloadAreaSelectors.getSelectedIndex()));
Main.pref.put("download.osm", cbDownloadOsmData.isSelected());
Main.pref.put("download.gps", cbDownloadGpxData.isSelected());
Main.pref.put("download.newlayer", cbNewLayer.isSelected());
if (currentBounds != null) {
Main.pref.put("osm-download.bounds", currentBounds.encodeAsString(";"));
}
}
public void restoreSettings() {
cbDownloadOsmData.setSelected(Main.pref.getBoolean("download.osm", true));
cbDownloadGpxData.setSelected(Main.pref.getBoolean("download.gps", false));
cbNewLayer.setSelected(Main.pref.getBoolean("download.newlayer", false));
int idx = Main.pref.getInteger("download.tab", 0);
if (idx < 0 || idx > tpDownloadAreaSelectors.getTabCount()) {
idx = 0;
}
tpDownloadAreaSelectors.setSelectedIndex(idx);
if (Main.map != null) {
MapView mv = Main.map.mapView;
currentBounds = new Bounds(
mv.getLatLon(0, mv.getHeight()),
mv.getLatLon(mv.getWidth(), 0)
);
boundingBoxChanged(currentBounds,null);
}
else if (Main.pref.hasKey("osm-download.bounds")) {
// read the bounding box from the preferences
try {
currentBounds = new Bounds(Main.pref.get("osm-download.bounds"), ";");
boundingBoxChanged(currentBounds,null);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Replies the currently selected download area. May be null, if no download area is selected
* yet.
*/
public Bounds getSelectedDownloadArea() {
return currentBounds;
}
@Override
public void setVisible(boolean visible) {
if (visible) {
new WindowGeometry(
getClass().getName() + ".geometry",
WindowGeometry.centerInWindow(
getParent(),
new Dimension(1000,600)
)
).applySafe(this);
} else if (!visible && isShowing()){
new WindowGeometry(this).remember(getClass().getName() + ".geometry");
}
super.setVisible(visible);
}
/**
* Replies true if the dialog was canceled
*
* @return true if the dialog was canceled
*/
public boolean isCanceled() {
return canceled;
}
protected void setCanceled(boolean canceled) {
this.canceled = canceled;
}
class CancelAction extends AbstractAction {
public CancelAction() {
putValue(NAME, tr("Cancel"));
putValue(SMALL_ICON, ImageProvider.get("cancel"));
putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and to abort downloading"));
}
public void run() {
setCanceled(true);
setVisible(false);
}
public void actionPerformed(ActionEvent e) {
run();
}
}
class DownloadAction extends AbstractAction {
public DownloadAction() {
putValue(NAME, tr("Download"));
putValue(SMALL_ICON, ImageProvider.get("download"));
putValue(SHORT_DESCRIPTION, tr("Click do download the currently selected area"));
}
public void run() {
if (currentBounds == null) {
JOptionPane.showMessageDialog(
DownloadDialog.this,
tr("Please select a download area first."),
tr("Error"),
JOptionPane.ERROR_MESSAGE
);
return;
}
if (!isDownloadOsmData() && !isDownloadGpxData()) {
JOptionPane.showMessageDialog(
DownloadDialog.this,
tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> is enabled.<br>"
+ "Please chose to either download OSM data, or GPX data, or both.</html>",
cbDownloadOsmData.getText(),
cbDownloadGpxData.getText()
),
tr("Error"),
JOptionPane.ERROR_MESSAGE
);
return;
}
setCanceled(false);
setVisible(false);
}
public void actionPerformed(ActionEvent e) {
run();
}
}
class WindowEventHandler extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
new CancelAction().run();
}
@Override
public void windowActivated(WindowEvent e) {
btnDownload.requestFocusInWindow();
}
}
}