]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/DetailsDisplay.java
Version 1, September 2006
[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.JPanel;
21 import javax.swing.JScrollBar;
22 import javax.swing.border.EtchedBorder;
23
24 import tim.prune.App;
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;
34
35 /**
36  * Class to hold point details and selection details
37  * as a visual component
38  */
39 public class DetailsDisplay extends GenericDisplay
40 {
41         // App object to be notified of editing commands
42         private App _app = null;
43
44         // Track details
45         private JLabel _trackpointsLabel = null;
46         private JLabel _filenameLabel = null;
47         // Point details
48         private JLabel _indexLabel = null;
49         private JLabel _latLabel = null, _longLabel = null;
50         private JLabel _altLabel = null, _nameLabel = null;
51         private JLabel _timeLabel = null;
52         // Scroll bar
53         private JScrollBar _scroller = null;
54         private boolean _ignoreScrollEvents = false;
55         // Button panel
56         private JButton _startRangeButton = null, _endRangeButton = null;
57         private JButton _deletePointButton = null, _deleteRangeButton = null;
58
59         // Range details
60         private JLabel _rangeLabel = null;
61         private JLabel _distanceLabel = null, _durationLabel = null;
62         private JLabel _altRangeLabel = null, _updownLabel = null;
63         // Units
64         private JComboBox _unitsDropdown = null;
65         // Formatter
66         private NumberFormat _distanceFormatter = NumberFormat.getInstance();
67
68         // Cached labels
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;
83         // scrollbar interval
84         private static final int SCROLLBAR_INTERVAL = 50;
85
86
87         /**
88          * Constructor
89          * @param inApp App object for callbacks
90          * @param inTrackInfo Track info object
91          */
92         public DetailsDisplay(App inApp, TrackInfo inTrackInfo)
93         {
94                 super(inTrackInfo);
95                 _app = inApp;
96                 setLayout(new BorderLayout());
97
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))
106                 );
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);
116                 
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))
122                 );
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);
139
140                 // Scroll bar
141                 _scroller = new JScrollBar(JScrollBar.HORIZONTAL, 0, SCROLLBAR_INTERVAL, 0, 100);
142                 _scroller.addAdjustmentListener(new AdjustmentListener() {
143                         public void adjustmentValueChanged(AdjustmentEvent e)
144                         {
145                                 selectPoint(e.getValue());
146                         }
147                 });
148                 _scroller.setEnabled(false);
149
150                 // Button panel
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()
155                         {
156                                 public void actionPerformed(ActionEvent e)
157                                 {
158                                         _trackInfo.getSelection().selectRangeStart();
159                                 }
160                         });
161                 _startRangeButton.setEnabled(false);
162                 buttonPanel.add(_startRangeButton);
163                 _endRangeButton = new JButton(I18nManager.getText("button.endrange"));
164                 _endRangeButton.addActionListener(new ActionListener()
165                         {
166                                 public void actionPerformed(ActionEvent e)
167                                 {
168                                         _trackInfo.getSelection().selectRangeEnd();
169                                 }
170                         });
171                 _endRangeButton.setEnabled(false);
172                 buttonPanel.add(_endRangeButton);
173                 _deletePointButton = new JButton(I18nManager.getText("button.deletepoint"));
174                 _deletePointButton.addActionListener(new ActionListener()
175                         {
176                                 public void actionPerformed(ActionEvent e)
177                                 {
178                                         _app.deleteCurrentPoint();
179                                 }
180                         });
181                 _deletePointButton.setEnabled(false);
182                 buttonPanel.add(_deletePointButton);
183                 _deleteRangeButton = new JButton(I18nManager.getText("button.deleterange"));
184                 _deleteRangeButton.addActionListener(new ActionListener()
185                         {
186                                 public void actionPerformed(ActionEvent e)
187                                 {
188                                         _app.deleteSelectedRange();
189                                 }
190                         });
191                 _deleteRangeButton.setEnabled(false);
192                 buttonPanel.add(_deleteRangeButton);
193                 buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
194
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))
200                 );
201
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);
216
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);
229
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)
238                         {
239                                 dataUpdated();
240                         }
241                 });
242                 lowerPanel.add(_unitsDropdown);
243                 add(lowerPanel, BorderLayout.SOUTH);
244         }
245
246
247         /**
248          * Select the specified point
249          * @param inValue value to select
250          */
251         private void selectPoint(int inValue)
252         {
253                 if (_track != null && !_ignoreScrollEvents)
254                 {
255                         _trackInfo.getSelection().selectPoint(inValue);
256                 }
257         }
258
259
260         /**
261          * Notification that Track has been updated
262          */
263         public void dataUpdated()
264         {
265                 // Update track data
266                 if (_track == null || _track.getNumPoints() <= 0)
267                 {
268                         _trackpointsLabel.setText(I18nManager.getText("details.notrack"));
269                         _filenameLabel.setText("");
270                 }
271                 else
272                 {
273                         _trackpointsLabel.setText(I18nManager.getText("details.track.points") + ": "
274                                 + _track.getNumPoints());
275                         int numFiles = _trackInfo.getFileInfo().getNumFiles();
276                         if (numFiles == 1)
277                         {
278                                 _filenameLabel.setText(I18nManager.getText("details.track.file") + ": "
279                                         + _trackInfo.getFileInfo().getFilename());
280                         }
281                         else if (numFiles > 1)
282                         {
283                                 _filenameLabel.setText(I18nManager.getText("details.track.numfiles") + ": "
284                                         + numFiles);
285                         }
286                         else _filenameLabel.setText("");
287                 }
288
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)
294                 {
295                         _indexLabel.setText(I18nManager.getText("details.nopointselection"));
296                         _latLabel.setText("");
297                         _longLabel.setText("");
298                         _altLabel.setText("");
299                         _timeLabel.setText("");
300                         _nameLabel.setText("");
301                 }
302                 else
303                 {
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())):
312                                 ""));
313                         if (currentPoint.getTimestamp().isValid())
314                                 _timeLabel.setText(LABEL_POINT_TIMESTAMP + currentPoint.getTimestamp().getText());
315                         else
316                                 _timeLabel.setText("");
317                         String name = currentPoint.getFieldValue(Field.WAYPT_NAME);
318                         if (name != null && !name.equals(""))
319                         {
320                                 _nameLabel.setText(LABEL_POINT_WAYPOINTNAME + name);
321                         }
322                         else _nameLabel.setText("");
323                 }
324
325                 // Update scroller settings
326                 _ignoreScrollEvents = true;
327                 if (_track == null || _track.getNumPoints() < 2)
328                 {
329                         // careful to avoid event loops here
330                         // _scroller.setValue(0);
331                         _scroller.setEnabled(false);
332                 }
333                 else
334                 {
335                         _scroller.setMaximum(_track.getNumPoints() + SCROLLBAR_INTERVAL);
336                         if (currentPointIndex >= 0)
337                                 _scroller.setValue(currentPointIndex);
338                         _scroller.setEnabled(true);
339                 }
340                 _ignoreScrollEvents = false;
341
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());
348
349                 // Update range details
350                 if (_track == null || !selection.hasRangeSelected())
351                 {
352                         _rangeLabel.setText(I18nManager.getText("details.norangeselection"));
353                         _distanceLabel.setText("");
354                         _durationLabel.setText("");
355                         _altRangeLabel.setText("");
356                         _updownLabel.setText("");
357                 }
358                 else
359                 {
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"));
367                         else
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()));
373                         else
374                                 _durationLabel.setText("");
375                         String altUnitsLabel = getAltitudeUnitsLabel(selection.getAltitudeFormat());
376                         IntegerRange altRange = selection.getAltitudeRange();
377                         if (altRange.getMinimum() >= 0 && altRange.getMaximum() >= 0)
378                         {
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);
385                         }
386                         else
387                         {
388                                 _altRangeLabel.setText("");
389                                 _updownLabel.setText("");
390                         }
391                 }
392         }
393
394
395         /**
396          * Choose the appropriate altitude units label for the specified format
397          * @param inFormat altitude format
398          * @return language-sensitive string
399          */
400         private static String getAltitudeUnitsLabel(int inFormat)
401         {
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");
408         }
409
410
411         /**
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
415          */
416         private static String buildDurationString(long inNumSecs)
417         {
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");
425                 return "big";
426         }
427
428
429         /**
430          * Build a String to describe a distance
431          * @param inDist distance
432          * @return formatted String
433          */
434         private String buildDistanceString(double inDist)
435         {
436                 // Set precision of formatter
437                 int numDigits = 0;
438                 if (inDist < 1.0)
439                         numDigits = 3;
440                 else if (inDist < 10.0)
441                         numDigits = 2;
442                 else if (inDist < 100.0)
443                         numDigits = 1;
444                 // set formatter
445                 _distanceFormatter.setMaximumFractionDigits(numDigits);
446                 _distanceFormatter.setMinimumFractionDigits(numDigits);
447                 return _distanceFormatter.format(inDist);
448         }
449 }