1
package tim.prune.save;
3
import java.awt.BorderLayout;
5
import java.awt.Component;
6
import java.awt.FlowLayout;
7
import java.awt.GridLayout;
8
import java.awt.event.ActionEvent;
9
import java.awt.event.ActionListener;
10
import java.awt.event.KeyAdapter;
11
import java.awt.event.KeyEvent;
14
import javax.swing.BorderFactory;
15
import javax.swing.JButton;
16
import javax.swing.JCheckBox;
17
import javax.swing.JComboBox;
18
import javax.swing.JDialog;
19
import javax.swing.JLabel;
20
import javax.swing.JPanel;
22
import tim.prune.DataSubscriber;
23
import tim.prune.I18nManager;
24
import tim.prune.config.Config;
25
import tim.prune.data.Track;
26
import tim.prune.gui.map.MapSource;
27
import tim.prune.gui.map.MapSourceLibrary;
30
* Dialog to let you choose the parameters for a base image
33
public class BaseImageConfigDialog implements Runnable
35
/** Parent to notify */
36
private DataSubscriber _parent = null;
37
/** Parent dialog for position */
38
private JDialog _parentDialog = null;
39
/** Track to use for preview image */
40
private Track _track = null;
42
private JDialog _dialog = null;
43
/** Checkbox for using an image or not */
44
private JCheckBox _useImageCheckbox = null;
45
/** Panel to hold the other controls */
46
private JPanel _mainPanel = null;
47
/** Dropdown for map source */
48
private JComboBox _mapSourceDropdown = null;
49
/** Dropdown for zoom levels */
50
private JComboBox _zoomDropdown = null;
51
/** Warning label that image is incomplete */
52
private JLabel _imageIncompleteLabel = null;
53
/** Label for number of tiles found */
54
private JLabel _tilesFoundLabel = null;
55
/** Label for image size in pixels */
56
private JLabel _imageSizeLabel = null;
57
/** Image preview panel */
58
private ImagePreviewPanel _previewPanel = null;
59
/** OK button, needs to be enabled/disabled */
60
private JButton _okButton = null;
61
/** Flag for rebuilding dialog, don't bother refreshing and recalculating */
62
private boolean _rebuilding = false;
63
/** Cached values to allow cancellation of dialog */
64
private boolean _useImage = false;
65
private int _sourceIndex = 0;
66
private int _zoomLevel = 0;
71
* @param inParent parent object to notify on completion of dialog
72
* @param inParentDialog parent dialog
73
* @param inTrack track object
75
public BaseImageConfigDialog(DataSubscriber inParent, JDialog inParentDialog, Track inTrack)
78
_parentDialog = inParentDialog;
79
_dialog = new JDialog(inParentDialog, I18nManager.getText("dialog.baseimage.title"), true);
80
_dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
81
_dialog.getContentPane().add(makeDialogComponents());
92
_dialog.setLocationRelativeTo(_parentDialog);
93
_dialog.setVisible(true);
97
* Begin the function with a default of using an image
99
public void beginWithImageYes()
102
_useImageCheckbox.setSelected(true);
104
_dialog.setVisible(true);
108
* Initialise the dialog from the cached values
110
private void initDialog()
113
_useImageCheckbox.setSelected(_useImage);
114
// Populate the dropdown of map sources from the library in case it has changed
115
_mapSourceDropdown.removeAllItems();
116
for (int i=0; i<MapSourceLibrary.getNumSources(); i++)
118
_mapSourceDropdown.addItem(MapSourceLibrary.getSource(i).getName());
120
if (_sourceIndex < 0 || _sourceIndex >= _mapSourceDropdown.getItemCount()) {
123
_mapSourceDropdown.setSelectedIndex(_sourceIndex);
128
for (int i=0; i<_zoomDropdown.getItemCount(); i++)
130
String item = _zoomDropdown.getItemAt(i).toString();
132
if (Integer.parseInt(item) == _zoomLevel) {
133
_zoomDropdown.setSelectedIndex(i);
137
catch (NumberFormatException nfe) {}
145
* Update the visibility of the controls, and update the zoom dropdown based on the selected map source
147
private void refreshDialog()
149
_mainPanel.setVisible(_useImageCheckbox.isSelected());
150
// Exit if we're in the middle of something
151
if (_rebuilding) {return;}
154
currentZoom = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
156
catch (Exception nfe) {}
157
// Get the extent of the track so we can work out how big the images are going to be for each zoom level
158
// System.out.println("Ranges are: x=" + _track.getXRange().getRange() + ", y=" + _track.getYRange().getRange());
159
final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange());
160
int zoomToSelect = -1;
163
_zoomDropdown.removeAllItems();
164
if (_useImageCheckbox.isSelected() && _mapSourceDropdown.getItemCount() > 0)
166
int currentSource = _mapSourceDropdown.getSelectedIndex();
167
for (int i=5; i<18; i++)
169
// How many pixels does this give?
170
final int zoomFactor = 1 << i;
171
final int pixCount = (int) (xyExtent * zoomFactor * 256);
172
if (pixCount > 100 // less than this isn't worth it
173
&& pixCount < 4000 // don't want to run out of memory
174
&& isZoomAvailable(i, MapSourceLibrary.getSource(currentSource)))
176
_zoomDropdown.addItem("" + i);
177
if (i == currentZoom) {
178
zoomToSelect = _zoomDropdown.getItemCount() - 1;
181
// else System.out.println("Not using zoom " + i + " because pixCount=" + pixCount + " and xyExtent=" + xyExtent);
184
_zoomDropdown.setSelectedIndex(zoomToSelect);
187
_okButton.setEnabled(!_useImageCheckbox.isSelected() ||
188
(_zoomDropdown.getItemCount() > 0 && _zoomDropdown.getSelectedIndex() >= 0));
189
updateImagePreview();
193
* @return true if it should be possible to use an image, false if no disk cache or cache empty
195
public static boolean isImagePossible()
197
String path = Config.getConfigString(Config.KEY_DISK_CACHE);
198
if (path != null && !path.equals(""))
200
File cacheDir = new File(path);
201
if (cacheDir.exists() && cacheDir.isDirectory())
203
// Check if there are any directories in the cache
204
for (File subdir : cacheDir.listFiles())
206
if (subdir.exists() && subdir.isDirectory()) {
216
* See if the requested zoom level is available
217
* @param inZoom zoom level
218
* @param inSource selected map source
219
* @return true if there is a zoom directory for each of the source's layers
221
private static boolean isZoomAvailable(int inZoom, MapSource inSource)
223
if (inSource == null) {return false;}
224
String path = Config.getConfigString(Config.KEY_DISK_CACHE);
225
if (path == null || path.equals("")) {
228
File cacheDir = new File(path);
229
if (!cacheDir.exists() || !cacheDir.isDirectory()) {
233
File layer0 = new File(cacheDir, inSource.getSiteName(0) + inZoom);
234
if (!layer0.exists() || !layer0.isDirectory() || !layer0.canRead()) {
237
// Second layer, if any
238
if (inSource.getNumLayers() > 1)
240
File layer1 = new File(cacheDir, inSource.getSiteName(1) + inZoom);
241
if (!layer1.exists() || !layer1.isDirectory() || !layer1.canRead()) {
251
* @return true if image has been selected
253
public boolean useImage() {
258
* @return index of selected image source
260
public int getSourceIndex() {
265
* @return selected zoom level
267
public int getZoomLevel() {
272
* Make the dialog components to select the options
273
* @return Component holding gui elements
275
private Component makeDialogComponents()
277
JPanel panel = new JPanel();
278
panel.setLayout(new BorderLayout());
279
_useImageCheckbox = new JCheckBox(I18nManager.getText("dialog.baseimage.useimage"));
280
_useImageCheckbox.setBorder(BorderFactory.createEmptyBorder(4, 4, 6, 4));
281
_useImageCheckbox.setHorizontalAlignment(JLabel.CENTER);
282
_useImageCheckbox.addActionListener(new ActionListener() {
283
public void actionPerformed(ActionEvent arg0) {
287
panel.add(_useImageCheckbox, BorderLayout.NORTH);
289
// Outer panel with the grid and the map preview
290
_mainPanel = new JPanel();
291
_mainPanel.setLayout(new BorderLayout(1, 10));
292
// Central stuff with labels and dropdowns
293
JPanel controlsPanel = new JPanel();
294
controlsPanel.setLayout(new GridLayout(0, 2, 10, 4));
296
JLabel sourceLabel = new JLabel(I18nManager.getText("dialog.baseimage.mapsource") + ": ");
297
sourceLabel.setHorizontalAlignment(JLabel.RIGHT);
298
controlsPanel.add(sourceLabel);
299
_mapSourceDropdown = new JComboBox();
300
_mapSourceDropdown.addItem("name of map source");
301
// Add listener to dropdown to change zoom levels
302
_mapSourceDropdown.addActionListener(new ActionListener() {
303
public void actionPerformed(ActionEvent arg0) {
307
controlsPanel.add(_mapSourceDropdown);
309
JLabel zoomLabel = new JLabel(I18nManager.getText("dialog.baseimage.zoom") + ": ");
310
zoomLabel.setHorizontalAlignment(JLabel.RIGHT);
311
controlsPanel.add(zoomLabel);
312
_zoomDropdown = new JComboBox();
313
// Add action listener to enable ok button when zoom changed
314
_zoomDropdown.addActionListener(new ActionListener() {
315
public void actionPerformed(ActionEvent arg0) {
316
if (_zoomDropdown.getSelectedIndex() >= 0) {
317
_okButton.setEnabled(true);
318
updateImagePreview();
322
controlsPanel.add(_zoomDropdown);
323
_mainPanel.add(controlsPanel, BorderLayout.NORTH);
325
JPanel imagePanel = new JPanel();
326
imagePanel.setLayout(new BorderLayout(10, 1));
328
_previewPanel = new ImagePreviewPanel();
329
imagePanel.add(_previewPanel, BorderLayout.CENTER);
331
// Label panel on right
332
JPanel labelPanel = new JPanel();
333
labelPanel.setLayout(new BorderLayout());
334
_imageIncompleteLabel = new JLabel(I18nManager.getText("dialog.baseimage.incomplete"));
335
_imageIncompleteLabel.setForeground(Color.RED);
336
_imageIncompleteLabel.setVisible(false);
337
labelPanel.add(_imageIncompleteLabel, BorderLayout.NORTH);
338
JPanel labelGridPanel = new JPanel();
339
labelGridPanel.setLayout(new GridLayout(0, 2, 10, 4));
340
labelGridPanel.add(new JLabel(I18nManager.getText("dialog.baseimage.tiles") + ": "));
341
_tilesFoundLabel = new JLabel("11 / 11");
342
labelGridPanel.add(_tilesFoundLabel);
343
labelGridPanel.add(new JLabel(I18nManager.getText("dialog.baseimage.size") + ": "));
344
_imageSizeLabel = new JLabel("1430");
345
labelGridPanel.add(_imageSizeLabel);
346
labelGridPanel.add(new JLabel(" ")); // just for spacing
347
labelPanel.add(labelGridPanel, BorderLayout.SOUTH);
348
imagePanel.add(labelPanel, BorderLayout.EAST);
350
_mainPanel.add(imagePanel, BorderLayout.CENTER);
351
panel.add(_mainPanel, BorderLayout.CENTER);
353
// OK, Cancel buttons
354
JPanel buttonPanel = new JPanel();
355
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
356
_okButton = new JButton(I18nManager.getText("button.ok"));
357
_okButton.addActionListener(new ActionListener() {
358
public void actionPerformed(ActionEvent e)
360
// Check values, maybe don't want to exit
361
if (!_useImageCheckbox.isSelected()
362
|| (_mapSourceDropdown.getSelectedIndex() >= 0 && _zoomDropdown.getSelectedIndex() >= 0))
369
buttonPanel.add(_okButton);
370
JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
371
cancelButton.addActionListener(new ActionListener() {
372
public void actionPerformed(ActionEvent e)
377
buttonPanel.add(cancelButton);
378
panel.add(buttonPanel, BorderLayout.SOUTH);
380
// Listener to close dialog if escape pressed
381
KeyAdapter closer = new KeyAdapter() {
382
public void keyReleased(KeyEvent e)
384
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
389
_useImageCheckbox.addKeyListener(closer);
390
_mapSourceDropdown.addKeyListener(closer);
391
_zoomDropdown.addKeyListener(closer);
392
_okButton.addKeyListener(closer);
393
cancelButton.addKeyListener(closer);
399
* Use the selected settings to make a preview image and (asynchronously) update the preview panel
401
private void updateImagePreview()
404
_imageIncompleteLabel.setVisible(false);
405
_tilesFoundLabel.setText("");
406
_imageSizeLabel.setText("");
407
if (_useImageCheckbox.isSelected() && _mapSourceDropdown.getSelectedIndex() >= 0
408
&& _zoomDropdown.getItemCount() > 0 && _zoomDropdown.getSelectedIndex() >= 0)
410
_previewPanel.startLoading();
411
// Launch a separate thread to create an image and pass it to the preview panel
412
new Thread(this).start();
416
_previewPanel.setImage(null);
421
* Store the selected details in the variables
423
private void storeValues()
425
// Store values of controls in variables
426
_useImage = _useImageCheckbox.isSelected();
427
_sourceIndex = _mapSourceDropdown.getSelectedIndex();
429
String zoomStr = _zoomDropdown.getSelectedItem().toString();
430
_zoomLevel = Integer.parseInt(zoomStr);
432
catch (Exception nfe) {
435
// Call parent to retrieve values
436
_parent.dataUpdated(DataSubscriber.ALL);
440
* Run method for separate thread. Uses the current dialog parameters
441
* to trigger a call to the Grouter, and pass the image to the preview panel
445
// Remember the current dropdown indices, so we know whether they've changed or not
446
final int mapIndex = _mapSourceDropdown.getSelectedIndex();
447
final int zoomIndex = _zoomDropdown.getSelectedIndex();
448
if (!_useImageCheckbox.isSelected() || mapIndex < 0 || zoomIndex < 0) {return;}
450
// Get the map source and zoom level
451
MapSource mapSource = MapSourceLibrary.getSource(mapIndex);
454
zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString());
456
catch (Exception e) {}
458
// Use the Grouter to create an image (slow, blocks thread)
459
GroutedImage groutedImage = MapGrouter.createMapImage(_track, mapSource, zoomLevel);
461
// If the dialog hasn't changed, pass the generated image to the preview panel
462
if (_useImageCheckbox.isSelected()
463
&& _mapSourceDropdown.getSelectedIndex() == mapIndex
464
&& _zoomDropdown.getSelectedIndex() == zoomIndex
465
&& groutedImage != null)
467
_previewPanel.setImage(groutedImage);
468
// Set values of labels
469
_imageIncompleteLabel.setVisible(!groutedImage.isComplete());
470
_tilesFoundLabel.setText(groutedImage.getNumTilesUsed() + " / " + groutedImage.getNumTilesTotal());
471
if (groutedImage.getImageSize() > 0) {
472
_imageSizeLabel.setText("" + groutedImage.getImageSize());
475
_imageSizeLabel.setText("");
480
_previewPanel.setImage(null);
482
_imageIncompleteLabel.setVisible(false);
483
_tilesFoundLabel.setText("");
484
_imageSizeLabel.setText("");
489
* @return true if any map data has been found for the image
491
public boolean getFoundData()
493
return _useImage && _zoomLevel > 0 && _previewPanel != null && _previewPanel.getTilesFound();