3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 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.AdjustmentEvent;
11 import java.awt.event.AdjustmentListener;
12 import java.text.NumberFormat;
14 import javax.swing.BorderFactory;
15 import javax.swing.Box;
16 import javax.swing.BoxLayout;
17 import javax.swing.JButton;
18 import javax.swing.JComboBox;
19 import javax.swing.JLabel;
20 import javax.swing.JPanel;
21 import javax.swing.JScrollBar;
22 import javax.swing.border.EtchedBorder;
25 import tim.prune.I18nManager;
26 import tim.prune.data.Altitude;
27 import tim.prune.data.Coordinate;
28 import tim.prune.data.DataPoint;
29 import tim.prune.data.Distance;
30 import tim.prune.data.Field;
31 import tim.prune.data.IntegerRange;
32 import tim.prune.data.Selection;
33 import tim.prune.data.TrackInfo;
36 * Class to hold point details and selection details
37 * as a visual component
39 public class DetailsDisplay extends GenericDisplay
41 // App object to be notified of editing commands
42 private App _app = null;
45 private JLabel _trackpointsLabel = null;
46 private JLabel _filenameLabel = null;
48 private JLabel _indexLabel = null;
49 private JLabel _latLabel = null, _longLabel = null;
50 private JLabel _altLabel = null, _nameLabel = null;
51 private JLabel _timeLabel = null;
53 private JScrollBar _scroller = null;
54 private boolean _ignoreScrollEvents = false;
56 private JButton _startRangeButton = null, _endRangeButton = null;
57 private JButton _deletePointButton = null, _deleteRangeButton = null;
60 private JLabel _rangeLabel = null;
61 private JLabel _distanceLabel = null, _durationLabel = null;
62 private JLabel _altRangeLabel = null, _updownLabel = null;
64 private JComboBox _unitsDropdown = null;
66 private NumberFormat _distanceFormatter = NumberFormat.getInstance();
69 private static final String LABEL_POINT_SELECTED1 = I18nManager.getText("details.index.selected") + ": ";
70 private static final String LABEL_POINT_LATITUDE = I18nManager.getText("fieldname.latitude") + ": ";
71 private static final String LABEL_POINT_LONGITUDE = I18nManager.getText("fieldname.longitude") + ": ";
72 private static final String LABEL_POINT_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
73 private static final String LABEL_POINT_TIMESTAMP = I18nManager.getText("fieldname.timestamp") + ": ";
74 private static final String LABEL_POINT_WAYPOINTNAME = I18nManager.getText("fieldname.waypointname") + ": ";
75 private static final String LABEL_RANGE_SELECTED1 = I18nManager.getText("details.range.selected") + ": ";
76 private static final String LABEL_RANGE_DURATION = I18nManager.getText("fieldname.duration") + ": ";
77 private static final String LABEL_RANGE_DISTANCE = I18nManager.getText("fieldname.distance") + ": ";
78 private static final String LABEL_RANGE_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
79 private static final String LABEL_RANGE_CLIMB = I18nManager.getText("details.range.climb") + ": ";
80 private static final String LABEL_RANGE_DESCENT = ", " + I18nManager.getText("details.range.descent") + ": ";
81 private static String LABEL_POINT_ALTITUDE_UNITS = null;
82 private static int LABEL_POINT_ALTITUDE_FORMAT = Altitude.FORMAT_NONE;
84 private static final int SCROLLBAR_INTERVAL = 50;
89 * @param inApp App object for callbacks
90 * @param inTrackInfo Track info object
92 public DetailsDisplay(App inApp, TrackInfo inTrackInfo)
96 setLayout(new BorderLayout());
98 JPanel mainPanel = new JPanel();
99 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
100 mainPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
101 // Track details panel
102 JPanel trackDetailsPanel = new JPanel();
103 trackDetailsPanel.setLayout(new BoxLayout(trackDetailsPanel, BoxLayout.Y_AXIS));
104 trackDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
105 BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
107 JLabel trackDetailsLabel = new JLabel(I18nManager.getText("details.trackdetails"));
108 Font biggerFont = trackDetailsLabel.getFont();
109 biggerFont = biggerFont.deriveFont(Font.BOLD, biggerFont.getSize2D() + 2.0f);
110 trackDetailsLabel.setFont(biggerFont);
111 trackDetailsPanel.add(trackDetailsLabel);
112 _trackpointsLabel = new JLabel(I18nManager.getText("details.notrack"));
113 trackDetailsPanel.add(_trackpointsLabel);
114 _filenameLabel = new JLabel("");
115 trackDetailsPanel.add(_filenameLabel);
117 // Point details panel
118 JPanel pointDetailsPanel = new JPanel();
119 pointDetailsPanel.setLayout(new BoxLayout(pointDetailsPanel, BoxLayout.Y_AXIS));
120 pointDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
121 BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
123 JLabel pointDetailsLabel = new JLabel(I18nManager.getText("details.pointdetails"));
124 pointDetailsLabel.setFont(biggerFont);
125 pointDetailsPanel.add(pointDetailsLabel);
126 _indexLabel = new JLabel(I18nManager.getText("details.nopointselection"));
127 pointDetailsPanel.add(_indexLabel);
128 _latLabel = new JLabel("");
129 pointDetailsPanel.add(_latLabel);
130 _longLabel = new JLabel("");
131 pointDetailsPanel.add(_longLabel);
132 _altLabel = new JLabel("");
133 pointDetailsPanel.add(_altLabel);
134 _timeLabel = new JLabel("");
135 pointDetailsPanel.add(_timeLabel);
136 _nameLabel = new JLabel("");
137 pointDetailsPanel.add(_nameLabel);
138 pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
141 _scroller = new JScrollBar(JScrollBar.HORIZONTAL, 0, SCROLLBAR_INTERVAL, 0, 100);
142 _scroller.addAdjustmentListener(new AdjustmentListener() {
143 public void adjustmentValueChanged(AdjustmentEvent e)
145 selectPoint(e.getValue());
148 _scroller.setEnabled(false);
151 JPanel buttonPanel = new JPanel();
152 buttonPanel.setLayout(new GridLayout(2, 2, 3, 3));
153 _startRangeButton = new JButton(I18nManager.getText("button.startrange"));
154 _startRangeButton.addActionListener(new ActionListener()
156 public void actionPerformed(ActionEvent e)
158 _trackInfo.getSelection().selectRangeStart();
161 _startRangeButton.setEnabled(false);
162 buttonPanel.add(_startRangeButton);
163 _endRangeButton = new JButton(I18nManager.getText("button.endrange"));
164 _endRangeButton.addActionListener(new ActionListener()
166 public void actionPerformed(ActionEvent e)
168 _trackInfo.getSelection().selectRangeEnd();
171 _endRangeButton.setEnabled(false);
172 buttonPanel.add(_endRangeButton);
173 _deletePointButton = new JButton(I18nManager.getText("button.deletepoint"));
174 _deletePointButton.addActionListener(new ActionListener()
176 public void actionPerformed(ActionEvent e)
178 _app.deleteCurrentPoint();
181 _deletePointButton.setEnabled(false);
182 buttonPanel.add(_deletePointButton);
183 _deleteRangeButton = new JButton(I18nManager.getText("button.deleterange"));
184 _deleteRangeButton.addActionListener(new ActionListener()
186 public void actionPerformed(ActionEvent e)
188 _app.deleteSelectedRange();
191 _deleteRangeButton.setEnabled(false);
192 buttonPanel.add(_deleteRangeButton);
193 buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
195 // range details panel
196 JPanel otherDetailsPanel = new JPanel();
197 otherDetailsPanel.setLayout(new BoxLayout(otherDetailsPanel, BoxLayout.Y_AXIS));
198 otherDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
199 BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
202 JLabel otherDetailsLabel = new JLabel(I18nManager.getText("details.rangedetails"));
203 otherDetailsLabel.setFont(biggerFont);
204 otherDetailsPanel.add(otherDetailsLabel);
205 _rangeLabel = new JLabel(I18nManager.getText("details.norangeselection"));
206 otherDetailsPanel.add(_rangeLabel);
207 _distanceLabel = new JLabel("");
208 otherDetailsPanel.add(_distanceLabel);
209 _durationLabel = new JLabel("");
210 otherDetailsPanel.add(_durationLabel);
211 _altRangeLabel = new JLabel("");
212 otherDetailsPanel.add(_altRangeLabel);
213 _updownLabel = new JLabel("");
214 otherDetailsPanel.add(_updownLabel);
215 otherDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
217 // add the main panel at the top
218 add(mainPanel, BorderLayout.NORTH);
219 // add the slider, point details, and the other details to the main panel
220 mainPanel.add(buttonPanel);
221 mainPanel.add(Box.createVerticalStrut(5));
222 mainPanel.add(_scroller);
223 mainPanel.add(Box.createVerticalStrut(5));
224 mainPanel.add(trackDetailsPanel);
225 mainPanel.add(Box.createVerticalStrut(5));
226 mainPanel.add(pointDetailsPanel);
227 mainPanel.add(Box.createVerticalStrut(5));
228 mainPanel.add(otherDetailsPanel);
230 // Add units selection
231 JPanel lowerPanel = new JPanel();
232 lowerPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
233 lowerPanel.add(new JLabel(I18nManager.getText("details.distanceunits") + ": "));
234 String[] distUnits = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.miles")};
235 _unitsDropdown = new JComboBox(distUnits);
236 _unitsDropdown.addActionListener(new ActionListener() {
237 public void actionPerformed(ActionEvent e)
242 lowerPanel.add(_unitsDropdown);
243 add(lowerPanel, BorderLayout.SOUTH);
248 * Select the specified point
249 * @param inValue value to select
251 private void selectPoint(int inValue)
253 if (_track != null && !_ignoreScrollEvents)
255 _trackInfo.getSelection().selectPoint(inValue);
261 * Notification that Track has been updated
263 public void dataUpdated()
266 if (_track == null || _track.getNumPoints() <= 0)
268 _trackpointsLabel.setText(I18nManager.getText("details.notrack"));
269 _filenameLabel.setText("");
273 _trackpointsLabel.setText(I18nManager.getText("details.track.points") + ": "
274 + _track.getNumPoints());
275 int numFiles = _trackInfo.getFileInfo().getNumFiles();
278 _filenameLabel.setText(I18nManager.getText("details.track.file") + ": "
279 + _trackInfo.getFileInfo().getFilename());
281 else if (numFiles > 1)
283 _filenameLabel.setText(I18nManager.getText("details.track.numfiles") + ": "
286 else _filenameLabel.setText("");
289 // Update current point data, if any
290 DataPoint currentPoint = _trackInfo.getCurrentPoint();
291 Selection selection = _trackInfo.getSelection();
292 int currentPointIndex = selection.getCurrentPointIndex();
293 if (_track == null || currentPoint == null)
295 _indexLabel.setText(I18nManager.getText("details.nopointselection"));
296 _latLabel.setText("");
297 _longLabel.setText("");
298 _altLabel.setText("");
299 _timeLabel.setText("");
300 _nameLabel.setText("");
304 _indexLabel.setText(LABEL_POINT_SELECTED1
305 + (currentPointIndex+1) + " " + I18nManager.getText("details.index.of")
306 + " " + _track.getNumPoints());
307 _latLabel.setText(LABEL_POINT_LATITUDE + currentPoint.getLatitude().output(Coordinate.FORMAT_NONE));
308 _longLabel.setText(LABEL_POINT_LONGITUDE + currentPoint.getLongitude().output(Coordinate.FORMAT_NONE));
309 _altLabel.setText(LABEL_POINT_ALTITUDE
310 + (currentPoint.hasAltitude()?
311 (currentPoint.getAltitude().getValue() + getAltitudeUnitsLabel(currentPoint.getAltitude().getFormat())):
313 if (currentPoint.getTimestamp().isValid())
314 _timeLabel.setText(LABEL_POINT_TIMESTAMP + currentPoint.getTimestamp().getText());
316 _timeLabel.setText("");
317 String name = currentPoint.getFieldValue(Field.WAYPT_NAME);
318 if (name != null && !name.equals(""))
320 _nameLabel.setText(LABEL_POINT_WAYPOINTNAME + name);
322 else _nameLabel.setText("");
325 // Update scroller settings
326 _ignoreScrollEvents = true;
327 if (_track == null || _track.getNumPoints() < 2)
329 // careful to avoid event loops here
330 // _scroller.setValue(0);
331 _scroller.setEnabled(false);
335 _scroller.setMaximum(_track.getNumPoints() + SCROLLBAR_INTERVAL);
336 if (currentPointIndex >= 0)
337 _scroller.setValue(currentPointIndex);
338 _scroller.setEnabled(true);
340 _ignoreScrollEvents = false;
342 // Update button panel
343 boolean hasPoint = (_track != null && currentPointIndex >= 0);
344 _startRangeButton.setEnabled(hasPoint);
345 _endRangeButton.setEnabled(hasPoint);
346 _deletePointButton.setEnabled(hasPoint);
347 _deleteRangeButton.setEnabled(selection.hasRangeSelected());
349 // Update range details
350 if (_track == null || !selection.hasRangeSelected())
352 _rangeLabel.setText(I18nManager.getText("details.norangeselection"));
353 _distanceLabel.setText("");
354 _durationLabel.setText("");
355 _altRangeLabel.setText("");
356 _updownLabel.setText("");
360 _rangeLabel.setText(LABEL_RANGE_SELECTED1
361 + (selection.getStart()+1) + " " + I18nManager.getText("details.range.to")
362 + " " + (selection.getEnd()+1));
363 if (_unitsDropdown.getSelectedIndex() == 0)
364 _distanceLabel.setText(LABEL_RANGE_DISTANCE + buildDistanceString(
365 selection.getDistance(Distance.UNITS_KILOMETRES))
366 + " " + I18nManager.getText("units.kilometres.short"));
368 _distanceLabel.setText(LABEL_RANGE_DISTANCE + buildDistanceString(
369 selection.getDistance(Distance.UNITS_MILES))
370 + " " + I18nManager.getText("units.miles.short"));
371 if (selection.getNumSeconds() > 0)
372 _durationLabel.setText(LABEL_RANGE_DURATION + buildDurationString(selection.getNumSeconds()));
374 _durationLabel.setText("");
375 String altUnitsLabel = getAltitudeUnitsLabel(selection.getAltitudeFormat());
376 IntegerRange altRange = selection.getAltitudeRange();
377 if (altRange.getMinimum() >= 0 && altRange.getMaximum() >= 0)
379 _altRangeLabel.setText(LABEL_RANGE_ALTITUDE
380 + altRange.getMinimum() + altUnitsLabel + " "
381 + I18nManager.getText("details.altitude.to") + " "
382 + altRange.getMaximum() + altUnitsLabel);
383 _updownLabel.setText(LABEL_RANGE_CLIMB + selection.getClimb() + altUnitsLabel
384 + LABEL_RANGE_DESCENT + selection.getDescent() + altUnitsLabel);
388 _altRangeLabel.setText("");
389 _updownLabel.setText("");
396 * Choose the appropriate altitude units label for the specified format
397 * @param inFormat altitude format
398 * @return language-sensitive string
400 private static String getAltitudeUnitsLabel(int inFormat)
402 if (inFormat == LABEL_POINT_ALTITUDE_FORMAT && LABEL_POINT_ALTITUDE_UNITS != null)
403 return LABEL_POINT_ALTITUDE_UNITS;
404 LABEL_POINT_ALTITUDE_FORMAT = inFormat;
405 if (inFormat == Altitude.FORMAT_METRES)
406 return " " + I18nManager.getText("units.metres.short");
407 return " " + I18nManager.getText("units.feet.short");
412 * Build a String to describe a time duration
413 * @param inNumSecs number of seconds
414 * @return time as a string, days, hours, mins, secs as appropriate
416 private static String buildDurationString(long inNumSecs)
418 if (inNumSecs <= 0L) return "";
419 if (inNumSecs < 60L) return "" + inNumSecs + I18nManager.getText("display.range.time.secs");
420 if (inNumSecs < 3600L) return "" + (inNumSecs / 60) + I18nManager.getText("display.range.time.mins")
421 + " " + (inNumSecs % 60) + I18nManager.getText("display.range.time.secs");
422 if (inNumSecs < 86400L) return "" + (inNumSecs / 60 / 60) + I18nManager.getText("display.range.time.hours")
423 + " " + ((inNumSecs / 60) % 60) + I18nManager.getText("display.range.time.mins");
424 if (inNumSecs < 8640000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days");
430 * Build a String to describe a distance
431 * @param inDist distance
432 * @return formatted String
434 private String buildDistanceString(double inDist)
436 // Set precision of formatter
440 else if (inDist < 10.0)
442 else if (inDist < 100.0)
445 _distanceFormatter.setMaximumFractionDigits(numDigits);
446 _distanceFormatter.setMinimumFractionDigits(numDigits);
447 return _distanceFormatter.format(inDist);