+++ /dev/null
-package tim.prune.save;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.FlowLayout;
-import java.awt.GridLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.io.File;
-
-import javax.swing.BorderFactory;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-
-import tim.prune.I18nManager;
-import tim.prune.config.Config;
-import tim.prune.data.Track;
-import tim.prune.gui.map.MapSource;
-import tim.prune.gui.map.MapSourceLibrary;
-import tim.prune.threedee.ImageDefinition;
-
-/**
- * Dialog to let you choose the parameters for a base image
- * (source and zoom) including preview
- */
-public class BaseImageConfigDialog implements Runnable
-{
- /** Parent to notify */
- private BaseImageConsumer _parent = null;
- /** Parent dialog for position */
- private JDialog _parentDialog = null;
- /** Track to use for preview image */
- private Track _track = null;
- /** Dialog to show */
- private JDialog _dialog = null;
- /** Checkbox for using an image or not */
- private JCheckBox _useImageCheckbox = null;
- /** Panel to hold the other controls */
- private JPanel _mainPanel = null;
- /** Dropdown for map source */
- private JComboBox<String> _mapSourceDropdown = null;
- /** Dropdown for zoom levels */
- private JComboBox<String> _zoomDropdown = null;
- /** Button to trigger a download of the missing map tiles */
- private JButton _downloadTilesButton = null;
- /** Progress bar for downloading additional tiles */
- private JProgressBar _progressBar = null;
- /** Label for number of tiles found */
- private JLabel _tilesFoundLabel = null;
- /** Label for image size in pixels */
- private JLabel _imageSizeLabel = null;
- /** Image preview panel */
- private ImagePreviewPanel _previewPanel = null;
- /** Grouter, used to avoid regenerating images */
- private MapGrouter _grouter = new MapGrouter();
- /** OK button, needs to be enabled/disabled */
- private JButton _okButton = null;
- /** Flag for rebuilding dialog, don't bother refreshing and recalculating */
- private boolean _rebuilding = false;
- /** Cached values to allow cancellation of dialog */
- private ImageDefinition _imageDef = new ImageDefinition();
-
-
- /**
- * Constructor
- * @param inParent parent object to notify on completion of dialog
- * @param inParentDialog parent dialog
- * @param inTrack track object
- */
- public BaseImageConfigDialog(BaseImageConsumer inParent, JDialog inParentDialog, Track inTrack)
- {
- _parent = inParent;
- _parentDialog = inParentDialog;
- _dialog = new JDialog(inParentDialog, I18nManager.getText("dialog.baseimage.title"), true);
- _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
- _dialog.getContentPane().add(makeDialogComponents());
- _dialog.pack();
- _track = inTrack;
- }
-
- /**
- * @param inDefinition image definition object from previous dialog
- */
- public void setImageDefinition(ImageDefinition inDefinition)
- {
- _imageDef = inDefinition;
- if (_imageDef == null) {
- _imageDef = new ImageDefinition();
- }
- }
-
- /**
- * Begin the function
- */
- public void begin()
- {
- initDialog();
- _dialog.setLocationRelativeTo(_parentDialog);
- _dialog.setVisible(true);
- }
-
- /**
- * Begin the function with a default of using an image
- */
- public void beginWithImageYes()
- {
- initDialog();
- _useImageCheckbox.setSelected(true);
- refreshDialog();
- _dialog.setVisible(true);
- }
-
- /**
- * Initialise the dialog from the cached values
- */
- private void initDialog()
- {
- _rebuilding = true;
- _useImageCheckbox.setSelected(_imageDef.getUseImage());
- // Populate the dropdown of map sources from the library in case it has changed
- _mapSourceDropdown.removeAllItems();
- for (int i=0; i<MapSourceLibrary.getNumSources(); i++)
- {
- _mapSourceDropdown.addItem(MapSourceLibrary.getSource(i).getName());
- }
- int sourceIndex = _imageDef.getSourceIndex();
- if (sourceIndex < 0 || sourceIndex >= _mapSourceDropdown.getItemCount()) {
- sourceIndex = 0;
- }
- _mapSourceDropdown.setSelectedIndex(sourceIndex);
-
- // Zoom level
- int zoomLevel = _imageDef.getZoom();
- if (_imageDef.getUseImage())
- {
- for (int i=0; i<_zoomDropdown.getItemCount(); i++)
- {
- String item = _zoomDropdown.getItemAt(i).toString();
- try {
- if (Integer.parseInt(item) == zoomLevel)
- {
- _zoomDropdown.setSelectedIndex(i);
- break;
- }
- }
- catch (NumberFormatException nfe) {}
- }
- }
- _rebuilding = false;
- refreshDialog();
- }
-
- /**
- * Update the visibility of the controls, and update the zoom dropdown based on the selected map source
- */
- private void refreshDialog()
- {
- _mainPanel.setVisible(_useImageCheckbox.isSelected());
- // Exit if we're in the middle of something
- if (_rebuilding) {return;}
- int currentZoom = 0;
- try {
- currentZoom = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
- }
- catch (Exception nfe) {}
- // First time in, the dropdown might be empty but we still might have a zoom in the definition
- if (_zoomDropdown.getItemCount() == 0) {
- currentZoom = _imageDef.getZoom();
- }
- // Get the extent of the track so we can work out how big the images are going to be for each zoom level
- final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange());
- int zoomToSelect = -1;
-
- _rebuilding = true;
- _zoomDropdown.removeAllItems();
- if (_useImageCheckbox.isSelected() && _mapSourceDropdown.getItemCount() > 0)
- {
- int currentSource = _mapSourceDropdown.getSelectedIndex();
- for (int i=5; i<18; i++)
- {
- // How many pixels does this give?
- final int zoomFactor = 1 << i;
- final int pixCount = (int) (xyExtent * zoomFactor * 256);
- if (pixCount > 100 // less than this isn't worth it
- && pixCount < 4000 // don't want to run out of memory
- && isZoomAvailable(i, MapSourceLibrary.getSource(currentSource)))
- {
- _zoomDropdown.addItem("" + i);
- if (i == currentZoom) {
- zoomToSelect = _zoomDropdown.getItemCount() - 1;
- }
- }
- // else System.out.println("Not using zoom " + i + " because pixCount=" + pixCount + " and xyExtent=" + xyExtent);
- }
- }
- _zoomDropdown.setSelectedIndex(zoomToSelect);
- _rebuilding = false;
-
- _okButton.setEnabled(!_useImageCheckbox.isSelected() ||
- (_zoomDropdown.getItemCount() > 0 && _zoomDropdown.getSelectedIndex() >= 0));
- updateImagePreview();
- }
-
- /**
- * @return true if it should be possible to use an image, false if no disk cache or cache empty
- */
- public static boolean isImagePossible()
- {
- String path = Config.getConfigString(Config.KEY_DISK_CACHE);
- if (path != null && !path.equals(""))
- {
- File cacheDir = new File(path);
- if (cacheDir.exists() && cacheDir.isDirectory())
- {
- // Check if there are any directories in the cache
- for (File subdir : cacheDir.listFiles())
- {
- if (subdir.exists() && subdir.isDirectory()) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * See if the requested zoom level is available
- * @param inZoom zoom level
- * @param inSource selected map source
- * @return true if there is a zoom directory for each of the source's layers
- */
- private static boolean isZoomAvailable(int inZoom, MapSource inSource)
- {
- if (inSource == null) {return false;}
- String path = Config.getConfigString(Config.KEY_DISK_CACHE);
- if (path == null || path.equals("")) {
- return false;
- }
- File cacheDir = new File(path);
- if (!cacheDir.exists() || !cacheDir.isDirectory()) {
- return false;
- }
- // First layer
- File layer0 = new File(cacheDir, inSource.getSiteName(0) + inZoom);
- if (!layer0.exists() || !layer0.isDirectory() || !layer0.canRead()) {
- return false;
- }
- // Second layer, if any
- if (inSource.getNumLayers() > 1)
- {
- File layer1 = new File(cacheDir, inSource.getSiteName(1) + inZoom);
- if (!layer1.exists() || !layer1.isDirectory() || !layer1.canRead()) {
- return false;
- }
- }
- // must be ok
- return true;
- }
-
-
- /**
- * @return image definition object
- */
- public ImageDefinition getImageDefinition() {
- return _imageDef;
- }
-
- /**
- * Make the dialog components to select the options
- * @return Component holding gui elements
- */
- private Component makeDialogComponents()
- {
- JPanel panel = new JPanel();
- panel.setLayout(new BorderLayout());
- _useImageCheckbox = new JCheckBox(I18nManager.getText("dialog.baseimage.useimage"));
- _useImageCheckbox.setBorder(BorderFactory.createEmptyBorder(4, 4, 6, 4));
- _useImageCheckbox.setHorizontalAlignment(JLabel.CENTER);
- _useImageCheckbox.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent arg0) {
- refreshDialog();
- }
- });
- panel.add(_useImageCheckbox, BorderLayout.NORTH);
-
- // Outer panel with the grid and the map preview
- _mainPanel = new JPanel();
- _mainPanel.setLayout(new BorderLayout(1, 10));
- // Central stuff with labels and dropdowns
- JPanel controlsPanel = new JPanel();
- controlsPanel.setLayout(new GridLayout(0, 2, 10, 4));
- // map source
- JLabel sourceLabel = new JLabel(I18nManager.getText("dialog.baseimage.mapsource") + ": ");
- sourceLabel.setHorizontalAlignment(JLabel.RIGHT);
- controlsPanel.add(sourceLabel);
- _mapSourceDropdown = new JComboBox<String>();
- _mapSourceDropdown.addItem("name of map source");
- // Add listener to dropdown to change zoom levels
- _mapSourceDropdown.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent arg0) {
- refreshDialog();
- }
- });
- controlsPanel.add(_mapSourceDropdown);
- // zoom level
- JLabel zoomLabel = new JLabel(I18nManager.getText("dialog.baseimage.zoom") + ": ");
- zoomLabel.setHorizontalAlignment(JLabel.RIGHT);
- controlsPanel.add(zoomLabel);
- _zoomDropdown = new JComboBox<String>();
- // Add action listener to enable ok button when zoom changed
- _zoomDropdown.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent arg0) {
- if (_zoomDropdown.getSelectedIndex() >= 0) {
- _okButton.setEnabled(true);
- updateImagePreview();
- }
- }
- });
- controlsPanel.add(_zoomDropdown);
- _mainPanel.add(controlsPanel, BorderLayout.NORTH);
-
- JPanel imagePanel = new JPanel();
- imagePanel.setLayout(new BorderLayout(10, 1));
- // image preview
- _previewPanel = new ImagePreviewPanel();
- imagePanel.add(_previewPanel, BorderLayout.CENTER);
-
- // Label panel on right
- JPanel labelPanel = new JPanel();
- labelPanel.setLayout(new BorderLayout());
- JPanel downloadPanel = new JPanel();
- downloadPanel.setLayout(new BorderLayout(4, 4));
- _downloadTilesButton = new JButton(I18nManager.getText("button.load"));
- _downloadTilesButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent arg0) {
- downloadRemainingTiles();
- }
- });
- _downloadTilesButton.setVisible(false);
- downloadPanel.add(_downloadTilesButton, BorderLayout.NORTH);
- _progressBar = new JProgressBar();
- _progressBar.setIndeterminate(true);
- _progressBar.setVisible(false);
- downloadPanel.add(_progressBar, BorderLayout.SOUTH);
- labelPanel.add(downloadPanel, BorderLayout.NORTH);
- JPanel labelGridPanel = new JPanel();
- labelGridPanel.setLayout(new GridLayout(0, 2, 10, 4));
- labelGridPanel.add(new JLabel(I18nManager.getText("dialog.baseimage.tiles") + ": "));
- _tilesFoundLabel = new JLabel("11 / 11");
- labelGridPanel.add(_tilesFoundLabel);
- labelGridPanel.add(new JLabel(I18nManager.getText("dialog.baseimage.size") + ": "));
- _imageSizeLabel = new JLabel("1430");
- labelGridPanel.add(_imageSizeLabel);
- labelGridPanel.add(new JLabel(" ")); // just for spacing
- labelPanel.add(labelGridPanel, BorderLayout.SOUTH);
- imagePanel.add(labelPanel, BorderLayout.EAST);
-
- _mainPanel.add(imagePanel, BorderLayout.CENTER);
- panel.add(_mainPanel, BorderLayout.CENTER);
-
- // OK, Cancel buttons
- JPanel buttonPanel = new JPanel();
- buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
- _okButton = new JButton(I18nManager.getText("button.ok"));
- _okButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- // Check values, maybe don't want to exit
- if (!_useImageCheckbox.isSelected()
- || (_mapSourceDropdown.getSelectedIndex() >= 0 && _zoomDropdown.getSelectedIndex() >= 0))
- {
- storeValues();
- _dialog.dispose();
- }
- }
- });
- buttonPanel.add(_okButton);
- JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
- cancelButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _dialog.dispose();
- }
- });
- buttonPanel.add(cancelButton);
- panel.add(buttonPanel, BorderLayout.SOUTH);
-
- // Listener to close dialog if escape pressed
- KeyAdapter closer = new KeyAdapter() {
- public void keyReleased(KeyEvent e)
- {
- if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
- _dialog.dispose();
- }
- }
- };
- _useImageCheckbox.addKeyListener(closer);
- _mapSourceDropdown.addKeyListener(closer);
- _zoomDropdown.addKeyListener(closer);
- _okButton.addKeyListener(closer);
- cancelButton.addKeyListener(closer);
-
- return panel;
- }
-
- /**
- * Use the selected settings to make a preview image and (asynchronously) update the preview panel
- */
- private void updateImagePreview()
- {
- // Clear labels
- _downloadTilesButton.setVisible(false);
- _tilesFoundLabel.setText("");
- _imageSizeLabel.setText("");
- if (_useImageCheckbox.isSelected() && _mapSourceDropdown.getSelectedIndex() >= 0
- && _zoomDropdown.getItemCount() > 0 && _zoomDropdown.getSelectedIndex() >= 0)
- {
- _previewPanel.startLoading();
- // Launch a separate thread to create an image and pass it to the preview panel
- new Thread(this).start();
- }
- else {
- // clear preview
- _previewPanel.setImage(null);
- }
- }
-
- /**
- * Store the selected details in the variables
- */
- private void storeValues()
- {
- // Store values of controls in variables
- _imageDef.setUseImage(_useImageCheckbox.isSelected(),
- _mapSourceDropdown.getSelectedIndex(),
- getSelectedZoomLevel());
- // Inform parent that details have changed
- _parent.baseImageChanged();
- }
-
- /**
- * Run method for separate thread. Uses the current dialog parameters
- * to trigger a call to the Grouter, and pass the image to the preview panel
- */
- public void run()
- {
- // Remember the current dropdown indices, so we know whether they've changed or not
- final int mapIndex = _mapSourceDropdown.getSelectedIndex();
- final int zoomIndex = _zoomDropdown.getSelectedIndex();
- if (!_useImageCheckbox.isSelected() || mapIndex < 0 || zoomIndex < 0) {return;}
-
- // Get the map source from the index
- MapSource mapSource = MapSourceLibrary.getSource(mapIndex);
-
- // Use the Grouter to create an image (slow, blocks thread)
- GroutedImage groutedImage = _grouter.createMapImage(_track, mapSource, getSelectedZoomLevel());
-
- // If the dialog hasn't changed, pass the generated image to the preview panel
- if (_useImageCheckbox.isSelected()
- && _mapSourceDropdown.getSelectedIndex() == mapIndex
- && _zoomDropdown.getSelectedIndex() == zoomIndex
- && groutedImage != null)
- {
- _previewPanel.setImage(groutedImage);
- final int numTilesRemaining = groutedImage.getNumTilesTotal() - groutedImage.getNumTilesUsed();
- final boolean offerDownload = numTilesRemaining > 0 && numTilesRemaining < 50 && Config.getConfigBoolean(Config.KEY_ONLINE_MODE);
- // Set values of labels
- _downloadTilesButton.setVisible(offerDownload);
- _downloadTilesButton.setEnabled(offerDownload);
- _tilesFoundLabel.setText(groutedImage.getNumTilesUsed() + " / " + groutedImage.getNumTilesTotal());
- if (groutedImage.getImageSize() > 0) {
- _imageSizeLabel.setText("" + groutedImage.getImageSize());
- }
- else {
- _imageSizeLabel.setText("");
- }
- }
- else
- {
- _previewPanel.setImage(null);
- // Clear labels
- _downloadTilesButton.setVisible(false);
- _tilesFoundLabel.setText("");
- _imageSizeLabel.setText("");
- }
- }
-
- /**
- * @return zoom level selected in the dropdown
- */
- private int getSelectedZoomLevel()
- {
- int zoomLevel = 0;
- try {
- zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
- }
- catch (NullPointerException npe) {}
- catch (Exception e) {
- System.err.println("Exception: " + e.getClass().getName() + " : " + e.getMessage());
- }
- return zoomLevel;
- }
-
- /**
- * @return true if any map data has been found for the image
- */
- public boolean getFoundData()
- {
- return _imageDef.getUseImage() && _imageDef.getZoom() > 0
- && _previewPanel != null && _previewPanel.getTilesFound();
- }
-
- /**
- * @return true if selected zoom is valid for the current track (based only on pixel size)
- */
- public boolean isSelectedZoomValid()
- {
- final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange());
- // How many pixels does this give?
- final int zoomFactor = 1 << _imageDef.getZoom();
- final int pixCount = (int) (xyExtent * zoomFactor * 256);
- return (pixCount > 100 // less than this isn't worth it
- && pixCount < 4000); // don't want to run out of memory
- }
-
- /**
- * @return the map grouter for retrieval of generated image
- */
- public MapGrouter getGrouter()
- {
- return _grouter;
- }
-
- /**
- * @return method triggered by "download" button, to asynchronously download the missing tiles
- */
- private void downloadRemainingTiles()
- {
- _downloadTilesButton.setEnabled(false);
- new Thread(new Runnable() {
- public void run()
- {
- _progressBar.setVisible(true);
- // Use a grouter to get all tiles from the TileManager, including downloading
- MapGrouter grouter = new MapGrouter();
- final int mapIndex = _mapSourceDropdown.getSelectedIndex();
- if (!_useImageCheckbox.isSelected() || mapIndex < 0) {return;}
- MapSource mapSource = MapSourceLibrary.getSource(mapIndex);
- grouter.createMapImage(_track, mapSource, getSelectedZoomLevel(), true);
- _progressBar.setVisible(false);
- // And then refresh the dialog
- _grouter.clearMapImage();
- updateImagePreview();
- }
- }).start();
- }
-}