]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/DetailsDisplay.java
765b88b30c37f5206e6abb30ac676c5d850ce885
[GpsPrune.git] / tim / prune / gui / DetailsDisplay.java
1 package tim.prune.gui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.FlowLayout;
6 import java.awt.Font;
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;
13
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.JList;
21 import javax.swing.JPanel;
22 import javax.swing.JScrollBar;
23 import javax.swing.JScrollPane;
24 import javax.swing.JTabbedPane;
25 import javax.swing.border.EtchedBorder;
26 import javax.swing.event.ListSelectionEvent;
27 import javax.swing.event.ListSelectionListener;
28
29 import tim.prune.App;
30 import tim.prune.DataSubscriber;
31 import tim.prune.I18nManager;
32 import tim.prune.data.Altitude;
33 import tim.prune.data.Coordinate;
34 import tim.prune.data.DataPoint;
35 import tim.prune.data.Distance;
36 import tim.prune.data.IntegerRange;
37 import tim.prune.data.Selection;
38 import tim.prune.data.TrackInfo;
39
40 /**
41  * Class to hold point details and selection details
42  * as a visual component
43  */
44 public class DetailsDisplay extends GenericDisplay
45 {
46         // App object to be notified of editing commands
47         private App _app = null;
48
49         // Track details
50         private JLabel _trackpointsLabel = null;
51         private JLabel _filenameLabel = null;
52         // Point details
53         private JLabel _indexLabel = null;
54         private JLabel _latLabel = null, _longLabel = null;
55         private JLabel _altLabel = null, _nameLabel = null;
56         private JLabel _timeLabel = null, _photoFileLabel = null;
57         // Scroll bar
58         private JScrollBar _scroller = null;
59         private boolean _ignoreScrollEvents = false;
60         // Button panel
61         private JButton _startRangeButton = null, _endRangeButton = null;
62         private JButton _deletePointButton = null, _deleteRangeButton = null;
63
64         // Range details
65         private JLabel _rangeLabel = null;
66         private JLabel _distanceLabel = null, _durationLabel = null;
67         private JLabel _altRangeLabel = null, _updownLabel = null;
68         // Photos
69         private JList _photoList = null;
70         private PhotoListModel _photoListModel = null;
71         // Waypoints
72         private JList _waypointList = null;
73         private WaypointListModel _waypointListModel = null;
74         // Units
75         private JComboBox _unitsDropdown = null;
76         // Formatter
77         private NumberFormat _distanceFormatter = NumberFormat.getInstance();
78
79         // Cached labels
80         private static final String LABEL_POINT_SELECTED1 = I18nManager.getText("details.index.selected") + ": ";
81         private static final String LABEL_POINT_LATITUDE = I18nManager.getText("fieldname.latitude") + ": ";
82         private static final String LABEL_POINT_LONGITUDE = I18nManager.getText("fieldname.longitude") + ": ";
83         private static final String LABEL_POINT_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
84         private static final String LABEL_POINT_TIMESTAMP = I18nManager.getText("fieldname.timestamp") + ": ";
85         private static final String LABEL_POINT_WAYPOINTNAME = I18nManager.getText("fieldname.waypointname") + ": ";
86         private static final String LABEL_RANGE_SELECTED1 = I18nManager.getText("details.range.selected") + ": ";
87         private static final String LABEL_RANGE_DURATION = I18nManager.getText("fieldname.duration") + ": ";
88         private static final String LABEL_RANGE_DISTANCE = I18nManager.getText("fieldname.distance") + ": ";
89         private static final String LABEL_RANGE_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
90         private static final String LABEL_RANGE_CLIMB = I18nManager.getText("details.range.climb") + ": ";
91         private static final String LABEL_RANGE_DESCENT = ", " + I18nManager.getText("details.range.descent") + ": ";
92         private static String LABEL_POINT_ALTITUDE_UNITS = null;
93         private static int LABEL_POINT_ALTITUDE_FORMAT = Altitude.FORMAT_NONE;
94         // scrollbar interval
95         private static final int SCROLLBAR_INTERVAL = 50;
96
97
98         /**
99          * Constructor
100          * @param inApp App object for callbacks
101          * @param inTrackInfo Track info object
102          */
103         public DetailsDisplay(App inApp, TrackInfo inTrackInfo)
104         {
105                 super(inTrackInfo);
106                 _app = inApp;
107                 setLayout(new BorderLayout());
108
109                 JPanel mainPanel = new JPanel();
110                 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
111                 mainPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
112                 // Track details panel
113                 JPanel trackDetailsPanel = new JPanel();
114                 trackDetailsPanel.setLayout(new BoxLayout(trackDetailsPanel, BoxLayout.Y_AXIS));
115                 trackDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
116                         BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
117                 );
118                 JLabel trackDetailsLabel = new JLabel(I18nManager.getText("details.trackdetails"));
119                 Font biggerFont = trackDetailsLabel.getFont();
120                 biggerFont = biggerFont.deriveFont(Font.BOLD, biggerFont.getSize2D() + 2.0f);
121                 trackDetailsLabel.setFont(biggerFont);
122                 trackDetailsPanel.add(trackDetailsLabel);
123                 _trackpointsLabel = new JLabel(I18nManager.getText("details.notrack"));
124                 trackDetailsPanel.add(_trackpointsLabel);
125                 _filenameLabel = new JLabel("");
126                 trackDetailsPanel.add(_filenameLabel);
127
128                 // Point details panel
129                 JPanel pointDetailsPanel = new JPanel();
130                 pointDetailsPanel.setLayout(new BoxLayout(pointDetailsPanel, BoxLayout.Y_AXIS));
131                 pointDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
132                         BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
133                 );
134                 JLabel pointDetailsLabel = new JLabel(I18nManager.getText("details.pointdetails"));
135                 pointDetailsLabel.setFont(biggerFont);
136                 pointDetailsPanel.add(pointDetailsLabel);
137                 _indexLabel = new JLabel(I18nManager.getText("details.nopointselection"));
138                 pointDetailsPanel.add(_indexLabel);
139                 _latLabel = new JLabel("");
140                 pointDetailsPanel.add(_latLabel);
141                 _longLabel = new JLabel("");
142                 pointDetailsPanel.add(_longLabel);
143                 _altLabel = new JLabel("");
144                 pointDetailsPanel.add(_altLabel);
145                 _timeLabel = new JLabel("");
146                 pointDetailsPanel.add(_timeLabel);
147                 _photoFileLabel = new JLabel("");
148                 pointDetailsPanel.add(_photoFileLabel);
149                 _nameLabel = new JLabel("");
150                 pointDetailsPanel.add(_nameLabel);
151                 pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
152
153                 // Scroll bar
154                 _scroller = new JScrollBar(JScrollBar.HORIZONTAL, 0, SCROLLBAR_INTERVAL, 0, 100);
155                 _scroller.addAdjustmentListener(new AdjustmentListener() {
156                         public void adjustmentValueChanged(AdjustmentEvent e)
157                         {
158                                 selectPoint(e.getValue());
159                         }
160                 });
161                 _scroller.setEnabled(false);
162
163                 // Button panel
164                 JPanel buttonPanel = new JPanel();
165                 buttonPanel.setLayout(new GridLayout(2, 2, 3, 3));
166                 _startRangeButton = new JButton(I18nManager.getText("button.startrange"));
167                 _startRangeButton.addActionListener(new ActionListener()
168                         {
169                                 public void actionPerformed(ActionEvent e)
170                                 {
171                                         _trackInfo.getSelection().selectRangeStart();
172                                 }
173                         });
174                 _startRangeButton.setEnabled(false);
175                 buttonPanel.add(_startRangeButton);
176                 _endRangeButton = new JButton(I18nManager.getText("button.endrange"));
177                 _endRangeButton.addActionListener(new ActionListener()
178                         {
179                                 public void actionPerformed(ActionEvent e)
180                                 {
181                                         _trackInfo.getSelection().selectRangeEnd();
182                                 }
183                         });
184                 _endRangeButton.setEnabled(false);
185                 buttonPanel.add(_endRangeButton);
186                 _deletePointButton = new JButton(I18nManager.getText("button.deletepoint"));
187                 _deletePointButton.addActionListener(new ActionListener()
188                         {
189                                 public void actionPerformed(ActionEvent e)
190                                 {
191                                         _app.deleteCurrentPoint();
192                                 }
193                         });
194                 _deletePointButton.setEnabled(false);
195                 buttonPanel.add(_deletePointButton);
196                 _deleteRangeButton = new JButton(I18nManager.getText("button.deleterange"));
197                 _deleteRangeButton.addActionListener(new ActionListener()
198                         {
199                                 public void actionPerformed(ActionEvent e)
200                                 {
201                                         _app.deleteSelectedRange();
202                                 }
203                         });
204                 _deleteRangeButton.setEnabled(false);
205                 buttonPanel.add(_deleteRangeButton);
206                 buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
207
208                 // range details panel
209                 JPanel otherDetailsPanel = new JPanel();
210                 otherDetailsPanel.setLayout(new BoxLayout(otherDetailsPanel, BoxLayout.Y_AXIS));
211                 otherDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
212                         BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
213                 );
214
215                 JLabel otherDetailsLabel = new JLabel(I18nManager.getText("details.rangedetails"));
216                 otherDetailsLabel.setFont(biggerFont);
217                 otherDetailsPanel.add(otherDetailsLabel);
218                 _rangeLabel = new JLabel(I18nManager.getText("details.norangeselection"));
219                 otherDetailsPanel.add(_rangeLabel);
220                 _distanceLabel = new JLabel("");
221                 otherDetailsPanel.add(_distanceLabel);
222                 _durationLabel = new JLabel("");
223                 otherDetailsPanel.add(_durationLabel);
224                 _altRangeLabel = new JLabel("");
225                 otherDetailsPanel.add(_altRangeLabel);
226                 _updownLabel = new JLabel("");
227                 otherDetailsPanel.add(_updownLabel);
228                 otherDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
229
230                 // Add tab panel for waypoints / photos
231                 JPanel waypointsPanel = new JPanel();
232                 waypointsPanel.setLayout(new BoxLayout(waypointsPanel, BoxLayout.Y_AXIS));
233                 waypointsPanel.setBorder(BorderFactory.createCompoundBorder(
234                         BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
235                 );
236                 JTabbedPane tabPane = new JTabbedPane();
237                 _waypointListModel = new WaypointListModel(_trackInfo.getTrack());
238                 _waypointList = new JList(_waypointListModel);
239                 _waypointList.setVisibleRowCount(5);
240                 _waypointList.addListSelectionListener(new ListSelectionListener() {
241                         public void valueChanged(ListSelectionEvent e)
242                         {
243                                 if (!e.getValueIsAdjusting()) selectWaypoint(_waypointList.getSelectedIndex());
244                         }});
245                 tabPane.addTab(I18nManager.getText("details.waypointsphotos.waypoints"), new JScrollPane(_waypointList));
246                 _photoListModel = new PhotoListModel(_trackInfo.getPhotoList());
247                 _photoList = new JList(_photoListModel);
248                 _photoList.setVisibleRowCount(5);
249                 _photoList.addListSelectionListener(new ListSelectionListener() {
250                         public void valueChanged(ListSelectionEvent e)
251                         {
252                                 if (!e.getValueIsAdjusting()) selectPhoto(_photoList.getSelectedIndex());
253                         }});
254                 // TODO: Re-add photos list after v2
255                 // tabPane.addTab(I18nManager.getText("details.waypointsphotos.photos"), new JScrollPane(_photoList));
256                 tabPane.setAlignmentX(Component.LEFT_ALIGNMENT);
257                 waypointsPanel.add(tabPane);
258                 waypointsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
259
260                 // add the slider, point details, and the other details to the main panel
261                 mainPanel.add(buttonPanel);
262                 mainPanel.add(Box.createVerticalStrut(5));
263                 mainPanel.add(_scroller);
264                 mainPanel.add(Box.createVerticalStrut(5));
265                 mainPanel.add(trackDetailsPanel);
266                 mainPanel.add(Box.createVerticalStrut(5));
267                 mainPanel.add(pointDetailsPanel);
268                 mainPanel.add(Box.createVerticalStrut(5));
269                 mainPanel.add(otherDetailsPanel);
270                 mainPanel.add(Box.createVerticalStrut(5));
271                 mainPanel.add(waypointsPanel);
272                 // add the main panel at the top
273                 add(mainPanel, BorderLayout.NORTH);
274
275                 // Add units selection
276                 JPanel lowerPanel = new JPanel();
277                 lowerPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
278                 lowerPanel.add(new JLabel(I18nManager.getText("details.distanceunits") + ": "));
279                 String[] distUnits = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.miles")};
280                 _unitsDropdown = new JComboBox(distUnits);
281                 _unitsDropdown.addActionListener(new ActionListener() {
282                         public void actionPerformed(ActionEvent e)
283                         {
284                                 dataUpdated(DataSubscriber.UNITS_CHANGED);
285                         }
286                 });
287                 lowerPanel.add(_unitsDropdown);
288                 add(lowerPanel, BorderLayout.SOUTH);
289         }
290
291
292         /**
293          * Select the specified point
294          * @param inValue value to select
295          */
296         private void selectPoint(int inValue)
297         {
298                 if (_track != null && !_ignoreScrollEvents)
299                 {
300                         _trackInfo.getSelection().selectPoint(inValue);
301                 }
302         }
303
304
305         /**
306          * Select the specified photo
307          * @param inPhotoIndex index of selected photo
308          */
309         private void selectPhoto(int inPhotoIndex)
310         {
311                 if (_photoListModel.getPhoto(inPhotoIndex) != null)
312                 {
313                         // TODO: Deselect the photo when another point is selected
314                         // TODO: show photo thumbnail
315                         // select associated point, if any
316                         DataPoint point = _photoListModel.getPhoto(inPhotoIndex).getDataPoint();
317                         if (point != null)
318                         {
319                                 _trackInfo.selectPoint(point);
320                         }
321                 }
322         }
323
324
325         /**
326          * Select the specified waypoint
327          * @param inWaypointIndex index of selected waypoint
328          */
329         private void selectWaypoint(int inWaypointIndex)
330         {
331                 if (inWaypointIndex >= 0)
332                 {
333                         _trackInfo.selectPoint(_waypointListModel.getWaypoint(inWaypointIndex));
334                 }
335         }
336
337
338         /**
339          * Notification that Track has been updated
340          */
341         public void dataUpdated(byte inUpdateType)
342         {
343                 // Update track data
344                 if (_track == null || _track.getNumPoints() <= 0)
345                 {
346                         _trackpointsLabel.setText(I18nManager.getText("details.notrack"));
347                         _filenameLabel.setText("");
348                 }
349                 else
350                 {
351                         _trackpointsLabel.setText(I18nManager.getText("details.track.points") + ": "
352                                 + _track.getNumPoints());
353                         int numFiles = _trackInfo.getFileInfo().getNumFiles();
354                         if (numFiles == 1)
355                         {
356                                 _filenameLabel.setText(I18nManager.getText("details.track.file") + ": "
357                                         + _trackInfo.getFileInfo().getFilename());
358                         }
359                         else if (numFiles > 1)
360                         {
361                                 _filenameLabel.setText(I18nManager.getText("details.track.numfiles") + ": "
362                                         + numFiles);
363                         }
364                         else _filenameLabel.setText("");
365                 }
366
367                 // Update current point data, if any
368                 DataPoint currentPoint = _trackInfo.getCurrentPoint();
369                 Selection selection = _trackInfo.getSelection();
370                 int currentPointIndex = selection.getCurrentPointIndex();
371                 if (_track == null || currentPoint == null)
372                 {
373                         _indexLabel.setText(I18nManager.getText("details.nopointselection"));
374                         _latLabel.setText("");
375                         _longLabel.setText("");
376                         _altLabel.setText("");
377                         _timeLabel.setText("");
378                         _photoFileLabel.setText("");
379                         _nameLabel.setText("");
380                 }
381                 else
382                 {
383                         _indexLabel.setText(LABEL_POINT_SELECTED1
384                                 + (currentPointIndex+1) + " " + I18nManager.getText("details.index.of")
385                                 + " " + _track.getNumPoints());
386                         _latLabel.setText(LABEL_POINT_LATITUDE + currentPoint.getLatitude().output(Coordinate.FORMAT_NONE));
387                         _longLabel.setText(LABEL_POINT_LONGITUDE + currentPoint.getLongitude().output(Coordinate.FORMAT_NONE));
388                         _altLabel.setText(LABEL_POINT_ALTITUDE
389                                 + (currentPoint.hasAltitude()?
390                                         (currentPoint.getAltitude().getValue() + getAltitudeUnitsLabel(currentPoint.getAltitude().getFormat())):
391                                 ""));
392                         if (currentPoint.getTimestamp().isValid())
393                                 _timeLabel.setText(LABEL_POINT_TIMESTAMP + currentPoint.getTimestamp().getText());
394                         else
395                                 _timeLabel.setText("");
396                         if (currentPoint.getPhoto() != null && currentPoint.getPhoto().getFile() != null)
397                         {
398                                 _photoFileLabel.setText(I18nManager.getText("details.photofile") + ": "
399                                         + currentPoint.getPhoto().getFile().getName());
400                         }
401                         else
402                                 _photoFileLabel.setText("");
403                         String name = currentPoint.getWaypointName();
404                         if (name != null && !name.equals(""))
405                         {
406                                 _nameLabel.setText(LABEL_POINT_WAYPOINTNAME + name);
407                         }
408                         else _nameLabel.setText("");
409                 }
410
411                 // Update scroller settings
412                 _ignoreScrollEvents = true;
413                 if (_track == null || _track.getNumPoints() < 2)
414                 {
415                         // careful to avoid event loops here
416                         // _scroller.setValue(0);
417                         _scroller.setEnabled(false);
418                 }
419                 else
420                 {
421                         _scroller.setMaximum(_track.getNumPoints() + SCROLLBAR_INTERVAL);
422                         if (currentPointIndex >= 0)
423                                 _scroller.setValue(currentPointIndex);
424                         _scroller.setEnabled(true);
425                 }
426                 _ignoreScrollEvents = false;
427
428                 // Update button panel
429                 boolean hasPoint = (_track != null && currentPointIndex >= 0);
430                 _startRangeButton.setEnabled(hasPoint);
431                 _endRangeButton.setEnabled(hasPoint);
432                 _deletePointButton.setEnabled(hasPoint);
433                 _deleteRangeButton.setEnabled(selection.hasRangeSelected());
434
435                 // Update range details
436                 if (_track == null || !selection.hasRangeSelected())
437                 {
438                         _rangeLabel.setText(I18nManager.getText("details.norangeselection"));
439                         _distanceLabel.setText("");
440                         _durationLabel.setText("");
441                         _altRangeLabel.setText("");
442                         _updownLabel.setText("");
443                 }
444                 else
445                 {
446                         _rangeLabel.setText(LABEL_RANGE_SELECTED1
447                                 + (selection.getStart()+1) + " " + I18nManager.getText("details.range.to")
448                                 + " " + (selection.getEnd()+1));
449                         if (_unitsDropdown.getSelectedIndex() == 0)
450                                 _distanceLabel.setText(LABEL_RANGE_DISTANCE + buildDistanceString(
451                                         selection.getDistance(Distance.UNITS_KILOMETRES))
452                                         + " " + I18nManager.getText("units.kilometres.short"));
453                         else
454                                 _distanceLabel.setText(LABEL_RANGE_DISTANCE + buildDistanceString(
455                                         selection.getDistance(Distance.UNITS_MILES))
456                                         + " " + I18nManager.getText("units.miles.short"));
457                         if (selection.getNumSeconds() > 0)
458                                 _durationLabel.setText(LABEL_RANGE_DURATION + buildDurationString(selection.getNumSeconds()));
459                         else
460                                 _durationLabel.setText("");
461                         String altUnitsLabel = getAltitudeUnitsLabel(selection.getAltitudeFormat());
462                         IntegerRange altRange = selection.getAltitudeRange();
463                         if (altRange.getMinimum() >= 0 && altRange.getMaximum() >= 0)
464                         {
465                                 _altRangeLabel.setText(LABEL_RANGE_ALTITUDE
466                                         + altRange.getMinimum() + altUnitsLabel + " "
467                                         + I18nManager.getText("details.altitude.to") + " "
468                                         + altRange.getMaximum() + altUnitsLabel);
469                                 _updownLabel.setText(LABEL_RANGE_CLIMB + selection.getClimb() + altUnitsLabel
470                                         + LABEL_RANGE_DESCENT + selection.getDescent() + altUnitsLabel);
471                         }
472                         else
473                         {
474                                 _altRangeLabel.setText("");
475                                 _updownLabel.setText("");
476                         }
477                 }
478                 // update waypoints and photos if necessary
479                 if ((inUpdateType |
480                         (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED | DataSubscriber.WAYPOINTS_MODIFIED)) > 0)
481                 {
482                         _waypointListModel.fireChanged();
483                 }
484                 if ((inUpdateType |
485                         (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED | DataSubscriber.PHOTOS_MODIFIED)) > 0)
486                 {
487                         _photoListModel.fireChanged();
488                 }
489                 // Deselect selected waypoint if selected point has since changed
490                 if (_waypointList.getSelectedIndex() >= 0)
491                 {
492                         if (_trackInfo.getCurrentPoint() == null
493                          || !_waypointListModel.getWaypoint(_waypointList.getSelectedIndex()).equals(_trackInfo.getCurrentPoint()))
494                         {
495                                 // point is selected in list but different from current point - deselect
496                                 _waypointList.clearSelection();
497                         }
498                 }
499                 // Do the same for the photos
500                 if (_photoList.getSelectedIndex() >= 0)
501                 {
502                         if (_trackInfo.getCurrentPoint() == null
503                                 || !_photoListModel.getPhoto(_photoList.getSelectedIndex()).getDataPoint().equals(_trackInfo.getCurrentPoint()))
504                         {
505                                 // photo is selected in list but different from current point - deselect
506                                 _photoList.clearSelection();
507                         }
508                 }
509         }
510
511
512         /**
513          * Choose the appropriate altitude units label for the specified format
514          * @param inFormat altitude format
515          * @return language-sensitive string
516          */
517         private static String getAltitudeUnitsLabel(int inFormat)
518         {
519                 if (inFormat == LABEL_POINT_ALTITUDE_FORMAT && LABEL_POINT_ALTITUDE_UNITS != null)
520                         return LABEL_POINT_ALTITUDE_UNITS;
521                 LABEL_POINT_ALTITUDE_FORMAT = inFormat;
522                 if (inFormat == Altitude.FORMAT_METRES)
523                         return " " + I18nManager.getText("units.metres.short");
524                 return " " + I18nManager.getText("units.feet.short");
525         }
526
527
528         /**
529          * Build a String to describe a time duration
530          * @param inNumSecs number of seconds
531          * @return time as a string, days, hours, mins, secs as appropriate
532          */
533         private static String buildDurationString(long inNumSecs)
534         {
535                 if (inNumSecs <= 0L) return "";
536                 if (inNumSecs < 60L) return "" + inNumSecs + I18nManager.getText("display.range.time.secs");
537                 if (inNumSecs < 3600L) return "" + (inNumSecs / 60) + I18nManager.getText("display.range.time.mins")
538                         + " " + (inNumSecs % 60) + I18nManager.getText("display.range.time.secs");
539                 if (inNumSecs < 86400L) return "" + (inNumSecs / 60 / 60) + I18nManager.getText("display.range.time.hours")
540                         + " " + ((inNumSecs / 60) % 60) + I18nManager.getText("display.range.time.mins");
541                 if (inNumSecs < 8640000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days");
542                 return "big";
543         }
544
545
546         /**
547          * Build a String to describe a distance
548          * @param inDist distance
549          * @return formatted String
550          */
551         private String buildDistanceString(double inDist)
552         {
553                 // Set precision of formatter
554                 int numDigits = 0;
555                 if (inDist < 1.0)
556                         numDigits = 3;
557                 else if (inDist < 10.0)
558                         numDigits = 2;
559                 else if (inDist < 100.0)
560                         numDigits = 1;
561                 // set formatter
562                 _distanceFormatter.setMaximumFractionDigits(numDigits);
563                 _distanceFormatter.setMinimumFractionDigits(numDigits);
564                 return _distanceFormatter.format(inDist);
565         }
566 }