1 package tim.prune.function;
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Dimension;
6 import java.awt.FlowLayout;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.KeyEvent;
10 import java.awt.event.KeyListener;
11 import java.util.TimeZone;
13 import javax.swing.JButton;
14 import javax.swing.JDialog;
15 import javax.swing.JPanel;
16 import javax.swing.JScrollPane;
17 import javax.swing.JTabbedPane;
18 import javax.swing.JTextArea;
21 import tim.prune.GenericFunction;
22 import tim.prune.I18nManager;
23 import tim.prune.config.Config;
24 import tim.prune.config.TimezoneHelper;
25 import tim.prune.data.AltitudeRange;
26 import tim.prune.data.AudioClip;
27 import tim.prune.data.Coordinate;
28 import tim.prune.data.DataPoint;
29 import tim.prune.data.Field;
30 import tim.prune.data.Photo;
31 import tim.prune.data.RangeStatsWithGradients;
32 import tim.prune.data.Selection;
33 import tim.prune.data.SpeedCalculator;
34 import tim.prune.data.SpeedValue;
35 import tim.prune.data.Track;
36 import tim.prune.data.Unit;
37 import tim.prune.data.UnitSet;
38 import tim.prune.gui.CoordDisplay;
39 import tim.prune.gui.DisplayUtils;
43 * Class to show the full point/range details in a separate popup
45 public class ShowFullDetails extends GenericFunction
47 private JDialog _dialog = null;
48 private JTabbedPane _tabs = null;
49 private JButton _okButton = null;
51 private JTextArea _pointTextArea = null;
52 private JTextArea _rangeTextArea = null;
57 * @param inApp App object
59 public ShowFullDetails(App inApp)
64 /** Get the name key */
65 public String getNameKey() {
66 return "function.viewfulldetails";
76 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
77 _dialog.setLocationRelativeTo(_parentFrame);
78 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
79 _dialog.getContentPane().add(makeDialogComponents());
83 _dialog.setVisible(true);
84 _okButton.requestFocus();
88 * Create dialog components
89 * @return Panel containing all gui elements in dialog
91 private Component makeDialogComponents()
93 JPanel mainPanel = new JPanel();
94 mainPanel.setLayout(new BorderLayout());
96 _tabs = new JTabbedPane();
97 mainPanel.add(_tabs, BorderLayout.CENTER);
99 JPanel pointPanel = new JPanel();
100 pointPanel.setLayout(new BorderLayout());
101 _pointTextArea = new JTextArea(I18nManager.getText("details.nopointselection"));
102 _pointTextArea.setEditable(false);
103 _pointTextArea.setLineWrap(true);
104 JScrollPane scrollPane = new JScrollPane(_pointTextArea);
105 scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
106 scrollPane.setPreferredSize(new Dimension(500, 230));
107 pointPanel.add(scrollPane, BorderLayout.CENTER);
108 _tabs.add(I18nManager.getText("details.pointdetails"), pointPanel);
110 JPanel rangePanel = new JPanel();
111 rangePanel.setLayout(new BorderLayout());
112 _rangeTextArea = new JTextArea(I18nManager.getText("details.norangeselection"));
113 _rangeTextArea.setEditable(false);
114 _rangeTextArea.setLineWrap(true);
115 JScrollPane scrollPane2 = new JScrollPane(_rangeTextArea);
116 scrollPane2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
117 scrollPane2.setPreferredSize(new Dimension(500, 230));
118 rangePanel.add(scrollPane2, BorderLayout.CENTER);
119 _tabs.add(I18nManager.getText("details.rangedetails"), rangePanel);
121 // OK button at the bottom
122 JPanel okPanel = new JPanel();
123 okPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
124 _okButton = new JButton(I18nManager.getText("button.ok"));
125 _okButton.addActionListener(new ActionListener()
127 public void actionPerformed(ActionEvent e)
132 _okButton.addKeyListener(new KeyListener() {
133 public void keyPressed(KeyEvent e)
135 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
137 public void keyTyped(KeyEvent e) {}
138 public void keyReleased(KeyEvent e) {}
140 okPanel.add(_okButton);
141 mainPanel.add(okPanel, BorderLayout.SOUTH);
147 * Update the labels with the current details
149 private void updateDetails()
151 if (_app.getTrackInfo().getCurrentPoint() != null)
153 final String pointString = makePointDescription(_app.getTrackInfo().getTrack(),
154 _app.getTrackInfo().getSelection().getCurrentPointIndex());
155 _pointTextArea.setText(pointString);
157 _tabs.setSelectedIndex(0);
161 _pointTextArea.setText(I18nManager.getText("details.nopointselection"));
163 _tabs.setSelectedIndex(1);
166 Selection selection = _app.getTrackInfo().getSelection();
167 if (selection.hasRangeSelected())
169 RangeStatsWithGradients stats = new RangeStatsWithGradients(_app.getTrackInfo().getTrack(),
170 selection.getStart(), selection.getEnd());
171 SpeedValue maxSpeed = calculateMaxSpeed(_app.getTrackInfo().getTrack(),
172 selection.getStart(), selection.getEnd());
173 _rangeTextArea.setText(makeRangeDescription(stats, maxSpeed));
177 _rangeTextArea.setText(I18nManager.getText("details.norangeselection"));
182 * Calculate the maximum horizontal speed value in the given selection
183 * @param inTrack track object
184 * @param inStartIndex start of selection
185 * @param inEndIndex end of selection
186 * @return max speed, if any
188 private static SpeedValue calculateMaxSpeed(Track inTrack, int inStartIndex, int inEndIndex)
190 SpeedValue maxSpeed = new SpeedValue();
191 SpeedValue currSpeed = new SpeedValue();
192 for (int i=inStartIndex; i<=inEndIndex; i++)
194 SpeedCalculator.calculateSpeed(inTrack, i, currSpeed);
195 if (currSpeed.isValid() && (!maxSpeed.isValid() || currSpeed.getValue() > maxSpeed.getValue()))
197 maxSpeed.setValue(currSpeed.getValue());
204 * @param inTrack current track
205 * @param inPointIndex current point index
206 * @return string describing point details
208 private static String makePointDescription(Track inTrack, int inPointIndex)
210 DataPoint point = inTrack.getPoint(inPointIndex);
216 final int coordDisplayFormat = Coordinate.getCoordinateFormatForDisplay(
217 Config.getConfigInt(Config.KEY_COORD_DISPLAY_FORMAT));
218 StringBuffer result = new StringBuffer();
219 final String latStr = CoordDisplay.makeCoordinateLabel(point.getLatitude(), coordDisplayFormat);
220 final String lonStr = CoordDisplay.makeCoordinateLabel(point.getLongitude(), coordDisplayFormat);
221 addTextPair(result, "fieldname.latitude", latStr);
222 addTextPair(result, "fieldname.longitude", lonStr);
223 addTextPair(result, "fieldname.coordinates", latStr + ", " + lonStr);
225 if (point.hasAltitude())
227 final Unit altUnit = Config.getUnitSet().getAltitudeUnit();
228 addTextPair(result, "fieldname.altitude", "" + point.getAltitude().getValue(altUnit),
229 I18nManager.getText(altUnit.getShortnameKey()));
231 if (point.hasTimestamp())
233 TimeZone timezone = TimezoneHelper.getSelectedTimezone();
234 addTextPair(result, "fieldname.date", point.getTimestamp().getDateText(timezone));
235 addTextPair(result, "fieldname.timestamp", point.getTimestamp().getTimeText(timezone));
238 addTextPair(result, "fieldname.waypointname", point.getWaypointName());
240 addTextPair(result, "fieldname.description", point.getFieldValue(Field.DESCRIPTION));
242 addTextPair(result, "fieldname.comment", point.getFieldValue(Field.COMMENT));
244 addTextPair(result, "fieldname.waypointtype", point.getFieldValue(Field.WAYPT_TYPE));
246 // Speed can come from either timestamps and distances, or speed values in data
247 SpeedValue speedValue = new SpeedValue();
248 SpeedCalculator.calculateSpeed(inTrack, inPointIndex, speedValue);
249 UnitSet unitSet = Config.getUnitSet();
250 if (speedValue.isValid())
252 final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
253 String speed = DisplayUtils.roundedNumber(speedValue.getValue());
254 addTextPair(result, "fieldname.speed", speed, speedUnitsStr);
257 // Now do the vertical speed in the same way
258 SpeedCalculator.calculateVerticalSpeed(inTrack, inPointIndex, speedValue);
259 if (speedValue.isValid())
261 final String vSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
262 String speed = DisplayUtils.roundedNumber(speedValue.getValue());
263 addTextPair(result, "fieldname.verticalspeed", speed, vSpeedUnitsStr);
266 Photo currentPhoto = point.getPhoto();
267 if (currentPhoto != null)
269 addTextPair(result, "details.photofile", currentPhoto.getName());
270 addTextPair(result, "details.media.fullpath", currentPhoto.getFullPath());
273 AudioClip currentAudio = point.getAudio();
274 if (currentAudio != null)
276 addTextPair(result, "details.audio.file", currentAudio.getName());
277 addTextPair(result, "details.media.fullpath", currentAudio.getFullPath());
280 return result.toString();
284 * Make the range description text
285 * @param inStats stats object
286 * @param inMaxSpeed maximum speed info
287 * @return string describing range
289 private static String makeRangeDescription(RangeStatsWithGradients inStats, SpeedValue inMaxSpeed)
291 StringBuffer result = new StringBuffer();
292 addTextPair(result, "details.track.points", "" + inStats.getNumPoints());
293 addTextPair(result, "details.range.numsegments", "" + inStats.getNumSegments());
294 final boolean hasMultipleSegments = (inStats.getNumSegments() > 1);
296 UnitSet unitSet = Config.getUnitSet();
297 final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
298 if (inMaxSpeed.isValid())
300 final String maxSpeedStr = DisplayUtils.roundedNumber(inMaxSpeed.getValue()) + " " + speedUnitsStr;
301 addTextPair(result, "details.range.maxspeed", maxSpeedStr);
304 addHeading(result, "dialog.fullrangedetails.colsegments");
305 final Unit distUnit = Config.getUnitSet().getDistanceUnit();
306 final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
307 final double movingDist = inStats.getMovingDistance();
308 addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(movingDist),
310 long numSecs = inStats.getMovingDurationInSeconds();
311 addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(numSecs));
313 if (numSecs > 0 && movingDist > 0.0)
315 addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(movingDist/numSecs*3600.0),
317 addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (numSecs/movingDist)),
318 "/ " + distUnitsStr);
320 final Unit altUnit = unitSet.getAltitudeUnit();
321 final String altUnitsStr = I18nManager.getText(altUnit.getShortnameKey());
322 if (inStats.getMovingAltitudeRange().hasRange())
324 AltitudeRange altRange = inStats.getMovingAltitudeRange();
325 addTextPair(result, "fieldname.altitude", "" + altRange.getMinimum(altUnit) + altUnitsStr + " "
326 + I18nManager.getText("details.altitude.to") + " "
327 + altRange.getMaximum(altUnit) + altUnitsStr);
328 addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
329 addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
330 addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getMovingGradient()), "%");
333 final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
334 final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getMovingVerticalSpeed());
335 addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
339 if (hasMultipleSegments)
341 addHeading(result, "dialog.fullrangedetails.coltotal");
342 final double totalDist = inStats.getTotalDistance();
343 addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(totalDist), distUnitsStr);
344 long totalSecs = inStats.getTotalDurationInSeconds();
345 addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(totalSecs));
346 if (totalSecs > 0 && totalDist > 0.0)
348 addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(totalDist/totalSecs*3600.0),
350 addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (totalSecs/totalDist)),
351 "/ " + distUnitsStr);
353 if (inStats.getTotalAltitudeRange().hasRange())
355 AltitudeRange altRange = inStats.getTotalAltitudeRange();
356 addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
357 addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
358 addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getTotalGradient()), "%");
361 final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
362 final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getTotalVerticalSpeed());
363 addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
367 return result.toString();
371 * Add the label and value to the buffer
372 * @param inBuffer buffer to append to
373 * @param inLabelKey label key
374 * @param inValue value text
376 private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue)
378 addTextPair(inBuffer, inLabelKey, inValue, null);
382 * Add the label and value to the buffer
383 * @param inBuffer buffer to append to
384 * @param inLabelKey label key
385 * @param inValue value text
386 * @param inUnits optional units string
388 private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue, String inUnits)
390 if (inValue != null && !inValue.equals(""))
392 inBuffer.append(I18nManager.getText(inLabelKey));
393 inBuffer.append(": ");
394 inBuffer.append(inValue);
395 if (inUnits != null && !inUnits.equals(""))
397 inBuffer.append(' ');
398 inBuffer.append(inUnits);
400 inBuffer.append("\n");
405 * Add a heading to the buffer
406 * @param inBuffer buffer to append to
407 * @param inLabelKey key for heading
409 private static void addHeading(StringBuffer inBuffer, String inLabelKey)
411 final String heading = I18nManager.getText(inLabelKey);
412 inBuffer.append('\n').append(heading).append('\n');
413 for (int i=0; i<heading.length(); i++)
415 inBuffer.append('=');
417 inBuffer.append('\n');